# -*- 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