Wideband autonomous SDR analysis engine forked from sdr-visual-suite
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

105 líneas
2.3KB

  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/dsp"
  9. )
  10. func mustParseDuration(raw string, fallback time.Duration) time.Duration {
  11. if raw == "" {
  12. return fallback
  13. }
  14. if d, err := time.ParseDuration(raw); err == nil {
  15. return d
  16. }
  17. return fallback
  18. }
  19. func buildDecoderMap(cfg config.Config) map[string]string {
  20. out := map[string]string{}
  21. if cfg.Decoder.FT8Cmd != "" {
  22. out["FT8"] = cfg.Decoder.FT8Cmd
  23. }
  24. if cfg.Decoder.WSPRCmd != "" {
  25. out["WSPR"] = cfg.Decoder.WSPRCmd
  26. }
  27. if cfg.Decoder.DMRCmd != "" {
  28. out["DMR"] = cfg.Decoder.DMRCmd
  29. }
  30. if cfg.Decoder.DStarCmd != "" {
  31. out["D-STAR"] = cfg.Decoder.DStarCmd
  32. }
  33. if cfg.Decoder.FSKCmd != "" {
  34. out["FSK"] = cfg.Decoder.FSKCmd
  35. }
  36. if cfg.Decoder.PSKCmd != "" {
  37. out["PSK"] = cfg.Decoder.PSKCmd
  38. }
  39. return out
  40. }
  41. func decoderKeys(cfg config.Config) []string {
  42. m := buildDecoderMap(cfg)
  43. keys := make([]string, 0, len(m))
  44. for k := range m {
  45. keys = append(keys, k)
  46. }
  47. sort.Strings(keys)
  48. return keys
  49. }
  50. func extractSignalIQ(iq []complex64, sampleRate int, centerHz float64, sigHz float64, bwHz float64) []complex64 {
  51. if len(iq) == 0 || sampleRate <= 0 {
  52. return nil
  53. }
  54. offset := sigHz - centerHz
  55. decimTarget := 200000
  56. if decimTarget <= 0 {
  57. decimTarget = sampleRate
  58. }
  59. if gpudemod.Available() {
  60. if eng, err := gpudemod.New(len(iq), sampleRate); err == nil {
  61. defer eng.Close()
  62. if out, _, err := eng.ShiftFilterDecimate(iq, offset, bwHz, decimTarget); err == nil && len(out) > 0 {
  63. return out
  64. }
  65. }
  66. }
  67. shifted := dsp.FreqShift(iq, sampleRate, offset)
  68. cutoff := bwHz / 2
  69. if cutoff < 200 {
  70. cutoff = 200
  71. }
  72. if cutoff > float64(sampleRate)/2-1 {
  73. cutoff = float64(sampleRate)/2 - 1
  74. }
  75. taps := dsp.LowpassFIR(cutoff, sampleRate, 101)
  76. filtered := dsp.ApplyFIR(shifted, taps)
  77. decim := sampleRate / decimTarget
  78. if decim < 1 {
  79. decim = 1
  80. }
  81. return dsp.Decimate(filtered, decim)
  82. }
  83. func parseSince(raw string) (time.Time, error) {
  84. if raw == "" {
  85. return time.Time{}, nil
  86. }
  87. if ms, err := strconv.ParseInt(raw, 10, 64); err == nil {
  88. if ms > 1e12 {
  89. return time.UnixMilli(ms), nil
  90. }
  91. return time.Unix(ms, 0), nil
  92. }
  93. if t, err := time.Parse(time.RFC3339Nano, raw); err == nil {
  94. return t, nil
  95. }
  96. return time.Parse(time.RFC3339, raw)
  97. }