In [1]:
%pylab inline

Populating the interactive namespace from numpy and matplotlib

In [2]:
from __future__ import division
from deltasigma import *
import warnings
from scipy.signal import ss2zpk
warnings.filterwarnings('ignore')


# Example continuous-time lowpass modulator - dsexample #3¶

Demonstration of a low-pass, 3rd-order continuous-time delta sigma modulator, in a very similar fashion as as done in the MATLAB Delta Sigma Toolbox, albeit employing the Python port of the MATLAB package, deltasigma.

• The Noise Transfer Function (NTF) is synthesized for a 3th-order, low-pass modulator.
• Time domain simulations are then performed to extract the SNR.
• The continuous-time NTF is realized and mapped to an equivalent discrete-time structure.
• The SNQR is again assessed.
• The modulator coefficients are then scaled and the final results are presented.

## General modulator parameters¶

In [3]:
order = 3
osr = 32
nlev = 2
f0 = 0.
Hinf = 1.5
tdac = [0, 1]
form = 'FB'
M = nlev - 1
#generic options
plotsize = (15, 8) #inches


## Synthesis¶

In [4]:
print '\t\t\t %dth-Order Continuous-Time Lowpass Example\n' % order
print 'Doing NTF synthesis...',
ntf0 = synthesizeNTF(order, osr, 2, Hinf, f0) # 2: Optimized zero placement
print 'done.'
print "\nSynthesized a %d-order NTF, with roots:\n" % order
print " Zeros:\t\t\t Poles:"
for z, p in zip(ntf0[0], ntf0[1]):
print "(%f, %fj)\t(%f, %fj)" % (np.real(z), np.imag(z), np.real(p), np.imag(p))
print ""

			 3th-Order Continuous-Time Lowpass Example

Doing NTF synthesis... done.

Synthesized a 3-order NTF, with roots:

Zeros:			 Poles:
(1.000000, 0.000000j)	(0.764515, -0.280052j)
(0.997110, 0.075973j)	(0.764515, 0.280052j)
(0.997110, -0.075973j)	(0.668460, 0.000000j)


In [5]:
figure(figsize=plotsize)
plotPZ(ntf0, showlist=True)
changeFig(10, 1.5, 7)

In [6]:
figure(figsize=plotsize)
DocumentNTF(ntf0, osr, f0)

<matplotlib.figure.Figure at 0x10b325c50>

## Time-domain NTF simulation¶

In [7]:
print 'Doing time-domain simulations...'
figure(figsize=plotsize)
PlotExampleSpectrum(ntf0, M, osr, f0)
title('Example Spectrum');

Doing time-domain simulations...


## Signal to Noise Ratio¶

If we have a two level quantizer, we run the prediction routine predictSNR, which employs the describing function method of Ardalan and Paulos.

Otherwise, we skip the prediction step and we move directly to time simulation of the NTF, to evaluate the SNR.

In [8]:
if nlev == 2:
snr_pred, amp_pred, k0, k1, se = predictSNR(ntf0, osr)
snr, amp = simulateSNR(ntf0, osr, None, f0, nlev)

In [9]:
figure(figsize=plotsize)
if nlev == 2:
plot(amp_pred, snr_pred, '-', label='Predicted')
hold(True)
plot(amp, snr,'o-.g', label='simulated')
#figureMagic(np.array([- 100,0]).reshape(1,-1),10,2,np.array([0,100]).reshape(1,-1),10,2,np.array([7,3]).reshape(1,-1),'Discrete-Time Simulation')
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([-80, 0], 10, None, [0, 80], 10, None, [12, 6], 'Time-Domain Simulations')
legend(loc=2);


## Realize the NTF and map it to an equivalent discrete-time structure¶

Performed in two steps:

• We call realizeNTF_ct to realize an NTF with a continuous-time loop filter, obtaining ABCD matrix and DAC timings.

• We map the synthesized NTF to a discrete-time equivalent. The criterion for equivalence is set in mapCtoD: the sampled pulse response of the CT system must be identical to the impulse response of the DT system.

In [10]:
print 'Mapping  to continuous-time... ',
ABCDc, tdac2 = realizeNTF_ct(ntf0, form, tdac)
Ac, Bc, Cc, Dc = partitionABCD(ABCDc)
sys_c = []
for i in range(Bc.shape[1]):
sys_c.append(ss2zpk(Ac, Bc, Cc, Dc, input=i))
print 'done.'
print 'ABCD matrix:'
print ABCDc
print "DAC timing (tdac2):"
print tdac2

Mapping  to continuous-time...  done.
ABCD matrix:
[[ 0.          0.          0.          0.04440879 -0.04440879]
[ 1.          0.         -0.00578297  0.         -0.23997612]
[ 0.          1.          0.          0.         -0.67004645]
[ 0.          0.          1.          0.          0.        ]]
DAC timing (tdac2):
[[-1. -1.]
[ 0.  1.]]

In [11]:
figure(figsize=plotsize)
n_imp = 10
y = -impL1(ntf0, n_imp)
lollipop(np.arange(n_imp + 1), y)
hold(True)
grid(True)
dt = 1./16
tppulse = np.vstack((np.zeros((1, 2)), tdac2[1:, :])).tolist()
yy = -pulse(sys_c, tppulse, dt, n_imp).squeeze()
t = np.linspace(0, n_imp + dt, 10/dt + 1)
plot(t, yy, 'g', label='continuous-time')
legend()
title('Loop filter pulse/impulse responses (negated)');

In [12]:
sys_d, Gp = mapCtoD(ABCDc, tdac2)
ABCD = np.vstack((
np.hstack((sys_d[0], sys_d[1])),
np.hstack((sys_d[2], sys_d[3]))
))
ntf, G = calculateTF(ABCD)
ntf = cancelPZ(ntf)
figure(figsize=plotsize)
DocumentNTF(ntf0, osr, f0)
subplot(121)
plotPZ(ntf, 'c', 10)
hold(True)
plotPZ(ntf0, 'k')
hold(False)

L0 = sys_c[0]
f = linspace(0, 0.5)
G = evalTFP(L0, ntf, f)
subplot(122)
hold(True)
plot(f, dbv(G), 'm')
hold(False)
title('NTF and STF');

<matplotlib.figure.Figure at 0x10df01d50>
In [13]:
print 'Re-evaluating the SNR... ',
snrR, ampR = simulateSNR(ABCD, osr, None, f0, nlev)
print 'done'

Re-evaluating the SNR...  done

In [14]:
figure(figsize=plotsize)
plot(ampR, snrR, 'o-')
hold(True)
plot(amp, snr, '-')
peak_snrR, peak_ampR = peakSNR(snrR, ampR)
msg = 'Peak SNR %.1fdB at amp = %-.1fdB' % (peak_snrR, peak_ampR)
text(peak_ampR - 10, peak_snrR, msg, horizontalalignment='right', verticalalignment='bottom');
figureMagic([-80, 0], 10, 1, [0, 80], 10, 1, None,'SQNR vs. Input Amplitude')
xlabel('Input Amplitude (dBFS)')
ylabel('SNR (dB)')
title('Continuous-Time Implementation');


## Dynamic range scaling¶

In [15]:
print 'Doing dynamic range scaling... ',
ABCDs, umax, S = scaleABCD(ABCD, nlev, f0, 1, None, None, 10000.0)
S = S[:order, :order]
Sinv = inv(S)
Acs = np.dot(np.dot(S, Ac), Sinv)
Bcs = np.dot(S, Bc)
Ccs = np.dot(Cc, Sinv)
ABCDcs = np.vstack((np.hstack((Acs, Bcs)),
np.hstack((Ccs, Dc))
))
sys_cs = (Acs, Bcs, Ccs, Dc)
print 'Done.'
print "During scaling, umax was found to be %g"% umax
print "Scaled ABCD matrix:"
print ABCDcs

Doing dynamic range scaling...  Done.
During scaling, umax was found to be 0.78
Scaled ABCD matrix:
[[ 0.          0.          0.          0.16352917 -0.16352917]
[ 0.29968151  0.         -0.01756681  0.         -0.26482216]
[ 0.          0.32919873  0.          0.         -0.24341613]
[ 0.          0.          2.75267892  0.          0.        ]]


## Conclusions: synthesis results¶

The ADC parameters were found to be:

In [16]:
adc = {
'order':order,
'osr':osr,
'M':M,
'f0':f0,
'ntf':ntf,
'ABCD':ABCD,
'umax':umax,
'peak_snr':peak_snr,
'form':form,
'ABCDc':ABCDc,
'tdac':tdac,
'tdac2':tdac2,
'L0':L0,
'sys_c':sys_c,
'ABCDcs':ABCDcs,
'sys_cs':sys_cs
}
print "%s:" % k,
print ""

ABCD:
[[ 1.          0.          0.          0.04440879 -0.04440879]
[ 0.99903645  0.99710991 -0.0057774   0.02219369 -0.26000209]
[ 0.49975909  0.99903645  0.99710991  0.00739932 -0.7967304 ]
[ 0.          0.          1.          0.          0.        ]]
ABCDc:
[[ 0.          0.          0.          0.04440879 -0.04440879]
[ 1.          0.         -0.00578297  0.         -0.23997612]
[ 0.          1.          0.          0.         -0.67004645]
[ 0.          0.          1.          0.          0.        ]]
ABCDcs:
[[ 0.          0.          0.          0.16352917 -0.16352917]
[ 0.29968151  0.         -0.01756681  0.         -0.26482216]
[ 0.          0.32919873  0.          0.         -0.24341613]
[ 0.          0.          2.75267892  0.          0.        ]]
L0: (array([], dtype=float64), array([-0.+0.07604585j,  0.-0.07604585j,  0.+0.j        ]), 0.044408785215249033)
M: 1
f0: 0.0
form: FB
ntf:
(array([ 0.99710991-0.07597258j,  0.99710991+0.07597258j,  1.00000000+0.j        ]), array([ 0.66846011+0.j        ,  0.76451465-0.28005204j,
0.76451465+0.28005204j]), 1.0)
order: 3
osr: 32
peak_snr: [[ 70.50168227]]
sys_c: [(array([], dtype=float64), array([-0.+0.07604585j,  0.-0.07604585j,  0.+0.j        ]), 0.044408785215249033), (array([-0.17907424+0.18495835j, -0.17907424-0.18495835j]), array([-0.+0.07604585j,  0.-0.07604585j,  0.+0.j        ]), -0.67004645380658423)]
sys_cs:
(array([[ 0.        ,  0.        ,  0.        ],
[ 0.29968151,  0.        , -0.01756681],
[ 0.        ,  0.32919873,  0.        ]]), array([[ 0.16352917, -0.16352917],
[ 0.        , -0.26482216],
[ 0.        , -0.24341613]]), array([[ 0.        ,  0.        ,  2.75267892]]), array([[ 0.,  0.]]))
tdac: [0, 1]
tdac2:
[[-1. -1.]
[ 0.  1.]]
umax: 0.78


### System version information¶

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