feat: Implemented UDP Outbound Adif message, freq to pstrotator
This commit is contained in:
@@ -176,7 +176,7 @@ const (
|
||||
keyWKUsePTT = "winkeyer.use_ptt"
|
||||
keyWKSerialEcho = "winkeyer.serial_echo"
|
||||
keyWKMacros = "winkeyer.macros" // JSON array of {label,text}
|
||||
keyWKEngine = "winkeyer.engine" // "winkeyer" | "tci"
|
||||
keyWKEngine = "winkeyer.engine" // "winkeyer" | "icom" | "tci"
|
||||
keyWKEscClears = "winkeyer.esc_clears_call" // ESC also clears the callsign
|
||||
keyWKSendOnType = "winkeyer.send_on_type" // key characters live as typed
|
||||
|
||||
@@ -459,6 +459,12 @@ type App struct {
|
||||
opLat float64
|
||||
opLon float64
|
||||
opSet bool
|
||||
opCall string // active profile callsign, cached for outbound UDP emitters
|
||||
|
||||
// Dedup for the frequency/mode outbound UDP emitters (PstRotator, N1MM): only
|
||||
// send when the frequency or mode actually changes.
|
||||
udpLastFreqHz int64
|
||||
udpLastMode string
|
||||
}
|
||||
|
||||
// gridToLatLon parses a Maidenhead locator (4 or 6 chars) and returns the
|
||||
@@ -534,6 +540,7 @@ func (a *App) refreshOperatorGrid() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
a.opCall = strings.ToUpper(strings.TrimSpace(p.Callsign))
|
||||
lat, lon, ok := gridToLatLon(p.MyGrid)
|
||||
if !ok {
|
||||
return
|
||||
@@ -543,6 +550,36 @@ func (a *App) refreshOperatorGrid() {
|
||||
a.opSet = true
|
||||
}
|
||||
|
||||
// emitRadioUDP forwards the rig's frequency/mode to any enabled outbound UDP
|
||||
// emitters (PstRotator, N1MM RadioInfo). Deduped so it only fires when the
|
||||
// operating frequency or mode actually changes. The send runs off the CAT
|
||||
// goroutine so a slow/failed datagram can't stall polling.
|
||||
func (a *App) emitRadioUDP(s cat.RigState) {
|
||||
if a.udp == nil {
|
||||
return
|
||||
}
|
||||
rx := s.FreqHz
|
||||
if s.RxFreqHz != 0 { // split: RxFreqHz is the active/listening VFO
|
||||
rx = s.RxFreqHz
|
||||
}
|
||||
if rx <= 0 {
|
||||
return
|
||||
}
|
||||
if rx == a.udpLastFreqHz && s.Mode == a.udpLastMode {
|
||||
return
|
||||
}
|
||||
a.udpLastFreqHz = rx
|
||||
a.udpLastMode = s.Mode
|
||||
rs := udp.RadioState{
|
||||
StationName: a.opCall,
|
||||
OpCall: a.opCall,
|
||||
RxFreqHz: rx,
|
||||
TxFreqHz: s.FreqHz,
|
||||
Mode: s.Mode,
|
||||
}
|
||||
go a.udp.EmitRadioState(rs)
|
||||
}
|
||||
|
||||
// dxccAdapter bridges *dxcc.Manager to the lookup.DXCCResolver interface
|
||||
// without making the lookup package import dxcc.
|
||||
type dxccAdapter struct{ m *dxcc.Manager }
|
||||
@@ -726,11 +763,13 @@ func (a *App) startup(ctx context.Context) {
|
||||
fmt.Printf("OpsLog: clublog cty.xml loaded — %d exceptions (%s)\n", n, d)
|
||||
}
|
||||
}()
|
||||
// CAT manager: emit pushes state to the frontend via Wails events.
|
||||
// CAT manager: emit pushes state to the frontend via Wails events, and
|
||||
// forwards frequency/mode to any outbound UDP emitters (PstRotator, N1MM).
|
||||
a.cat = cat.NewManager(func(s cat.RigState) {
|
||||
if a.ctx != nil {
|
||||
wruntime.EventsEmit(a.ctx, "cat:state", s)
|
||||
}
|
||||
a.emitRadioUDP(s)
|
||||
})
|
||||
a.reloadCAT()
|
||||
|
||||
@@ -1583,6 +1622,10 @@ func (a *App) AddQSO(q qso.QSO) (id int64, err error) {
|
||||
a.extsvc.OnQSOLogged(id)
|
||||
}
|
||||
a.maybeAutoSendEQSL(q)
|
||||
// Forward the ADIF of this QSO to any outbound "ADIF message" UDP rows.
|
||||
if a.udp != nil {
|
||||
go a.udp.EmitLoggedADIF(adif.SingleRecordADIF(q))
|
||||
}
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
@@ -7866,6 +7909,42 @@ func (a *App) IcomSetXITOn(on bool) error {
|
||||
return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetXITOn(on) })
|
||||
}
|
||||
|
||||
// IcomSendCW keys a CW message through the rig's internal keyer (CI-V 0x17).
|
||||
func (a *App) IcomSendCW(text string) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
err := a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SendCW(text) })
|
||||
if err != nil {
|
||||
applog.Printf("icom cw: IcomSendCW(%q) failed: %v", text, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// IcomStopCW aborts the CW message currently being sent.
|
||||
func (a *App) IcomStopCW() error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.StopCW() })
|
||||
}
|
||||
|
||||
// IcomSetKeySpeed sets the CW keyer speed in WPM.
|
||||
func (a *App) IcomSetKeySpeed(wpm int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetKeySpeed(wpm) })
|
||||
}
|
||||
|
||||
// IcomSetBreakIn sets CW break-in (0=OFF, 1=SEMI, 2=FULL).
|
||||
func (a *App) IcomSetBreakIn(mode int) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
}
|
||||
return a.cat.IcomDo(func(ic cat.IcomController) error { return ic.SetBreakIn(mode) })
|
||||
}
|
||||
|
||||
func (a *App) IcomSetPTT(on bool) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("cat not initialized")
|
||||
@@ -9092,7 +9171,7 @@ type WKMacro struct {
|
||||
type WinkeyerSettings struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
winkeyer.Config
|
||||
Engine string `json:"engine"` // keyer backend: "winkeyer" | "tci"
|
||||
Engine string `json:"engine"` // keyer backend: "winkeyer" | "icom" (rig keyer via CI-V) | "tci"
|
||||
EscClearsCall bool `json:"esc_clears_call"` // ESC also resets the callsign
|
||||
SendOnType bool `json:"send_on_type"` // key chars live as typed
|
||||
Macros []WKMacro `json:"macros"`
|
||||
|
||||
Reference in New Issue
Block a user