u_testfftunit.pp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. unit u_testfftunit;
  2. interface
  3. uses Math, Sysutils, u_ap, u_ftbase, u_fft;
  4. function TestFFT(Silent : Boolean):Boolean;
  5. function testfftunit_test_silent():Boolean;
  6. function testfftunit_test():Boolean;
  7. implementation
  8. procedure RefFFTC1D(var A : TComplex1DArray; N : Integer);forward;
  9. procedure RefFFTC1DInv(var A : TComplex1DArray; N : Integer);forward;
  10. procedure RefInternalCFFT(var a : TReal1DArray;
  11. nn : Integer;
  12. InverseFFT : Boolean);forward;
  13. procedure RefInternalRFFT(const a : TReal1DArray;
  14. nn : Integer;
  15. var F : TComplex1DArray);forward;
  16. (*************************************************************************
  17. Test
  18. *************************************************************************)
  19. function TestFFT(Silent : Boolean):Boolean;
  20. var
  21. N : Integer;
  22. I : Integer;
  23. K : Integer;
  24. A1 : TComplex1DArray;
  25. A2 : TComplex1DArray;
  26. A3 : TComplex1DArray;
  27. R1 : TReal1DArray;
  28. R2 : TReal1DArray;
  29. Buf : TReal1DArray;
  30. Plan : FTPlan;
  31. MaxN : Integer;
  32. BiDiErr : Double;
  33. BiDiRErr : Double;
  34. RefErr : Double;
  35. RefRErr : Double;
  36. REIntErr : Double;
  37. ErrTol : Double;
  38. RefErrors : Boolean;
  39. BiDiErrors : Boolean;
  40. RefRErrors : Boolean;
  41. BiDiRErrors : Boolean;
  42. REIntErrors : Boolean;
  43. WasErrors : Boolean;
  44. begin
  45. MaxN := 128;
  46. ErrTol := 100000*Power(MaxN, AP_Double(3)/2)*MachineEpsilon;
  47. BiDiErrors := False;
  48. RefErrors := False;
  49. BiDiRErrors := False;
  50. RefRErrors := False;
  51. REIntErrors := False;
  52. WasErrors := False;
  53. //
  54. // Test bi-directional error: norm(x-invFFT(FFT(x)))
  55. //
  56. BiDiErr := 0;
  57. BiDiRErr := 0;
  58. N:=1;
  59. while N<=MaxN do
  60. begin
  61. //
  62. // Complex FFT/invFFT
  63. //
  64. SetLength(A1, N);
  65. SetLength(A2, N);
  66. SetLength(A3, N);
  67. I:=0;
  68. while I<=N-1 do
  69. begin
  70. A1[I].X := 2*RandomReal-1;
  71. A1[I].Y := 2*RandomReal-1;
  72. A2[I] := A1[I];
  73. A3[I] := A1[I];
  74. Inc(I);
  75. end;
  76. FFTC1D(A2, N);
  77. FFTC1DInv(A2, N);
  78. FFTC1DInv(A3, N);
  79. FFTC1D(A3, N);
  80. I:=0;
  81. while I<=N-1 do
  82. begin
  83. BiDiErr := Max(BiDiErr, AbsComplex(C_Sub(A1[I],A2[I])));
  84. BiDiErr := Max(BiDiErr, AbsComplex(C_Sub(A1[I],A3[I])));
  85. Inc(I);
  86. end;
  87. //
  88. // Real
  89. //
  90. SetLength(R1, N);
  91. SetLength(R2, N);
  92. I:=0;
  93. while I<=N-1 do
  94. begin
  95. R1[I] := 2*RandomReal-1;
  96. R2[I] := R1[I];
  97. Inc(I);
  98. end;
  99. FFTR1D(R2, N, A1);
  100. APVMul(@R2[0], 0, N-1, 0);
  101. FFTR1DInv(A1, N, R2);
  102. I:=0;
  103. while I<=N-1 do
  104. begin
  105. BiDiRErr := Max(BiDiRErr, AbsComplex(C_Complex(R1[I]-R2[I])));
  106. Inc(I);
  107. end;
  108. Inc(N);
  109. end;
  110. BiDiErrors := BiDiErrors or AP_FP_Greater(BiDiErr,ErrTol);
  111. BiDiRErrors := BiDiRErrors or AP_FP_Greater(BiDiRErr,ErrTol);
  112. //
  113. // Test against reference O(N^2) implementation
  114. //
  115. RefErr := 0;
  116. RefRErr := 0;
  117. N:=1;
  118. while N<=MaxN do
  119. begin
  120. //
  121. // Complex FFT
  122. //
  123. SetLength(A1, N);
  124. SetLength(A2, N);
  125. I:=0;
  126. while I<=N-1 do
  127. begin
  128. A1[I].X := 2*RandomReal-1;
  129. A1[I].Y := 2*RandomReal-1;
  130. A2[I] := A1[I];
  131. Inc(I);
  132. end;
  133. FFTC1D(A1, N);
  134. RefFFTC1D(A2, N);
  135. I:=0;
  136. while I<=N-1 do
  137. begin
  138. RefErr := Max(RefErr, AbsComplex(C_Sub(A1[I],A2[I])));
  139. Inc(I);
  140. end;
  141. //
  142. // Complex inverse FFT
  143. //
  144. SetLength(A1, N);
  145. SetLength(A2, N);
  146. I:=0;
  147. while I<=N-1 do
  148. begin
  149. A1[I].X := 2*RandomReal-1;
  150. A1[I].Y := 2*RandomReal-1;
  151. A2[I] := A1[I];
  152. Inc(I);
  153. end;
  154. FFTC1DInv(A1, N);
  155. RefFFTC1DInv(A2, N);
  156. I:=0;
  157. while I<=N-1 do
  158. begin
  159. RefErr := Max(RefErr, AbsComplex(C_Sub(A1[I],A2[I])));
  160. Inc(I);
  161. end;
  162. //
  163. // Real forward/inverse FFT:
  164. // * calculate and check forward FFT
  165. // * use precalculated FFT to check backward FFT
  166. // fill unused parts of frequencies array with random numbers
  167. // to ensure that they are not really used
  168. //
  169. SetLength(R1, N);
  170. SetLength(R2, N);
  171. I:=0;
  172. while I<=N-1 do
  173. begin
  174. R1[I] := 2*RandomReal-1;
  175. R2[I] := R1[I];
  176. Inc(I);
  177. end;
  178. FFTR1D(R1, N, A1);
  179. RefInternalRFFT(R2, N, A2);
  180. I:=0;
  181. while I<=N-1 do
  182. begin
  183. RefRErr := Max(RefRErr, AbsComplex(C_Sub(A1[I],A2[I])));
  184. Inc(I);
  185. end;
  186. SetLength(A3, Floor(AP_Double(N)/2)+1);
  187. I:=0;
  188. while I<=Floor(AP_Double(N)/2) do
  189. begin
  190. A3[I] := A2[I];
  191. Inc(I);
  192. end;
  193. A3[0].Y := 2*RandomReal-1;
  194. if N mod 2=0 then
  195. begin
  196. A3[Floor(AP_Double(N)/2)].Y := 2*RandomReal-1;
  197. end;
  198. I:=0;
  199. while I<=N-1 do
  200. begin
  201. R1[I] := 0;
  202. Inc(I);
  203. end;
  204. FFTR1DInv(A3, N, R1);
  205. I:=0;
  206. while I<=N-1 do
  207. begin
  208. RefRErr := Max(RefRErr, AbsReal(R2[I]-R1[I]));
  209. Inc(I);
  210. end;
  211. Inc(N);
  212. end;
  213. RefErrors := RefErrors or AP_FP_Greater(RefErr,ErrTol);
  214. RefRErrors := RefRErrors or AP_FP_Greater(RefRErr,ErrTol);
  215. //
  216. // test internal real even FFT
  217. //
  218. REIntErr := 0;
  219. K:=1;
  220. while K<=MaxN div 2 do
  221. begin
  222. N := 2*K;
  223. //
  224. // Real forward FFT
  225. //
  226. SetLength(R1, N);
  227. SetLength(R2, N);
  228. I:=0;
  229. while I<=N-1 do
  230. begin
  231. R1[I] := 2*RandomReal-1;
  232. R2[I] := R1[I];
  233. Inc(I);
  234. end;
  235. FTBaseGenerateComplexFFTPlan(N div 2, Plan);
  236. SetLength(Buf, N);
  237. FFTR1DInternalEven(R1, N, Buf, Plan);
  238. RefInternalRFFT(R2, N, A2);
  239. REIntErr := Max(REIntErr, AbsReal(R1[0]-A2[0].X));
  240. REIntErr := Max(REIntErr, AbsReal(R1[1]-A2[N div 2].X));
  241. I:=1;
  242. while I<=N div 2-1 do
  243. begin
  244. REIntErr := Max(REIntErr, AbsReal(R1[2*I+0]-A2[I].X));
  245. REIntErr := Max(REIntErr, AbsReal(R1[2*I+1]-A2[I].Y));
  246. Inc(I);
  247. end;
  248. //
  249. // Real backward FFT
  250. //
  251. SetLength(R1, N);
  252. I:=0;
  253. while I<=N-1 do
  254. begin
  255. R1[I] := 2*RandomReal-1;
  256. Inc(I);
  257. end;
  258. SetLength(A2, Floor(AP_Double(N)/2)+1);
  259. A2[0] := C_Complex(R1[0]);
  260. I:=1;
  261. while I<=Floor(AP_Double(N)/2)-1 do
  262. begin
  263. A2[I].X := R1[2*I+0];
  264. A2[I].Y := R1[2*I+1];
  265. Inc(I);
  266. end;
  267. A2[Floor(AP_Double(N)/2)] := C_Complex(R1[1]);
  268. FTBaseGenerateComplexFFTPlan(N div 2, Plan);
  269. SetLength(Buf, N);
  270. FFTR1DInvInternalEven(R1, N, Buf, Plan);
  271. FFTR1DInv(A2, N, R2);
  272. I:=0;
  273. while I<=N-1 do
  274. begin
  275. REIntErr := Max(REIntErr, AbsReal(R1[I]-R2[I]));
  276. Inc(I);
  277. end;
  278. Inc(K);
  279. end;
  280. REIntErrors := REIntErrors or AP_FP_Greater(REIntErr,ErrTol);
  281. //
  282. // end
  283. //
  284. WasErrors := BiDiErrors or BiDiRErrors or RefErrors or RefRErrors or REIntErrors;
  285. if not Silent then
  286. begin
  287. Write(Format('TESTING FFT'#13#10'',[]));
  288. Write(Format('FINAL RESULT: ',[]));
  289. if WasErrors then
  290. begin
  291. Write(Format('FAILED'#13#10'',[]));
  292. end
  293. else
  294. begin
  295. Write(Format('OK'#13#10'',[]));
  296. end;
  297. Write(Format('* BI-DIRECTIONAL COMPLEX TEST: ',[]));
  298. if BiDiErrors then
  299. begin
  300. Write(Format('FAILED'#13#10'',[]));
  301. end
  302. else
  303. begin
  304. Write(Format('OK'#13#10'',[]));
  305. end;
  306. Write(Format('* AGAINST REFERENCE COMPLEX FFT: ',[]));
  307. if RefErrors then
  308. begin
  309. Write(Format('FAILED'#13#10'',[]));
  310. end
  311. else
  312. begin
  313. Write(Format('OK'#13#10'',[]));
  314. end;
  315. Write(Format('* BI-DIRECTIONAL REAL TEST: ',[]));
  316. if BiDiRErrors then
  317. begin
  318. Write(Format('FAILED'#13#10'',[]));
  319. end
  320. else
  321. begin
  322. Write(Format('OK'#13#10'',[]));
  323. end;
  324. Write(Format('* AGAINST REFERENCE REAL FFT: ',[]));
  325. if RefRErrors then
  326. begin
  327. Write(Format('FAILED'#13#10'',[]));
  328. end
  329. else
  330. begin
  331. Write(Format('OK'#13#10'',[]));
  332. end;
  333. Write(Format('* INTERNAL EVEN FFT: ',[]));
  334. if REIntErrors then
  335. begin
  336. Write(Format('FAILED'#13#10'',[]));
  337. end
  338. else
  339. begin
  340. Write(Format('OK'#13#10'',[]));
  341. end;
  342. if WasErrors then
  343. begin
  344. Write(Format('TEST FAILED'#13#10'',[]));
  345. end
  346. else
  347. begin
  348. Write(Format('TEST PASSED'#13#10'',[]));
  349. end;
  350. end;
  351. Result := not WasErrors;
  352. end;
  353. (*************************************************************************
  354. Reference FFT
  355. *************************************************************************)
  356. procedure RefFFTC1D(var A : TComplex1DArray; N : Integer);
  357. var
  358. Buf : TReal1DArray;
  359. I : Integer;
  360. begin
  361. Assert(N>0, 'FFTC1D: incorrect N!');
  362. SetLength(Buf, 2*N);
  363. I:=0;
  364. while I<=N-1 do
  365. begin
  366. Buf[2*I+0] := A[I].X;
  367. Buf[2*I+1] := A[I].Y;
  368. Inc(I);
  369. end;
  370. RefInternalCFFT(Buf, N, False);
  371. I:=0;
  372. while I<=N-1 do
  373. begin
  374. A[I].X := Buf[2*I+0];
  375. A[I].Y := Buf[2*I+1];
  376. Inc(I);
  377. end;
  378. end;
  379. (*************************************************************************
  380. Reference inverse FFT
  381. *************************************************************************)
  382. procedure RefFFTC1DInv(var A : TComplex1DArray; N : Integer);
  383. var
  384. Buf : TReal1DArray;
  385. I : Integer;
  386. begin
  387. Assert(N>0, 'FFTC1DInv: incorrect N!');
  388. SetLength(Buf, 2*N);
  389. I:=0;
  390. while I<=N-1 do
  391. begin
  392. Buf[2*I+0] := A[I].X;
  393. Buf[2*I+1] := A[I].Y;
  394. Inc(I);
  395. end;
  396. RefInternalCFFT(Buf, N, True);
  397. I:=0;
  398. while I<=N-1 do
  399. begin
  400. A[I].X := Buf[2*I+0];
  401. A[I].Y := Buf[2*I+1];
  402. Inc(I);
  403. end;
  404. end;
  405. (*************************************************************************
  406. Internal complex FFT stub.
  407. Uses straightforward formula with O(N^2) complexity.
  408. *************************************************************************)
  409. procedure RefInternalCFFT(var a : TReal1DArray;
  410. nn : Integer;
  411. InverseFFT : Boolean);
  412. var
  413. Tmp : TReal1DArray;
  414. I : Integer;
  415. J : Integer;
  416. K : Integer;
  417. Hre : Double;
  418. Him : Double;
  419. C : Double;
  420. S : Double;
  421. re : Double;
  422. im : Double;
  423. begin
  424. SetLength(Tmp, 2*nn-1+1);
  425. if not InverseFFT then
  426. begin
  427. i:=0;
  428. while i<=nn-1 do
  429. begin
  430. HRe := 0;
  431. Him := 0;
  432. K:=0;
  433. while K<=nn-1 do
  434. begin
  435. re := A[2*k];
  436. im := A[2*k+1];
  437. C := Cos(-2*Pi*k*i/nn);
  438. S := Sin(-2*Pi*k*i/nn);
  439. HRe := HRe+C*Re-S*im;
  440. HIm := HIm+C*im+S*re;
  441. Inc(K);
  442. end;
  443. Tmp[2*i] := Hre;
  444. Tmp[2*i+1] := Him;
  445. Inc(i);
  446. end;
  447. i:=0;
  448. while i<=2*nn-1 do
  449. begin
  450. A[I] := Tmp[I];
  451. Inc(i);
  452. end;
  453. end
  454. else
  455. begin
  456. k:=0;
  457. while k<=nn-1 do
  458. begin
  459. HRe := 0;
  460. Him := 0;
  461. i:=0;
  462. while i<=nn-1 do
  463. begin
  464. re := A[2*i];
  465. im := A[2*i+1];
  466. C := Cos(2*Pi*k*i/nn);
  467. S := Sin(2*Pi*k*i/nn);
  468. HRe := HRe+C*Re-S*im;
  469. HIm := HIm+C*im+S*re;
  470. Inc(i);
  471. end;
  472. Tmp[2*k] := Hre/nn;
  473. Tmp[2*k+1] := Him/nn;
  474. Inc(k);
  475. end;
  476. i:=0;
  477. while i<=2*nn-1 do
  478. begin
  479. A[I] := Tmp[I];
  480. Inc(i);
  481. end;
  482. end;
  483. end;
  484. (*************************************************************************
  485. Internal real FFT stub.
  486. Uses straightforward formula with O(N^2) complexity.
  487. *************************************************************************)
  488. procedure RefInternalRFFT(const a : TReal1DArray;
  489. nn : Integer;
  490. var F : TComplex1DArray);
  491. var
  492. Tmp : TReal1DArray;
  493. I : Integer;
  494. begin
  495. SetLength(Tmp, 2*nn-1+1);
  496. I:=0;
  497. while I<=nn-1 do
  498. begin
  499. Tmp[2*I] := A[I];
  500. Tmp[2*I+1] := 0;
  501. Inc(I);
  502. end;
  503. RefInternalCFFT(Tmp, nn, False);
  504. SetLength(F, nn);
  505. I:=0;
  506. while I<=nn-1 do
  507. begin
  508. F[I].X := Tmp[2*I+0];
  509. F[I].Y := Tmp[2*I+1];
  510. Inc(I);
  511. end;
  512. end;
  513. (*************************************************************************
  514. Silent unit test
  515. *************************************************************************)
  516. function testfftunit_test_silent():Boolean;
  517. begin
  518. Result := TestFFT(True);
  519. end;
  520. (*************************************************************************
  521. Unit test
  522. *************************************************************************)
  523. function testfftunit_test():Boolean;
  524. begin
  525. Result := TestFFT(False);
  526. end;
  527. end.