CMSIS DSP library for ARM and MATLAB gives two different results of FFT - Page 1
Forum section: electronics / general

author: syntax333
author_rank: Regular Contributor
timestamp: January 11, 2023, 10:27:11 am
content: I am working on a project where I have to compute FFT of a signal. Same signal is fed through both MATLAB and STM32 controller and results are compared.Results seems to be different from each other. I don't know what I am doing wrong.The STM32 Code is below:Code://Pre-FFTarm_fill_f32(0, &tempBuff_1, 256-(FrameLen)); //zero paddingPreFFTProcess(tempBuff_1, tempBuff_2, 256); //Convert format from Real,Real,Real,Real.. to Real,Img,Real,Img...//FFTarm_cfft_f32(&fftInstance, tempBuff_2 , 0, 1); //FFTarm_cmplx_mag_f32(tempBuff_2, tempBuff_1, 256); //ABSThe MATLAB code:Code:Out = abs(fft(Input,256));The results are shown in images.

author: Kalvin
author_rank: Super Contributor
timestamp: January 11, 2023, 10:34:54 am
content: Convert your two-sided spectrum to a single-sided spectrum. See:https://www.mathworks.com/help/matlab/ref/fft.html

author: syntax333
author_rank: Regular Contributor
timestamp: January 11, 2023, 10:49:17 am
content: Quote from: Kalvin on January 11, 2023, 10:34:54 amI don't understand how this helps?

author: Kalvin
author_rank: Super Contributor
timestamp: January 11, 2023, 11:05:42 am
content: Quote from: syntax333 on January 11, 2023, 10:49:17 amWell, you didn't explain what the graphs are supposed to show.Check that STM32 FFT and Matlab FFT are using the same windowing function.

author: syntax333
author_rank: Regular Contributor
timestamp: January 11, 2023, 11:23:36 am
content: Blue line shows FFT result of STM32, orange line show FFT result of MATLAB.

author: wek
author_rank: Frequent Contributor
timestamp: January 11, 2023, 11:29:29 am
content: ARM code uses 32-bit floats, Matlab is likely to use 80-bit long doubles or so. So what you see may quite well be rounding errors.JW

author: syntax333
author_rank: Regular Contributor
timestamp: January 11, 2023, 11:43:25 am
content: Quote from: wek on January 11, 2023, 11:29:29 amI used "single()" casting to input signal of MATLAB to make the MATLAB data format to match 32 bit floating point format of STM32. Result is same.

author: Kalvin
author_rank: Super Contributor
timestamp: January 11, 2023, 12:29:00 pm
content: Quote from: syntax333 on January 11, 2023, 11:43:25 amAre you sure you are feeding the same input vector to STM32 and Matlab FFT functions? I mean, when you are calling STM32 fft-functionCode:arm_cfft_f32(&fftInstance, tempBuff_2 , 0, 1); //FFTthe tempBuff_2 contains really the same values that you are using with Matlab?

author: syntax333
author_rank: Regular Contributor
timestamp: January 11, 2023, 12:31:55 pm
content: Quote from: Kalvin on January 11, 2023, 12:29:00 pmYes. I checked it point to point. The only thing I can think of is FFT function in MATLAB and STM32 is different.

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: January 11, 2023, 01:02:50 pm
content: Are you complaining the graphs don't look similar (easy answer, already answered)? Or that the values for each bucket slightly differs (probably rounding error differences)?They look the same to me.But as Kalvin pointed out, one of the graphs shows a two-sided spectrum. If you scale the single sided to overlay the first half of the two-sided they seem to match ~perfectly.

author: syntax333
author_rank: Regular Contributor
timestamp: January 11, 2023, 01:25:08 pm
content: I am expecting ~100% match between my STM and MATLAB outputs since same input signal is used. FFT function shouldn't differ between platform to platform. Its like summation. If I add two numbers in STM and MATLAB I expect them to be exactly the same. Why can't I expect the same for FFT function?

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: January 11, 2023, 01:40:05 pm
content: How do you determine them to not be the same? The most logical explanation here is not that the implementation of FFT differs between the two.

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 11, 2023, 03:45:54 pm
content: Floating point numbers have limited precision. IEEE-754 Binary32 has 24 bits of precision, and Binary64 has 53 bits of precision. You cannot expect a large sum (a 256-point FFT) done using Binary32 (float/f32 when using ARM cores) to match the same done using Binary64 (double on x86-64, as Matlab uses SSE/AVX vectorized math for these and not the 80-bit old x87 FPU instructions). Domain cancellation error –– adding values of similar magnitude but with different signs, or subtracting values of similar magnitude and same sign –– will typically be noticeable in complicated signals, so to verify, I suggest you use test signals instead: sine, triangle, and rectangle waves with period 256/NforNbetween say 1 and 16, inclusive.I do not believe you can tell Matlab to only use float/Binary32 precision when doing the FFT, so some rounding-error-scaled differences are to be expected here in all cases.

author: SiliconWizard
author_rank: Super Contributor
timestamp: January 11, 2023, 07:22:01 pm
content: Quote from: syntax333 on January 11, 2023, 01:25:08 pmAs Nominal said.And even for "summation" your statement is not true.You won't get exactly the same result for a sum if the floating point format is not the same. Also, Matlab is optimized for accuracy, so it is likely to take extra steps beyond using bare FPU instructions to minimize rounding errors.FP is not exact anyway. Deal with it.Question being, why would it matter?If there's a large (but proportionally identical) difference, suspect just a scaling difference. Learn about FFT. Also check that the applied window is the same in both cases.If the difference is modest, then it's just a rounding difference. And it's very likely not to matter whatsoever in your application.

author: ejeffrey
author_rank: Super Contributor
timestamp: January 12, 2023, 07:52:52 pm
content: Quote from: syntax333 on January 11, 2023, 11:43:25 amBut the intermediate calculations are still done with float64. Thats far more important than the input data resolution.

author: ejeffrey
author_rank: Super Contributor
timestamp: January 12, 2023, 07:59:03 pm
content: I don't think floating point precision is actually what is going on here. It looks more like one of windowing difference, fft convention, or handling of the real/imaginary parts.

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 12, 2023, 08:06:38 pm
content: We couldtriviallycheck if OP provided the 256 samples involved. The fact that they haven't bothered indicates they're not interested in helping anyone else verify their findings, or to find out what the reason was.I admit, I even considered making a static HTML+JS page (like I did forFIR filters) that does two FFTs on the given samples, one at default (IEEE 754 Binary64 aka 'double') precision, and the other doing the FFT using specified number of decimal digits of precision (1-15, using the.toPrecision(N)method of the Number type; 7 matching Binary32 or 'float' AKA single precision pretty well). But since OP hasn't bothered to post the original samples, why should anyone else bother either?

author: syntax333
author_rank: Regular Contributor
timestamp: January 13, 2023, 07:20:02 am
content: DataINFFT.txt file contains input samples for both MATLAB fft function and STM32 PreFFTProcess function shown in my original post. STM32fftvsMATLABfft.PNG shows MATLAB fft result for these samples in orange. STM32 fft result for these samples is blue.

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: January 13, 2023, 07:21:49 am
content: You have still not answered the question how you determine them to be different.When I overlay one FFT the one over the other they match perfectly visually.Please, how do you determine them to be different?

author: syntax333
author_rank: Regular Contributor
timestamp: January 13, 2023, 07:25:19 am
content: Quote from: alexanderbrevig on January 13, 2023, 07:21:49 amI plot both fft results in same graph. They are different as I show in STM32fftvsMATLABfft.PNG file.

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: January 13, 2023, 07:29:29 am
content: Right. Sorry! I thought the different plots were channel 1 and 2, and that you compared image 1 and 2 from the first post. My bad.Quote from: syntax333 on January 11, 2023, 11:23:36 amyou even explained it. Apologies.

author: Kalvin
author_rank: Super Contributor
timestamp: January 13, 2023, 08:32:47 am
content: Did you call the initialization functionarm_cfft_init_f32()? In this example code snippet functionarm_cfft_init_f32()is called before performing the actual FFT:https://www.keil.com/pack/doc/CMSIS/DSP/html/arm_fft_bin_example_f32_8c-example.html#_a5Or, did you initialize your FFT state variable from the correct predefined value listed in this header file:https://www.keil.com/pack/doc/CMSIS/DSP/html/arm__const__structs_8h.html#aeb2f0a0be605963264217cc10b7bd3b2

author: syntax333
author_rank: Regular Contributor
timestamp: January 13, 2023, 08:42:22 am
content: Quote from: Kalvin on January 13, 2023, 08:32:47 amYes. Below snipped is in initialization part of my code.Code:arm_cfft_instance_f32 fftInstance;arm_cfft_init_f32(&fftInstance,256);

author: Kalvin
author_rank: Super Contributor
timestamp: January 13, 2023, 08:46:19 am
content: Quote from: syntax333 on January 11, 2023, 10:27:11 amAre you really sure that this real -> imaginary process is something youneedwant to do? From the example code (link in my previous post) seems to be operating with real-valued sample vector, producing complex output vector.

author: Kalvin
author_rank: Super Contributor
timestamp: January 13, 2023, 08:54:22 am
content: Also, check that the vector lengths are used correctly. Pls see the example code, and check that your code matches with the vector lengths accordingly.

author: langwadt
author_rank: Super Contributor
timestamp: January 13, 2023, 09:11:09 am
content: Quote from: ejeffrey on January 12, 2023, 07:52:52 pmare you sure?x=single(rand(1024,1));plot(abs(fft(double(x)))-abs(fft(x)))is not zeros

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 13, 2023, 03:50:14 pm
content: Quote from: syntax333 on January 13, 2023, 07:20:02 amNow we're cooking!The rounding error between single- and double-precision DFT for these samples is less than ±0.000050, and magnitude differences less than ±0.000053.See the attached dft.txt file for the float (single-precision IEEE 754 Binary32) and double (Binary64) magnitudes and real and imaginary components, listed at sufficient precision to be unambiguous/exact when converted to their respective types. (I used a dedicated C program and FFTW3 library for this.)As expected, the Matlab graph agrees with these results.Now, if I only use 128 of your samples, inserting a zero after each sample, I get your "bad" results pretty exactly; see the attached bad.txt.Thus, your error is interleaving zeros ("imaginary components") between the consecutive samples.The DFT your CMSIS DSP library is doing a real-to-complex Discrete Fourier Transform (r2c in FFTW3 planning system); you simply supply it with the real components only, as it does the computations assuming all imaginary components are zeroes (which also speeds things up considerably, which is what you want). This is a computational optimization, and not an approximation or "shortcut", by the way: its results are the same (to within rounding error, i.e. somewhere in the fifth decimal digit, at most, for this sample sequence, using Binary32/float/f32) as doing the computations the "hard way".

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 13, 2023, 06:53:52 pm
content: (In more simple terms, syntax333's bug is in using PreFFTProcess() at all; just likeKalvin suspected, earlier in the thread. The proof is in the two data sets,bad.txtduplicating the error, withdft.txtshowing that when omitted/done correctly, the f32/IEEE 754 Binary32 DFT implementation yields results that only minimally differ from the f64/IEEE Binary64 DFT, using a similar DFT-on-real-data-yielding-complex-results from FFTW3 library.)

author: turny
author_rank: Newbie
timestamp: January 13, 2023, 08:51:39 pm
content: Quote from: Nominal Animal on January 13, 2023, 03:50:14 pmExcept it does not. arm_cfft_f32 is complex to complex, arm_rfft_f32 is real to complex.But I agree that most likely PreFFTProcess is the culprit. One thing syntax333 could do is to take the CMSIS FFT output and do inverse FFT of it in MATLAB and compare the result with the original input. Or just post the code here.

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 13, 2023, 11:33:28 pm
content: Quote from: turny on January 13, 2023, 08:51:39 pmAh, I didn't check that; thanks.In any case, the 512-sample DFT ofCode:12, 0, 10, 0, 13, 0, 7, 0, 11, 0, 11, 0, 18, 0, 20, 0, 16, 0, 12, 0, 8, 0, 6, 0, 2, 0, 4, 0, 0, 0, 2, 0,1, 0, -1, 0, -4, 0, -3, 0, -9, 0, -12, 0, -11, 0, -14, 0, -10, 0, -8, 0, -4, 0, -2, 0, 4, 0, 8, 0, 11, 0, 14, 0,14, 0, 16, 0, 20, 0, 17, 0, 18, 0, 16, 0, 13, 0, 12, 0, 8, 0, 4, 0, -3, 0, -4, 0, -5, 0, -12, 0, -15, 0, -20, 0,-23, 0, -25, 0, -24, 0, -21, 0, -23, 0, -18, 0, -16, 0, -11, 0, -3, 0, 0, 0, 4, 0, 10, 0, 11, 0, 15, 0, 13, 0, 14, 0,14, 0, 16, 0, 19, 0, 15, 0, 20, 0, 21, 0, 22, 0, 20, 0, 17, 0, 17, 0, 15, 0, 13, 0, 14, 0, 8, 0, 11, 0, 11, 0,11, 0, 9, 0, 1, 0, -2, 0, -7, 0, -8, 0, -9, 0, -14, 0, -14, 0, -17, 0, -13, 0, -11, 0, -11, 0, -7, 0, -9, 0, -8, 0,-11, 0, -10, 0, -10, 0, -9, 0, -6, 0, -10, 0, -8, 0, -3, 0, -4, 0, -3, 0, -4, 0, -6, 0, -8, 0, -4, 0, -2, 0, 0, 0,3, 0, 9, 0, 14, 0, 22, 0, 25, 0, 24, 0, 25, 0, 24, 0, 27, 0, 23, 0, 19, 0, 10, 0, 2, 0, 2, 0, 1, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0where each value is a complex number if one does a complex DFT, and real if one does a real DFT –– i.e., first 128 samples fromDataINFFT.txtwith an extra zero after each sample, plus an additional 256 zeroes –– reproduces OP's blue curve.The expected 256-sample DFT of (complex samples if one does a complex DFT, and real if one does a real DFT)Code:12, 10, 13, 7, 11, 11, 18, 20, 16, 12, 8, 6, 2, 4, 0, 2,1, -1, -4, -3, -9,-12,-11,-14,-10, -8, -4, -2, 4, 8, 11, 14,14, 16, 20, 17, 18, 16, 13, 12, 8, 4, -3, -4, -5,-12,-15,-20,-23,-25,-24,-21,-23,-18,-16,-11, -3, 0, 4, 10, 11, 15, 13, 14,14, 16, 19, 15, 20, 21, 22, 20, 17, 17, 15, 13, 14, 8, 11, 11,11, 9, 1, -2, -7, -8, -9,-14,-14,-17,-13,-11,-11, -7, -9, -8,-11,-10,-10, -9, -6,-10, -8, -3, -4, -3, -4, -6, -8, -4, -2, 0,3, 9, 14, 22, 25, 24, 25, 24, 27, 23, 19, 10, 2, 2, 1, 0,-2, -2, 0, 5, 11, 11, 7, 6, 5, 5, 1, -2, -2, -6, -5, -9,-16,-21,-22,-27,-36,-37,-46, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0is shown in red:This matches the STM32fftvsMATLABfft.PNG OP attached toreply #17exactly:I did overlap the two curves in Inkscape (my Gnuplot original is in SVG), and they do match precisely.

author: turny
author_rank: Newbie
timestamp: January 13, 2023, 11:58:40 pm
content: That to me indicates that the values that syntax333 uses to call arm_cfft_f32 are 12, 0, 0, 0, 10, 0, 0, 0, 13, 0, 0, 0 etc (12+0i, 0+0i, 10+0i, 0+0i, 13+0i, 0+0i, ...) instead of 12, 0, 10, 0, 13, 0 etc (12+0i, 10+0i, 13+0i, ...).

author: Nominal Animal
author_rank: Super Contributor
timestamp: January 14, 2023, 12:50:13 am
content: Quote from: turny on January 13, 2023, 11:58:40 pmI modified my wording to hopefully make it clear: by "samples", I mean logical samples as in complex numbers if doing a complex DFT, and real numbers if doing a real DFT; not elements of the array supplied to some specific arm*fft*() call. (Remember, we want the discussion to encompass the Matlab or other-tool derived DFTs for comparison.)(In the frequency domain, I prefer to use the term 'bin', as in 'frequency bin'. These are always complex numbers, and only the first half are useful, since the latter half corresponds to negative frequencies and thus contain no additional information, just a mirrored copy. The very first bin is the DC component, i.e. average of the input samples.)Assuming OP is usinggithub.com/ARM-software/CMSIS/blob/master/CMSIS/DSP_Lib/Source/TransformFunctions/arm_cfft_f32.c:arm_cfft_f32()(or an earlier version), then there are several issues in OP's approach. One is the extra zero insertion, the other is using complex FFT when the samples are purely real.As I showed above, OP's current ARM code uses a DFT over 512 complex samples, where every odd complex number is zero, 0.0+0.0i, and the last 256 complex numbers are also zero, 0.0+0.0i. The first 128 even complex samples are the real values copied from the data OP supplied.For thearm_cfft_f32(), the input array contents are indeed{ 12.0f, 0.0f, 0.0f, 0.0f, 10.0f, 0.0f, 0.0f, 0.0f, 13.0f, 0.0f, 0.0f, 0.0f, ... }, because forarm_cfft_f32(), each consecutive pair of inputf32's forms one complex sample.The proper functions to use with real samples isarm_rfft_fast_f32()(which supersedes the olderarm_rfft_f32()and related functions). The input array to this is simply the float samples; the output array is an array of floats, with each bin described by two consecutive elements. The first pair is the DC bin, with imaginary part always zero (because the DC component for real samples is always real).Exactly where OP's error is, I cannot tell. It could be thattempBuf_1[]already contains complex samples, i.e. all odd indexes (imaginary parts) are already zero, and the call to PreFFTProcess() expands that so that every other complex sample, i.e. every other f32 pair, supplied to arm_cfft_f32(), ends up being zero. Or, it could be a bug in PreFFTProcess() itself. Also, we haven't seen the contents of thearm_cfft_instance_f32 fftInstance; itsfftLenis the number of samples, not the number off32's, supplied toarm_cfft_f32(); here, it should be 256, not 512.