PROGRAM Sprint; {$G+} { This is a new version of the sprint log checking software - based on the new SS program as of September 1999. } USES Cabrillo, KeyCode, ZoneCont, Country9, Sprtsubs, Dos, Crt, FPCSlow, FPCTree; VAR Key: CHAR; OriginalTextMode: INTEGER; FUNCTION GetTitle: STRING; { Gets the title that is printed at the top of each report from the file TITLE.DAT. } VAR FileRead: TEXT; TitleString: STRING; DesiredLength: INTEGER; BEGIN IF OpenfileforRead (FileRead, 'TITLE.DAT') THEN BEGIN ReadLn (FileRead, TitleString); Close (FileRead); END ELSE BEGIN ReportError ('File TITLE.DAT is missing. This is used at top of each report.'); Halt; END; DesiredLength := Length (TitleString) DIV 2 + 37; { Leave room for call } WHILE Length (TitleString) < DesiredLength DO TitleString := ' ' + TitleString; GetTitle := TitleString; END; PROCEDURE UnstableReport; VAR InputFileName, OutputFileName, FileString: Str40; FileRead, FileWrite: TEXT; Call: CallString; DirInfo: SearchRec; SearchAddress, LogAddress, Result: INTEGER; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('UNSTABLE REPORT'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will generate a report from a list of calls showing the '); WriteLn ('details of what information was used to determine if the call was unstable.'); WriteLn; InputFileName := GetResponse ('Enter file to process : '); IF InputFileName = '' THEN Exit; IF NOT FileExists (InputFileName) THEN BEGIN ReportError (InputFileName + ' does not exist!!'); WaitForKeyPressed; Exit; END; OutputFileName := GetResponse ('Enter file to save results to : '); IF OutputFilEName = '' THEN Exit; NumberSearchCalls := 0; IF OpenFileForRead (FileRead, InputFileName) THEN BEGIN OpenFileForWrite (FileWrite, OutputFileName); WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); FileString := UpperCase (GetFirstString (FileString)); IF FileString <> '' THEN BEGIN AddCallToSearchArray (FileString); IF NumberSearchCalls = MaxSearchCalls THEN BEGIN FindFirst ('logs\*.LOG', Archive, DirInfo); { First go through the logs and collect as much data as we can } WHILE DosError = 0 DO BEGIN Call := GetCallFromFileName (DirInfo.Name); IF Call <> 'B0GUS' THEN IF LoadLogIntoTargetLog (Call) THEN IF TargetLog^.NumberQSOEntries > 0 THEN FOR LogAddress := 0 TO TargetLog^.NumberQSOEntries - 1 DO FOR SearchAddress := 0 TO NumberSearchCalls - 1 DO IF TargetLog^.QSOList [LogAddress].Call = SearchArray^ [SearchAddress].Call THEN AddSearchData (TargetLog^.QSOList [LogAddress], SearchAddress); FindNext (DirInfo); END; FOR SearchAddress := 0 TO NumberSearchCalls - 1 DO WITH SearchArray^ [SearchAddress] DO BEGIN WriteLn (FileWrite, Call, ' ', NumberResults, ' '); FOR Result := 0 TO NumberResults - 1 DO WITH SearchResults [Result] DO WriteLn (FileWrite, Hits:4, ' ', Name, ' ', QTH, ' '); WriteLn (FileWrite); WriteLn (FileWrite); END; NumberSearchCalls := 0; WHILE KeyPressed DO IF ReadKey = EscapeKey THEN BEGIN Close (FileRead); Close (FileWrite); WriteLn ('ESCAPE pressed.'); WaitForKeyPressed; Exit; END; END; END; END; Close (FileRead); END; IF NumberSearchCalls > 0 THEN BEGIN FindFirst ('logs\*.LOG', Archive, DirInfo); { First go through the logs and collect as much data as we can } WHILE DosError = 0 DO BEGIN Call := GetCallFromFileName (DirInfo.Name); IF Call <> 'B0GUS' THEN IF LoadLogIntoTargetLog (Call) THEN IF TargetLog^.NumberQSOEntries > 0 THEN FOR LogAddress := 0 TO TargetLog^.NumberQSOEntries - 1 DO FOR SearchAddress := 0 TO NumberSearchCalls - 1 DO IF TargetLog^.QSOList [LogAddress].Call = SearchArray^ [SearchAddress].Call THEN AddSearchData (TargetLog^.QSOList [LogAddress], SearchAddress); FindNext (DirInfo); END; FOR SearchAddress := 0 TO NumberSearchCalls - 1 DO WITH SearchArray^ [SearchAddress] DO BEGIN WriteLn (FileWrite, Call, ' ', NumberResults, ' '); FOR Result := 0 TO NumberResults - 1 DO WITH SearchResults [Result] DO WriteLn (FileWrite, Hits:4, ' ', Name, ' ', QTH, ' '); WriteLn (FileWrite); WriteLn (FileWrite); END; Close (FileWrite); END; END; PROCEDURE AddTeamScore (ScoreData: ScoreRecord); { Looks through the list of teams and adds this stations score to the appropriate team } VAR TeamAddress: INTEGER; BEGIN IF ScoreData.TeamName = '' THEN Exit; { No team } { See if we have started making a list yet } IF NumberOutputTeams = 0 THEN BEGIN New (TeamScores [NumberOutputTeams]); TeamScores [NumberOutputTeams]^.Name := ScoreData.TeamName; TeamScores [NumberOutputTeams]^.Calls [0] := ScoreData.Call; TeamScores [NumberOutputTeams]^.Scores [0] := ScoreData.FinalScore; TeamScores [NumberOutputTeams]^.TotalScore := ScoreData.FinalScore; TeamScores [NumberOutputTeams]^.NumberEntries := 1; Inc (NumberOutputTeams); Exit; END; { We have some entries already - see if we can find the team we are looking for } FOR TeamAddress := 0 TO NumberOutputTeams - 1 DO IF TeamScores [TeamAddress]^.Name = ScoreData.TeamName THEN WITH TeamScores [TeamAddress]^ DO IF NumberEntries < 5 THEN BEGIN Calls [NumberEntries] := ScoreData.Call; Scores [NumberEntries] := ScoreData.FinalScore; TotalScore := TotalScore + ScoreData.FinalScore; Inc (NumberEntries); Exit; END ELSE BEGIN ReportError ('Trying to add 6th member to team ' + ScoreData.TeamName + '!'); Halt; END; WriteLn ('Adding new team = ', ScoreData.TeamName); New (TeamScores [NumberOutputTeams]); TeamScores [NumberOutputTeams]^.Name := ScoreData.TeamName; TeamScores [NumberOutputTeams]^.Calls [0] := ScoreData.Call; TeamScores [NumberOutputTeams]^.Scores [0] := ScoreData.FinalScore; TeamScores [NumberOutputTeams]^.TotalScore := ScoreData.FinalScore; TeamScores [NumberOutputTeams]^.NumberEntries := 1; Inc (NumberOutputTeams); END; PROCEDURE ProcessTeamScores (FileName: Str40); VAR Key: CHAR; Team, Entry: INTEGER; TempString: Str160; SwapMade: BOOLEAN; TempCall: CallString; TempScore: INTEGER; TempTeamScore: TeamScoreType; NumberString: Str20; BEGIN IF NumberOutputTeams = 0 THEN Exit; SwapMade := True; WHILE SwapMade DO BEGIN SwapMade := False; FOR Team := 0 TO NumberOutputTeams - 2 DO IF TeamScores [Team]^.TotalScore < TeamScores [Team + 1]^.TotalScore THEN BEGIN TempTeamScore := TeamScores [Team]^; TeamScores [Team]^ := TeamScores [Team + 1]^; TeamScores [Team + 1]^ := TempTeamScore; SwapMade := True; END; END; IF NOT OpenFileForAppend (FileWrite, FileName) THEN Exit; WriteLn (FileWrite); WriteLn (FileWrite); WriteLn (FileWrite, 'TEAM SCORES'); WriteLn (FileWrite); FOR Team := 0 TO NumberOutputTeams - 1 DO IF Team < 4 THEN BEGIN WriteLn (FileWrite, TeamScores [Team]^.Name); WriteLn (FileWrite); WITH TeamScores [Team]^ DO REPEAT SwapMade := False; FOR Entry := 0 TO NumberEntries - 2 DO IF Scores [Entry] < Scores [Entry + 1] THEN BEGIN SwapMade := True; TempCall := Calls [Entry]; TempScore := Scores [Entry]; Calls [Entry] := Calls [Entry + 1]; Scores [Entry] := Scores [Entry + 1]; Calls [Entry + 1] := TempCall; Scores [Entry + 1] := TempScore; END; UNTIL NOT SwapMade; WITH TeamScores [Team]^ DO FOR Entry := 0 TO NumberEntries - 1 DO BEGIN TempString := Calls [Entry] + TabKey; Str (Scores [Entry], NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TempString, NumberString, TabKey); END; Str (TeamScores [Team]^.TotalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TabKey, NumberString); WriteLn (FileWrite); END ELSE BEGIN Str (Team + 1, TempString); TempString := TempString + '. ' + TeamScores [Team]^.Name + ' ('; WITH TeamScores [Team]^ DO REPEAT SwapMade := False; FOR Entry := 0 TO NumberEntries - 2 DO IF Scores [Entry] < Scores [Entry + 1] THEN BEGIN SwapMade := True; TempCall := Calls [Entry]; TempScore := Scores [Entry]; Calls [Entry] := Calls [Entry + 1]; Scores [Entry] := Scores [Entry + 1]; Calls [Entry + 1] := TempCall; Scores [Entry + 1] := TempScore; END; UNTIL NOT SwapMade; WITH TeamScores [Team]^ DO FOR Entry := 0 TO NumberEntries - 1 DO BEGIN TempString := TempString + Calls [Entry]; IF Entry < NumberEntries - 1 THEN TempString := TempString + ', ' ELSE TempString := TempString + ') '; END; TempString := TempString + TabKey; Write (FileWrite, TempString); Str (TeamScores [Team]^.TotalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, NumberString); END; Close (FileWrite); FOR Team := 0 TO NumberOutputTeams - 1 DO Dispose (TeamScores [Team]); NumberOutputTeams := 0; END; PROCEDURE GlobalLogProcess; { This is where some magic is } VAR DirInfo: SearchRec; Key: CHAR; MinimumHits: INTEGER; Call: CallString; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('LOOK THROUGH ALL LOG FOR NEW DATABASE CALLS'); TextColor (Cyan); WriteLn; WriteLn ('This procedure will look through all of the sprint logs and add any new'); WriteLn ('callsigns to the database that meet the stability criteria.'); WriteLn; REPEAT Key := UpCase (GetKey ('Okay to proceed? (Y/N) : ')); IF (Key = EscapeKey) OR (Key = 'N') THEN Exit; UNTIL Key = 'Y'; WriteLn; LoadInSprintDatabaseFile; TextColor (Cyan); WriteLn; WriteLn ('You will now need to decide how many logs a call must be found in before I'); WriteLn ('will consier adding it to the database. Typically, you would pick a higher'); WriteLn ('number at the start of your process, then after you have detected most of the'); WriteLn ('bad calls that occur multiple times, you can get more aggressive with lower'); WriteLn ('numbers. Also, be aware that each log can contribute 3 QSOs, one for each'); WriteLn ('band. This means if someone screws up once and replicates the error on two'); WriteLn ('other bands - a single error can produce three entries.'); WriteLn; MinimumHits := GetValue ('Minimum number of found QSOs to add calls to database : '); NumberSearchCalls := 0; FindFirst ('logs\*.log', Archive, DirInfo); IF DosError <> 0 THEN BEGIN ReportError ('No .LOG files found to process.'); WaitForKeyPressed; Exit; END; WHILE DosError = 0 DO BEGIN Call := GetCallFromFileName (DirInfo.Name); AddUnknownCallsFromThisLogToSearchArray (Call, MinimumHits); FindNext (DirInfo); END; ProcessAnyRemainingCallsLeftOverInSearchArray (MinimumHits); WaitForKeyPressed; END; PROCEDURE ReportCard; VAR Key: CHAR; Call: CallString; FileString: STRING; LookForNewDatabaseCalls: BOOLEAN; FileRead, FileWrite: TEXT; Address: INTEGER; FilePrefix: Str20; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('GENERATE REPORT CARD FOR A LOG'); TextColor (Cyan); WriteLn; LoadInSprintDatabaseFile; REPEAT Call := UpperCase (GetResponse ('Call of log for report card (none to exit) : ')); IF Call = '' THEN Exit; FilePrefix := GetFilenamePrefixForCall (Call); Writeln ('File prefix = ', FilePrefix); IF FileExists ( 'logs\' + FilePrefix + '.log') THEN BEGIN IF OpenFileForWrite (FileWrite, 'rpt\' + FilePrefix + '.rpt') THEN BEGIN WriteLn (FileWrite, GetTitle, ' ', Call); WriteLn (FileWrite); IF OpenFileForRead (FileRead, 'INTRO.DAT') THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); WriteLn (FileWrite, FileString); END; Close (FileRead); END; Close (FileWrite); END; IF LoadLogIntoActiveLog (Call) THEN IF ActiveLog^.NumberQSOEntries > 0 THEN BEGIN FilePrefix := GetFilenamePrefixForCall (Call); FOR Address := 0 TO ActiveLog^.NumberQSOEntries - 1 DO ActiveLog^.QSOList [Address].QSOStatus := GoodQSOStatus; WriteLn ('There were ', ActiveLog^.NumberQSOEntries, ' calls found.'); PrintOutUnknownAndPossibleCalls ( 'RPT\' + FilePrefix + '.RPT'); CheckExchanges ( 'RPT\' + FilePrefix + '.RPT'); CrossCheck (Call, 'RPT\' + FilePrefix + '.RPT'); CheckForDupes ( 'RPT\' + FilePrefix + '.RPT'); MultiplierCalculation (Call, 'RPT\' + FilePrefix + '.RPT'); END ELSE BEGIN ReportError ('There were no QSOs found!'); IF OpenFileForAppend (FileWrite, SprintLogFileName) THEN BEGIN WriteLn (FileWrite, 'No QSOs found in ' + Call); Close (Filewrite); END; END; END ELSE BEGIN ReportError ('No log found for ' + Call + '!!'); IF OpenFileForAppend (FileWrite, SprintLogFileName) THEN BEGIN WriteLn (FileWrite, 'No log found for ' + Call); Close (Filewrite); END; END; UNTIL False; END; PROCEDURE SpecialReportCard; VAR Key: CHAR; Call: CallString; FileString: STRING; LookForNewDatabaseCalls: BOOLEAN; FileRead, FileWrite: TEXT; Address: INTEGER; TimeString, FilePrefix: Str20; xResult, NumberOfMinutes: INTEGER; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('REPORT CARD UP TO CERTAIN TIME'); WriteLn ('This will generate a report card for the specified log - up to a certain point'); WriteLn ('in the contest.'); TextColor (Cyan); WriteLn; REPEAT TimeString := GetResponse ('Enter number of minutes to count (none to abort) : '); IF TimeString = '' THEN Exit; Val (TimeString, NumberOfMinutes, xResult); UNTIL (NumberOfMinutes > 0 ) AND (NumberOfMinutes <= 240); LoadInSprintDatabaseFile; REPEAT Call := UpperCase (GetResponse ('Call of log for report card (none to exit) : ')); IF Call = '' THEN Exit; FilePrefix := GetFilenamePrefixForCall (Call); Writeln ('File prefix = ', FilePrefix); IF FileExists ( 'logs\' + FilePrefix + '.log') THEN BEGIN IF OpenFileForWrite (FileWrite, 'rpt\' + FilePrefix + '.rpt') THEN BEGIN WriteLn (FileWrite, GetTitle, ' ', Call); WriteLn (FileWrite); IF OpenFileForRead (FileRead, 'INTRO.DAT') THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); WriteLn (FileWrite, FileString); END; Close (FileRead); END; Close (FileWrite); END; IF LoadPartialLogIntoActiveLog (Call, NumberOfMinutes) THEN IF ActiveLog^.NumberQSOEntries > 0 THEN BEGIN FilePrefix := GetFilenamePrefixForCall (Call); FOR Address := 0 TO ActiveLog^.NumberQSOEntries - 1 DO ActiveLog^.QSOList [Address].QSOStatus := GoodQSOStatus; WriteLn ('There were ', ActiveLog^.NumberQSOEntries, ' calls found.'); PrintOutUnknownAndPossibleCalls ( 'RPT\' + FilePrefix + '.RPT'); CheckExchanges ( 'RPT\' + FilePrefix + '.RPT'); CrossCheck (Call, 'RPT\' + FilePrefix + '.RPT'); CheckForDupes ( 'RPT\' + FilePrefix + '.RPT'); MultiplierCalculation (Call, 'RPT\' + FilePrefix + '.RPT'); END ELSE BEGIN ReportError ('There were no QSOs found!'); IF OpenFileForAppend (FileWrite, SprintLogFileName) THEN BEGIN WriteLn (FileWrite, 'No QSOs found in ' + Call); Close (Filewrite); END; END; END ELSE BEGIN ReportError ('No log found for ' + Call + '!!'); IF OpenFileForAppend (FileWrite, SprintLogFileName) THEN BEGIN WriteLn (FileWrite, 'No log found for ' + Call); Close (Filewrite); END; END; UNTIL False; END; PROCEDURE LookForHighFrequencyBustedCalls; VAR FileName, TempString, InputFileName, OutputFileName: Str40; FileString: STRING; DirInfo: SearchRec; FileRead, FileWrite: TEXT; ExchangeCheckFound, CrossFound, Change, DoingPercent: BOOLEAN; NumberFilesRead, EstimatedQSOs, Address, Range: INTEGER; TempBustedCallData: BustedCallRecord; Call: CallString; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('LOOK FOR CALLS THAT ARE BUSTED MANY TIMES'); TextColor (Cyan); WriteLn ('This routine will examine the .RPT files that you have generated and make a'); WriteLn ('file that shows callsigns that were busted multiple times. The file will be'); WriteLn ('sorted so the calls that are busted the most appear at the start of the file.'); WriteLn ('The number of times they appear in the RPT files will also be shown.'); WriteLn; WriteLn ('This is a great routine to run after you have generated some, or all, of the'); WriteLn ('report files for the contest.'); WriteLn; OutputFileName := GetResponse ('Name of file to save results to (none to exit) : '); IF OutputFileName = '' THEN Exit; InputFileName := GetResponse ('Estimated QSO total file (if any - from E command) : '); IF InputfileName <> '' THEN WriteLn ('Your totals will be shown in percent.'); WriteLn ('Collecting data from .RPT files...'); FindFirst ('rpt\*.rpt', Archive, DirInfo); New (BustedCallList); NumberBustedCalls := 0; DoingPercent := False; NumberFilesRead := 0; WHILE DosError = 0 DO BEGIN FileName := DirInfo.Name; ExchangeCheckFound := False; CrossFound := False; IF OpenFileForRead (FileRead, 'rpt\' + FileName) THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); GetRidOfPrecedingSpaces (FileString); IF FileString <> '' THEN BEGIN IF NOT ExchangeCheckFound THEN BEGIN IF StringHas (FileString, 'busted call') THEN BEGIN RemoveFirstString (FileString); { 'QSO' } RemoveFirstString (FileString); { '#xxx' } AddBustedCall (RemoveFirstString (FileString), BustedCall); END; IF StringHas (FileString, 'EXCHANGE CHECK') THEN BEGIN ExchangeCheckFound := True; Inc (NumberFilesRead); END; END ELSE IF NOT CrossFound THEN BEGIN IF StringHas (FileString, 'QSO #') THEN BEGIN RemoveFirstString (FileString); { 'QSO' } RemoveFirstString (FileString); { '#xxx' } AddBustedCall (RemoveFirstString (FileString), BustedExchange); Continue; END; IF StringHas (FileString, 'CROSS CHECK') THEN CrossFound := True; END ELSE BEGIN IF StringHas (FileString, 'should be') THEN AddBustedCall (RemoveLastString (FileString), BustedNumber); IF StringHas (FileString, 'not found') THEN BEGIN TempString := RemoveLastString (FileString); IF TempString = 'mismatched.' THEN BEGIN RemoveLastString (FileString); RemoveLastString (FileString); TempString := RemoveLastString (FileString); END; AddBustedCall (TempString, NotInLog); END; END; END; END; Close (FileRead); END; FindNext (DirInfo); END; { If we have an estimated QSO total file to work with, convert the totals to percentages. } IF InputFileName <> '' THEN IF OpenFileForRead (FileRead, InputFileName) THEN BEGIN DoingPercent := True; WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); Call := RemoveFirstString (FileString); EstimatedQSOs := RemoveFirstLongInteger (FileString); FOR Address := 0 TO NumberBustedCalls - 1 DO IF BustedCallList^ [Address].Call = Call THEN WITH BustedCallList^ [Address] DO BEGIN NumberBustedCalls := Round ((NumberBustedCalls / EstimatedQSOs) * 100); NumberBustedExchanges := Round ((NumberBustedExchanges / EstimatedQSOs) * 100); NumberBustedNumbers := Round ((NumberBustedNumbers / EstimatedQSOs) * 100); NumberNotInLogs := Round ((NumberNotInLogs / EstimatedQSOs) * 100); TotalBusts := Round ((TotalBusts / EstimatedQSOs) * 100); EstQSOs := EstimatedQSOs; Break; END; END; Close (FileRead); END; { We have the data in BustedCallList^ - now sort it by # hits } WriteLn ('Sorting data...'); IF NumberBustedCalls > 1 THEN BEGIN Range := NumberBustedCalls - 2; REPEAT Change := False; FOR Address := 0 TO Range DO BEGIN IF BustedCallList^ [Address].TotalBusts < BustedCallList^ [Address + 1].TotalBusts THEN BEGIN TempBustedCallData := BustedCallList^ [Address]; BustedCallList^ [Address] := BustedCallList^ [Address + 1]; BustedCallList^ [Address + 1] := TempBustedCallData; Change := True; END; END; Dec (Range); UNTIL (NOT Change) OR (Range < 1); END; { Now spit out results to the output file } IF NumberBustedCalls > 0 THEN BEGIN OpenFileForWrite (FileWrite, OutputFileName); WriteLn (FileWrite, 'Number of report files looked at = ', NumberFilesRead); IF DoingPercent THEN WriteLn (FileWrite, 'Totals are shown in percent of estimated QSO total.') ELSE WriteLn (FileWrite, 'Totals are actual total counts (not in percent of estimated QSOs).'); WriteLn (FileWrite); WriteLn (FileWrite, 'CALLSIGN TOTAL CALL NAME/QTH QSO# NILs'); FOR Address := 0 TO NumberBustedCalls - 1 DO BEGIN TempString := BustedCallList^ [Address].Call; WHILE Length (TempString) < 12 DO TempString := TempString + ' '; WITH BustedCallList^ [Address] DO WriteLn (FileWrite, TempString, TotalBusts:8, NumberBustedCalls:8, NumberBustedExchanges:8, NumberBustedNumbers:8, NumberNotInLogs:8); END; Close (FileWrite); END; Dispose (BustedCallList); END; PROCEDURE AppendBustedInfoToReportFile (Call: CallString); VAR DirInfo: SearchRec; ReportCall: CallString; FileRead, FileWrite: TEXT; FileString: STRING; NumberBustedQSOsFound, NumberQSOsInLog: INTEGER; Percent: REAL; BEGIN OpenFileForAppend (FileWrite, 'RPT\' + Call + '.RPT'); WriteLn (FileWrite); WriteLn (FileWrite, 'BUSTS FOUND IN OTHER LOGS'); WriteLn (FileWrite, '-------------------------'); WriteLn (FileWrite, 'The following information shows contacts you made that were removed from the'); WriteLn (FileWrite, 'other station''s log. These are not deducted from your score. They are listed'); WriteLn (FileWrite, 'for your information only.'); WriteLn (FileWrite); NumberBustedQSOsFound := 0; NumberQSOsInLog := GetTotalQSOsFromReportFile ('RPT\' + Call + '.RPT'); FindFirst ('RPT\*.rpt', Archive, DirInfo); WHILE DosError = 0 DO BEGIN ReportCall := GetCallFromFileName (DirInfo.Name); IF ReportCall <> Call THEN IF OpenFileForRead (FileRead, 'rpt\' + ReportCall + '.rpt') THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); IF StringHas (FileString, Call + ' is a dupe') THEN BEGIN WriteLn (FileWrite, ReportCall + ': ' + FileString); Inc (NumberBustedQSOsFound); Continue; END; IF StringHas (FileString, 'correct') THEN IF StringHas (FileString, Call + '.') THEN BEGIN WriteLn (FileWrite, ReportCall + ': ' + FileString); Inc (NumberBustedQSOsFound); Continue; END; IF StringHas (FileString, ' ' + Call + ' ') THEN IF NOT StringHas (FileString, 'Calls with same ') THEN BEGIN WriteLn (FileWrite, ReportCall + ': ' + FileString); Inc (NumberBustedQSOsFound); Continue; END; IF GetLastString (FileString) = Call THEN IF StringHas (FileString, 'QSO #') THEN BEGIN WriteLn (FileWrite, ReportCall + ': ' + FileString); Inc (NumberBustedQSOsFound); Continue; END; END; Close (FileRead); END; FindNext (DirInfo); END; WriteLn (FileWrite); Write (FileWrite, 'Number busts found in other logs = ', NumberBustedQSOsFound, ' ('); IF NumberQSOsInLog > 0 THEN BEGIN Percent := NumberBustedQSOsFound; Percent := (Percent / NumberQSOsInLog) * 100; END ELSE Percent := 0.0; WriteLn (FileWrite, Percent:3:1, '%)'); Close (FileWrite); END; PROCEDURE CheckAllLogs; VAR Key: CHAR; LogAddress, Address: INTEGER; FilePrefix: Str20; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('CHECK ALL SPRINT LOGS'); TextColor (Cyan); WriteLn; WriteLn ('This procedure will check all the SS logs found in the active directory and'); WriteLn ('generate report files for each of them. It will sort them in order of the'); WriteLn ('number of QSOs found in the log, doing the big logs first.'); WriteLn; WriteLn ('This can take a very long time. Pressing the escape key will stop the'); WriteLn ('process after the completion of the current log.'); WriteLn; REPEAT Key := UpCase (GetKey ('Okay to proceed? (Y/N) : ')); IF (Key = 'N') OR (Key = EscapeKey) THEN Exit; UNTIL Key = 'Y'; WriteLn; REPEAT Key := UpCase (GetKey ('Stop if I find a cross check bust that is strange? (Y/N) : ')); IF Key = EscapeKey THEN Exit; UNTIL (Key = 'Y') OR (Key = 'N'); WriteLn; StopOnStrangeCrossCheckBust := Key = 'Y'; CreateListOfAvailableLogs; LoadInSprintDataBaseFile; FOR Address := 0 TO NumberAvailableLogs - 1 DO WITH AvailableLogs^ [Address] DO BEGIN WriteLn ('Processing ', Call, ' with ', QSOs, ' QSOs...'); FilePrefix := GetFilenamePrefixForCall (Call); IF OpenFileForWrite (FileWrite, 'rpt\' + FilePrefix + '.RPT') THEN BEGIN WriteLn (FileWrite, GetTitle, ' ', Call); WriteLn (FileWrite); Close (FileWrite); END; IF LoadLogIntoActiveLog (Call) THEN BEGIN IF ActiveLog^.NumberQSOEntries > 0 THEN BEGIN FOR LogAddress := 0 TO ActiveLog^.NumberQSOEntries - 1 DO ActiveLog^.QSOList [LogAddress].QSOStatus := GoodQSOStatus; PrintOutUnknownAndPossibleCalls ( 'rpt\' + FilePrefix + '.RPT'); CheckExchanges ( 'rpt\' + FilePrefix + '.RPT'); CrossCheck (Call, 'rpt\' + FilePrefix + '.RPT'); CheckForDupes ( 'rpt\' + FilePrefix + '.RPT'); MultiplierCalculation (Call, 'rpt\' + FilePrefix + '.RPT'); END ELSE BEGIN ReportError ('WARNING!!! This log has no QSOs in it.'); WaitForKeyPressed; END; END ELSE BEGIN ReportError ('WARNING!! This log has no QSOs in it.'); WaitForKeyPressed; END; WHILE KeyPressed DO BEGIN Key := ReadKey; IF Key = EscapeKey THEN BEGIN ReportError ('Aborted by operator'); WaitForKeyPressed; Exit; END; END; END; END; FUNCTION SeenThisBefore (BustString: Str80): BOOLEAN; VAR FileRead: TEXT; FileString: Str80; Address, NumberEntries: INTEGER; BEGIN IF OpenFileForRead (FileRead, 'SEENB4.DAT') THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); IF FileString = BustString THEN BEGIN SeenThisBefore := True; Close (FileRead); Exit; END; END; Close (FileRead); END; IF OpenFileForAppend (FileRead, 'SEENB4.DAT') THEN BEGIN WriteLn (FileRead, BustString); Close (FileRead); END; SeenThisBefore := False; END; PROCEDURE BustCalls; VAR TempString: Str80; FileName, OutputFileName: Str40; ReportCall, TempCall, PotentialBadCall, GoodCall, CorrectCall: CallString; PotentialGoodCalls: ARRAY [0..20] OF CallString; ShowListsWithoutPossibleCalls: BOOLEAN; FileString: STRING; DirInfo: SearchRec; Key: CHAR; FileRead: TEXT; Result, ReceivedNumber, NumberPotentialGoodCalls, NumberFilesRead: INTEGER; Address, Range, NumberSimilarCalls, SimilarCallAddress: INTEGER; AddSimilarCalls, ExchangeCheckFound, CrossFound: BOOLEAN; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('BUST CALLSIGNS'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will look at the various .RPT files for calls that might be'); WriteLn ('incorrect and allow you to insert them into the BADCALLS.DAT file,'); WriteLn; LoadInBadCallArray; { Make sure we have the latest copy of the bad call array } WriteLn ('There are currently ', NumberBadCalls, ' bad calls loaded in ', BadCallFileName); REPEAT Key := UpCase (GetKey ('Add singular similar call automatically? (Y/N or ESCAPE to abort) : ')); IF Key = EscapeKey THEN Exit; UNTIL (Key = 'Y') OR (Key = 'N'); WriteLn; AddSimilarCalls := Key = 'Y'; REPEAT Key := UpCase (GetKey ('Do you want to see lists of calls that don''t have a similar call? (Y/N) : ')); IF Key = EscapeKey THEN Exit; UNTIL (Key = 'Y') OR (Key = 'N'); WriteLn; ShowListsWithoutPossibleCalls := Key = 'Y'; FindFirst ( 'rpt\*.rpt', Archive, DirInfo); NumberFilesRead := 0; WHILE DosError = 0 DO BEGIN FileName := DirInfo.Name; ReportCall := GetCallFromFileName (DirInfo.Name); ExchangeCheckFound := False; CrossFound := False; IF OpenFileForRead (FileRead, 'rpt\' + FileName) THEN BEGIN TextColor (Red); WriteLn (ReportCall); WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); GetRidOfPrecedingSpaces (FileString); IF FileString <> '' THEN IF StringHas (FileString, 'is a unique call') THEN BEGIN PotentialBadCall := GetFirstString (FileString); IF PotentialBadCall = 'QSO' THEN BEGIN RemoveFirstString (FileString); { QSO } RemoveFirstString (FileString); { # } PotentialBadCall := GetFirstString (FileString); END; IF StringIsAllNumbers (PotentialBadCall) THEN BEGIN RemoveFirstString (FileString); { xxx } PotentialBadCall := GetFirstString (FileString); END; IF CallInBadList (PotentialBadCall) THEN Continue; { Get rid of . at end } Delete (FileString, Length (FileString), 1); Val (GetLastString (FileString), ReceivedNumber, Result); NumberPotentialGoodCalls := 0; ReadLn (FileRead, FileString); GetRidOfPrecedingSpaces (FileString); FileString := PostcedingString (FileString, ': '); WHILE (FileString <> '') AND (NumberPotentialGoodCalls < 20) DO BEGIN PotentialGoodCalls [NumberPotentialGoodCalls] := RemoveFirstString (FileString); Inc (NumberPotentialGoodCalls); END; IF NumberPotentialGoodCalls > 0 THEN BEGIN NumberSimilarCalls := 0; FOR Address := 0 TO NumberPotentialGoodCalls - 1 DO IF SimilarCall (PotentialBadCall, PotentialGoodCalls [Address]) THEN BEGIN Inc (NumberSimilarCalls); SimilarCallAddress := Address; END; { Make possible call first one shown } IF (NumberSimilarCalls = 1) AND (SimilarCallAddress > 0) THEN BEGIN TempCall := PotentialGoodCalls [0]; PotentialGoodCalls [0] := PotentialGoodCalls [SimilarCallAddress]; PotentialGoodCalls [SimilarCallAddress] := TempCall; SimilarCallAddress := 0; END; IF (NumberSimilarCalls = 1) AND AddSimilarCalls THEN IF AddBadCall (PotentialBadCall, PotentialGoodcalls [SimilarCallAddress]) THEN BEGIN TextColor (Green); WriteLn (PotentialBadCall, ' ', PotentialGoodCalls [SimilarCallAddress], ' added automagically. BadCallTotal = ', NumberBadCalls); Continue; END; END; IF ShowListsWithoutPossibleCalls OR (NumberSimilarCalls > 0) THEN IF NumberPotentialGoodCalls > 0 THEN BEGIN TextColor (Cyan); Str (ReceivedNumber, TempString); TempString := PotentialBadCall + ' (' + TempString + ') '; FOR Address := 0 TO NumberPotentialGoodCalls - 1 DO TempString := TempString + ' ' + PotentialGoodCalls [Address]; IF NOT SeenThisBefore (TempString) THEN BEGIN WriteLn (TempString); GoodCall := UpperCase (GetResponse ('Enter correct call (or none) : ')); IF GoodCall <> '' THEN BEGIN IF GoodCall = ' ' THEN AddBadCall (PotentialBadCall, PotentialGoodCalls [0]) ELSE AddBadCall (PotentialBadCall, GoodCall); END; END; END; END; END; Close (FileRead); END; FindNext (DirInfo); END; WriteLn ('Saving bad calls to ', BadCallFilename, ' Total Bad Calls = ', NumberBadCalls); SaveBadCallArray; WaitForKeyPressed; END; FUNCTION GetBoxNumber (ScoreData: ScoreRecord): INTEGER; BEGIN ScoreData.QTH := UpperCase (ScoreData.QTH); IF (ScoreData.QTH = 'CO') OR (ScoreData.QTH = 'IA') OR (ScoreData.QTH = 'KS') OR (ScoreData.QTH = 'MN') OR (ScoreData.QTH = 'MO') OR (ScoreData.QTH = 'NE') OR (ScoreData.QTH = 'ND') OR (ScoreData.QTH = 'SD') THEN BEGIN GetBoxNumber := 10; Exit; END; IF (ScoreData.QTH = 'CT') OR (ScoreData.QTH = 'MA') OR (ScoreData.QTH = 'ME') OR (ScoreData.QTH = 'RI') OR (ScoreData.QTH = 'VT') OR (ScoreData.QTH = 'NH') THEN BEGIN GetBoxNumber := 1; Exit; END; IF (ScoreData.QTH = 'NJ') OR (ScoreData.QTH = 'NY') THEN BEGIN GetBoxNumber := 2; Exit; END; IF (ScoreData.QTH = 'DE') OR (ScoreData.QTH = 'MD') OR (ScoreData.QTH = 'PA') OR (ScoreData.QTH = 'DC') THEN BEGIN GetBoxNumber := 3; Exit; END; IF (ScoreData.QTH = 'AL') OR (ScoreData.QTH = 'FL') OR (ScoreData.QTH = 'GA') OR (ScoreData.QTH = 'KY') OR (ScoreData.QTH = 'NC') OR (ScoreData.QTH = 'SC') OR (ScoreData.QTH = 'TN') OR (ScoreData.QTH = 'VA') THEN BEGIN GetBoxNumber := 4; Exit; END; IF (ScoreData.QTH = 'AR') OR (ScoreData.QTH = 'LA') OR (ScoreData.QTH = 'MS') OR (ScoreData.QTH = 'NM') OR (ScoreData.QTH = 'OK') OR (ScoreData.QTH = 'TX') THEN BEGIN GetBoxNumber := 5; Exit; END; IF (ScoreData.QTH = 'CA') OR (ScoreData.QTH = 'HI') THEN BEGIN GetBoxNumber := 6; Exit; END; IF (ScoreData.QTH = 'AK') OR (ScoreData.QTH = 'AZ') OR (ScoreData.QTH = 'ID') OR (ScoreData.QTH = 'MT') OR (ScoreData.QTH = 'NV') OR (ScoreData.QTH = 'OR') OR (ScoreData.QTH = 'UT') OR (ScoreData.QTH = 'WA') OR (ScoreData.QTH = 'WY') THEN BEGIN GetBoxNumber := 7; Exit; END; IF (ScoreData.QTH = 'MI') OR (ScoreData.QTH = 'OH') OR (ScoreData.QTH = 'WV') THEN BEGIN GetBoxNumber := 8; Exit; END; IF (ScoreData.QTH = 'IL') OR (ScoreData.QTH = 'IN') OR (ScoreData.QTH = 'WI') THEN BEGIN GetBoxNumber := 9; Exit; END; IF Copy (ScoreData.QTH, 1, 2) = 'VE' THEN GetBoxNumber := 11 ELSE GetBoxNumber := 12; END; PROCEDURE LoadInTeams; VAR FileRead: TEXT; FileString: STRING; Position: INTEGER; BEGIN TeamList.NumberTeams := 0; IF OpenFileForRead (FileRead, TeamFileName) THEN BEGIN REPEAT ReadLn (FileRead, FileString); GetRidOfPostcedingSpaces (FileString); UNTIL (Eof (FileRead) OR (FileString <> '')); WHILE NOT Eof (FileRead) DO BEGIN IF StringHas (FileString, 'NAME = ') THEN BEGIN TeamList.Teams [TeamList.NumberTeams].Name := PostcedingString (FileString, ' = '); WriteLn (TeamList.NumberTeams,' ', TeamList.Teams [TeamList.NumberTeams].Name); ReadLn (FileRead, FileString); IF StringHas (FileString, 'TIMESTAMP') THEN ReadLn (FileRead, FileString); IF StringHas (FileString, 'OWNER') THEN ReadLn (FileRead, FileString); { FileString now has the first call in it } WITH TeamList.Teams [TeamList.NumberTeams] DO BEGIN NumberMembers := 0; REPEAT GetRidOfPostcedingSpaces (FileString); IF StringHas (FileString, '(') THEN BEGIN Position := Pos ('(', FileString); Insert (' ', FileString, Position); END; FileString := GetFirstString (FileString); IF FileString <> '' THEN BEGIN MemberList [NumberMembers] := RootCall (FileString); Inc (NumberMembers); END; IF Eof (FileRead) THEN BEGIN Inc (TeamList.NumberTeams); WriteLn ('There were ', TeamList.NumberTeams, ' teams loaded in'); Close (FileRead); WaitForKeyPressed; Exit; END; ReadLn (FileRead, FileString); UNTIL (StringHas (FileString, 'NAME = ')); END; Inc (TeamList.NumberTeams); END; END; WriteLn ('There were ', TeamList.NumberTeams, ' teams loaded in'); Close (FileRead); WaitForKeyPressed; END; END; FUNCTION GetTeam (Call: CallString): Str80; VAR TeamAddress, MemberAddress: INTEGER; BEGIN Call := RootCall (Call); IF TeamList.NumberTeams > 0 THEN FOR TeamAddress := 0 TO TeamList.NumberTeams - 1 DO WITH TeamList.Teams [TeamAddress] DO BEGIN IF NumberMembers > 0 THEN FOR MemberAddress := 0 TO NumberMembers - 1 DO IF MemberList [MemberAddress] = Call THEN BEGIN GetTeam := TeamList.Teams [TeamAddress].Name; Exit; END; END; GetTeam := ''; END; FUNCTION GetNumberOfBandChanges (Call: CallString): INTEGER; VAR LastBand: BandType; Address: INTEGER; NumberChanges: INTEGER; BEGIN IF NOT LoadLogIntoActiveLog (Call) THEN Exit; NumberChanges := 0; LastBand := ActiveLog^.QSOList [0].Band; FOR Address := 0 TO ActiveLog^.NumberQSOEntries - 1 DO WITH ActiveLog^.QSOList [Address] DO IF LastBand <> Band THEN BEGIN LastBand := Band; Inc (NumberChanges); END; GetNumberOfBandChanges := NumberChanges; END; PROCEDURE BandChangesForALog; VAR Callsign: Callstring; BEGIN REPEAT Callsign := GetResponse ('Enter callsign to get number of band changes for (none to exit) : '); IF Callsign = '' THEN Exit; WriteLn (GetNumberOfBandChanges (Callsign)); UNTIL False; END; PROCEDURE FixQTH (Var QTH: STRING); { Used to put the QTH in the desired format for the writeup } BEGIN QTH := UpperCase (QTH); IF QTH = 'VE1' THEN QTH := 'MAR'; IF QTH = 'VE2' THEN QTH := 'PQ'; IF QTH = 'VE3' THEN QTH := 'ON'; IF QTH = 'VE4' THEN QTH := 'MB'; IF QTH = 'VE5' THEN QTH := 'SK'; IF QTH = 'VE6' THEN QTH := 'AB'; IF QTH = 'VE7' THEN QTH := 'BC'; IF QTH = 'VE8' THEN QTH := 'NWT'; END; PROCEDURE CollectScoreInformation; VAR ReportCall: Callstring; ShiftEntry, TopTenEntry, LastBoxPrinted, Result, Range, Address: INTEGER; Entry: INTEGER; NumberString, OutputFilename, FileName: Str40; TempString: Str80; DirInfo: SearchRec; FileRead, FileWrite: TEXT; FileString: STRING; TempScoreData: ScoreRecord; Change: BOOLEAN; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('COLLECT SCORE INFORMATION'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will collect the score information from the .RPT files and'); WriteLn ('save it into a tab delimited file that can be exported into Excel.'); WriteLn; OutputFilename := 'SCORES.TXT'; LoadInSprintDataBaseFile; LoadInTeams; NumberOutputTeams := 0; { Used for the list of output teams } New (ScoreArray); NumberScoreEntries := 0; FOR Address := 1 TO 15 DO BEGIN TopTenScores [Address].FinalScore := 0; TopTenScores [Address].Call := ''; TopTenOneRadioHP [Address].FinalScore := 0; TopTenOneRadioHP [Address].Call := ''; TopTenOneRadioLP [Address].FinalScore := 0; TopTenOneRadioLP [Address].Call := ''; TopTenQSOs [Address].NumberFinalQSOs := 0; TopTenQSOs [Address].Call := ''; TopTenMults [Address].NumberMultipliers := 0; TopTenMults [Address].Call := ''; TopTenLowPower [Address].FinalScore := 0; TopTenLowPower [Address].Call := ''; TopTenQRP [Address].FinalScore := 0; TopTenQRP [Address].Call := ''; TopTenBandChanges [Address].BandChanges := 0; TopTenBandChanges [Address].Call := ''; TopTenGoldenLogs [Address].NumberFinalQSOs := 0; TopTenGoldenLogs [Address].Call := ''; END; FOR Address := 16 TO 30 DO BEGIN TopTenGoldenLogs [Address].NumberFinalQSOs := 0; TopTenGoldenLogs [Address].Call := ''; END; FindFirst ('rpt\*.rpt', Archive, DirInfo); WHILE DosError = 0 DO BEGIN FileName := DirInfo.Name; ReportCall := GetCallFromFilename (DirInfo.Name); IF OpenFileForRead (FileRead, 'rpt\' + FileName) THEN BEGIN WITH ScoreArray^ [NumberScoreEntries] DO BEGIN Call := ReportCall; Category := GetCategory (Call); TeamName := GetTeam (Call); BandChanges := GetNumberOfBandChanges (Call); IF NOT GetDataBaseData (ReportCall, Name, QTH) THEN BEGIN ReportError (ReportCall + ' NOT FOUND IN DATABASE FILE!! Please add.'); Halt; END; WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); IF StringHas (FileString, 'SCORE SUMMARY') THEN BEGIN ReadLn (FileRead, FileString); { ------------- } ReadLn (FileRead, FileString); { Raw QSOs } TempString := GetLastString (FileString); Val (TempString, NumberRawQSOs, Result); ReadLn (FileRead, FileString); { Dupes } TempString := GetLastString (FileString); Val (TempString, NumberDupes, Result); ReadLn (FileRead, FileString); { Busted QSOs } TempString := GetLastString (FileString); Val (TempString, NumberBustedQSOs, Result); ReadLn (FileRead, FileString); { Final QSOs } TempString := GetLastString (FileString); Val (TempString, NumberFinalQSOs, Result); ReadLn (FileRead, FileString); { Penalty QSOs } TempString := GetLastString (FileString); Val (TempString, NumberPenaltyQSOs, Result); ReadLn (FileRead, FileString); { Final QSOs } TempString := RemoveLastString (FileString); Val (TempString, NumberFinal20MeterQSOs, Result); RemoveLastString (FileString); { = } RemoveLastString (FileString); { 20 } TempString := RemoveLastString (FileString); Val (TempString, NumberFinal40MeterQSOs, Result); RemoveLastString (FileString); { = } RemoveLastString (FileString); { 40 } TempString := RemoveLastString (FileString); Val (TempString, NumberFinal80MeterQSOs, Result); RemoveLastString (FileString); { = } RemoveLastString (FileString); { 80 } TempString := RemoveLastString (FileString); Val (TempString, NumberFinalQSOs, Result); ReadLn (FileRead, FileString); { QSO Points } ReadLn (FileRead, FileString); { Multipliers } TempString := GetLastString (FileString); Val (TempString, NumberMultipliers, Result); ReadLn (FileRead, FileString); { ---------- } ReadLn (FileRead, FileString); { FinalScore } TempString := GetLastString (FileString); Val (TempString, FinalScore, Result); ReadLn (FileRead, FileString); { Error rate } TempString := PostcedingString (FileString, '='); TempString := PrecedingString (TempString, '%'); Val (TempString, ErrorRate, Result); ReadLn (FileRead, FileString); { Hourly rate } FileString := PostcedingString (FileString, '='); QSO00 := RemoveFirstLongInteger (FileString); QSO01 := RemoveFirstLongInteger (FileString); QSO02 := RemoveFirstLongInteger (FileString); QSO03 := RemoveFirstLongInteger (FileString); END; BustRate := 0; IF StringHas (FileString, 'Number busts found') THEN BEGIN TempString := GetLastString (FileString); TempString := PostcedingString (TempString, '('); TempString := PrecedingString (TempString, '%'); Val (TempString, BustRate, Result); END; END; Close (FileRead); AddTeamScore (ScoreArray^ [NumberScoreEntries]); { Maintain top scores - any power level works } TopTenEntry := 15; WHILE (FinalScore > TopTenScores [TopTenEntry].FinalScore) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenScores [ShiftEntry] := TopTenScores [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenScores [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; { Maintain top one radio scores - High Power and Low Power } IF BandChanges <= 12 THEN BEGIN TopTenEntry := 15; IF Category = HighPower THEN BEGIN WHILE (FinalScore > TopTenOneRadioHP [TopTenEntry].FinalScore) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenOneRadioHP [ShiftEntry] := TopTenOneRadioHP [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenOneRadioHP [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; END; IF Category = LowPower THEN BEGIN WHILE (FinalScore > TopTenOneRadioLP [TopTenEntry].FinalScore) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenOneRadioLP [ShiftEntry] := TopTenOneRadioLP [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenOneRadioLP [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; END; END; { Maintain top QSOs } TopTenEntry := 15; WHILE (NumberFinalQSOs > TopTenQSOs [TopTenEntry].NumberFinalQSOs) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenQSOs [ShiftEntry] := TopTenQSOs [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenQSOs [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; { Maintain top mults } TopTenEntry := 15; WHILE (NumberMultipliers > TopTenMults [TopTenEntry].NumberMultipliers) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenMults [ShiftEntry] := TopTenMults [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenMults [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; { Maintain top Low Power scores } IF Category = LowPower THEN BEGIN TopTenEntry := 15; WHILE (FinalScore > TopTenLowPower [TopTenEntry].FinalScore) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenLowPower [ShiftEntry] := TopTenLowPower [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenLowPower [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; END; { Maintain QRP Scores } IF Category = QRP THEN BEGIN TopTenEntry := 15; WHILE (FinalScore > TopTenQRP [TopTenEntry].FinalScore) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenQRP [ShiftEntry] := TopTenQRP [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenQRP [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; END; IF BandChanges > 0 THEN BEGIN TopTenEntry := 15; WHILE (BandChanges > TopTenBandChanges [TopTenEntry].BandChanges) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 14 THEN FOR ShiftEntry := 15 DOWNTO TopTenEntry + 2 DO TopTenBandChanges [ShiftEntry] := TopTenBandChanges [ShiftEntry - 1]; IF TopTenEntry < 15 THEN TopTenBandChanges [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; END; IF NumberBustedQSOs = 0 THEN BEGIN TopTenEntry := 30; WHILE (NumberFinalQSOs > TopTenGoldenLogs [TopTenEntry].NumberFinalQSOs) AND (TopTenEntry > 0) DO Dec (TopTenEntry); IF TopTenEntry < 29 THEN FOR ShiftEntry := 30 DOWNTO TopTenEntry + 2 DO TopTenGoldenLogs [ShiftEntry] := TopTenGoldenLogs [ShiftEntry - 1]; IF TopTenEntry < 30 THEN TopTenGoldenLogs [TopTenEntry + 1] := ScoreArray^ [NumberScoreEntries]; END; END; Inc (NumberScoreEntries); END; FindNext (DirInfo); END; { Sort all results - first by call area, then by score } IF NumberScoreEntries > 1 THEN BEGIN Range := NumberScoreEntries - 2; REPEAT Change := False; FOR Address := 0 TO Range DO BEGIN IF GetBoxNumber (ScoreArray^ [Address]) > GetBoxNumber (ScoreArray^ [Address + 1]) THEN BEGIN TempScoreData := ScoreArray^ [Address]; ScoreArray^ [Address] := ScoreArray^ [Address + 1]; ScoreArray^ [Address + 1] := TempScoreData; Change := True; END ELSE IF GetBoxNumber (ScoreArray^ [Address]) = GetBoxNumber (ScoreArray^ [Address + 1]) THEN IF ScoreArray^ [Address].FinalScore < ScoreArray^ [Address + 1].FinalScore THEN BEGIN TempScoreData := ScoreArray^ [Address]; ScoreArray^ [Address] := ScoreArray^ [Address + 1]; ScoreArray^ [Address + 1] := TempScoreData; Change := True; END; END; Dec (Range); UNTIL (NOT Change) OR (Range < 1); END; { Write scores output to file } LastBoxPrinted := GetBoxNumber (ScoreArray^ [0]); IF OpenFileForWrite (FileWrite, OutputFileName) THEN BEGIN WriteLn (FileWrite, GetTitle); TimeStamp (FileWrite); WriteLn (FileWrite); WriteLn (FileWrite, 'Call', TabKey, 'Name', TabKey, 'QTH', TabKey, '20', TabKey, '40', TabKey, '80', TabKey, 'QSO', TabKey, 'Mult', TabKey, 'Score', TabKey, 'Band Changes', TabKey, 'Team'); FOR Address := 0 TO NumberScoreEntries - 1 DO WITH ScoreArray^ [Address] DO BEGIN IF LastBoxPrinted <> GetBoxNumber (ScoreArray^ [Address]) THEN BEGIN LastBoxPrinted := GetBoxNumber (ScoreArray^ [Address]); WriteLn (FileWrite); END; TempString := Call + TabKey; CASE Category OF LowPower: TempString := TempString + '*'; QRP: TempString := TempString + '**'; END; TempString := TempString + Name + TabKey; FixQTH (QTH); TempString := TempString + QTH + TabKey; Str (NumberFinal20MeterQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberFinal40MeterQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberFinal80MeterQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberFinalQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberMultipliers, NumberString); TempString := TempString + NumberString + TabKey; Str (FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); TempString := TempString + NumberString + TabKey; Str (BandChanges, NumberString); TempString := TempString + NumberString + TabKey + TeamName; WriteLn (FileWrite, TempString); END; WriteLn (FileWrite); WriteLn (FileWrite); { Write Top Ten Scores } WriteLn (FileWrite, 'Top Ten Scores'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Score', TabKey, 'QSOs', TabKey, 'Mults', TabKey, 'Bnd Chgs', TabKey, 'Qs Lost', TabKey, '00Z', TabKey, '01Z', TabKey, '02Z', TabKey, '03Z'); FOR Entry := 1 TO 10 DO BEGIN Str (TopTenScores [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenScores [Entry].Call, TabKey, NumberString, TabKey, TopTenScores [Entry].NumberFinalQSOs, TabKey, TopTenScores [Entry].NumberMultipliers, TabKey, TopTenScores [Entry].BandChanges, TabKey, TopTenScores [Entry].NumberBustedQSOs + TopTenScores [Entry].NumberPenaltyQSOs, TabKey, TopTenScores [Entry].QSO00, TabKey, TopTenScores [Entry].QSO01, TabKey, TopTenScores [Entry].QSO02, TabKey, TopTenScores [Entry].QSO03); END; WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO BEGIN Str (TopTenScores [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenScores [Entry].Call, TabKey, NumberString, TabKey, TopTenScores [Entry].NumberFinalQSOs, TabKey, TopTenScores [Entry].NumberMultipliers, TabKey, TopTenScores [Entry].BandChanges, TabKey, TopTenScores [Entry].NumberBustedQSOs + TopTenScores [Entry].NumberPenaltyQSOs, TabKey, TopTenScores [Entry].QSO00, TabKey, TopTenScores [Entry].QSO01, TabKey, TopTenScores [Entry].QSO02, TabKey, TopTenScores [Entry].QSO03); END; WriteLn (FileWrite); { Write Top Ten High Power - Max 12 Band Changes } WriteLn (FileWrite, 'Top Ten High Power - Max 12 Band Changes'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Score', TabKey, 'QSOs', TabKey, 'Mults', TabKey, 'Bnd Chgs', TabKey, 'Qs Lost', TabKey, '00Z', TabKey, '01Z', TabKey, '02Z', TabKey, '03Z'); FOR Entry := 1 TO 10 DO BEGIN Str (TopTenOneRadioHP [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenOneRadioHP [Entry].Call, TabKey, NumberString, TabKey, TopTenOneRadioHP [Entry].NumberFinalQSOs, TabKey, TopTenOneRadioHP [Entry].NumberMultipliers, TabKey, TopTenOneRadioHP [Entry].BandChanges, TabKey, TopTenOneRadioHP [Entry].NumberBustedQSOs + TopTenOneRadioHP [Entry].NumberPenaltyQSOs, TabKey, TopTenOneRadioHP [Entry].QSO00, TabKey, TopTenOneRadioHP [Entry].QSO01, TabKey, TopTenOneRadioHP [Entry].QSO02, TabKey, TopTenOneRadioHP [Entry].QSO03); END; WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO BEGIN Str (TopTenOneRadioHP [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenOneRadioHP [Entry].Call, TabKey, NumberString, TabKey, TopTenOneRadioHP [Entry].NumberFinalQSOs, TabKey, TopTenOneRadioHP [Entry].NumberMultipliers, TabKey, TopTenOneRadioHP [Entry].BandChanges, TabKey, TopTenOneRadioHP [Entry].NumberBustedQSOs + TopTenOneRadioHP [Entry].NumberPenaltyQSOs, TabKey, TopTenOneRadioHP [Entry].QSO00, TabKey, TopTenOneRadioHP [Entry].QSO01, TabKey, TopTenOneRadioHP [Entry].QSO02, TabKey, TopTenOneRadioHP [Entry].QSO03); END; WriteLn (FileWrite); { Top Ten Low Power } WriteLn (FileWrite, 'Top Ten Low Power'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Score', TabKey, 'QSOs', TabKey, 'Mults', TabKey, 'Bnd Chgs', TabKey, 'Qs Lost', TabKey, '00Z', TabKey, '01Z', TabKey, '02Z', TabKey, '03Z'); FOR Entry := 1 TO 10 DO BEGIN Str (TopTenLowPower [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenLowPower [Entry].Call, TabKey, NumberString, TabKey, TopTenLowPower [Entry].NumberFinalQSOs, TabKey, TopTenLowPower [Entry].NumberMultipliers, TabKey, TopTenLowPower [Entry].BandChanges, TabKey, TopTenLowPower [Entry].NumberBustedQSOs + TopTenLowPower [Entry].NumberPenaltyQSOs, TabKey, TopTenLowPower [Entry].QSO00, TabKey, TopTenLowPower [Entry].QSO01, TabKey, TopTenLowPower [Entry].QSO02, TabKey, TopTenLowPower [Entry].QSO03); END; WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO BEGIN Str (TopTenLowPower [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenLowPower [Entry].Call, TabKey, NumberString, TabKey, TopTenLowPower [Entry].NumberFinalQSOs, TabKey, TopTenLowPower [Entry].NumberMultipliers, TabKey, TopTenLowPower [Entry].BandChanges, TabKey, TopTenLowPower [Entry].NumberBustedQSOs + TopTenLowPower [Entry].NumberPenaltyQSOs, TabKey, TopTenLowPower [Entry].QSO00, TabKey, TopTenLowPower [Entry].QSO01, TabKey, TopTenLowPower [Entry].QSO02, TabKey, TopTenLowPower [Entry].QSO03); END; WriteLn (FileWrite); { Top Ten Low Power - Max 12 Band Changes } WriteLn (FileWrite, 'Top Ten Low Power - Max 12 Band Changes'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Score', TabKey, 'QSOs', TabKey, 'Mults', TabKey, 'Bnd Chgs', TabKey, 'Qs Lost', TabKey, '00Z', TabKey, '01Z', TabKey, '02Z', TabKey, '03Z'); FOR Entry := 1 TO 10 DO BEGIN Str (TopTenOneRadioLP [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenOneRadioLP [Entry].Call, TabKey, NumberString, TabKey, TopTenOneRadioLP [Entry].NumberFinalQSOs, TabKey, TopTenOneRadioLP [Entry].NumberMultipliers, TabKey, TopTenOneRadioLP [Entry].BandChanges, TabKey, TopTenOneRadioLP [Entry].NumberBustedQSOs + TopTenOneRadioLP [Entry].NumberPenaltyQSOs, TabKey, TopTenOneRadioLP [Entry].QSO00, TabKey, TopTenOneRadioLP [Entry].QSO01, TabKey, TopTenOneRadioLP [Entry].QSO02, TabKey, TopTenOneRadioLP [Entry].QSO03); END; WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO BEGIN Str (TopTenOneRadioLP [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenOneRadioLP [Entry].Call, TabKey, NumberString, TabKey, TopTenOneRadioLP [Entry].NumberFinalQSOs, TabKey, TopTenOneRadioLP [Entry].NumberMultipliers, TabKey, TopTenOneRadioLP [Entry].BandChanges, TabKey, TopTenOneRadioLP [Entry].NumberBustedQSOs + TopTenOneRadioLP [Entry].NumberPenaltyQSOs, TabKey, TopTenOneRadioLP [Entry].QSO00, TabKey, TopTenOneRadioLP [Entry].QSO01, TabKey, TopTenOneRadioLP [Entry].QSO02, TabKey, TopTenOneRadioLP [Entry].QSO03); END; WriteLn (FileWrite); { Top Ten QRP } WriteLn (FileWrite, 'Top Ten QRP'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Score', TabKey, 'QSOs', TabKey, 'Mults', TabKey, 'Bnd Chgs', TabKey, 'Qs Lost', TabKey, '00Z', TabKey, '01Z', TabKey, '02Z', TabKey, '03Z'); FOR Entry := 1 TO 10 DO BEGIN Str (TopTenQRP [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenQRP [Entry].Call, TabKey, NumberString, TabKey, TopTenQRP [Entry].NumberFinalQSOs, TabKey, TopTenQRP [Entry].NumberMultipliers, TabKey, TopTenQRP [Entry].BandChanges, TabKey, TopTenQRP [Entry].NumberBustedQSOs + TopTenQRP [Entry].NumberPenaltyQSOs, TabKey, TopTenQRP [Entry].QSO00, TabKey, TopTenQRP [Entry].QSO01, TabKey, TopTenQRP [Entry].QSO02, TabKey, TopTenQRP [Entry].QSO03); END; WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO BEGIN Str (TopTenQRP [Entry].FinalScore, NumberString); IF Length (NumberString) > 3 THEN Insert (',', NumberString, Length (NumberString) - 2); WriteLn (FileWrite, TopTenQRP [Entry].Call, TabKey, NumberString, TabKey, TopTenQRP [Entry].BandChanges, TabKey, TopTenQRP [Entry].NumberFinalQSOs, TabKey, TopTenQRP [Entry].NumberMultipliers, TabKey, TopTenQRP [Entry].NumberBustedQSOs + TopTenQRP [Entry].NumberPenaltyQSOs, TabKey, TopTenQRP [Entry].QSO00, TabKey, TopTenQRP [Entry].QSO01, TabKey, TopTenQRP [Entry].QSO02, TabKey, TopTenQRP [Entry].QSO03); END; WriteLn (FileWrite); { Top Ten QSOs } WriteLn (FileWrite, 'Top Ten QSO Totals'); WriteLn (FileWrite, 'Call Sign', TabKey, 'QSOs'); FOR Entry := 1 TO 10 DO WriteLn (FileWrite, TopTenQSOs [Entry].Call, TabKey, TopTenQSOs [Entry].NumberFinalQSOs); WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO WriteLn (FileWrite, TopTenQSOs [Entry].Call, TabKey, TopTenQSOs [Entry].NumberFinalQSOs); WriteLn (FileWrite); WriteLn (FileWrite); WriteLn (FileWrite, 'Top Ten Multipliers'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Mults'); FOR Entry := 1 TO 10 DO WriteLn (FileWrite, TopTenMults [Entry].Call, TabKey, TopTenMults [Entry].NumberMultipliers); WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO WriteLn (FileWrite, TopTenMults [Entry].Call, TabKey, TopTenMults [Entry].NumberMultipliers); WriteLn (FileWrite); { Golden Logs } WriteLn (FileWrite, 'Golden Logs'); WriteLn (FileWrite, 'Call Sign', TabKey, 'QSOs'); FOR Entry := 1 TO 30 DO BEGIN IF TopTenGoldenLogs [Entry].NumberFinalQSOs = 0 THEN Break; WriteLn (FileWrite, TopTenGoldenLogs [Entry].Call, TabKey, TopTenGoldenLogs [Entry].NumberFinalQSOs); END; WriteLn (FileWrite); { Top Ten Band Changes } WriteLn (FileWrite, 'Top Ten Number of Band Changes'); WriteLn (FileWrite, 'Call Sign', TabKey, 'Bnd Chgs'); FOR Entry := 1 TO 10 DO WriteLn (FileWrite, TopTenBandChanges [Entry].Call, TabKey, TopTenBandChanges [Entry].BandChanges); WriteLn (FileWrite, '<<< top ten cutoff >>>'); FOR Entry := 11 TO 15 DO WriteLn (FileWrite, TopTenBandChanges [Entry].Call, TabKey, TopTenBandChanges [Entry].BandChanges); Close (FileWrite); END; ProcessTeamScores (OutputFileName); Dispose (ScoreArray); WriteLn ('Scores saved in SCORES.TXT'); WaitForKeyPressed; END; PROCEDURE CollectScoreInformationInWA7BNMFormat; VAR ReportCall: Callstring; ShiftEntry, LastBoxPrinted, Result, Range, Address: INTEGER; Entry: INTEGER; OpCall, StationCall, NumberString, OutputFilename, FileName: Str40; TeamName, TempString: Str80; DirInfo: SearchRec; FileRead, FileWrite: TEXT; FileString: STRING; TempScoreData: ScoreRecord; Change: BOOLEAN; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('COLLECT SCORE INFORMATION IN WA7BNM FORMAT'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will collect the score information from the .RPT files and'); WriteLn ('save it into file format that WA7BNM has defined.'); WriteLn; OutputFileName := GetResponse ('Enter filename to save output to (none to exit) : '); IF OutputFileName = '' THEN Exit; LoadInSprintDataBaseFile; LoadInTeams; New (ScoreArray); NumberScoreEntries := 0; FindFirst ('rpt\*.rpt', Archive, DirInfo); WHILE DosError = 0 DO BEGIN FileName := DirInfo.Name; ReportCall := GetCallFromFilename (DirInfo.Name); IF OpenFileForRead (FileRead, 'rpt\' + FileName) THEN BEGIN WITH ScoreArray^ [NumberScoreEntries] DO BEGIN Call := ReportCall; Category := GetCategory (Call); TeamName := GetTeam (Call); BandChanges := GetNumberOfBandChanges (Call); IF NOT GetDataBaseData (ReportCall, Name, QTH) THEN BEGIN ReportError (ReportCall + ' NOT FOUND IN DATABASE FILE!! Please add.'); Halt; END; WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); IF StringHas (FileString, 'SCORE SUMMARY') THEN BEGIN ReadLn (FileRead, FileString); { ------------- } ReadLn (FileRead, FileString); { Raw QSOs } TempString := GetLastString (FileString); Val (TempString, NumberRawQSOs, Result); ReadLn (FileRead, FileString); { Dupes } TempString := GetLastString (FileString); Val (TempString, NumberDupes, Result); ReadLn (FileRead, FileString); { Busted QSOs } TempString := GetLastString (FileString); Val (TempString, NumberBustedQSOs, Result); ReadLn (FileRead, FileString); { Final QSOs } TempString := GetLastString (FileString); Val (TempString, NumberFinalQSOs, Result); ReadLn (FileRead, FileString); { Penalty QSOs } TempString := GetLastString (FileString); Val (TempString, NumberPenaltyQSOs, Result); ReadLn (FileRead, FileString); { Final QSOs } TempString := RemoveLastString (FileString); Val (TempString, NumberFinal20MeterQSOs, Result); RemoveLastString (FileString); { = } RemoveLastString (FileString); { 20 } TempString := RemoveLastString (FileString); Val (TempString, NumberFinal40MeterQSOs, Result); RemoveLastString (FileString); { = } RemoveLastString (FileString); { 40 } TempString := RemoveLastString (FileString); Val (TempString, NumberFinal80MeterQSOs, Result); RemoveLastString (FileString); { = } RemoveLastString (FileString); { 80 } TempString := RemoveLastString (FileString); Val (TempString, NumberFinalQSOs, Result); ReadLn (FileRead, FileString); { QSO Points } ReadLn (FileRead, FileString); { Multipliers } TempString := GetLastString (FileString); Val (TempString, NumberMultipliers, Result); ReadLn (FileRead, FileString); { ---------- } ReadLn (FileRead, FileString); { FinalScore } TempString := GetLastString (FileString); Val (TempString, FinalScore, Result); ReadLn (FileRead, FileString); { Error rate } TempString := PostcedingString (FileString, '='); TempString := PrecedingString (TempString, '%'); Val (TempString, ErrorRate, Result); ReadLn (FileRead, FileString); { Hourly rate } FileString := PostcedingString (FileString, '='); QSO00 := RemoveFirstLongInteger (FileString); QSO01 := RemoveFirstLongInteger (FileString); QSO02 := RemoveFirstLongInteger (FileString); QSO03 := RemoveFirstLongInteger (FileString); END; BustRate := 0; IF StringHas (FileString, 'Number busts found') THEN BEGIN TempString := GetLastString (FileString); TempString := PostcedingString (TempString, '('); TempString := PrecedingString (TempString, '%'); Val (TempString, BustRate, Result); END; END; Close (FileRead); END; Inc (NumberScoreEntries); END; FindNext (DirInfo); END; { Sort results - first by call area, then by score } IF NumberScoreEntries > 1 THEN BEGIN Range := NumberScoreEntries - 2; REPEAT Change := False; FOR Address := 0 TO Range DO BEGIN IF GetBoxNumber (ScoreArray^ [Address]) > GetBoxNumber (ScoreArray^ [Address + 1]) THEN BEGIN TempScoreData := ScoreArray^ [Address]; ScoreArray^ [Address] := ScoreArray^ [Address + 1]; ScoreArray^ [Address + 1] := TempScoreData; Change := True; END ELSE IF GetBoxNumber (ScoreArray^ [Address]) = GetBoxNumber (ScoreArray^ [Address + 1]) THEN IF ScoreArray^ [Address].FinalScore < ScoreArray^ [Address + 1].FinalScore THEN BEGIN TempScoreData := ScoreArray^ [Address]; ScoreArray^ [Address] := ScoreArray^ [Address + 1]; ScoreArray^ [Address + 1] := TempScoreData; Change := True; END; END; Dec (Range); UNTIL (NOT Change) OR (Range < 1); END; { Write output to file } IF OpenFileForWrite (FileWrite, OutputFileName) THEN BEGIN FOR Address := 0 TO NumberScoreEntries - 1 DO WITH ScoreArray^ [Address] DO BEGIN OpCall := GetOpCall (Call, StationCall); TempString := Call + TabKey + OpCall + TabKey + StationCall + TabKey; CASE Category OF HighPower: TempString := TempString + 'HP' + TabKey; LowPower: TempString := TempString + 'LP' + TabKey; QRP: TempString := TempString + 'QRP' + TabKey; END; TempString := TempString + Name + TabKey; TempString := TempString + QTH + TabKey; Str (NumberFinal20MeterQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberFinal40MeterQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberFinal80MeterQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberFinalQSOs, NumberString); TempString := TempString + NumberString + TabKey; Str (NumberMultipliers, NumberString); TempString := TempString + NumberString + TabKey; Str (FinalScore, NumberString); TempString := TempString + NumberString + TabKey + TeamName; WriteLn (FileWrite, TempString); END; Close (FileWrite); END; Dispose (ScoreArray); END; PROCEDURE EstimateNumberOfQSOsForDatabaseStations; VAR DirInfo: SearchRec; Key: CHAR; LogAddress, Average, Address, DatabaseAddress: INTEGER; DatabaseCall: CallString; OutputFileName: Str40; OutputFile: TEXT; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('ESTIMATE NUMBER OF QSOs MADE BY STATIONS IN SS DATABASE'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will look through all the logs and estimate the number of QSOs'); WriteLn ('made by each station in the database. This data will then be saved in a file'); WriteLn ('which can be used by the L command to convert the results into percentages of'); WriteLn ('the estimated # of QSOs made by the stations appearing in the busted list.'); WriteLn; WriteLn ('The format of the output file is the call, the estimated # of QSOs and the'); WriteLn ('number of logs the call was found in.'); WriteLn; WriteLn ('This process can take a few minutes to run.'); WriteLn; REPEAT Key := UpCase (GetKey ('Okay to proceed? (Y/N) : ')); IF (Key = 'N') OR (Key = EscapeKey) THEN Exit; UNTIL Key = 'Y'; WriteLn; OutputFileName := GetResponse ('Filename to save results to (none to exit) : '); IF OutputFileName = '' THEN Exit; IF NOT LoadInSprintDataBaseFile THEN Exit; { CreateListOfAvailableLogs } NumberAvailableLogs := 0; FindFirst ('LOGS\*.LOG', Archive, DirInfo); WHILE DosError = 0 DO BEGIN IF UpperCase (DirInfo.Name) <> 'B0GUS.LOG' THEN BEGIN AvailableLogs^ [NumberAvailableLogs].Call := GetCallFromFileName (DirInfo.Name); Inc (NumberAvailableLogs); END; FindNext (DirInfo); END; New (SprintDatabaseQSOTotals); IF NumberSprintDatabaseEntries > 0 THEN FOR Address := 0 TO NumberSprintDatabaseEntries - 1 DO BEGIN SprintDatabaseQSOTotals^ [Address].RunningTotal := 0; SprintDatabaseQSOTotals^ [Address].NumberLogs := 0; END; FOR Address := 0 TO NumberAvailableLogs - 1 DO BEGIN PinWheel; IF LoadLogIntoActiveLog (AvailableLogs^ [Address].Call) THEN IF ActiveLog^.NumberQSOEntries > 0 THEN BEGIN FOR DatabaseAddress := 0 TO NumberSprintDatabaseEntries - 1 DO BEGIN DatabaseCall := SprintDatabase^ [DatabaseAddress].Call; FOR LogAddress := 0 TO ActiveLog^.NumberQSOEntries - 1 DO IF ActiveLog^.QSOList [LogAddress].Call = DatabaseCall THEN BEGIN SprintDatabaseQSOTotals^ [DatabaseAddress].RunningTotal := SprintDatabaseQSOTotals^ [DatabaseAddress].RunningTotal + ActiveLog^.QSOList [LogAddress].ReceivedQSONumber; Inc (SprintDatabaseQSOTotals^ [DatabaseAddress].NumberLogs); END; END; END; END; { Save the results to a file } WriteLn; IF OpenFileForWrite (FileWrite, OutputFileName) THEN BEGIN IF NumberSprintDatabaseEntries > 0 THEN FOR Address := 0 TO NumberSprintDatabaseEntries - 1 DO BEGIN WITH SprintDatabaseQSOTotals^ [Address] DO BEGIN IF NumberLogs > 0 THEN Average := RunningTotal DIV NumberLogs ELSE Average := 0; WriteLn (FileWrite, SprintDatabase^ [Address].Call, ' ', 2 * Average, ' ', NumberLogs, ' ', SprintDatabase^ [Address].QTH); END; END; Close (FileWrite); END; Dispose (SprintDatabaseQSOTotals); END; FUNCTION GenerateTimeString (Time: INTEGER): Str20; VAR Hour, Minute: INTEGER; HourString, MinuteString: Str20; BEGIN Hour := Time DIV 60; Minute := Time MOD 60; IF StartTime = '2300' THEN BEGIN IF Hour = 0 THEN HourString := '23' ELSE BEGIN Hour := Hour - 1; Str (Hour, HourString); END; END ELSE Str (Hour, HourString); Str (Minute, MinuteString); IF Length (HourString) = 1 THEN HourString := '0' + HourString; IF Length (MinuteString) = 1 THEN MinuteString := '0' + MinuteString; GenerateTimeString := HourString + ':' + MinuteString; END; PROCEDURE ReconstitutedLog; VAR DirInfo: SearchRec; Key: CHAR; Range, Address, LogAddress, Average, QSOAddress: INTEGER; LogFileCall, Call, TempString: CallString; OutputFileName: Str40; OutputFile: TEXT; Change: BOOLEAN; TempQSOData: QSOEntry; FileName, FileString: Str80; FileRead: TEXT; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('CREATE RECONSTITUTED LOG'); TextColor (Cyan); WriteLn; WriteLn ('This procedure will pull out all the QSOs it can find for the callsign you'); WriteLn ('type in, sort them into order, and then save the log to an ASCII file.'); WriteLn; WriteLn ('This is very useful when looking at activity patterns to determine if a call'); WriteLn ('was busted. The file will be saved as call.RCL.'); WriteLn; FileName := GetResponse ('Enter filename for multiple calls or none for single calls) : '); IF FileName = '' THEN BEGIN Call := UpperCase (GetResponse ('Call to create a log for (none to exit) : ')); IF Call = '' THEN Exit; END ELSE BEGIN IF NOT OpenFileForRead (FileRead, FileName) THEN Exit; ReadLn (FileRead, FileString); Call := RemoveFirstString (FileString); END; REPEAT WriteLn ('Processing ', Call, ' ', FileName); FindFirst ('LOGS\*.LOG', Archive, DirInfo); TargetLog^.NumberQSOEntries := 0; WHILE DosError = 0 DO BEGIN LogFileCall := GetCallFromFilename (DirInfo.Name); IF LogFileCall <> 'B0GUS' THEN IF LoadLogIntoActiveLog (LogFileCall) THEN IF ActiveLog^.NumberQSOEntries > 0 THEN FOR QSOAddress := 0 TO ActiveLog^.NumberQSOEntries - 1 DO IF Call = ActiveLog^.QSOList [QSOAddress].Call THEN BEGIN TargetLog^.QSOList [TargetLog^.NumberQSOEntries] := ActiveLog^.QSOList [QSOAddress]; TargetLog^.QSOList [TargetLog^.NumberQSOEntries].Call := LogFileCall; Inc (TargetLog^.NumberQSOEntries); { No break here as we want to find QSOs on other bands } END; FindNext (DirInfo); END; WriteLn ('There were ', TargetLog^.NumberQSOEntries, ' QSOs found with ', Call); { Now sort them into order } IF TargetLog^.NumberQSOEntries > 1 THEN BEGIN Range := TargetLog^.NumberQSOEntries - 1; REPEAT Change := False; FOR Address := 0 TO Range - 1 DO BEGIN IF TargetLog^.QSOList [Address].ReceivedQSONumber > TargetLog^.QSOList [Address + 1].ReceivedQSONumber THEN BEGIN TempQSOData := TargetLog^.QSOList [Address]; TargetLog^.QSOList [Address] := TargetLog^.QSOList [Address + 1]; TargetLog^.QSOList [Address + 1] := TempQSOData; Change := True; END; END; Dec (Range); UNTIL (NOT Change) OR (Range < 1); END; { Write output to a file } IF TargetLog^.NumberQSOEntries > 0 THEN BEGIN IF StringHas (Call, '/') THEN Call := PrecedingString (Call, '/'); IF OpenFileForWrite (FileWrite, Call + '.RCL') THEN BEGIN FOR Address := 0 TO TargetLog^.NumberQSOEntries - 1 DO WITH TargetLog^.QSOList [Address] DO WriteLn (FileWrite, Call:12, ' ', GenerateTimeString (Time), ' ', SentQSONumber:5, ReceivedQSONumber:5, ' ', ReceivedName, ' ', ReceivedQTH); Close (FileWrite); END; END; IF FileName <> '' THEN BEGIN IF Eof (FileRead) THEN FileName := '' ELSE BEGIN ReadLn (FileRead, FileString); Call := RemoveFirstString (FileString); WriteLn ('Processing ', Call); END; END; UNTIL FileName = ''; END; PROCEDURE VerifyAllReceivedLogsAreInDatabase; VAR Key: CHAR; Address: INTEGER; DirInfo: SearchRec; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('VERIFY THAT ALL .LOG LOGS APPEAR IN THE DATABASE'); TextColor (Cyan); WriteLn; WriteLn ('This procedure will check to make sure all the SS logs are found in the'); WriteLn ('database. The A command will stop if it finds a log that isn''t listed.'); WriteLn; REPEAT Key := UpCase (GetKey ('Okay to proceed? (Y/N) : ')); IF (Key = 'N') OR (Key = EscapeKey) THEN Exit; UNTIL Key = 'Y'; WriteLn; NumberAvailableLogs := 0; FindFirst ( 'LOGS\*.LOG', Archive, DirInfo); WHILE DosError = 0 DO BEGIN IF UpperCase (DirInfo.Name) <> 'B0GUS.LOG' THEN BEGIN AvailableLogs^ [NumberAvailableLogs].Call := GetCallFromFileName (DirInfo.Name); Inc (NumberAvailableLogs); END; FindNext (DirInfo); END; LoadInSprintDataBaseFile; IF NumberAvailableLogs > 0 THEN FOR Address := 0 TO NumberAvailableLogs - 1 DO WITH AvailableLogs^ [Address] DO IF NOT CallInDatabase (Call) THEN WriteLn (Call, ' not found in the database.'); WaitForKeyPressed; END; FUNCTION GetActualQSOs (Call: CallString): INTEGER; BEGIN IF LoadLogIntoActiveLog (Call) THEN GetActualQSOs := ActiveLog^.NumberQSOEntries ELSE GetActualQSOs := 0; END; FUNCTION GetEstimatedQSOs (Call: CallString; FileName: Str80): INTEGER; VAR FileRead: TEXT; FileString: Str80; BEGIN IF OpenFileForRead (FileRead, FileName) THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); GetRidOfPrecedingSpaces (FileString); IF FileString <> '' THEN IF RemoveFirstString (FileString) = Call THEN BEGIN GetEstimatedQSOs := RemoveFirstLongInteger (FileString); Close (FileRead); Exit; END; END; Close (FileRead); END; GetEstimatedQSOs := 0; END; PROCEDURE FindMungedLogs; VAR DirInfo: SearchRec; Call: CallString; ActualQSOs, EstimatedQSOs: INTEGER; OutputFileName, InputFileName: Str40; FileWrite: TEXT; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('LOOK FOR MUNGED LOGS'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will look for logs that appear to be truncated. You will'); WriteLn ('need to run the E command first and save the results to a file.'); WriteLn; InputFileName := GetResponse ('Enter filename of estimate results (none to abort) : '); IF NOT FileExists (InputFileName) THEN BEGIN ReportError (InputFileName + ' not found!!'); WaitForKeyPressed; Exit; END; OutputFileName := GetResponse ('Filename to save results to (optional) : '); IF OutputFileName <> '' THEN OpenFileForWrite (FileWrite, OutputFileName); FindFirst ('LOGS\*.LOG', Archive, DirInfo); WHILE DosError = 0 DO BEGIN PinWheel; Call := GetCallFromFilename (DirInfo.Name); IF Call <> 'B0GUS' THEN BEGIN ActualQSOs := GetActualQSOs (Call); EstimatedQSOs := GetEstimatedQSOs (Call, InputFileName); IF ActualQSOs > 50 THEN IF ActualQSOs < (EstimatedQSOs * 0.80) THEN BEGIN WriteLn (Call, ' estimated QSOs = ', EstimatedQSOs, '. Actual QSOs = ', ActualQSOs); IF OutputFileName <> '' THEN WriteLn (FileWrite, Call, ' estimated QSOs = ', EstimatedQSOs, '. Actual QSOs = ', ActualQSOs); END; END; FindNext (DirInfo); END; IF OutputFileName <> '' THEN Close (FileWrite); WaitForKeyPressed; END; PROCEDURE BustInfo; VAR Key: Char; Call: CallString; DirInfo: SearchRec; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('APPEND BUSTED QSO INFO TO REPORT FILES'); WriteLn; TextColor (Cyan); WriteLn ('This procedure will add information at the end of report files to show who'); WriteLn ('lost QSOs made with the station whom the report file is for.'); WriteLn; REPEAT Key := UpCase (GetKey ('Do you want to do this for all of the reports? (Y/N) : ')); IF Key = EscapeKey THEN Exit; UNTIL (Key = 'Y') OR (Key = 'N'); WriteLn; WriteLn; IF Key = 'Y' THEN BEGIN FindFirst ( 'RPT\*.rpt', Archive, DirInfo); WHILE DosError = 0 DO BEGIN Call := GetCallFromFileName (DirInfo.Name); GoToXY (1, WhereY); ClrEol; Write ('Processing ', Call); AppendBustedInfoToReportFile (Call); FindNext (DirInfo); END; END ELSE BEGIN REPEAT Call := UpperCase (GetResponse ('Enter report call to append busted info to (none to quit) : ')); IF Call = '' THEN Exit; AppendBustedInfoToReportFile (Call); UNTIL False; END; END; FUNCTION LooksLikeAGoodCall (Call: CallString): BOOLEAN; { Recreates a log for the station indicated and analyzes the received QSO number profile to see if it looks like a real station. ' } VAR DirInfo: SearchRec; LogFileCall: CallString; NumberSuspectNumbers, QSOAddress, Address, Range: INTEGER; Change: BOOLEAN; TempQSOData: QSOEntry; FileWrite: TEXT; BEGIN FindFirst ( 'LOGS\*.LOG', Archive, DirInfo); TargetLog^.NumberQSOEntries := 0; WHILE DosError = 0 DO BEGIN LogFileCall := GetCallFromFileName (DirInfo.Name); IF LogFileCall <> 'B0GUS' THEN IF LoadLogIntoActiveLog (LogFileCall) THEN IF ActiveLog^.NumberQSOEntries > 0 THEN FOR QSOAddress := 0 TO ActiveLog^.NumberQSOEntries - 1 DO IF Call = ActiveLog^.QSOList [QSOAddress].Call THEN BEGIN TargetLog^.QSOList [TargetLog^.NumberQSOEntries] := ActiveLog^.QSOList [QSOAddress]; TargetLog^.QSOList [TargetLog^.NumberQSOEntries].Call := LogFileCall; Inc (TargetLog^.NumberQSOEntries); Break; END; FindNext (DirInfo); END; { Now sort them into order } IF TargetLog^.NumberQSOEntries > 1 THEN BEGIN Range := TargetLog^.NumberQSOEntries - 2; REPEAT Change := False; FOR Address := 0 TO Range DO BEGIN IF TargetLog^.QSOList [Address].ReceivedQSONumber > TargetLog^.QSOList [Address + 1].ReceivedQSONumber THEN BEGIN TempQSOData := TargetLog^.QSOList [Address]; TargetLog^.QSOList [Address] := TargetLog^.QSOList [Address + 1]; TargetLog^.QSOList [Address + 1] := TempQSOData; Change := True; END; END; Dec (Range); UNTIL (NOT Change) OR (Range < 1); END; { Save the file since we went to all this trouble } IF TargetLog^.NumberQSOEntries > 0 THEN IF OpenFileForWrite (FileWrite, Call + '.RCL') THEN BEGIN FOR Address := 0 TO TargetLog^.NumberQSOEntries - 1 DO WITH TargetLog^.QSOList [Address] DO WriteLn (FileWrite, Call:12, SentQSONumber:5, ReceivedQSONumber:5, ' ', ReceivedName, ' ', ReceivedQTH); Close (FileWrite); END; { Look at QSO Numbers } LooksLikeAGoodCall := False; IF TargetLog^.NumberQSOEntries > 100 THEN BEGIN IF TargetLog^.QSOList [90].ReceivedQSONumber < 300 THEN LooksLikeAGoodCall := True; Exit; END; IF TargetLog^.NumberQSOEntries > 50 THEN BEGIN IF TargetLog^.QSOList [45].ReceivedQSONumber < 150 THEN LooksLikeAGoodCall := True; Exit; END; IF TargetLog^.NumberQSOEntries > 10 THEN BEGIN IF TargetLog^.QSOList [9].ReceivedQSONumber < 30 THEN LooksLikeAGoodCall := True; Exit; END; IF TargetLog^.NumberQSOEntries = 0 THEN Exit; NumberSuspectNumbers := 0; FOR Address := 0 TO TargetLog^.NumberQSOEntries - 1 DO IF TargetLog^.QSOList [Address].ReceivedQSONumber > (Address + 1) * 5 THEN Inc (NumberSuspectNumbers); IF NumberSuspectNumbers > TargetLog^.NumberQSOEntries DIV 3 THEN Exit; LooksLikeAGoodCall := True; END; PROCEDURE DetermineIfCallsLookReal; VAR InputFileName: Str40; FileRead, FileWrite: TEXT; Call: CallString; NumberBadCalls, NumberGoodCalls: INTEGER; FileString: Str80; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('DETERMINE IF A LIST OF CALLSIGNS LOOK REAL'); TextColor (Cyan); WriteLn ('This procedure will look through a list of callsigns and decide if any of'); WriteLn ('calls look real. This is done by looking at the QSO number distribution'); WriteLn ('found for the log when recreating a log for them. Any suspect calls are'); WriteLn ('saved in the file NOTREAL.DAT.'); WriteLn; InputFileName := GetResponse ('Enter filename to process (none to exit) : '); IF InputFileName = '' THEN Exit; NumberBadCalls := 0; NumberGoodCalls := 0; IF OpenFileForRead (FileRead, InputFileName) THEN BEGIN OpenFileForWrite (FileWrite, 'NOTREAL.DAT'); WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); Call := RemoveFirstString (FileString); IF NOT LooksLikeAGoodCall (Call) THEN BEGIN WriteLn (Call, ' appears to be a BAD call.'); WriteLn (FileWrite, Call); Inc (NumberBadCalls); END ELSE BEGIN WriteLn (Call, ' appears to be a GOOD call.'); Inc (NumberGoodCalls); END; END; Close (FileWrite); Close (FileRead); END; WriteLn ('There were ', NumberBadCalls, ' bad looking calls found.'); WriteLn ('There were ', NumberGoodCalls, ' good looking calls found.'); WaitForKeyPressed; END; PROCEDURE AutomaticBadCallDetection; VAR Address: INTEGER; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('AUTOMAGIC BAD CALL DETECTION'); TextColor (Cyan); WriteLn; WriteLn ('This procedure will look at all the calls in the database and find the ones'); WriteLn ('that appear to be bad calls.'); WriteLn; REPEAT Key := UpCase (GetKey ('Okay to proceed? (Y/N) : ')); IF (Key = 'N') OR (Key = EscapeKey) THEN Exit; UNTIL Key = 'Y'; WriteLn; IF NOT LoadInSprintDatabaseFile THEN BEGIN ReportError ('No database file found!!'); WaitForKeyPressed; Exit; END; { Sort the database by name/QTH. We expect the same name/QTH for any busted calls to match the name/QTH of any real calls. } SortSprintDatabaseFile (True); { Now - look through and see if any name/QTHs are doubled up } FOR Address := 0 TO NumberSprintDatabaseEntries - 2 DO BEGIN IF (SprintDatabase^ [Address].Name = SprintDatabase^ [Address + 1].Name) AND (SprintDatabase^ [Address].QTH = SprintDatabase^ [Address + 1].QTH) THEN BEGIN IF NOT LooksLikeAGoodCall (SprintDatabase^ [Address].Call) THEN WriteLn (SprintDatabase^ [Address].Call, ' looks like a bad call.'); IF NOT LooksLikeAGoodCall (SprintDatabase^ [Address + 1].Call) THEN WriteLn (SprintDatabase^ [Address + 1].Call, ' looks like a bad call.'); END; END; WaitForKeyPressed; END; PROCEDURE CheckSPRFiles; VAR Key: CHAR; ProcessAllFiles: BOOLEAN; DirInfo: SearchRec; FileName: Str80; NumberLogsFound: INTEGER; Call: CallString; BEGIN ClearScreenAndTitle ('CHECK SPR FILES'); WriteLn ('The logs that you want this program to work on are ASCII files with .LOG as'); WriteLn ('their file extension. However, these files are very inefficient for the'); WriteLn ('program to deal with. Instead, the program will convert these files to binary'); WriteLn ('files with .bin as their extension.'); WriteLn; WriteLn ('Whenever the program is about to use a .bin file, it will first check the time'); WriteLn ('stamp for the .LOG file. If it is newer than the .bin file (indicating that you'); WriteLn ('may have edited or replaced the file), it will regenerate the .bin file.'); WriteLn; WriteLn ('It is not necessary for you to run this procedure, unless you desire to check'); WriteLn ('the conversion process without using any of the other log checking commands.'); WriteLn; WriteLn ('If you like, you can process all of the .LOG files and freshen the .bin files'); WriteLn ('even if their time stamps are after the .LOG files. You can check the results'); WriteLn ('of the conversion process by looking at the file SPRLOG.DAT.'); WriteLn; REPEAT Key := UpCase (GetKey ('Process all of the .LOG files? (Y/N or ESCAPE to abort) : ')); IF Key = EscapeKey THEN Exit; UNTIL (Key = 'Y') OR (Key = 'N'); WriteLn; ProcessAllFiles := Key = 'Y'; REPEAT Key := UpCase (GetKey ('Delete ' + SprintLogFileName +'? (Y/N or ESCAPE to abort) : ')); IF Key = EscapeKey THEN Exit; UNTIL (Key = 'Y') OR (Key = 'N'); WriteLn; IF Key = 'Y' THEN DeleteFile (SprintlogFileName); FindFirst ( 'LOGS\*.LOG', Archive, DirInfo); NumberLogsFound := 0; WHILE DosError = 0 DO BEGIN FileName := UpperCase (DirInfo.Name); Inc (NumberLogsFound); Call := GetCallFromFileName (FileName); IF (NOT BinaryFileOkayToUse (Call)) OR ProcessAllFiles THEN BEGIN Write ('Processing ', Call); ConvertASCIIToBinaryFile (Call); END; FindNext (DirInfo); END; WriteLn ('You can find information on any errors found in ', SprintLogFileName); WaitForKeyPressed; END; PROCEDURE FindMissingLogs; VAR Key: CHAR; AvailableLogAddress, DatabaseAddress, LogAddress: INTEGER; EstimatedTotal, NewDatabaseTotal: INTEGER; DatabaseCall, Callsign: CallString; FileWrite: TEXT; DirInfo: SearchRec; FileNamePrefix: Str20; BEGIN ClrScr; TextColor (Yellow); WriteLnCenter ('FIND MISSING LOGS'); WriteLn; TextColor (Cyan); WriteLn ('This procedure looks for calls in the database that were worked by at least'); WriteLn ('10 stations for which no .LOG file exists and outputs them into MISSING.LOG.'); WriteLn; REPEAT Key := UpCase (GetKey ('Okay to proceed? (Y/N) : ')); IF (Key = EscapeKey) OR (Key = 'N') THEN Exit; UNTIL Key = 'Y'; WriteLn; IF NOT LoadInSprintDatabaseFile THEN Exit; { CreateListOfAvailableLogs } NumberAvailableLogs := 0; FindFirst ( 'LOGS\*.LOG', Archive, DirInfo); WHILE DosError = 0 DO BEGIN IF UpperCase (DirInfo.Name) <> 'B0GUS.LOG' THEN BEGIN AvailableLogs^ [NumberAvailableLogs].Call := GetCallFromFileName (DirInfo.Name); Inc (NumberAvailableLogs); END; FindNext (DirInfo); END; IF NumberSprintDatabaseEntries = 0 THEN Exit; OpenFileForWrite (FileWrite, 'MISSING.LOG'); WriteLn (FileWrite, ' Callsign Estimated QSOs'); { Set up database total array } New (SprintDatabaseQSOTotals); IF NumberSprintDatabaseEntries > 0 THEN FOR DatabaseAddress := 0 TO NumberSprintDatabaseEntries - 1 DO BEGIN SprintDatabaseQSOTotals^ [DatabaseAddress].RunningTotal := 0; SprintDatabaseQSOTotals^ [DatabaseAddress].NumberLogs := 0; END; { Mark the logs we have (NumberLogs = 999) } FOR DatabaseAddress := 0 TO NumberSprintDatabaseEntries - 1 DO BEGIN WITH SprintDatabase^ [DatabaseAddress] DO BEGIN FilenamePrefix := GetFilenamePrefixForCall (Call); IF FileExists ( 'logs\' + FileNamePrefix + '.log') THEN SprintDatabaseQSOTotals^ [DatabaseAddress].NumberLogs := 999; END; END; { Remove database calls that we have logs for } NewDatabaseTotal := 0; FOR DatabaseAddress := 0 TO NumberSprintDatabaseEntries - 1 DO IF SprintDatabaseQSOTotals^ [DatabaseAddress].NumberLogs <> 999 THEN BEGIN SprintDatabase^ [NewDatabaseTotal] := SprintDatabase^ [DatabaseAddress]; SprintDatabaseQSOTotals^ [NewDatabaseTotal].NumberLogs := 0; Inc (NewDatabaseTotal); END; NumberSprintDatabaseEntries := NewDatabaseTotal; WriteLn (NewDatabaseTotal, ' calls found in the database that we don''t have logs for.'); { Look through each log we have and compute totals for the calls that we don't have logs for } FOR AvailableLogAddress := 0 TO NumberAvailableLogs - 1 DO BEGIN IF LoadLogIntoActiveLog (AvailableLogs^ [AvailableLogAddress].Call) THEN IF ActiveLog^.NumberQSOEntries > 0 THEN BEGIN FOR DatabaseAddress := 0 TO NumberSprintDatabaseEntries - 1 DO BEGIN DatabaseCall := SprintDatabase^ [DatabaseAddress].Call; FOR LogAddress := 0 TO ActiveLog^.NumberQSOEntries - 1 DO IF RootCall (ActiveLog^.QSOList [LogAddress].Call) = DatabaseCall THEN BEGIN SprintDatabaseQSOTotals^ [DatabaseAddress].RunningTotal := SprintDatabaseQSOTotals^ [DatabaseAddress].RunningTotal + ActiveLog^.QSOList [LogAddress].ReceivedQSONumber; Inc (SprintDatabaseQSOTotals^ [DatabaseAddress].NumberLogs); END; END; END; END; { Spit out the ones that worked at least 10 guys to the output file } FOR DatabaseAddress := 0 TO NumberSprintDatabaseEntries - 1 DO WITH SprintDatabaseQSOTotals^ [DatabaseAddress] DO BEGIN IF NumberLogs > 10 THEN BEGIN EstimatedTotal := 2 * Round ((RunningTotal / NumberLogs)); Callsign := SprintDatabase^ [DatabaseAddress].Call; WHILE Length (Callsign) < 12 DO CallSign := ' ' + CallSign; WriteLn (FileWrite, Callsign, ' ', EStimatedTotal:3); END; END; Close (FileWrite); NumberSprintDatabaseEntries := 0; { We messed with the list } WriteLn ('Results saved in file MISSING.LOG'); WHILE KeyPressed DO ReadKey; WaitForKeyPressed; END; PROCEDURE GuestOpList; VAR FileRead, FileWrite: TEXT; DirInfo: SearchRec; WriteFileName, Call, OpCall: Str80; FileString: STRING; BEGIN ClearScreenAndTitle ('GUEST OP LIST'); WriteLn ('Generates a list of logs that have an OPERATOR different than the root call'); WriteLn ('of the log. Saves the results to a file.'); WriteLn; WriteFileName := GetResponse ('Enter filename to save results to (none to abort) : '); OpenFileForWrite (FileWrite, WriteFileName); FindFirst ('LOGS\*.LOG', Archive, DirInfo); WHILE DosError = 0 DO BEGIN Call := GetCallFromFilename (DirInfo.Name); IF OpenFileForRead (FileRead, 'logs\' + Call + '.log') THEN BEGIN WHILE NOT Eof (FileRead) DO BEGIN ReadLn (FileRead, FileString); IF Pos ('OPERATOR', FileString) = 1 THEN BEGIN OpCall := PostcedingString (FileString, ':'); GetRidOfPostcedingSpaces (OpCall); WHILE Pos (Chr (0), OpCall) <> 0 DO Delete (OpCall, Pos (Chr (0), OpCall), 1); GetRidOfPrecedingSpaces (OpCall); GetRidOfPostcedingSpaces (OpCall); IF (OpCall <> Call) AND (OpCall <> '') THEN BEGIN WriteLn (Call, ' (', OpCall, ')'); WriteLn (FileWrite, Call, ' (', OpCall, ')'); END; Break; END; END; Close (FileRead); END; FindNext (DirInfo); END; Close (FileWrite); WaitForKeyPressed; END; PROCEDURE PullAddressDataOutOfLogFiles; VAR InputFileName, LogFileName, OutputFileName: Str20; InputFile, OutputFile, LogFile: TEXT; LogFileString, InputFileString, TestString: STRING; BEGIN ClearScreenAndTitle ('PULL ADDRESS DATA OUT OF LOG FILES'); WriteLn ('Uses first entry in each line of the file as the root part of the filename.'); WriteLn; WriteLn ('The output can be sucked into Excel and then saved as an Excel file. Then'); WriteLn ('you open Word and go to Mailings and start a mail merge for labels. You'); WriteLn ('suck in the data from the spreadsheet and then use the Insert Merge Field'); WriteLn ('button to make the layout you want. Update labels and then preview.'); WriteLn; InputFilename := GetResponse ('Enter filename of calls to get data for (none to abort) : '); IF InputFilename = '' THEN Exit; IF NOT OpenFileForRead (InputFile, InputFileName) THEN BEGIN ReportError ('Unable to open ' + InputFileName + '!!'); WaitForKeyPressed; Exit; END; OutputFileName := GetResponse ('Enter filename for output file : '); IF OutputFileName = '' THEN BEGIN Close (InputFile); Exit; END; OpenFileForWrite (OutputFile, OutputFileName); WHILE NOT Eof (InputFile) DO BEGIN ReadLn (InputFile, InputFileString); InputFileString := GetFirstString (InputFileString); IF InputFileString <> '' THEN BEGIN LogFileName := 'logs\' + InputFileString + '.log'; IF OpenFileForRead (LogFile, LogFileName) THEN BEGIN Write (OutputFile, InputFileString); { Tab Key comes later } WHILE NOT Eof (LogFile) DO BEGIN ReadLn (LogFile, LogFileString); TestString := UpperCase (RemoveFirstString (LogFileString)); IF StringHas (TestString, 'ADDRESS') OR (TestString = 'NAME:') THEN BEGIN GetRidOfPrecedingSpaces (LogFileString); Write (OutputFile, TabKey, LogFileString); END; IF TestString = 'QSO:' THEN Break; END; Close (LogFile); WriteLn (OutputFile); END ELSE BEGIN WriteLn (OutputFile, 'No log found for ', InputFileString); WriteLn (OutputFile); END; END; END; Close (InputFile); Close (OutputFile); END; PROCEDURE FixNumbersInLog; VAR InputFileName, OutputFileName: Str80; FileString: STRING; InputFile, OutputFile: TEXT; Entry, QSONumber: INTEGER; BEGIN InputFileName := GetResponse ('Input file (none to exit) : '); IF InputFileName = '' THEN Exit; OutputFileName := GetResponse ('Output file (none to exit) : '); IF OutputFileName = '' THEN Exit; IF NOT OpenFileForRead (InputFile, InputFileName) THEN BEGIN ReportError ('Unable to find ' + InputFileName); WaitForKeyPressed; Exit; END; OpenFileForWrite (OutputFile, OutputFileName); QSONumber := 1; WHILE NOT Eof (InputFile) DO BEGIN ReadLn (InputFile, FileString); IF StringHas (FileString, 'QSO: ') THEN BEGIN FOR Entry := 1 TO 6 DO Write (OutputFile, RemoveFirstString (FileSTring), ' '); RemoveFirstString (FileString); { Get rid of sent info } WriteLn (OutputFile, QSONumber, ' ', FileString); Inc (QSONumber); END ELSE WriteLn (OutputFile, FileString); END; Close (OutputFile); Close (InputFile); WriteLn ('There were ' , QSONumber, ' QSOs saved to ', OutputFileName); WaitForKeyPressed; END; PROCEDURE UpdateBinFiles (ProcessAllFiles: BOOLEAN); VAR DirInfo: SearchRec; FileName: Str80; NumberLogsFound: INTEGER; Call: CallString; BEGIN IF ProcessAllFiles THEN WriteLn ('Creating BIN Files for all logs') ELSE WriteLn ('Creating BIN files for any new logs'); FindFirst ('logs\*.log', Archive, DirInfo); NumberLogsFound := 0; WHILE DosError = 0 DO BEGIN FileName := DirInfo.Name; IF Copy (FileName, 1, 1) <> '#' THEN BEGIN Inc (NumberLogsFound); Call := GetCallFromFilename (FileName); IF (NOT BinaryFileOkayToUse (Call)) OR ProcessAllFiles THEN BEGIN Write ('Processing ', Call); ConvertASCIIToBinaryFile (Call); END; END; FindNext (DirInfo); END; END; PROCEDURE ChangeStartTime; BEGIN ClearScreenAndTitle ('CHANGE START TIME'); WriteLn; WriteLn ('Actually - since we read in the .LOG files when the program starts now - you need to change the start'); WriteLn ('time before that happens!! So - you will now exit the program and can enter it at the start. You'); WriteLn ('might want to delete all of the .bin files before to make sure they are all created with the right'); WriteLn ('start time.'); WaitForKeyPressed; Halt; END; PROCEDURE UtilityMenu; BEGIN REPEAT ClrScr; TextColor (Yellow); WriteLnCenter ('UTILITY MENU'); WriteLn; TextColor (Cyan); WriteLn; WriteLn (' A - All of the multipliers active in the contest.'); WriteLn (' B - Band changes for a log.'); WriteLn (' C - Certificate File Update.'); WriteLn (' D - Determine if a ASCII list of callsigns look real.'); WriteLn (' F - Filter bad calls out of database.'); WriteLn (' G - Guest op list.'); WriteLn (' H - Hour report. Shows activity for each 10 minutes.'); WriteLn (' L - Look for calls that were busted a lot - maybe unstable or bad data?'); WriteLn (' M - Look for munged logs.'); WriteLn (' N - Fix Numbers in log.'); WriteLn (' O - Look for missing logs.'); WriteLn (' P - Pull addresses out of log files.'); IF RTTYMode THEN WriteLn (' R - Toggle RTTY mode (RTTY Mode = TRUE)') ELSE WriteLn (' R - Toggle RTTY mode (RTTY Mode = FALSE)'); WriteLn (' S - Generate soapbox from Cabrillo logs.'); WriteLn (' T - Reconstitute a log. Generate it from the other logs.'); WriteLn (' U - Unstable report. Takes list of calls and shows stability.'); WriteLn (' V - Verify that all calls that we have logs for appear in database.'); WriteLn (' W - Write contents of .bin file to .TXT'); WriteLn (' X - Exit Utility Menu.'); WriteLn (' Z - Grep tool (harvest email addresses)'); IF Debug THEN WriteLn (' 1 - Debug IS ON!! - Use 1 key to disable') ELSE WriteLn (' 1 - Debug if OFF - use 1 key to enable'); WriteLn; Key := UpCase (GetKey (' Enter desired command : ')); CASE Key OF 'A': ListActiveMultipliers; 'B': BandChangesForALog; 'C': CertificateFileUpdate; 'D': DetermineIfCallsLookReal; 'F': FilterBadCallsOutOfDatabase; 'G': GuestOpList; 'H': QSOByHourReport; 'L': LookForHighFrequencyBustedCalls; 'M': FindMungedLogs; 'N': FixNumbersInLog; 'O': FindMissingLogs; 'P': PullAddressDataOutOfLogFiles; 'R': RTTYMode := NOT RTTYMode; 'S': GenerateSoapBox; 'T': ReconstitutedLog; 'U': UnstableReport; 'V': VerifyAllReceivedLogsAreInDatabase; 'W': WriteBINFileToText; 'X': Exit; 'Z': GrepTool; '1': BEGIN Debug := NOT Debug; IF Debug THEN DeleteFile (SprintLogFileName); END; END; UNTIL False; END; BEGIN CountryTable.CountryMode := ARRLCountryMode; IF NOT CountryTable.LoadInCountryFile THEN Halt; LoadInNameEQFile; UpdateBinFiles (False); LoadInAllBinFiles; OriginalTextMode := LastMode; TextMode (C80 + Font8X8); REPEAT ClrScr; TextColor (Yellow); WriteLnCenter ('N6TR Sprint Checking Program Version 2.61'); WriteLnCenter ('30 October 2014'); WriteLn; TextColor (Cyan); WriteLn; WriteLn (' A - Check all of the logs and generate report files.'); WriteLn (' B - Look for bad calls in the report files.'); WriteLn (' C - Collect score information from report files into a table.'); WriteLn (' D - Collect score information in WA7BNM perferred format.'); WriteLn (' E - Estimate number of QSOs for stations in database.'); WriteLn (' G - Global log process. Look at all logs for new database calls.'); WriteLn (' H - Help'); WriteLn (' J - Automatic bad call detection.'); WriteLn (' K - Check .LOG files.'); WriteLn (' P - Report card of partial log.'); WriteLn (' R - Generate report card for a log (same as A but only for one log).'); WriteLn (' S - Sort Sprint Database file by name/QTH.'); WriteLn (' T - Change start time - currently = ', StartTime); WriteLn (' U - Utility Menu.'); WriteLn (' X - Exit Program.'); WriteLn (' Y - Append busted information to report files.'); WriteLn; Key := UpCase (GetKey (' Enter desired command : ')); CASE Key OF 'A': CheckAllLogs; 'B': BustCalls; 'C': CollectScoreInformation; 'D': CollectScoreInformationInWA7BNMFormat; 'E': EstimateNumberOfQSOsForDatabaseStations; 'G': GlobalLogProcess; 'H': Help; 'J': AutomaticBadCallDetection; 'K': CheckSPRFiles; 'P': SpecialReportCard; 'R': ReportCard; 'S': SortSprintDatabaseFile (False); 'T': ChangeStartTime; 'U': UtilityMenu; 'X': BEGIN TextMode (OriginalTextMode); ClrScr; Exit; END; 'Y': BustInfo; END; UNTIL False; END.