import numpy as np
import matplotlib.pyplot as plt
n = 500
T = 10
dw = 2 * np.pi / T
t = np.linspace(0, T, n)
x = 5 * np.sin(20 * t + np.pi) + 10 * np.sin( 40 * t + np.pi/2)
fftx = np.fft.rfft(x)
freq = np.fft.rfftfreq(n) * n * dw
amps = np.abs(fftx) * 2 / n
angs = np.angle(fftx)
_, ax = plt.subplots(3, 1)
ax[0].plot(t, x)
ax[1].plot(freq, amps)
ax[2].plot(freq, angs)
I get correct values for frequency and amplitude. But as seen from the plot the phase values are not correct. How to extract correct values for phase from fft? What exactly am I looking at in the phase plot?
I am expecting approx 3.14 and 3.14/2 for frequencies 20 and 40 respectively.
There are two issues with computing the phase:
Your input signal is not an integer number of periods. If you replicate the signal repeatedly, you'll see you actually have a different set of frequency components than you assume when you construct the signal (the DFT can the thought of as using an infinite repetition of your signal as input). This causes the peaks to have some width to them, it also causes the phase to shift a bit.
This issue you can fix by either windowing your signal, or creating it so it has an integer number of periods. The latter is:
T = 3 * np.pi
t = np.linspace(0, T, n, endpoint=False)
The frequencies where there is no signal (which after the fix above is all except for two frequencies), the phase will be given by noise. You can set the phase here to zero:
angs[amps < 1] = 0
Now your plot looks like this:
The phases are not as you expected, because the sine has a phase of -Ď€/2. Repeat the experiment with cos instead of sin and you get the phases you were expecting.
Related
I have a dataset which looks roughly as follows (and is sinusoidal in nature):
TW-240-run1.txt
Point Number Temperature
0 51.504781
1 51.487722
2 51.487722
3 51.828893
4 51.828893
5 51.436547
6 51.368312
7 51.726542
8 51.368312
9 51.317137
10 51.317137
11 51.283020
12 51.590073
.
.
.
9599 51.675366
I am tasked with finding the fundamental/first fourier coefficients, a_n and b_n for this dataset, by means of a numerical integration technique. In this case, I am simply using numpy.trapz from numpy, which aims to implement the trapezium rule. The fourier coefficients, a_n and b_n can be calculated with the following formulae:
where tau (𝛕) is the time period of the sine function. For my case, 𝛕 = 240 seconds (referring to the point number 240 on the data sheet), and thus the bounds of integration are 0 to 240. T(t) from the above formulae is the data set and n = 1.
My current code for trying to calculate the fourier coefficients is as follows:
# Packages
import numpy as np
import matplotlib.pyplot as plt
import scipy as sp
#input data from datasheet, the loadtxt below takes in the data from t = 0s to t = 240s
x1, y1 = np.loadtxt(r'C:\Users\Sidharth\Documents\y2python\y2python\thermal_4min_a.txt', unpack=True, skiprows=3)
tau_4min = 240.0
def cosine(period, t, n):
return np.cos((2*np.pi*n*t)/(period)) #defines the cos term for the a_n formula
def sine(period, t, n): #defines the sin term for the a_n formula
return np.sin((2*np.pi*n*t)/(period))
a_1_4min = (2/tau_4min)*np.trapz((y1*cos_term_4min), x1) #implement a_n formula (trapezium rule for T(t)*cos)
print('a_1 is', a_1_4min)
b_1_4min = (2/tau_4min)*np.trapz((y1*sin_term_4min), x1) #implement b_n formula (trapezium rule for T(t)*cos)
print('b_1 is', b_1_4min)
Essentially what this is doing is, it takes in the data, but only up to the row index 241 (point number 240), and then multiplies it by the sine/cosine term from each of the above formulae. However, I realise this isn't calculating the fourier coefficients properly.
My question(s) are as follows:
Will my code work if I can find a way to set limits of integration for np.trapz and then importing the entire data set, instead of only importing the data points from 0 to 240 and multiplying it by the cos or sine term, then using np trapz on that product, as I am currently doing (0 and 240 are supposed to be my limits of integration)
I have a target of target = 11.82 with two variables
x = 9
y = 15
How do I find the percentage weighting that would blend x & y to equal my target? i.e. 55% of x and 45% of y - what function is most efficient way to calc a weighting to obtain my target?
Looking at it again, what I think you want is really two equations:
9x + 15y = 11.82
x + y = 1
Solving that system of equations is pretty fast on pen and paper (just do linear combination). Or you could use sympy to solve the system of linear equations:
>>> from sympy import *
>>> from sympy.solvers.solveset import linsolve
>>> x, y = symbols('x, y')
>>> linsolve([x + y - 1, 9 * x + 15 * y - 11.82], (x, y)) # make 0 on right by subtraction
FiniteSet((0.53, 0.47))
We can confirm this by substitution:
>>> 9 * 0.53 + 15 * 0.47
11.82
I have been trying to work out how to calculate Poincaré sections for a system of non-linear ODEs, using a paper on the exact system as reference, and have been wrestling with numpy to try and make it run better. This is intended to run within a bounded domain.
Currently, I have the following code
import numpy as np
from scipy.integrate import odeint
X = 0
Y = 1
Z = 2
def generate_poincare_map(function, initial, plane, iterations, delta):
intersections = []
p_i = odeint(function, initial.flatten(), [0, delta])[-1]
for i in range(1, iterations):
p_f = odeint(function, p_i, [i * delta, (i+1) * delta])[-1]
if (p_f[Z] > plane) and (p_i[Z] < plane):
intersections.append(p_i[:2])
if (p_f[Z] > plane) and (p_i[Z] < plane):
intersections.append(p_i[:2])
p_i = p_f
return np.stack(intersections)
This is pretty wasteful due to the integration solely between successive time steps, and seems to produce incorrect results. The original reference includes sections along the lines of
whereas mine tend to result in something along the lines of
Do you have any advice on how to proceed to make this more correct, and perhaps a little faster?
To get a Pointcaré map of the ABC flow
def ABC_ode(u,t):
A, B, C = 0.75, 1, 1 # matlab parameters
x, y, z = u
return np.array([
A*np.sin(z)+C*np.cos(y),
B*np.sin(x)+A*np.cos(z),
C*np.sin(y)+B*np.cos(x)
])
def mysolver(u0, tspan): return odeint(ABC_ode, u0, tspan, atol=1e-10, rtol=1e-11)
you have first to understand that the dynamical system is really about the points (cos(x),sin(x)) etc. on the unit circle. So values different by multiples of 2*pi represent the same point. In the computation of the section one has to reflect this, either by computing it on the Cartesian product of the 3 circles. Let's stay with the second variant, and chose [-pi,pi] as the fundamental period to have the zero location well in the center. Keep in mind that jumps larger pi are from the angle reduction, not from a real crossing of that interval.
def find_crosssections(x0,y0):
u0 = [x0,y0,0]
px = []
py = []
u = mysolver(u0, np.arange(0, 4000, 0.5)); u0 = u[-1]
u = np.mod(u+pi,2*pi)-pi
x,y,z = u.T
for k in range(len(z)-1):
if z[k]<=0 and z[k+1]>=0 and z[k+1]-z[k]<pi:
# find a more exact intersection location by linear interpolation
s = -z[k]/(z[k+1]-z[k]) # 0 = z[k] + s*(z[k+1]-z[k])
rx, ry = (1-s)*x[k]+s*x[k+1], (1-s)*y[k]+s*y[k+1]
px.append(rx);
py.append(ry);
return px,py
To get a full picture of the Poincare cross-section and avoid duplicate work, use a grid of squares and mark if one of the intersections already fell in it. Only start new iterations from the centers of free squares.
N=20
grid = np.zeros([N,N], dtype=int)
for i in range(N):
for j in range(N):
if grid[i,j]>0: continue;
x0, y0 = (2*i+1)*pi/N-pi, (2*j+1)*pi/N-pi
px, py = find_crosssections(x0,y0)
for rx,ry in zip(px,py):
m, n = int((rx+pi)*N/(2*pi)), int((ry+pi)*N/(2*pi))
grid[m,n]=1
plt.plot(px, py, '.', ms=2)
You can now play with the density of the grid and the length of the integration interval to get the plot a little more filled out, but all characteristic features are already here. But I'd recommend re-programming this in a compiled language, as the computation will take some time.
I have to measure slew rates in signals like the one in the image below. I need the slew rate of the part marked by the grey arrow.
At the moment I smoothen the signal with a hann window to get rid of eventual noise and to flatten the peaks. Then I search (starting right) the 30% and 70% points and calculate the slew rate between this two points.
But my problem is, that the signal gets flattened after smoothing. Therefore the calculated slew rate is not as high as it should be. An if I reduce smoothing, then the peaks (you can see right side in the image) get higher and the 30% point is eventually found at the wrong position.
Is there a better/safer way to find the required slew rate?
If you know between what values your signal is transitioning, and your noise is not too large, you can simply compute the time differences between all crossings of 30% and all crossings of 70% and keep the smallest one:
import numpy as np
import matplotlib.pyplot as plt
s100, s0 = 5, 0
signal = np.concatenate((np.ones((25,)) * s100,
s100 + (np.random.rand(25) - 0.5) * (s100-s0),
np.linspace(s100, s0, 25),
s0 + (np.random.rand(25) - 0.5) * (s100-s0),
np.ones((25,)) * s0))
# Interpolate to find crossings with 30% and 70% of signal
# The general linear interpolation formula between (x0, y0) and (x1, y1) is:
# y = y0 + (x-x0) * (y1-y0) / (x1-x0)
# to find the x at which the crossing with y happens:
# x = x0 + (y-y0) * (x1-x0) / (y1-y0)
# Because we are using indices as time, x1-x0 == 1, and if the crossing
# happens within the interval, then 0 <= x <= 1.
# The following code is just a vectorized version of the above
delta_s = np.diff(signal)
t30 = (s0 + (s100-s0)*.3 - signal[:-1]) / delta_s
idx30 = np.where((t30 > 0) & (t30 < 1))[0]
t30 = idx30 + t30[idx30]
t70 = (s0 + (s100-s0)*.7 - signal[:-1]) / delta_s
idx70 = np.where((t70 > 0) & (t70 < 1))[0]
t70 = idx70 + t70[idx70]
# compute all possible transition times, keep the smallest
idx = np.unravel_index(np.argmin(t30[:, None] - t70),
(len(t30), len(t70),))
print t30[idx[0]] - t70[idx[1]]
# 9.6
plt. plot(signal)
plt.plot(t30, [s0 + (s100-s0)*.3]*len(t30), 'go')
plt.plot(t30[idx[0]], [s0 + (s100-s0)*.3], 'o', mec='g', mfc='None', ms=10)
plt.plot(t70, [s0 + (s100-s0)*.7]*len(t70), 'ro')
plt.plot(t70[idx[1]], [s0 + (s100-s0)*.7], 'o', mec='r', mfc='None', ms=10 )
plt.show()
I want to filter the accelerometer values using a moving average, how is this done?
Thanks
A simple, single pole, low pass, recursive IIR filter is quick and easy to implement, e.g.
xf = k * xf + (1.0 - k) * x;
yf = k * yf + (1.0 - k) * y;
where x, y are the raw (unfiltered) X/Y accelerometer signals, xf, yf are the filtered output signals, and k determines the time constant of the filters (typically a value between 0.9 and 0.9999..., where a bigger k means a longer time constant).
You can determine k empirically, or if you know your required cut-off frequency, Fc, then you can use the formula:
k = 1 - exp(-2.0 * PI * Fc / Fs)
where Fs is the sample rate.
Note that xf, yf are the previous values of the output signal on the RHS, and the new output values on the LHS of the expression above.
Note also that we are assuming here that you will be sampling the accelerometer signals at regular time intervals, e.g. every 10 ms. The time constant will be a function both of k and of this sampling interval.