text: # -*- 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
  
  
  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')
          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.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.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')
          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.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.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
  