I am trying to replicate a spectrogram from MATLAB in Python. I've read other posts but they either don't use complex data or the data doesn't match between languages.
I've defined my FFT length (nff
), sample rate (fs
), overlap (nov
) and window (hamming) to all be the same between MATLAB and Python.
Here is the spectrogram from scipy and you can see the issue is the scaling is incorrect. I see that Matlab handles windowing differently than scipy. In scipy your window length and segment value (nperseg
) must be the same. This is not the case in Matlab. Also, in Matlab your FFT length does not have to be larger than your window length however that is a requirement in scipy.
I've tried to keep the parameters the same between Matlab and Python but I still am getting the wrong matrix values for plotting. I've been banging my head against the wall on this for longer than I'd like to admit, any help would be great.
MATLAB:
fs = 50000;
t = (0:1/fs:10);
x = sin(2*pi*10000*t)+sin(2*pi*20000*t);
nsc = floor(50e-3*fs); % Section Length
nov = floor(nsc*0.90); % Percent Overlap
nff = 2^12; % FFT Length
%Plot Spectrogram
[Sxx, spec_f,t] = spectrogram(x,hamming(nsc),nov,nff,fs);
Sxx_dB = 10*log10(abs(Sxx));
figure;
imagesc(t,spec_f,Sxx_dB)
ylabel('Frequency (kHz)','Fontsize',12,'Interpreter','latex')
xlabel('Time (sec)','Fontsize',12,'Interpreter','latex')
colorbar
colormap('jet')
set(gca,'Fontsize',12);
set(gca, 'YDir','normal')
Python:
import numpy as np
from scipy import signal
import math
from matplotlib import pyplot as plt
fs = 50000
t = np.arange(0, 10, 1/fs)
x = np.sin(2*np.pi*10000*t) + np.sin(2*np.pi*20000*t)
nsc = math.floor(50e-3*fs);
nov = math.floor(nsc*0.90);
nff = 2**12;
f,t,Sxx = signal.spectrogram(x, fs=fs, window=np.hamming(nsc),
noverlap=nov, nperseg=nsc, nfft=nff, mode='complex')
fig1 = plt.figure()
im1= plt.pcolormesh(t,f,10*np.log10(np.abs(Sxx)), cmap='jet')
fig1.colorbar(im1).set_label('Intensity (dB)')
plt.xlabel('Time [sec]')
plt.ylabel('Frequency [Hz]')
plt.show()