Wideband autonomous SDR analysis engine forked from sdr-visual-suite
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

135 行
3.1KB

  1. package main
  2. import (
  3. "sort"
  4. "strconv"
  5. "time"
  6. "sdr-visual-suite/internal/config"
  7. "sdr-visual-suite/internal/demod/gpudemod"
  8. "sdr-visual-suite/internal/detector"
  9. "sdr-visual-suite/internal/dsp"
  10. )
  11. func mustParseDuration(raw string, fallback time.Duration) time.Duration {
  12. if raw == "" {
  13. return fallback
  14. }
  15. if d, err := time.ParseDuration(raw); err == nil {
  16. return d
  17. }
  18. return fallback
  19. }
  20. func buildDecoderMap(cfg config.Config) map[string]string {
  21. out := map[string]string{}
  22. if cfg.Decoder.FT8Cmd != "" {
  23. out["FT8"] = cfg.Decoder.FT8Cmd
  24. }
  25. if cfg.Decoder.WSPRCmd != "" {
  26. out["WSPR"] = cfg.Decoder.WSPRCmd
  27. }
  28. if cfg.Decoder.DMRCmd != "" {
  29. out["DMR"] = cfg.Decoder.DMRCmd
  30. }
  31. if cfg.Decoder.DStarCmd != "" {
  32. out["D-STAR"] = cfg.Decoder.DStarCmd
  33. }
  34. if cfg.Decoder.FSKCmd != "" {
  35. out["FSK"] = cfg.Decoder.FSKCmd
  36. }
  37. if cfg.Decoder.PSKCmd != "" {
  38. out["PSK"] = cfg.Decoder.PSKCmd
  39. }
  40. return out
  41. }
  42. func decoderKeys(cfg config.Config) []string {
  43. m := buildDecoderMap(cfg)
  44. keys := make([]string, 0, len(m))
  45. for k := range m {
  46. keys = append(keys, k)
  47. }
  48. sort.Strings(keys)
  49. return keys
  50. }
  51. func extractSignalIQ(iq []complex64, sampleRate int, centerHz float64, sigHz float64, bwHz float64) []complex64 {
  52. if len(iq) == 0 || sampleRate <= 0 {
  53. return nil
  54. }
  55. results := extractSignalIQBatch(iq, sampleRate, centerHz, []detector.Signal{{CenterHz: sigHz, BWHz: bwHz}})
  56. if len(results) == 0 {
  57. return nil
  58. }
  59. return results[0]
  60. }
  61. func extractSignalIQBatch(iq []complex64, sampleRate int, centerHz float64, signals []detector.Signal) [][]complex64 {
  62. out := make([][]complex64, len(signals))
  63. if len(iq) == 0 || sampleRate <= 0 || len(signals) == 0 {
  64. return out
  65. }
  66. decimTarget := 200000
  67. if decimTarget <= 0 {
  68. decimTarget = sampleRate
  69. }
  70. var runner *gpudemod.BatchRunner
  71. if gpudemod.Available() {
  72. if gpuRunner, err := gpudemod.NewBatchRunner(len(iq), sampleRate); err == nil {
  73. runner = gpuRunner
  74. defer runner.Close()
  75. }
  76. }
  77. if runner != nil {
  78. jobs := make([]gpudemod.ExtractJob, len(signals))
  79. for i, sig := range signals {
  80. jobs[i] = gpudemod.ExtractJob{OffsetHz: sig.CenterHz - centerHz, BW: sig.BWHz, OutRate: decimTarget}
  81. }
  82. if gpuOuts, _, err := runner.ShiftFilterDecimateBatch(iq, jobs); err == nil && len(gpuOuts) == len(signals) {
  83. for i := range gpuOuts {
  84. out[i] = gpuOuts[i]
  85. }
  86. return out
  87. }
  88. }
  89. for i, sig := range signals {
  90. offset := sig.CenterHz - centerHz
  91. shifted := dsp.FreqShift(iq, sampleRate, offset)
  92. cutoff := sig.BWHz / 2
  93. if cutoff < 200 {
  94. cutoff = 200
  95. }
  96. if cutoff > float64(sampleRate)/2-1 {
  97. cutoff = float64(sampleRate)/2 - 1
  98. }
  99. taps := dsp.LowpassFIR(cutoff, sampleRate, 101)
  100. filtered := dsp.ApplyFIR(shifted, taps)
  101. decim := sampleRate / decimTarget
  102. if decim < 1 {
  103. decim = 1
  104. }
  105. out[i] = dsp.Decimate(filtered, decim)
  106. }
  107. return out
  108. }
  109. func parseSince(raw string) (time.Time, error) {
  110. if raw == "" {
  111. return time.Time{}, nil
  112. }
  113. if ms, err := strconv.ParseInt(raw, 10, 64); err == nil {
  114. if ms > 1e12 {
  115. return time.UnixMilli(ms), nil
  116. }
  117. return time.Unix(ms, 0), nil
  118. }
  119. if t, err := time.Parse(time.RFC3339Nano, raw); err == nil {
  120. return t, nil
  121. }
  122. return time.Parse(time.RFC3339, raw)
  123. }