Square wave FFT results show frequency that seems too low?

I have created an open source plotting package for a low cost oscilloscope - see link here: GitHub Plotting Software

But during my testing I have one result that may be wrong: a noisy 7 kHz square wave shows an FFT peak at 6.25 kHz. Can this be correct, or am I missing something?

Here is a python code extract for my FFT

# stack FFT plot in this figure vertically
plt.subplot(grid_stack[grid_row, :])

# Reduce signal_data array size by a factor of n (used to reduce the frequency range)
n = self.n
updated_df_data = self.reduce_sample_array(self.df_data, n)

# update sample rate and array size
sample_rate = float(self.info_dict['rate'])/n

# Calculate y-axis magnitude scaled to same units as y-axis in heading2Use data (ie volts)

# Calculate x axis as frequency in Hz
x = fftfreq(array_size, 1 / float(sample_rate))
x_half = x[:x.size//2]

freq_units, freq_multiplier = rescale_frequency(x_half)
x_half = rescale_data(x_half, 10 ** freq_multiplier)

y_half = abs(yf)[:array_size//2]
engr_power_v = calculate_scale(y_half)
y_half = rescale_data(y_half, 10**engr_power_v)

plt.plot(x_half, y_half, color=self.iplot_colors[-1])

Sample plot with clean square wave that seems to be ok Sample plot with noisy square wave that seems to have wrong peak value (6.25 instead of 7 kHz) Any comments or suggestions are welcome as this is my first python signal analysis software project.

• It's difficult to know what's going on without also knowing what self.reduce_sample_array() is doing and what the size of the FFT and sample rate are. Oct 16 '19 at 7:34
• Hi @kippertoffee Sorry I can't get code to display using back ticks so the code looks too confusing in this comment. But the function is only one line of code: return signal_data = signal_data[::n] Oct 16 '19 at 8:32
• Of course the full code is available on github with the fft class along with self.reduce_sample_array() located here: github.com/RonFredericks/ADALM2000-Scope-CSV-Plot/blob/master/… Oct 16 '19 at 8:34
• The sample rate and samples can be seen from the "lab report" that prints after the plot. It too is in the github repository here: github.com/RonFredericks/ADALM2000-Scope-CSV-Plot/blob/master/… Oct 16 '19 at 8:41
• @RonatBiophysicsLab The second square wave doesn't look "noisy" to me; rather it looks like it is distorted. Distortion is very different from noise: noise adds stuff to the spectrum, whereas distortion can also take stuff away, and in general change the shape of the original spectrum in ways that noise can't. In order to answer your question, I'd need to know what was the process to generate that distorted wave. Also: one of your plots is confusing because you show two channels; it's not clear which should we pay attention to.
– MBaz
Oct 16 '19 at 13:28

The frequency resolution of your FFT is determined by its length and the sampling rate.

In your second plot you show Fs = 10e6 and 3200 samples. Assuming you don't zero-pad these samples when you pass them to the FFT this gives you a frequency resolution of 3125Hz:

$$3215 = \frac{10 \times 10^6}{3200}$$

So at lower frequencies you have frequency bins as 0Hz, 3125Hz, 6250Hz and 9375Hz. The largest peak in the FFT will be in the bin closest to the signal fundamental frequency.

If you want better resolution you either need to capture more samples, or append a load of zeros to the end of your time series (zero-padding).

I would recommend reading about windowing for FFT analysis also, especially if you go down the zero-padding route.

As an aside: the reduce_sample_array function seems to decimate the time series by simply discarding samples. You really should be doing proper down-sampling here, to avoid aliasing.

• Thank you kippertoffee. You bring out some good points. I am going back to the books and my code base to follow your suggestions. Oct 21 '19 at 22:33