Source code for deltasigma._synthesizeChebyshevNTF

# -*- coding: utf-8 -*-
# _synthesizeChebyshevNTF.py
# Module providing the synthesizeChebyshevNTF function
# Copyright 2013 Giuseppe Venturini
# This file is part of python-deltasigma.
#
# python-deltasigma is a 1:1 Python replacement of Richard Schreier's
# MATLAB delta sigma toolbox (aka "delsigma"), upon which it is heavily based.
# The delta sigma toolbox is (c) 2009, Richard Schreier.
#
# python-deltasigma is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# LICENSE file for the licensing terms.

"""Module providing the synthesizeChebyshevNTF() function
"""

from __future__ import division

from warnings import warn

import numpy as np
from scipy.signal import cheby2

from ._ds_f1f2 import ds_f1f2


[docs]def synthesizeChebyshevNTF(order=3, OSR=64, opt=0, H_inf=1.5, f0=0.): """Synthesize a noise transfer function for a delta-sigma modulator. The NTF is a type-2 highpass Chebyshev function. :func:`synthesizeNTF` assumes that magnitude of the denominator of the NTF is approximately constant in the passband. When the OSR or ``H_inf`` are low, this assumption breaks down and :func:`synthesizeNTF` yields a non-optimal NTF. :func:`synthesizeChebyshevNTF` creates non-optimal NTFs, but fares better than :func:`synthesizeNTF` in the aforementioned circumstances. **Parameters:** order : int, optional order of the modulator, defaults to 3 OSR : int, optional oversampling ratio, defaults to 64 opt : int, optional ignored value, for consistency with :func:`synthesizeNTF` H_inf : float, optional maximum NTF gain, defaults to 1.5 f0 : float, optional center frequency (1->fs), defaults to 0. **Returns:** z, p, k : tuple a zpk tuple containing the zeros and poles of the NTF. **Warns:** * If a non-zero value is passed for ``opt``. **Raises:** * ValueError: Order must be even for a bandpass modulator. **Example:** Compare the NTFs created by :func:`synthesizeNTF` and :func:`synthesizeChebyshevNTF` when ``OSR`` is low:: OSR = 4 order = 8 H_inf = 3 H0 = synthesizeNTF(order, OSR, 1, H_inf) H1 = synthesizeChebyshevNTF(order, OSR, 0, H_inf) .. plot:: import pylab as plt import numpy as np from deltasigma import * OSR = 4 order = 8 H_inf = 3 plt.figure(figsize=(12,6)) H0 = synthesizeNTF(order, OSR, 1, H_inf) H1 = synthesizeChebyshevNTF(order, OSR, 0, H_inf) # 1. Plot the singularities. plt.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') plt.hold(True) plotPZ(H0, markersize=5) plt.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. plt.subplot(222) plt.plot(f, magH0, label='synthesizeNTF') plt.hold(True) plt.plot(f, magH1, label='synthesizeChebyshevNTF') figureMagic([0, 0.5], 0.05, None, [-80, 20], 10, None) plt.xlabel('Normalized frequency ($1\\\\rightarrow f_s)$') plt.ylabel('dB') plt.legend(loc=4) plt.title('NTF Magnitude Response') # 3. Plot the magnitude responses in the signal band. plt.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)) plt.semilogx(f*2*OSR, magH0, label='synthesizeNTF') plt.hold(True) plt.semilogx(f*2*OSR, magH1, label='synthesizeChebyshevNTF') plt.axis([fstart, 1, -50, 0]) plt.grid(True) sigma_H0 = dbv(rmsGain(H0, 0, 0.5/OSR)) sigma_H1 = dbv(rmsGain(H1, 0, 0.5/OSR)) plt.semilogx([fstart, 1], sigma_H0*np.array([1, 1]), linewidth=3, color='#191970') plt.text(0.15, sigma_H0 + 1.5, 'RMS gain = %5.0fdB' % sigma_H0) plt.semilogx([fstart, 1], sigma_H1*np.array([1, 1]), linewidth=3, color='#228B22') plt.text(0.15, sigma_H1 + 1.5, 'RMS gain = %5.0fdB' % sigma_H1) plt.xlabel('Normalized frequency ($1\\\\rightarrow f_B$)') plt.ylabel('dB') plt.legend(loc=3) plt.tight_layout() Repeat for ``H_inf`` low:: OSR = 32 order = 5 H_inf = 1.2 H0 = synthesizeNTF(order, OSR, 1, H_inf) H1 = synthesizeChebyshevNTF(order, OSR, 0, H_inf) .. plot:: import pylab as plt import numpy as np from deltasigma import * OSR = 32 order = 5 H_inf = 1.2 plt.figure(figsize=(12,6)) H0 = synthesizeNTF(order, OSR, 1, H_inf) H1 = synthesizeChebyshevNTF(order, OSR, 0, H_inf) # 1. Plot the singularities. plt.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') plt.hold(True) plotPZ(H0, markersize=5) plt.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. plt.subplot(222) plt.plot(f, magH0, label='synthesizeNTF') plt.hold(True) plt.plot(f, magH1, label='synthesizeChebyshevNTF') figureMagic([0, 0.5], 0.05, None, [-80, 20], 10, None) plt.xlabel('Normalized frequency ($1\\\\rightarrow f_s)$') plt.ylabel('dB') plt.legend(loc=4) plt.title('NTF Magnitude Response') # 3. Plot the magnitude responses in the signal band. plt.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)) plt.semilogx(f*2*OSR, magH0, label='synthesizeNTF') plt.hold(True) plt.semilogx(f*2*OSR, magH1, label='synthesizeChebyshevNTF') plt.axis([fstart, 1, -60, -20]) plt.grid(True) sigma_H0 = dbv(rmsGain(H0, 0, 0.5/OSR)) sigma_H1 = dbv(rmsGain(H1, 0, 0.5/OSR)) plt.semilogx([fstart, 1], sigma_H0*np.array([1, 1]), linewidth=3, color='#191970') plt.text(0.15, sigma_H0 + 1.5, 'RMS gain = %5.0fdB' % sigma_H0) plt.semilogx([fstart, 1], sigma_H1*np.array([1, 1]), linewidth=3, color='#228B22') plt.text(0.15, sigma_H1 + 1.5, 'RMS gain = %5.0fdB' % sigma_H1) plt.xlabel('Normalized frequency ($1\\\\rightarrow f_B$)') plt.ylabel('dB') plt.legend(loc=3) plt.tight_layout() """ if opt: warn("Got a non-zero 'opt' value. Not such optimization is " + \ "available, opt is only meant to ease switching between " + \ "synthesizeNTF and synthesizeChebyshevNTF.") if f0 != 0: if order % 2 != 0: raise ValueError('Order must be even for a bandpass modulator.') else: f1, f2 = ds_f1f2(OSR, f0) f1f2 = np.array([f1, f2]) x_min = 0 x_max = 300 dx_max = 10 ftol = 1e-06 xtol = 1e-06 x = 60 f_p = None # will be redefined later itn_limit = 10 converged = False for itn in range(itn_limit): if f0 == 0: z, p, k = cheby2(order, x, 1./OSR, btype='high', output='zpk') else: z, p, k = cheby2(order/2., x, 2.*f1f2, btype='stop', output='zpk') f = 1./k - H_inf if f > 0: x_max = x else: x_min = x if itn == 0: dx = -dx_max*np.sign(f) else: df = f - f_p if abs(df) < ftol: converged = True break dx = -f*dx/df x_p = x f_p = f x = max(x_min, min(x + dx, x_max)) dx = x - x_p if abs(dx) < xtol: break ntf = (z, p, 1) return ntf