diff --git a/ArrlFd.pas b/ArrlFd.pas index e207118..1a6cedb 100644 --- a/ArrlFd.pas +++ b/ArrlFd.pas @@ -7,8 +7,7 @@ interface uses - Generics.Defaults, Generics.Collections, ARRL, - SysUtils, Classes, {Contnrs,} PerlRegEx, pcre; + Generics.Defaults, Generics.Collections, Contest, DxStn; type TFdCallRec = class @@ -21,22 +20,21 @@ TFdCallRec = class class function compareCall(const left, right: TFdCallRec) : integer; static; end; -TArrlFieldDay = class +TArrlFieldDay = class(TContest) private FdCallList: TObjectList; Comparer: IComparer; - procedure LoadFdHistoryFile; - public constructor Create; destructor Destroy; override; - function pickStation(): integer; - //function getcwopsid(): integer; - //function getcwopscall(id:integer): string; - //function getcwopsname(id:integer): string; - //function getcwopsnum(id:integer): integer; - function getCall(id:integer): string; // returns station callsign + function LoadCallHistory(const AUserCallsign : string) : boolean; override; + + function PickStation(): integer; override; + procedure DropStation(id : integer); override; + function GetCall(id : integer): string; override; // returns station callsign + procedure GetExchange(id : integer; out station : TDxStation); override; + function getExch1(id:integer): string; // returns station info (e.g. 3A) function getExch2(id:integer): string; // returns section info (e.g. OR) function getClass(id:integer): string; // returns station class (e.g. 3A) @@ -44,19 +42,16 @@ TArrlFieldDay = class function getUserText(id:integer): string; // returns optional club name //function IsNum(Num: String): Boolean; function FindCallRec(out fdrec: TFdCallRec; const ACall: string): Boolean; - function GetStationInfo(const ACallsign: string) : string; + function GetStationInfo(const ACallsign: string) : string; override; end; -var - gARRLFD: TArrlFieldDay; - implementation uses - log; + SysUtils, Classes, Log, PerlRegEx, pcre, ARRL; -procedure TArrlFieldDay.LoadFdHistoryFile; +function TArrlFieldDay.LoadCallHistory(const AUserCallsign : string) : boolean; const DelimitChar: char = ','; var @@ -64,13 +59,18 @@ procedure TArrlFieldDay.LoadFdHistoryFile; i: integer; rec: TFdCallRec; begin + // reload call history iff user's callsign has changed. + Result := not HasUserCallsignChanged(AUserCallsign); + if Result then + Exit; + slst:= TStringList.Create; tl:= TStringList.Create; tl.Delimiter := DelimitChar; tl.StrictDelimiter := True; try - FdCallList:= TObjectList.Create; + FdCallList.Clear; slst.LoadFromFile(ParamStr(1) + 'FD_2022-004.TXT'); @@ -96,6 +96,10 @@ procedure TArrlFieldDay.LoadFdHistoryFile; end; end; + // retain user's callsign after successful load + SetUserCallsign(AUserCallsign); + Result := True; + finally slst.Free; tl.Free; @@ -106,8 +110,8 @@ procedure TArrlFieldDay.LoadFdHistoryFile; constructor TArrlFieldDay.Create; begin inherited Create; + FdCallList:= TObjectList.Create; Comparer := TComparer.Construct(TFdCallRec.compareCall); - LoadFdHistoryFile; end; @@ -118,12 +122,19 @@ destructor TArrlFieldDay.Destroy; end; -function TArrlFieldDay.pickStation(): integer; +function TArrlFieldDay.PickStation(): integer; begin result := random(FdCallList.Count); end; +procedure TArrlFieldDay.DropStation(id : integer); +begin + assert(id < FdCallList.Count); + FdCallList.Delete(id); +end; + + function TArrlFieldDay.FindCallRec(out fdrec: TFdCallRec; const ACall: string): Boolean; var rec: TFdCallRec; @@ -163,7 +174,7 @@ function TArrlFieldDay.GetStationInfo(const ACallsign: string) : string; dxEntity := ''; result:= ''; - if gArrlFd.FindCallRec(fdrec, ACallsign) then + if FindCallRec(fdrec, ACallsign) then begin userText:= fdrec.UserText; @@ -184,12 +195,21 @@ function TArrlFieldDay.GetStationInfo(const ACallsign: string) : string; end; -function TArrlFieldDay.getCall(id:integer): string; // returns station callsign +// returns station callsign +function TArrlFieldDay.GetCall(id : integer): string; begin result := FdCallList.Items[id].Call; end; +procedure TArrlFieldDay.GetExchange(id : integer; out station : TDxStation); +begin + station.Exch1 := getExch1(id); + station.Exch2 := getExch2(id); + station.UserText := getUserText(id); +end; + + function TArrlFieldDay.getExch1(id:integer): string; // returns station info (e.g. 3A) begin result := FdCallList.Items[id].StnClass; diff --git a/CWOPS.pas b/CWOPS.pas index 10bb60d..6d7001b 100644 --- a/CWOPS.pas +++ b/CWOPS.pas @@ -3,7 +3,7 @@ interface uses - SysUtils, Classes, Contnrs, PerlRegEx, pcre; + Classes, Contest, Contnrs, DxStn; type TCWOPSRec= class @@ -13,40 +13,50 @@ TCWOPSRec= class Number: string; end; - TCWOPS= class + TCWOPS= class(TContest) private CWOPSList: TList; - procedure LoadCWOPS; procedure Delimit(var AStringList: TStringList; const AText: string); public constructor Create; - function getcwopsid(): integer; - function getcwopscall(id:integer): string; + destructor Destroy; override; + function LoadCallHistory(const AUserCallsign : string) : boolean; override; + + function PickStation(): integer; override; + procedure DropStation(id : integer); override; + function GetCall(id : integer): string; override; + procedure GetExchange(id : integer; out station : TDxStation); override; + function getcwopsname(id:integer): string; function getcwopsnum(id:integer): integer; - function IsNum(Num: String): Boolean; end; -var - CWOPSCWT: TCWOPS; + function IsNum(Num: String): Boolean; implementation uses - log; + SysUtils, Log; -procedure TCWOPS.LoadCWOPS; +function TCWOPS.LoadCallHistory(const AUserCallsign : string) : boolean; var slst, tl: TStringList; i: integer; CWO: TCWOPSRec; begin + // reload call history iff user's callsign has changed. + Result := not HasUserCallsignChanged(AUserCallsign); + if Result then + Exit; + slst:= TStringList.Create; tl:= TStringList.Create; + try - CWOPSList:= TList.Create; + CWOPSList.Clear; + slst.LoadFromFile(ParamStr(1) + 'CWOPS.LIST'); slst.Sort; @@ -70,6 +80,10 @@ procedure TCWOPS.LoadCWOPS; end; end; + // retain user's callsign after successful load + SetUserCallsign(AUserCallsign); + Result := True; + finally slst.Free; tl.Free; @@ -81,21 +95,42 @@ procedure TCWOPS.LoadCWOPS; constructor TCWOPS.Create; begin inherited Create; - LoadCWOPS; + CWOPSList:= TList.Create; end; -function TCWOPS.getcwopsid(): integer; +destructor TCWOPS.Destroy; +begin + FreeAndNil(CWOPSList); + inherited; +end; + + +function TCWOPS.PickStation(): integer; begin result := random(CWOPSList.Count); end; -function TCWOPS.getcwopscall(id:integer): string; +procedure TCWOPS.DropStation(id : integer); +begin + assert(id < CWOPSList.Count); + CWOPSList.Delete(id); +end; + +function TCWOPS.GetCall(id : integer): string; begin result := TCWOPSRec(CWOPSList.Items[id]).Call; end; + +procedure TCWOPS.GetExchange(id : integer; out station : TDxStation); +begin + station.OpName := getcwopsname(id); + station.NR := getcwopsnum(id); +end; + + function TCWOPS.getcwopsname(id:integer): string; begin @@ -108,7 +143,7 @@ function TCWOPS.getcwopsnum(id:integer): integer; result := strtoint(TCWOPSRec(CWOPSList.Items[id]).Number); end; -function TCWOPS.IsNum(Num: String): Boolean; +function IsNum(Num: String): Boolean; var X : Integer; begin diff --git a/CallLst.pas b/CallLst.pas index 176338a..d6e9189 100644 --- a/CallLst.pas +++ b/CallLst.pas @@ -8,22 +8,35 @@ interface uses - SysUtils, Classes, Ini; - -procedure LoadCallList; -function PickCall: string; + Classes; + +type + // simple calllist. contains a TStringList of callsigns. + TCallList = class + protected + Calls: TStringList; + + public + constructor Create; + destructor Destroy; override; + procedure LoadCallList; + procedure Clear(); + function PickCall : string; + end; -var - Calls: TStringList; implementation +uses + SysUtils, Ini; + function CompareCalls(Item1, Item2: Pointer): Integer; begin Result := StrComp(PChar(Item1), PChar(Item2)); end; -procedure LoadCallList; +// reads callsigns from Master.dta file +procedure TCallList.LoadCallList; const Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/'; CHRCOUNT = Length(Chars); @@ -89,7 +102,14 @@ procedure LoadCallList; end; -function PickCall: string; +procedure TCallList.Clear(); +begin + Calls.Clear; +end; + + +// returns a single callsign +function TCallList.PickCall : string; var Idx: integer; begin @@ -106,11 +126,17 @@ function PickCall: string; end; -initialization +constructor TCallList.Create; +begin Calls := TStringList.Create; +end; -finalization +destructor TCallList.Destroy; +begin + Calls.Clear; Calls.Free; +end; + end. diff --git a/Contest.pas b/Contest.pas index c7caa1d..329a410 100644 --- a/Contest.pas +++ b/Contest.pas @@ -14,8 +14,16 @@ interface type TContest = class private + UserCallsign : String; // used by LoadCallHistory() to minimize reloads + function DxCount: integer; procedure SwapFilters; + + protected + constructor Create; + function HasUserCallsignChanged(const AUserCallsign : String) : boolean; + procedure SetUserCallsign(const AUserCallsign : String); + public BlockNumber: integer; Me: TMyStation; @@ -26,9 +34,17 @@ TContest = class RitPhase: Single; FStopPressed: boolean; - constructor Create; destructor Destroy; override; procedure Init; + function LoadCallHistory(const AHomeCallsign : string) : boolean; virtual; abstract; + + function PickStation : integer; virtual; abstract; + procedure DropStation(id : integer); virtual; abstract; + function GetCall(id : integer) : string; virtual; abstract; + procedure GetExchange(id : integer; out station : TDxStation); virtual; abstract; + function GetStationInfo(const ACallsign : string) : string; virtual; + function PickCallOnly : string; + function GetSentExchTypes( const AStationKind : TStationKind; const AMyCallsign : string) : TExchTypes; @@ -49,7 +65,7 @@ TContest = class implementation uses - Main; + Main, CallLst, ARRL; { TContest } @@ -80,6 +96,7 @@ constructor TContest.Create; Agc.HoldSamples := 155; Agc.AgcEnabled := true; NoActivityCnt :=0; + UserCallsign := ''; Init; end; @@ -102,6 +119,43 @@ procedure TContest.Init; Me.Init; Stations.Clear; BlockNumber := 0; + UserCallsign := ''; +end; + + +{ return whether to call history file is valid based on user's callsign. } +function TContest.HasUserCallsignChanged(const AUserCallsign : string) : boolean; +begin + // user's home callsign is required to load this contest. + Result := (not AUserCallsign.IsEmpty) and (UserCallsign <> AUserCallsign); +end; + + +procedure TContest.SetUserCallsign(const AUserCallsign : String); +begin + UserCallsign := AUserCallsign; +end; + + +{ + GetStationInfo() returns station's DXCC information. + + Adding a contest: UpdateSbar - update status bar with station info (e.g. FD shows UserText) + Override as needed for each contest. +} +function TContest.GetStationInfo(const ACallsign : string) : string; +begin + Result := gDXCCList.Search(ACallsign); +end; + + +// helper function to return only a callsign (used by QrnStation) +function TContest.PickCallOnly : string; +var + id : integer; +begin + id := PickStation; + Result := GetCall(id); end; diff --git a/CqWW.pas b/CqWW.pas index 0184662..414c04e 100644 --- a/CqWW.pas +++ b/CqWW.pas @@ -7,8 +7,7 @@ interface uses - Generics.Defaults, Generics.Collections, - SysUtils, Classes, {Contnrs,} PerlRegEx, pcre; + Generics.Defaults, Generics.Collections, Contest, DxStn; type TCqWwCallRec = class @@ -20,35 +19,35 @@ TCqWwCallRec = class class function compareCall(const left, right: TCqWwCallRec) : integer; static; end; -TCqWw = class +TCqWw = class(TContest) private CqWwCallList: TObjectList; Comparer: IComparer; - procedure LoadHistoryFile; - public constructor Create; destructor Destroy; override; - function pickStation(): integer; - function getCall(id:integer): string; // returns station callsign + function LoadCallHistory(const AUserCallsign : string) : Boolean; override; + + function PickStation(): integer; override; + procedure DropStation(id : integer); override; + function GetCall(id:integer): string; override; // returns station callsign + procedure GetExchange(id : integer; out station : TDxStation); override; + function getExch1(id:integer): string; // returns RST (e.g. 5NN) function getExch2(id:integer): string; // returns section info (e.g. 3) function getZone(id:integer): string; // returns CQZone (e.g. 3) function FindCallRec(out fdrec: TCqWwCallRec; const ACall: string): Boolean; - function GetStationInfo(const ACallsign: string) : string; + function GetStationInfo(const ACallsign: string) : string; override; function IsNum(Num: String): Boolean; end; -var - gCQWW: TCqWw; - implementation uses - log, ARRL; + SysUtils, Classes, log, ARRL; -procedure TCqWw.LoadHistoryFile; +function TCqWw.LoadCallHistory(const AUserCallsign : string) : boolean; const DelimitChar: char = ','; var @@ -56,13 +55,18 @@ procedure TCqWw.LoadHistoryFile; i: integer; rec: TCqWwCallRec; begin + // reload call history iff user's callsign has changed. + Result := not HasUserCallsignChanged(AUserCallsign); + if Result then + Exit; + slst:= TStringList.Create; tl:= TStringList.Create; tl.Delimiter := DelimitChar; tl.StrictDelimiter := True; try - CqWwCallList := TObjectList.Create; + CqWwCallList.Clear; slst.LoadFromFile(ParamStr(1) + 'CQWWCW.TXT'); @@ -84,6 +88,10 @@ procedure TCqWw.LoadHistoryFile; end; end; + // retain user's callsign after successful load + SetUserCallsign(AUserCallsign); + Result := True; + finally slst.Free; tl.Free; @@ -94,8 +102,8 @@ procedure TCqWw.LoadHistoryFile; constructor TCqWw.Create; begin inherited Create; + CqWwCallList := TObjectList.Create; Comparer := TComparer.Construct(TCqWwCallRec.compareCall); - LoadHistoryFile; end; @@ -106,12 +114,19 @@ destructor TCqWw.Destroy; end; -function TCqWw.pickStation(): integer; +function TCqWw.PickStation(): integer; begin result := random(CqWwCallList.Count); end; +procedure TCqWw.DropStation(id : integer); +begin + assert(id < CqWwCallList.Count); + CqWwCallList.Delete(id); +end; + + function TCqWw.FindCallRec(out fdrec: TCqWwCallRec; const ACall: string): Boolean; var rec: TCqWwCallRec; @@ -151,7 +166,7 @@ function TCqWw.GetStationInfo(const ACallsign: string) : string; dxEntity := ''; result:= ''; - if gCQWW.FindCallRec(fdrec, ACallsign) then + if Self.FindCallRec(fdrec, ACallsign) then begin userText:= fdrec.UserText; @@ -171,12 +186,20 @@ function TCqWw.GetStationInfo(const ACallsign: string) : string; end; -function TCqWw.getCall(id:integer): string; // returns station callsign +function TCqWw.getCall(id : integer): string; // returns station callsign begin result := CqWwCallList.Items[id].Call; end; +procedure TCqWw.GetExchange(id : integer; out station : TDxStation); +begin + station.Exch2 := getExch2(station.Operid); + station.NR := StrToInt(getExch2(station.Operid)); +end; + + + function TCqWw.getExch1(id:integer): string; // returns RST (e.g. 5NN) begin result := '5NN'; diff --git a/CqWpx.pas b/CqWpx.pas new file mode 100644 index 0000000..10cf5cb --- /dev/null +++ b/CqWpx.pas @@ -0,0 +1,128 @@ +unit CQWPX; + +{$ifdef FPC} +{$MODE Delphi} +{$endif} + +interface + +uses + Contest, CallLst, DxStn; + +type +TCqWpx = class(TContest) +private + // CQ Wpx Contest uses the global Calls variable (see CallLst.pas) + CallLst : TCallList; + +public + constructor Create; + destructor Destroy; override; + function LoadCallHistory(const AUserCallsign : string) : boolean; override; + + function PickStation(): integer; override; + procedure DropStation(id : integer); override; + function GetCall(id:integer): string; override; // returns station callsign + procedure GetExchange(id : integer; out station : TDxStation); override; + + function getExch1(id:integer): string; // returns RST (e.g. 5NN) + function getExch2(id:integer): string; // returns section info (e.g. 3) + {function getZone(id:integer): string; // returns CQZone (e.g. 3) + function FindCallRec(out fdrec: TCqWpxCallRec; const ACall: string): Boolean; + } + function GetStationInfo(const ACallsign: string) : string; override; +end; + +implementation + +uses + SysUtils, Classes, log, ARRL; + +function TCqWpx.LoadCallHistory(const AUserCallsign : string) : boolean; +begin + // reload call history iff user's callsign has changed. + Result := not HasUserCallsignChanged(AUserCallsign); + if Result then + Exit; + + CallLst.LoadCallList; + + // retain user's callsign after successful load + SetUserCallsign(AUserCallsign); + Result := True; +end; + + +constructor TCqWpx.Create; +begin + inherited Create; + CallLst := TCallList.Create; +end; + + +destructor TCqWpx.Destroy; +begin + CallLst.Free; + inherited; +end; + + +function TCqWpx.PickStation(): integer; +begin + Result := -1; +end; + + +procedure TCqWpx.DropStation(id : integer); +begin + // already deleted by GetCall +end; + + +// return status bar information string from DXCC data file. +// the callsign, Entity and Continent are returned. +// this string is used in MainForm.sbar.Caption (status bar). +// Format: ' - Entity/Continent' +function TCqWpx.GetStationInfo(const ACallsign: string) : string; +var + dxrec : TDXCCRec; +begin + dxrec := nil; + Result := ''; + + // find caller's Continent/Entity + if gDXCCList.FindRec(dxrec, ACallsign) then + Result := Format('%s - %s/%s', [ACallsign, dxRec.Continent, dxRec.Entity]); +end; + + +function TCqWpx.GetCall(id : integer): string; // returns station callsign +begin + Result := CallLst.PickCall; +end; + + +procedure TCqWpx.GetExchange(id : integer; out station : TDxStation); +begin + station.Exch1 := getExch1(station.Operid); // RST + station.NR := station.Oper.GetNR; // serial number +end; + + + +function TCqWpx.getExch1(id:integer): string; // returns RST (e.g. 5NN) +begin + result := '599'; +end; + + +function TCqWpx.getExch2(id:integer): string; // returns serial number (e.g. 3) +begin + Result := '1'; +end; + + +end. + + + diff --git a/DxStn.pas b/DxStn.pas index b0814c3..7c0a91e 100644 --- a/DxStn.pas +++ b/DxStn.pas @@ -8,9 +8,7 @@ interface uses - SysUtils, Classes, Station, RndFunc, Dialogs, Ini, ARRLFD, NAQP, CWOPS, - CQWW, - CallLst, Qsb, DxOper, Log, SndTypes; + Station, Qsb, DxOper, SndTypes; type TDxStation = class(TStation) @@ -32,7 +30,8 @@ TDxStation = class(TStation) implementation uses - Contest; + SysUtils, Classes, RndFunc, Dialogs, + CallLst, Log, Ini, Contest; { TDxStation } @@ -41,27 +40,10 @@ constructor TDxStation.CreateStation; inherited Create(nil); HisCall := Ini.Call; - // Adding a contest: DxStation.CreateStation - load a random callsign - case SimContest of - scCwt: begin - Operid := CWOPSCWT.getcwopsid(); - MyCall := CWOPSCWT.getcwopscall(Operid); - end; - scFieldDay: begin - Operid := gARRLFD.pickStation(); - MyCall := gARRLFD.getCall(Operid); - end; - scNaQp: begin - Operid := gNAQP.pickStation(); - MyCall := gNAQP.getCall(Operid); - end; - scCQWW: begin - Operid := gCQWW.pickStation(); - MyCall := gCQWW.getCall(Operid); - end - else - MyCall := PickCall; // Pick one Callsign from Calllist - end; + + // Pick one Callsign from call history file + Operid := Tst.PickStation; + MyCall := Tst.GetCall(Operid); Oper := TDxOperator.Create; Oper.Call := MyCall; @@ -75,30 +57,8 @@ constructor TDxStation.CreateStation; SentExchTypes := Tst.GetSentExchTypes(skDxStation, MyCall); // Adding a contest: DxStation.CreateStation - get Exch1 (e.g. Name), Exch2 (e.g. NR), and optional UserText - case SimContest of - scCwt: begin - OpName := CWOPSCWT.getcwopsname(Operid); - NR := CWOPSCWT.getcwopsnum(Operid); - end; - scFieldDay: begin - Exch1 := gARRLFD.getExch1(Operid); - Exch2 := gARRLFD.getExch2(Operid); - UserText := gARRLFD.getUserText(Operid); - end; - scNaQp: begin - Exch1 := gNAQP.getExch1(Operid); - OpName := Exch1; // TODO - refactor etOpName to use Exch1 - Exch2 := gNAQP.getExch2(Operid); - UserText := gNAQP.getUserText(Operid); - end; - scCQWW: begin - Exch2 := gCQWW.getExch2(Operid); - NR := StrToInt(gCQWW.getExch2(Operid)); - end - else - NR := Oper.GetNR; - end; - //showmessage(MyCall); + // load dynamic exchange field information into this DxStation. + Tst.GetExchange(Operid, Self); if Ini.Lids and (Random < 0.03) then RST := 559 + 10 * Random(4) @@ -114,6 +74,12 @@ constructor TDxStation.CreateStation; Amplitude := 9000 + 18000 * (1 + RndUShaped); Pitch := Round(RndGaussLim(0, 300)); + if Ini.RunMode = rmHst then + begin + Tst.DropStation(Operid); + Operid := -1; + end; + //the MeSent event will follow immediately TimeOut := NEVER; State := stCopying; diff --git a/Ini.pas b/Ini.pas index c4b22f4..c92987a 100644 --- a/Ini.pas +++ b/Ini.pas @@ -199,9 +199,11 @@ procedure FromIni; ArrlClass := ReadString(SEC_STN, 'ArrlClass', '3A'); ArrlSection := ReadString(SEC_STN, 'ArrlSection', 'OR'); - MainForm.SetMyCall(ReadString(SEC_STN, 'Call', Call)); - MainForm.SetPitch(ReadInteger(SEC_STN, 'Pitch', 3)); - MainForm.SetBw(ReadInteger(SEC_STN, 'BandWidth', 9)); + // load station settings... + // Calls to SetMyCall, SetPitch, SetBw, etc., moved to MainForm.SetContest + Call := ReadString(SEC_STN, 'Call', Call); + MainForm.ComboBox1.ItemIndex := ReadInteger(SEC_STN, 'Pitch', 3); + MainForm.ComboBox2.ItemIndex := ReadInteger(SEC_STN, 'BandWidth', 9); HamName := ReadString(SEC_STN, 'Name', ''); CWOPSNum := ReadString(SEC_STN, 'cwopsnum', CWOPSNum); @@ -210,8 +212,8 @@ procedure FromIni; MainForm.UpdCWMinRxSpeed(ReadInteger(SEC_STN, 'CWMinRxSpeed', MinRxWpm)); MainForm.UpdNRDigits(ReadInteger(SEC_STN, 'NRDigits', NRDigits)); - MainForm.SetWpm(ReadInteger(SEC_STN, 'Wpm', Wpm)); - MainForm.SetQsk(ReadBool(SEC_STN, 'Qsk', Qsk)); + Wpm := ReadInteger(SEC_STN, 'Wpm', Wpm); + Qsk := ReadBool(SEC_STN, 'Qsk', Qsk); CallsFromKeyer := ReadBool(SEC_STN, 'CallsFromKeyer', CallsFromKeyer); GetWpmUsesGaussian := ReadBool(SEC_STN, 'GetWpmUsesGaussian', GetWpmUsesGaussian); @@ -241,8 +243,6 @@ procedure FromIni; begin V := 3; WriteInteger(SEC_SYS, 'BufSize', V); end; V := Max(1, Min(5, V)); BufSize := 64 shl V; - Tst.Filt.SamplesInInput := BufSize; - Tst.Filt2.SamplesInInput := BufSize; V := ReadInteger(SEC_STN, 'SelfMonVolume', 0); MainForm.VolumeSlider1.Value := V / 80 + 0.75; diff --git a/Log.pas b/Log.pas index fa2613a..05bb547 100644 --- a/Log.pas +++ b/Log.pas @@ -8,9 +8,7 @@ interface uses - Windows, SysUtils, Classes, Graphics, RndFunc, Math, Controls, - StdCtrls, ExtCtrls, ARRL, ARRLFD, NAQP, PerlRegEx, pcre; - + Classes, Controls, ExtCtrls; procedure SaveQso; procedure LastQsoToScreen; @@ -68,6 +66,8 @@ THisto= class(TObject) implementation uses + Windows, SysUtils, Graphics, RndFunc, Math, + StdCtrls, PerlRegEx, pcre, Contest, Main, DxStn, DxOper, Ini, MorseKey; @@ -145,14 +145,7 @@ procedure UpdateSbar(const ACallsign: string); s: string; begin // Adding a contest: UpdateSbar - update status bar with station info (e.g. FD shows UserText) - case Ini.SimContest of - scFieldDay: - s := gArrlFd.GetStationInfo(ACallsign); - scNaQp: - s := gNAQP.GetStationInfo(ACallsign); - else - s := gDXCCList.Search(ACallsign); - end; + s := Tst.GetStationInfo(ACallsign); // '&' are suppressed in this control; replace with '&&' s:= StringReplace(s, '&', '&&', [rfReplaceAll]); diff --git a/Main.pas b/Main.pas index 19bf3b3..a309e99 100644 --- a/Main.pas +++ b/Main.pas @@ -16,7 +16,7 @@ interface Buttons, SndCustm, SndOut, Contest, Ini, MorseKey, CallLst, VolmSldr, VolumCtl, StdCtrls, Station, Menus, ExtCtrls, MAth, ComCtrls, Spin, SndTypes, ShellApi, jpeg, ToolWin, ImgList, Crc32, - WavFile, IniFiles, Idhttp, ARRL, ARRLFD, NAQP, CWOPS, CQWW, + WavFile, IniFiles, Idhttp, System.ImageList; const @@ -352,6 +352,7 @@ TMainForm = class(TForm) private MustAdvance: boolean; + function CreateContest(AContestId : TSimContest) : TContest; procedure ConfigureExchangeFields( AExchType1: TExchange1Type; AExchType2: TExchange2Type); @@ -409,7 +410,10 @@ function ToStr(const val : TExchange2Type): string; overload; MainForm: TMainForm; implementation -uses TypInfo, ScoreDlg, Log, PerlRegEx; + +uses + ARRL, ARRLFD, NAQP, CWOPS, CQWW, CQWPX, + TypInfo, ScoreDlg, Log, PerlRegEx; {$R *.DFM} @@ -446,16 +450,8 @@ procedure TMainForm.FormCreate(Sender: TObject); ListView2.Visible:= False; ListView2.Clear; - Tst := TContest.Create; - LoadCallList; - - // Adding a contest: implement a new contest-specific call history .pas file. - // Adding a contest: load call history file (be sure to delete it below). + // load DXCC support gDXCCList := TDXCC.Create; - gARRLFD := TArrlFieldDay.Create; - gNAQP := TNcjNaQp.Create; - gCQWW := TCqWW.Create; - CWOPSCWT := TCWOPS.Create; Histo:= THisto.Create(PaintBox1); @@ -466,6 +462,7 @@ procedure TMainForm.FormCreate(Sender: TObject); Keyer.Rate := DEFAULTRATE; Keyer.BufSize := Ini.BufSize; + // create a derived TContest of the appropriate type SetContest(Ini.SimContest); end; @@ -474,16 +471,29 @@ procedure TMainForm.FormDestroy(Sender: TObject); begin ToIni; gDXCCList.Free; - gARRLFD.Free; - gNAQP.Free; - gCQWW.Free; - CWOPSCWT.Free; Histo.Free; Tst.Free; DestroyKeyer; end; +// Contest Factory - allocate a derived TContest of the appropriate type +function TMainForm.CreateContest(AContestId : TSimContest) : TContest; +begin + // Adding a contest: implement a new contest-specific call history .pas file. + Result := nil; + case AContestId of + scWpx, scHst: Result := TCqWpx.Create; + scCwt: Result := TCWOPS.Create; + scFieldDay: Result := TArrlFieldDay.Create; + scNaQp: Result := TNcjNaQp.Create; + scCQWW: Result := TCqWW.Create; + else + assert(false); + end; +end; + + procedure TMainForm.AlSoundOut1BufAvailable(Sender: TObject); begin if AlSoundOut1.Enabled then @@ -871,6 +881,11 @@ procedure TMainForm.SetContest(AContestNum: TSimContest); assert(ContestDefinitions[AContestNum].T = AContestNum, 'Contest definitions are out of order'); + + // drop prior contest + if Assigned(Tst) then + FreeAndNil(Tst); + Ini.SimContest := AContestNum; Ini.ActiveContest := @ContestDefinitions[AContestNum]; SimContestCombo.ItemIndex := Ord(AContestNum); @@ -881,8 +896,29 @@ procedure TMainForm.SetContest(AContestNum: TSimContest); sbar.Font.Color := clDefault; sbar.Visible := mnuShowCallsignInfo.Checked; - // update my sent exchange types - Tst.Me.SentExchTypes := Tst.GetSentExchTypes(skMyStation, Ini.Call); + // create new contest + Tst := CreateContest(AContestNum); + + // the following will initialize simulation-specific data owned by contest. + // (moved here from Ini.FromIni) + begin + SetMyCall(Ini.Call); + SetPitch(ComboBox1.ItemIndex); + SetBw(ComboBox2.ItemIndex); + SetWpm(Ini.Wpm); + SetQsk(Ini.Qsk); + + // buffer size - set in TContest.Create() + assert(Tst.Filt.SamplesInInput = Ini.BufSize); + assert(Tst.Filt2.SamplesInInput = Ini.BufSize); + + // load contest-specific call history file + if not Tst.LoadCallHistory(Ini.Call) then + Exit; + + // update my sent exchange types (must be called after loading call lists?) + Tst.Me.SentExchTypes:= Tst.GetSentExchTypes(skMyStation, Ini.Call); + end; // update Exchange field labels and length settings (e.g. RST, Nr.) ConfigureExchangeFields(ActiveContest.ExchType1, ActiveContest.ExchType2); @@ -2015,7 +2051,7 @@ procedure TMainForm.CWOPSNumberClick(Sender: TObject); if buf = '' then begin exit; end; - if CWOPSCWT.isnum(buf)=False then begin + if not CWOPS.isnum(buf) then begin exit; end; CWOPSNum := buf; diff --git a/MorseRunner.dpr b/MorseRunner.dpr index af49ccc..eeb1734 100644 --- a/MorseRunner.dpr +++ b/MorseRunner.dpr @@ -37,7 +37,8 @@ uses ArrlFd in 'ArrlFd.pas', NaQp in 'NaQp.pas', CWOPS in 'CWOPS.pas', - CqWW in 'CqWW.pas'; + CqWW in 'CqWW.pas', + CqWpx in 'CqWpx.pas'; {$R *.RES} diff --git a/MorseRunner.dproj b/MorseRunner.dproj index 91b5ba1..9aa5598 100644 --- a/MorseRunner.dproj +++ b/MorseRunner.dproj @@ -155,6 +155,7 @@ + Cfg_2 Base diff --git a/MyStn.pas b/MyStn.pas index 03459ee..4489649 100644 --- a/MyStn.pas +++ b/MyStn.pas @@ -8,7 +8,7 @@ interface uses - SysUtils, Classes, Station, RndFunc, Ini, SndTypes, MorseKey; + Station, Classes, SndTypes; type TMyStation = class(TStation) @@ -31,7 +31,7 @@ TMyStation = class(TStation) implementation uses - Contest, Main; + SysUtils, RndFunc, Ini, MorseKey, Contest, Main; { TMyStation } diff --git a/NaQp.pas b/NaQp.pas index 4c60d2e..79eddc6 100644 --- a/NaQp.pas +++ b/NaQp.pas @@ -3,9 +3,7 @@ interface uses - Generics.Defaults, Generics.Collections, ARRL, - StrUtils, - SysUtils, Classes, Contnrs, PerlRegEx, pcre; + Generics.Defaults, Generics.Collections, Contest, DxStn; type TNaQpCallRec = class @@ -18,36 +16,38 @@ TNaQpCallRec = class class function compareCall(const left, right: TNaQpCallRec) : integer; static; end; -TNcjNaQp = class +TNcjNaQp = class(TContest) private NaQpCallList: TList; Comparer: IComparer; - procedure LoadHistoryFile; - public constructor Create; - function pickStation(): integer; - function getCall(id:integer): string; // returns station callsign + destructor Destroy; override; + function LoadCallHistory(const AUserCallsign : string) : boolean; override; + + function PickStation(): integer; override; + procedure DropStation(id : integer); override; + function GetCall(id : integer): string; override; // returns station callsign + procedure GetExchange(id : integer; out station : TDxStation); override; + function getExch1(id:integer): string; // returns station info (e.g. MIKE) function getExch2(id:integer): string; // returns section info (e.g. OR) function getName(id:integer): string; // returns station op name (e.g. MIKE) function getState(id:integer): string; // returns state (e.g. OR) function getUserText(id:integer): string; // returns optional UserText function FindCallRec(out recOut: TNaQpCallRec; const ACall: string): Boolean; - function GetStationInfo(const ACallsign: string) : string; + function GetStationInfo(const ACallsign: string) : string; override; end; -var - gNAQP: TNcjNaQp; - implementation uses - log; + StrUtils, SysUtils, Classes, Contnrs, PerlRegEx, pcre, + log, ARRL; -procedure TNcjNaQp.LoadHistoryFile; +function TNcjNaQp.LoadCallHistory(const AUserCallsign : string) : boolean; const DelimitChar: char = ','; var @@ -55,13 +55,18 @@ procedure TNcjNaQp.LoadHistoryFile; i: integer; rec: TNaQpCallRec; begin + // reload call history iff user's callsign has changed. + Result := not HasUserCallsignChanged(AUserCallsign); + if Result then + Exit; + slst:= TStringList.Create; tl:= TStringList.Create; tl.Delimiter := DelimitChar; tl.StrictDelimiter := True; try - NaQpCallList:= TList.Create; + NaQpCallList.Clear; slst.LoadFromFile(ParamStr(1) + 'NAQPCW.TXT'); @@ -86,6 +91,10 @@ procedure TNcjNaQp.LoadHistoryFile; end; end; + // retain user's callsign after successful load + SetUserCallsign(AUserCallsign); + Result := True; + finally slst.Free; tl.Free; @@ -96,17 +105,31 @@ procedure TNcjNaQp.LoadHistoryFile; constructor TNcjNaQp.Create; begin inherited Create; + NaQpCallList := TList.Create; Comparer := TComparer.Construct(TNaQpCallRec.compareCall); - LoadHistoryFile; end; -function TNcjNaQp.pickStation(): integer; +destructor TNcjNaQp.Destroy; +begin + FreeAndNil(NaQpCallList); + inherited; +end; + + +function TNcjNaQp.PickStation(): integer; begin result := random(NaQpCallList.Count); end; +procedure TNcjNaQp.DropStation(id : integer); +begin + assert(id < NaQpCallList.Count); + NaQpCallList.Delete(id); +end; + + function TNcjNaQp.FindCallRec(out recOut: TNaQpCallRec; const ACall: string): Boolean; var rec: TNaQpCallRec; @@ -142,7 +165,7 @@ function TNcjNaQp.GetStationInfo(const ACallsign: string) : string; dxEntity := ''; result:= ''; - if gNAQP.FindCallRec(rec, ACallsign) then + if FindCallRec(rec, ACallsign) then begin userText:= rec.UserText; @@ -163,12 +186,20 @@ function TNcjNaQp.GetStationInfo(const ACallsign: string) : string; end; -function TNcjNaQp.getCall(id:integer): string; // returns station callsign +function TNcjNaQp.GetCall(id : integer): string; // returns station callsign begin result := NaQpCallList.Items[id].Call; end; +procedure TNcjNaQp.GetExchange(id : integer; out station : TDxStation); +begin + station.Exch1 := getExch1(station.Operid); + station.OpName := station.Exch1; // TODO - refactor etOpName to use Exch1 + station.Exch2 := getExch2(station.Operid); + station.UserText := getUserText(station.Operid); +end; + function TNcjNaQp.getExch1(id:integer): string; // returns station info (e.g. MIKE) begin result := NaQpCallList.Items[id].Name; diff --git a/QrmStn.pas b/QrmStn.pas index c0d120a..bb6343a 100644 --- a/QrmStn.pas +++ b/QrmStn.pas @@ -8,7 +8,7 @@ interface uses - SysUtils, Classes, Station, RndFunc, Ini, CallLst; + Station; type TQrmStation = class(TStation) @@ -22,6 +22,7 @@ TQrmStation = class(TStation) implementation uses + SysUtils, Classes, RndFunc, Ini, CallLst, Contest; constructor TQrmStation.CreateStation; @@ -29,7 +30,7 @@ constructor TQrmStation.CreateStation; inherited Create(nil); Patience := 1 + Random(5); - MyCall := PickCall; + MyCall := Tst.PickCallOnly; HisCall := Ini.Call; Amplitude := 5000 + 25000 * Random; Pitch := Round(RndGaussLim(0, 300)); diff --git a/QrnStn.pas b/QrnStn.pas index 661d425..9bbe7e1 100644 --- a/QrnStn.pas +++ b/QrnStn.pas @@ -8,8 +8,7 @@ interface uses - SysUtils, Classes, Station, RndFunc, Ini, CallLst, - Math; + Station; type TQrnStation = class(TStation) @@ -20,6 +19,9 @@ TQrnStation = class(TStation) implementation +uses + Ini, RndFunc, Math; + constructor TQrnStation.CreateStation; var i: integer; diff --git a/Qsb.pas b/Qsb.pas index 9eddc1e..b8083b8 100644 --- a/Qsb.pas +++ b/Qsb.pas @@ -8,7 +8,7 @@ interface uses - SysUtils, QuickAvg, SndTypes, RndFunc, Math, Ini; + QuickAvg, SndTypes; type TQsb = class @@ -29,6 +29,9 @@ TQsb = class implementation +uses + SysUtils, RndFunc, Math, Ini; + constructor TQsb.Create; begin diff --git a/Station.pas b/Station.pas index bc45d64..3565a65 100644 --- a/Station.pas +++ b/Station.pas @@ -8,7 +8,7 @@ interface uses - SysUtils, Classes, Math, SndTypes, Ini, MorseKey; + Classes, SndTypes, Ini; const NEVER = MAXINT; @@ -96,7 +96,7 @@ TStation = class (TCollectionItem) implementation uses - Contest; + SysUtils, Math, MorseKey, Contest; { TExchTypes }