getting output ifft at a different resolution - python

I'm trying to smooth and interpolate some periodic data in python using scipy.fftp. I have managed to take the fft of the data, remove the higher order frequencies above wn (by doing myfft[wn:-wn] = 0) and then reconstruct a "smoothed" version of the data with ifft(myfft). The array created by the ifft has the same number of points as the original data. How can I use that fft to create an array with more points.
x = [i*2*np.pi/360 for i in range(0,360,30)]
data = np.sin(x)
#get fft
myfft = fftp.fft(data)
#kill feqs above wn
myfft[wn:-wn] = 0
#make new series
newdata = fftp.ifft(myfft)
I've also been able to manually recreate the series at the same resolution as demonstrated here
Recreating time series data using FFT results without using ifft
but when I tried upping the resolution of the x-values array it didn't give me the right answer either.
Thanks in advance
Niall

What np.fft.fft returns has the DC component at position 0, followed by all positive frequencies, then the Nyquist frequency (only if the number of elements is even), then the negative frequencies in reverse order. So to add more resolution you could add zeros at both sides of the Nyquist frequency:
import numpy as np
import matplotlib.pyplot as plt
y = np.sin(np.linspace(0, 2*np.pi, 32, endpoint=False))
f = np.fft.fft(y)
n = len(f)
f_ = np.concatenate((f[0:(n+1)//2],
np.zeros(n//2),
[] if n%2 != 0 else f[(n+1)//2:(n+3)//2],
np.zeros(n//2),
f[(n+3)//2:]))
y_ = np.fft.ifft(f_)
plt.plot(y, 'ro')
plt.plot(y_, 'bo')
plt.show()

Related

Python Audio fftpack: Filter noise signal and graph it?

So I'm follow a tutorial where we create a signal and filter the noise using fftpack.
Problem 1: I'm trying to plot the filtered and unfiltered signal noise on a graph so that I can see them side by side.
I'm getting this error:
Warning (from warnings module): File
"C:\Python39\lib\site-packages\numpy\core_asarray.py", line 83
return array(a, dtype, copy=False, order=order) ComplexWarning: Casting complex values to real discards the imaginary part
I think this is causing the error:
y = sig
x = time_vec
Problem 2: I'm not sure how to plot the two graphs in the same window?
import numpy as np
from scipy import fftpack
time_step = 0.05
# Return evenly spaced time vector (0.5) between [0, 10]
time_vec = np.arange(0, 10, time_step)
print(time_vec)
period = 5
# create a signal and add some noise:
# input angle 2pi * time vector) in radians and return value ranging from -1 to +1 -- essentially mimicking a sigla wave that is goes in cycles + adding some noise to this bitch
# numpy.random.randn() - return samples from the standard normal distribution of mean 0 and variance 1
sig = (np.sin(2*np.pi*time_vec)/period) + 0.25 * np.random.randn(time_vec.size)
# Return discrete Fourier transform of real or complex sequence
sig_fft = fftpack.fft(sig) # tranform the sin function
# Get Amplitude ?
Amplitude = np.abs(sig_fft) # np.abs() - calculate absolute value from a complex number a + ib
Power = Amplitude**2 # create a power spectrum by power of 2 of amplitude
# Get the (angle) base spectrrum of these transform values i.e. sig_fft
Angle = np.angle(sig_fft) # Return the angle of the complex argument
# For each Amplitude and Power (of each element in the array?) - there is will be a corresponding difference in xxx
# This is will return the sampling frequecy or corresponding frequency of each of the (magnitude) i.e. Power
sample_freq = fftpack.fftfreq(sig.size, d=time_step)
print(Amplitude)
print(sample_freq)
# Because we would like to remove the noise we are concerned with peak freqence that contains the peak amplitude
Amp_Freq = np.array([Amplitude, sample_freq])
# Now we try to find the peak amplitude - so we try to extract
Amp_position = Amp_Freq[0,:].argmax()
peak_freq = Amp_Freq[1, Amp_position] # find the positions of max value position (Amplitude)
# print the position of max Amplitude
print("--", Amp_position)
# print the frequecies of those max amplitude
print(peak_freq)
high_freq_fft = sig_fft.copy()
# assign all the value the corresponding frequecies larger than the peak frequence - assign em 0 - cancel!! in the array (elements) (?)
high_freq_fft[np.abs(sample_freq) > peak_freq] = 0
print("yes:", high_freq_fft)
# Return discrete inverse Fourier transform of real or complex sequence
filtered_sig = fftpack.ifft(high_freq_fft)
print("filtered noise: ", filtered_sig)
# Using Fast Fourier Transform and inverse Fast Fourier Transform we can remove the noise from the frequency domain (that would be otherwise impossible to do in Time Domain) - done.
# Plotting the signal with noise (?) and filtered
import matplotlib.pyplot as plt
y = filtered_sig
x = time_vec
plt.plot(x, y)
plt.xlabel('Time')
plt.ylabel('Filtered Amplitude')
plt.show()
y = sig
x = time_vec
plt.plot(x, y)
plt.xlabel('Time')
plt.ylabel('Unfiltered Amplitude')
plt.show()
Problem 1: arises within matplotlib when you plot filtered_sig as it includes small imaginary parts. You can chop them off by real_if_close.
Problem 2: just don't use show between the first and the second plot
Here is the complete working plotting part in one chart with a legend:
import matplotlib.pyplot as plt
x = time_vec
y = np.real_if_close(filtered_sig)
plt.plot(x, y, label='Filtered')
plt.xlabel('Time')
plt.ylabel('Amplitude')
y = sig
plt.plot(x, y, label='Unfiltered')
plt.legend()
plt.show()

function to determine the frequency of a sinusoid

I am having trouble with my Digital Signal Processing homework. Using Python, I need to create a function that is able to determine the frequency of a sinusoid. I am given random frequencies form 0-4000 Hz with an Fs=8000. Can someone please help?
import numpy as np
def freqfinder(signal):
"""REPLACE"""
x=np.fft.fft(signal)
x=np.abs(x)
x=np.max(x)
return x
t=np.linspace(0,2*np.pi,8*8000)
y=np.sin(2*t)
print(freqfinder(y))
z = np.fft.fft(y)
zz = np.abs(z)
plt.plot(zz)
I tried this as a test for the fft.
Your code is off to a good start. A few things to note:
You should only look at the first half of your FFT -- For a REAL input, the output is symmetric around 0 and you only care about the frequencies greater than 0 (the first half of the fft output).
You want the magnitude of each frequency - so you should then take the absolute value of the resulting fft.
The max you are locating is NOT the frequency, but is related to the index of the frequency. It is the strength of the strongest frequency.
Here is a little script demonstrating these ideas:
import numpy as np
import matplotlib.pyplot as plt
fs = 8000
t = np.linspace(0, 2*np.pi, fs)
freqs = [ 2, 152, 423, 2423, 3541] # Frequencies to test
amps = [0.5, 0.5, 1.0, 0.8, 0.3] # Amplitude for each freq
y = np.zeros(len(t))
for freq, amp in zip(freqs, amps):
y += amp*np.sin(freq*t)
fig, ax = plt.subplots(1, 2)
ax = ax.flatten()
ax[0].plot(t, y)
ax[0].set_title("Original signal")
y_fft = np.fft.fft(y) # Original FFT
y_fft = y_fft[:round(len(t)/2)] # First half ( pos freqs )
y_fft = np.abs(y_fft) # Absolute value of magnitudes
y_fft = y_fft/max(y_fft) # Normalized so max = 1
freq_x_axis = np.linspace(0, fs/2, len(y_fft))
ax[1].plot(freq_x_axis, y_fft, "o-")
ax[1].set_title("Frequency magnitudes")
ax[1].set_xlabel("Frequency")
ax[1].set_ylabel("Magnitude")
plt.grid()
plt.tight_layout()
plt.show()
f_loc = np.argmax(y_fft) # Finds the index of the max
f_val = freq_x_axis[f_loc] # The strongest frequency value
print(f"The strongest frequency is f = {f_val}")
The output:
The strongest frequency is f = 423.1057764441111
You can see on the right graph that there is a peak at each of the frequencies we specified in freqs, which is what is expected.
This kind of setup is fine if you only have one frequency you're looking for, but otherwise you may need to find and implement some peak finding algorithms to find all the indices of all the frequency peaks of y_fft and then correlate that with the frequencies in freq_x_axis

How to randomly generate continuous functions

My objective is to randomly generate good looking continuous functions, good looking meaning that functions which can be recovered from their plots.
Essentially I want to generate a random time series data for 1 second with 1024 samples per second. If I randomly choose 1024 values, then the plot looks very noisy and nothing meaningful can be extracted out of it. In the end I have attached plots of two sinusoids, one with a frequency of 3Hz and another with a frequency of 100Hz. I consider 3Hz cosine as a good function because I can extract back the timeseries by looking at the plot. But the 100 Hz sinusoid is bad for me as I cant recover the timeseries from the plot. So in the above mentioned meaning of goodness of a timeseries, I want to randomly generate good looking continuos functions/timeseries.
The method I am thinking of using is as follows (python language):
(1) Choose 32 points in x-axis between 0 to 1 using x=linspace(0,1,32).
(2) For each of these 32 points choose a random value using y=np.random.rand(32).
(3) Then I need an interpolation or curve fitting method which takes as input (x,y) and outputs a continuos function which would look something like func=curve_fit(x,y)
(4) I can obtain the time seires by sampling from the func function
Following are the questions that I have:
1) What is the best curve-fitting or interpolation method that I can
use. They should also be available in python.
2) Is there a better method to generate good looking functions,
without using curve fitting or interpolation.
Edit
Here is the code I am using currently for generating random time-series of length 1024. In my case I need to scale the function between 0 and 1 in the y-axis. Hence for me l=0 and h=0. If that scaling is not needed you just need to uncomment a line in each function to randomize the scaling.
import numpy as np
from scipy import interpolate
from sklearn.preprocessing import MinMaxScaler
import matplotlib.pyplot as plt
## Curve fitting technique
def random_poly_fit():
l=0
h=1
degree = np.random.randint(2,11)
c_points = np.random.randint(2,32)
cx = np.linspace(0,1,c_points)
cy = np.random.rand(c_points)
z = np.polyfit(cx, cy, degree)
f = np.poly1d(z)
y = f(x)
# l,h=np.sort(np.random.rand(2))
y = MinMaxScaler(feature_range=(l,h)).fit_transform(y.reshape(-1, 1)).reshape(-1)
return y
## Cubic Spline Interpolation technique
def random_cubic_spline():
l=0
h=1
c_points = np.random.randint(4,32)
cx = np.linspace(0,1,c_points)
cy = np.random.rand(c_points)
z = interpolate.CubicSpline(cx, cy)
y = z(x)
# l,h=np.sort(np.random.rand(2))
y = MinMaxScaler(feature_range=(l,h)).fit_transform(y.reshape(-1, 1)).reshape(-1)
return y
func_families = [random_poly_fit, random_cubic_spline]
func = np.random.choice(func_families)
x = np.linspace(0,1,1024)
y = func()
plt.plot(x,y)
plt.show()
Add sin and cosine signals
from numpy.random import randint
x= np.linspace(0,1,1000)
for i in range(10):
y = randint(0,100)*np.sin(randint(0,100)*x)+randint(0,100)*np.cos(randint(0,100)*x)
y = MinMaxScaler(feature_range=(-1,1)).fit_transform(y.reshape(-1, 1)).reshape(-1)
plt.plot(x,y)
plt.show()
Output:
convolve sin and cosine signals
for i in range(10):
y = np.convolve(randint(0,100)*np.sin(randint(0,100)*x), randint(0,100)*np.cos(randint(0,100)*x), 'same')
y = MinMaxScaler(feature_range=(-1,1)).fit_transform(y.reshape(-1, 1)).reshape(-1)
plt.plot(x,y)
plt.show()
Output:

Python - Inverse Fourier Transform

Problem
I am trying to remove a frequency from a set of data obtained from an audio file.
To simplify down my problem, I have created the code below which creates a set of waves and merges them into a complex wave. Then it finds the fourier transform of this complex wave and inverses it.
I am expecting to see the original wave as a result since there should be no data loss, however I receive a very different wave instead.
Code:
import numpy as np
import matplotlib.pyplot as plt
import random
#Get plots
fig, c1 = plt.subplots()
c2 = c1.twinx()
fs = 100 # sample rate
f_list = [5,10,15,20,100] # the frequency of the signal
x = np.arange(fs) # the points on the x axis for plotting
# compute the value (amplitude) of the sin wave for each sample
wave = []
for f in f_list:
wave.append(list(np.sin(2*np.pi*f * (x/fs))))
#Adds the sine waves together into a single complex wave
wave4 = []
for i in range(len(wave[0])):
data = 0
for ii in range(len(wave)):
data += wave[ii][i]
wave4.append(data)
#Get frequencies from complex wave
fft = np.fft.rfft(wave4)
fft = np.abs(fft)
#Note: Here I will add some code to remove specific frequencies
#Get complex wave from frequencies
waveV2 = np.fft.irfft(fft)
#Plot the complex waves, should be the same
c1.plot(wave4, color="orange")
c1.plot(waveV2)
plt.show()
Results: (Orange is created wave, blue is original wave)
Expected results:
The blue and orange lines (the original and new wave created) should have the exact same values
You took the absolute value of the FFT before you do the inverse FFT. That changes things, and is probably the cause of your problem.

Not getting expected results with Numpy FFT

So I have this car that moves at a velocity that is the sum of three different sine waves (whose individual frequencies I know). I used the following to construct this
velocity time graph
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.read_csv('drivingdata.csv') # velocity values
s = df['leadspeed'].values # transform csv col into array
t = np.linspace(0, 1, 5067)
plt.ylabel("Amplitude")
plt.xlabel("Time[s]")
plt.plot(t, s)
plt.show()
This is fine, and then I perform a FFT on this data with the following numpy function:
T = t[1]-t[0] # sample rate
N = s.size
fft = np.fft.fft(s)
f = np.linspace(0, 1//T, N) # 1/T is the frequency
plt.ylabel("Amplitude")
plt.xlabel("Frequency [Hz]")
plt.bar(f[:N // 2], np.abs(fft)[:N // 2] * 1 // N) # 1/N is a normalization factor
plt.show()
Then I get this amplitude vs frequency graph. How do I "zoom-in" so that I can confirm my initial frequencies (all under 0.2) ?
I'm completely new to fft, so criticism/help would be appreciated.
EDIT:
I followed your helpful advice, Cris Luengo, and this is my new graph. The frequencies I input into my waves were 0.033, 0.083, and 0.117, so I'm still left seeking answers.
EDIT 2:
My apologies, Cris. Here you go. Are the frequencies I'm looking for just right past the 0 there? Is there a way to "zoom in" ? New graph

Categories

Resources