Pārlūkot izejas kodu

Inject runtime demod metadata into live signal frames

master
Jan Svabenik pirms 22 stundām
vecāks
revīzija
ad69a57deb
5 mainītis faili ar 63 papildinājumiem un 15 dzēšanām
  1. +10
    -0
      cmd/sdrd/dsp_loop.go
  2. +13
    -10
      internal/detector/detector.go
  3. +7
    -0
      internal/recorder/recorder.go
  4. +24
    -0
      internal/recorder/streamer.go
  5. +9
    -5
      web/app.js

+ 10
- 0
cmd/sdrd/dsp_loop.go Parādīt failu

@@ -106,6 +106,16 @@ func runDSP(ctx context.Context, srcMgr *sourceManager, cfg config.Config, det *
} else {
displaySignals = rt.det.StableSignals()
}
if rec != nil && len(displaySignals) > 0 {
runtimeInfo := rec.RuntimeInfoBySignalID()
for i := range displaySignals {
if info, ok := runtimeInfo[displaySignals[i].ID]; ok {
displaySignals[i].DemodName = info.DemodName
displaySignals[i].PlaybackMode = info.PlaybackMode
displaySignals[i].StereoState = info.StereoState
}
}
}
state.arbitration = rt.arbitration
state.presentation = state.surveillance.DisplayLevel
if phaseSnap != nil {


+ 13
- 10
internal/detector/detector.go Parādīt failu

@@ -68,16 +68,19 @@ type activeEvent struct {
}

type Signal struct {
ID int64 `json:"id"`
FirstBin int `json:"first_bin"`
LastBin int `json:"last_bin"`
CenterHz float64 `json:"center_hz"`
BWHz float64 `json:"bw_hz"`
PeakDb float64 `json:"peak_db"`
SNRDb float64 `json:"snr_db"`
NoiseDb float64 `json:"noise_db,omitempty"`
Class *classifier.Classification `json:"class,omitempty"`
PLL *classifier.PLLResult `json:"pll,omitempty"`
ID int64 `json:"id"`
FirstBin int `json:"first_bin"`
LastBin int `json:"last_bin"`
CenterHz float64 `json:"center_hz"`
BWHz float64 `json:"bw_hz"`
PeakDb float64 `json:"peak_db"`
SNRDb float64 `json:"snr_db"`
NoiseDb float64 `json:"noise_db,omitempty"`
Class *classifier.Classification `json:"class,omitempty"`
PLL *classifier.PLLResult `json:"pll,omitempty"`
DemodName string `json:"demod,omitempty"`
PlaybackMode string `json:"playback_mode,omitempty"`
StereoState string `json:"stereo_state,omitempty"`
}

func New(detCfg config.DetectorConfig, sampleRate int, fftSize int) *Detector {


+ 7
- 0
internal/recorder/recorder.go Parādīt failu

@@ -356,6 +356,13 @@ func (m *Manager) StreamerRef() *Streamer {
return m.streamer
}

func (m *Manager) RuntimeInfoBySignalID() map[int64]RuntimeSignalInfo {
if m == nil || m.streamer == nil {
return nil
}
return m.streamer.RuntimeInfoBySignalID()
}

// ActiveStreams returns info about currently active streaming sessions.
func (m *Manager) ActiveStreams() int {
if m == nil || m.streamer == nil {


+ 24
- 0
internal/recorder/streamer.go Parādīt failu

@@ -115,6 +115,14 @@ type audioSub struct {
ch chan []byte
}

type RuntimeSignalInfo struct {
DemodName string
PlaybackMode string
StereoState string
Channels int
SampleRate int
}

// AudioInfo describes the audio format of a live-listen subscription.
// Sent to the WebSocket client as the first message.
type AudioInfo struct {
@@ -457,6 +465,22 @@ func (st *Streamer) attachPendingListeners(sess *streamSession) {
}

// CloseAll finalises all sessions and stops the worker goroutine.
func (st *Streamer) RuntimeInfoBySignalID() map[int64]RuntimeSignalInfo {
st.mu.RLock()
defer st.mu.RUnlock()
out := make(map[int64]RuntimeSignalInfo, len(st.sessions))
for _, sess := range st.sessions {
out[sess.signalID] = RuntimeSignalInfo{
DemodName: sess.demodName,
PlaybackMode: sess.playbackMode,
StereoState: sess.stereoState,
Channels: sess.channels,
SampleRate: sess.sampleRate,
}
}
return out
}

func (st *Streamer) CloseAll() {
close(st.feedCh)
<-st.done


+ 9
- 5
web/app.js Parādīt failu

@@ -307,6 +307,8 @@ function isListeningSignal(signal) {
}

function getSignalPrimaryMode(signal) {
if (signal?.playback_mode) return signal.playback_mode;
if (signal?.demod) return signal.demod;
if (isListeningSignal(signal) && liveListenInfo?.playback_mode && liveListenInfo.playback_mode !== '-') {
return liveListenInfo.playback_mode;
}
@@ -315,10 +317,12 @@ function getSignalPrimaryMode(signal) {
}

function getSignalRuntimeSummary(signal) {
if (!isListeningSignal(signal)) return '';
const bits = [];
if (liveListenInfo?.status && !['Idle', '-'].includes(liveListenInfo.status)) bits.push(liveListenInfo.status);
if (liveListenInfo?.stereo_state && liveListenInfo.stereo_state !== '-') bits.push(liveListenInfo.stereo_state);
if (signal?.stereo_state) bits.push(signal.stereo_state);
if (!bits.length && isListeningSignal(signal)) {
if (liveListenInfo?.status && !['Idle', '-'].includes(liveListenInfo.status)) bits.push(liveListenInfo.status);
if (liveListenInfo?.stereo_state && liveListenInfo.stereo_state !== '-') bits.push(liveListenInfo.stereo_state);
}
return bits.join(' · ');
}

@@ -1511,15 +1515,15 @@ function _createSignalItem(s) {
btn.dataset.id = s.id || 0;
const primaryMode = getSignalPrimaryMode(s);
const mc = modColor(primaryMode);
const rds = s.class?.pll?.rds_station || '';
const dec = decisionIndex.get(String(s.id || 0));
const decText = dec?.reason ? `${dec.reason}` : '';
const decFlags = dec ? `${dec.record ? 'REC' : ''}${dec.decode ? (dec.record ? '+DEC' : 'DEC') : ''}` : '';
const metaBits = [];
if (decFlags) metaBits.push(decFlags);
if (decText) metaBits.push(decText);
if (s.class?.pll?.rds_station) metaBits.push(`RDS ${s.class.pll.rds_station}`);
btn.title = metaBits.join(' · ');
btn.innerHTML = `<div class="item-top"><span class="item-title" data-field="freq">${fmtMHz(s.center_hz, 6)}</span><span class="item-badge" data-field="snr" style="color:${snrColor(s.snr_db || 0)}">${(s.snr_db || 0).toFixed(1)} dB</span></div><div class="item-bottom"><span class="item-meta item-meta--runtime" data-field="mode" style="color:${mc.label}">${primaryMode}</span></div>`;
btn.innerHTML = `<div class="item-top"><span class="item-title" data-field="freq">${fmtMHz(s.center_hz, 6)}</span><span class="item-badge" data-field="snr" style="color:${snrColor(s.snr_db || 0)}">${(s.snr_db || 0).toFixed(1)} dB</span></div><div class="item-bottom"><span class="item-meta item-meta--runtime" data-field="mode" style="color:${mc.label}">${primaryMode}</span>${rds ? `<span class="item-meta item-meta--rds" data-field="rds">${rds}</span>` : ''}</div>`;
btn.style.borderLeftColor = mc.label;
btn.style.borderLeftWidth = '3px';
btn.style.borderLeftStyle = 'solid';


Notiek ielāde…
Atcelt
Saglabāt