Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

143 linhas
3.7KB

  1. package dsp
  2. import (
  3. "testing"
  4. )
  5. func TestDecimateStateful_ContinuousPhase(t *testing.T) {
  6. // Simulate what happens in the streaming pipeline:
  7. // Two consecutive frames with non-divisible lengths decimated by 3.
  8. // Stateful version must produce the same output as decimating the
  9. // concatenated input in one go.
  10. factor := 3
  11. // Frame lengths that don't divide evenly (like real WFM: 41666 % 3 = 2)
  12. frame1 := make([]complex64, 41666)
  13. frame2 := make([]complex64, 41666)
  14. for i := range frame1 {
  15. frame1[i] = complex(float32(i), 0)
  16. }
  17. for i := range frame2 {
  18. frame2[i] = complex(float32(len(frame1)+i), 0)
  19. }
  20. // Reference: concatenate and decimate in one shot
  21. combined := make([]complex64, len(frame1)+len(frame2))
  22. copy(combined, frame1)
  23. copy(combined[len(frame1):], frame2)
  24. ref := Decimate(combined, factor)
  25. // Stateful: decimate frame by frame
  26. phase := 0
  27. out1 := DecimateStateful(frame1, factor, &phase)
  28. out2 := DecimateStateful(frame2, factor, &phase)
  29. got := make([]complex64, len(out1)+len(out2))
  30. copy(got, out1)
  31. copy(got[len(out1):], out2)
  32. if len(got) != len(ref) {
  33. t.Fatalf("length mismatch: stateful=%d reference=%d", len(got), len(ref))
  34. }
  35. for i := range ref {
  36. if got[i] != ref[i] {
  37. t.Fatalf("sample %d: got %v want %v", i, got[i], ref[i])
  38. }
  39. }
  40. }
  41. func TestDecimateStateful_Factor4_NFM(t *testing.T) {
  42. // NFM scenario: 200kHz/48kHz → decim=4, frame=16666 samples
  43. // 16666 % 4 = 2 → phase slip every frame with stateless decimation
  44. factor := 4
  45. frameLen := 16666
  46. nFrames := 10
  47. // Build continuous signal
  48. total := make([]complex64, frameLen*nFrames)
  49. for i := range total {
  50. total[i] = complex(float32(i), float32(-i))
  51. }
  52. ref := Decimate(total, factor)
  53. // Stateful frame-by-frame
  54. phase := 0
  55. var got []complex64
  56. for f := 0; f < nFrames; f++ {
  57. chunk := total[f*frameLen : (f+1)*frameLen]
  58. out := DecimateStateful(chunk, factor, &phase)
  59. got = append(got, out...)
  60. }
  61. if len(got) != len(ref) {
  62. t.Fatalf("length mismatch: stateful=%d reference=%d (frames=%d)", len(got), len(ref), nFrames)
  63. }
  64. for i := range ref {
  65. if got[i] != ref[i] {
  66. t.Fatalf("frame-boundary glitch at sample %d: got %v want %v", i, got[i], ref[i])
  67. }
  68. }
  69. }
  70. func TestDecimateStateful_Factor1_Passthrough(t *testing.T) {
  71. in := []complex64{1 + 2i, 3 + 4i, 5 + 6i}
  72. phase := 0
  73. out := DecimateStateful(in, 1, &phase)
  74. if len(out) != len(in) {
  75. t.Fatalf("passthrough: got len %d want %d", len(out), len(in))
  76. }
  77. }
  78. func TestDecimateStateful_ExactDivisible(t *testing.T) {
  79. // When frame length is exactly divisible, phase should stay 0
  80. factor := 4
  81. frame := make([]complex64, 100) // 100 % 4 = 0
  82. for i := range frame {
  83. frame[i] = complex(float32(i), 0)
  84. }
  85. phase := 0
  86. out := DecimateStateful(frame, factor, &phase)
  87. if phase != 0 {
  88. t.Fatalf("exact divisible: phase should be 0, got %d", phase)
  89. }
  90. if len(out) != 25 {
  91. t.Fatalf("exact divisible: got %d samples, want 25", len(out))
  92. }
  93. }
  94. func TestDecimateStateful_VaryingFrameSizes(t *testing.T) {
  95. // Real-world: buffer jitter causes varying frame sizes
  96. factor := 3
  97. frameSizes := []int{41600, 41700, 41666, 41650, 41680}
  98. // Build total
  99. totalLen := 0
  100. for _, s := range frameSizes {
  101. totalLen += s
  102. }
  103. total := make([]complex64, totalLen)
  104. for i := range total {
  105. total[i] = complex(float32(i), float32(i*2))
  106. }
  107. ref := Decimate(total, factor)
  108. phase := 0
  109. var got []complex64
  110. offset := 0
  111. for _, sz := range frameSizes {
  112. chunk := total[offset : offset+sz]
  113. out := DecimateStateful(chunk, factor, &phase)
  114. got = append(got, out...)
  115. offset += sz
  116. }
  117. if len(got) != len(ref) {
  118. t.Fatalf("varying frames: stateful=%d reference=%d", len(got), len(ref))
  119. }
  120. for i := range ref {
  121. if got[i] != ref[i] {
  122. t.Fatalf("varying frames: mismatch at %d: got %v want %v", i, got[i], ref[i])
  123. }
  124. }
  125. }