code wiki / (root) / nx_fft_test.nx

nx_fft_test.nx source

↩ module page · 152 lines · 4897 B

1// nx_fft_test.nx -- smoke for V7 FFT primitives. 2 3import "syscalls.nx" 4import "nx_fft.nx" 5 6func main() -> i64 { 7 // === Test 1: log2 helper === 8 if nx_fft_log2(2) != 1 { return 1 } 9 if nx_fft_log2(8) != 3 { return 2 } 10 if nx_fft_log2(32) != 5 { return 3 } 11 12 // === Test 2: bit reverse === 13 // For log2n=3 (N=8): 0->0, 1->4, 2->2, 3->6, 4->1, 5->5, 6->3, 7->7 14 if nx_fft_bit_reverse(0, 3) != 0 { return 10 } 15 if nx_fft_bit_reverse(1, 3) != 4 { return 11 } 16 if nx_fft_bit_reverse(3, 3) != 6 { return 12 } 17 if nx_fft_bit_reverse(5, 3) != 5 { return 13 } 18 if nx_fft_bit_reverse(7, 3) != 7 { return 14 } 19 20 // === Test 3: FFT of DC signal [1, 1, 1, 1, 1, 1, 1, 1] === 21 // Should produce [8, 0, 0, 0, 0, 0, 0, 0] (only DC bin). 22 // Twiddle precision allows +-1 tolerance. 23 let re: *i64 = (sys_mmap(32 * 8)) as *i64 24 let im: *i64 = (sys_mmap(32 * 8)) as *i64 25 var i: i64 = 0 26 while i < 8 { 27 re[i] = 1 28 im[i] = 0 29 i = i + 1 30 } 31 nx_fft_forward(re, im, 8) 32 if re[0] != 8 { return 20 } 33 if im[0] != 0 { return 21 } 34 // All other bins ~ 0 35 var k: i64 = 1 36 while k < 8 { 37 var ar: i64 = re[k] 38 if ar < 0 { ar = -ar } 39 var ai: i64 = im[k] 40 if ai < 0 { ai = -ai } 41 if ar > 2 { return 22 } 42 if ai > 2 { return 23 } 43 k = k + 1 44 } 45 46 // === Test 4: FFT of delta [1, 0, 0, 0, 0, 0, 0, 0] === 47 // Should produce [1, 1, 1, 1, 1, 1, 1, 1] (flat spectrum). 48 var i2: i64 = 0 49 while i2 < 8 { 50 re[i2] = 0 51 im[i2] = 0 52 i2 = i2 + 1 53 } 54 re[0] = 1 55 nx_fft_forward(re, im, 8) 56 var k2: i64 = 0 57 while k2 < 8 { 58 if re[k2] != 1 { return 30 } 59 if im[k2] != 0 { return 31 } 60 k2 = k2 + 1 61 } 62 63 // === Test 5: round-trip FFT -> iFFT recovers input === 64 var i3: i64 = 0 65 while i3 < 8 { 66 re[i3] = i3 + 1 // [1, 2, 3, ..., 8] 67 im[i3] = 0 68 i3 = i3 + 1 69 } 70 nx_fft_forward(re, im, 8) 71 nx_fft_inverse(re, im, 8) 72 var k3: i64 = 0 73 while k3 < 8 { 74 let expected: i64 = k3 + 1 75 var diff: i64 = re[k3] - expected 76 if diff < 0 { diff = -diff } 77 if diff > 1 { return 40 } // tolerance 1 per element 78 k3 = k3 + 1 79 } 80 81 // === Test 6: power spectrum of cosine signal === 82 // x[n] = 100 * cos(2*pi*k*n/N), N=16, k=2. 83 // FFT should peak at bin k=2 and bin N-k=14. 84 let N: i64 = 16 85 // Approximate cos values for k=2 (frequency 2 cycles in 16 samples). 86 // n=0: cos(0) = 1.0 * 100 = 100 87 // n=1: cos(pi/4) ~ 0.707 * 100 = 71 88 // n=2: cos(pi/2) = 0 89 // n=3: cos(3pi/4) ~ -0.707 * 100 = -71 90 // n=4: cos(pi) = -1.0 * 100 = -100 91 // n=5: cos(5pi/4) ~ -0.707 * 100 = -71 92 // n=6: cos(3pi/2) = 0 93 // n=7: cos(7pi/4) ~ 0.707 * 100 = 71 94 // (then repeats) 95 let cos_v: *i64 = (sys_mmap(16 * 8)) as *i64 96 cos_v[0] = 100; cos_v[1] = 71; cos_v[2] = 0; cos_v[3] = -71 97 cos_v[4] = -100; cos_v[5] = -71; cos_v[6] = 0; cos_v[7] = 71 98 cos_v[8] = 100; cos_v[9] = 71; cos_v[10] = 0; cos_v[11] = -71 99 cos_v[12] = -100; cos_v[13] = -71; cos_v[14] = 0; cos_v[15] = 71 100 var i4: i64 = 0 101 while i4 < 16 { 102 re[i4] = cos_v[i4] 103 im[i4] = 0 104 i4 = i4 + 1 105 } 106 nx_fft_forward(re, im, 16) 107 let pwr: *i64 = (sys_mmap(16 * 8)) as *i64 108 nx_fft_power_spectrum(re, im, 16, pwr) 109 // Peak bins should be 2 and 14 (mirror). 110 var max_bin: i64 = 0 111 var max_v: i64 = pwr[0] 112 var b: i64 = 1 113 while b < 16 { 114 if pwr[b] > max_v { 115 max_v = pwr[b] 116 max_bin = b 117 } 118 b = b + 1 119 } 120 if max_bin == 2 { } 121 if max_bin == 14 { } 122 var ok_peak: i64 = 0 123 if max_bin == 2 { ok_peak = 1 } 124 if max_bin == 14 { ok_peak = 1 } 125 if ok_peak != 1 { return 50 } 126 // DC bin should be roughly 0 (zero-mean signal). 127 if pwr[0] > 1000 { return 51 } 128 129 // === Test 7: 2D FFT on a 4x4 DC image === 130 // All-ones 4x4: should yield only (0,0) bin = 16. 131 let data_re: *i64 = (sys_mmap(16 * 8)) as *i64 132 let data_im: *i64 = (sys_mmap(16 * 8)) as *i64 133 var p: i64 = 0 134 while p < 16 { 135 data_re[p] = 1 136 data_im[p] = 0 137 p = p + 1 138 } 139 nx_fft_2d_forward(data_re, data_im, 4, 4) 140 if data_re[0] != 16 { return 60 } 141 if data_im[0] != 0 { return 61 } 142 // Other bins should be near zero 143 var p2: i64 = 1 144 while p2 < 16 { 145 var ar: i64 = data_re[p2] 146 if ar < 0 { ar = -ar } 147 if ar > 4 { return 62 } // 2D adds tolerance 148 p2 = p2 + 1 149 } 150 151 return 0 152}