Skip to content

Commit

Permalink
Fix problem where ghosted calls are not added to log
Browse files Browse the repository at this point in the history
This is the third of three changes addressing the issue of
caller ghosting. A ghosted call was originally handled as
a failed QSO after the Dx Station timeout out due to User
inactivity. Once failed, it was deleted from the simulation
before the QSO was logged by the user. With this fix, DxStation
will stop sending and remaining in the set of active stations.
This allows the QSO to be logged as usual by the user.

This fixes issue #200. See Issue #200 for more information.
  • Loading branch information
w7sst committed May 24, 2024
1 parent f072b85 commit 0c6dfa3
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 8 deletions.
38 changes: 35 additions & 3 deletions DxOper.pas
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ TDxOperator = class
// Patience is increased with calls to MorePatience.
RepeatCnt: integer;
State: TOperatorState;
function IsGhosting: boolean;
function GetSendDelay: integer;
function GetReplyTimeout: integer;
function GetWpm(out AWpmC : integer) : integer;
Expand All @@ -114,6 +115,25 @@ implementation
{ TDxOperator }


{
The notion of ghosting refers to a DxOperator who has run out of
Patience and is leaving the QSO because the User has failed to respond.
This will occur if the User does not respond or continue to interact with
this DxOperator. A station is considered ghosting whenever Patience = 0.
When a DxStation is ghosting, it will:
- leaving the QSO because User did not complete QSO
- will not send additional transmissions to the user
- will retain in set of active stations so it can still receive messages
from the user. Most often, it is waiting for the final 'TU' message.
- if 'TU' is received, then the station can be added to the log.
}
function TDxOperator.IsGhosting: boolean;
begin
Result := Patience = 0;
end;


//Delay before reply, keying speed and exchange number are functions
//of the operator's skills

Expand Down Expand Up @@ -189,14 +209,21 @@ function TDxOperator.GetReplyTimeout: integer;
{
DecPatience is typically called after an evTimeout event.
The TDxOperator.Patience value is decremented down to zero.
When this count reaches zero, the DxStation is deleted from the simulation.
When this count reaches zero, the DxStation will start "ghosting" and
stop transmitting. The ghosting station will remain active so it can
receive final messages from user, logged and deleted from the simulation.
}
procedure TDxOperator.DecPatience;
begin
if State = osDone then Exit;

Dec(Patience);
if Patience < 1 then State := osFailed;
if Patience > 0 then
Dec(Patience);

// starting in v1.85, caller ghosting will occur when a QSO has started, but
// has not yet completed. If the QSO has not yet started, set State=osFailed.
if (Patience < 1) and (State in [osNeedPrevEnd, osNeedQso]) then
State := osFailed;
end;


Expand Down Expand Up @@ -451,6 +478,11 @@ procedure TDxOperator.MsgReceived(AMsg: TStationMessages);

function TDxOperator.GetReply: TStationMessage;
begin
// A ghosting station (Patience=0) will not send any additional messages
assert(not IsGhosting, 'this should not be called when ghosting');
if IsGhosting then
Result := msgNone
else
case State of
osNeedPrevEnd, osDone, osFailed: Result := msgNone;
osNeedQso: Result := msgMyCall;
Expand Down
30 changes: 25 additions & 5 deletions DxStn.pas
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,18 @@ procedure TDxStation.ProcessEvent(AEvent: TStationEvent);
Free;
Exit;
end;
State := stPreparingToSend;

if Oper.IsGhosting then
begin
// if the operator is ghosting, this station will stop transmitting.
// force this station's state into stListening mode so it can
// receive final messages from the operator.
State := stListening;
end
else
State := stPreparingToSend;
end;

//preparations to send are done, now send
if State = stPreparingToSend then
for i:=1 to Oper.RepeatCnt do SendMsg(Oper.GetReply)
Expand Down Expand Up @@ -173,18 +183,28 @@ procedure TDxStation.ProcessEvent(AEvent: TStationEvent);
Mainform.sbar.Caption).Substring(0, 80);
Free;
Exit;
end
else
end;

if Oper.IsGhosting then begin
// if the operator is ghosting, this station will stop transmitting.
// force this station's state into stListening mode so it can
// receive final messages from the operator.
State := stListening;
end
else begin
TimeOut := Oper.GetSendDelay; //reply or switch to standby
State := stPreparingToSend;
State := stPreparingToSend;
end;
end;

evMeStarted:
//If we are not sending, we can start copying
//Cancel timeout, he is replying
begin
if State <> stSending then
if State <> stSending then begin
assert(State in [stPreparingToSend, stListening]);
State := stCopying;
end;
TimeOut := NEVER;
end;
end;
Expand Down
15 changes: 15 additions & 0 deletions Station.pas
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ interface
msgQrl, msgQrl2, msqQsy, msgAgn);

TStationMessages = set of TStationMessage;

{
TStationState represents the operational states of a Station.
stListening
Station is waiting for Operator (the User) to send a message.
stCopying
Station is in this state while Operator's message is transmitted.
stPreparingToSend
Station is preparing a new message to be sent to the User.
After a brief delay time, this message is transmitted.
stSending
Station is sending it's message to the User.
}
TStationState = (stListening, stCopying, stPreparingToSend, stSending);
TStationEvent = (evTimeout, evMsgSent, evMeStarted, evMeFinished);

Expand Down Expand Up @@ -201,6 +215,7 @@ function TStation.GetBlock: TSingleArray;

procedure TStation.SendMsg(AMsg: TStationMessage);
begin
assert(State in [stPreparingToSend, stSending, stListening]);
if Envelope = nil then Msg := [];
if AMsg = msgNone then begin State := stListening; Exit; End;
Include(Msg, AMsg);
Expand Down

0 comments on commit 0c6dfa3

Please sign in to comment.