M = len(h)<EOL>hq = int16(rint(h * <NUM_LIT:2> ** <NUM_LIT:15>))<EOL>N = <NUM_LIT:8>  <EOL>f = open(fname_out, '<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>' % M)<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>')<EOL>kk = <NUM_LIT:0>;<EOL>for k in range(M):<EOL><INDENT>if (kk < N - <NUM_LIT:1>) and (k < M - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' % hq[k])<EOL>kk += <NUM_LIT:1><EOL><DEDENT>elif (kk == N - <NUM_LIT:1>) & (k < M - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' % hq[k])<EOL>if k < M:<EOL><INDENT>f.write('<STR_LIT:U+0020>')<EOL>kk = <NUM_LIT:0><EOL><DEDENT><DEDENT>else:<EOL><INDENT>f.write('<STR_LIT>' % hq[k])<EOL><DEDENT><DEDENT>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.close()<EOL>

Write FIR Fixed-Point Filter Header Files

Mark Wickert February 2015


Ns, Mcol = SOS_mat.shape<EOL>f = open(fname_out, '<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>' % Ns)<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>' % (<NUM_LIT:5> * Ns))<EOL>for k in range(Ns):<EOL><INDENT>if (k < Ns - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' %(SOS_mat[k, <NUM_LIT:0>], SOS_mat[k, <NUM_LIT:1>], SOS_mat[k, <NUM_LIT:2>]))<EOL>f.write('<STR_LIT>' %(-SOS_mat[k, <NUM_LIT:4>], -SOS_mat[k, <NUM_LIT:5>]))<EOL><DEDENT>else:<EOL><INDENT>f.write('<STR_LIT>' %(SOS_mat[k, <NUM_LIT:0>], SOS_mat[k, <NUM_LIT:1>], SOS_mat[k, <NUM_LIT:2>]))<EOL>f.write('<STR_LIT>' %(-SOS_mat[k, <NUM_LIT:4>], -SOS_mat[k, <NUM_LIT:5>]))<EOL><DEDENT><DEDENT>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.close()<EOL>

Write IIR SOS Header Files
File format is compatible with CMSIS-DSP IIR
Directform II Filter Functions

Mark Wickert March 2015-October 2016


def freqz_resp_list(b, a=np.array([<NUM_LIT:1>]), mode='<STR_LIT>', fs=<NUM_LIT:1.0>, Npts=<NUM_LIT>, fsize=(<NUM_LIT:6>, <NUM_LIT:4>)):

if type(b) == list:<EOL><INDENT>N_filt = len(b)<EOL><DEDENT>f = np.arange(<NUM_LIT:0>, Npts) / (<NUM_LIT> * Npts)<EOL>for n in range(N_filt):<EOL><INDENT>w, H = signal.freqz(b[n], a[n], <NUM_LIT:2> * np.pi * f)<EOL>if n == <NUM_LIT:0>:<EOL><INDENT>plt.figure(figsize=fsize)<EOL><DEDENT>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f * fs, <NUM_LIT:20> * np.log10(np.abs(H)))<EOL>if n == N_filt - <NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f * fs, np.angle(H))<EOL>if n == N_filt - <NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif (mode.lower() == '<STR_LIT>') or (mode.lower() == '<STR_LIT>'):<EOL><INDENT>"""<STR_LIT>"""<EOL>theta = np.unwrap(np.angle(H))<EOL>theta2 = np.unwrap(<NUM_LIT:2> * theta) / <NUM_LIT><EOL>theta_dif = np.diff(theta2)<EOL>f_diff = np.diff(f)<EOL>Tg = -np.diff(theta2) / np.diff(w)<EOL>idx = pylab.find(<NUM_LIT:20> * np.log10(H[:-<NUM_LIT:1>]) < -<NUM_LIT>)<EOL>Tg[idx] = np.zeros(len(idx))<EOL>max_Tg = np.max(Tg)<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>max_Tg /= fs<EOL>plt.plot(f[:-<NUM_LIT:1>] * fs, Tg / fs)<EOL>plt.ylim([<NUM_LIT:0>, <NUM_LIT> * max_Tg])<EOL><DEDENT>else:<EOL><INDENT>plt.plot(f[:-<NUM_LIT:1>] * fs, Tg)<EOL>plt.ylim([<NUM_LIT:0>, <NUM_LIT> * max_Tg])<EOL><DEDENT>if n == N_filt - <NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>else:<EOL><INDENT>s1 = '<STR_LIT>'<EOL>s2 = '<STR_LIT>'<EOL>print(s1 + s2)<EOL><DEDENT><DEDENT>

A method for displaying digital filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freq_resp(self,mode = 'dB',Npts = 1024)

A method for displaying the filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freqz_resp(b,a=[1],mode = 'dB',Npts = 1024,fsize=(6,4))

Parameters
----------
b : ndarray of numerator coefficients
a : ndarray of denominator coefficents
mode : display mode: 'dB' magnitude, 'phase' in radians, or
        'groupdelay_s' in samples and 'groupdelay_t' in sec,
        all versus frequency in Hz
Npts : number of points to plot; default is 1024
fsize : figure size; defult is (6,4) inches

Mark Wickert, January 2015


dir_path = os.path.dirname(os.path.realpath(__file__))<EOL>ca = loadtxt(dir_path + '<STR_LIT>', dtype=int16, usecols=(Nca - <NUM_LIT:1>,), unpack=True)<EOL>M = <NUM_LIT>  <EOL>N = <NUM_LIT>  <EOL>Sca = '<STR_LIT>' + str(Nca)<EOL>f = open(fname_out, '<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>' % M)<EOL>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>');<EOL>f.write('<STR_LIT>'% Nca);<EOL>f.write('<STR_LIT>' % Nca)<EOL>kk = <NUM_LIT:0>;<EOL>for k in range(M):<EOL><INDENT>if (kk < N - <NUM_LIT:1>) and (k < M - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' % ca[k])<EOL>kk += <NUM_LIT:1><EOL><DEDENT>elif (kk == N - <NUM_LIT:1>) & (k < M - <NUM_LIT:1>):<EOL><INDENT>f.write('<STR_LIT>' % ca[k])<EOL>if k < M:<EOL><INDENT>if Nca < <NUM_LIT:10>:<EOL><INDENT>f.write('<STR_LIT:U+0020>')<EOL><DEDENT>else:<EOL><INDENT>f.write('<STR_LIT:U+0020>')<EOL><DEDENT>kk = <NUM_LIT:0><EOL><DEDENT><DEDENT>else:<EOL><INDENT>f.write('<STR_LIT>' % ca[k])<EOL><DEDENT><DEDENT>f.write('<STR_LIT>')<EOL>f.write('<STR_LIT>')<EOL>f.close()<EOL>

Write 1023 bit CA (Gold) Code Header Files

Mark Wickert February 2015


def IIR_lpf(f_pass, f_stop, Ripple_pass, Atten_stop, <EOL>fs = <NUM_LIT>, ftype = '<STR_LIT>'):

b,a = signal.iirdesign(<NUM_LIT:2>*float(f_pass)/fs, <NUM_LIT:2>*float(f_stop)/fs,<EOL>Ripple_pass, Atten_stop,<EOL>ftype = ftype, output='<STR_LIT>')<EOL>sos = signal.iirdesign(<NUM_LIT:2>*float(f_pass)/fs, <NUM_LIT:2>*float(f_stop)/fs,<EOL>Ripple_pass, Atten_stop,<EOL>ftype = ftype, output='<STR_LIT>')<EOL>tag = '<STR_LIT>' + ftype + '<STR_LIT>'<EOL>print('<STR_LIT>' % (tag,len(a)-<NUM_LIT:1>))<EOL>return b, a, sos<EOL>

Design an IIR lowpass filter using scipy.signal.iirdesign.
The filter order is determined based on
f_pass Hz, f_stop Hz, and the desired stopband attenuation
d_stop in dB, all relative to a sampling rate of fs Hz.

Parameters
----------
f_pass : Passband critical frequency in Hz
f_stop : Stopband critical frequency in Hz
Ripple_pass : Filter gain in dB at f_pass
Atten_stop : Filter attenuation in dB at f_stop
fs : Sampling rate in Hz
ftype : Analog prototype from 'butter' 'cheby1', 'cheby2',
        'ellip', and 'bessel'

Returns
-------
b : ndarray of the numerator coefficients
a : ndarray of the denominator coefficients
sos : 2D ndarray of second-order section coefficients

Notes
-----
Additionally a text string telling the user the filter order is
written to the console, e.g., IIR cheby1 order = 8.

Examples
--------
>>> fs = 48000
>>> f_pass = 5000
>>> f_stop = 8000
>>> b_but,a_but,sos_but = IIR_lpf(f_pass,f_stop,0.5,60,fs,'butter')
>>> b_cheb1,a_cheb1,sos_cheb1 = IIR_lpf(f_pass,f_stop,0.5,60,fs,'cheby1')
>>> b_cheb2,a_cheb2,sos_cheb2 = IIR_lpf(f_pass,f_stop,0.5,60,fs,'cheby2')
>>> b_elli,a_elli,sos_elli = IIR_lpf(f_pass,f_stop,0.5,60,fs,'ellip')

Mark Wickert October 2016


def IIR_hpf(f_stop, f_pass, Ripple_pass, Atten_stop, <EOL>fs = <NUM_LIT>, ftype = '<STR_LIT>'):

b,a = signal.iirdesign(<NUM_LIT:2>*float(f_pass)/fs, <NUM_LIT:2>*float(f_stop)/fs,<EOL>Ripple_pass, Atten_stop,<EOL>ftype = ftype, output='<STR_LIT>')<EOL>sos = signal.iirdesign(<NUM_LIT:2>*float(f_pass)/fs, <NUM_LIT:2>*float(f_stop)/fs,<EOL>Ripple_pass, Atten_stop,<EOL>ftype =ftype, output='<STR_LIT>')<EOL>tag = '<STR_LIT>' + ftype + '<STR_LIT>'<EOL>print('<STR_LIT>' % (tag,len(a)-<NUM_LIT:1>))<EOL>return b, a, sos<EOL>

Design an IIR highpass filter using scipy.signal.iirdesign.
The filter order is determined based on
f_pass Hz, f_stop Hz, and the desired stopband attenuation
d_stop in dB, all relative to a sampling rate of fs Hz.

Parameters
----------
f_stop :
f_pass :
Ripple_pass :
Atten_stop :
fs : sampling rate in Hz
ftype : Analog prototype from 'butter' 'cheby1', 'cheby2',
        'ellip', and 'bessel'

Returns
-------
b : ndarray of the numerator coefficients
a : ndarray of the denominator coefficients
sos : 2D ndarray of second-order section coefficients

Examples
--------
>>> fs = 48000
>>> f_pass = 8000
>>> f_stop = 5000
>>> b_but,a_but,sos_but = IIR_hpf(f_stop,f_pass,0.5,60,fs,'butter')
>>> b_cheb1,a_cheb1,sos_cheb1 = IIR_hpf(f_stop,f_pass,0.5,60,fs,'cheby1')
>>> b_cheb2,a_cheb2,sos_cheb2 = IIR_hpf(f_stop,f_pass,0.5,60,fs,'cheby2')
>>> b_elli,a_elli,sos_elli = IIR_hpf(f_stop,f_pass,0.5,60,fs,'ellip')

Mark Wickert October 2016


def IIR_bpf(f_stop1, f_pass1, f_pass2, f_stop2, Ripple_pass, Atten_stop, <EOL>fs = <NUM_LIT>, ftype = '<STR_LIT>'):

b,a = signal.iirdesign([<NUM_LIT:2>*float(f_pass1)/fs, <NUM_LIT:2>*float(f_pass2)/fs],<EOL>[<NUM_LIT:2>*float(f_stop1)/fs, <NUM_LIT:2>*float(f_stop2)/fs],<EOL>Ripple_pass, Atten_stop,<EOL>ftype = ftype, output='<STR_LIT>')<EOL>sos = signal.iirdesign([<NUM_LIT:2>*float(f_pass1)/fs, <NUM_LIT:2>*float(f_pass2)/fs],<EOL>[<NUM_LIT:2>*float(f_stop1)/fs, <NUM_LIT:2>*float(f_stop2)/fs],<EOL>Ripple_pass, Atten_stop,<EOL>ftype =ftype, output='<STR_LIT>')<EOL>tag = '<STR_LIT>' + ftype + '<STR_LIT>'<EOL>print('<STR_LIT>' % (tag,len(a)-<NUM_LIT:1>))<EOL>return b, a, sos<EOL>

Design an IIR bandpass filter using scipy.signal.iirdesign.
The filter order is determined based on
f_pass Hz, f_stop Hz, and the desired stopband attenuation
d_stop in dB, all relative to a sampling rate of fs Hz.

Parameters
----------
f_stop1 : ndarray of the numerator coefficients
f_pass : ndarray of the denominator coefficients
Ripple_pass :
Atten_stop :
fs : sampling rate in Hz
ftype : Analog prototype from 'butter' 'cheby1', 'cheby2',
        'ellip', and 'bessel'

Returns
-------
b : ndarray of the numerator coefficients
a : ndarray of the denominator coefficients
sos : 2D ndarray of second-order section coefficients

Examples
--------
>>> fs = 48000
>>> f_pass = 8000
>>> f_stop = 5000
>>> b_but,a_but,sos_but = IIR_hpf(f_stop,f_pass,0.5,60,fs,'butter')
>>> b_cheb1,a_cheb1,sos_cheb1 = IIR_hpf(f_stop,f_pass,0.5,60,fs,'cheby1')
>>> b_cheb2,a_cheb2,sos_cheb2 = IIR_hpf(f_stop,f_pass,0.5,60,fs,'cheby2')
>>> b_elli,a_elli,sos_elli = IIR_hpf(f_stop,f_pass,0.5,60,fs,'ellip')

Mark Wickert October 2016


def IIR_bsf(f_pass1, f_stop1, f_stop2, f_pass2, Ripple_pass, Atten_stop, <EOL>fs = <NUM_LIT>, ftype = '<STR_LIT>'):

Design an IIR bandstop filter using scipy.signal.iirdesign.
The filter order is determined based on
f_pass Hz, f_stop Hz, and the desired stopband attenuation
d_stop in dB, all relative to a sampling rate of fs Hz.

Mark Wickert October 2016


def freqz_resp_list(b,a=np.array([<NUM_LIT:1>]),mode = '<STR_LIT>',fs=<NUM_LIT:1.0>,Npts = <NUM_LIT>,fsize=(<NUM_LIT:6>,<NUM_LIT:4>)):

if type(b) == list:<EOL><INDENT>N_filt = len(b)<EOL><DEDENT>f = np.arange(<NUM_LIT:0>,Npts)/(<NUM_LIT>*Npts)<EOL>for n in range(N_filt):<EOL><INDENT>w,H = signal.freqz(b[n],a[n],<NUM_LIT:2>*np.pi*f)<EOL>if n == <NUM_LIT:0>:<EOL><INDENT>plt.figure(figsize=fsize)<EOL><DEDENT>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,<NUM_LIT:20>*np.log10(np.abs(H)))<EOL>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,np.angle(H))<EOL>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif (mode.lower() == '<STR_LIT>') or (mode.lower() == '<STR_LIT>'):<EOL><INDENT>"""<STR_LIT>"""<EOL>theta = np.unwrap(np.angle(H))<EOL>theta2 = np.unwrap(<NUM_LIT:2>*theta)/<NUM_LIT><EOL>theta_dif = np.diff(theta2)<EOL>f_diff = np.diff(f)<EOL>Tg = -np.diff(theta2)/np.diff(w)<EOL>idx = np.nonzero(np.ravel(<NUM_LIT:20>*np.log10(H[:-<NUM_LIT:1>]) < -<NUM_LIT>))[<NUM_LIT:0>]<EOL>Tg[idx] = np.zeros(len(idx))<EOL>max_Tg = np.max(Tg)<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>max_Tg /= fs<EOL>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg/fs)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>else:<EOL><INDENT>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>else:<EOL><INDENT>s1 = '<STR_LIT>'<EOL>s2 = '<STR_LIT>'<EOL>print(s1 + s2)<EOL><DEDENT><DEDENT>

A method for displaying digital filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freq_resp(self,mode = 'dB',Npts = 1024)

A method for displaying the filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freqz_resp(b,a=[1],mode = 'dB',Npts = 1024,fsize=(6,4))

    b = ndarray of numerator coefficients
    a = ndarray of denominator coefficents
 mode = display mode: 'dB' magnitude, 'phase' in radians, or
        'groupdelay_s' in samples and 'groupdelay_t' in sec,
        all versus frequency in Hz
 Npts = number of points to plot; default is 1024
fsize = figure size; defult is (6,4) inches

Mark Wickert, January 2015


Ns,Mcol = sos.shape<EOL>w,Hcas = signal.freqz(sos[<NUM_LIT:0>,:<NUM_LIT:3>],sos[<NUM_LIT:0>,<NUM_LIT:3>:],w)<EOL>for k in range(<NUM_LIT:1>,Ns):<EOL><INDENT>w,Htemp = signal.freqz(sos[k,:<NUM_LIT:3>],sos[k,<NUM_LIT:3>:],w)<EOL>Hcas *= Htemp<EOL><DEDENT>return w, Hcas<EOL>

Cascade frequency response

Mark Wickert October 2016


def freqz_resp_cas_list(sos,mode = '<STR_LIT>',fs=<NUM_LIT:1.0>,Npts = <NUM_LIT>,fsize=(<NUM_LIT:6>,<NUM_LIT:4>)):

if type(sos) == list:<EOL><INDENT>N_filt = len(sos)<EOL><DEDENT>f = np.arange(<NUM_LIT:0>,Npts)/(<NUM_LIT>*Npts)<EOL>for n in range(N_filt):<EOL><INDENT>w,H = freqz_cas(sos[n],<NUM_LIT:2>*np.pi*f)<EOL>if n == <NUM_LIT:0>:<EOL><INDENT>plt.figure(figsize=fsize)<EOL><DEDENT>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,<NUM_LIT:20>*np.log10(np.abs(H)))<EOL>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,np.angle(H))<EOL>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif (mode.lower() == '<STR_LIT>') or (mode.lower() == '<STR_LIT>'):<EOL><INDENT>"""<STR_LIT>"""<EOL>theta = np.unwrap(np.angle(H))<EOL>theta2 = np.unwrap(<NUM_LIT:2>*theta)/<NUM_LIT><EOL>theta_dif = np.diff(theta2)<EOL>f_diff = np.diff(f)<EOL>Tg = -np.diff(theta2)/np.diff(w)<EOL>idx = np.nonzero(np.ravel(<NUM_LIT:20>*np.log10(H[:-<NUM_LIT:1>]) < -<NUM_LIT>))[<NUM_LIT:0>]<EOL>Tg[idx] = np.zeros(len(idx))<EOL>max_Tg = np.max(Tg)<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>max_Tg /= fs<EOL>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg/fs)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>else:<EOL><INDENT>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>else:<EOL><INDENT>s1 = '<STR_LIT>'<EOL>s2 = '<STR_LIT>'<EOL>print(s1 + s2)<EOL><DEDENT><DEDENT>

A method for displaying cascade digital filter form frequency response
magnitude, phase, and group delay. A plot is produced using matplotlib

freq_resp(self,mode = 'dB',Npts = 1024)

A method for displaying the filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freqz_resp(b,a=[1],mode = 'dB',Npts = 1024,fsize=(6,4))

    b = ndarray of numerator coefficients
    a = ndarray of denominator coefficents
 mode = display mode: 'dB' magnitude, 'phase' in radians, or
        'groupdelay_s' in samples and 'groupdelay_t' in sec,
        all versus frequency in Hz
 Npts = number of points to plot; default is 1024
fsize = figure size; defult is (6,4) inches

Mark Wickert, January 2015


def unique_cpx_roots(rlist,tol = <NUM_LIT>):

uniq = [rlist[<NUM_LIT:0>]]<EOL>mult = [<NUM_LIT:1>]<EOL>for k in range(<NUM_LIT:1>,len(rlist)):<EOL><INDENT>N_uniq = len(uniq)<EOL>for m in range(N_uniq):<EOL><INDENT>if abs(rlist[k]-uniq[m]) <= tol:<EOL><INDENT>mult[m] += <NUM_LIT:1><EOL>uniq[m] = (uniq[m]*(mult[m]-<NUM_LIT:1>) + rlist[k])/float(mult[m])<EOL>break<EOL><DEDENT><DEDENT>uniq = np.hstack((uniq,rlist[k]))<EOL>mult = np.hstack((mult,[<NUM_LIT:1>]))<EOL><DEDENT>return np.array(uniq), np.array(mult)<EOL>

The average of the root values is used when multiplicity
is greater than one.

Mark Wickert October 2016


return np.vstack((sos1,sos2))<EOL>


def sos_zplane(sos,auto_scale=True,size=<NUM_LIT:2>,tol = <NUM_LIT>):

Ns,Mcol = sos.shape<EOL>N_roots = []<EOL>for k in range(Ns):<EOL><INDENT>N_roots_tmp = np.roots(sos[k,:<NUM_LIT:3>])<EOL>if N_roots_tmp[<NUM_LIT:1>] == <NUM_LIT:0.>:<EOL><INDENT>N_roots = np.hstack((N_roots,N_roots_tmp[<NUM_LIT:0>]))<EOL><DEDENT>else:<EOL><INDENT>N_roots = np.hstack((N_roots,N_roots_tmp))<EOL><DEDENT><DEDENT>D_roots = []<EOL>for k in range(Ns):<EOL><INDENT>D_roots_tmp = np.roots(sos[k,<NUM_LIT:3>:])<EOL>if D_roots_tmp[<NUM_LIT:1>] == <NUM_LIT:0.>:<EOL><INDENT>D_roots = np.hstack((D_roots,D_roots_tmp[<NUM_LIT:0>]))<EOL><DEDENT>else:<EOL><INDENT>D_roots = np.hstack((D_roots,D_roots_tmp))<EOL><DEDENT><DEDENT>x_scale = <NUM_LIT>*size<EOL>y_scale = <NUM_LIT>*size   <EOL>x_off = <NUM_LIT><EOL>y_off = <NUM_LIT><EOL>M = len(N_roots)<EOL>N = len(D_roots)<EOL>if auto_scale:<EOL><INDENT>if M > <NUM_LIT:0> and N > <NUM_LIT:0>:<EOL><INDENT>size = max(np.max(np.abs(N_roots)),np.max(np.abs(D_roots)))+<NUM_LIT><EOL><DEDENT>elif M > <NUM_LIT:0>:<EOL><INDENT>size = max(np.max(np.abs(N_roots)),<NUM_LIT:1.0>)+<NUM_LIT><EOL><DEDENT>elif N > <NUM_LIT:0>:<EOL><INDENT>size = max(<NUM_LIT:1.0>,np.max(np.abs(D_roots)))+<NUM_LIT><EOL><DEDENT>else:<EOL><INDENT>size = <NUM_LIT><EOL><DEDENT><DEDENT>plt.figure(figsize=(<NUM_LIT:5>,<NUM_LIT:5>))<EOL>plt.axis('<STR_LIT>')<EOL>r = np.linspace(<NUM_LIT:0>,<NUM_LIT:2>*np.pi,<NUM_LIT:200>)<EOL>plt.plot(np.cos(r),np.sin(r),'<STR_LIT>')<EOL>plt.plot([-size,size],[<NUM_LIT:0>,<NUM_LIT:0>],'<STR_LIT>')<EOL>plt.plot([<NUM_LIT:0>,<NUM_LIT:0>],[-size,size],'<STR_LIT>')<EOL>if M > <NUM_LIT:0>:<EOL><INDENT>N_uniq, N_mult=unique_cpx_roots(N_roots,tol=tol)<EOL>plt.plot(np.real(N_uniq),np.imag(N_uniq),'<STR_LIT>',mfc='<STR_LIT:None>',ms=<NUM_LIT:8>)<EOL>idx_N_mult = np.nonzero(np.ravel(N_mult><NUM_LIT:1>))[<NUM_LIT:0>]<EOL>for k in range(len(idx_N_mult)):<EOL><INDENT>x_loc = np.real(N_uniq[idx_N_mult[k]]) + x_off*x_scale<EOL>y_loc =np.imag(N_uniq[idx_N_mult[k]]) + y_off*y_scale<EOL>plt.text(x_loc,y_loc,str(N_mult[idx_N_mult[k]]),<EOL>ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)<EOL><DEDENT><DEDENT>if N > <NUM_LIT:0>:<EOL><INDENT>D_uniq, D_mult=unique_cpx_roots(D_roots,tol=tol)<EOL>plt.plot(np.real(D_uniq),np.imag(D_uniq),'<STR_LIT>',ms=<NUM_LIT:8>)<EOL>idx_D_mult = np.nonzero(np.ravel(D_mult><NUM_LIT:1>))[<NUM_LIT:0>]<EOL>for k in range(len(idx_D_mult)):<EOL><INDENT>x_loc = np.real(D_uniq[idx_D_mult[k]]) + x_off*x_scale<EOL>y_loc =np.imag(D_uniq[idx_D_mult[k]]) + y_off*y_scale<EOL>plt.text(x_loc,y_loc,str(D_mult[idx_D_mult[k]]),<EOL>ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)            <EOL><DEDENT><DEDENT>if M - N < <NUM_LIT:0>:<EOL><INDENT>plt.plot(<NUM_LIT:0.0>,<NUM_LIT:0.0>,'<STR_LIT>',mfc='<STR_LIT:None>',ms=<NUM_LIT:8>)<EOL><DEDENT>elif M - N > <NUM_LIT:0>:<EOL><INDENT>plt.plot(<NUM_LIT:0.0>,<NUM_LIT:0.0>,'<STR_LIT>',ms=<NUM_LIT:8>)<EOL><DEDENT>if abs(M - N) > <NUM_LIT:1>:<EOL><INDENT>plt.text(x_off*x_scale,y_off*y_scale,str(abs(M-N)),<EOL>ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)        <EOL><DEDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL>plt.axis([-size,size,-size,size])<EOL>return M,N<EOL>

Create an z-plane pole-zero plot.

Create an z-plane pole-zero plot using the numerator
and denominator z-domain system function coefficient
ndarrays b and a respectively. Assume descending powers of z.

Parameters
----------
sos : ndarray of the sos coefficients
auto_scale : bool (default True)
size : plot radius maximum when scale = False

Returns
-------
(M,N) : tuple of zero and pole counts + plot window

Notes
-----
This function tries to identify repeated poles and zeros and will
place the multiplicity number above and to the right of the pole or zero.
The difficulty is setting the tolerance for this detection. Currently it
is set at 1e-3 via the function signal.unique_roots.

Examples
--------
>>> # Here the plot is generated using auto_scale
>>> sos_zplane(sos)
>>> # Here the plot is generated using manual scaling
>>> sos_zplane(sos,False,1.5)


<EOL>K0 = -<NUM_LIT:1.0> <EOL>Kp = <NUM_LIT:1.0><EOL>K1 = <NUM_LIT:4>*zeta/(zeta + <NUM_LIT:1>/(<NUM_LIT:4>*zeta))*BnTs/Ns/Kp/K0<EOL>K2 = <NUM_LIT:4>/(zeta + <NUM_LIT:1>/(<NUM_LIT:4>*zeta))**<NUM_LIT:2>*(BnTs/Ns)**<NUM_LIT:2>/Kp/K0<EOL>zz = np.zeros(len(z),dtype=np.complex128)<EOL>e_tau = np.zeros(len(z))<EOL>c1_buff = np.zeros(<NUM_LIT:2>*L+<NUM_LIT:1>)<EOL>vi = <NUM_LIT:0><EOL>CNT_next = <NUM_LIT:0><EOL>mu_next = <NUM_LIT:0><EOL>underflow = <NUM_LIT:0><EOL>epsilon = <NUM_LIT:0><EOL>mm = <NUM_LIT:1><EOL>z = np.hstack(([<NUM_LIT:0>], z))<EOL>for nn in range(<NUM_LIT:1>,Ns*int(np.floor(len(z)/float(Ns)-(Ns-<NUM_LIT:1>)))):<EOL><INDENT>CNT = CNT_next<EOL>mu = mu_next<EOL>if underflow == <NUM_LIT:1>:<EOL><INDENT>if I_ord == <NUM_LIT:1>:<EOL><INDENT>z_interp = mu*z[nn] + (<NUM_LIT:1> - mu)*z[nn-<NUM_LIT:1>]<EOL><DEDENT>elif I_ord == <NUM_LIT:2>:<EOL><INDENT>v2 = <NUM_LIT:1>/<NUM_LIT>*np.sum(z[nn+<NUM_LIT:2>:nn-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[<NUM_LIT:1>, -<NUM_LIT:1>, -<NUM_LIT:1>, <NUM_LIT:1>])<EOL>v1 = <NUM_LIT:1>/<NUM_LIT>*np.sum(z[nn+<NUM_LIT:2>:nn-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[-<NUM_LIT:1>, <NUM_LIT:3>, -<NUM_LIT:1>, -<NUM_LIT:1>])<EOL>v0 = z[nn]<EOL>z_interp = (mu*v2 + v1)*mu + v0<EOL><DEDENT>elif I_ord == <NUM_LIT:3>:<EOL><INDENT>v3 = np.sum(z[nn+<NUM_LIT:2>:nn-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[<NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>, <NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>])<EOL>v2 = np.sum(z[nn+<NUM_LIT:2>:nn-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[<NUM_LIT:0>, <NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>, <NUM_LIT:1>/<NUM_LIT>])<EOL>v1 = np.sum(z[nn+<NUM_LIT:2>:nn-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[-<NUM_LIT:1>/<NUM_LIT>, <NUM_LIT:1>, -<NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>])<EOL>v0 = z[nn]<EOL>z_interp = ((mu*v3 + v2)*mu + v1)*mu + v0<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>c1 = <NUM_LIT:0><EOL>for kk in range(Ns):<EOL><INDENT>if I_ord == <NUM_LIT:1>:<EOL><INDENT>z_TED_interp = mu*z[nn+kk] + (<NUM_LIT:1> - mu)*z[nn-<NUM_LIT:1>+kk]<EOL><DEDENT>elif I_ord == <NUM_LIT:2>:<EOL><INDENT>v2 = <NUM_LIT:1>/<NUM_LIT>*np.sum(z[nn+kk+<NUM_LIT:2>:nn+kk-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[<NUM_LIT:1>, -<NUM_LIT:1>, -<NUM_LIT:1>, <NUM_LIT:1>])<EOL>v1 = <NUM_LIT:1>/<NUM_LIT>*np.sum(z[nn+kk+<NUM_LIT:2>:nn+kk-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[-<NUM_LIT:1>, <NUM_LIT:3>, -<NUM_LIT:1>, -<NUM_LIT:1>])<EOL>v0 = z[nn+kk]<EOL>z_TED_interp = (mu*v2 + v1)*mu + v0<EOL><DEDENT>elif I_ord == <NUM_LIT:3>:<EOL><INDENT>v3 = np.sum(z[nn+kk+<NUM_LIT:2>:nn+kk-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[<NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>, <NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>])<EOL>v2 = np.sum(z[nn+kk+<NUM_LIT:2>:nn+kk-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[<NUM_LIT:0>, <NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>, <NUM_LIT:1>/<NUM_LIT>])<EOL>v1 = np.sum(z[nn+kk+<NUM_LIT:2>:nn+kk-<NUM_LIT:1>-<NUM_LIT:1>:-<NUM_LIT:1>]*[-<NUM_LIT:1>/<NUM_LIT>, <NUM_LIT:1>, -<NUM_LIT:1>/<NUM_LIT>, -<NUM_LIT:1>/<NUM_LIT>])<EOL>v0 = z[nn+kk]<EOL>z_TED_interp = ((mu*v3 + v2)*mu + v1)*mu + v0<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>c1 = c1 + np.abs(z_TED_interp)**<NUM_LIT:2> * np.exp(-<NUM_LIT>*<NUM_LIT:2>*np.pi/Ns*kk)<EOL><DEDENT>c1 = c1/Ns<EOL>c1_buff = np.hstack(([c1], c1_buff[:-<NUM_LIT:1>]))<EOL>epsilon = -<NUM_LIT:1>/(<NUM_LIT:2>*np.pi)*np.angle(np.sum(c1_buff)/(<NUM_LIT:2>*L+<NUM_LIT:1>))<EOL>zz[mm] = z_interp<EOL>e_tau[mm] = epsilon <EOL>mm += <NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT>vp = K1*epsilon       <EOL>vi = vi + K2*epsilon  <EOL>v = vp + vi           <EOL>W = <NUM_LIT:1>/float(Ns) + v          <EOL>CNT_next = CNT - W           <EOL>if CNT_next < <NUM_LIT:0>:             <EOL><INDENT>CNT_next = <NUM_LIT:1> + CNT_next  <EOL>underflow = <NUM_LIT:1>            <EOL>mu_next = CNT/W          <EOL><DEDENT>else:<EOL><INDENT>underflow = <NUM_LIT:0><EOL>mu_next = mu<EOL><DEDENT><DEDENT>zz = zz[:-(len(zz)-mm+<NUM_LIT:1>)]<EOL>zz /=np.std(zz)<EOL>e_tau = e_tau[:-(len(e_tau)-mm+<NUM_LIT:1>)]<EOL>return zz, e_tau<EOL>

zz,e_tau = NDA_symb_sync(z,Ns,L,BnTs,zeta=0.707,I_ord=3)

       z = complex baseband input signal at nominally Ns samples
           per symbol
      Ns = Nominal number of samples per symbol (Ts/T) in the symbol
           tracking loop, often 4
    BnTs = time bandwidth product of loop bandwidth and the symbol period,
           thus the loop bandwidth as a fraction of the symbol rate.
    zeta = loop damping factor
   I_ord = interpolator order, 1, 2, or 3

   e_tau = the timing error e(k) input to the loop filter

      Kp = The phase detector gain in the symbol tracking loop; for the
           NDA algoithm used here always 1

Mark Wickert July 2014

Motivated by code found in M. Rice, Digital Communications A Discrete-Time
Approach, Prentice Hall, New Jersey, 2009. (ISBN 978-0-13-030497-1).


Ns = <NUM_LIT:1><EOL>Kp = np.sqrt(<NUM_LIT>) <EOL>z_prime = np.zeros_like(z)<EOL>a_hat = np.zeros_like(z)<EOL>e_phi = np.zeros(len(z))<EOL>theta_h = np.zeros(len(z))<EOL>theta_hat = <NUM_LIT:0><EOL>K0 = <NUM_LIT:1>;<EOL>K1 = <NUM_LIT:4>*zeta/(zeta + <NUM_LIT:1>/(<NUM_LIT:4>*zeta))*BnTs/Ns/Kp/K0;<EOL>K2 = <NUM_LIT:4>/(zeta + <NUM_LIT:1>/(<NUM_LIT:4>*zeta))**<NUM_LIT:2>*(BnTs/Ns)**<NUM_LIT:2>/Kp/K0;<EOL>vi = <NUM_LIT:0><EOL>for nn in range(len(z)):<EOL><INDENT>z_prime[nn] = z[nn]*np.exp(-<NUM_LIT>*theta_hat)<EOL>if M == <NUM_LIT:2>:<EOL><INDENT>a_hat[nn] = np.sign(z_prime[nn].real) + <NUM_LIT>*<NUM_LIT:0><EOL><DEDENT>elif M == <NUM_LIT:4>:<EOL><INDENT>a_hat[nn] = np.sign(z_prime[nn].real) + <NUM_LIT>*np.sign(z_prime[nn].imag)<EOL><DEDENT>elif M == <NUM_LIT:8>:<EOL><INDENT>a_hat[nn] = np.angle(z_prime[nn])/(<NUM_LIT:2>*np.pi/<NUM_LIT>)<EOL>a_hat[nn] = np.mod(round(a_hat[nn]),<NUM_LIT:8>)<EOL>a_hat[nn] = np.exp(<NUM_LIT>*<NUM_LIT:2>*np.pi*a_hat[nn]/<NUM_LIT:8>)<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if type == <NUM_LIT:0>:<EOL><INDENT>e_phi[nn] = z_prime[nn].imag * a_hat[nn].real -z_prime[nn].real * a_hat[nn].imag<EOL><DEDENT>elif type == <NUM_LIT:1>:<EOL><INDENT>e_phi[nn] = np.angle(z_prime[nn]) - np.angle(a_hat[nn])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>vp = K1*e_phi[nn]      <EOL>vi = vi + K2*e_phi[nn] <EOL>v = vp + vi        <EOL>theta_hat = np.mod(theta_hat + v,<NUM_LIT:2>*np.pi)<EOL>theta_h[nn] = theta_hat <EOL><DEDENT>return z_prime, a_hat, e_phi, theta_h<EOL>

z_prime,a_hat,e_phi = DD_carrier_sync(z,M,BnTs,zeta=0.707,type=0)
Decision directed carrier phase tracking

       z = complex baseband PSK signal at one sample per symbol
       M = The PSK modulation order, i.e., 2, 8, or 8.
    BnTs = time bandwidth product of loop bandwidth and the symbol period,
           thus the loop bandwidth as a fraction of the symbol rate.
    zeta = loop damping factor
    type = Phase error detector type: 0 <> ML, 1 <> heuristic

 z_prime = phase rotation output (like soft symbol values)
   a_hat = the hard decision symbol values landing at the constellation
           values
   e_phi = the phase error e(k) into the loop filter

      Ns = Nominal number of samples per symbol (Ts/T) in the carrier
           phase tracking loop, almost always 1
      Kp = The phase detector gain in the carrier phase tracking loop;
           This value depends upon the algorithm type. For the ML scheme
           described at the end of notes Chapter 9, A = 1, K 1/sqrt(2),
           so Kp = sqrt(2).

Mark Wickert July 2014

Motivated by code found in M. Rice, Digital Communications A Discrete-Time
Approach, Prentice Hall, New Jersey, 2009. (ISBN 978-0-13-030497-1).


z_step = np.hstack((z[:Ns*Nstep], z[(Ns*Nstep+t_step):], np.zeros(t_step)))<EOL>return z_step<EOL>

Create a one sample per symbol signal containing a phase rotation
step Nsymb into the waveform.

:param z: complex baseband signal after matched filter
:param Ns: number of sample per symbol
:param t_step: in samples relative to Ns
:param Nstep: symbol sample location where the step turns on
:return: the one sample per symbol signal containing the phase step

Mark Wickert July 2014


nn = np.arange(<NUM_LIT:0>,len(z[::Ns]))<EOL>theta = np.zeros(len(nn))<EOL>idx = np.where(nn >= Nstep)<EOL>theta[idx] = p_step*np.ones(len(idx))<EOL>z_rot = z[::Ns]*np.exp(<NUM_LIT>*theta)<EOL>return z_rot<EOL>

Create a one sample per symbol signal containing a phase rotation
step Nsymb into the waveform.

:param z: complex baseband signal after matched filter
:param Ns: number of sample per symbol
:param p_step: size in radians of the phase step
:param Nstep: symbol sample location where the step turns on
:return: the one sample symbol signal containing the phase step

Mark Wickert July 2014


T = <NUM_LIT:1>/float(fs)<EOL>Kv = <NUM_LIT:2>*np.pi*Kv <EOL>if loop_type == <NUM_LIT:1>:<EOL><INDENT>K = <NUM_LIT:2>*np.pi*fn <EOL><DEDENT>elif loop_type == <NUM_LIT:2>:<EOL><INDENT>K = <NUM_LIT:4> *np.pi*zeta*fn <EOL>tau2 = zeta/(np.pi*fn)<EOL><DEDENT>elif loop_type == <NUM_LIT:3>:<EOL><INDENT>K = Kv <EOL>tau1 = K/((<NUM_LIT:2>*np.pi*fn)**<NUM_LIT:2>)<EOL>tau2 = <NUM_LIT:2>*zeta/(<NUM_LIT:2>*np.pi*fn)*(<NUM_LIT:1> - <NUM_LIT:2>*np.pi*fn/K*<NUM_LIT:1>/(<NUM_LIT:2>*zeta))<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>filt_in_last = <NUM_LIT:0>; filt_out_last = <NUM_LIT:0>;<EOL>vco_in_last = <NUM_LIT:0>; vco_out = <NUM_LIT:0>; vco_out_last = <NUM_LIT:0>;<EOL>n = np.arange(len(theta))<EOL>theta_hat = np.zeros_like(theta)<EOL>ev = np.zeros_like(theta)<EOL>phi = np.zeros_like(theta)<EOL>for k in  range(len(n)):<EOL><INDENT>phi[k] = theta[k] - vco_out<EOL>if non_lin == <NUM_LIT:1>:<EOL><INDENT>pd_out = np.sin(phi[k])<EOL><DEDENT>else:<EOL><INDENT>pd_out = phi[k]<EOL><DEDENT>gain_out = K/Kv*pd_out <EOL>if loop_type == <NUM_LIT:2>:<EOL><INDENT>filt_in = (<NUM_LIT:1>/tau2)*gain_out<EOL>filt_out = filt_out_last + T/<NUM_LIT:2>*(filt_in + filt_in_last)<EOL>filt_in_last = filt_in<EOL>filt_out_last = filt_out<EOL>filt_out = filt_out + gain_out<EOL><DEDENT>elif loop_type == <NUM_LIT:3>:<EOL><INDENT>filt_in = (tau2/tau1)*gain_out - (<NUM_LIT:1>/tau1)*filt_out_last<EOL>u3 = filt_in + (<NUM_LIT:1>/tau2)*filt_out_last<EOL>filt_out = filt_out_last + T/<NUM_LIT:2>*(filt_in + filt_in_last)<EOL>filt_in_last = filt_in<EOL>filt_out_last = filt_out<EOL><DEDENT>else:<EOL><INDENT>filt_out = gain_out;<EOL><DEDENT>vco_in = filt_out<EOL>if loop_type == <NUM_LIT:3>:<EOL><INDENT>vco_in = u3<EOL><DEDENT>vco_out = vco_out_last + T/<NUM_LIT:2>*(vco_in + vco_in_last)<EOL>vco_in_last = vco_in<EOL>vco_out_last = vco_out<EOL>vco_out = Kv*vco_out <EOL>ev[k] = vco_in<EOL>theta_hat[k] = vco_out<EOL><DEDENT>return theta_hat, ev, phi<EOL>

Baseband Analog PLL Simulation Model

:param theta: input phase deviation in radians
:param fs: sampling rate in sample per second or Hz
:param loop_type: 1, first-order loop filter F(s)=K_LF; 2, integrator
            with lead compensation F(s) = (1 + s tau2)/(s tau1),
            i.e., a type II, or 3, lowpass with lead compensation
            F(s) = (1 + s tau2)/(1 + s tau1)
:param Kv: VCO gain in Hz/v; note presently assume Kp = 1v/rad
            and K_LF = 1; the user can easily change this
:param fn: Loop natural frequency (loops 2 & 3) or cutoff
            frquency (loop 1)
:param zeta: Damping factor for loops 2 & 3
:param non_lin: 0, linear phase detector; 1, sinusoidal phase detector
:return: theta_hat = Output phase estimate of the input theta in radians,
         ev = VCO control voltage,
         phi = phase error = theta - theta_hat

Notes
-----
Alternate input in place of natural frequency, fn, in Hz is
the noise equivalent bandwidth Bn in Hz.

Mark Wickert, April 2007 for ECE 5625/4625
Modified February 2008 and July 2014 for ECE 5675/4675
Python version August 2014


T = <NUM_LIT:1>/float(fs)<EOL>Kv = <NUM_LIT:2>*np.pi*Kv <EOL>if loop_type == <NUM_LIT:1>:<EOL><INDENT>K = <NUM_LIT:2>*np.pi*fn <EOL><DEDENT>elif loop_type == <NUM_LIT:2>:<EOL><INDENT>K = <NUM_LIT:4> *np.pi*zeta*fn <EOL>tau2 = zeta/(np.pi*fn)<EOL><DEDENT>elif loop_type == <NUM_LIT:3>:<EOL><INDENT>K = Kv <EOL>tau1 = K/((<NUM_LIT:2>*np.pi*fn)^<NUM_LIT:2>);<EOL>tau2 = <NUM_LIT:2>*zeta/(<NUM_LIT:2>*np.pi*fn)*(<NUM_LIT:1> - <NUM_LIT:2>*np.pi*fn/K*<NUM_LIT:1>/(<NUM_LIT:2>*zeta))<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>filt_in_last = <NUM_LIT:0>; filt_out_last = <NUM_LIT:0>;<EOL>vco_in_last = <NUM_LIT:0>; vco_out = <NUM_LIT:0>; vco_out_last = <NUM_LIT:0>;<EOL>vco_out_cbb = <NUM_LIT:0><EOL>n = np.arange(len(x))<EOL>theta_hat = np.zeros(len(x))<EOL>ev = np.zeros(len(x))<EOL>phi = np.zeros(len(x))<EOL>for k in  range(len(n)):<EOL><INDENT>phi[k] = np.imag(x[k] * np.conj(vco_out_cbb))<EOL>pd_out = phi[k]<EOL>gain_out = K/Kv*pd_out <EOL>if loop_type == <NUM_LIT:2>:<EOL><INDENT>filt_in = (<NUM_LIT:1>/tau2)*gain_out<EOL>filt_out = filt_out_last + T/<NUM_LIT:2>*(filt_in + filt_in_last)<EOL>filt_in_last = filt_in<EOL>filt_out_last = filt_out<EOL>filt_out = filt_out + gain_out<EOL><DEDENT>elif loop_type == <NUM_LIT:3>:<EOL><INDENT>filt_in = (tau2/tau1)*gain_out - (<NUM_LIT:1>/tau1)*filt_out_last<EOL>u3 = filt_in + (<NUM_LIT:1>/tau2)*filt_out_last<EOL>filt_out = filt_out_last + T/<NUM_LIT:2>*(filt_in + filt_in_last)<EOL>filt_in_last = filt_in<EOL>filt_out_last = filt_out<EOL><DEDENT>else:<EOL><INDENT>filt_out = gain_out;<EOL><DEDENT>vco_in = filt_out<EOL>if loop_type == <NUM_LIT:3>:<EOL><INDENT>vco_in = u3<EOL><DEDENT>vco_out = vco_out_last + T/<NUM_LIT:2>*(vco_in + vco_in_last)<EOL>vco_in_last = vco_in<EOL>vco_out_last = vco_out<EOL>vco_out = Kv*vco_out <EOL>vco_out_cbb = np.exp(<NUM_LIT>*vco_out)<EOL>ev[k] = vco_in<EOL>theta_hat[k] = vco_out<EOL><DEDENT>return theta_hat, ev, phi<EOL>

Baseband Analog PLL Simulation Model

:param x: input phase deviation in radians
:param fs: sampling rate in sample per second or Hz
:param loop_type: 1, first-order loop filter F(s)=K_LF; 2, integrator
            with lead compensation F(s) = (1 + s tau2)/(s tau1),
            i.e., a type II, or 3, lowpass with lead compensation
            F(s) = (1 + s tau2)/(1 + s tau1)
:param Kv: VCO gain in Hz/v; note presently assume Kp = 1v/rad
            and K_LF = 1; the user can easily change this
:param fn: Loop natural frequency (loops 2 & 3) or cutoff
            frequency (loop 1)
:param zeta: Damping factor for loops 2 & 3
:return: theta_hat = Output phase estimate of the input theta in radians,
         ev = VCO control voltage,
         phi = phase error = theta - theta_hat

Mark Wickert, April 2007 for ECE 5625/4625
Modified February 2008 and July 2014 for ECE 5675/4675
Python version August 2014


def firwin_lpf(N_taps, fc, fs = <NUM_LIT:1.0>):

return signal.firwin(N_taps,<NUM_LIT:2>*fc/fs)<EOL>

Design a windowed FIR lowpass filter in terms of passband
critical frequencies f1 < f2 in Hz relative to sampling rate
fs in Hz. The number of taps must be provided.

Mark Wickert October 2016


def firwin_bpf(N_taps, f1, f2, fs = <NUM_LIT:1.0>, pass_zero=False):

return signal.firwin(N_taps,<NUM_LIT:2>*(f1,f2)/fs,pass_zero=pass_zero)<EOL>

Design a windowed FIR bandpass filter in terms of passband
critical frequencies f1 < f2 in Hz relative to sampling rate
fs in Hz. The number of taps must be provided.

Mark Wickert October 2016


def firwin_kaiser_lpf(f_pass, f_stop, d_stop, fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:0>):

wc = <NUM_LIT:2>*np.pi*(f_pass + f_stop)/<NUM_LIT:2>/fs<EOL>delta_w = <NUM_LIT:2>*np.pi*(f_stop - f_pass)/fs<EOL>M = np.ceil((d_stop - <NUM_LIT:8>)/(<NUM_LIT>*delta_w))<EOL>M += N_bump<EOL>N_taps = M + <NUM_LIT:1><EOL>beta = signal.kaiser_beta(d_stop)<EOL>w_k = signal.kaiser(N_taps,beta)<EOL>n = np.arange(N_taps)<EOL>b_k = wc/np.pi*np.sinc(wc/np.pi*(n-M/<NUM_LIT:2>)) * w_k<EOL>b_k /= np.sum(b_k)<EOL>print('<STR_LIT>' % N_taps)<EOL>return b_k<EOL>

Design an FIR lowpass filter using the sinc() kernel and
a Kaiser window. The filter order is determined based on
f_pass Hz, f_stop Hz, and the desired stopband attenuation
d_stop in dB, all relative to a sampling rate of fs Hz.
Note: the passband ripple cannot be set independent of the
stopband attenuation.

Mark Wickert October 2016


def firwin_kaiser_hpf(f_stop, f_pass, d_stop, fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:0>):

<EOL>f_pass_eq = fs/<NUM_LIT> - f_pass<EOL>f_stop_eq = fs/<NUM_LIT> - f_stop<EOL>wc = <NUM_LIT:2>*np.pi*(f_pass_eq + f_stop_eq)/<NUM_LIT:2>/fs<EOL>delta_w = <NUM_LIT:2>*np.pi*(f_stop_eq - f_pass_eq)/fs<EOL>M = np.ceil((d_stop - <NUM_LIT:8>)/(<NUM_LIT>*delta_w))<EOL>M += N_bump<EOL>N_taps = M + <NUM_LIT:1><EOL>beta = signal.kaiser_beta(d_stop)<EOL>w_k = signal.kaiser(N_taps,beta)<EOL>n = np.arange(N_taps)<EOL>b_k = wc/np.pi*np.sinc(wc/np.pi*(n-M/<NUM_LIT:2>)) * w_k<EOL>b_k /= np.sum(b_k)<EOL>n = np.arange(len(b_k))<EOL>b_k *= (-<NUM_LIT:1>)**n<EOL>print('<STR_LIT>' % N_taps)<EOL>return b_k<EOL>

Design an FIR highpass filter using the sinc() kernel and
a Kaiser window. The filter order is determined based on
f_pass Hz, f_stop Hz, and the desired stopband attenuation
d_stop in dB, all relative to a sampling rate of fs Hz.
Note: the passband ripple cannot be set independent of the
stopband attenuation.

Mark Wickert October 2016


def firwin_kaiser_bpf(f_stop1, f_pass1, f_pass2, f_stop2, d_stop, <EOL>fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:0>):

<EOL>f_pass = (f_pass2 - f_pass1)/<NUM_LIT:2><EOL>f_stop = (f_stop2 - f_stop1)/<NUM_LIT:2><EOL>wc = <NUM_LIT:2>*np.pi*(f_pass + f_stop)/<NUM_LIT:2>/fs<EOL>delta_w = <NUM_LIT:2>*np.pi*(f_stop - f_pass)/fs<EOL>M = np.ceil((d_stop - <NUM_LIT:8>)/(<NUM_LIT>*delta_w))<EOL>M += N_bump<EOL>N_taps = M + <NUM_LIT:1><EOL>beta = signal.kaiser_beta(d_stop)<EOL>w_k = signal.kaiser(N_taps,beta)<EOL>n = np.arange(N_taps)<EOL>b_k = wc/np.pi*np.sinc(wc/np.pi*(n-M/<NUM_LIT:2>)) * w_k<EOL>b_k /= np.sum(b_k)<EOL>f0 = (f_pass2 + f_pass1)/<NUM_LIT:2><EOL>w0 = <NUM_LIT:2>*np.pi*f0/fs<EOL>n = np.arange(len(b_k))<EOL>b_k_bp = <NUM_LIT:2>*b_k*np.cos(w0*(n-M/<NUM_LIT:2>))<EOL>print('<STR_LIT>' % N_taps)<EOL>return b_k_bp<EOL>

Design an FIR bandpass filter using the sinc() kernel and
a Kaiser window. The filter order is determined based on
f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
desired stopband attenuation d_stop in dB for both stopbands,
all relative to a sampling rate of fs Hz.
Note: the passband ripple cannot be set independent of the
stopband attenuation.

Mark Wickert October 2016


def firwin_kaiser_bsf(f_stop1, f_pass1, f_pass2, f_stop2, d_stop, <EOL>fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:0>):

<EOL>f_pass = (f_pass2 - f_pass1)/<NUM_LIT:2><EOL>f_stop = (f_stop2 - f_stop1)/<NUM_LIT:2><EOL>wc = <NUM_LIT:2>*np.pi*(f_pass + f_stop)/<NUM_LIT:2>/fs<EOL>delta_w = <NUM_LIT:2>*np.pi*(f_stop - f_pass)/fs<EOL>M = np.ceil((d_stop - <NUM_LIT:8>)/(<NUM_LIT>*delta_w))<EOL>M += N_bump<EOL>if ((M+<NUM_LIT:1>)/<NUM_LIT>-int((M+<NUM_LIT:1>)/<NUM_LIT>)) == <NUM_LIT:0>:<EOL><INDENT>M += <NUM_LIT:1><EOL><DEDENT>N_taps = M + <NUM_LIT:1><EOL>beta = signal.kaiser_beta(d_stop)<EOL>w_k = signal.kaiser(N_taps,beta)<EOL>n = np.arange(N_taps)<EOL>b_k = wc/np.pi*np.sinc(wc/np.pi*(n-M/<NUM_LIT:2>)) * w_k<EOL>b_k /= np.sum(b_k)<EOL>f0 = (f_pass2 + f_pass1)/<NUM_LIT:2><EOL>w0 = <NUM_LIT:2>*np.pi*f0/fs<EOL>n = np.arange(len(b_k))<EOL>b_k_bs = <NUM_LIT:2>*b_k*np.cos(w0*(n-M/<NUM_LIT:2>))<EOL>b_k_bs = -b_k_bs<EOL>b_k_bs[int(M/<NUM_LIT:2>)] += <NUM_LIT:1> <EOL>print('<STR_LIT>' % N_taps)<EOL>return b_k_bs<EOL>

Design an FIR bandstop filter using the sinc() kernel and
a Kaiser window. The filter order is determined based on
f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
desired stopband attenuation d_stop in dB for both stopbands,
all relative to a sampling rate of fs Hz.
Note: The passband ripple cannot be set independent of the
stopband attenuation.
Note: The filter order is forced to be even (odd number of taps)
so there is a center tap that can be used to form 1 - H_BPF.

Mark Wickert October 2016


def lowpass_order(f_pass, f_stop, dpass_dB, dstop_dB, fsamp = <NUM_LIT:1>):

dpass = <NUM_LIT:1> - <NUM_LIT:10>**(-dpass_dB/<NUM_LIT:20>)<EOL>dstop = <NUM_LIT:10>**(-dstop_dB/<NUM_LIT:20>)<EOL>Df = (f_stop - f_pass)/fsamp<EOL>a1 = <NUM_LIT><EOL>a2 = <NUM_LIT><EOL>a3 = -<NUM_LIT><EOL>a4 = -<NUM_LIT><EOL>a5 = -<NUM_LIT><EOL>a6 = -<NUM_LIT><EOL>Dinf = np.log10(dstop)*(a1*np.log10(dpass)**<NUM_LIT:2> + a2*np.log10(dpass) + a3)+ (a4*np.log10(dpass)**<NUM_LIT:2> + a5*np.log10(dpass) + a6)<EOL>f = <NUM_LIT> + <NUM_LIT>*(np.log10(dpass) - np.log10(dstop))<EOL>N = Dinf/Df - f*Df + <NUM_LIT:1><EOL>ff = <NUM_LIT:2>*np.array([<NUM_LIT:0>, f_pass, f_stop, fsamp/<NUM_LIT:2>])/fsamp<EOL>aa = np.array([<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>])<EOL>wts = np.array([<NUM_LIT:1.0>, dpass/dstop])<EOL>return int(N), ff, aa, wts<EOL>

Optimal FIR (equal ripple) Lowpass Order Determination

Text reference: Ifeachor, Digital Signal Processing a Practical Approach,
second edition, Prentice Hall, 2002.
Journal paper reference: Herriman et al., Practical Design Rules for Optimum
Finite Imulse Response Digitl Filters, Bell Syst. Tech. J., vol 52, pp.
769-799, July-Aug., 1973.IEEE, 1973.


def bandpass_order(f_stop1, f_pass1, f_pass2, f_stop2, dpass_dB, dstop_dB, fsamp = <NUM_LIT:1>):

dpass = <NUM_LIT:1> - <NUM_LIT:10>**(-dpass_dB/<NUM_LIT:20>)<EOL>dstop = <NUM_LIT:10>**(-dstop_dB/<NUM_LIT:20>)<EOL>Df1 = (f_pass1 - f_stop1)/fsamp<EOL>Df2 = (f_stop2 - f_pass2)/fsamp<EOL>b1 = <NUM_LIT><EOL>b2 = <NUM_LIT><EOL>b3 = -<NUM_LIT><EOL>b4 = <NUM_LIT><EOL>b5 = -<NUM_LIT><EOL>b6 = -<NUM_LIT><EOL>Df = min(Df1, Df2)<EOL>Cinf = np.log10(dstop)*(b1*np.log10(dpass)**<NUM_LIT:2> + b2*np.log10(dpass) + b3)+ (b4*np.log10(dpass)**<NUM_LIT:2> + b5*np.log10(dpass) + b6)<EOL>g = -<NUM_LIT>*np.log10(dpass/dstop) - <NUM_LIT><EOL>N = Cinf/Df + g*Df + <NUM_LIT:1><EOL>ff = <NUM_LIT:2>*np.array([<NUM_LIT:0>, f_stop1, f_pass1, f_pass2, f_stop2, fsamp/<NUM_LIT:2>])/fsamp<EOL>aa = np.array([<NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>])<EOL>wts = np.array([dpass/dstop, <NUM_LIT:1>, dpass/dstop])<EOL>return int(N), ff, aa, wts<EOL>

Optimal FIR (equal ripple) Bandpass Order Determination

Text reference: Ifeachor, Digital Signal Processing a Practical Approach,
second edition, Prentice Hall, 2002.
Journal paper reference: F. Mintzer & B. Liu, Practical Design Rules for Optimum
FIR Bandpass Digital Filters, IEEE Transactions on Acoustics and Speech, pp.
204-206, April,1979.


def bandstop_order(f_stop1, f_pass1, f_pass2, f_stop2, dpass_dB, dstop_dB, fsamp = <NUM_LIT:1>):

dpass = <NUM_LIT:1> - <NUM_LIT:10>**(-dpass_dB/<NUM_LIT:20>)<EOL>dstop = <NUM_LIT:10>**(-dstop_dB/<NUM_LIT:20>)<EOL>Df1 = (f_pass1 - f_stop1)/fsamp<EOL>Df2 = (f_stop2 - f_pass2)/fsamp<EOL>b1 = <NUM_LIT><EOL>b2 = <NUM_LIT><EOL>b3 = -<NUM_LIT><EOL>b4 = <NUM_LIT><EOL>b5 = -<NUM_LIT><EOL>b6 = -<NUM_LIT><EOL>Df = min(Df1, Df2)<EOL>Cinf = np.log10(dstop)*(b1*np.log10(dpass)**<NUM_LIT:2> + b2*np.log10(dpass) + b3)+ (b4*np.log10(dpass)**<NUM_LIT:2> + b5*np.log10(dpass) + b6)<EOL>g = -<NUM_LIT>*np.log10(dpass/dstop) - <NUM_LIT><EOL>N = Cinf/Df + g*Df + <NUM_LIT:1><EOL>ff = <NUM_LIT:2>*np.array([<NUM_LIT:0>, f_stop1, f_pass1, f_pass2, f_stop2, fsamp/<NUM_LIT:2>])/fsamp<EOL>aa = np.array([<NUM_LIT:1>, <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>, <NUM_LIT:1>, <NUM_LIT:1>])<EOL>wts = np.array([<NUM_LIT:2>, dpass/dstop, <NUM_LIT:2>])<EOL>return int(N), ff, aa, wts<EOL>

Optimal FIR (equal ripple) Bandstop Order Determination

Text reference: Ifeachor, Digital Signal Processing a Practical Approach,
second edition, Prentice Hall, 2002.
Journal paper reference: F. Mintzer & B. Liu, Practical Design Rules for Optimum
FIR Bandpass Digital Filters, IEEE Transactions on Acoustics and Speech, pp.
204-206, April,1979.


def fir_remez_lpf(f_pass, f_stop, d_pass, d_stop, fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:5>):

n, ff, aa, wts = lowpass_order(f_pass, f_stop, d_pass, d_stop, fsamp=fs)<EOL>N_taps = n<EOL>N_taps += N_bump<EOL>b = signal.remez(N_taps, ff, aa[<NUM_LIT:0>::<NUM_LIT:2>], wts,Hz=<NUM_LIT:2>)<EOL>print('<STR_LIT>' % N_taps)<EOL>return b<EOL>

Design an FIR lowpass filter using remez with order
determination. The filter order is determined based on
f_pass Hz, fstop Hz, and the desired passband ripple
d_pass dB and stopband attenuation d_stop dB all
relative to a sampling rate of fs Hz.

Mark Wickert October 2016, updated October 2018


def fir_remez_hpf(f_stop, f_pass, d_pass, d_stop, fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:5>):

<EOL>f_pass_eq = fs/<NUM_LIT> - f_pass<EOL>f_stop_eq = fs/<NUM_LIT> - f_stop<EOL>n, ff, aa, wts = lowpass_order(f_pass_eq, f_stop_eq, d_pass, d_stop, fsamp=fs)<EOL>N_taps = n<EOL>N_taps += N_bump<EOL>b = signal.remez(N_taps, ff, aa[<NUM_LIT:0>::<NUM_LIT:2>], wts,Hz=<NUM_LIT:2>)<EOL>n = np.arange(len(b))<EOL>b *= (-<NUM_LIT:1>)**n<EOL>print('<STR_LIT>' % N_taps)<EOL>return b<EOL>

Design an FIR highpass filter using remez with order
determination. The filter order is determined based on
f_pass Hz, fstop Hz, and the desired passband ripple
d_pass dB and stopband attenuation d_stop dB all
relative to a sampling rate of fs Hz.

Mark Wickert October 2016, updated October 2018


def fir_remez_bpf(f_stop1, f_pass1, f_pass2, f_stop2, d_pass, d_stop, <EOL>fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:5>):

n, ff, aa, wts = bandpass_order(f_stop1, f_pass1, f_pass2, f_stop2, <EOL>d_pass, d_stop, fsamp=fs)<EOL>N_taps = n<EOL>N_taps += N_bump<EOL>b = signal.remez(N_taps, ff, aa[<NUM_LIT:0>::<NUM_LIT:2>], wts,Hz=<NUM_LIT:2>)<EOL>print('<STR_LIT>' % N_taps)<EOL>return b<EOL>

Design an FIR bandpass filter using remez with order
determination. The filter order is determined based on
f_stop1 Hz, f_pass1 Hz, f_pass2 Hz, f_stop2 Hz, and the
desired passband ripple d_pass dB and stopband attenuation
d_stop dB all relative to a sampling rate of fs Hz.

Mark Wickert October 2016, updated October 2018


def fir_remez_bsf(f_pass1, f_stop1, f_stop2, f_pass2, d_pass, d_stop, <EOL>fs = <NUM_LIT:1.0>, N_bump=<NUM_LIT:5>):

n, ff, aa, wts = bandstop_order(f_pass1, f_stop1, f_stop2, f_pass2, <EOL>d_pass, d_stop, fsamp=fs)<EOL>if np.mod(n,<NUM_LIT:2>) != <NUM_LIT:0>:<EOL><INDENT>n += <NUM_LIT:1><EOL><DEDENT>N_taps = n<EOL>N_taps += N_bump<EOL>b = signal.remez(N_taps, ff, aa[<NUM_LIT:0>::<NUM_LIT:2>], wts, Hz=<NUM_LIT:2>,<EOL>maxiter = <NUM_LIT>, grid_density = <NUM_LIT:16>)<EOL>print('<STR_LIT>')<EOL>print('<STR_LIT>' % N_taps)<EOL>return b<EOL>

Design an FIR bandstop filter using remez with order
determination. The filter order is determined based on
f_pass1 Hz, f_stop1 Hz, f_stop2 Hz, f_pass2 Hz, and the
desired passband ripple d_pass dB and stopband attenuation
d_stop dB all relative to a sampling rate of fs Hz.

Mark Wickert October 2016, updated October 2018


def freqz_resp_list(b,a=np.array([<NUM_LIT:1>]),mode = '<STR_LIT>',fs=<NUM_LIT:1.0>,Npts = <NUM_LIT>,fsize=(<NUM_LIT:6>,<NUM_LIT:4>)):

if type(b) == list:<EOL><INDENT>N_filt = len(b)<EOL><DEDENT>f = np.arange(<NUM_LIT:0>,Npts)/(<NUM_LIT>*Npts)<EOL>for n in range(N_filt):<EOL><INDENT>w,H = signal.freqz(b[n],a[n],<NUM_LIT:2>*np.pi*f)<EOL>if n == <NUM_LIT:0>:<EOL><INDENT>plt.figure(figsize=fsize)<EOL><DEDENT>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,<NUM_LIT:20>*np.log10(np.abs(H)))<EOL>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f*fs,np.angle(H))<EOL>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>elif (mode.lower() == '<STR_LIT>') or (mode.lower() == '<STR_LIT>'):<EOL><INDENT>"""<STR_LIT>"""<EOL>theta = np.unwrap(np.angle(H))<EOL>theta2 = np.unwrap(<NUM_LIT:2>*theta)/<NUM_LIT><EOL>theta_dif = np.diff(theta2)<EOL>f_diff = np.diff(f)<EOL>Tg = -np.diff(theta2)/np.diff(w)<EOL>idx = np.nonzero(np.ravel(<NUM_LIT:20>*np.log10(H[:-<NUM_LIT:1>]) < -<NUM_LIT>))[<NUM_LIT:0>]<EOL>Tg[idx] = np.zeros(len(idx))<EOL>max_Tg = np.max(Tg)<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>max_Tg /= fs<EOL>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg/fs)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>else:<EOL><INDENT>plt.plot(f[:-<NUM_LIT:1>]*fs,Tg)<EOL>plt.ylim([<NUM_LIT:0>,<NUM_LIT>*max_Tg])<EOL><DEDENT>if n == N_filt-<NUM_LIT:1>:<EOL><INDENT>plt.xlabel('<STR_LIT>')<EOL>if mode.lower() == '<STR_LIT>':<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>plt.ylabel('<STR_LIT>')<EOL><DEDENT>plt.title('<STR_LIT>')<EOL><DEDENT><DEDENT>else:<EOL><INDENT>s1 = '<STR_LIT>'<EOL>s2 = '<STR_LIT>'<EOL>print(s1 + s2)<EOL><DEDENT><DEDENT>

A method for displaying digital filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freq_resp(self,mode = 'dB',Npts = 1024)

A method for displaying the filter frequency response magnitude,
phase, and group delay. A plot is produced using matplotlib

freqz_resp(b,a=[1],mode = 'dB',Npts = 1024,fsize=(6,4))

    b = ndarray of numerator coefficients
    a = ndarray of denominator coefficents
 mode = display mode: 'dB' magnitude, 'phase' in radians, or
        'groupdelay_s' in samples and 'groupdelay_t' in sec,
        all versus frequency in Hz
 Npts = number of points to plot; default is 1024
fsize = figure size; defult is (6,4) inches

Mark Wickert, January 2015


if K == <NUM_LIT:1>:<EOL><INDENT>b = np.ones(M)<EOL><DEDENT>else:<EOL><INDENT>h = np.ones(M)<EOL>b = h<EOL>for i in range(<NUM_LIT:1>, K):<EOL><INDENT>b = signal.convolve(b, h)  <EOL><DEDENT><DEDENT>return b / np.sum(b)<EOL>

A functional form implementation of a cascade of integrator comb (CIC) filters.

Parameters
----------
M : Effective number of taps per section (typically the decimation factor).
K : The number of CIC sections cascaded (larger K gives the filter a wider image rejection bandwidth).

Returns
-------
b : FIR filter coefficients for a simple direct form implementation using the filter() function.

Notes
-----
Commonly used in multirate signal processing digital down-converters and digital up-converters. A true CIC filter
requires no multiplies, only add and subtract operations. The functional form created here is a simple FIR requiring
real coefficient multiplies via filter().

Mark Wickert July 2013


fs = <NUM_LIT> <EOL>NB = len(GdB)<EOL>if not NB == <NUM_LIT:10>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>Fc = <NUM_LIT>*<NUM_LIT:2>**np.arange(NB)<EOL>B = np.zeros((NB,<NUM_LIT:3>))<EOL>A = np.zeros((NB,<NUM_LIT:3>))<EOL>for k in range(NB):<EOL><INDENT>[b,a] = peaking(GdB[k],Fc[k],Q)<EOL>B[k,:] = b<EOL>A[k,:] = a<EOL><DEDENT>y = np.zeros(len(x))<EOL>for k in range(NB):<EOL><INDENT>if k == <NUM_LIT:0>:<EOL><INDENT>y = signal.lfilter(B[k,:],A[k,:],x)<EOL><DEDENT>else:<EOL><INDENT>y = signal.lfilter(B[k,:],A[k,:],y)<EOL><DEDENT><DEDENT>return y<EOL>

Filter the input signal x with a ten-band equalizer having octave gain values in ndarray GdB.

The signal x is filtered using octave-spaced peaking filters starting at 31.25 Hz and
stopping at 16 kHz. The Q of each filter is 3.5, but can be changed. The sampling rate
is assumed to be 44.1 kHz.

Parameters
----------
x : ndarray of the input signal samples
GdB : ndarray containing ten octave band gain values [G0dB,...,G9dB]
Q : Quality factor vector for each of the NB peaking filters

Returns
-------
y : ndarray of output signal samples

Examples
--------
>>> # Test with white noise
>>> w = randn(100000)
>>> y = ten_band_eq_filt(x,GdB)
>>> psd(y,2**10,44.1)


fs = <NUM_LIT> <EOL>NB = len(GdB)<EOL>if not NB == <NUM_LIT:10>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>Fc = <NUM_LIT>*<NUM_LIT:2>**np.arange(NB)<EOL>B = np.zeros((NB,<NUM_LIT:3>));<EOL>A = np.zeros((NB,<NUM_LIT:3>));<EOL>for k in range(NB):<EOL><INDENT>b,a = peaking(GdB[k],Fc[k],Q,fs)<EOL>B[k,:] = b<EOL>A[k,:] = a<EOL><DEDENT>F = np.logspace(<NUM_LIT:1>,np.log10(<NUM_LIT>),<NUM_LIT:1000>)<EOL>H = np.ones(len(F))*np.complex(<NUM_LIT:1.0>,<NUM_LIT:0.0>)<EOL>for k in range(NB):<EOL><INDENT>w,Htemp = signal.freqz(B[k,:],A[k,:],<NUM_LIT:2>*np.pi*F/fs)<EOL>H *= Htemp<EOL><DEDENT>plt.figure(figsize=(<NUM_LIT:6>,<NUM_LIT:4>))<EOL>plt.subplot(<NUM_LIT>)<EOL>plt.semilogx(F,<NUM_LIT:20>*np.log10(abs(H)))<EOL>plt.axis([<NUM_LIT:10>, fs/<NUM_LIT:2>, -<NUM_LIT:12>, <NUM_LIT:12>])<EOL>plt.grid()<EOL>plt.title('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.subplot(<NUM_LIT>)<EOL>plt.stem(np.arange(NB),GdB,'<STR_LIT:b>','<STR_LIT>')<EOL>plt.axis([<NUM_LIT:0>, NB-<NUM_LIT:1>, -<NUM_LIT:12>, <NUM_LIT:12>])<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.grid()<EOL>

Create a frequency response magnitude plot in dB of a ten band equalizer
using a semilogplot (semilogx()) type plot

Parameters
----------
GdB : Gain vector for 10 peaking filters [G0,...,G9]
Q : Quality factor for each peaking filter (default 3.5)

Returns
-------
Nothing : two plots are created

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import sigsys as ss
>>> ss.ten_band_eq_resp([0,10.0,0,0,-1,0,5,0,-4,0])
>>> plt.show()


def peaking(GdB, fc, Q=<NUM_LIT>, fs=<NUM_LIT>):

mu = <NUM_LIT:10>**(GdB/<NUM_LIT>)<EOL>kq = <NUM_LIT:4>/(<NUM_LIT:1> + mu)*np.tan(<NUM_LIT:2>*np.pi*fc/fs/(<NUM_LIT:2>*Q))<EOL>Cpk = (<NUM_LIT:1> + kq *mu)/(<NUM_LIT:1> + kq)<EOL>b1 = -<NUM_LIT:2>*np.cos(<NUM_LIT:2>*np.pi*fc/fs)/(<NUM_LIT:1> + kq*mu)<EOL>b2 = (<NUM_LIT:1> - kq*mu)/(<NUM_LIT:1> + kq*mu)<EOL>a1 = -<NUM_LIT:2>*np.cos(<NUM_LIT:2>*np.pi*fc/fs)/(<NUM_LIT:1> + kq)<EOL>a2 = (<NUM_LIT:1> - kq)/(<NUM_LIT:1> + kq)<EOL>b = Cpk*np.array([<NUM_LIT:1>, b1, b2])<EOL>a = np.array([<NUM_LIT:1>, a1, a2])<EOL>return b,a<EOL>

A second-order peaking filter having GdB gain at fc and approximately
and 0 dB otherwise.

The filter coefficients returns correspond to a biquadratic system function
containing five parameters.

Parameters
----------
GdB : Lowpass gain in dB
fc : Center frequency in Hz
Q : Filter Q which is inversely proportional to bandwidth
fs : Sampling frquency in Hz

Returns
-------
b : ndarray containing the numerator filter coefficients
a : ndarray containing the denominator filter coefficients

Examples
--------
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> from sk_dsp_comm.sigsys import peaking
>>> from scipy import signal
>>> b,a = peaking(2.0,500)
>>> f = np.logspace(1,5,400)
>>> w,H = signal.freqz(b,a,2*np.pi*f/44100)
>>> plt.semilogx(f,20*np.log10(abs(H)))
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.xlabel("Frequency (Hz)")
>>> plt.show()

>>> b,a = peaking(-5.0,500,4)
>>> w,H = signal.freqz(b,a,2*np.pi*f/44100)
>>> plt.semilogx(f,20*np.log10(abs(H)))
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.xlabel("Frequency (Hz)")


x = np.zeros(len(n))<EOL>for k, nn in enumerate(n):<EOL><INDENT>if nn >= -<NUM_LIT:2> and nn <= <NUM_LIT:5>:<EOL><INDENT>x[k] = <NUM_LIT:8> - nn<EOL><DEDENT><DEDENT>return x<EOL>

Generate a triangle pulse as described in Example 6-2
of Chapter 6.

You need to supply an index array n that covers at least [-2, 5].
The function returns the hard-coded signal of the example.

Parameters
----------
n : time index ndarray covering at least -2 to +5.

Returns
-------
x : ndarray of signal samples in x

Examples
--------
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import sigsys as ss
>>> n = np.arange(-5,8)
>>> x = ss.ex6_2(n)
>>> plt.stem(n,x) # creates a stem plot of x vs n


def position_CD(Ka,out_type = '<STR_LIT>'):

rs = <NUM_LIT:10>/(<NUM_LIT:2>*np.pi)<EOL>if out_type.lower() == '<STR_LIT>':<EOL><INDENT>b = np.array([Ka*<NUM_LIT>*rs])<EOL>a = np.array([<NUM_LIT:1>,<NUM_LIT>,<NUM_LIT>,<NUM_LIT:0>])<EOL><DEDENT>elif out_type.lower() == '<STR_LIT>':<EOL><INDENT>b = np.array([<NUM_LIT>*Ka*rs])<EOL>a = np.array([<NUM_LIT:1>, <NUM_LIT>, <NUM_LIT>*Ka*rs])<EOL><DEDENT>elif out_type.lower() == '<STR_LIT>':<EOL><INDENT>b = np.array([<NUM_LIT>*Ka*rs])<EOL>a = np.array([<NUM_LIT:1>, <NUM_LIT>+<NUM_LIT>, <NUM_LIT>*<NUM_LIT>, <NUM_LIT>*Ka*rs])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>return b, a<EOL>

CD sled position control case study of Chapter 18.

The function returns the closed-loop and open-loop
system function for a CD/DVD sled position control
system. The loop amplifier gain is the only variable
that may be changed. The returned system function can
however be changed.

Parameters
----------
Ka : loop amplifier gain, start with 50.
out_type : 'open_loop' for open loop system function
out_type : 'fb_approx' for closed-loop approximation
out_type : 'fb_exact' for closed-loop exact

Returns
-------
b : numerator coefficient ndarray
a : denominator coefficient ndarray

Notes
-----
With the exception of the loop amplifier gain, all
other parameters are hard-coded from Case Study example.

Examples
--------
>>> b,a = position_CD(Ka,'fb_approx')
>>> b,a = position_CD(Ka,'fb_exact')


tau = T/<NUM_LIT>*vmax/vcruise<EOL>g = <NUM_LIT><EOL>g *= <NUM_LIT:3>*<NUM_LIT>**<NUM_LIT:2>/<NUM_LIT> <EOL>Kp = T*(<NUM_LIT:2>*zeta*wn-<NUM_LIT:1>/tau)/vmax<EOL>Ki = T*wn**<NUM_LIT>/vmax<EOL>K = Kp*vmax/T<EOL>print('<STR_LIT>', np.sqrt(K/(Kp/Ki)))<EOL>print('<STR_LIT>', (K + <NUM_LIT:1>/tau)/(<NUM_LIT:2>*wn))<EOL>a = np.array([<NUM_LIT:1>, <NUM_LIT:2>*zeta*wn, wn**<NUM_LIT:2>])<EOL>if tf_mode == '<STR_LIT:H>':<EOL><INDENT>b = np.array([K, wn**<NUM_LIT:2>])      <EOL><DEDENT>elif tf_mode == '<STR_LIT>':<EOL><INDENT>b = np.array([<NUM_LIT:1>, <NUM_LIT:2>*zeta*wn-K, <NUM_LIT:0.>])   <EOL><DEDENT>elif tf_mode == '<STR_LIT>':<EOL><INDENT>b = np.array([ <NUM_LIT:1>, wn**<NUM_LIT:2>/K+<NUM_LIT:1>/tau, wn**<NUM_LIT:2>/(K*tau)])<EOL>b *= Kp<EOL><DEDENT>elif tf_mode == '<STR_LIT>':<EOL><INDENT>b = np.array([g, <NUM_LIT:0>])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>return b, a<EOL>

Cruise control with PI controller and hill disturbance.

This function returns various system function configurations
for a the cruise control Case Study example found in
the supplementary article. The plant model is obtained by the
linearizing the equations of motion and the controller contains a
proportional and integral gain term set via the closed-loop parameters
natuarl frequency wn (rad/s) and damping zeta.

Parameters
----------
wn : closed-loop natural frequency in rad/s, nominally 0.1
zeta : closed-loop damping factor, nominally 1.0
T : vehicle time constant, nominally 10 s
vcruise : cruise velocity set point, nominally 75 mph
vmax : maximum vehicle velocity, nominally 120 mph
tf_mode : 'H', 'HE', 'HVW', or 'HED' controls the system function returned by the function
'H'   : closed-loop system function V(s)/R(s)
'HE'  : closed-loop system function E(s)/R(s)
'HVW' : closed-loop system function V(s)/W(s)
'HED' : closed-loop system function E(s)/D(s), where D is the hill disturbance input

Returns
-------
b : numerator coefficient ndarray
a : denominator coefficient ndarray

Examples
--------
>>> # return the closed-loop system function output/input velocity
>>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='H')
>>> # return the closed-loop system function loop error/hill disturbance
>>> b,a = cruise_control(wn,zeta,T,vcruise,vmax,tf_mode='HED')


M = len(b) - <NUM_LIT:1><EOL>N = len(a) - <NUM_LIT:1><EOL>plt.figure(figsize=(<NUM_LIT:5>,<NUM_LIT:5>))<EOL>N_roots = np.array([<NUM_LIT:0.0>])<EOL>if M > <NUM_LIT:0>:<EOL><INDENT>N_roots = np.roots(b)<EOL><DEDENT>D_roots = np.array([<NUM_LIT:0.0>])<EOL>if N > <NUM_LIT:0>:<EOL><INDENT>D_roots = np.roots(a)<EOL><DEDENT>if auto_scale:<EOL><INDENT>size[<NUM_LIT:0>] = min(np.min(np.real(N_roots)),np.min(np.real(D_roots)))-<NUM_LIT:0.5><EOL>size[<NUM_LIT:1>] = max(np.max(np.real(N_roots)),np.max(np.real(D_roots)))+<NUM_LIT:0.5><EOL>size[<NUM_LIT:1>] = max(size[<NUM_LIT:1>],<NUM_LIT:0.5>)<EOL>size[<NUM_LIT:2>] = min(np.min(np.imag(N_roots)),np.min(np.imag(D_roots)))-<NUM_LIT:0.5><EOL>size[<NUM_LIT:3>] = max(np.max(np.imag(N_roots)),np.max(np.imag(D_roots)))+<NUM_LIT:0.5><EOL><DEDENT>plt.plot([size[<NUM_LIT:0>],size[<NUM_LIT:1>]],[<NUM_LIT:0>,<NUM_LIT:0>],'<STR_LIT>')<EOL>plt.plot([<NUM_LIT:0>,<NUM_LIT:0>],[size[<NUM_LIT:2>],size[<NUM_LIT:3>]],'<STR_LIT>')<EOL>x_scale = size[<NUM_LIT:1>]-size[<NUM_LIT:0>]<EOL>y_scale = size[<NUM_LIT:3>]-size[<NUM_LIT:2>]<EOL>x_off = <NUM_LIT><EOL>y_off = <NUM_LIT><EOL>if M > <NUM_LIT:0>:<EOL><INDENT>N_uniq, N_mult=signal.unique_roots(N_roots,tol=<NUM_LIT>, rtype='<STR_LIT>')<EOL>plt.plot(np.real(N_uniq),np.imag(N_uniq),'<STR_LIT>',mfc='<STR_LIT:None>',ms=<NUM_LIT:8>)<EOL>idx_N_mult = np.nonzero(np.ravel(N_mult><NUM_LIT:1>))[<NUM_LIT:0>]<EOL>for k in range(len(idx_N_mult)):<EOL><INDENT>x_loc = np.real(N_uniq[idx_N_mult[k]]) + x_off*x_scale<EOL>y_loc =np.imag(N_uniq[idx_N_mult[k]]) + y_off*y_scale<EOL>plt.text(x_loc,y_loc,str(N_mult[idx_N_mult[k]]),ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)<EOL><DEDENT><DEDENT>if N > <NUM_LIT:0>:<EOL><INDENT>D_uniq, D_mult=signal.unique_roots(D_roots,tol=<NUM_LIT>, rtype='<STR_LIT>')<EOL>plt.plot(np.real(D_uniq),np.imag(D_uniq),'<STR_LIT>',ms=<NUM_LIT:8>)<EOL>idx_D_mult = np.nonzero(np.ravel(D_mult><NUM_LIT:1>))[<NUM_LIT:0>]<EOL>for k in range(len(idx_D_mult)):<EOL><INDENT>x_loc = np.real(D_uniq[idx_D_mult[k]]) + x_off*x_scale<EOL>y_loc =np.imag(D_uniq[idx_D_mult[k]]) + y_off*y_scale<EOL>plt.text(x_loc,y_loc,str(D_mult[idx_D_mult[k]]),ha='<STR_LIT>',va='<STR_LIT>',fontsize=<NUM_LIT:10>)<EOL><DEDENT><DEDENT>plt.xlabel('<STR_LIT>')<EOL>plt.ylabel('<STR_LIT>')<EOL>plt.title('<STR_LIT>')<EOL>plt.axis(np.array(size))<EOL>return M,N<EOL>

Create an s-plane pole-zero plot.

As input the function uses the numerator and denominator
s-domain system function coefficient ndarrays b and a respectively.
Assumed to be stored in descending powers of s.

Parameters
----------
b : numerator coefficient ndarray.
a : denominator coefficient ndarray.
auto_scale : True
size : [xmin,xmax,ymin,ymax] plot scaling when scale = False

Returns
-------
(M,N) : tuple of zero and pole counts + plot window

Notes
-----
This function tries to identify repeated poles and zeros and will
place the multiplicity number above and to the right of the pole or zero.
The difficulty is setting the tolerance for this detection. Currently it
is set at 1e-3 via the function signal.unique_roots.

Examples
--------
>>> # Here the plot is generated using auto_scale
>>> splane(b,a)
>>> # Here the plot is generated using manual scaling
>>> splane(b,a,False,[-10,1,-10,10])


P = len(h)<EOL>x = np.hstack((np.zeros(P-<NUM_LIT:1>),x))<EOL>L = N - P + <NUM_LIT:1><EOL>Nx = len(x)<EOL>Nframe = int(np.ceil(Nx/float(L)))<EOL>x = np.hstack((x,np.zeros(Nframe*L-Nx)))<EOL>y = np.zeros(int(Nframe*N))<EOL>y_mat = np.zeros((Nframe,int(Nframe*N)))<EOL>H = fft.fft(h,N)<EOL>for k in range(Nframe):<EOL><INDENT>xk = x[k*L:k*L+N]<EOL>Xk = fft.fft(xk,N)<EOL>Yk = H*Xk<EOL>yk = np.real(fft.ifft(Yk)) <EOL>y[k*L+P-<NUM_LIT:1>:k*L+N] = yk[P-<NUM_LIT:1>:]<EOL>y_mat[k,k*L:k*L+N] = yk<EOL><DEDENT>if mode == <NUM_LIT:1>:<EOL><INDENT>return y[P-<NUM_LIT:1>:Nx], y_mat[:,P-<NUM_LIT:1>:Nx]<EOL><DEDENT>else:<EOL><INDENT>return y[P-<NUM_LIT:1>:Nx]<EOL><DEDENT>

Overlap and save transform domain FIR filtering.

This function implements the classical overlap and save method of
transform domain filtering using a length P FIR filter.

Parameters
----------
x : input signal to be filtered as an ndarray
h : FIR filter coefficients as an ndarray of length P
N : FFT size > P, typically a power of two
mode : 0 or 1, when 1 returns a diagnostic matrix

Returns
-------
y : the filtered output as an ndarray
y_mat : an ndarray whose rows are the individual overlap outputs.

Notes
-----
y_mat is used for diagnostics and to gain understanding of the algorithm.

Examples
--------
>>> n = arange(0,100)
>>> x = cos(2*pi*0.05*n)
>>> b = ones(10)
>>> y = OS_filter(x,h,N)
>>> # set mode = 1
>>> y, y_mat = OS_filter(x,h,N,1)


P = len(h)<EOL>L = int(N) - P + <NUM_LIT:1> <EOL>Nx = len(x)<EOL>Nframe = int(np.ceil(Nx/float(L)))<EOL>x = np.hstack((x,np.zeros(Nframe*L-Nx)))<EOL>y = np.zeros(Nframe*N)<EOL>y_mat = np.zeros((Nframe,Nframe*N))<EOL>H = fft.fft(h,N)<EOL>for k in range(Nframe):<EOL><INDENT>xk = x[k*L:(k+<NUM_LIT:1>)*L]<EOL>Xk = fft.fft(xk,N)<EOL>Yk = H*Xk<EOL>yk = np.real(fft.ifft(Yk))<EOL>y[k*L:k*L+N] += yk<EOL>y_mat[k,k*L:k*L+N] = yk<EOL><DEDENT>if mode == <NUM_LIT:1>:<EOL><INDENT>return y[<NUM_LIT:0>:Nx], y_mat[:,<NUM_LIT:0>:Nx]<EOL><DEDENT>else:<EOL><INDENT>return y[<NUM_LIT:0>:Nx]<EOL><DEDENT>

Overlap and add transform domain FIR filtering.

This function implements the classical overlap and add method of
transform domain filtering using a length P FIR filter.

Parameters
----------
x : input signal to be filtered as an ndarray
h : FIR filter coefficients as an ndarray of length P
N : FFT size > P, typically a power of two
mode : 0 or 1, when 1 returns a diagnostic matrix

Returns
-------
y : the filtered output as an ndarray
y_mat : an ndarray whose rows are the individual overlap outputs.

Notes
-----
y_mat is used for diagnostics and to gain understanding of the algorithm.

Examples
--------
>>> import numpy as np
>>> from sk_dsp_comm.sigsys import OA_filter
>>> n = np.arange(0,100)
>>> x = np.cos(2*pi*0.05*n)
>>> b = np.ones(10)
>>> y = OA_filter(x,h,N)
>>> # set mode = 1
>>> y, y_mat = OA_filter(x,h,N,1)


plt.figure(figsize=fsize)<EOL>f = np.arange(-fmax,fmax+fmax/<NUM_LIT>,fmax/<NUM_LIT>)<EOL>A = <NUM_LIT:1.0><EOL>line_ampl = A/<NUM_LIT>*np.array([<NUM_LIT:0>, <NUM_LIT:1>])<EOL>shapes = ['<STR_LIT>', '<STR_LIT>']<EOL>if shape.lower() not in shapes:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if shape.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot(f,lp_tri(f,fb))<EOL>for n in range(N):<EOL><INDENT>plt.plot(f, lp_tri(f - (n + <NUM_LIT:1>) * fs, fb), '<STR_LIT>')<EOL>plt.plot(f, lp_tri(f + (n + <NUM_LIT:1>) * fs, fb), '<STR_LIT>')<EOL><DEDENT><DEDENT>elif shape.lower() == '<STR_LIT>':<EOL><INDENT>plt.plot([fb, fb],line_ampl,'<STR_LIT:b>', linewidth=<NUM_LIT:2>)<EOL>plt.plot([-fb, -fb],line_ampl,'<STR_LIT:b>', linewidth=<NUM_LIT:2>)<EOL>for n in range(N):<EOL><INDENT>plt.plot([fb+(n+<NUM_LIT:1>)*fs, fb+(n+<NUM_LIT:1>)*fs],line_ampl,'<STR_LIT>', linewidth=<NUM_LIT:2>)<EOL>plt.plot([-fb+(n+<NUM_LIT:1>)*fs, -fb+(n+<NUM_LIT:1>)*fs],line_ampl,'<STR_LIT>', linewidth=<NUM_LIT:2>)<EOL>plt.plot([fb-(n+<NUM_LIT:1>)*fs, fb-(n+<NUM_LIT:1>)*fs],line_ampl,'<STR_LIT>', linewidth=<NUM_LIT:2>)<EOL>plt.plot([-fb-(n+<NUM_LIT:1>)*fs, -fb-(n+<NUM_LIT:1>)*fs],line_ampl,'<STR_LIT>', linewidth=<NUM_LIT:2>)<EOL><DEDENT><DEDENT>plt.ylabel('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')<EOL>plt.axis([-fmax,fmax,<NUM_LIT:0>,<NUM_LIT:1>])<EOL>plt.grid()<EOL>

Lowpass sampling theorem plotting function.

Display the spectrum of a sampled signal after setting the bandwidth,
sampling frequency, maximum display frequency, and spectral shape.

Parameters
----------
fb : spectrum lowpass bandwidth in Hz
fs : sampling frequency in Hz
fmax : plot over [-fmax,fmax]
shape : 'tri' or 'line'
N : number of translates, N positive and N negative
fsize : the size of the figure window, default (6,4)

Returns
-------
Nothing : A plot window opens containing the spectrum plot

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm.sigsys import lp_samp

No aliasing as bandwidth 10 Hz < 25/2; fs > fb.

>>> lp_samp(10,25,50,10)
>>> plt.show()

Now aliasing as bandwidth 15 Hz > 25/2; fs < fb.

>>> lp_samp(15,25,50,10)


x = np.zeros(len(f))<EOL>for k in range(len(f)):<EOL><INDENT>if abs(f[k]) <= fb:<EOL><INDENT>x[k] = <NUM_LIT:1> - abs(f[k])/float(fb)<EOL><DEDENT><DEDENT>return x<EOL>

Triangle spectral shape function used by :func:`lp_samp`.

Parameters
----------
f : ndarray containing frequency samples
fb : the bandwidth as a float constant

Returns
-------
x : ndarray of spectrum samples for a single triangle shape

Notes
-----
This is a support function for the lowpass spectrum plotting function
:func:`lp_samp`.

Examples
--------
>>> x = lp_tri(f, fb)


<EOL>x_pwr = np.var(x)<EOL>noise = np.sqrt(x_pwr/<NUM_LIT:10>**(SNRdB/<NUM_LIT>))*np.random.randn(len(x));<EOL>return x + noise<EOL>

Add white Gaussian noise to a single real sinusoid.

Input a single sinusoid to this function and it returns a noisy
sinusoid at a specific SNR value in dB. Sinusoid power is calculated
using np.var.

Parameters
----------
x : Input signal as ndarray consisting of a single sinusoid
SNRdB : SNR in dB for output sinusoid

Returns
-------
y : Noisy sinusoid return vector

Examples
--------
>>> # set the SNR to 10 dB
>>> n = arange(0,10000)
>>> x = cos(2*pi*0.04*n)
>>> y = sinusoidAWGN(x,10.0)


B = Btot-<NUM_LIT:1><EOL>x = x/Xmax<EOL>if Limit.lower() == '<STR_LIT>':<EOL><INDENT>xq = (np.mod(np.round(x*<NUM_LIT:2>**B)+<NUM_LIT:2>**B,<NUM_LIT:2>**Btot)-<NUM_LIT:2>**B)/<NUM_LIT:2>**B<EOL><DEDENT>elif Limit.lower() == '<STR_LIT>':<EOL><INDENT>xq = np.round(x*<NUM_LIT:2>**B)+<NUM_LIT:2>**B<EOL>s1 = np.nonzero(np.ravel(xq >= <NUM_LIT:2>**Btot-<NUM_LIT:1>))[<NUM_LIT:0>]<EOL>s2 = np.nonzero(np.ravel(xq < <NUM_LIT:0>))[<NUM_LIT:0>]<EOL>xq[s1] = (<NUM_LIT:2>**Btot - <NUM_LIT:1>)*np.ones(len(s1))<EOL>xq[s2] = np.zeros(len(s2))<EOL>xq = (xq - <NUM_LIT:2>**B)/<NUM_LIT:2>**B<EOL><DEDENT>elif Limit.lower() == '<STR_LIT:none>':<EOL><INDENT>xq = np.round(x*<NUM_LIT:2>**B)/<NUM_LIT:2>**B<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>return xq*Xmax<EOL>

A simple rounding quantizer for bipolar signals having Btot = B + 1 bits.

This function models a quantizer that employs Btot bits that has one of
three selectable limiting types: saturation, overflow, and none.
The quantizer is bipolar and implements rounding.

Parameters
----------
x : input signal ndarray to be quantized
Btot : total number of bits in the quantizer, e.g. 16
Xmax : quantizer full-scale dynamic range is [-Xmax, Xmax]
Limit = Limiting of the form 'sat', 'over', 'none'

Returns
-------
xq : quantized output ndarray

Notes
-----
The quantization can be formed as e = xq - x

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from matplotlib.mlab import psd
>>> import numpy as np
>>> from sk_dsp_comm import sigsys as ss
>>> n = np.arange(0,10000)
>>> x = np.cos(2*np.pi*0.211*n)
>>> y = ss.sinusoidAWGN(x,90)
>>> Px, f = psd(y,2**10,Fs=1)
>>> plt.plot(f, 10*np.log10(Px))
>>> plt.ylim([-80, 25])
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.xlabel(r'Normalized Frequency $\omega/2\pi$')
>>> plt.show()

>>> yq = ss.simpleQuant(y,12,1,'sat')
>>> Px, f = psd(yq,2**10,Fs=1)
>>> plt.plot(f, 10*np.log10(Px))
>>> plt.ylim([-80, 25])
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.xlabel(r'Normalized Frequency $\omega/2\pi$')
>>> plt.show()


return abs(np.rint(f_in/fs)*fs - f_in)<EOL>"""<STR_LIT>"""<EOL>

Calculate the principle alias frequencies.

Given an array of input frequencies the function returns an
array of principle alias frequencies.

Parameters
----------
f_in : ndarray of input frequencies
fs : sampling frequency

Returns
-------
f_out : ndarray of principle alias frequencies

Examples
--------
>>> # Linear frequency sweep from 0 to 50 Hz
>>> f_in = arange(0,50,0.1)
>>> # Calculate principle alias with fs = 10 Hz
>>> f_out = prin_alias(f_in,10)


return signal.convolve(b1,b2), signal.convolve(a1,a2)<EOL>

Cascade two IIR digital filters into a single (b,a) coefficient set.

To cascade two digital filters (system functions) given their numerator
and denominator coefficients you simply convolve the coefficient arrays.

Parameters
----------
b1 : ndarray of numerator coefficients for filter 1
a1 : ndarray of denominator coefficients for filter 1
b2 : ndarray of numerator coefficients for filter 2
a2 : ndarray of denominator coefficients for filter 2

Returns
-------
b : ndarray of numerator coefficients for the cascade
a : ndarray of denominator coefficients for the cascade

Examples
--------
>>> from scipy import signal
>>> b1,a1 = signal.butter(3, 0.1)
>>> b2,a2 = signal.butter(3, 0.15)
>>> b,a = cascade_filters(b1,a1,b2,a2)


def soi_snoi_gen(s,SIR_dB,N,fi,fs = <NUM_LIT>):

n = np.arange(<NUM_LIT:0>,N+<NUM_LIT:1>)<EOL>K = len(fi)<EOL>si = np.zeros(N+<NUM_LIT:1>)<EOL>for k in range(K):<EOL><INDENT>si += np.cos(<NUM_LIT:2>*np.pi*fi[k]/fs*n);<EOL><DEDENT>s = s[:N+<NUM_LIT:1>]<EOL>Ps = np.var(s)<EOL>Psi = np.var(si)<EOL>r = s + np.sqrt(Ps/Psi*<NUM_LIT:10>**(-SIR_dB/<NUM_LIT:10>))*si<EOL>return r<EOL>

Add an interfering sinusoidal tone to the input signal at a given SIR_dB.

The input is the signal of interest (SOI) and number of sinsuoid signals
not of interest (SNOI) are addedto the SOI at a prescribed signal-to-
intereference SIR level in dB.

Parameters
----------
s : ndarray of signal of SOI
SIR_dB : interference level in dB
N : Trim input signal s to length N + 1 samples
fi : ndarray of intereference frequencies in Hz
fs : sampling rate in Hz, default is 8000 Hz

Returns
-------
r : ndarray of combined signal plus intereference of length N+1 samples

Examples
--------
>>> # load a speech ndarray and trim to 5*8000 + 1 samples
>>> fs,s = from_wav('OSR_us_000_0030_8k.wav')
>>> r = soi_snoi_gen(s,10,5*8000,[1000, 1500])


N = len(r)-<NUM_LIT:1>;<EOL>y = signal.lfilter(np.hstack((np.zeros(delta), np.array([<NUM_LIT:1>]))),<NUM_LIT:1>,r)<EOL>r_hat = np.zeros(N+<NUM_LIT:1>)<EOL>e = np.zeros(N+<NUM_LIT:1>)<EOL>ao = np.zeros(M+<NUM_LIT:1>)<EOL>z = np.zeros(M)<EOL>ym = np.zeros(M+<NUM_LIT:1>)<EOL>for k in range(N+<NUM_LIT:1>):<EOL><INDENT>r_hat[k],z = signal.lfilter(ao,np.array([<NUM_LIT:1>]),np.array([y[k]]),zi=z)<EOL>e[k] = r[k] - r_hat[k]<EOL>ao = ao + <NUM_LIT:2>*mu*e[k]*ym<EOL>ym = np.hstack((np.array([y[k]]), ym[:-<NUM_LIT:1>]))<EOL><DEDENT>F, Ao = signal.freqz(ao,<NUM_LIT:1>,<NUM_LIT>)<EOL>F/= (<NUM_LIT:2>*np.pi)<EOL>Ao = <NUM_LIT:20>*np.log10(abs(Ao))<EOL>return np.arange(<NUM_LIT:0>,N+<NUM_LIT:1>), r, r_hat, e, ao, F, Ao<EOL>

Least mean square (LMS) interference canceller adaptive filter.

A complete LMS adaptive filter simulation function for the case of
interference cancellation. Used in the digital filtering case study.

Parameters
----------
M : FIR Filter length (order M-1)
delta : Delay used to generate the reference signal
mu : LMS step-size
delta : decorrelation delay between input and FIR filter input

Returns
-------
n : ndarray Index vector
r : ndarray noisy (with interference) input signal
r_hat : ndarray filtered output (NB_hat[n])
e : ndarray error sequence (WB_hat[n])
ao : ndarray final value of weight vector
F : ndarray frequency response axis vector
Ao : ndarray frequency response of filter

Examples
----------
>>> # import a speech signal
>>> fs,s = from_wav('OSR_us_000_0030_8k.wav')
>>> # add interference at 1kHz and 1.5 kHz and
>>> # truncate to 5 seconds
>>> r = soi_snoi_gen(s,10,5*8000,[1000, 1500])
>>> # simulate with a 64 tap FIR and mu = 0.005
>>> n,r,r_hat,e,ao,F,Ao = lms_ic(r,64,0.005)


w0 = <NUM_LIT:2>*np.pi*fi/float(fs)<EOL>if r >= <NUM_LIT:1>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>elif r == <NUM_LIT:0>:<EOL><INDENT>a = np.array([<NUM_LIT:1.0>])<EOL><DEDENT>else:<EOL><INDENT>a = np.array([<NUM_LIT:1>, -<NUM_LIT:2>*r*np.cos(w0), r**<NUM_LIT:2>])<EOL><DEDENT>b = np.array([<NUM_LIT:1>, -<NUM_LIT:2>*np.cos(w0), <NUM_LIT:1>])<EOL>return b, a<EOL>

Design a second-order FIR or IIR notch filter.

A second-order FIR notch filter is created by placing conjugate
zeros on the unit circle at angle corresponidng to the notch center
frequency. The IIR notch variation places a pair of conjugate poles
at the same angle, but with radius r < 1 (typically 0.9 to 0.95).

Parameters
----------
fi : notch frequency is Hz relative to fs
fs : the sampling frequency in Hz, e.g. 8000
r : pole radius for IIR version, default = 0.95

Returns
-------
b : numerator coefficient ndarray
a : denominator coefficient ndarray

Notes
-----
If the pole radius is 0 then an FIR version is created, that is
there are no poles except at z = 0.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from sk_dsp_comm import sigsys as ss

>>> b_FIR, a_FIR = ss.fir_iir_notch(1000,8000,0)
>>> ss.zplane(b_FIR, a_FIR)
>>> plt.show()

>>> b_IIR, a_IIR = ss.fir_iir_notch(1000,8000)
>>> ss.zplane(b_IIR, a_IIR)


Nx = len(x)<EOL>K = int(Nx/NS)<EOL>print('<STR_LIT>', K)<EOL>if NAVG > K:<EOL><INDENT>print('<STR_LIT>')<EOL>return <NUM_LIT:0>,<NUM_LIT:0><EOL><DEDENT>if window.lower() == '<STR_LIT>' or window.lower() == '<STR_LIT>':<EOL><INDENT>w = signal.boxcar(NS)<EOL><DEDENT>elif window.lower() == '<STR_LIT>':<EOL><INDENT>w = signal.hanning(NS)<EOL><DEDENT>xsw = np.zeros((K,NS)) + <NUM_LIT>*np.zeros((K,NS))<EOL>for k in range(NAVG):<EOL><INDENT>xsw[k,] = w*x[k*NS:(k+<NUM_LIT:1>)*NS]<EOL><DEDENT>Sx = np.zeros(NFFT)<EOL>for k in range(NAVG):<EOL><INDENT>X = fft.fft(xsw[k,],NFFT)<EOL>Sx += abs(X)**<NUM_LIT:2><EOL><DEDENT>Sx /= float(NAVG)<EOL>Sx /= float(NFFT**<NUM_LIT:2>)<EOL>NFFTby2 = int(NFFT/<NUM_LIT:2>)<EOL>if x.dtype != '<STR_LIT>':<EOL><INDENT>n = np.arange(NFFTby2)<EOL>f = fs*n/float(NFFT)<EOL>Sx = Sx[<NUM_LIT:0>:NFFTby2]<EOL><DEDENT>else:<EOL><INDENT>n = np.arange(NFFTby2)<EOL>f = fs*np.hstack((np.arange(-NFFTby2,<NUM_LIT:0>),np.arange(NFFTby2)))/float(NFFT)<EOL>Sx = np.hstack((Sx[NFFTby2:],Sx[<NUM_LIT:0>:NFFTby2]))<EOL><DEDENT>return f, Sx<EOL>

Spectral estimation using windowing and averaging.

This function implements averaged periodogram spectral estimation
estimation similar to the NumPy's psd() function, but more
specialized for the the windowing case study of Chapter 16.

Parameters
----------
x : ndarray containing the input signal
NS : The subrecord length less zero padding, e.g. NS < NFFT
NFFT : FFT length, e.g., 1024 = 2**10
fs : sampling rate in Hz
NAVG : the number of averages, e.g., 1 for deterministic signals
window : hardcoded window 'boxcar' (default) or 'hanning'

Returns
-------
f : ndarray frequency axis in Hz on [0, fs/2]
Sx : ndarray the power spectrum estimate

Notes
-----
The function also prints the maximum number of averages K possible
for the input data record.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> from sk_dsp_comm import sigsys as ss
>>> n = np.arange(0,2048)
>>> x = np.cos(2*np.pi*1000/10000*n) + 0.01*np.cos(2*np.pi*3000/10000*n)
>>> f, Sx = ss.simple_SA(x,128,512,10000)
>>> plt.plot(f, 10*np.log10(Sx))
>>> plt.ylim([-80, 0])
>>> plt.xlabel("Frequency (Hz)")
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.show()

With a hanning window.

>>> f, Sx = ss.simple_SA(x,256,1024,10000,window='hanning')
>>> plt.plot(f, 10*np.log10(Sx))
>>> plt.xlabel("Frequency (Hz)")
>>> plt.ylabel("Power Spectral Density (dB)")
>>> plt.ylim([-80, 0])


plt.figure(figsize=fsize)<EOL>idx = np.nonzero(Xk)[<NUM_LIT:0>]<EOL>Xk = Xk[idx]<EOL>fk = fk[idx]<EOL>if mode == '<STR_LIT>':<EOL><INDENT>for k in range(len(fk)):<EOL><INDENT>if fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.abs(Xk[k])],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.abs(Xk[k])],linetype, linewidth=<NUM_LIT:2>*lwidth)            <EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.abs(Xk[k])],linetype, linewidth=lwidth)<EOL>plt.plot([-fk[k], -fk[k]],[<NUM_LIT:0>, np.abs(Xk[k])],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, <NUM_LIT>*np.abs(Xk[k])],linetype, linewidth=lwidth)<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT><DEDENT>plt.grid()<EOL>if sides == <NUM_LIT:2>:<EOL><INDENT>plt.axis([-<NUM_LIT>*max(fk), <NUM_LIT>*max(fk), <NUM_LIT:0>, <NUM_LIT>*max(abs(Xk))])        <EOL><DEDENT>elif sides == <NUM_LIT:1>:<EOL><INDENT>plt.axis([<NUM_LIT:0>, <NUM_LIT>*max(fk), <NUM_LIT:0>, <NUM_LIT>*<NUM_LIT:2>*max(abs(Xk))])<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>plt.ylabel('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')<EOL><DEDENT>elif mode == '<STR_LIT>':<EOL><INDENT>Xk_dB = <NUM_LIT:20>*np.log10(np.abs(Xk))<EOL>for k in range(len(fk)):<EOL><INDENT>if fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=<NUM_LIT:2>*lwidth)            <EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=lwidth)<EOL>plt.plot([-fk[k], -fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]+<NUM_LIT>],linetype, linewidth=lwidth)<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT><DEDENT>plt.grid()<EOL>max_dB = np.ceil(max(Xk_dB/<NUM_LIT>))*<NUM_LIT:10><EOL>min_dB = max(floor_dB,np.floor(min(Xk_dB/<NUM_LIT>))*<NUM_LIT:10>)<EOL>if sides == <NUM_LIT:2>:<EOL><INDENT>plt.axis([-<NUM_LIT>*max(fk), <NUM_LIT>*max(fk), min_dB, max_dB])        <EOL><DEDENT>elif sides == <NUM_LIT:1>:<EOL><INDENT>plt.axis([<NUM_LIT:0>, <NUM_LIT>*max(fk), min_dB, max_dB])<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>plt.ylabel('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')<EOL><DEDENT>elif mode == '<STR_LIT>':<EOL><INDENT>Xk_dB = <NUM_LIT:20>*np.log10(np.abs(Xk)/max(np.abs(Xk)))<EOL>for k in range(len(fk)):<EOL><INDENT>if fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=<NUM_LIT:2>*lwidth)            <EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=lwidth)<EOL>plt.plot([-fk[k], -fk[k]],[floor_dB, Xk_dB[k]],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[floor_dB, Xk_dB[k]+<NUM_LIT>],linetype, linewidth=lwidth)<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT><DEDENT>plt.grid()<EOL>max_dB = np.ceil(max(Xk_dB/<NUM_LIT>))*<NUM_LIT:10><EOL>min_dB = max(floor_dB,np.floor(min(Xk_dB/<NUM_LIT>))*<NUM_LIT:10>)<EOL>if sides == <NUM_LIT:2>:<EOL><INDENT>plt.axis([-<NUM_LIT>*max(fk), <NUM_LIT>*max(fk), min_dB, max_dB])        <EOL><DEDENT>elif sides == <NUM_LIT:1>:<EOL><INDENT>plt.axis([<NUM_LIT:0>, <NUM_LIT>*max(fk), min_dB, max_dB])<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>plt.ylabel('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')    <EOL><DEDENT>elif mode == '<STR_LIT>':<EOL><INDENT>for k in range(len(fk)):<EOL><INDENT>if fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.angle(Xk[k])],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] == <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.angle(Xk[k])],linetype, linewidth=<NUM_LIT:2>*lwidth)<EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.angle(Xk[k])],linetype, linewidth=lwidth)<EOL>plt.plot([-fk[k], -fk[k]],[<NUM_LIT:0>, -np.angle(Xk[k])],linetype, linewidth=lwidth)<EOL><DEDENT>elif fk[k] > <NUM_LIT:0> and sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([fk[k], fk[k]],[<NUM_LIT:0>, np.angle(Xk[k])],linetype, linewidth=lwidth)<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT><DEDENT>plt.grid()<EOL>if sides == <NUM_LIT:2>:<EOL><INDENT>plt.plot([-<NUM_LIT>*max(fk), <NUM_LIT>*max(fk)], [<NUM_LIT:0>, <NUM_LIT:0>],'<STR_LIT:k>')<EOL>plt.axis([-<NUM_LIT>*max(fk), <NUM_LIT>*max(fk), -<NUM_LIT>*max(np.abs(np.angle(Xk))), <NUM_LIT>*max(np.abs(np.angle(Xk)))])<EOL><DEDENT>elif sides == <NUM_LIT:1>:<EOL><INDENT>plt.plot([<NUM_LIT:0>, <NUM_LIT>*max(fk)], [<NUM_LIT:0>, <NUM_LIT:0>],'<STR_LIT:k>')<EOL>plt.axis([<NUM_LIT:0>, <NUM_LIT>*max(fk), -<NUM_LIT>*max(np.abs(np.angle(Xk))), <NUM_LIT>*max(np.abs(np.angle(Xk)))])<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>plt.ylabel('<STR_LIT>')<EOL>plt.xlabel('<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>

Plot the Fouier series line spectral given the coefficients.

This function plots two-sided and one-sided line spectra of a periodic
signal given the complex exponential Fourier series coefficients and
the corresponding harmonic frequencies.

Parameters
----------
fk : vector of real sinusoid frequencies
Xk : magnitude and phase at each positive frequency in fk
mode : 'mag' => magnitude plot, 'magdB' => magnitude in dB plot,
mode cont : 'magdBn' => magnitude in dB normalized, 'phase' => a phase plot in radians
sides : 2; 2-sided or 1-sided
linetype : line type per Matplotlib definitions, e.g., 'b';
lwidth : 2; linewidth in points
fsize : optional figure size in inches, default = (6,4) inches

Returns
-------
Nothing : A plot window opens containing the line spectrum plot

Notes
-----
Since real signals are assumed the frequencies of fk are 0 and/or positive
numbers. The supplied Fourier coefficients correspond.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> from sk_dsp_comm.sigsys import line_spectra
>>> n = np.arange(0,25)
>>> # a pulse train with 10 Hz fundamental and 20% duty cycle
>>> fk = n*10
>>> Xk = np.sinc(n*10*.02)*np.exp(-1j*2*np.pi*n*10*.01) # 1j = sqrt(-1)

>>> line_spectra(fk,Xk,'mag')
>>> plt.show()

>>> line_spectra(fk,Xk,'phase')


Nint = len(xp)<EOL>if Nint < <NUM_LIT:2>*N+<NUM_LIT:1>:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>Xp = fft.fft(xp,Nint)/float(Nint)<EOL>if one_side:<EOL><INDENT>Xk = Xp[<NUM_LIT:0>:N+<NUM_LIT:1>]<EOL>fk = f0*np.arange(<NUM_LIT:0>,N+<NUM_LIT:1>)<EOL><DEDENT>else:<EOL><INDENT>Xk = np.hstack((Xp[-N:],Xp[<NUM_LIT:0>:N+<NUM_LIT:1>]))<EOL>fk = f0*np.arange(-N,N+<NUM_LIT:1>)        <EOL><DEDENT>return Xk, fk<EOL>

Numerically approximate the Fourier series coefficients given periodic x(t).

The input is assummed to represent one period of the waveform
x(t) that has been uniformly sampled. The number of samples supplied
to represent one period of the waveform sets the sampling rate.

Parameters
----------
xp : ndarray of one period of the waveform x(t)
N : maximum Fourier series coefficient, [0,...,N]
f0 : fundamental frequency used to form fk.

Returns
-------
Xk : ndarray of the coefficients over indices [0,1,...,N]
fk : ndarray of the harmonic frequencies [0, f0,2f0,...,Nf0]

Notes
-----
len(xp) >= 2*N+1 as len(xp) is the fft length.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> import sk_dsp_comm.sigsys as ss
>>> t = arange(0,1,1/1024.)
>>> # a 20% duty cycle pulse starting at t = 0
>>> x_rect = ss.rect(t-.1,0.2)
>>> Xk, fk = ss.fs_coeff(x_rect,25,10)
>>> # plot the spectral lines
>>> ss.line_spectra(fk,Xk,'mag')
>>> plt.show()


x_approx = np.zeros(len(t))<EOL>for k,Xkk in enumerate(Xk):<EOL><INDENT>if fk[k] == <NUM_LIT:0>:<EOL><INDENT>x_approx += Xkk.real*np.ones(len(t))<EOL><DEDENT>else:<EOL><INDENT>x_approx += <NUM_LIT:2>*np.abs(Xkk)*np.cos(<NUM_LIT:2>*np.pi*fk[k]*t+np.angle(Xkk))<EOL><DEDENT><DEDENT>return x_approx<EOL>

Synthesize periodic signal x(t) using Fourier series coefficients at harmonic frequencies

Assume the signal is real so coefficients Xk are supplied for nonnegative
indicies. The negative index coefficients are assumed to be complex
conjugates.

Parameters
----------
Xk : ndarray of complex Fourier series coefficients
fk : ndarray of harmonic frequencies in Hz
t : ndarray time axis corresponding to output signal array x_approx

Returns
-------
x_approx : ndarray of periodic waveform approximation over time span t

Examples
--------
>>> t = arange(0,2,.002)
>>> # a 20% duty cycle pulse train
>>> n = arange(0,20,1) # 0 to 19th harmonic
>>> fk = 1*n % period = 1s
>>> t, x_approx = fs_approx(Xk,fk,t)
>>> plot(t,x_approx)


fs = <NUM_LIT:1>/(t[<NUM_LIT:1>] - t[<NUM_LIT:0>])<EOL>t0 = (t[-<NUM_LIT:1>]+t[<NUM_LIT:0>])/<NUM_LIT:2> <EOL>N0 = len(t)/<NUM_LIT:2> <EOL>f = np.arange(-<NUM_LIT:1.>/<NUM_LIT:2>,<NUM_LIT:1.>/<NUM_LIT:2>,<NUM_LIT:1.>/Nfft)<EOL>w, X = signal.freqz(x,<NUM_LIT:1>,<NUM_LIT:2>*np.pi*f)<EOL>X /= fs <EOL>X *= np.exp(-<NUM_LIT>*<NUM_LIT:2>*np.pi*f*fs*t0)<EOL>X *= np.exp(<NUM_LIT>*<NUM_LIT:2>*np.pi*f*N0)<EOL>F = f*fs<EOL>return F, X<EOL>

Approximate the Fourier transform of a finite duration signal using scipy.signal.freqz()

Parameters
----------
x : input signal array
t : time array used to create x(t)
Nfft : the number of frdquency domain points used to
       approximate X(f) on the interval [fs/2,fs/2], where
       fs = 1/Dt. Dt being the time spacing in array t

Returns
-------
f : frequency axis array in Hz
X : the Fourier transform approximation (complex)

Notes
-----
The output time axis starts at the sum of the starting values in x1 and x2
and ends at the sum of the two ending values in x1 and x2. The default
extents of ('f','f') are used for signals that are active (have support)
on or within n1 and n2 respectively. A right-sided signal such as
:math:`a^n*u[n]` is semi-infinite, so it has extent 'r' and the
convolution output will be truncated to display only the valid results.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import sk_dsp_comm.sigsys as ss
>>> fs = 100 # sampling rate in Hz
>>> tau = 1
>>> t = np.arange(-5,5,1/fs)
>>> x0 = ss.rect(t-.5,tau)
>>> plt.figure(figsize=(6,5))
>>> plt.plot(t,x0)
>>> plt.grid()
>>> plt.ylim([-0.1,1.1])
>>> plt.xlim([-2,2])
>>> plt.title(r'Exact Waveform')
>>> plt.xlabel(r'Time (s)')
>>> plt.ylabel(r'$x_0(t)$')
>>> plt.show()

>>> # FT Exact Plot
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import sk_dsp_comm.sigsys as ss
>>> fs = 100 # sampling rate in Hz
>>> tau = 1
>>> t = np.arange(-5,5,1/fs)
>>> x0 = ss.rect(t-.5,tau)
>>> fe = np.arange(-10,10,.01)
>>> X0e = tau*np.sinc(fe*tau)
>>> plt.plot(fe,abs(X0e))
>>> #plot(f,angle(X0))
>>> plt.grid()
>>> plt.xlim([-10,10])
>>> plt.title(r'Exact (Theory) Spectrum Magnitude')
>>> plt.xlabel(r'Frequency (Hz)')
>>> plt.ylabel(r'$|X_0e(f)|$')
>>> plt.show()

>>> # FT Approximation Plot
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import sk_dsp_comm.sigsys as ss
>>> fs = 100 # sampling rate in Hz
>>> tau = 1
>>> t = np.arange(-5,5,1/fs)
>>> x0 = ss.rect(t-.5,tau)
>>> f,X0 = ss.ft_approx(x0,t,4096)
>>> plt.plot(f,abs(X0))
>>> #plt.plot(f,angle(X0))
>>> plt.grid()
>>> plt.xlim([-10,10])
>>> plt.title(r'Approximation Spectrum Magnitude')
>>> plt.xlabel(r'Frequency (Hz)')
>>> plt.ylabel(r'$|X_0(f)|$');
>>> plt.tight_layout()
>>> plt.show()


nnx1 = np.arange(<NUM_LIT:0>,len(nx1))<EOL>nnx2 = np.arange(<NUM_LIT:0>,len(nx2))<EOL>n1 = nnx1[<NUM_LIT:0>]<EOL>n2 = nnx1[-<NUM_LIT:1>]<EOL>n3 = nnx2[<NUM_LIT:0>]<EOL>n4 = nnx2[-<NUM_LIT:1>]<EOL>if extent[<NUM_LIT:0>] == '<STR_LIT:f>' and extent[<NUM_LIT:1>] == '<STR_LIT:f>':<EOL><INDENT>nny = np.arange(n1+n3,n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ny = np.arange(<NUM_LIT:0>,len(x1)+len(x2)-<NUM_LIT:1>) + nx1[<NUM_LIT:0>]+nx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:f>' and extent[<NUM_LIT:1>] == '<STR_LIT:r>':<EOL><INDENT>nny = np.arange(n1+n3,n1+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ny = nny + nx1[<NUM_LIT:0>]+nx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:r>' and extent[<NUM_LIT:1>] == '<STR_LIT:f>':<EOL><INDENT>nny = np.arange(n1+n3,n2+<NUM_LIT:1>+n3+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ny = nny + nx1[<NUM_LIT:0>]+nx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:f>' and extent[<NUM_LIT:1>] == '<STR_LIT:l>':<EOL><INDENT>nny = np.arange(n2+n3,n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ny = nny + nx1[-<NUM_LIT:1>]+nx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:l>' and extent[<NUM_LIT:1>] == '<STR_LIT:f>':<EOL><INDENT>nny = np.arange(n1+n4,n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ny = nny + nx1[<NUM_LIT:0>]+nx2[-<NUM_LIT:1>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:r>' and extent[<NUM_LIT:1>] == '<STR_LIT:r>':<EOL><INDENT>nny = np.arange(n1+n3,min(n1+<NUM_LIT:1>+n4+<NUM_LIT:1>,n2+<NUM_LIT:1>+n3+<NUM_LIT:1>)-<NUM_LIT:1>)<EOL>ny = nny + nx1[<NUM_LIT:0>]+nx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:l>' and extent[<NUM_LIT:1>] == '<STR_LIT:l>':<EOL><INDENT>nny = np.arange(max(n1+n4,n2+n3),n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ny = nny + max(nx1[<NUM_LIT:0>]+nx2[-<NUM_LIT:1>],nx1[-<NUM_LIT:1>]+nx2[<NUM_LIT:0>])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>y = signal.convolve(x1, x2)<EOL>print('<STR_LIT>' % (ny[<NUM_LIT:0>],ny[-<NUM_LIT:1>]))<EOL>return y[nny], ny<EOL>

Discrete convolution of x1 and x2 with proper tracking of the output time axis.

Convolve two discrete-time signals using the SciPy function :func:`scipy.signal.convolution`.
The time (sequence axis) are managed from input to output. y[n] = x1[n]*x2[n].

Parameters
----------
x1 : ndarray of signal x1 corresponding to nx1
nx1 : ndarray time axis for x1
x2  : ndarray of signal x2 corresponding to nx2
nx2 : ndarray time axis for x2
extent : ('e1','e2') where 'e1', 'e2' may be 'f' finite, 'r' right-sided, or 'l' left-sided

Returns
-------
y : ndarray of output values y
ny : ndarray of the corresponding sequence index n

Notes
-----
The output time axis starts at the sum of the starting values in x1 and x2
and ends at the sum of the two ending values in x1 and x2. The default
extents of ('f','f') are used for signals that are active (have support)
on or within n1 and n2 respectively. A right-sided signal such as
a^n*u[n] is semi-infinite, so it has extent 'r' and the
convolution output will be truncated to display only the valid results.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import sk_dsp_comm.sigsys as ss
>>> nx = np.arange(-5,10)
>>> x = ss.drect(nx,4)
>>> y,ny = ss.conv_sum(x,nx,x,nx)
>>> plt.stem(ny,y)
>>> plt.show()

Consider a pulse convolved with an exponential. ('r' type extent)

>>> h = 0.5**nx*ss.dstep(nx)
>>> y,ny = ss.conv_sum(x,nx,h,nx,('f','r')) # note extents set
>>> plt.stem(ny,y) # expect a pulse charge and discharge sequence


dt = tx1[<NUM_LIT:1>] - tx1[<NUM_LIT:0>]<EOL>nx1 = np.arange(<NUM_LIT:0>,len(tx1))<EOL>nx2 = np.arange(<NUM_LIT:0>,len(tx2))<EOL>n1 = nx1[<NUM_LIT:0>]<EOL>n2 = nx1[-<NUM_LIT:1>]<EOL>n3 = nx2[<NUM_LIT:0>]<EOL>n4 = nx2[-<NUM_LIT:1>]<EOL>if extent[<NUM_LIT:0>] == '<STR_LIT:f>' and extent[<NUM_LIT:1>] == '<STR_LIT:f>':<EOL><INDENT>ny = np.arange(n1+n3,n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ty = np.arange(<NUM_LIT:0>,len(x1)+len(x2)-<NUM_LIT:1>)*dt + tx1[<NUM_LIT:0>]+tx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:f>' and extent[<NUM_LIT:1>] == '<STR_LIT:r>':<EOL><INDENT>ny = np.arange(n1+n3,n1+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ty = ny*dt + tx1[<NUM_LIT:0>]+tx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:r>' and extent[<NUM_LIT:1>] == '<STR_LIT:f>':<EOL><INDENT>ny = np.arange(n1+n3,n2+<NUM_LIT:1>+n3+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ty = ny*dt + tx1[<NUM_LIT:0>]+tx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:f>' and extent[<NUM_LIT:1>] == '<STR_LIT:l>':<EOL><INDENT>ny = np.arange(n2+n3,n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ty = ny*dt + tx1[-<NUM_LIT:1>]+tx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:l>' and extent[<NUM_LIT:1>] == '<STR_LIT:f>':<EOL><INDENT>ny = np.arange(n1+n4,n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ty = ny*dt + tx1[<NUM_LIT:0>]+tx2[-<NUM_LIT:1>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:r>' and extent[<NUM_LIT:1>] == '<STR_LIT:r>':<EOL><INDENT>ny = np.arange(n1+n3,min(n1+<NUM_LIT:1>+n4+<NUM_LIT:1>,n2+<NUM_LIT:1>+n3+<NUM_LIT:1>)-<NUM_LIT:1>)<EOL>ty = ny*dt + tx1[<NUM_LIT:0>]+tx2[<NUM_LIT:0>]<EOL><DEDENT>elif extent[<NUM_LIT:0>] == '<STR_LIT:l>' and extent[<NUM_LIT:1>] == '<STR_LIT:l>':<EOL><INDENT>ny = np.arange(max(n1+n4,n2+n3),n2+<NUM_LIT:1>+n4+<NUM_LIT:1>-<NUM_LIT:1>)<EOL>ty = ny*dt + max(tx1[<NUM_LIT:0>]+tx2[-<NUM_LIT:1>],tx1[-<NUM_LIT:1>]+tx2[<NUM_LIT:0>])<EOL><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>y = signal.convolve(x1, x2)*dt<EOL>print('<STR_LIT>' % (ty[<NUM_LIT:0>],ty[-<NUM_LIT:1>]))<EOL>return y[ny], ty<EOL>

Continuous-time convolution of x1 and x2 with proper tracking of the output time axis.

Appromimate the convolution integral for the convolution of two continuous-time signals using the SciPy function signal. The time (sequence axis) are managed from input to output. y(t) = x1(t)*x2(t).

Parameters
----------
x1 : ndarray of signal x1 corresponding to tx1
tx1 : ndarray time axis for x1
x2  : ndarray of signal x2 corresponding to tx2
tx2 : ndarray time axis for x2
extent : ('e1','e2') where 'e1', 'e2' may be 'f' finite, 'r' right-sided, or 'l' left-sided

Returns
-------
y : ndarray of output values y
ty : ndarray of the corresponding time axis for y

Notes
-----
The output time axis starts at the sum of the starting values in x1 and x2
and ends at the sum of the two ending values in x1 and x2. The time steps used in
x1(t) and x2(t) must match. The default extents of ('f','f') are used for signals
that are active (have support) on or within t1 and t2 respectively. A right-sided
signal such as exp(-a*t)*u(t) is semi-infinite, so it has extent 'r' and the
convolution output will be truncated to display only the valid results.

Examples
--------
>>> import matplotlib.pyplot as plt
>>> import numpy as np
>>> import sk_dsp_comm.sigsys as ss
>>> tx = np.arange(-5,10,.01)
>>> x = ss.rect(tx-2,4) # pulse starts at t = 0
>>> y,ty = ss.conv_integral(x,tx,x,tx)
>>> plt.plot(ty,y) # expect a triangle on [0,8]
>>> plt.show()

Now, consider a pulse convolved with an exponential.

>>> h = 4*np.exp(-4*tx)*ss.step(tx)
>>> y,ty = ss.conv_integral(x,tx,h,tx,extent=('f','r')) # note extents set
>>> plt.plot(ty,y) # expect a pulse charge and discharge waveform


d = np.zeros(len(t))<EOL>for k,tt in enumerate(t):<EOL><INDENT>if abs(tt) <= eps/<NUM_LIT>:<EOL><INDENT>d[k] = <NUM_LIT:1>/float(eps)<EOL><DEDENT><DEDENT>return d<EOL>

Rectangular pulse approximation to impulse function.

Parameters
----------
t : ndarray of time axis
eps : pulse width

Returns
-------
d : ndarray containing the impulse approximation

Examples
--------
>>> import matplotlib.pyplot as plt
>>> from numpy import arange
>>> from sk_dsp_comm.sigsys import delta_eps
>>> t = np.arange(-2,2,.001)
>>> d = delta_eps(t,.1)
>>> plt.plot(t,d)
>>> plt.show()