package main // TCI receive audio — bindings. // // A SunSDR carries its receive audio on the same WebSocket as its commands, so // OpsLog can take it directly instead of asking the operator to install a // virtual audio cable and wire ExpertSDR's output into it. This is the first // half: RECEIVE only, which is what the QSO recorder and the CW decoder need. // Transmit audio (the voice keyer) is the other half and is not here yet — it // has to answer the radio's chrono packets at the right pace, and that is worth // doing once the receive side has proved the format on a real radio. import ( "fmt" "hamlog/internal/applog" "hamlog/internal/cat" ) // StartTCIAudio opens the receive-audio stream for one receiver. // // rate 0 means 48 kHz, which is what ExpertSDR streams by default and what the // recorder wants anyway. func (a *App) StartTCIAudio(rx, rate int) error { if a.cat == nil { return fmt.Errorf("CAT not initialized") } applog.Printf("tci: opening the receive-audio stream (rx %d, %d Hz)", rx, rate) return a.cat.TCIAudioDo(func(t cat.TCIAudioController) error { return t.StartTCIAudio(rx, rate) }) } // StopTCIAudio closes it. func (a *App) StopTCIAudio() error { if a.cat == nil { return fmt.Errorf("CAT not initialized") } applog.Printf("tci: closing the receive-audio stream") return a.cat.TCIAudioDo(func(t cat.TCIAudioController) error { return t.StopTCIAudio() }) } // GetTCIAudioStatus reports what is arriving: the sample rate the radio chose, // how much has come in, and the peak level of the last second. // // The level is the point. "The stream is open" and "audio is arriving" are // different claims, and only the second one is worth anything to someone // testing this on a radio for the first time. func (a *App) GetTCIAudioStatus() cat.TCIAudioStatus { if a.cat == nil { return 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) }) }