feat(tci): a control console for the SunSDR
TCI already carries the frequency, the mode, the meters and now the audio. It also carries everything else about the radio — and OpsLog was logging most of it once as '(unhandled once)' and throwing it away. The console is mostly a place to put what was already arriving. That makes it the cheapest panel here, and it is worth saying why. A K3 console costs a command and a reply for every value it shows, which is why it reads its settings in a rotation and its meters only while on screen. TCI PUSHES: the radio announces its drive, its filters, its noise blanker and the rest on connect, and again whenever any of them changes — including when the operator changes them in ExpertSDR3's own window, which this panel therefore follows without asking anything. What it drives: drive and tune drive, mic gain, TUNE, volume, mute, squelch and its threshold, NB, NR, ANF, APF, AGC speed, the passband, RIT and XIT with their offsets, and the VFO lock. The S-meter is a real dBm reading, so its S units are arithmetic rather than the calibration guess a K3's meter needs. Setters never update the cached state. The radio answers with the new value, and taking its word is what keeps the panel honest when a setting is refused, clamped, or changed at the radio a second later — the one exception being a slider mid-drag, held for 900 ms so it is not dragged back by its own echo. Capped width and centred, like the other consoles. Also offered as a docked pane — and the Elecraft console is offered there too now: App has always had that pane, Settings simply never listed it.
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
// The TCI control console — bindings.
|
||||
//
|
||||
// Thin on purpose: the state is a snapshot the radio pushed and the setters are
|
||||
// one command each. Everything interesting is in internal/cat/tci_panel.go.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"hamlog/internal/cat"
|
||||
)
|
||||
|
||||
// GetTCIPanel returns the console state. Connected=false when the active CAT
|
||||
// backend is not a TCI radio, so the frontend has one thing to look at rather
|
||||
// than an error to distinguish from a disconnected radio.
|
||||
func (a *App) GetTCIPanel() cat.TCIPanelState {
|
||||
if a.cat == nil {
|
||||
return cat.TCIPanelState{}
|
||||
}
|
||||
st, _ := a.cat.TCIPanelState()
|
||||
return st
|
||||
}
|
||||
|
||||
// tciDo is the shape every setter below takes.
|
||||
func (a *App) tciDo(fn func(cat.TCIPanelController) error) error {
|
||||
if a.cat == nil {
|
||||
return fmt.Errorf("CAT not initialized")
|
||||
}
|
||||
return a.cat.TCIPanelDo(fn)
|
||||
}
|
||||
|
||||
// SetTCIDrive sets the transmit drive (0-100).
|
||||
func (a *App) SetTCIDrive(v int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetDrive(v) })
|
||||
}
|
||||
|
||||
// SetTCITuneDrive sets the drive TUNE uses (0-100).
|
||||
func (a *App) SetTCITuneDrive(v int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetTuneDrive(v) })
|
||||
}
|
||||
|
||||
// SetTCIMicLevel sets the microphone gain (0-100).
|
||||
func (a *App) SetTCIMicLevel(v int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetMicLevel(v) })
|
||||
}
|
||||
|
||||
// SetTCIVolume sets the receive volume in dB (0 down to -60).
|
||||
func (a *App) SetTCIVolume(db int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetVolume(db) })
|
||||
}
|
||||
|
||||
// SetTCIMute mutes or unmutes the receiver.
|
||||
func (a *App) SetTCIMute(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetMute(on) })
|
||||
}
|
||||
|
||||
// SetTCIAGC picks the AGC speed: off, long, slow, med, fast.
|
||||
func (a *App) SetTCIAGC(mode string) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetAGC(mode) })
|
||||
}
|
||||
|
||||
// SetTCISquelch turns the squelch on or off.
|
||||
func (a *App) SetTCISquelch(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetSquelch(on) })
|
||||
}
|
||||
|
||||
// SetTCISquelchLevel sets the squelch threshold in dBm.
|
||||
func (a *App) SetTCISquelchLevel(v int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetSquelchLevel(v) })
|
||||
}
|
||||
|
||||
// SetTCINB, SetTCINR, SetTCIANF and SetTCIAPF switch the receive processing.
|
||||
func (a *App) SetTCINB(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetNB(on) })
|
||||
}
|
||||
func (a *App) SetTCINR(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetNR(on) })
|
||||
}
|
||||
func (a *App) SetTCIANF(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetANF(on) })
|
||||
}
|
||||
func (a *App) SetTCIAPF(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetAPF(on) })
|
||||
}
|
||||
|
||||
// SetTCIFilter sets the passband edges in Hz.
|
||||
func (a *App) SetTCIFilter(lo, hi int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetFilter(lo, hi) })
|
||||
}
|
||||
|
||||
// SetTCIRIT / SetTCIXIT switch the offsets on; the Offset calls move them.
|
||||
func (a *App) SetTCIRIT(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetRIT(on) })
|
||||
}
|
||||
func (a *App) SetTCIXIT(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetXIT(on) })
|
||||
}
|
||||
func (a *App) SetTCIRITOffset(hz int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetRITOffset(hz) })
|
||||
}
|
||||
func (a *App) SetTCIXITOffset(hz int) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetXITOffset(hz) })
|
||||
}
|
||||
|
||||
// SetTCILock locks the radio's VFO knob.
|
||||
func (a *App) SetTCILock(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetLock(on) })
|
||||
}
|
||||
|
||||
// SetTCITune starts or stops the tune carrier.
|
||||
//
|
||||
// IT TRANSMITS, and at tune_drive rather than at drive — which is why the panel
|
||||
// shows those two numbers next to the button rather than hiding one of them in
|
||||
// a menu.
|
||||
func (a *App) SetTCITune(on bool) error {
|
||||
return a.tciDo(func(t cat.TCIPanelController) error { return t.SetTune(on) })
|
||||
}
|
||||
Reference in New Issue
Block a user