feat(decodes): answer a station on click, DT and Freq, badge filters
Clicking a decode now ANSWERS it. It sends WSJT-X/MSHV a Reply message (type 4), which is the same thing as double-clicking the line in their own Band Activity window: the application looks the decode up, sets its transmit frequency to the caller's and starts the exchange. It deliberately does not tune the radio, which is what it did before and why nothing happened. On FT8 the whole band sits inside one passband, so moving the dial changes nothing about who gets answered - the decision belongs to the decoding application, and the Reply is the only way to hand it over. Tuning would also just fight it for the VFO. The entry is still filled so the QSO can be logged here. The reply is routed by PROGRAM ID, not by listener: two receivers can share one multicast group, and answering a station heard on the 6 m instance by talking to the 20 m one would start a call on the wrong band. It goes to the address that instance's packets actually arrive from - a multicast listener must answer the sender, never the group. WSJT-X matches the reply against its own decode list, so the payload replays the decode field for field: time, snr, delta time, audio offset, mode and message text. Two columns added, DT and Freq - the audio offset inside the passband, not the RF frequency, which is the same for every station in the list and says nothing. Past about two seconds DT takes a warning tint: that station is drifting out of the window. The transmit strip. "You cannot see what you are sending, or who you are calling" - two separate faults. The message was only ever threaded into its period, and in FT8 you transmit in the slots you are NOT receiving in, so its period had no decodes and the whole line was dropped; a transmit slot now creates its period. And the state is a strip of its own at the top, because it is the one thing on the screen that is about the operator rather than the band. It is fed by every Status rather than only by one carrying transmit text, so it can still name the station being called on MSHV and older JTDX builds, which stop before tx_message in the Status payload. "New only" became per-category badges, in the colours and the vocabulary of the Chase New panel. None lit shows the whole band - this is a decode log first, and a panel that opened by hiding most of the traffic would be lying about what is on the air.
This commit is contained in:
@@ -127,6 +127,13 @@ type Event struct {
|
||||
DecodeTRPeriod int
|
||||
DecodeDial int64 // dial frequency the decode was heard on, for the band
|
||||
DecodeOffAir bool // decoded from a file rather than off the air
|
||||
// The three fields below are shown in the panel AND replayed verbatim when
|
||||
// answering the station — WSJT-X matches a Reply against its own decode list,
|
||||
// so every one has to go back exactly as it came.
|
||||
DecodeDT float64 // seconds into the slot the transmission started
|
||||
DecodeAudioHz int64 // audio offset inside the passband
|
||||
DecodeMs uint32 // the decode's raw ms-since-midnight, as sent
|
||||
DecodeLowConf bool
|
||||
// ProgramID is the sending application's own id ("WSJT-X", "MSHV", or
|
||||
// "WSJT-X - 2" for a second instance started with --rig-name). It is what
|
||||
// tells two receivers apart on one multicast group — and it is the address a
|
||||
@@ -173,6 +180,9 @@ type Server struct {
|
||||
// trPeriod is the T/R period (seconds) from each program's last Status —
|
||||
// what tells a decode which slot it belongs to.
|
||||
trPeriod map[string]int
|
||||
// lastFrom is the address each program's packets arrive from — where a Reply
|
||||
// has to be sent. See SendReply.
|
||||
lastFrom map[string]*net.UDPAddr
|
||||
lastDX string // WSJT: last non-empty DX Call seen, to detect a clear
|
||||
|
||||
// badPkts counts datagrams this listener could not parse, so the diagnostic
|
||||
@@ -397,6 +407,18 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// Where this application's packets come from, so a Reply can be sent back
|
||||
// to it. Per PROGRAM, not per listener: two receivers share one multicast
|
||||
// group, and a reply must reach the one that heard the station — and it
|
||||
// must go to the sender's own address, never to the group.
|
||||
if w.ProgramID != "" && remote != nil {
|
||||
s.mu.Lock()
|
||||
if s.lastFrom == nil {
|
||||
s.lastFrom = map[string]*net.UDPAddr{}
|
||||
}
|
||||
s.lastFrom[w.ProgramID] = remote
|
||||
s.mu.Unlock()
|
||||
}
|
||||
// Status carries the current dial frequency; remember it so Decode audio
|
||||
// offsets can be turned into RF frequencies for the panadapter.
|
||||
if w.FreqHz > 0 && !w.IsDecode {
|
||||
@@ -417,11 +439,14 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
|
||||
s.mu.Unlock()
|
||||
}
|
||||
if !w.IsDecode && (w.TxMessage != "" || w.DECall != "") {
|
||||
// What the operator is sending. Carried on every Status, so the
|
||||
// consumer sees it change as the QSO progresses.
|
||||
// What the operator is sending, and to whom. Carried on every Status,
|
||||
// so the consumer sees it change as the QSO progresses. The program id
|
||||
// travels with it because a second receiver has a transmit state of
|
||||
// its own.
|
||||
ev.TxMessage = w.TxMessage
|
||||
ev.Transmitting = w.Transmitting
|
||||
ev.DECall = w.DECall
|
||||
ev.ProgramID = w.ProgramID
|
||||
}
|
||||
if w.IsDecode {
|
||||
s.mu.Lock()
|
||||
@@ -446,6 +471,10 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
|
||||
ev.DecodeDial = dial
|
||||
ev.DecodeOffAir = w.OffAir
|
||||
ev.ProgramID = w.ProgramID
|
||||
ev.DecodeDT = w.DeltaTime
|
||||
ev.DecodeAudioHz = w.DeltaFreqHz
|
||||
ev.DecodeMs = w.DecodeMsSinceMidnight
|
||||
ev.DecodeLowConf = w.LowConfidence
|
||||
break
|
||||
}
|
||||
// Only a logged QSO is worth a line — WSJT-X/MSHV send a Status packet
|
||||
|
||||
Reference in New Issue
Block a user