Explorar el Código

Auto-restart on IQ timeout and report last-sample age

master
Jan Svabenik hace 4 días
padre
commit
956f06e612
Se han modificado 4 ficheros con 49 adiciones y 5 borrados
  1. +36
    -0
      cmd/sdrd/main.go
  2. +4
    -3
      internal/sdr/source.go
  3. +7
    -1
      internal/sdrplay/sdrplay.go
  4. +2
    -1
      web/app.js

+ 36
- 0
cmd/sdrd/main.go Ver fichero

@@ -10,6 +10,7 @@ import (
"os/signal"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
@@ -97,6 +98,36 @@ type sourceManager struct {
newSource func(cfg config.Config) (sdr.Source, error)
}

func (m *sourceManager) Restart(cfg config.Config) error {
m.mu.Lock()
defer m.mu.Unlock()
old := m.src
_ = old.Stop()
next, err := m.newSource(cfg)
if err != nil {
_ = old.Start()
m.src = old
return err
}
if err := next.Start(); err != nil {
_ = next.Stop()
_ = old.Start()
m.src = old
return err
}
m.src = next
return nil
}

func (m *sourceManager) Stats() sdr.SourceStats {
m.mu.RLock()
defer m.mu.RUnlock()
if sp, ok := m.src.(sdr.StatsProvider); ok {
return sp.Stats()
}
return sdr.SourceStats{}
}

func newSourceManager(src sdr.Source, newSource func(cfg config.Config) (sdr.Source, error)) *sourceManager {
return &sourceManager{src: src, newSource: newSource}
}
@@ -463,6 +494,11 @@ func runDSP(ctx context.Context, src sdr.Source, cfg config.Config, det *detecto
iq, err := src.ReadIQ(cfg.FFTSize)
if err != nil {
log.Printf("read IQ: %v", err)
if strings.Contains(err.Error(), "timeout") {
if err := src.Restart(cfg); err != nil {
log.Printf("restart failed: %v", err)
}
}
continue
}
if !gotSamples {


+ 4
- 3
internal/sdr/source.go Ver fichero

@@ -13,9 +13,10 @@ type ConfigurableSource interface {
}

type SourceStats struct {
BufferSamples int `json:"buffer_samples"`
Dropped uint64 `json:"dropped"`
Resets uint64 `json:"resets"`
BufferSamples int `json:"buffer_samples"`
Dropped uint64 `json:"dropped"`
Resets uint64 `json:"resets"`
LastSampleAgoMs int64 `json:"last_sample_ago_ms"`
}

type StatsProvider interface {


+ 7
- 1
internal/sdrplay/sdrplay.go Ver fichero

@@ -97,6 +97,7 @@ type Source struct {
bwKHz int
dropped uint64
resets uint64
lastSample time.Time
cond *sync.Cond
}

@@ -284,7 +285,11 @@ func (s *Source) appendRing(samples []complex64) {
func (s *Source) Stats() sdr.SourceStats {
s.mu.Lock()
defer s.mu.Unlock()
return sdr.SourceStats{BufferSamples: s.size, Dropped: s.dropped, Resets: s.resets}
ago := int64(-1)
if !s.lastSample.IsZero() {
ago = time.Since(s.lastSample).Milliseconds()
}
return sdr.SourceStats{BufferSamples: s.size, Dropped: s.dropped, Resets: s.resets, LastSampleAgoMs: ago}
}

func (s *Source) Flush() {
@@ -383,6 +388,7 @@ func goStreamCallback(xi *C.short, xq *C.short, numSamples C.uint, reset C.uint,
iq[i] = complex(re, im)
}
src.mu.Lock()
src.lastSample = time.Now()
src.appendRing(iq)
src.mu.Unlock()
}


+ 2
- 1
web/app.js Ver fichero

@@ -353,7 +353,8 @@ function renderSpectrum() {

const binHz = sample_rate / n;
const gpuState = gpuInfo.active ? 'GPU:ON' : (gpuInfo.available ? 'GPU:OFF' : 'GPU:N/A');
metaEl.textContent = `Center ${(center_hz/1e6).toFixed(3)} MHz | Span ${(span/1e6).toFixed(3)} MHz | Res ${binHz.toFixed(1)} Hz/bin | Buf ${stats.buffer_samples} Drop ${stats.dropped} Reset ${stats.resets} | ${gpuState}`;
const lastAge = stats.last_sample_ago_ms >= 0 ? `${stats.last_sample_ago_ms}ms` : 'n/a';
metaEl.textContent = `Center ${(center_hz/1e6).toFixed(3)} MHz | Span ${(span/1e6).toFixed(3)} MHz | Res ${binHz.toFixed(1)} Hz/bin | Buf ${stats.buffer_samples} Drop ${stats.dropped} Reset ${stats.resets} Last ${lastAge} | ${gpuState}`;
}

function renderWaterfall() {


Cargando…
Cancelar
Guardar