25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

81 satır
1.9KB

  1. package gpudemod
  2. import (
  3. "math"
  4. "math/cmplx"
  5. "testing"
  6. "sdr-visual-suite/internal/dsp"
  7. )
  8. func TestMixedBandwidthBatch(t *testing.T) {
  9. if !Available() {
  10. t.Skip("no GPU")
  11. }
  12. sampleRate := 2048000
  13. n := 2048
  14. iq := makeSyntheticIQ(n, sampleRate, []float64{50e3, -120e3, 300e3, -80e3})
  15. jobs := []ExtractJob{
  16. {OffsetHz: 50e3, BW: 12000, OutRate: 48000},
  17. {OffsetHz: -120e3, BW: 150000, OutRate: 192000},
  18. {OffsetHz: 300e3, BW: 3000, OutRate: 48000},
  19. {OffsetHz: -80e3, BW: 500, OutRate: 48000},
  20. }
  21. cpuOuts := make([][]complex64, len(jobs))
  22. for i, job := range jobs {
  23. cpuOuts[i] = extractCPU(iq, sampleRate, job)
  24. }
  25. runner, err := NewBatchRunner(n, sampleRate)
  26. if err != nil {
  27. t.Fatalf("NewBatchRunner: %v", err)
  28. }
  29. defer runner.Close()
  30. gpuOuts, rates, err := runner.ShiftFilterDecimateBatch(iq, jobs)
  31. if err != nil {
  32. t.Fatalf("Batch: %v", err)
  33. }
  34. for i := range jobs {
  35. if !complexSliceClose(cpuOuts[i], gpuOuts[i], 1e-3) {
  36. t.Errorf("job %d: GPU/CPU mismatch (rate=%d)", i, rates[i])
  37. }
  38. }
  39. }
  40. func makeSyntheticIQ(n int, sr int, freqs []float64) []complex64 {
  41. iq := make([]complex64, n)
  42. for _, f := range freqs {
  43. for i := range iq {
  44. phase := 2 * math.Pi * f * float64(i) / float64(sr)
  45. iq[i] += complex(float32(math.Cos(phase)), float32(math.Sin(phase)))
  46. }
  47. }
  48. return iq
  49. }
  50. func extractCPU(iq []complex64, sr int, job ExtractJob) []complex64 {
  51. shifted := dsp.FreqShift(iq, sr, job.OffsetHz)
  52. cutoff := job.BW / 2
  53. if cutoff < 200 {
  54. cutoff = 200
  55. }
  56. taps := dsp.LowpassFIR(cutoff, sr, 101)
  57. filtered := dsp.ApplyFIR(shifted, taps)
  58. decim := int(math.Round(float64(sr) / float64(job.OutRate)))
  59. if decim < 1 {
  60. decim = 1
  61. }
  62. return dsp.Decimate(filtered, decim)
  63. }
  64. func complexSliceClose(a, b []complex64, tol float64) bool {
  65. if len(a) != len(b) {
  66. return false
  67. }
  68. for i := range a {
  69. if cmplx.Abs(complex128(a[i]-b[i])) > tol {
  70. return false
  71. }
  72. }
  73. return true
  74. }