Context :
I am discovering the vast field of DSP. Yes I'm a beginner.
My goal :
Apply fft on an audio array given by audiolab to get the different frequencies of the signal.
Question :
One question : I just cannot get what to do with a numpy array which contains audio datas, thanks to audiolab. :
import numpy as np
from scikits.audiolab import Sndfile
f = Sndfile('first.ogg', 'r')
# Sndfile instances can be queried for the audio file meta-data
fs = f.samplerate
nc = f.channels
enc = f.encoding
print(fs,nc,enc)
# Reading is straightfoward
data = f.read_frames(10)
print(data)
print(np.fft.fft(data))
Now I have got my datas.
Readings
I read those two nice articles here :
Analyze audio using Fast Fourier Transform (the accepted answser is wonderful)
and
http://www.onlamp.com/pub/a/python/2001/01/31/numerically.html?page=2
Now there are two technics : apparently one suggests square (first link) whereas the other a log, especially : 10ln10(abs(1.10**-20 + value))
Which one is the best ?
SUM UP :
I would like to get the fourier analysis of my array but any of those two answers seems to only emphasis the signal and not isolating the components.
I may be wrong, I am a still a noob.
What should I really do then ?
Thanks,
UPDATE:
I ask this question :
DSP - get the amplitude of all the frequencies which is related to this one.
Your question seems pretty confused, but you've obviously tried something, which is great. Let me take a step back and suggest an overall route for you:
Start by breaking your audio into chunks of some size, say N.
Perform the FFT on each chunk of N samples.
THEN worry about displaying the data as RMS (the square approach) or dB (the ln-based approach).
Really, you can think of those values as scaling factors for display.
If you need help with the FFT itself, my blog post on pitch detection with the FFT may help: http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html
Adding to the answer given by #Bjorn Roche.
Here is a simple code for plotting frequency spectrum, using dB scale.
It uses matplotlib for plotting.
import numpy as np
import pylab
# for a real signal
def plotfftspectrum(signal, dt): # where dt is the sample rate
n = signal.size
spectrum = np.abs(np.fft.fft(signal))
spectrum = 20*np.log(spectrum/spectrum.max()) # dB scale
frequencies = np.fft.fftfreq(n, dt)
pylab.plot(frequencies[:n//2], spectrum[:n//2])
# plot n//2 due real function symmetry
pylab.show()
You can use it, after reading at least some samples of your data, e.g like 1024.
data = f.read_frames(1024)
plotfftspectrum(data, 1./f.samplerate)
Where I believe your sample rate is in frequency.
Related
I am using the RPLidar A1: https://www.adafruit.com/product/4010
My goal is to collect data sets and plot them in order to get a live visual representation of the data.
My current code is:
import numpy as np
import matplotlib.pyplot as plt
from rplidar import RPLidar
def get_data():
lidar = RPLidar('COM6', baudrate=115200)
for scan in lidar.iter_scans(max_buf_meas=500):
break
lidar.stop()
return scan
for i in range(1000000):
if(i%7==0):
x = []
y = []
print(i)
current_data=get_data()
for point in current_data:
if point[0]==15:
x.append(point[2]*np.sin(point[1]))
y.append(point[2]*np.cos(point[1]))
plt.clf()
plt.scatter(x, y)
plt.pause(.1)
plt.show()
The above code produces a refreshing graph with changing data as shown below:
The issue is that this is not an accurate representation. There is a native application by SLAMTEC called frame_grabber which clearly shows this device giving accurate rectangular shaped representation of my room. Instead I keep getting a circular shape ranging from small to large.
The raw data from the sensor comes in the form of an array containing roughly 100 sets of the following data: (quality, theta, r). My code checks if the quality is good (15 is maximum), and then goes on to plot data sets and clears the data array every seven instances in order to get rid of old data.
I checked the raw polar data in excel and the data also appears to create a circularish shape.
After a few days of trying out various libraries for plotting and trying a few other things I've noticed the mistake.
Hopefully this can prevent someone from making the same mistake in the future.
The lidars typically give data in terms of "theta" and "r". On the other hand the numpy as well as the built in math library in Python accept arguments in radians for performing cos and sin operations.
I've converted the units from degrees to radians and now everything works perfectly.
I am trying to use simple FFT to make Fourier transform of some function, but apparently the numpy and scipy FFT doesn't work so well even for 1024 points.
For example, suppose I want to make FFT of sin(50x)+cos(80x). Then, at k=50 point should be purely imaginary and k=80 should be purely real. Generally there is some error, but working with a number of points as large as 1024 generally gives quite satisfactory output. But here there is quite a bit of error in the output. The result doesn't improve much with increasing number of points.
Can someone explain the reason of this?
I have tried the following code in Python:
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack
pi = np.pi
#no. of points
N = 1024
#real axis
x = np.linspace(0,2*pi,N)
#real fn
f_x = np.sin(50*x)+np.cos(80*x)
f_k = (2/N)*scipy.fftpack.fft(f_x)
print f_k[50]
print f_k[80]
and it gives the following output:
(0.155273271152-0.983403030451j)
(0.960302223459+0.242617561413j)
which should have been, 0-1j and 1+0j. With 1024 points I was expecting a more accurate result.
I have also tried transforming by using explicit FT formula, and numpy instead of scipy. Both give the same accuracy.
For ideal, infinite-length signals it would be 0-1j and 1+0j. However, this is a finite-length, digital signal. Due to windowing and the limitations in representing floating-point numbers on a computer, it is never going to perfectly match the ideal case.
Your input length looks like it is off by one.
An FFT only give exact results for sinusoids that exactly integer periodic in the FFT length. For the infinitely many other frequencies, you can improve the results by parabolic or Sinc interpolation between FFT result bins.
this question is related to :
DSP : audio processing : squart or log to leverage fft?
in which I was lost about the right algorithm to choose.
Now,
Goal :
I want to get all the frequencies of my signal, that I get from an audio file.
Context:
I use numpy, and scikits.audiolab. I made a lot of reading on the dsp subject, went to dspguru.com as well, read papers and nice blogs over the net.
The code I use is this one :
import numpy as np
from scikits.audiolab import Sndfile
f = Sndfile('first.ogg', 'r')
# Sndfile instances can be queried for the audio file meta-data
fs = f.samplerate
nc = f.channels
enc = f.encoding
print(fs,nc,enc)
# Reading is straightfoward
data = f.read_frames(10)
print(data)
print(np.fft.rfft(data))
I am new to DSP.
My question
I would like to be able to separate all the frequencies of a signal to compare different signals.
I use numpy.fft.rfft on the array of sound; But now, this operation alone is not enough. So, what is the best solution to get all the magnitudes of frequencies correctly ?
I saw that multiplying the resulting values get the complex numbers off and transform the whole as a real number.
Now what please ? Is that it ?
if you need me to clarify anything, just ask.
Thanks a lot !
You say "I want to get all the frequencies of my signal, that I get from an audio file." but what you really want is the magnitude of the frequencies.
In your code, it looks like (I don't know python) you only read the first 10 samples. Assuming your file is mono, that's fine, but you probably want to look at a larger set of samples, say 1024 samples. Once you do that, of course, you'll want to repeat on the next set of N samples. You may or may not want to overlap the sets of samples, and you may want to apply a window function, but what you've done here is a good start.
What sleepyhead says is true. The output of the fft is complex. To find the magnitude of a given frequency, you need to find the length or absolute value of the complex number, which is simply sqrt( r^2 + i^2 ).
Mathematically Fourier Transform returns complex values as it is transform with the function *exp(-i*omega*t). So the PC gives you spectrum as a complex number corresponding to the cosine and sine transforms. In order to get the amplitude you just need to take the absolute value: np.abs(spectrum). In order to get the power spectrum square the absolute value. Complex representation is valuable as you can get not only amplitude, but also phase of the frequencies - that may be useful in DSP as well.
If I got it right, you want walk over all data(sound) and capture amplitude, for this make a "while" over the data capturing at each time 1024 samples
data = f.read_frames(1024)
while data != '':
print(data)
print(np.fft.rfft(data))
data = f.read_frames(1024)
I have a miniseed file with a singlechannel trace and I assume the data is in counts (how can i check the units of the trace?). I need to transform this in to m/s.
I already checked the obspy tutorial and my main problem is that i dont know how to access the poles and zeros and amplification factor from the miniseed file.
Also, do I need the calibration file for this?
Here is my code:
from obspy.core import *
st=read('/Users/guilhermew/Documents/Projecto/Dados sismicos 1 dia/2012_130_DOC01.mseed')
st.plot()
Thanks in advance,
Guilherme
EDIT:
I finally understood how to convert the data. Obspy has different ways to achieve this, but it all comes down to removing the instrument response from the waveform data.
Like #Robert Barsch said, I needed another file to get the instrument response metadata.
So I came up with the following code:
parser=Parser("dir/parser/file")
for tr in stream_aux:
stream_id=tr.stats.network+'.'+tr.stats.station+ '..' + tr.stats.channel
paz=parser.getPAZ(stream_id, tr.stats.starttime)
df = tr.stats.sampling_rate
tr.data = seisSim(tr.data, df, paz_remove=paz)
Im using the seisSim function to convert the data.
My problem now is that the output dosen't look right (but i cant seem to post the image)
This is clearly a question which should be asked to the seismology community and not at StackOverflow! How about you write to the ObsPy user mailinglist?
Update: I still feel the answer is that he/she should ask directly at the ObsPy mailing list. However, in order to give a proper answer for the actual question: MiniSEED is a data only format which does not contain any meta information such as poles and zeros or the used unit. So yes you will need another file such as RESP, SAC PAZ, Dataless SEED, Full SEED etc in order to get the station specific meta data. To apply your seismometer correction read http://docs.obspy.org/tutorial/code_snippets/seismometer_correction_simulation.html
To get it in real-life units and not counts, you need to remove the instrument response. I remove instrument response using this code:
# Define math defaults
from __future__ import division #allows real devision without rounding
# Retrieve modules needed
from obspy.core import read
import numpy as np
import matplotlib.pyplot as plt
#%% Choose and import data
str1 = read(fileloc)
print(str1) #show imported data
print(str1[0].stats) #show stats for trace
#%% Remove instrument response
# create dictionary of poles and zeros
TrillC = {'gain': 800.0,
'poles': [complex(-3.691000e-02,3.712000e-02),
complex(-3.691000e-02,-3.712000e-02),
complex(-3.739000e+02,4.755000e+02),
complex(-3.739000e+02,-4.755000e+02),
complex(-5.884000e+02,1.508000e+03),
complex(-5.884000e+02,-1.508000e+03)],
'sensitivity': 8.184000E+11,
'zeros': [0 -4.341E+02]}
str1_remres = str1.copy() #make a copy of data, so original isn't changed
str1_remres.simulate(paz_remove=TrillC, paz_simulate=None, water_level=60.0)
print("Instrument Response Removed")
plt.figure()
str1_remres_m = str1_remres.merge()
plt.plot(str1_remres_m[0].data) #only will plot first trace of the stream
As you can see I have manually defined the poles and zeros. There is probably a way to automatically input it but this was the way that I found that worked.
Remember each instrument has different poles and zeros.
The number of zeros you use depends on what you want your output to be. Seismometers are normally velocity (2 zeros)
3 zeros = displacement
2 zeros = velocity
1 zero = acceleration
I am a bit of a novice with programming as we are being made to do it in our physics degree. I am using Python 2.
I've been given a txt file with two columns of data, the first few lines look like this:
0.000000000000000000e+00 7.335686114232199684e-02
1.999999999999999909e-07 7.571960558042964973e-01
3.999999999999999819e-07 9.909475704320810374e-01
5.999999999999999728e-07 3.412754086075696081e-01
7.999999999999999638e-07 -5.558766000866324219e-01
9.999999999999999547e-07 -9.810046985453722002e-01
1.199999999999999946e-06 -5.436864816312496629e-01
1.399999999999999937e-06 2.645021165628647641e-01
1.599999999999999928e-06 9.667259209284312371e-01
1.799999999999999919e-06 7.395753817164774091e-01
1.999999999999999909e-06 7.289488801158025555e-02
2.200000000000000112e-06 -7.925906572709742193e-01
2.399999999999999891e-06 -9.727702002847055107e-01
2.599999999999999671e-06 -1.772398644968510018e-01
2.799999999999999873e-06 6.627909312992285029e-01
3.000000000000000076e-06 1.022032186188189362e+00
3.199999999999999855e-06 5.531242183135693935e-01
and on it goes for many hundreds of lines.
The question asks:
This week you have been provided with a file which consists of a simulated NMR time domain response following an external impulse. This free induction decay (FID) is characterized by a frequency, an initial amplitude and a decay constant. The data has a single
oscillation frequency and the second contains a mixture of two frequencies.
Write a program to evaluate the Fast Fourier transform of both signals and plot them
in the frequency domain.
Could someone give me an example of how I might go about doing this? Unfortunately we are not given much guidance in the lab, just some online tutorials and otherwise told to google stuff.
I'll turn my comment into an answer:
It is actually very easy. Load your data using numpy.genfromtxt() into a numpy array, and then you can choose some form of FFT from numpy.fft.
As this is your exercise I won't write down exact code but that basically sums it.
for reading the .txt file, you'll want to do something like this (not the fastest but most clear):
column1 = []
column2 = []
infile = open("MyFile.txt", "r")
for l in infile.readlines():
if l.strip():
v1 = float(l.split()[0])
v2 = float(l.split()[1])
column1.append(v1)
column2.append(v2)
For the fft, look into numpy