Find equivalent digital filter - scipy

I'm trying to find an equivalent digital filter for a simple RC filter. The bode plots don't line up and I don't know why.
====================================================
import numpy as np
import scipy.signal as sig
import matplotlib.pyplot as plt
# Analog filter
tau = 0.001 # param
lti = sig.lti([1], [tau, 1])
# Equivalent digital filter
fs = 20000 # param
T = 1 / fs
wd = 1 / tau
wa = 2 / T * np.tan(wd * T / 2)
dtau = 1 / wa
dnum, dden = sig.bilinear([1], [dtau, 1], fs)
dlti = sig.dlti(dnum, dden)
w, mag, phase = sig.bode(lti)
dw, dmag, dphase = sig.dbode(dlti)
plt.figure()
plt.subplot(211)
plt.semilogx(w, mag) # Bode magnitude plot
plt.semilogx(dw, dmag)
plt.subplot(212)
plt.semilogx(w, phase) # Bode phase plot
plt.semilogx(dw, dphase)
plt.show()

sig.bode returns in the first argument Frequency array [rad/s].
sig.dbode returns instead Frequency array [rad/time_unit].
The time unit of a digital signal is a sample, so you need to multiply by the sample rate to convert rad/sample to rad/second:
plt.subplot(211)
plt.semilogx(w, mag) # Bode magnitude plot
plt.semilogx(dw * fs, dmag)
plt.subplot(212)
plt.semilogx(w, phase) # Bode phase plot
plt.semilogx(dw * fs, dphase)

Related

How to compute an IIR Filter frequency response in Octave/MATLAB

I'm trying to compute the frequency response of an IIR filter.
The transfer function of the filter is:
          
The value of a is computed as:
f = 1000;
fsamp = 16000;
a = 1 / (1 + (2 * pi * f) / fsamp);
Ok so now I have the transfer function of my IIR filter. How should I now compute the frequency response? I thought about using the freqz function, but I need help defining the arguments, how do i define the numerator and denominator polynomials?
f=1000; fsamp=16000; a=1/(1+(2*pi*f)/fsamp);
a = [1 -a];
b = [(1-a) 0];
w = logspace(-1,1);
h = freqs(b,a,w);
mag = abs(h);
phase = angle(h);
phasedeg = phase*180/pi;
subplot(2,1,1)
loglog(w,mag)
grid on
xlabel('Frequency (rad/s)')
ylabel('Magnitude')
subplot(2,1,2)
semilogx(w,phasedeg)
grid on
xlabel('Frequency (rad/s)')
ylabel('Phase (degrees)')
This is based on this solution https://www.mathworks.com/help/signal/ref/freqs.html by Mathworks. And I get this outcome:
          
The first two inputs of freqz are respectively the numerator and denominator of the transfer function expressed as polynomials of the variable z−1:
a = 0.7; % example value
num = 1-a;
den = [1, -a];
freqz(num, den, 1001)

IFFT to find sin(x^2)?

I want to use an inverse FFT to calculate inverse Fourier Transforms. I find that I can readily do so with square integrable functions but not with distributions.
First I set up a wavenumber vector k and a spatial coordinate x,
clear;
nx = 2^10;
L = 20;
dx = L/nx;
x = [0:nx-1]' * dx - L/2;
k = zeros(nx,1);
k(1:nx/2+1) = 2*[0:nx/2]/nx;
k(nx:-1:nx/2+2) = -k(2:nx/2);
k = k*pi/dx ;
Next I verify that everything works for two examples: the unit boxcar function and sech:
% boxcar
Ghat = sin( k/2 ) ./ (k/2);
Ghat(1) = 1;
Gi = ifft(Ghat) / dx ;
Gi = ifftshift(Gi);
figure; plot(x,Gi);
% sech( x )
Ghat = pi*sech( pi*k /2 );
Gi = ifft(Ghat) / dx ;
Gi = ifftshift(Gi);
figure; plot(x,Gi,'o'); hold on;
analytical = sech(x);
plot(x,analytical,'-');
Those both look fine...
This is where things stop working:
% sin(x^2)
Ghat = -sqrt(pi)*sin( (k.^2-pi)/4 );
Gi = ifft(Ghat) / dx ;
Gi = ifftshift(Gi);
analytical = sin(x.^2);
figure;
plot(x,analytical,'-'); hold on;
plot(x,Gi,'o');
You will notice that the calculated values look nothing like the intended function.
I don't know why this wont work. The only thing that I notice on Wikipedia is that sin(x^2) is a distribution and therefore not square integrable. Is this the source of my problems? Is there a solution?
I'll give an example in python, should be easy to translate to Matlab.
import numpy as np
import matplotlib.pyplot as p
%matplotlib inline
dt=0.01
time = np.arange(0,10,dt) # 10 secs, sampled at 10 ms, so nyquist is 100 Hz
f=3 # hz
dat= np.sin(2*np.pi* f*time)
p.figure(figsize=(10,5))
p.subplot(221)
p.plot(time,dat)
p.subplot(222)
freqs = np.fft.fftfreq(len(time),d=dt)
#print(freqs)
spec=np.fft.fft(dat)
p.plot( freqs,np.abs(spec) );
f=0.5
dat2= np.sin(2*np.pi* f*time**2)
p.subplot(223)
p.plot(time,dat2)
p.subplot(224)
p.plot( freqs,np.abs(np.fft.fft(dat2)) );
On the left the time-behaviour of the sine and the sine with squared argument (effectively you have a linear frequency change, an upchirp. You could think of one x as the time, the other incorporated into your frequency, which is now rising linearly with time. In the spectrum (showing positive and neg frequency as FFT is defined as complex FT) you can see that the frequency of the sine wave is stable , whereas the sin(x**2) sweeps a range of frequencies.
It always helps to plot things. With the sin(x**2) you could easily violate the Nyquist theorem if the argument rises too fast , leading to undersampling in the time -domain (left) and aliasing the frequency domain (right). This is not shown, just try with a higher base frequency.

Add random noise with specific SNR to a signal

I have randomly generated signal for example:
%frequency
f1 = 1000; f2 = 2000;
Fs = 8000; %sampling frequency
%amplitudes
amp1 = 0.75;
amp2 = 0.2;
%time
dt = 1/Fs
stopTime = 0.3;
t = 0:dt:stopTime;
%random noise
noise = randn(1,length(t))
%generated signal
Signal = amp1*sin(2*pi*f1*t) + amp2*sin(2*pi*f1*t) + noise;
Now i need to create two Signals S1, S2 with random noise with specific SNR.
Noise added to S1 must be uncorrelated with noise added to S2
Here is what i tried:
%SNR in dB
SNR = [-80,-40,-20,0,20,40,80];
%S1,S2 = Signal + rand noise with SNR -40dB
S1 = awgn(Signal,SNR(2),'measured')
S2 = awgn(Signal,SNR(2),'measured')
Is this correct approach to create random noise with SNR from range -80dB to +80dB? will noise added to S1 be uncorrelated with noise added to S2?
You could just calculate variance of signal and add noise with variance required to produce desired SNR. Here is some python code as an example
import scipy as sp
import scipy.fftpack as fft
# frequency
f1 = 1000.0
Fs = 8000.0 # sampling frequency
# amplitudes
amp1 = 0.75
# time
n = sp.arange(1024)
# Desired SNR in dB
SNR_dB = 40
# Desired linear SNR
snr = 10.0**(SNR_dB/10.0)
print "Linear snr = ", snr
# Measure power of signal
signal1 = amp1*sp.sin(2*sp.pi*f1/Fs*n)
p1 = signal1.var()
print "Power of signal1 = ", p1
# Calculate required noise power for desired SNR
n = p1/snr
print "Noise power = ", n
print "Calculated SNR = %f dB" % (10*sp.log10(p1/n))
# Generate noise with calculated power
w = sp.sqrt(n)*sp.randn(1024)
# Add noise to signal
s1 = signal1 + w
will noise added to S1 be uncorrelated with noise added to S2?
Yes.

How to fit a curve to a damped sine wave in matlab

I have some measurements done and It should be a damped sine wave but I can't find any information on how to make (if possible) a good damped sine wave with Matlab's curve fitting tool.
Here's what I get using a "Smoothing spline":
Image http://s21.postimg.org/yznumla1h/damped.png.
Edit 1:
Here's what I got using the "custom equation" option:
Edit 2:
I've uploaded the data to pastebin in csv format where the first column is the amplitude and the second is the time.
The damped sin function can be created using the following code:
f=f*2*pi;
t=0:.001:1;
y=A*sin(f*t + phi).*exp(-a*t);
plot(t,y);
axis([0 1 -2.2 2.2]);
Now you can use "cftool" from matlab and load your data then set the equation type to custom and enter the formula of the damped sin function. Here you can see what I found so far...
I think the distribution of the data makes it hard for the fitting tool to do a good fit. It may need more data or more distributed data. In addition, there are other tools and methods as well, for instance check this for now: docstoc.com/docs/74524947/Mathcad-Damped-sine-fit-mcd
For all these methods that actually trying to search and find a local optimum (for each fitting parameters), the most important thing is the initial condition. I believe if you choose a good initial condition (initial guess), the fitting tool works well.
Good Luck
I wouldn't use the curve fitting toolbox for this, I'd use a curve-fitting function, e.g. lsqcurvefit. Here is an example taken from something I did a while back:
% Define curve model functions
expsin = #(a, f, phi, tau, t)a * sin(omega * t + phi) .* exp(-tau * t);
lsqexpsin = #(p, t)expsin(p(1), p(2), p(3), p(4), t);
% Setup data params
a = 1; % gain
f = 10; % frequency
phi = pi/2; % phase angle
tau = 0.9252523;% time constant
fs = 100; % sample rate
N = fs; % length
SNR = 10; % signal to noise ratio
% Generate time vector
dt = 1/fs;
t = (0:N-1)*dt;
omega = 2 * pi * f; % angular freq
noiseGain = 10^(-SNR/20); % gain for given SNR
% Generate dummy data: decaying sinusoid plus noise
x = expsin(a, omega, phi, tau, t);
noise = noiseGain * rand(size(x));
noise = noise - mean(noise);
x = x + noise;
close all; figure; hold on;
plot(t, x, 'k-', 'LineWidth', 2);
% Count zero crossings to find frequency
zCross = find(x(1:end-1) .* x(2:end) < 0);
T = mean(diff(zCross) * dt) * 2;
fEstimate = 1 / T;
omegaEstimate = 2 * pi * fEstimate;
% Fit model to data
init = [0.5, omegaEstimate, 0, 0.5];
[newparams, err] = lsqcurvefit(lsqexpsin, init, t, x);
plot(t, lsqexpsin(newparams, t))
Here some data with known parameters is generated, and some random noise added; the data is plotted. The parameters [a, phi, tau] are estimated from the data and a curve with the estimated parameters plotted on top.

FFT in MATLAB: wrong 0Hz frequency

I want to use fft in MATLAB to analize some exprimental data saved as an excell file.
my code:
A=xlsread('Book.xls'); G=A'; x=G(2, : );
N=length(x);
F=[-N/2:N/2-1]/N;
X = abs(fft(x-mean(x),N))
X = fftshift(X);
plot(F,X)
But it plots a graph with a large 0Hz wrong component, my true frequency is about 395Hz and it is not shown in the plotted graph.
Please tell me what is wrong.
Any help would be appreciated.
Assume we read the signal from file:
G = xlsread('Book.xls');
t = G(:,1);
x = G(:,2);
N = length(x);
First we estimate the sampling frequency from the time axis, and build the frequency vector:
Fs = 1 ./ abs( t(2)-t(1) );
F = (-N/2:N/2-1)*Fs/N;
then compute the FFT and plot:
X = abs( fft(x-mean(x),N) );
X = fftshift(X);
stem(F,X)
finally find the peak and the corresponding frequency:
>> [~,ind] = max(X);
>> F(ind)
ans =
-400
you might want to zoom-in near the origin to see things more clearly:
xlim([-1000 1000])