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}