# Source code for deltasigma._simulateQSNR

# -*- coding: utf-8 -*-
# _simulateQSNR.py
# Module providing the simulateQSNR function
# 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 simulateQSNR() function
"""

from __future__ import division, print_function

from warnings import warn

import numpy as np
from numpy.fft import fft, fftshift

from ._calculateSNR import calculateSNR
from ._simulateQDSM import simulateQDSM

[docs]def simulateQSNR(ntf, R=64, amp=None, f0=0, nlev=2, f=None, k=13): """ Determine the SNR for a quadrature delta-sigma modulator using simulations. The modulator is described by a Noise Transfer Function (NTF) and the number of quantizer levels. Using sine waves located in FFT bins, the SNR is calculated as the ratio of the sine wave power to the power in all in-band bins other than those associated with the input tone. Due to spectral smearing, the input tone is not allowed to lie in bins 0 or 1. **Parameters:** ntf : tuple, ndarray or LTI object The Noise Transfer Function in any form supported by :func:simulateQDSM, such as an ABCD matrix or an NTF description, for example a zpk tuple, num-den tuple or an LTI object. If no information is available regarding the STF, it is assumed to be unitary. R : int, optional The oversampling ratio, defining the band of interest. Defaults to 64. amp : sequence, optional The sequence of the amplitudes to be used for the input signal, in ascending order, expressed in dB, where 0 dB means a full-scale (peak value :math:n_{lev}-1) sine wave. Defaults to [-120 -110...-20 -15 -10 -9 -8 ... 0] dB. f0 : float, optional The normalized center frequency of the modulator. Defaults to 0. nlev : int, optional The number of levels in the modulator quantizer. Defaults to 2. f : float, optional The input signal frequency, normalized such that :math:1 \\rightarrow f_s. It is rounded to an FFT bin. If not set, defaults to :math:1/(4\\cdot OSR). k : int, optional The integer k sets the length of the FFT, which is :math:2^k. Defaults to 13. **Returns:** snr : ndarray The calculated SNR. amp : ndarray The amplitude vector corresponding to the SNR. """ if amp is None: amp = np.concatenate((np.arange(-120, -20+1, 10), np.atleast_1d(-15), np.arange(-10, 1))) if f is None: f = f0 + 1./(4*R) if np.abs(f - f0) > 1./(2 * R): warn('The input tone is out-of-band.') N = 2**k if N < 8*R: # Require at least 8 bins to be "in-band" warn('Increasing k to accommodate a large oversampling ratio.') k = np.ceil(np.log2(8*R)) N = 2**k F = np.round(f*N) if F <= 1: warn('Increasing k to accommodate a low input frequency.') # We want f*N > 1 k = np.ceil(np.log2(1./f)) N = 2**k F = 2 Ntransient = 100 tone = (nlev - 1)*np.exp(2j*np.pi*F/N*np.arange(-Ntransient, N)) # Hanning window of length N window = 0.5*(1 - np.cos(2*np.pi*np.arange(N)/N)) f1 = max((np.round(N*(0.5 + f0 - 0.5/R)), 0)) inBandBins = np.arange(f1, np.round(N*(0.5 + f0 + 0.5/R)) + 1, dtype=np.int32) F = F - f1 + N/2. snr = np.zeros(amp.shape) i = 0 for A in 10.0**(amp/20.): v, _, _, _ = simulateQDSM(A*tone, ntf, nlev) hwfft = fftshift(fft(window*v[Ntransient:N + Ntransient])) snr[i] = calculateSNR(hwfft[inBandBins], F) i = i + 1 return snr, amp