In [1]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib
In [2]:
from __future__ import division
from IPython.core.display import Image
from deltasigma import *
import warnings
warnings.filterwarnings('ignore')
In [3]:
# skip this, this is just to display nice tables.
from itertools import izip_longest
class Table(list):
    def _repr_html_(self):
        html = ["<table>"]
        for row in self:
            html.append("<tr>")
            for col in row:
                try:
                    float(col)
                    html.append("<td>%.3f</td>" % col)
                except(ValueError):
                    html.append("<td><b>%s</b></td>" % col)
            html.append("</tr>")
        html.append("</table>")
        return ''.join(html)
In [4]:
np.set_printoptions(suppress=True, precision=3)

Delta Sigma modulator synthesis - example #1

Demonstration of the synthesis of an example delta sigma modulator, 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, having the following characteristics:

    • A relatively low Over Sampling Ratio (OSR), equal to 32,
    • A 1-bit quantizer.
    • Maximum NTF gain equal to 1.5.
  • For this example modulator, we select to implement the CRFB topology -- see below, for order being odd -- with a single feed-in, ie $b_n = 0\ \forall n > 1$ .

In [5]:
Image(url='http://python-deltasigma.readthedocs.org/en/latest/_images/CRFB.png', retina=True)
Out[5]:

Set up the parameters

In [6]:
order = 5
osr = 32
nlev = 2
f0 = 0.
Hinf = 1.5
form = 'CRFB'

Synthesize the NTF

In [7]:
ntf = synthesizeNTF(order, osr, 2, Hinf, f0)            # Optimized zero placement
print "Synthesized a %d-order NTF, with roots:\n" % order
print " Zeros:\t\t\t Poles:"
for z, p in zip(ntf[0], ntf[1]):
    print "(%f, %fj)\t(%f, %fj)" % (np.real(z), np.imag(z), np.real(p), np.imag(p))
print ""
print "The NTF transfer function has the following expression:\n"
print pretty_lti(ntf)
print ""
Synthesized a 5-order NTF, with roots:

 Zeros:			 Poles:
(1.000000, 0.000000j)	(0.806557, -0.119823j)
(0.998603, 0.052839j)	(0.806557, 0.119823j)
(0.998603, -0.052839j)	(0.898071, -0.219819j)
(0.996045, 0.088847j)	(0.898071, 0.219819j)
(0.996045, -0.088847j)	(0.777767, 0.000000j)

The NTF transfer function has the following expression:

      (z^2 - 1.992z + 0.9999) (z^2 - 1.997z + 1) (z - 1)     
-------------------------------------------------------------
 (z^2 - 1.613z + 0.665) (z^2 - 1.796z + 0.8549) (z - 0.7778) 

Graphical inspection of the synthesized singularities

In [8]:
plotPZ(ntf, showlist=True)

Realize the NTF with the CRFB topology and create the ABCD representation

In [9]:
a, g, b, c = realizeNTF(ntf, form)
b = np.hstack(( # Use a single feed-in for the input
               np.atleast_1d(b[0]),
               np.zeros((b.shape[0] - 1, ))
             ))
ABCD = stuffABCD(a, g, b, c, form)
print "ABCD Matrix:"
print ABCD
ABCD Matrix:
[[ 1.     0.     0.     0.     0.     0.001 -0.001]
 [ 1.     1.    -0.003  0.     0.     0.    -0.008]
 [ 1.     1.     0.997  0.     0.     0.    -0.063]
 [ 0.     0.     1.     1.    -0.008  0.    -0.244]
 [ 0.     0.     1.     1.     0.992  0.    -0.802]
 [ 0.     0.     0.     0.     1.     0.     0.   ]]
In [10]:
DocumentNTF(ABCD, osr, f0)
f = gcf()
f.set_size_inches((15, 6))

Typical spectral characteristics

In [11]:
figure(figsize=(15,8))
PlotExampleSpectrum(ntf, M=1, osr=osr, f0=f0)

Signal to Noise Ratio analysis

In [12]:
snr, amp = simulateSNR(ntf, osr, None, f0, nlev)
In [13]:
figure(figsize=(15,8))
if nlev == 2:
    snr_pred, amp_pred, k0, k1, se = predictSNR(ntf, osr)
    plot(amp_pred, snr_pred, '-', label='predicted')
    hold(True)
plot(amp, snr,'o-.g', label='simulated')
xlabel('Input Level (dBFS)')
ylabel('SQNR (dB)')
peak_snr, peak_amp = peakSNR(snr, amp)
msg = 'peak SQNR = %4.1fdB  \n@ amp = %4.1fdB  ' % (peak_snr, peak_amp)
text(peak_amp-10,peak_snr,msg, horizontalalignment='right', verticalalignment='center');
msg = 'OSR = %d ' % osr
text(-2, 5, msg, horizontalalignment='right');
hold(False)
figureMagic([-100, 0], 10, None, [0, 100], 10, None, [12, 6], 'Time-Domain Simulations')
legend(loc=2);

Dynamic range scaling

Keep the integrator outputs within their working range -- to avoid waveform clipping and instabilities.

The ABCD matrix is scaled opportunely, then the effectiveness of the dynamic range scaling is verified and the state maxima are plotted for different amplitudes.

In [14]:
print 'Doing dynamic range scaling... ',
ABCD0 = ABCD.copy()
ABCD, umax, S = scaleABCD(ABCD0, nlev, f0)
print 'Done.'
print "Maximum input magnitude: %.3f" % umax
Doing dynamic range scaling...  Done.
Maximum input magnitude: 0.583
In [15]:
print 'Verifying dynamic range scaling... ',
u = np.linspace(0, 0.95*umax, 30)
N = 1e4
N0 = 50
test_tone = np.cos(2*np.pi*f0*np.arange(N))
test_tone[:N0] = test_tone[:N0]*(0.5 - 0.5*np.cos(2*np.pi/N0*np.arange(N0)))
maxima = np.zeros((order, u.shape[0]))
for i in np.arange(u.shape[0]):
    ui = u[i]
    v, xn, xmax, y = simulateDSM(ui*test_tone, ABCD, nlev)
    maxima[:, i] = xmax[:, 0]
    if (xmax > 1e2).any(): 
        print 'Warning, umax from scaleABCD was too high.'
        umax = ui
        u = u[:i]
        maxima = maxima[:, :i]
        break
print 'Done.'
print "Maximum DC input level: %.3f" % umax
Verifying dynamic range scaling...  Done.
Maximum DC input level: 0.583
In [16]:
colors = get_cmap('jet')(np.linspace(0, 1.0, order))
hold(True)
for i in range(order):
    plot(u,maxima[i,:], 'o-', color=colors[i], label='State %d' % (i+1))
grid(True)
#text(umax/2, 0.05, 'DC input', horizontalalignment='center', verticalalignment='center')
figureMagic([0, umax], None, None, [0, 1] , 0.1, 2, [12, 6], 'State Maxima')
xlabel('DC input')
ylabel('Maxima')
legend(loc='best');

Conclusions

Scaled coefficients

In [17]:
a, g, b, c = mapABCD(ABCD, form)
In [18]:
adc = {
       'order':order,
       'osr':osr,
       'nlev':nlev,
       'f0':f0,
       'ntf':ntf,
       'ABCD':ABCD,
       'umax':umax,
       'peak_snr':peak_snr,
       'form':form,
       'coefficients':{
                       'a':a,
                       'g':g,
                       'b':b,
                       'c':c
                      }
      }

Final ADC coefficients

In [19]:
t = Table()
ilabels = ['#1', '#2', '#3', '#4', '#5', '#6']
t.append(['Coefficients', 'DAC feedback', 'Resonator feedback', 
          'Feed-in', 'Interstage'])
t.append(['', 'a(n)', 'g(n)', ' b(n)', ' c(n)'])
[t.append(x) for x in izip_longest(ilabels, 
                                   adc['coefficients']['a'].tolist(), 
                                   adc['coefficients']['g'].tolist(), 
                                   adc['coefficients']['b'].tolist(), 
                                   adc['coefficients']['c'].tolist(), fillvalue="")]
t
Out[19]:
CoefficientsDAC feedbackResonator feedbackFeed-inInterstage
a(n)g(n) b(n) c(n)
#10.1000.0150.1000.137
#20.1700.0180.0000.191
#30.2130.0000.384
#40.3630.0000.440
#50.3650.0001.530
#60.000

System version information

In [20]:
#%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py
%load_ext version_information
%reload_ext version_information

%version_information numpy, scipy, matplotlib, deltasigma
Out[20]:
SoftwareVersion
Python2.7.10 64bit [GCC 4.2.1 (Apple Inc. build 5577)]
IPython3.2.1
OSDarwin 14.4.0 x86_64 i386 64bit
numpy1.9.2
scipy0.16.0
matplotlib1.4.3
deltasigma0.2.2
Fri Aug 07 16:22:06 2015 CEST