I have captured RF data and am using dsp.SpectrumAnalyzer to find the frequency peaks of the signal. But the peaks are not reproducible. They are changed whenever I run the code again. My data is the same, so I should get the same results each time.
Someone suggested me to look at the random number that is used in MATLAB internal clock. But I have not found anything related yet.
Here is my code. I want stable and reproducible peak values.
Edit: here is the data file link.
https://uofnelincoln-my.sharepoint.com/:u:/g/personal/mbaig4_unl_edu/EbQl4lG1PcpMr4bt8-Xqwf4BTMjKyMoC-PXKeMK1VwqCKg?e=yS9LZf
The Figure shows the spectrum of one of the frames. It get the spectrum but the peak values are changed a bit every time i run the code. I'm expecting to get the same peaks because data is the same.
r_data=load("LTE5M_1s_t0.mat");
data=r_data.ans.Data;
measurements=[]; %scope measurements
peakValues=[]; %peak frequency and their RSSI
nframes=size(data,3); %extract number of frames from the data
%% Initialize Spectrum analyzer with defined parameters
specScope = dsp.SpectrumAnalyzer('SpectrumType','Power density', ...
'SampleRate',10000000, ...
'FrequencySpan','Span and center frequency', ...
'Span',10000000, ...
'FrequencyResolutionMethod','WindowLength', ...
'WindowLength',65536, ...
'FFTLengthSource','Property', ...
'FFTLength',65536, ...
'Window','Hamming');
%% Peak Finder
specScope.PeakFinder.NumPeaks = 10;
specScope.PeakFinder.Enable = true;
mdata=getMeasurementsData(specScope);
for counter = 1:nframes
%Launch the spectrum analyzer
specScope(data(:,:,counter));
if specScope.isNewDataReady
measurements = [measurements;getMeasurementsData(specScope)];
end
end
peakValues=measurements.PeakFinder;
save("peaks.mat","peakValues",'-mat')
Related
Hello wise people of the internet
My final goal is to create a code that shows a real-time spectrogram of 4-channel audio input device.
I have this code that can show spectrogram, but only after recording is complete:
clc; clear;
%~~~~~~~~~~~~~~~~~~~~~~~~
% Parameters
%~~~~~~~~~~~~~~~~~~~~~~~~
FileName2Save='mySpeech.wav';
NumChannels=2; % Number of recording channels
fs=44100; % Sampling Rate
Tw=50; Ts=10; % Parameters of Spectogram Framing: Tw = window Size [msec] | Ts = Windows steps [msec]
SessionTime=10; % Session Recording Time [Seconds] - After SessionTime The code stops sampling and plots spectogram
%~~~~~~~~~~~~~~~~~~~~~~~~
deviceReader=audioDeviceReader(fs, Tw*0.001*fs, "Driver","ASIO", "NumChannels", NumChannels);
setup(deviceReader)
disp('Recording Start')
All=[];
tic
% this bit is from the official documentation, showing how to get audio from 'audioDeviceReader'.
% In my opinion this is a really bad API design for Matlab.
while toc < SessionTime
acquiredAudio = deviceReader();
All=[All; acquiredAudio];
end
disp('Session Complete - Creating Spectogram')
% this bit is showing the spectrogram of the channels in a figure.
for iC=1:NumChannels
subplot(NumChannels,1,iC) % Change the image order as you like
spectrogram(All(:,iC),hann(round(Tw*0.001*fs)),round((Tw-Ts)*0.001*fs),20000, fs, 'yaxis', 'power');
drawnow
end
release(deviceReader)
disp('Recording complete')
I would like to show the spectrogram at real time while recording audio.
I tried couple of approaches:
1. To put the spectrogram command inside of the while.
This did not work because spectrogram command can take time, that make the while to not catch all of the samples.
2. Use parfeval command to run spectrogram in the background.
This did not work because the background thread can not do spectrogram. It also can't do simpler commands like disp.
I also tried afterEach with the parfeval but it makes the code that recieves the audio sample get stuck, and that's the same problem I mentioned earlier: If the code inside the while that receives samples gets stuck even a bit, we're losing samples here.
3. Use parfeval command to get the samples of the audio in the background.
I tried running the code that gets the audio in the background, and then use send command to get the new data and do spectrogram every time.
This did not work (see comment in code):
function GetSamples(q)
All = [];
tic;
while toc < 0.5
newValues = ...;
All = [All, newValues];
send(q, arr); % Goes to next only when spectrogram is complete drawing
end
end
The 'send' command calls the code that prints the spectrogram, and apparently only continues to the next in the while when the spectrogram drawing is complete, which can take a few seconds.
When the next values are queried, were losing a couple of samples here.
4. Use audiorecorder, which is asynchronous by default
Can't, because the requirement here is recording 4 channels at the same time.
If audiorecorder was able to record 4 channels, the problem will get solved because I could do something like:
record(recorder);
tic;
while toc < 5
y = getaudiodata(recorder);
spectogram(...);
drawnow;
end
I have some EEG data that I would like to break down into 30-second windows and run a fast Fourier transform on each window of data. I've tried to implement a for-loop and increment the index value by the number of samples in the time window. When I run this, I can see that (1) this works for the first window of data, but not the rest of them because (I think) the "number of samples minus one" leads to fewer elements than necessary for data_fft and thus doesn't have the same dimensions as f, which are both being plotted in a figure. (2) I tried to update the index value by adding the number of samples in a window, but after i = 1, it goes to i = 2 in my workspace and not to i = 7681 as I'd hoped. I've spent an embarrassingly long time on trying to figure out how to change this so it works correctly, so any advice is appreciated! Code is below. Let me know if I can clarify anything.
data_ch6 = data(:,6); % looking at just 1 electrode
tmax = 2*60; % total time in sec I want to analyze; just keeping it to 2 minutes for this exercise
tmax_window = 30; %30 sec window
times = tmax/tmax_window; % number of times fft should be run
Nsamps = tmax*hdr.SPR; % total # samples in tmax; sample rate is 256 hz
Nsamps_window = tmax_window*hdr.SPR; % # samples in time window
f = hdr.SPR*(0:((Nsamps_window-1)/2))/Nsamps_window; % frequency for plotting
for i = 1:Nsamps; % need to loop through data in 30 second windows in tmax
data_fft = abs(fft(data_ch6(i:i+Nsamps_window-1))); %run fft on data window
data_fft = data_fft(i:((i+Nsamps_window-1)/2)); %discard half the points
figure
plot(f, data_fft)
i = i+Nsamps_window;
end
Well there are a few things that are wrong in your code. First, let me start be saying that i is a very poor choice for a variable name since in matlab it usually stand for sqrt{-1}.
As for your code, I assume that you intend to perform windowed FFT without overlapping.
1) Your loop goes from 1 to Nsamps with an increment of 1. That means the each time you advance 1 sample. In other words you have Nsamps_window-1 overlap. You can use perhaps i=1:Nsamps_window:Nsamps-Nsamps_window-1 if you are not interested in overlapping.
2) The length of data_fft is Nsamps_window, so I think what you wanted to do is data_fft = data_fft(1:round(Nsamps_window/2));
3) When plotting FFT results, I suggest using dB: plot(20*log10(abs(data_fft)));
4) The line i = i+Nsamps_window; is meaningless since i is your loop variable (it has not effect).
Here's my goal:
I'm trying to find a way to search through a data signal and find (index) locations where a known, repeating binary data sequence is located. Then, because the spreading code and demodulation is known, pull out the corresponding chip of data and read it. Currently, I believe xcorr will do the trick.
Here's my problem:
I can't seem to interpret my result from xcorr or xcorr2 to give me what I'm looking for. I'm either having a problem cross-referencing from the vector location of my xcorr function to my time vector, or a problem properly identifying my data sequence with xcorr, or both. Other possibilities may exist.
Where I am at/What I have:
I have created a random BPSK signal that consists of the data sequence of interest and garbage data over a repeating period. I have tried processing it using xcorr, which is where I am stuck.
Here's my code:
%% Clear Variables
clc;
clear all, close all;
%% Create random data
nbits = 2^10;
ngarbage = 3*nbits;
data = randi([0,1],1,nbits);
garbage = randi([0,1],1,ngarbage);
stream = horzcat(data,garbage);
%% Convert from Unipolar to Bipolar Encoding
stream_b = 2*stream - 1;
%% Define Parameters
%%% Variable Parameters
nsamples = 20*nbits;
nseq = 5 %# Iterate stream nseq times
T = 10; %# Number of periods
Ts = 1; %# Symbol Duration
Es = Ts/2; %# Energy per Symbol
fc = 1e9; %# Carrier frequency
%%% Dependent Parameters
A = sqrt(2*Es/Ts); %# Amplitude of Carrier
omega = 2*pi*fc %# Frequency in radians
t = linspace(0,T,nsamples) %# Discrete time from 0 to T periods with nsamples samples
nspb = nsamples/length(stream) %# Number of samples per bit
%% Creating the BPSK Modulation
%# First we have to stretch the stream to fit the time vector. We can quickly do this using _
%# simple matrix manipulation.
% Replicate each bit nspb/nseq times
repStream_b = repmat(stream_b',1,nspb/nseq);
% Tranpose and replicate nseq times to be able to fill to t
modSig_proto = repmat(repStream_b',1,nseq);
% Tranpose column by column, then rearrange into a row vector
modSig = modSig_proto(:)';
%% The Carrier Wave
carrier = A*cos(omega*t);
%% Modulated Signal
sig = modSig.*carrier;
Using XCORR
I use xcorr2() to eliminate the zero padding effect of xcorr on unequal vectors. See comments below for clarification.
corr = abs(xcorr2(data,sig); %# pull the absolute correlation between data and sig
[val,ind] = sort(corr(:),'descend') %# sort the correlation data and assign values and indices
ind_max = ind(1:nseq); %# pull the nseq highest valued indices and send to ind_max
Now, I think this should pull the five highest correlations between data and sig. These should correspond to the end bit of data in the stream for every iteration of stream, because I would think that is where the data would most strongly cross-correlate with sig, but they do not. Sometimes the maxes are not even one stream length apart. So I'm confused here.
Question
In a three part question:
Am I missing a certain step? How do I use xcorr in this case to find where data and sig are most strongly correlated?
Is my entire method wrong? Should I not be looking for the max correlations?
Or should I be attacking this problem from another angle, id est, not use xcorr and maybe use filter or another function?
Your overall method is great and makes a lot of sense. The problem you're having is that you're getting some actual correlation with your garbage data. I noticed that you shifted all of your sream to be zero-centered, but didn't do the same to your data. If you zero-center the data, your correlation peaks will be better defined (at least that worked when I tried it).
data = 2*data -1;
Also, I don't recommend using a simple sort to find your peaks. If you have a wide peak, which is especially possible with a noisy signal, you could have two high points right next to each other. Find a single maximum, and then zero that point and a few neighbors. Then just repeat however many times you like. Alternatively, if you know how long your epoch is, only do a correlation with one epoch's worth of data, and iterate through the signal as it arrives.
With #David K 's and #Patrick Mineault's help I manage to track down where I went wrong. First #Patrick Mineault suggested I flip the signals. The best way to see what you would expect from the result is to slide the small vector along the larger, searched vector. So
corr = xcorr2(sig,data);
Then I like to chop off the end there because it's just extra. I did this with a trim function I made that simply takes the signal you're sliding and trims it's irrelevant pieces off the end of the xcorr result.
trim = #(x,s2) x(1:end - (length(s2) - 1));
trim(corr,data);
Then, as #David K suggests, you need to have the data stream you're looking for encoded the same as your searched signal. So in this case
data = 2*data-1;
Second, if you just have your data at it's original bit length, and not at it's stretched, iterated length, it can be found in the signal but it will be VERY noisy. To reduce the noise, simply stretch the data to match it's stretched length in the iterated signal. So
rdata = repmat(data',1,nspb/nseq);
rdata = repmat(rdata',1,nseq);
data = rdata(:)';
Now finally, we should have crystal clear correlations for this case. And to pull out the maxes that should correspond to those correlations I wrote
[sortedValues sortIndex] = sort(corr(:),'descend');
c = 0 ;
for r = 1 : length(sortedValues)
if sortedValues(r,:) == max(corr)
c = c + 1;
maxIndex(1,c) = sortIndex(r,:);
else break % If you don't do this, you get loop lock
end
end
Now c should end up being nseq for this case and you should have 5 index times where the corrs should be! You can easily pull out the bits with another loop and c or length(maxIndex). I've also made this into a more "real world" toy script, where there is a data stream, doppler, fading, and it's over a time vector in seconds instead of samples.
Thanks for the help!
Try flipping the signal, i.e.:
corr = abs(xcorr2(data,sig(end:-1:1));
Is that any better?
I have been working on a (potentially super simple) assignment in which one of the steps is to get a Fourrier transform. I have followed a guide from my university to transform a wave-sound which can be found here (channels: 2, samples: 17600, sample frequency: 16KHz).
Looking at the graph, it seems to work:
[y,fs,wmode,fidx]=readwav('piano.wav','r',-1,0);
left=y(:,1);
amountOfSlices = 6;
samplesPerSlice = fix(length(left) / 6);
frames=enframe(left, samplesPerSlice);
frames=transpose(frames);
fftdata=rfft(frames);
fftdata=fftdata.*conj(fftdata);
plot(fftdata);
Next, I created a code file with the tutorial code as a basis with the addition of accepting parameters (which are needed for the assignment, but have been left out for brevity).
samplerate = 512;
% Read the file with raw unscaled audio data from begin to end
[multiData,fs,wmode,fidx]=readwav(filename,'r',-1,0);
disp(sprintf('Number of channels: %d', fidx(5)))
disp(sprintf('Number of samples: %d', fidx(4)))
disp(sprintf('Sample frequency: %d Hz', fs))
% Extract the left channel of the data
leftData = multiData(:, 1);
% Slice the left channel into pieces of the size of 'samplerate'
samplesPerSlice = samplerate
% Splits the leftData vector up into frames of length equal to sample rate
slicedLeftData = enframe(leftData, samplerate)';
% Apply the real data fast fourrier transformation on each data slice
fftdata=rfft(slicedLeftData);
fftdata=fftdata.*conj(fftdata);
plot(fftdata);
Do you guys have any idea what I'm doing wrong here?
What my actual question is: Why isn't the second data in the frequency domain of 0 to 16.000 Hz? What am I doing wrong?
I guess by "whats wrong here" you mean the multiple colors? If so, looks like the fftdata plottet in both images are matrices (you want an array, correct?). Doesn't the .* perform the operation on each and every element? Check the dimensions for your ingoing arguments.
Furthermore, remember Nyquist theorem: You can never resolve any frequencies greater than half your sample frequency. In the case of having a sample frequency of 16KHz, you may have datapoints beyong 8kHz but it will not contain any information, so no need to include that in the frequency domain plot.
FFT and changing frequency and vectorizing for loop
Greetings All
I can increase and decrease the frequency of a
signal using the combination of fft and a Fourier series expansion FOR loop in
the code below
but if the signal/array is to large it becomes extremely
slow (an array that's 1x44100 takes about 2 mins to complete) I'm sure
it has to do with the for loop but
I'm not exactly sure how to vectorize it to improve performance. Please note that this will be used with audio signals that are 3 to 6 mins long. The 1x44100 array is only a second and it takes about 2 mins to complete
Any recommendations
%create signal
clear all, clc,clf,tic
x= linspace(0,2*pi,44100)';
%Used in exporting to ycalc audio file make sure in sync with above
freq_orig=1;
freq_new=4
vertoff=0;
vertoffConj=0;
vertoffInv=0;
vertoffInvConj=0;
phaseshift=(0)*pi/180 ; %can use mod to limit to 180 degrees
y=sin(freq_orig*(x));
[size_r,size_c]=size(y);
N=size_r; %to test make 50
T=2*pi;
dt=T/N;
t=linspace(0,T-dt,N)';
phase = 0;
f0 = 1/T; % Exactly, one period
y=(y/max(abs(y))*.8)/2; %make the max amplitude here
C = fft(y)/N; % No semicolon to display output
A = real(C);
B = imag(C)*-1; %I needed to multiply by -1 to get the correct sign
% Single-Sided (f >= 0)
An = [A(1); 2*A(2:round(N/2)); A(round(N/2)+1)];
Bn = [B(1); 2*B(2:round(N/2)); B(round(N/2)+1)];
pmax=N/2;
ycalc=zeros(N,1); %preallocating space for ycalc
w=0;
for p=2:pmax
%
%%1 step) re-create signal using equation
ycalc=ycalc+An(p)*cos(freq_new*(p-1).*t-phaseshift)
+Bn(p)*sin(freq_new*(p-1).*t-phaseshift)+(vertoff/pmax);
w=w+(360/(pmax-1)); %used to create phaseshift
phaseshift=w;
end;
fprintf('\n- Completed in %4.4fsec or %4.4fmins\n',toc,toc/60);
subplot(2,1,1), plot(y),title('Orginal Signal');
subplot(2,1,2),plot(ycalc),title('FFT new signal');
Here's a pic of the plot if some one wants to see the output, which is correct the FOR loop is just really really slow
It appears as though you are basically shifting the signal upwards in the frequency domain, and then your "series expansion" is simply implementing the inverse DFT on the shifted version. As you have seen, the naive iDFT is going to be exceedingly slow. Try changing that entire loop into a call to ifft, and you should be able to get a tremendous speedup.