BPSK modulation and SNR : Matlab - matlab

Considering an Additive White Gaussian Noise (AWGN) communication channel where a signal taking values from BPSK modulation is being transmitted. Then, the received noisy signal is :y[k] = s[k] + w[k] where s[k] is either +1,-1 symbol and w[k] is the zero mean white gaussian noise.
-- I want to estimate the signal s and evaluate the performance by varing SNR from 0:40 dB. Let, the estimated signal be hat_s.
So, the graph for this would have on X axis the SNR range and on Y Axis the Mean Square Error obtained between the known signal values and the estimates i.e., s[k] - hat_s[k]
Question 1: How do I define signal-to-noise ratio? Would the formula of SNR be sigma^2/sigma^2_w. I am confused about the term in the numerator: what is the variance of the signal, sigma^2, usually considered?
Question 2: But, I don't know what the value of the variance of the noise is, so how does one add noise?
This is what I have done.
N = 100; %number of samples
s = 2*round(rand(N,1))-1;
%bpsk modulation
y = awgn(s,10,'measured'); %adding noise but I don't know
the variance of the signal and noise
%estimation using least squares
hat_s = y./s;
mse_s = ((s-hat_s).^2)/N;
Please correct me where wrong. Thank you.

First I think that it is important to know what are the things we have an a BPSK system:
The constellation of a BPSK system is [-A , A] in this case [-1,1]
the SNR will vary from 0 db to 40 db
I thing that the answer is in this function:
y = awgn( ... ); from matlab central:
y = awgn(x,snr) adds white Gaussian noise to the vector signal x. The
scalar snr specifies the signal-to-noise ratio per sample, in dB. If x
is complex, awgn adds complex noise. This syntax assumes that the
power of x is 0 dBW.
y = awgn(x,snr,sigpower) is the same as the syntax above, except that
sigpower is the power of x in dBW.
y = awgn(x,snr,'measured') is the same as y = awgn(x,snr), except that
awgn measures the power of x before adding noise.
you use y = awgn(x,snr,'measured'), so you do not need to worry, beacuse matlab carries all for you, measure the power of the signal, and then apply to channel a noise with the variance needed to get that SNR ratio.
let's see how can this happen
SNRbit = Eb/No = A^2/No = dmin^2 /4N0
the constelation [A,-A] in this case is [-1,1] so
10 log10(A^2/N0) = 10 log10(1/N0) = SNRbitdb
SNRlineal = 10^(0.1*SNRdb)
so with that:
noise_var=0.5/(EbN0_lin); % s^2=N0/2
and the signal will be something like this
y = s + sqrt(noise_var)*randn(1,size);
so in your case, I will generate the signal as you do:
>> N = 100; %number of samples
>> s = 2*round(rand(N,1))-1; %bpsk modulation
then prepare a SNR varies from 0 to 40 db
>> SNR_DB = 0:1:40;
after that calulating all the posible signals:
>> y = zeros(100,length(SNR_DB));
>> for i = 1:41
y(:,i) = awgn(s,SNR_DB(i),'measured');
end
at this point the best way to see the signal is using a constellation plot like this:
>> scatterplot(y(:,1));
>> scatterplot(y(:,41));
you can see a bad signal 0 db noise equal power as signal and a very good signal signal bigger than 40 DB noise. Eb/No = Power signal - Power noise db, so 0 means power noise equal to power of signal, 40 db means power of signal bigger bigger bigger than power of noise
then for you plot calculate the mse, matlab has one function for this
err = immse(X,Y) Description
example
err = immse(X,Y) calculates the mean-squared error (MSE) between the
arrays X and Y. X and Y can be arrays of any dimension, but must be of
the same size and class.
so with This:
>> for i = 1:41
err(i) = immse(s,y(:,i));
end
>> stem(SNR_DB,err)
For plots, and since we are working in db, it should be beeter to use logarithmic axes

Related

How do I create band-limited (100-640 Hz) white Gaussian noise?

I would like to create 500 ms of band-limited (100-640 Hz) white Gaussian noise with a (relatively) flat frequency spectrum. The noise should be normally distributed with mean = ~0 and 99.7% of values between ± 2 (i.e. standard deviation = 2/3). My sample rate is 1280 Hz; thus, a new amplitude is generated for each frame.
duration = 500e-3;
rate = 1280;
amplitude = 2;
npoints = duration * rate;
noise = (amplitude/3)* randn( 1, npoints );
% Gaus distributed white noise; mean = ~0; 99.7% of amplitudes between ± 2.
time = (0:npoints-1) / rate
Could somebody please show me how to filter the signal for the desired result (i.e. 100-640 Hz)? In addition, I was hoping somebody could also show me how to generate a graph to illustrate that the frequency spectrum is indeed flat.
I intend on importing the waveform to Signal (CED) to output as a form of transcranial electrical stimulation.
The following is Matlab implementation of the method alluded to by "Some Guy" in a comment to your question.
% In frequency domain, white noise has constant amplitude but uniformly
% distributed random phase. We generate this here. Only half of the
% samples are generated here, the rest are computed later using the complex
% conjugate symmetry property of the FFT (of real signals).
X = [1; exp(i*2*pi*rand(npoints/2-1,1)); 1]; % X(1) and X(NFFT/2) must be real
% Identify the locations of frequency bins. These will be used to zero out
% the elements of X that are not in the desired band
freqbins = (0:npoints/2)'/npoints*rate;
% Zero out the frequency components outside the desired band
X(find((freqbins < 100) | (freqbins > 640))) = 0;
% Use the complex conjugate symmetry property of the FFT (for real signals) to
% generate the other half of the frequency-domain signal
X = [X; conj(flipud(X(2:end-1)))];
% IFFT to convert to time-domain
noise = real(ifft(X));
% Normalize such that 99.7% of the times signal lies between ±2
noise = 2*noise/prctile(noise, 99.7);
Statistical analysis of around a million samples generated using this method results in the following spectrum and distribution:
Firstly, the spectrum (using Welch method) is, as expected, flat in the band of interest:
Also, the distribution, estimated using histogram of the signal, matches the Gaussian PDF quite well.

fft / ifft deconvolution in Matlab

I have a time varying signal (time,amplitude) and a measured frequency sensitivity (frequency,amplitude conversion factor (Mf)).
I know that if I use the center frequency of my time signal to select the amplitude conversion factor (e.g. 0.0312) for my signal I get a max. converted amplitude value of 1.4383.
I have written some code to deconvolve the time varying signal and known sensitivity (i.e. for all frequencies).
where Pt is the output/converted amplitude and Mf is amplitude conversion factor data and fft(a) is the fft of the time varying signal (a).
I take the real part of the fft(a):
xdft = fft(a);
xdft = xdft(1:length(x)/2+1); % only retaining the positive frequencies
freq = Fs*(0:(L/2))/L;
where Fs is sampling frequency and L is length of signal.
convS = real(xdft).*Mf;
assuming Mf is magnitude = real (I don't have phase info). I also interpolate
Mf=interp1(freq_Mf,Mf_in,freq,'cubic');
so at the same interrogation points as freq.
I then reconstruct the signal in time domain using:
fftRespI=complex(real(convS),imag(xdft));
pt = ifft(fftRespI,L,'symmetric')
where I use the imaginary part of the fft(a).
The reconstructed signal shape looks correct but the amplitude of the signal is not.
If I set all values of Mf = 0.0312 for f=0..N MHz I expect a max. converted amplitude value of ~1.4383 (similar to if I use the center frequency) but I get 13.0560.
How do I calibrate the amplitude axis? i.e. how do I correctly multiply fft(a) by Mf?
Some improved understanding of the y axis of the abs(magnitude) and real FFT would help me I think...
thanks
You need to re-arrange the order of your weights Mf to match the MATLAB's order for the frequencies. Let's say you have a signal
N=10;
x=randn(1,N);
y=fft(x);
The order of the frequencies in the output y is
[0:1:floor(N/2)-1,floor(-N/2):1:-1] = [0 1 2 3 4 -5 -4 -3 -2 -1]
So if your weights
Mf = randn(1,N)
are defined in this order: Mf == [0 1 2 3 4 5 6 7 8 9], you will have to re-arrange
Mfshift = [Mf(1:N/2), fliplr(Mf(2:(N/2+1)))];
and then you can get your filtered output
z = ifft(fft(x).*Mshift);
which should come out real.

Finding peak frequency in a complex signal using Matlab

I'm trying to find the peak frequency for two signals 'CA1' and 'PFC', within a specified range (25-140Hz).
In Matlab, so far I have plotted an FFT for each of these signals (see pictures below). These FFTs suggest that the peak frequency between 25-140Hz is different for each signal, but I would like to quantify this (e.g. CA1 peaks at 80Hz, whereas PFC peaks at 55Hz). However, I think the FFT is not smooth enough, so when I try and extract the peak frequencies it doesn't make sense as my code pulls out loads of values. I was only expecting a few values - one each time the FFT peaks (around 2Hz, 5Hz and ~60Hz).
I want to know, between 25-140Hz, what is the peak frequency in 'CA1' compared with 'PFC'. 'CA1' and 'PFC' are both 152401 x 7 matrices of EEG data, recorded
from 7 separate individuals. I want the MEAN peak frequency for each data set (i.e. averaged across the 7 test subjects for CA1 and PFC).
My code so far (based on Matlab help files and code I've scrabbled together online):
Fs = 508;
%notch filter
[b50,a50] = iirnotch(50/(Fs/2), (50/(Fs/2))/70);
CA1 = filtfilt(b50,a50,CA1);
PFC = filtfilt(b50,a50,PFC);
%FFT
L = length(CA1);
NFFT = 2^nextpow2(L);
%FFT for each of the 7 subjects
for i = 1:size(CA1,2);
CA1_FFT(:,i) = fft(CA1(:,i),NFFT)/L;
PFC_FFT(:,i) = fft(PFC(:,i),NFFT)/L;
end
%Average FFT across all 7 subjects - CA1
Mean_CA1_FFT = mean(CA1_FFT,2);
% Mean_CA1_FFT_abs = 2*abs(Mean_CA1_FFT(1:NFFT/2+1));
%Average FFT across all 7 subjects - PFC
Mean_PFC_FFT = mean(PFC_FFT,2);
% Mean_PFC_FFT_abs = 2*abs(Mean_PFC_FFT(1:NFFT/2+1));
f = Fs/2*linspace(0,1,NFFT/2+1);
%LEFT HAND SIDE FIGURE
plot(f,2*abs(Mean_CA1_FFT(1:NFFT/2+1)),'r');
set(gca,'ylim', [0 2]);
set(gca,'xlim', [0 200]);
[C,cInd] = sort(2*abs(Mean_CA1_FFT(1:NFFT/2+1)));
CFloor = 0.1; %CFloor is the minimum amplitude value (ignore small values)
Amplitudes_CA1 = C(C>=CFloor); %find all amplitudes above the CFloor
Frequencies_CA1 = f(cInd(1+end-numel(Amplitudes_CA1):end)); %frequency of the peaks
%RIGHT HAND SIDE FIGURE
figure;plot(f,2*abs(Mean_PFC_FFT(1:NFFT/2+1)),'r');
set(gca,'ylim', [0 2]);
set(gca,'xlim', [0 200]);
[P,pInd] = sort(2*abs(Mean_PFC_FFT(1:NFFT/2+1)));
PFloor = 0.1; %PFloor is the minimum amplitude value (ignore small values)
Amplitudes_PFC = P(P>=PFloor); %find all amplitudes above the PFloor
Frequencies_PFC = f(pInd(1+end-numel(Amplitudes_PFC):end)); %frequency of the peaks
Please help!! How do I calculate the 'major' peak frequencies from an FFT, and ignore all the 'minor' peaks (because the FFT is not smoothed).
FFTs assume that the signal has no trend (this is called a stationary signal), if it does then this will give a dominant frequency component at 0Hz as you have here. Try using the MATLAB function detrend, you may find this solves your problem.
Something along the lines of:
x = x - mean(x)
y = detrend(x, 'constant')

ODE with stochastic time dependent input

I am trying to repeat an example I found in a paper.
I have to solve this ODE:
25 a + 15 v + 330000 x = p(t)
where p(t) is a white noise sequence band-limited into the 10-25 Hz range; a is the acceleration, v is the velocity and x the displacement.
Then it says the system is simulated using a Runge-Kutta procedure. The sampling frequency is set to 1000 Hz and a Gaussian white noise is added to the data in such a way that the noise contributes to 5% of the signal r.m.s value (how do I use this last info?).
The main problem is related to the band-limited white noise. I followed the instructions I found here https://dsp.stackexchange.com/questions/9654/how-to-generate-band-limited-gaussian-white-noise and I wrote the following code:
% White noise excitation
% design FIR filter to filter noise in the range [10-25] Hz
Fs = 1000; % sampling frequency
% Time infos (for white noise)
T = 1/Fs;
tspan = 0:T:4; % I saw from the plots in the paper that the simulation last 4 seconds
tstart = tspan(1);
tend = tspan (end);
b = fir1(64, [10/(Fs/2) 25/(Fs/2)]);
% generate Gaussian (normally-distributed) white noise
noise = randn(length(tspan), 1);
% apply filter to yield bandlimited noise
p = filter(b,1,noise);
Now I have to define the function for the ode, but I do not know how to give it the p (white noise)...I tried this way:
function [y] = p(t)
b = fir1(64, [10/(Fs/2) 25/(Fs/2)]);
% generate Gaussian (normally-distributed) white noise
noise = randn(length(t), 1);
% apply filter to yield bandlimited noise
y = filter(b,1,noise);
end
odefun = #(t,u,m,k1,c,p)[u(2); 1/m*(-k1*u(1)-c*u(2)+p(t))];
[t,q,te,qe,ie] = ode45(#(t,u)odefun2(t,u,m,k2,c,#p),tspan,q0,options);
The fact is that the input excitation does not work properly: the natural frequency of the equation is around 14 Hz so I would expect to see the resonance in the response since the white noise is in the range 10-25 Hz.
I had a look at this Q/A also, but I can't still make it works:
How solve a system of ordinary differntial equation with time-dependent parameters
Solving an ODE when the function is given as discrete values -matlab-

Proper way to add noise to signal

In many areas I have found that while adding noise, we mention some specification like zero mean and variance. I need to add AWGN, colored noise, uniform noise of varying SNR in Db. The following code shows the way how I generated and added noise. I am aware of the function awgn() but it is a kind of black box thing without knowing how the noise is getting added. So, can somebody please explain the correct way to generate and add noise. Thank you
SNR = [-10:5:30]; %in Db
snr = 10 .^ (0.1 .* SNR);
for I = 1:length(snr)
noise = 1 / sqrt(2) * (randn(1, N) + 1i * randn(1, N));
u = y + noise .* snr(I);
end
I'm adding another answer since it strikes me that Steven's is not quite correct and Horchler's suggestion to look inside function awgn is a good one.
Either MATLAB or Octave (in the communications toolbox) have a function awgn that adds (white Gaussian) noise to attain a desired signal-to-noise power level; the following is the relevant portion of the code (from the Octave function):
if (meas == 1) % <-- if using signal power to determine appropriate noise power
p = sum( abs( x(:)) .^ 2) / length(x(:));
if (strcmp(type,"dB"))
p = 10 * log10(p);
endif
endif
if (strcmp(type,"linear"))
np = p / snr;
else % <-- in dB
np = p - snr;
endif
y = x + wgn (m, n, np, 1, seed, type, out);
As you can see by the way p (the power of the input data) is computed, the answer from Steven does not appear to be quite right.
You can ask the function to compute the total power of your data array and combine that with the desired s/n value you provide to compute the appropriate power level of the added noise. You do this by passing the string "measured" among the optional inputs, like this (see here for the Octave documentation or here for the MATLAB documentation):
y = awgn (x, snr, 'measured')
This leads ultimately to meas=1 and so meas==1 being true in the code above. The function awgn then uses the signal passed to it to compute the signal power, and from this and the desired s/n it then computes the appropriate power level for the added noise.
As the documentation further explains
By default the snr and pwr are assumed to be in dB and dBW
respectively. This default behavior can be chosen with type set to
"dB". In the case where type is set to "linear", pwr is assumed to be
in Watts and snr is a ratio.
This means you can pass a negative or 0 dB snr value. The result will also depend then on other options you pass, such as the string "measured".
For the MATLAB case I suggest reading the documentation, it explains how to use the function awgn in different scenarios. Note that implementations in Octave and MATLAB are not identical, the computation of noise power should be the same but there may be different options.
And here is the relevant part from wgn (called above by awgn):
if (strcmp(type,"dBW"))
np = 10 ^ (p/10);
elseif (strcmp(type,"dBm"))
np = 10 ^((p - 30)/10);
elseif (strcmp(type,"linear"))
np = p;
endif
if(!isempty(seed))
randn("state",seed);
endif
if (strcmp(out,"complex"))
y = (sqrt(imp*np/2))*(randn(m,n)+1i*randn(m,n)); % imp=1 assuming impedance is 1 Ohm
else
y = (sqrt(imp*np))*randn(m,n);
endif
If you want to check the power of your noise (np), the awgn and awg functions assume the following relationships hold:
np = var(y,1); % linear scale
np = 10*log10(np); % in dB
where var(...,1) is the population variance for the noise y.
Most answers here forget that SNR is specified in decibels. Therefore, you shouldn't encounter 'division by 0' error, because you should really divide by 10^(targetSNR/10) which is never negative nor zero for real targetSNR.
This 'should not divide by 0' problem could be easily solved if you add a condition to check if targetSNR is 0 and do these only if it is not 0. When your target SNR is 0, it means it's pure noise.
function out_signal = addAWGN(signal, targetSNR)
sigLength = length(signal); % length
awgnNoise = randn(size(signal)); % orignal noise
pwrSig = sqrt(sum(signal.^2))/sigLength; % signal power
pwrNoise = sqrt(sum(awgnNoise.^2))/sigLength; % noise power
if targetSNR ~= 0
scaleFactor = (pwrSig/pwrNoise)/targetSNR; %find scale factor
awgnNoise = scaleFactor*awgnNoise;
out_signal = signal + awgnNoise; % add noise
else
out_signal = awgnNoise; % noise only
end
You can use randn() to generate a noise vector 'awgnNoise' of the length you want. Then, given a specified SNR value, calculate the power of the orignal signal and the power of the noise vector 'awgnNoise'.
Get the right amplitude scaling factor for the noise vector and just scale it.
The following code is an example to corrupt signal with white noise, assuming input signal is 1D and real valued.
function out_signal = addAWGN(signal, targetSNR)
sigLength = length(signal); % length
awgnNoise = randn(size(signal)); % orignal noise
pwrSig = sqrt(sum(signal.^2))/sigLength; % signal power
pwrNoise = sqrt(sum(awgnNoise.^2))/sigLength; % noise power
scaleFactor = (pwrSig/pwrNoise)/targetSNR; %find scale factor
awgnNoise = scaleFactor*awgnNoise;
out_signal = signal + awgnNoise; % add noise
Be careful about the sqrt(2) factor when you deal with complex signal, if you want to generate the real and imag part separately.