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

128 行
2.9KB

  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 eng *gpudemod.Engine
  71. if gpudemod.Available() {
  72. if gpuEng, err := gpudemod.New(len(iq), sampleRate); err == nil {
  73. eng = gpuEng
  74. defer eng.Close()
  75. }
  76. }
  77. for i, sig := range signals {
  78. offset := sig.CenterHz - centerHz
  79. if eng != nil {
  80. if gpuOut, _, err := eng.ShiftFilterDecimate(iq, offset, sig.BWHz, decimTarget); err == nil && len(gpuOut) > 0 {
  81. out[i] = gpuOut
  82. continue
  83. }
  84. }
  85. shifted := dsp.FreqShift(iq, sampleRate, offset)
  86. cutoff := sig.BWHz / 2
  87. if cutoff < 200 {
  88. cutoff = 200
  89. }
  90. if cutoff > float64(sampleRate)/2-1 {
  91. cutoff = float64(sampleRate)/2 - 1
  92. }
  93. taps := dsp.LowpassFIR(cutoff, sampleRate, 101)
  94. filtered := dsp.ApplyFIR(shifted, taps)
  95. decim := sampleRate / decimTarget
  96. if decim < 1 {
  97. decim = 1
  98. }
  99. out[i] = dsp.Decimate(filtered, decim)
  100. }
  101. return out
  102. }
  103. func parseSince(raw string) (time.Time, error) {
  104. if raw == "" {
  105. return time.Time{}, nil
  106. }
  107. if ms, err := strconv.ParseInt(raw, 10, 64); err == nil {
  108. if ms > 1e12 {
  109. return time.UnixMilli(ms), nil
  110. }
  111. return time.Unix(ms, 0), nil
  112. }
  113. if t, err := time.Parse(time.RFC3339Nano, raw); err == nil {
  114. return t, nil
  115. }
  116. return time.Parse(time.RFC3339, raw)
  117. }