#!/usr/bin/env python # coding: utf-8 # In[1]: get_ipython().run_line_magic('pylab', 'inline') # In[2]: from __future__ import division from deltasigma import * # NTF synthesis - demo #1 # ======================= # # Demonstration of the **`synthesizeNTF`** function, as done in the **MATLAB Delta Sigma Toolbox**, employing its Python port **`deltasigma`**. # # # # * The **Noise Transfer Function** (NTF) is synthesized for a **5th-order**, **low-pass** modulator. # # * The first section deals with an **NTF without optimized zeros** (`opt=0`), # * while the second section with an **NTF *with optimized* zeros** (`opt=1`). # * Finally the two transfer functions are compared. # # * Then we move on to the synthesis of an **8th-order band-pass modulator** with optimized zeros. # 5th-order modulator # ------------------- # # General parameters: # In[3]: order = 5 OSR = 32 # ### 5th-order modulator: NTF without zeros optimization # # The synthesis of an NTF can be performed with the `synthesizeNTF(order, OSR, opt)`. # # We intentionally disable the zeros optimization, setting `opt=0`. # In[4]: # Synthesize! H0 = synthesizeNTF(order, OSR, opt=0) # 1. Plot the singularities. subplot(121) plotPZ(H0, markersize=5) title('NTF Poles and Zeros') f = np.concatenate((np.linspace(0, 0.75/OSR, 100), np.linspace(0.75/OSR, 0.5, 100))) z = np.exp(2j*np.pi*f) magH0 = dbv(evalTF(H0, z)) # 2. Plot the magnitude responses. subplot(222) plot(f, magH0) figureMagic([0, 0.5], 0.05, None, [-100, 10], 10, None, (16, 8)) xlabel('Normalized frequency ($1\\rightarrow f_s)$') ylabel('dB') title('NTF Magnitude Response') # 3. Plot the magnitude responses in the signal band. subplot(224) fstart = 0.01 f = np.linspace(fstart, 1.2, 200)/(2*OSR) z = np.exp(2j*np.pi*f) magH0 = dbv(evalTF(H0, z)) semilogx(f*2*OSR, magH0) axis([fstart, 1.2, -100,- 30]) grid(True) sigma_H0 = dbv(rmsGain(H0, 0, 0.5/OSR)) #semilogx([fstart, 1], sigma_H0*np.array([1, 1])) semilogx([fstart, 1], sigma_H0*np.array([1, 1]),'-o') text(0.15, sigma_H0 + 5, 'rms gain = %5.0fdB' % sigma_H0) xlabel('Normalized frequency ($1\\rightarrow f_B$)') ylabel('dB') tight_layout() # ### 5th-order modulator: NTF *with* zeros optimization # # This time we enable the zeros optimization, setting `opt=1` when calling synthesizeNTF(), then replot the NTF as above. # In[5]: # Synthesize again! H0 = None H1 = synthesizeNTF(order, OSR, opt=1) # 1. Plot the singularities. subplot(121) plotPZ(H1, markersize=5) title('NTF Poles and Zeros') f = np.concatenate((np.linspace(0, 0.75/OSR, 100), np.linspace(0.75/OSR, 0.5, 100))) z = np.exp(2j*np.pi*f) magH1 = dbv(evalTF(H1, z)) # 2. Plot the magnitude responses. subplot(222) plot(f, magH1) figureMagic([0, 0.5], 0.05, None, [-100, 10], 10, None, (16, 8)) xlabel('Normalized frequency ($1\\rightarrow f_s)$') ylabel('dB') title('NTF Magnitude Response') # 3. Plot the magnitude responses in the signal band. subplot(224) fstart = 0.01 f = np.linspace(fstart, 1.2, 200)/(2*OSR) z = np.exp(2j*np.pi*f) magH1 = dbv(evalTF(H1, z)) semilogx(f*2*OSR, magH1) axis([fstart, 1.2, -100,- 30]) grid(True) sigma_H1 = dbv(rmsGain(H1, 0, 0.5/OSR)) #semilogx([fstart, 1], sigma_H1*np.array([1, 1])) semilogx([fstart, 1], sigma_H1*np.array([1, 1]),'-o') text(0.15, sigma_H1 + 5, 'RMS gain = %5.0fdB' % sigma_H1) xlabel('Normalized frequency ($1\\rightarrow f_B$)') ylabel('dB') tight_layout() # 5th-order modulator: comparison # ------------------------------- # # Overlayed plots follow to ease comparison of the two synthetization approaches. # In[6]: # Synthesize! H0 = synthesizeNTF(order, OSR, opt=0) H1 = synthesizeNTF(order, OSR, opt=1) # 1. Plot the singularities. subplot(121) # we plot the singularities of the optimized NTF in light # green with slightly bigger markers so that we can better # distinguish the two NTF's when overlayed. plotPZ(H1, markersize=7, color='#90EE90') hold(True) plotPZ(H0, markersize=5) title('NTF Poles and Zeros') f = np.concatenate((np.linspace(0, 0.75/OSR, 100), np.linspace(0.75/OSR, 0.5, 100))) z = np.exp(2j*np.pi*f) magH0 = dbv(evalTF(H0, z)) magH1 = dbv(evalTF(H1, z)) # 2. Plot the magnitude responses. subplot(222) plot(f, magH0, label='All zeros in z=1') hold(True) plot(f, magH1, label='Optimized zeros') figureMagic([0, 0.5], 0.05, None, [-100, 10], 10, None, (16, 8)) xlabel('Normalized frequency ($1\\rightarrow f_s)$') ylabel('dB') legend(loc=4) title('NTF Magnitude Response') # 3. Plot the magnitude responses in the signal band. subplot(224) fstart = 0.01 f = np.linspace(fstart, 1.2, 200)/(2*OSR) z = np.exp(2j*np.pi*f) magH0 = dbv(evalTF(H0, z)) magH1 = dbv(evalTF(H1, z)) semilogx(f*2*OSR, magH0, label='All zeros in z=1') hold(True) semilogx(f*2*OSR, magH1, label='Optimized zeros') axis([fstart, 1.2, -100,- 30]) grid(True) sigma_H0 = dbv(rmsGain(H0, 0, 0.5/OSR)) sigma_H1 = dbv(rmsGain(H1, 0, 0.5/OSR)) #semilogx([fstart, 1], sigma_H0*np.array([1, 1])) plot([fstart, 1], sigma_H0*np.array([1, 1]), 'o-') text(0.15, sigma_H0 + 5, 'RMS gain = %5.0fdB' % sigma_H0) #semilogx([fstart, 1], sigma_H1*np.array([1, 1])) plot([fstart, 1], sigma_H1*np.array([1, 1]), 'o-') text(0.15, sigma_H1 + 5, 'RMS gain = %5.0fdB' % sigma_H1) xlabel('Normalized frequency ($1\\rightarrow f_B$)') ylabel('dB') legend(loc=4) tight_layout() # 8th-order bandpass Modulator # ---------------------------- # In the following, we synthesize an 8th-order modulator with optimized zeros. # In[7]: order = 8 OSR = 64 opt = 2 f0 = 0.125 H = synthesizeNTF(order, OSR, opt, 1.5, f0) subplot(121) plotPZ(H) title('Bandpass NTF Poles and Zeros') f = np.concatenate((np.linspace(0, f0 - 1./(2.*OSR), 50), np.linspace(f0 - 1./ (2 * OSR), f0 + 1./(2.*OSR), 100), np.linspace(f0 + 1./(2.*OSR), 0.5, 50))) z = np.exp(2j * pi * f) magH = dbv(evalTF(H, z)) subplot(222) plot(f, magH) hold(True) G = (np.zeros((order/2,)), H[1], 1) k = 1./np.abs(evalTF(G, np.exp(2j*np.pi*f0))) G = (G[0], G[1], k) magG = dbv(evalTF(G, z)) plot(f, magG, 'r') figureMagic([0, 0.5], 0.05, None, [-100, 10], 10, None, (16, 8)) #axis([0, 0.5, -100, 10]) grid(True) xlabel('Normalized frequency ($1 \\rightarrow fs$)') ylabel('dB') title('Bandpass NTF/STF Magnitude Response') f = np.linspace(f0 - 0.3/OSR, f0 + 0.3/OSR) z = np.exp(2j*np.pi*f) magH = dbv(evalTF(H, z)) subplot(224) fstart = -.5 plot(2*OSR*(f - f0), magH) axis([- 0.6, 0.6, -100, -60]) grid(True) sigma_H = dbv(rmsGain(H, f0 - 0.25/OSR, f0 + 0.25/OSR)) hold(True) #plot([-0.5, 0.5], sigma_H*np.array([1, 1])) plot([-0.5, 0.5], sigma_H*np.array([1, 1]), 'o-') text(-.2, sigma_H + 5, 'rms gain = %5.0fdB' % sigma_H) xlabel('Normalized frequency offset') ylabel('dB') tight_layout() # Further information about NTF synthesis # --------------------------------------- # # Please refer to `help(synthesizeNTF)` for detailed - and possibly more updated - documentation! # # ###`help(synthesizeNTF)` as of writing: # # Help on function synthesizeNTF in module deltasigma._synthesizeNTF: # # **synthesizeNTF(order=3, osr=64, opt=0, H_inf=1.5, f0=0.0)** # # Synthesize a noise transfer function for a delta-sigma modulator. # # **Parameters:** # # order : *int, optional* # the order of the modulator, defaults to 3 # # osr : *float, optional* # the oversamping ratio, defaults to 64 # # opt : *int or list of floats, optional* # flag for optimized zeros, defaults to 0 # # * 0 -> not optimized, # * 1 -> optimized, # * 2 -> optimized with at least one zero at band-center, # * 3 -> optimized zeros (with optimizer) # * 4 -> same as 3, but with at least one zero at band-center # * [z] -> zero locations in complex form # # H_inf : *real, optional* # max allowed peak value of the NTF. Defaults to 1.5 # # f0 : *real, optional* # center frequency for BP modulators, or 0 for LP modulators. # Defaults to 0. # # 1 corresponds to the sampling frequency, so that 0.5 is the # maximum value. A value of 0 specifies an LP modulator. # # **Returns:** # # ntf : *tuple* # noise transfer function in zpk form. # # **Raises:** # # ValueError # # * 'Error. f0 must be less than 0.5' if f0 is out of range # # * 'Order must be even for a bandpass modulator.' if the order is # incompatible with the modulator type. # # * 'The opt vector must be of length xxx' if opt is used to explicitly # pass the NTF zeros and these are in the wrong number. # # **Warns:** # # * 'Creating a lowpass ntf.' if the center frequency is different # from zero, but so low that a low pass modulator must be designed. # # * 'Unable to achieve specified H_inf ...' if the desired H_inf # cannot be achieved. # # * 'Iteration limit exceeded' if the routine converges too slowly. # # **Notes:** # # This is actually a wrapper function which calls the appropriate version # of synthesizeNTF, based on the module control flag `optimize_NTF` which # determines whether to use optimization tools. # # Parameter ``H_inf`` is used to enforce the Lee stability criterion. # # **See also:** # # * `clans()` : Closed-Loop Analysis of Noise-Shaper. # # An alternative method for selecting NTFs based on the 1-norm of the # impulse response of the NTF # # * `synthesizeChebyshevNTF()` : Select a type-2 highpass Chebyshev NTF. # # This function does a better job than synthesizeNTF if osr # or H_inf is low. # ### System version information # In[8]: #%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py get_ipython().run_line_magic('load_ext', 'version_information') get_ipython().run_line_magic('reload_ext', 'version_information') get_ipython().run_line_magic('version_information', 'numpy, scipy, matplotlib, deltasigma') # In[ ]: