diff --git a/app_tci_audio.go b/app_tci_audio.go index be2b283..c65ea1a 100644 --- a/app_tci_audio.go +++ b/app_tci_audio.go @@ -51,3 +51,30 @@ func (a *App) GetTCIAudioStatus() cat.TCIAudioStatus { st, _ := a.cat.TCIAudioState() return st } + +// ProbeTCITransmit keys the radio and pushes a tone over TCI, to find out +// whether the transmit half of the stream works at all. +// +// A first real transmission proved that the radio sends nothing extra while the +// OPERATOR keys it — 282 receive frames and no chrono over six seconds — so the +// only way forward is to key it from here and watch. Everything interesting +// lands in the log; see internal/cat/tci_tx_probe.go for what the two possible +// answers mean. +// +// THIS TRANSMITS. The button that calls it says so, and the radio must be on a +// dummy load. +func (a *App) ProbeTCITransmit(seconds int) error { + if a.cat == nil { + return fmt.Errorf("CAT not initialized") + } + return a.cat.TCIAudioDo(func(t cat.TCIAudioController) error { + p, ok := t.(interface { + ProbeTXStream(seconds int, toneHz float64) error + }) + if !ok { + return fmt.Errorf("this CAT backend is not a TCI radio") + } + applog.Printf("tci: TX PROBE requested (%d s) — the radio should be on a dummy load", seconds) + return p.ProbeTXStream(seconds, 1000) + }) +} diff --git a/frontend/src/components/SettingsModal.tsx b/frontend/src/components/SettingsModal.tsx index 3e8ff26..db1ae37 100644 --- a/frontend/src/components/SettingsModal.tsx +++ b/frontend/src/components/SettingsModal.tsx @@ -58,7 +58,7 @@ import { GetFolderSync, SaveFolderSync, PickFolderSyncFolder, GetFolderSyncStatus, SyncFolderNow, GetRelayAuto, SaveRelayAuto, GetStationDevices, GetAwardDefs, GetTrackedAwards, SaveTrackedAwards, - GetBandOpenSettings, SaveBandOpenSettings, GetGridScopeSettings, SaveGridScopeSettings, GetPSKReporterStatus, GetChaseNewGrids, SetChaseNewGrids, GetChaseNew, SetChaseNew, GetGridCacheStatus, GetLinkedAmps, SetLinkedAmps, GetSpotTTLMinutes, SetSpotTTLMinutes, GetSpotMax, SetSpotMax, StartTCIAudio, StopTCIAudio, GetTCIAudioStatus, RecordTCIAudio, GetTCIRecordAudio, SetTCIRecordAudio, + GetBandOpenSettings, SaveBandOpenSettings, GetGridScopeSettings, SaveGridScopeSettings, GetPSKReporterStatus, GetChaseNewGrids, SetChaseNewGrids, GetChaseNew, SetChaseNew, GetGridCacheStatus, GetLinkedAmps, SetLinkedAmps, GetSpotTTLMinutes, SetSpotTTLMinutes, GetSpotMax, SetSpotMax, StartTCIAudio, StopTCIAudio, GetTCIAudioStatus, RecordTCIAudio, GetTCIRecordAudio, SetTCIRecordAudio, ProbeTCITransmit, } from '../../wailsjs/go/main/App'; import type { profile as profileModels } from '../../wailsjs/go/models'; import type { LookupSettingsForm, StationSettingsForm, ListsSettingsForm, ModePresetForm } from '@/types'; @@ -1948,6 +1948,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan // done for nothing. const [tciAudio, setTciAudio] = useState({ running: false, sample_rate: 0, frames: 0, peak_db: -99 }); const [tciRecBusy, setTciRecBusy] = useState(false); + const [tciTXBusy, setTciTXBusy] = useState(false); // Whether the QSO recorder takes its audio from the radio's own stream. const [tciRec, setTciRec] = useState(false); useEffect(() => { GetTCIRecordAudio().then((v: boolean) => setTciRec(!!v)).catch(() => {}); }, []); @@ -6601,6 +6602,23 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan {!!tciRecPath && {tciRecPath}} )} + {/* The transmit experiment. It KEYS THE RADIO, which is why it is + worded as a warning and not as another test button: a first pass + on real hardware showed the radio sends nothing extra while the + operator keys it by hand, so the only way to learn what it wants + is to key it from here and push a tone. */} +
+ +

{t('aud.tciTXWarn')}

+
{!!tciAudio.last_err &&

{tciAudio.last_err}

}