|
- package main
-
- import (
- "log"
- "sort"
- "strconv"
- "time"
-
- "sdr-visual-suite/internal/config"
- "sdr-visual-suite/internal/demod/gpudemod"
- "sdr-visual-suite/internal/detector"
- "sdr-visual-suite/internal/dsp"
- )
-
- func mustParseDuration(raw string, fallback time.Duration) time.Duration {
- if raw == "" {
- return fallback
- }
- if d, err := time.ParseDuration(raw); err == nil {
- return d
- }
- return fallback
- }
-
- func buildDecoderMap(cfg config.Config) map[string]string {
- out := map[string]string{}
- if cfg.Decoder.FT8Cmd != "" {
- out["FT8"] = cfg.Decoder.FT8Cmd
- }
- if cfg.Decoder.WSPRCmd != "" {
- out["WSPR"] = cfg.Decoder.WSPRCmd
- }
- if cfg.Decoder.DMRCmd != "" {
- out["DMR"] = cfg.Decoder.DMRCmd
- }
- if cfg.Decoder.DStarCmd != "" {
- out["D-STAR"] = cfg.Decoder.DStarCmd
- }
- if cfg.Decoder.FSKCmd != "" {
- out["FSK"] = cfg.Decoder.FSKCmd
- }
- if cfg.Decoder.PSKCmd != "" {
- out["PSK"] = cfg.Decoder.PSKCmd
- }
- return out
- }
-
- func decoderKeys(cfg config.Config) []string {
- m := buildDecoderMap(cfg)
- keys := make([]string, 0, len(m))
- for k := range m {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- return keys
- }
-
- func (m *extractionManager) reset() {
- if m == nil {
- return
- }
- m.mu.Lock()
- defer m.mu.Unlock()
- if m.runner != nil {
- m.runner.Close()
- m.runner = nil
- }
- }
-
- func (m *extractionManager) get(sampleCount int, sampleRate int) *gpudemod.BatchRunner {
- if m == nil || sampleCount <= 0 || sampleRate <= 0 || !gpudemod.Available() {
- return nil
- }
- m.mu.Lock()
- defer m.mu.Unlock()
- if m.runner == nil {
- if r, err := gpudemod.NewBatchRunner(sampleCount, sampleRate); err == nil {
- m.runner = r
- } else {
- log.Printf("gpudemod: batch runner init failed: %v", err)
- }
- return m.runner
- }
- return m.runner
- }
-
- func extractSignalIQ(iq []complex64, sampleRate int, centerHz float64, sigHz float64, bwHz float64) []complex64 {
- if len(iq) == 0 || sampleRate <= 0 {
- return nil
- }
- results := extractSignalIQBatch(nil, iq, sampleRate, centerHz, []detector.Signal{{CenterHz: sigHz, BWHz: bwHz}})
- if len(results) == 0 {
- return nil
- }
- return results[0]
- }
-
- func extractSignalIQBatch(extractMgr *extractionManager, iq []complex64, sampleRate int, centerHz float64, signals []detector.Signal) [][]complex64 {
- out := make([][]complex64, len(signals))
- if len(iq) == 0 || sampleRate <= 0 || len(signals) == 0 {
- return out
- }
- decimTarget := 200000
- if decimTarget <= 0 {
- decimTarget = sampleRate
- }
-
- runner := extractMgr.get(len(iq), sampleRate)
- if runner != nil {
- jobs := make([]gpudemod.ExtractJob, len(signals))
- for i, sig := range signals {
- jobs[i] = gpudemod.ExtractJob{OffsetHz: sig.CenterHz - centerHz, BW: sig.BWHz, OutRate: decimTarget}
- }
- if gpuOuts, _, err := runner.ShiftFilterDecimateBatch(iq, jobs); err == nil && len(gpuOuts) == len(signals) {
- log.Printf("gpudemod: batch extraction used for %d signals", len(signals))
- for i := range gpuOuts {
- out[i] = gpuOuts[i]
- }
- return out
- } else if err != nil {
- log.Printf("gpudemod: batch extraction failed for %d signals: %v", len(signals), err)
- }
- }
-
- log.Printf("gpudemod: CPU extraction fallback used for %d signals", len(signals))
- for i, sig := range signals {
- offset := sig.CenterHz - centerHz
- shifted := dsp.FreqShift(iq, sampleRate, offset)
- cutoff := sig.BWHz / 2
- if cutoff < 200 {
- cutoff = 200
- }
- if cutoff > float64(sampleRate)/2-1 {
- cutoff = float64(sampleRate)/2 - 1
- }
- taps := dsp.LowpassFIR(cutoff, sampleRate, 101)
- filtered := dsp.ApplyFIR(shifted, taps)
- decim := sampleRate / decimTarget
- if decim < 1 {
- decim = 1
- }
- out[i] = dsp.Decimate(filtered, decim)
- }
- return out
- }
-
- func parseSince(raw string) (time.Time, error) {
- if raw == "" {
- return time.Time{}, nil
- }
- if ms, err := strconv.ParseInt(raw, 10, 64); err == nil {
- if ms > 1e12 {
- return time.UnixMilli(ms), nil
- }
- return time.Unix(ms, 0), nil
- }
- if t, err := time.Parse(time.RFC3339Nano, raw); err == nil {
- return t, nil
- }
- return time.Parse(time.RFC3339, raw)
- }
|