feat(tci): key the radio ourselves and push a tone, to see what it wants
A first real transmission settled one question and raised a better one. With the receive stream open and six seconds of transmit, the radio sent 282 frames of receive audio and NOTHING else: no chrono, no transmit audio. So the chrono the documentation describes is not offered to a client that merely happens to be connected while the operator keys the microphone, and waiting for it to appear is waiting for nothing. The reading that fits is that the radio asks for audio when the transmission is the CLIENT'S and takes the microphone when it is the operator's — which makes the experiment obvious. Key it from here, push a 1 kHz tone, and watch. Chrono frames appearing gives their size and cadence by measurement instead of by guesswork; no chrono but a tone on the meter is just as useful, because then the pacing is optional and the voice keyer can push frames at the rate the stream already runs at. A tone rather than silence so the answer shows on the power meter and not only in the log. It transmits, so: an explicit button inside a warning box, five seconds, capped at ten, and every path out unkeys — including the panic that has not happened yet and a socket that dies mid-tone. A transmitter left keyed by a defect is the one fault here that would reach somebody else's band. Writes are now serialised too. send() held the lock only long enough to read the connection, which was enough while every command came from the poll loop; a stream of audio frames from a second goroutine is not, and gorilla panics on a concurrent write rather than failing quietly.
This commit is contained in:
@@ -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<any>({ 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 && <span className="text-[11px] font-mono text-muted-foreground truncate">{tciRecPath}</span>}
|
||||
</div>
|
||||
)}
|
||||
{/* 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. */}
|
||||
<div className="rounded border border-caution-border bg-caution-muted p-2 space-y-1.5">
|
||||
<Button variant="outline" size="sm" className="h-8" disabled={tciTXBusy}
|
||||
onClick={() => {
|
||||
setTciTXBusy(true);
|
||||
ProbeTCITransmit(5)
|
||||
.catch((e: any) => setTciAudio((s: any) => ({ ...s, last_err: String(e?.message ?? e) })))
|
||||
.finally(() => setTciTXBusy(false));
|
||||
}}>
|
||||
{tciTXBusy ? t('aud.tciTXBusy') : t('aud.tciTX')}
|
||||
</Button>
|
||||
<p className="text-[11px] text-caution-muted-foreground">{t('aud.tciTXWarn')}</p>
|
||||
</div>
|
||||
{!!tciAudio.last_err && <p className="text-[11px] text-danger">{tciAudio.last_err}</p>}
|
||||
<label className="flex items-start gap-2 text-xs cursor-pointer">
|
||||
<Checkbox className="mt-0.5" checked={tciRec}
|
||||
|
||||
Reference in New Issue
Block a user