I integrate a system of 400 differential equations using odeint (the first 200 equations are the equation of x component of 200 neurons, and the other 200 of y component). So the main body of my code that does the integration is this
t_final = 100.0
dt = 0.01
t = np.arange(0, t_final, dt)
sol = odeint(full_derivative, z0, t)
x10 = sol[:,9]
y10 = sol[:,209]
It doesn't matter which is the model that I use (I don't want to make things more complicated), but the integration part is correct. In x10 there is the signal of x-component for the 10th oscillator of my system, which looks like that
It is obvious that this is a periodic signal with a specific period and frequency. So I want to do a Fourier transform to find this frequency. I use this code to do the transform
from scipy import fftpack
f_s = len(t)//2
X = fftpack.fft(x10)
freqs = fftpack.fftfreq(len(x10)) * f_s
fig, ax = plt.subplots()
ax.stem(freqs, np.abs(X))
ax.set_xlabel('Frequency in Hertz [Hz]')
ax.set_ylabel('Frequency Domain (Spectrum) Magnitude')
ax.set_xlim(-f_s / 2, f_s / 2)
#ax.set_ylim(-5, 110)
and the result that I take is this (which is not very beautiful because it shows that the frequency is approximately zero).
What can I do to fix the bug in my code?
p.s. Maybe in this example it is relatively obvious which is the frequency of the system, but if I change the parameters of my problem I can end up in more complicated solutions. This is the reason why I want to do a fourier transform.
The plot makes sense to me if the time unit in the first plot is second, because then you should have an important frequency component close to 0.1Hz.
I see in the first part you are using dt = 0.01 and I understand this is the sampling interval. In second you are using f_s = len(t) // 2 that should be the one 1.0/dt this will actually make the frequency you will find even smaller since now f_s will be 100 instead of 5000, but the frequency you are searching is still ~0.2% of the sampling frequency, so you have will have to zoom in to the region of interest. Other thing to pay attention is that if the signal has non-zero mean there will be a peak corresponding to frequency zero.
Related
I need your help because I want to code the movement of a tower for a sinusoidal excitation. The problem is that when I plot the result, there is like a sinusoidal noise which looks abnormal and I don't know where does it come from... I was indeed expecting a more smooth curve as it is normally the case for a driven damped harmonic oscillator.
Below is the equation of the movement:
ddx1 + (f1/m1)*dx1 + (k1/m1)*x1 = omega^2*Em*sin(omega*t)
with the initial conditions: x0 = 0 m and v0=dx0=0 m/s
here is my code:
from math import *
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
#params
m1=264000000. # kg
f1 = 5000000. # kg/s
k1=225000000. # N/m
#initial displacement of the tower:
x0 = 0. # m
dx0 = 0. # m/s
N=1000000
duration=200
time = np.linspace(0, duration, N)
# Creating the excitation
#sinusoidal excitation
def entry(Em,f,t):
omega = 2*np.pi*f
return -omega**2*Em*np.sin(omega*t)
# Equation: ddx1 + (f1/m1)*dx1 + (k1/m1)*x1 = omega^2*Em*sin(omega*t)
# Solving
def dX(X,t):
#X = [x1, dx1]
A=np.array([[ 0 , 1 ],
[-k1/m1, -f1/m1]])
B=np.array([0, -entry(1,50,t)])
dX=np.dot(A,X)+B
return dX
result = odeint(dX,[x0,dx0],time)
plt.plot(time, result[:, 0])
plt.show()
And here are some pictures:
a first picture
and here when I zoom-in
Could you please tell me what is wrong with my code?
Thank you by advance for your help!
[EDIT] I had tried the code for smaller frequencies and it was more what I expected. What I hadn't thought of is as pointed out by JustLearning, that the difference between the natural frequency and the driving frequency is very important and therefore it is in fact normal to have these micro oscillations. Concerning the value of the parameters, they are indeed very important because they are those of the Taipei tower. But as there is each time a ratio of all these quantities, I think (but I could be wrong) that python does not bother doing the calculations.
I am really new to this so thank you for answering so quickly and helping me.
Assessing what's wrong with your ODE purely based on your plots is probably not wise. In order to check whether your code makes sense when run, you should probably go for convergence tests: pick a known analytic solution and check that the L2 norm of |numerical - analytic| decreases as expected as you make the timestep smaller.
That said, by only looking at the oscillation on top of the oscillation from your plots, what you see is nothing more than a superposition between the not-forced damped oscillation, and the forcing term. The reason why this superposed frequency is so microscopic is because it is roughly FIFTY! times larger than the natural+damped frequency of the oscillator. If you change 50 in entry by something smaller, say 1, you will find that both the natural+damped oscillation and the forcing will superpose with roughly similar frequency. Try 0.1 for an even more comparable superposition of all oscillations in play.
By the way, given your very crazy large parameters, you truly may want to do a convergence test and, if not successful, try some ODE solvers that can handle stiff ODEs -- something that the odeint default solver can' manage most of the time!
I have data from the accelerometer in m/s2 (Y-axis) for a time period in seconds (X-axis). I would like to convert this data real-time so that I get the value of an acceleration related to the frequency in Hz. I know that, for example, there is an FFT function in numpy, but I have no idea at all how to use it. I would appreciate, if somebody could provide an example code to convert the raw data (Y: m/s2, X: s) to the desired data (Y: m/s2, X: Hz). It should not be necessarily exactly this function. Thanks!
First, let's create a time-domain signal.
For simplicity, I will create a sine wave with frequency components 12Hz and 24Hz and you can assume the unit of the values are m/s^2:
import numpy as np
import matplotlib.pyplot as plt
# This would be the actual sample rate of your signal
# since you didn't provide that, I just picked one
# big enough to make our graphs look pretty
sample_rate = 22050
# To produce a 1-second wave
length = 1
# The x-axis of your time-domain signal
t = np.linspace(0, length, sample_rate * length)
# A signal with 2 frequency components
# - 12Hz and 24Hz
y = np.sin(12 * (2 * np.pi) * t) + 0.5*np.sin(24 * (2 * np.pi) * t)
# Plot time domain signal
plt.plot(t, y)
plt.xlabel("Time (s)")
plt.show()
This will output:
Now, we continue on with the script by taking the Fourier transform of our original time-domain signal and then creating the magnitude spectrum (since that gives us a better way to visualize how each component is contributing than the phase spectrum):
# This returns the fourier transform coeficients as complex numbers
transformed_y = np.fft.fft(y)
# Take the absolute value of the complex numbers for magnitude spectrum
freqs_magnitude = np.abs(transformed_y)
# Create frequency x-axis that will span up to sample_rate
freq_axis = np.linspace(0, sample_rate, len(freqs_magnitude))
# Plot frequency domain
plt.plot(freq_axis, freqs_magnitude)
plt.xlabel("Frequency (Hz)")
plt.xlim(0, 100)
plt.show()
So now we can visualize the frequency-domain:
Notice how the magnitude of the 24Hz component is about half of the 12Hz component. That is because I purposely timed the 24Hz component by 0.5 on the time-domain signal, so the 24Hz component 'contributes' less to the overall signal, hence we get this halved spike for that component.
Note, also, that the y-axis of our output signal is not really in m/s^2 per Hz as you wanted. But you could compute the actual m/s^2 values by taking the integral over your desired frequency band.
I'll leave the jupyter notebook I created available here, feel free to use it and open issues if you have any problems with it.
Why is the amplitude I compute far, far away from original after fast Fourier transform (FFT)?
I have a signal with 1024 points and sampling frequency of 1/120000. I apply the fast Fourier transform in Python with scipy.fftpack. I normalize the calculated magnitude by number of bins and multiply by 2 as I plot only positive values.
As my initial signal amplitude is around 64 dB, I get very low amplitude values less then 1.
Please see my code.
Signal = well.ReadWellData(SignalNDB)
y, x = Signal.GetData(numpy=np)
N = y.size # Number of sample points 1024 ...
T = 1/120000 # sampling frequency (sec)
x = np.linspace(0.0, N*T, N)
yf = abs(fft(y)) # Perform fft returning Magnitude
xf = np.linspace(0.0, 1.0/(2.0*T), N//2) # Calculatel frequency bins
freqs = fftfreq(N, T)
ax1=plt.subplot(211)
ax1.plot(x,y)
plt.grid()
ax2=plt.subplot(212)
yf2 = 2/N * np.abs(yf[0:N//2]); # Normalize Magnitude by number of bins and multiply by 2
ax2.semilogy(xf, yf2) # freq vs ampl - positive only freq
plt.grid()
ax1.set_title(["check"])
#ax2.set_xlim([0,4000])
plt.show()
Please see my plot:
EDIT:
Finally my signal Amplitude after fft is exactly what I expected. What I did.
First I did fft for signal in mV. Then I converted the results to dB as per the formula: 20*log10(mV)+60; where 60 represents 1 mV proveded by the tool manufacturer.Therefore dB values presented on a linear scale format # the bottom plot rather than on the log format.
Please see the resulting plot below.
Results
Looks good to me. The FFT, or the Fourier transform in general, gives you the representation of your time-domain signal in the frequencies domain.
By taking a look at your signal, you have two main components : something oscillating at around 500Hz (period of 0.002s) and an offset (which corresponds to freq = 0Hz). Looking at the result of the FFT, we can see mainly two peaks : one at 0Hz and the other one could be at 500Hz (difficult to be sure without zooming on the signal).
The only relation between the intensities is defined by the Parseval's theorem, but having a signal oscillating around 64dB doesn't mean its FFT should have values close to 64dB. I suggest you take a look here.
I want to compute this integral $\frac{1}{L}\int_{-\infty}^{t}H(t^{'})\exp(-\frac{R}{L}(t-t^{'}))dt^{'}$ using numpy.convolution, where $H(t)$ is heavside function. I am supposed to get this equals to $\exp(-\frac{R}{L}t)H(t)$
below is what I did,
I changed the limitation of the integral into -inf to +inf by change of variable multiplying a different H(t) then I used this as my function to convolve with H(t)(the one inside the integral), but the output plot is definitely not a exp function, neither I could find any mistakes in my code, please help, any hint or suggestions will be appreciated!
import numpy as np
import matplotlib.pyplot as plt
R = 1e3
L = 3.
delta = 1
Nf = 100
Nw = 200
k = np.arange(0,Nw,delta)
dt = 0.1e-3
tk = k*dt
Ng = Nf + Nw -2
n = np.arange(0,Nf+Nw-1,delta)
tn = n*dt
#define H
def H(n):
H = np.ones(n)
H[0] = 0.5
return H
#build ftns that get convoluted
f = H(Nf)
w = np.exp((-R/L)*tk)*H(Nw)
#return the value of I
It = np.convolve(w,f)/L
#return the value of Voutput, b(t)
b = H(Ng+1) - R*It
plt.plot(tn,b,'o')
plt.show()
The issue with your code is not so much programming as it is conceptual. Rewrite the convolution as Integral[HeavisideTheta[t-t']*Exp[-R/L * t'], -Inf, t] (that's Mathematica code) and upon inspection you find that H(t-t') is always 1 within the limits (except for at t'=t which is the integration limit... but that's not important). So in reality you're not actually performing a complete convolution... you're basically just taking half (or a third) of the convolution.
If you think of a convolution as inverting one sequence and then going one shift at the time and adding it all up (see http://en.wikipedia.org/wiki/Convolution#Derivations - Visual Explanation of Convolution) then what you want is the middle half... i.e. only when they're overlapping. You don't want the lead-in (4-th graph down: http://en.wikipedia.org/wiki/File:Convolution3.svg). You do want the lead-out.
Now the easiest way to fix your code is as such:
#build ftns that get convoluted
f = H(Nf)
w = np.exp((-R/L)*tk)*H(Nw)
#return the value of I
It = np.convolve(w,f)/L
max_ind = np.argmax(It)
print max_ind
It1 = It[max_ind:]
The lead-in is the only time when the convolution integral (technically sum in our case) increases... thus after the lead-in is finished the convolution integral follows Exp[-x]... so you tell python to only take values after the maximum is achieved.
#return the value of Voutput, b(t) works perfectly now!
Note: Since you need the lead-out you can't use np.convolve(a,b, mode = 'valid').
So It1 looks like:
b(t) using It1 looks like:
There is no way you can ever get exp(-x) as the general form because the equation for b(t) is given by 1 - R*exp(-x)... It can't mathematically follow an exp(-x) form. At this point there are 3 things:
The units don't really make sense... check them. The Heaviside function is 1 and R*It1 is about 10,000. I'm not sure this is an issue but just in case, the normalized curve looks as such:
You can get an exp(-x) form if you use b(t) = R*It1 - H(t)... the code for that is here (You might have to normalize depending on your needs):
b = R*It1 - H(len(It1))
# print len(tn)
plt.plot(tn[:len(b)], b,'o')
plt.show()
And the plot looks like:
Your question might still not be resolved in which case you need to explain what exactly you think was wrong. With the info you've given me... b(t) can never have an Exp[-x] form unless the equation for b(t) is messed with. As it stands in your original code It1 follows Exp[-x] in form but b(t) cannot.
I think there's a bit of confusion here about convolution. We use convolution in the time domain to calculate the response of a linear system to an arbitrary input. To do this, we need to know the impulse response of the system. Be careful switching between continuous and discrete systems - see e.g. http://en.wikipedia.org/wiki/Impulse_invariance.
The (continuous) impulse response of your system (which I assume to be for the resistor voltage of an L-R circuit) I have defined for convenience as a function of time t: IR = lambda t: (R/L)*np.exp(-(R/L)*t) * H.
I have also assumed that your input is the Heaviside step function, which I've defined on the time interval [0, 1], for a timestep of 0.001 s.
When we convolve (discretely), we effectively flip one function around and slide it along the other one, multiplying corresponding values and then taking the sum. To use the continuous impulse response with a step function which actually comprises of a sequence of Dirac delta functions, we need to multiply the continuous impulse response by the time step dt, as described in the Wikipedia link above on impulse invariance. NB - setting H[0] = 0.5 is also important.
We can visualise this operation below. Any given red marker represents the response at a given time t, and is the "sum-product" of the green input and a flipped impulse response shifted to the right by t. I've tried to show this with a few grey impulse responses.
The code to do the calculation is here.
import numpy as np
import matplotlib.pyplot as plt
R = 1e3 # Resistance
L = 3. #Inductance
dt = 0.001 # Millisecond timestep
# Define interval 1 second long, interval dt
t = np.arange(0, 1, dt)
# Define step function
H = np.ones_like(t)
H[0] = 0.5 # Correction for impulse invariance (cf http://en.wikipedia.org/wiki/Impulse_invariance)
# RL circuit - resistor voltage impulse response (cf http://en.wikipedia.org/wiki/RL_circuit)
IR = lambda t: (R/L)*np.exp(-(R/L)*t) * H # Don't really need to multiply by H as w is zero for t < 0
# Response of resistor voltage
response = np.convolve(H, IR(t)*dt, 'full')
The extra code to make the plot is here:
# Define new, longer, time array for plotting response - must be same length as response, with step dt
tp = np.arange(len(response))* dt
plt.plot(0-t, IR(t), '-', label='Impulse response (flipped)')
for q in np.arange(0.01, 0.1, 0.01):
plt.plot(q-t, IR(t), 'o-', markersize=3, color=str(10*q))
t = np.arange(-1, 1, dt)
H = np.ones_like(t)
H[t<0] = 0.
plt.plot(t, H, 's', label='Unit step function')
plt.plot(tp, response, '-o', label='Response')
plt.tight_layout()
plt.grid()
plt.xlabel('Time (s)')
plt.ylabel('Voltage (V)')
plt.legend()
plt.show()
Finally, if you still have some confusion about convolution, I strongly recommend "Digital Signal Processing: A Practical Guide for Engineers and Scientists" by Steven W. Smith.
I am trying to find the power spectral density of a signal measured at uneven times. The data looks something like this:
0 1.55
755 1.58
2412256 2.42
2413137 0.32
2497761 1.19
...
where the first column is the time since the first measurement (in seconds) and the second column is the value of the measurement.
Currently, using the periodogram function in Matlab, I have been able to estimate the power spectral density by using:
nfft = length(data(:,2));
pxx = periodogram(data(:,2),[],nfft);
Now at the moment, to plot this I have been using
len = length(pxx);
num = 1:1:len;
plot(num,pxx)
Which clearly does not place the correct x-axis on the power spectral density (and yields something like the plot below), which needs to be in frequency space. I am confused about how to go about this given the uneven sampling of the data.
What is the correct way to convert to (and then plot in) frequency space when estimating the power spectral density for data that has been unevenly sampled? I am also interested in tackling this from a python/numpy/scipy perspective but have so far only looked at the Matlab function.
I am not aware of any functions that calculate a PSD from irregulary sampled data, so you need to convert the data to a uniform sample rate first. So the first step is to use interp1 to resample at regular time intervals.
avg_fs = 1/mean(diff(data(:, 1)));
min_time = min(data(:, 1));
max_time = max(data(:, 1));
num_pts = floor((max_time - min_time) * avg_fs);
new_time = (1:num_pts)' / avg_fs;
new_time = new_time - new_time(1) + min_time;
new_x = interp1(data(:, 1), data(:, 2), new_time);
I always use pwelch for calculating PSD's, here is how I would go about it
nfft = 512; % play with this to change your frequency resolution
noverlap = round(nfft * 0.75); % 75% overlap
window = hanning(nfft);
[Pxx,F] = pwelch(new_x, window, noverlap, nfft, avg_fs);
plot(F, Pxx)
xlabel('Frequency (Hz)')
grid on
You will definitely want to experiment with nfft, larger numbers will give you more frequency resolution (smaller spacing between frequencies), but the PSD will be noisier. One trick you can do to get fine resolution and low noise is to make the window smaller than nfft.