Chirping a sinusoid - python

Below is my code
where I am wanting to sweep a 400MHz tone between -50Mhz and 50Mhz. Meaning the
sweep should span between 350MHz and 450MHz. But that is no the case. Don't
understand what the reason for this is. It has been suggested that this is because frequency is the derivative of the phase. I have given that a shot too in the commented lines in the 'sweep_sine' function but that does not seem to help either. Any help would be appreciated.
Image of unexpected program output added. As you can see I am able to shift my 400MHz tone to 300MHz. When I try to sweep in a fashion similar to shift; the output is incorrect, as in it does not sweep from 350MHz to 450MHz with the 400MHz tone in the middle.
I am able to sweep the signal correctly now as can be seen in image 2. The time domain signal also looks fine when the signal is of the e^i2*pift form. But when I use a real signal of the form sin(2*pift) the time domain version looks corrupted (image 3). What could be the reason for that? Thanks.
https://i.stack.imgur.com/9H1Dk.png
https://i.stack.imgur.com/Ey3tQ.png
https://i.stack.imgur.com/FzmDS.png
import numpy as np
import matplotlib.pyplot as plt
def gen_sig(freq=380e6, fs=6080e6, n_samp=6400):
ts = 1/fs
t_arr = np.arange(0, n_samp)*ts
sig = np.exp(2 * 1j * np.pi * freq * t_arr)
#sig = np.sin(2 * np.pi * freq * t_arr)
return sig,ts
def freq_shift_sine(sine, ts, shift_freq = 50e6):
tx_sig_t = np.linspace(0, ts*sine.shape[-1], num=sine.shape[-1])
#tx_sig_sqrd = np.square(tx_sig_t)
#hift the sine
freq_shftd_sig = sine * np.exp(1.0j * 2 * np.pi * (shift_freq * tx_sig_t))
#freq_shftd_sig = sine * np.exp(1.0j * np.pi * (shift_freq * tx_sig_sqrd))
return freq_shftd_sig
def sweep_sine(sine, ts, up_lim = 50e6, low_lim = -50e6):
tx_sig_t = np.arange(0, sine.shape[-1])*ts
tx_sig_sqrd = np.square(tx_sig_t)
phi = low_lim*tx_sig_t + (up_lim-low_lim)*(tx_sig_sqrd/(2*ts*sine.shape[-1]))
dopp_shftd_sig = sine * np.exp(1.0j* 2 *np.pi * phi)
return dopp_shftd_sig
if __name__=='__main__':
#generate a sine wave 16 times over sampled
tx_sig, t_samp = gen_sig(freq=400e6, fs=6400e6, n_samp=6400)
#do an fft
tx_sig_fft = np.fft.fft(tx_sig)
#generate freqency axis for fft
freq_arr = np.fft.fftfreq(tx_sig.shape[-1], t_samp)
#shift sine wave
tx_sig_shifted = freq_shift_sine(tx_sig, t_samp, shift_freq = -100e6)
#fft the shifted sine
tx_sig_shftd_fft = np.fft.fft(tx_sig_shifted)
#sweep sine wave by up_lim+low_lim Hz
tx_sig_swept = sweep_sine(tx_sig, t_samp, up_lim = 50e6, low_lim = -50e6)
#fft the swept sine
tx_sig_swept_fft = np.fft.fft(tx_sig_swept)
plt.figure()
plt.plot(freq_arr, abs(tx_sig_fft))
plt.plot(freq_arr, abs(tx_sig_shftd_fft))
plt.plot(freq_arr, abs(tx_sig_swept_fft))
plt.axis([0,1e9, 0, 2e3])
plt.figure()
plt.plot(tx_sig)
plt.plot(tx_sig_shifted)
plt.plot(tx_sig_swept)
plt.axis([0,100, -1.2, 1.2])

I may be wrong but I think the problem is in your signal. You have only a real part of it, and shifting the phase in the complex plane doesn't help so much in the real part.
The possible treatment for this problem is to make the signal a complex one. The best way is to make a Hilbert transform and use it as a signal.
Your code may look like this
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import hilbert
def gen_sine(freq=380e6, fs=6080e6, n_samp=6400):
ts = 1/fs
t_arr = np.arange(0, n_samp)*ts
#sine_sig = np.exp(2 * 1j * np.pi * freq * t_arr)
sine_sig = np.sin(2 * np.pi * freq * t_arr)
return sine_sig,ts
def freq_shift_sine(sine, ts, shift_freq = 50e6):
tx_sig_t = np.linspace(0, ts*sine.shape[-1], num=sine.shape[-1])
#tx_sig_sqrd = np.square(tx_sig_t)
#hift the sine
freq_shftd_sig = hilbert(tx_sig) * np.exp(1.0j * 2 * np.pi * (shift_freq * tx_sig_t))
#freq_shftd_sig = sine * np.exp(1.0j * np.pi * (shift_freq * tx_sig_sqrd))
return freq_shftd_sig
def sweep_sine(sine, ts, up_lim = 50e6, low_lim = -50e6):
#tx_sig_t = np.arange(0, sine.shape[-1])*ts
tx_sig_t = np.linspace(0, ts*sine.shape[-1], num=sine.shape[-1])
#tx_sig_sqrd = np.square(tx_sig_t)
freq_step_arr = np.linspace(low_lim, up_lim, sine.shape[-1])
dopp_shftd_sig = hilbert(tx_sig) * np.exp(1.0j * 2 * np.pi * (freq_step_arr * tx_sig_t))
#dopp_shftd_sig = sine * np.exp(1.0j * np.pi * (freq_step_arr * tx_sig_sqrd))
return dopp_shftd_sig
if __name__=='__main__':
#generate a sine wave 16 times over sampled
tx_sig, t_samp = gen_sine(freq=400e6, fs=6400e6, n_samp=6400)
#do an fft
tx_sig_fft = np.fft.fft(tx_sig)
#generate freqency axis for fft
freq_arr = np.fft.fftfreq(tx_sig.shape[-1], t_samp)
#shift sine wave
tx_sig_shifted = freq_shift_sine(tx_sig, t_samp, shift_freq = -100e6)
#fft the shifted sine
tx_sig_shftd_fft = np.fft.fft(tx_sig_shifted)
#sweep sine wave by up_lim+low_lim Hz
tx_sig_swept = sweep_sine(tx_sig, t_samp, up_lim = 50e6, low_lim = -50e6)
#fft the swept sine
tx_sig_swept_fft = np.fft.fft(tx_sig_swept)
#plt.figure()
#plt.plot(freq_arr, abs(tx_sig_swept_fft))
#plot sine wave fft
#plt.figure()
plt.figure(1)
plt.plot(freq_arr, abs(tx_sig_fft))
plt.plot(freq_arr, abs(tx_sig_shftd_fft))
plt.plot(freq_arr, abs(tx_sig_swept_fft))
plt.axis([0,1e9, 0, 2e3])
plt.figure(2)
plt.specgram(tx_sig_swept, NFFT=80, Fs=6400e6, noverlap=16)
#plt.axis([0,0.000001, 0, 5e6])
plt.figure(3)
plt.subplot(311)
t_time = np.arange(0, tx_sig.shape[-1])*t_samp
plt.plot(t_time, tx_sig)
plt.plot(t_time, np.imag(hilbert(tx_sig)) )
plt.subplot(312)
plt.plot(t_time, tx_sig_shifted)
plt.subplot(313)
plt.plot(t_time, tx_sig_swept )
plt.show()
It produces more or less ok spectrogram and doesn't corrupt the resulted signal. Hope it helps.

You have made a very common mistake. Incrementing both the frequency and the time (when you multiply them to create a phase parameter input for the exp or sine function) at each time step, increments the frequency too much. Instead figure out how much the phase should change at each new time step for the desired frequency at that time step, and add that phase delta instead of doing a multiplication to get a new phase input to your sine or exp function.

Related

scipy Fast fourier transform doesn't recognize the signal

i'm trying to get the frequency of a signal via fourier transform but it's not able to recognize it (sets the peak to f=0). Maybe something is wrong in my code (FULL reprudible code at the end of the page):
PF = fft.fft(Y[0,:])/Npoints #/Npoints to get the true amplitudes
ZF = fft.fft(Y[1,:])/Npoints
freq = fft.fftfreq(Npoints,deltaT)
PF = fft.fftshift(PF) #change of ordering so that the frequencies are increasing
ZF = fft.fftshift(ZF)
freq = fft.fftshift(freq)
plt.plot(freq, np.abs(PF))
plt.show()
plt.plot(T,Y[0,:])
plt.show()
where Npoints is the number of intervals (points) and deltaT is the time spacing of the intervals. You can see that the peak is at f=0
I show also a plot of Y[0,:] (my signal) over time where it's clear that the signal has a characteristic frequency
FULL REPRUDICIBLE CODE
import numpy as np
import matplotlib.pyplot as plt
#numerical integration
from scipy.integrate import solve_ivp
import scipy.fft as fft
r=0.5
g=0.4
e=0.6
H=0.6
m=0.15
#define a vector of K between 0 and 4 with 50 componets
K=np.arange(0.1,4,0.4)
tsteps=np.arange(7200,10000,5)
Npoints=len(tsteps)
deltaT=2800/Npoints #sample spacing
for k in K :
i=0
def RmAmodel(t,y):
return [r*y[0]*(1-y[0]/k)-g*y[0]/(y[0]+H)*y[1], e*g*y[0]/(y[1]+H)*y[1]-m*y[1]]
sol = solve_ivp(RmAmodel, [0,10000], [3,3], t_eval=tsteps) #t_eval specify the points where the solution is desired
T=sol.t
Y=sol.y
vk=[]
for i in range(Npoints):
vk.append(k)
XYZ=[vk,Y[0,:],Y[1,:]]
#check periodicity over P and Z with fourier transform
#try Fourier analysis just for the last value of K
PF = fft.fft(Y[0,:])/Npoints #/Npoints to get the true amplitudes
ZF = fft.fft(Y[1,:])/Npoints
freq = fft.fftfreq(Npoints,deltaT)
PF = fft.fftshift(PF) #change of ordering so that the frequencies are increasing
ZF = fft.fftshift(ZF)
freq = fft.fftshift(freq)
plt.plot(T,Y[0,:])
plt.show()
plt.plot(freq, np.abs(PF))
plt.show()
I can't pinpoint where the problem is. It looks like there is some problem in the fft code. Anyway, I have little time so I will just put a sample code I made before. You can use it as reference or copy-paste it. It should work.
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq
fs = 1000 #sampling frequency
T = 1/fs #sampling period
N = int((1 / T) + 1) #number of sample points for 1 second
t = np.linspace(0, 1, N) #time array
pi = np.pi
sig1 = 1 * np.sin(2*pi*10*t)
sig2 = 2 * np.sin(2*pi*30*t)
sig3 = 3 * np.sin(2*pi*50*t)
#generate signal
signal = sig1 + sig2 + sig3
#plot signal
plt.plot(t, signal)
plt.show()
signal_fft = fft(signal) #getting fft
f2 = np.abs(signal_fft / N) #full spectrum
f1 = f2[:N//2] #half spectrum
f1[1:] = 2*f1[1:] #actual amplitude
freq = fs * np.linspace(0,N/2,int(N/2)) / N #frequency array
#plot fft result
plt.plot(freq, f1)
plt.xlim(0,100)
plt.show()

Multi-taper Spectral Analysis with spectrum in python

I'm using the multi-taper analysis using the spectrum library on python (https://pyspectrum.readthedocs.io/en/latest/install.html), but I can't understand fully the amplitude of the output.
Here a piece of code for illustration:
from spectrum import *
N=500
dt=2*10**-3
# Creating a signal with 2 sinus waves.
x = np.linspace(0.0, N*dt, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
# classical FFT
yf = fft.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*dt), N//2)
# The multitapered method
NW=2.5
k=4
[tapers, eigen] = dpss(N, NW, k)
Sk_complex, weights, eigenvalues=pmtm(y, e=eigen, v=tapers, NFFT=500, show=False)
Sk = abs(Sk_complex)
Sk = np.mean(Sk * np.transpose(weights), axis=0)
# ploting both the results
plt.plot(xf,abs(yf[0:N//2])*dt*2)
plt.plot(xf,Sk[0:N//2])
Both the results are similar and find frequency peak at 50 and 80 Hz.
The classical FFT finds as well the good amplitude (1 and 0.5)
But the multi taper method do not find the proper amplitude. In this example it is around 5 times to important.
Do anyone knows actually how to properly display the results ?
thanks
From my understanding, there is a couple of factors that are at play here.
First, to get the multitaper estimate of the power spectrum density, you should compute like this:
Sk = abs(Sk_complex)**2
Sk = np.mean(Sk * np.transpose(weights), axis=0) * dt
I.e., you need to average the power spectrum, not the Fourier components.
Second, to get the power spectrum, you just need to divide the energy spectrum by N of your estimation using fft and multiply by dt as you did (and you need the **2 to get the power from the fourier components):
plt.plot(xf,abs(yf[0:N//2])**2 / N * dt)
plt.plot(xf,Sk[0:N//2])
Finally, what should be directly comparable, is not so much the amplitude in the power spectrum density, but the total power. You can look at:
print(np.sum(abs(yf[0:N//2])**2/N * dt), np.sum(Sk[0:N//2]))
Which match very closely.
So your whole code becomes:
from spectrum import *
N=500
dt=2*10**-3
# Creating a signal with 2 sinus waves.
x = np.linspace(0.0, N*dt, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
# classical FFT
yf = fft.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*dt), N//2)
# The multitapered method
NW=2.5
k=4
[tapers, eigen] = dpss(N, NW, k)
Sk_complex, weights, eigenvalues=pmtm(y, e=eigen, v=tapers, NFFT=N, show=False)
Sk = abs(Sk_complex)**2
Sk = np.mean(Sk * np.transpose(weights), axis=0) * dt
# ploting both results
plt.figure()
plt.plot(xf,abs(yf[0:N//2])**2 / N * dt)
plt.plot(xf,Sk[0:N//2])
# ploting both results in log scale
plt.semilogy(xf, abs(yf[0:N // 2]) ** 2 / N * dt)
plt.semilogy(xf, Sk[0:N // 2])
# comparing total power
print(np.sum(abs(yf[0:N//2])**2 / N * dt), np.sum(Sk[0:N//2]))

Reading the data from file and plotting fft

I want to read data from the file and plot the fft of it.
I have used the scipy's fft function to plot the fft of the synthetically generated signal data however while I am trying to save the generated signal data and plot the fft I am getting the peaks as I was getting but the value of the amplitude and frequency is off as compared to the Original plotting.I found problem with the second fft function. Can anyone help me with the same.
import numpy as np
import pandas as pd
import scipy
from scipy.fftpack import fft,fftfreq
import matplotlib.pyplot as plt
import csv
#Function to plot the raw data
def raw_plot(signals,time):
'''Plot the raw values in the time domain '''
plt.figure(0)
plt.plot(time,signals)
plt.xlabel("Time(s)")
plt.ylabel("Amplitude(g)")
plt.show()
#Function to plot the fft of the data
def fft_signal(signal, sampling_frequency, timestart, timeend):
T = 1 / sampling_frequency
N = (timeend - timestart) * sampling_frequency
x = np.linspace(0.0, 1.0 / (2.0 * T), int(N / 2))
# print("Values of X are :",x)
# print(len(x))
yr2 = fft(signal)
y2 = 2 / N * np.abs(yr2[0:int(N / 2)])
# amp_spec = abs(fft(signal))/N
# freq = np.linspace(0,2,num=N)
plt.figure(2)
plt.plot(x, y2)
plt.xlabel("Frequency")
plt.ylabel("Amplitude")
plt.show()
def fftsignal2(data, sampling_freq = 100, timestep = 1 ):
N = len(data)
FS = 1000
T= 1/FS
timestep = timestep
yr2 = fft(data)
y2 = 2 / N * np.abs(yr2[0:int(N / 2)])
# f= np.arange(0,N)*FS/N
# f= np.linspace(0,1.0/T,N)
frq = np.fft.fftfreq(N,d=timestep)
# f= np.linspace(0.0,int(N/2)*sampling_freq)
frq= frq[0:len(frq)//2]
print("Frequencies Length :",len(frq))
print("DATA LENGTH :",len(y2))
plt.figure(3)
plt.plot(frq,y2)
plt.xlabel("Frequency")
plt.ylabel("Amplitude")
plt.show()
Mag1_Hz = 70
Mag1_AMP = 10
Mag4_AMP = 60
Mag4_Hz= 50
Mag2_Hz = 20
Mag2_AMP = 8
time_start = 0
time_end = 4
Sampling_frequency = 100
No_of_samples = (time_end-time_start) * Sampling_frequency
time = np.linspace(time_start,time_end,No_of_samples)
#Generate the signals values
data_signal = Mag1_AMP * np.sin(2* np.pi * Mag1_Hz * time ) + Mag2_AMP * np.cos(2* np.pi * Mag2_Hz * time)
raw_plot(signals=data_signal[:800],time=time[:800])
fft_signal(data_signal,sampling_frequency=Sampling_frequency,timestart=time_start,timeend=time_end)
# for (time, datavalues) in zip(time,data_signal):
# with open("data_signals_27.csv","a",newline="") as file:
# writefile = csv.writer(file)
# print("Time%f,Data %f"%(time,datavalues))
# print(time,datavalues)
# writefile.writerow([time,datavalues])
fftsignal2(data= data_signal, sampling_freq= Sampling_frequency,timestep=2)
The peaks are coming right in the fft_signal function no 1 while I am trying to construct the fftsignal2 to plot the fft of the data so the peaks which I am getting is correct but the magnitude and frequency is off.I don't understand why ? Can anyone help me with that.

Filtering signal frequency in Python

I tried to filter some signal with fft.
The signal I am working on is quite complicated and im not really experienced in this topic.
That's why I created a simple sin wave 3Hz and tried to cut off the 3 Hz.
and so far, so good
import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fftfreq, irfft, rfft
t = np.linspace(0, 2*np.pi, 1000, endpoint=True)
f = 3.0 # Frequency in Hz
A = 100.0 # Amplitude in Unit
s = A * np.sin(2*np.pi*f*t) # Signal
dt = t[1] - t[0] # Sample Time
W = fftfreq(s.size, d=dt)
f_signal = rfft(s)
cut_f_signal = f_signal.copy()
cut_f_signal[(np.abs(W)>3)] = 0 # cut signal above 3Hz
cs = irfft(cut_f_signal)
fig = plt.figure(figsize=(10,5))
plt.plot(s)
plt.plot(cs)
What i expected
What i got
I don't really know where the noise is coming from.
I think it is some basic stuff, but i dont get it.
Can someone explain to to me?
Edit
Just further information
Frequency
yf = fft(s)
N = s.size
xf = np.linspace(0, fa/2, N/2, endpoint=True)
fig, ax = plt.subplots()
ax.plot(xf,(2.0/N * np.abs(yf[:N//2])))
plt.xlabel('Frequency ($Hz$)')
plt.ylabel('Amplitude ($Unit$)')
plt.show()
You could change the way you create your signal and use a sample frequency:
fs = 1000
t = np.linspace(0, 1000 / fs, 1000, endpoint=False) # 1000 samples
f = 3.0 # Frequency in Hz
A = 100.0 # Amplitude in Unit
s = A * np.sin(2*np.pi*f*t) # Signal
dt = 1/fs
And here the whole code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.fftpack import fftfreq, irfft, rfft
fs = 1000
t = np.linspace(0, 1000 / fs, 1000, endpoint=False)
f = 3.0 # Frequency in Hz
A = 100.0 # Amplitude in Unit
s = A * np.sin(2*np.pi*f*t) # Signal
dt = 1/fs
W = fftfreq(s.size, d=dt)
f_signal = rfft(s)
cut_f_signal = f_signal.copy()
cut_f_signal[(np.abs(W)>3)] = 0 # cut signal above 3Hz
cs = irfft(cut_f_signal)
fig = plt.figure(figsize=(10,5))
plt.plot(s)
plt.plot(cs)
And with f = 3.0 Hz and (np.abs(W) >= 3):
And with f = 1.0 Hz:
Just some additional information about why A. As solution works better than yours:
A. A's model doesn't include any non-integer frequencies in its Solution and after filtering out the higher frequencies the result looks like:
1.8691714842589136e-12 * exp(2*pi*n*t*0.0)
1.033507502555532e-12 * exp(2*pi*n*t*1.0)
2.439774536202658e-12 * exp(2*pi*n*t*2.0)
-8.346741339115191e-13 * exp(2*pi*n*t*3.0)
-5.817427588021649e-15 * exp(2*pi*n*t*-3.0)
4.476938066992472e-14 * exp(2*pi*n*t*-2.0)
-3.8680170177940454e-13 * exp(2*pi*n*t*-1.0)
while your solution includes components like:
...
177.05936105690256 * exp(2*pi*n*t*1.5899578814880346)
339.28717376420747 * exp(2*pi*n*t*1.7489536696368382)
219.76658524130005 * exp(2*pi*n*t*1.9079494577856417)
352.1094590251063 * exp(2*pi*n*t*2.0669452459344453)
267.23939871205346 * exp(2*pi*n*t*2.2259410340832484)
368.3230130593005 * exp(2*pi*n*t*2.384936822232052)
321.0888818355804 * exp(2*pi*n*t*2.5439326103808555)
...
Please refer to this question regarding possible side effects of zeroing FFT bins out.

How do I generate a spectrogram of a 1D signal in python?

I'm not sure how to do this and I was given an example, spectrogram e.g. but this is in 2D.
I have code here that generates a mix of frequencies and I can pick these out in the fft, how may I
see these in a spectrogram? I appreciate that the frequencies in my example don't change over time; so does this mean I'll see a straight line across the spectrogram?
my code and the output image:
# create a wave with 1Mhz and 0.5Mhz frequencies
dt = 2e-9
t = np.arange(0, 10e-6, dt)
y = np.cos(2 * pi * 1e6 * t) + (np.cos(2 * pi * 2e6 *t) * np.cos(2 * pi * 2e6 * t))
y *= np.hanning(len(y))
yy = np.concatenate((y, ([0] * 10 * len(y))))
# FFT of this
Fs = 1 / dt # sampling rate, Fs = 500MHz = 1/2ns
n = len(yy) # length of the signal
k = np.arange(n)
T = n / Fs
frq = k / T # two sides frequency range
frq = frq[range(n / 2)] # one side frequency range
Y = fft(yy) / n # fft computing and normalization
Y = Y[range(n / 2)] / max(Y[range(n / 2)])
# plotting the data
subplot(3, 1, 1)
plot(t * 1e3, y, 'r')
xlabel('Time (micro seconds)')
ylabel('Amplitude')
grid()
# plotting the spectrum
subplot(3, 1, 2)
plot(frq[0:600], abs(Y[0:600]), 'k')
xlabel('Freq (Hz)')
ylabel('|Y(freq)|')
grid()
# plotting the specgram
subplot(3, 1, 3)
Pxx, freqs, bins, im = specgram(y, NFFT=512, Fs=Fs, noverlap=10)
show()
What you have is technically correct, but you just need to look at a signal with an interesting spectrogram. For that, you need the frequency to vary with time. (And for that to happen, you need many oscillations, since it takes a few oscillations to establish a frequency, and then you need many of these to have the frequency change with time in an interesting way.)
Below I've modified you code as little as possible to get a frequency that does something interesting (fscale just ramps the frequency over time). I'm posting all the code to get this to work, but I only change three of the top four lines.
# create a wave with 1Mhz and 0.5Mhz frequencies
dt = 40e-9
t = np.arange(0, 1000e-6, dt)
fscale = t/max(t)
y = np.cos(2 * pi * 1e6 * t*fscale) + (np.cos(2 * pi * 2e6 *t*fscale) * np.cos(2 * pi * 2e6 * t*fscale))
y *= np.hanning(len(y))
yy = np.concatenate((y, ([0] * 10 * len(y))))
# FFT of this
Fs = 1 / dt # sampling rate, Fs = 500MHz = 1/2ns
n = len(yy) # length of the signal
k = np.arange(n)
T = n / Fs
frq = k / T # two sides frequency range
frq = frq[range(n / 2)] # one side frequency range
Y = fft(yy) / n # fft computing and normalization
Y = Y[range(n / 2)] / max(Y[range(n / 2)])
# plotting the data
subplot(3, 1, 1)
plot(t * 1e3, y, 'r')
xlabel('Time (micro seconds)')
ylabel('Amplitude')
grid()
# plotting the spectrum
subplot(3, 1, 2)
plot(frq[0:600], abs(Y[0:600]), 'k')
xlabel('Freq (Hz)')
ylabel('|Y(freq)|')
grid()
# plotting the specgram
subplot(3, 1, 3)
Pxx, freqs, bins, im = specgram(y, NFFT=512, Fs=Fs, noverlap=10)
show()
Also, note here that only the spectrogram is useful. If you can see a good waveform or spectra, the spectrogram probably won't be interesting: 1) if the waveform is clear you probably don't have enough data and time over which the frequency is both well defined and changes enough to be interesting; 2) if the full spectra is clear, you probably don't have enough variation in frequency for the spectrogram, since the spectrum is basically just an average of what you see changing with time in the spectrogram.
If you really want to see the spectrogram of your original signal, you just need to zoom on the y-axis to see the peaks you are expecting (note that the spectrogram y-axis is 2.5e8, must larger than in your spectrum):
To get what you're after:
1) sample the 1d waveform at high frequency (at least 5 times the frequency of its highest frequency component)
2) use blocks of samples (powers of 2 like 1024,16384,etc) to compute an FFT
3) for each spectrum plot a vertical line of pixels whose color represents the amplitude of each frequency.
4) repeat steps 2 and 3 for each block of samples.
In your case, the plot has a whole rainbow of colors which should not be present with only a couple very distinct frequencies. Your spectral plot has rather wide bands around the peaks but that could be due to a low sampling rate and smooth plotting.
I am just starting on Python 3.6
Thank you for the Spectrogram sample code!
However with Python 3.6 I struggled a bit to make this sample spectrogram code to work (functions calls and float division
I have edited code so it now works on python 3.6 for my python newbies pals out there.
Enjoy
'''
Original Script for Python 2.7
https://stackoverflow.com/questions/19052324/how-do-i-generate-a-spectrogram-of-a-1d-signal-in-python
Modified in August 2017 for Python 3.6
Python 2.7 two integers / Division generate Integer
Python 3.6 two integers / Division generate Float
Python 3.6 two integers // Division generate integer
'''
import numpy as np
from scipy import fftpack
import matplotlib.pyplot as plt
dt = 40e-9
t = np.arange(0, 1000e-6, dt)
fscale = t/max(t)
y = np.cos(2 * np.pi * 1e6 * t*fscale) + (np.cos(2 * np.pi * 2e6 *t*fscale) * np.cos(2 * np.pi * 2e6 * t*fscale))
y *= np.hanning(len(y))
yy = np.concatenate((y, ([0] * 10 * len(y))))
# FFT of this
Fs = 1 / dt # sampling rate, Fs = 500MHz = 1/2ns
n = len(yy) # length of the signal
k = np.arange(n)
T = n / Fs
frq = k / T # two sides frequency range
frq = frq[range(n // 2)] # one side frequency range
Y = fftpack.fft(yy) / n # fft computing and normalization
Y = Y[range(n // 2)] / max(Y[range(n // 2)])
# plotting the data
plt.subplot(3, 1, 1)
plt.plot(t * 1e3, y, 'r')
plt.xlabel('Time (micro seconds)')
plt.ylabel('Amplitude')
plt.grid()
# plotting the spectrum
plt.subplot(3, 1, 2)
plt.plot(frq[0:600], abs(Y[0:600]), 'k')
plt.xlabel('Freq (Hz)')
plt.ylabel('|Y(freq)|')
plt.grid()
# plotting the specgram
plt.subplot(3, 1, 3)
Pxx, freqs, bins, im = plt.specgram(y, NFFT=512, Fs=Fs, noverlap=10)
plt.show()

Categories

Resources