0
$\begingroup$

I have a "test" 50 Hz tone signal, stored as 4-byte floating point values. The sample rate is 5120 Hz. The signal (roughly 0.25 seconds' worth) and the corresponding FFT are shown in this image:

enter image description here

The data file is then processed by converting the floats into 2-byte short values via an amplitude scaler (6.214e-5). The shorts are then scaled back into floats via the same scaler value. I'm doing this as a test case for other processing operations; the "real" data is not a single tone, and may arrive as either shorts or floats, but is always converted to floats before the FFT.

The problem is that in this very simple case, converting the data from float to short and back to float, I seem to be introducing harmonics in the FFT, as shown here:

enter image description here

The harmonics appear at regular 20 Hz separations.

I am trying to understand how these harmonics appear in what ought to be very nearly the same data values. I've compared the data files in MatLab, calculating the differences between data values, and only see differences on the order of +/- 6e-5, which I would expect given the short-to-float conversion.

Part of the processing involves buffering the data into fixed-size arrays, then performing the FFT on that array's worth of data. Could the size of those arrays be implicated in the harmonics showing up? (E.g., perhaps the array size is not some proper multiple of the sample rate?)

A colleague suggested that a sample (or samples) may be getting dropped during the processing, but I am not sure how missing a sample could create these harmonics across the entire data set.

Any suggestions for culprits?

$\endgroup$

1 Answer 1

3
$\begingroup$

I suspect you're clipping the data. The 20 Hz "harmonic" isn't a harmonic of the 50 Hz sinusoid, so it probably doesn't matter what your data is.

Using the python code below, I generate a sine wave like yours:

Sinusoid

and then look at its FFT

FFT of sine wave

Converting the original float sinusoid to a uint16 sinusoid and taking the FFT looks pretty similar

enter image description here

there is some "fuzziness" but nothing as problematic as what your plots show.

However, if I then clip the uint16 pretty severely

Clipped sinusoid

then the FFT I get looks closer to yours

FFT of clipped sinusoid

I don't think your data is as severely clipped as in my example. And it may not be that this is the distortion. However, I do think that some nonlinear distortion is kicking in.

Simply changing data types as you've done shouldn't have that profound an effect.


Code Below

import  numpy  as  np
import matplotlib.pyplot as plt

def ConvertFromFloatToShort(x):
    x_normalized = x/np.max(np.abs(x))
    x_short = np.int16(x_normalized*32767)
    return x_short


f = 50 # Hz
fs = 5120 # Hz
t = np.linspace(0.0,0.25, int(0.25*fs))
x = np.sin(2*np.pi*f*t)

plt.figure(1)
plt.plot(t,x)
plt.title('Original signal')

plt.figure(2)
plt.plot(np.log10(np.abs(np.fft.fft(x))))
plt.title('Original FFT (log scale)')
plt.ylim([-5, 3])


x_short = ConvertFromFloatToShort(x)

plt.figure(3)
plt.plot(np.log10(np.abs(np.fft.fft(x_short))))
plt.title('uint 16 FFT (log scale)')
plt.ylim([0, 7.5])

x_clipped = np.clip(x_short, -32767/2, 32767.2)


plt.figure(4)
plt.plot(np.log10(np.abs(np.fft.fft(x_clipped))))
plt.title('CLIPPED uint 16 FFT (log scale)')
plt.ylim([0, 7.5])
$\endgroup$
2
  • $\begingroup$ Thanks very much - I appreciate the worked example! $\endgroup$ Commented Jan 9, 2023 at 18:10
  • 1
    $\begingroup$ And, yes, it turns out my issue was indeed a clipping problem. The data had been scaled inappropriately, such that when I did my conversion it was well beyond values that can be stored in a 16-bit integer. Thanks again. $\endgroup$ Commented Jan 10, 2023 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.