def test_logsmooth(self): """Test function for logsmooth()""" f0 = 1./8 OSR = 64 order = 8 N = 8192 H = synthesizeNTF(order, OSR, 1, 1.5, f0) # fB = int(np.ceil(N/(2. * OSR))) quadrature = False M = 1 f1, f2 = ds_f1f2(OSR, f0, quadrature) Amp = undbv(-3) f = .3 fin = np.round(((1 - f)/2*f1 + (f + 1)/2 * f2) * N) t = np.arange(0, N).reshape((1, -1)) u = Amp * M * np.cos((2*np.pi/N)*fin*t) v, _, _, _ = simulateDSM(u, H, M + 1) window = ds_hann(N) # NBW = 1.5/N spec0 = fft(v * window) / (M*N/4) ### -1 is really important THIS IS NOT MATLAB!! fl, pl = logsmooth(spec0, fin - 1) fname = pkg_resources.resource_filename(__name__, "test_data/test_logsmooth.mat") data = scipy.io.loadmat(fname) self.assertTrue(np.allclose(fl, data['fl'], atol=1e-8, rtol=1e-5)) self.assertTrue(np.allclose(pl, data['pl'], atol=1e-8, rtol=1e-5))
def test_logsmooth(self): """Test function for logsmooth()""" f0 = 1. / 8 OSR = 64 order = 8 N = 8192 H = synthesizeNTF(order, OSR, 1, 1.5, f0) # fB = int(np.ceil(N/(2. * OSR))) quadrature = False M = 1 f1, f2 = ds_f1f2(OSR, f0, quadrature) Amp = undbv(-3) f = .3 fin = np.round(((1 - f) / 2 * f1 + (f + 1) / 2 * f2) * N) t = np.arange(0, N).reshape((1, -1)) u = Amp * M * np.cos((2 * np.pi / N) * fin * t) v, _, _, _ = simulateDSM(u, H, M + 1) window = ds_hann(N) # NBW = 1.5/N spec0 = fft(v * window) / (M * N / 4) ### -1 is really important THIS IS NOT MATLAB!! fl, pl = logsmooth(spec0, fin - 1) fname = pkg_resources.resource_filename( __name__, "test_data/test_logsmooth.mat") data = scipy.io.loadmat(fname) self.assertTrue(np.allclose(fl, data['fl'], atol=1e-8, rtol=1e-5)) self.assertTrue(np.allclose(pl, data['pl'], atol=1e-8, rtol=1e-5))
def test_plotSpectrum(self): """Test function for plotSpectrum()""" f0 = 0 osr = 32 quadrature = False Hinf = 1.5 order = 3 ntf = ds.synthesizeNTF(order, osr, 0, Hinf, f0) f1, f2 = ds.ds_f1f2(osr, f0, quadrature) delta = 2 Amp = ds.undbv(-3) f = 0.3 N = 2**12 f1_bin = np.round(f1*N) f2_bin = np.round(f2*N) fin = np.round(((1 - f)/2*f1 + (f + 1)/2*f2) * N) t = np.arange(0, N) u = Amp*np.cos((2*np.pi/N)*fin*t) v, xn, xmax, y = ds.simulateDSM(u, ntf, 2) window = ds.ds_hann(N) NBW = 1.5/N spec0 = fft(v * window)/(N/4) freq = np.linspace(0, 0.5, N/2 + 1) # plotting plt.subplot(211) plt.plot(freq, ds.dbv(spec0[:N/2 + 1]), 'c', linewidth=1, label='$S$') plt.hold(True) spec_smoothed = ds.circ_smooth(np.abs(spec0)**2., 16) plt.plot(freq, ds.dbp(spec_smoothed[:N/2 + 1]), 'b--', linewidth=2, label='$\\mathrm{circ\\_smooth}(S)$') ds.plotSpectrum(spec0, fin, 'r', linewidth=2, label='$\\mathrm{plotSpectrum}(S)$') Snn = np.abs(ds.evalTF(ntf, np.exp(2j*np.pi*freq)))**2 * 2/12*(delta)**2 plt.plot(freq, ds.dbp(Snn*NBW), 'm', linewidth=1.5, label='$\mathrm{from\\ NTF}$') plt.text(0.5, -3, 'NBW = %.1e ' % NBW, horizontalalignment='right', verticalalignment='top') ds.figureMagic((0, 0.5), None, None, (-140, 0), 20, None) plt.ylabel('Spectrum [dB]') ax = plt.gca() ax.set_title('Smoothing and plotting for LOG and LIN axes') plt.legend(loc=4) plt.subplot(212) plt.plot(freq, ds.dbv(spec0[:N/2 + 1]), 'c', linewidth=1, label='$S$') plt.hold(True) ds.plotSpectrum(spec0, fin, '--r', linewidth=2, label='$\\mathrm{plotSpectrum}(S)$') plt.plot(freq, ds.dbp(spec_smoothed[:N/2 + 1]), 'b', linewidth=2, label='$\\mathrm{circ\\_smooth}(S)$') plt.plot(freq, ds.dbp(Snn*NBW), 'm', linewidth=1.5, label='$\mathrm{from\\ NTF}$') plt.text(0.5, -3, 'NBW = %.1e ' % NBW, horizontalalignment='right', verticalalignment='top') ds.figureMagic((0, 0.5), None, None, (-140, 0), 20, None) ax = plt.gca() ax.set_xscale('linear') plt.ylabel('Spectrum [dB]') plt.xlabel('Normalized frequency ($f_s \\rightarrow 1$)') plt.legend(loc=4)
def testMultipleQ2(self): """Test function for DS simulation with nq>1 2/2""" # filtering and simulation filtM1 = [0., 0., 0., 2., -1.] filtM2 = [1., -2., 1.] ntf_eq = zpk_multiply(self.ntfs[1, 1], self.ntfs[1, 1]) M = self.nlev[0] - 1 osr = 64 f0 = 0. f1, f2 = ds.ds_f1f2(OSR=64, f0=0., complex_flag=False) delta = 2 Amp = ds.undbv(-3) # Test tone amplitude, relative to full-scale. f = 0.3 # will be adjusted to a bin N = 2**12 f1_bin = int(np.round(f1 * N)) f2_bin = int(np.round(f2 * N)) fin = np.round(((1 - f) / 2 * f1 + (f + 1) / 2 * f2) * N) # input sine t = np.arange(0, N).reshape((1, -1)) u = Amp * M * np.cos((2 * np.pi / N) * fin * t) vx, _, xmax, y = ds.simulateDSM(u, self.ABCD, nlev=self.nlev) # separate output #1 and output #2 v1 = vx[0, :] v2 = vx[1, :] # filter and combine vf = lfilter(filtM1, [1.], v1) + lfilter(filtM2, [1.], v2) # compute the spectra window = ds.ds_hann(N) NBW = 1.5 / N spec0 = np.fft.fft(vf * window) / (M * N / 2) / ds.undbv(-6) spec1 = np.fft.fft(v1 * window) / (M * N / 2) / ds.undbv(-6) spec2 = np.fft.fft(v1 * window) / (M * N / 2) / ds.undbv(-6) freq = np.linspace(0, 0.5, N // 2 + 1) # smooth, calculate the theorethical response and the SNR for VF spec0_smoothed = ds.circ_smooth(np.abs(spec0)**2., 16) Snn0 = np.abs(ds.evalTF(ntf_eq, np.exp( 2j * np.pi * freq)))**2 * 2 / 12 * (delta / M)**2 snr0 = ds.calculateSNR(spec0[f1_bin:f2_bin + 1], fin - f1_bin) # smooth, calculate the theorethical response and the SNR for V1 spec1_smoothed = ds.circ_smooth(np.abs(spec1)**2., 16) Snn1 = np.abs(ds.evalTF(self.ntfs[0, 0], np.exp( 2j * np.pi * freq)))**2 * 2 / 12 * (delta / M)**2 snr1 = ds.calculateSNR(spec1[f1_bin:f2_bin + 1], fin - f1_bin) assert snr0 > 40 assert snr1 > 40 assert snr0 - snr1 > 40
def setUp(self): fname = pkg_resources.resource_filename( __name__, "test_data/test_bplogsmooth.mat") self.data = scipy.io.loadmat(fname) f0 = 1./8 OSR = 64 order = 8 N = 8192 H = ds.synthesizeNTF(order, OSR, 1, 1.5, f0) fB = int(np.ceil(N/(2. * OSR))) ftest = int(mround(f0*N + 1./3*fB)) u = 0.5*np.sin(2*np.pi*ftest/N*np.arange(N)) v, xn, xmax, y = ds.simulateDSM(u, H) spec = np.fft.fft(v*ds.ds_hann(N))/(N/4) X = spec[:N//2 + 1] self.f, self.p = ds.bplogsmooth(X, ftest, f0)
def test_bilogplot(self): """Test function for bilogplot()""" f0 = 1./8 OSR = 64 order = 8 N = 8192 H = ds.synthesizeNTF(order, OSR, 1, 1.5, f0) fB = int(np.ceil(N/(2. * OSR))) ftest = int(np.round(f0*N + 1./3 * fB)) u = 0.5*np.sin(2*np.pi*ftest/N*np.arange(N)) v, xn, xmax, y = ds.simulateDSM(u, H) spec = np.fft.fft(v*ds.ds_hann(N))/(N/4) X = spec[:N/2 + 1] plt.figure() # graphical function: we check it doesn't fail ds.bilogplot(X, f0*N, ftest, (.03, .3, .3), (-140, 0, 10, 20))
def test_bilogplot(self): """Test function for bilogplot()""" f0 = 1. / 8 OSR = 64 order = 8 N = 8192 H = ds.synthesizeNTF(order, OSR, 1, 1.5, f0) fB = int(np.ceil(N / (2. * OSR))) ftest = int(np.round(f0 * N + 1. / 3 * fB)) u = 0.5 * np.sin(2 * np.pi * ftest / N * np.arange(N)) v, xn, xmax, y = ds.simulateDSM(u, H) spec = np.fft.fft(v * ds.ds_hann(N)) / (N / 4) X = spec[:N / 2 + 1] plt.figure() # graphical function: we check it doesn't fail ds.bilogplot(X, f0 * N, ftest, (.03, .3, .3), (-140, 0, 10, 20))
def setUp(self): fname = pkg_resources.resource_filename( __name__, "test_data/test_bplogsmooth.mat") self.data = scipy.io.loadmat(fname) f0 = 1./8 OSR = 64 order = 8 N = 8192 H = ds.synthesizeNTF(order, OSR, 1, 1.5, f0) fB = int(np.ceil(N/(2. * OSR))) ftest = int(mround(f0*N + 1./3*fB)) u = 0.5*np.sin(2*np.pi*ftest/N*np.arange(N)) v, xn, xmax, y = ds.simulateDSM(u, H) spec = np.fft.fft(v*ds.ds_hann(N))/(N/4) X = spec[:N/2 + 1] self.f, self.p = ds.bplogsmooth(X, ftest, f0)
def testMultipleQ2(self): """Test function for DS simulation with nq>1 2/2""" # filtering and simulation filtM1 = [0., 0., 0., 2., -1.] filtM2 = [1., -2., 1.] ntf_eq = zpk_multiply(self.ntfs[1, 1], self.ntfs[1, 1]) M = self.nlev[0] - 1 osr = 64 f0 = 0. f1, f2 = ds.ds_f1f2(OSR=64, f0=0., complex_flag=False) delta = 2 Amp = ds.undbv(-3) # Test tone amplitude, relative to full-scale. f = 0.3 # will be adjusted to a bin N = 2**12 f1_bin = np.round(f1*N) f2_bin = np.round(f2*N) fin = np.round(((1 - f)/2*f1 + (f + 1)/2*f2) * N) # input sine t = np.arange(0, N).reshape((1, -1)) u = Amp*M*np.cos((2*np.pi/N)*fin*t) vx, _, xmax, y = ds.simulateDSM(u, self.ABCD, nlev=self.nlev) # separate output #1 and output #2 v1 = vx[0, :] v2 = vx[1, :] # filter and combine vf = lfilter(filtM1, [1.], v1) + lfilter(filtM2, [1.], v2) # compute the spectra window = ds.ds_hann(N) NBW = 1.5/N spec0 = np.fft.fft(vf*window)/(M*N/2)/ds.undbv(-6) spec1 = np.fft.fft(v1*window)/(M*N/2)/ds.undbv(-6) spec2 = np.fft.fft(v1*window)/(M*N/2)/ds.undbv(-6) freq = np.linspace(0, 0.5, N/2 + 1) # smooth, calculate the theorethical response and the SNR for VF spec0_smoothed = ds.circ_smooth(np.abs(spec0)**2., 16) Snn0 = np.abs(ds.evalTF(ntf_eq, np.exp(2j*np.pi*freq)))**2 * 2/12*(delta/M)**2 snr0 = ds.calculateSNR(spec0[f1_bin:f2_bin + 1], fin - f1_bin) # smooth, calculate the theorethical response and the SNR for V1 spec1_smoothed = ds.circ_smooth(np.abs(spec1)**2., 16) Snn1 = np.abs(ds.evalTF(self.ntfs[0, 0], np.exp(2j*np.pi*freq)))**2 * 2/12*(delta/M)**2 snr1 = ds.calculateSNR(spec1[f1_bin:f2_bin + 1], fin - f1_bin) assert snr0 > 40 assert snr1 > 40 assert snr0-snr1 > 40
def test_plotSpectrum(self): """Test function for plotSpectrum()""" f0 = 0 osr = 32 quadrature = False Hinf = 1.5 order = 3 ntf = ds.synthesizeNTF(order, osr, 0, Hinf, f0) f1, f2 = ds.ds_f1f2(osr, f0, quadrature) delta = 2 Amp = ds.undbv(-3) f = 0.3 N = 2**12 f1_bin = np.round(f1 * N) f2_bin = np.round(f2 * N) fin = np.round(((1 - f) / 2 * f1 + (f + 1) / 2 * f2) * N) t = np.arange(0, N) u = Amp * np.cos((2 * np.pi / N) * fin * t) v, xn, xmax, y = ds.simulateDSM(u, ntf, 2) window = ds.ds_hann(N) NBW = 1.5 / N spec0 = fft(v * window) / (N / 4) freq = np.linspace(0, 0.5, N // 2 + 1) # plotting plt.subplot(211) plt.plot(freq, ds.dbv(spec0[:N // 2 + 1]), 'c', linewidth=1, label='$S$') #plt.hold(True) spec_smoothed = ds.circ_smooth(np.abs(spec0)**2., 16) plt.plot(freq, ds.dbp(spec_smoothed[:N // 2 + 1]), 'b--', linewidth=2, label='$\\mathrm{circ\\_smooth}(S)$') ds.plotSpectrum(spec0, fin, 'r', linewidth=2, label='$\\mathrm{plotSpectrum}(S)$') Snn = np.abs(ds.evalTF(ntf, np.exp( 2j * np.pi * freq)))**2 * 2 / 12 * (delta)**2 plt.plot(freq, ds.dbp(Snn * NBW), 'm', linewidth=1.5, label='$\\mathrm{from\\ NTF}$') plt.text(0.5, -3, 'NBW = %.1e ' % NBW, horizontalalignment='right', verticalalignment='top') ds.figureMagic((0, 0.5), None, None, (-140, 0), 20, None) plt.ylabel('Spectrum [dB]') ax = plt.gca() ax.set_title('Smoothing and plotting for LOG and LIN axes') plt.legend(loc=4) plt.subplot(212) plt.plot(freq, ds.dbv(spec0[:N // 2 + 1]), 'c', linewidth=1, label='$S$') #plt.hold(True) ds.plotSpectrum(spec0, fin, '--r', linewidth=2, label='$\\mathrm{plotSpectrum}(S)$') plt.plot(freq, ds.dbp(spec_smoothed[:N // 2 + 1]), 'b', linewidth=2, label='$\\mathrm{circ\\_smooth}(S)$') plt.plot(freq, ds.dbp(Snn * NBW), 'm', linewidth=1.5, label='$\\mathrm{from\\ NTF}$') plt.text(0.5, -3, 'NBW = %.1e ' % NBW, horizontalalignment='right', verticalalignment='top') ds.figureMagic((0, 0.5), None, None, (-140, 0), 20, None) ax = plt.gca() ax.set_xscale('linear') plt.ylabel('Spectrum [dB]') plt.xlabel('Normalized frequency ($f_s \\rightarrow 1$)') plt.legend(loc=4)
def fixp(self, y, scaling='mult'): """ Return fixed-point integer or fractional representation for `y` (scalar or array-like) with the same shape as `y`. Saturation / two's complement wrapping happens outside the range +/- MSB, requantization (round, floor, fix, ...) is applied on the ratio `y / LSB`. Parameters ---------- y: scalar or array-like object input value (floating point format) to be quantized scaling: String Determine the scaling before and after quantizing / saturation *'mult'* float in, int out: `y` is multiplied by `self.scale` *before* quantizing / saturating **'div'**: int in, float out: `y` is divided by `self.scale` *after* quantizing / saturating. **'multdiv'**: float in, float out (default): both of the above For all other settings, `y` is transformed unscaled. Returns ------- float scalar or ndarray with the same shape as `y`, in the range `-2*self.MSB` ... `2*self.MSB-self.LSB` Examples -------- >>> q_obj_a = {'WI':1, 'WF':6, 'ovfl':'sat', 'quant':'round'} >>> myQa = Fixed(q_obj_a) # instantiate fixed-point object myQa >>> myQa.resetN() # reset overflow counter >>> a = np.arange(0,5, 0.05) # create input signal >>> aq = myQa.fixed(a) # quantize input signal >>> plt.plot(a, aq) # plot quantized vs. original signal >>> print(myQa.N_over, "overflows!") # print number of overflows >>> # Convert output to same format as input: >>> b = np.arange(200, dtype = np.int16) >>> btype = np.result_type(b) >>> # MSB = 2**7, LSB = 2**(-2): >>> q_obj_b = {'WI':7, 'WF':2, 'ovfl':'wrap', 'quant':'round'} >>> myQb = Fixed(q_obj_b) # instantiate fixed-point object myQb >>> bq = myQb.fixed(b) >>> bq = bq.astype(btype) # restore original variable type """ #====================================================================== # (1) : INITIALIZATION # Convert input argument into proper floating point scalars / # arrays and initialize flags #====================================================================== scaling = scaling.lower() if np.shape(y): # Input is an array: # Create empty arrays for result and overflows with same shape as y # for speedup, test for invalid types SCALAR = False y = np.asarray(y) # convert lists / tuples / ... to numpy arrays yq = np.zeros(y.shape) over_pos = over_neg = np.zeros(y.shape, dtype=bool) self.ovr_flag = np.zeros(y.shape, dtype=int) if np.issubdtype(y.dtype, np.number): # numpy number type self.N += y.size elif y.dtype.kind in {'U', 'S'}: # string or unicode try: y = y.astype(np.float64) # try to convert to float self.N += y.size except (TypeError, ValueError): try: np.char.replace(y, ' ', '') # remove all whitespace y = y.astype(complex) # try to convert to complex self.N += y.size * 2 except (TypeError, ValueError ) as e: # try converting elements recursively y = np.asarray( list( map( lambda y_scalar: self.fixp( y_scalar, scaling=scaling), y))) # was: list() self.N += y.size else: logger.error("Argument '{0}' is of type '{1}',\n" "cannot convert to float.".format(y, y.dtype)) y = np.zeros(y.shape) else: # Input is a scalar SCALAR = True # get rid of errors that have occurred upstream if y is None or str(y) == "": y = 0 # If y is not a number, remove whitespace and try to convert to # to float and or to complex format: elif not np.issubdtype(type(y), np.number): y = qstr(y) y = y.replace(' ', '') # remove all whitespace try: y = float(y) except (TypeError, ValueError): try: y = complex(y) except (TypeError, ValueError) as e: logger.error("Argument '{0}' yields \n {1}".format( y, e)) y = 0.0 over_pos = over_neg = yq = 0 self.ovr_flag = 0 self.N += 1 # convert pseudo-complex (imag = 0) and complex values to real y = np.real_if_close(y) if np.iscomplexobj(y): logger.warning( "Casting complex values to real before quantization!") # quantizing complex objects is not supported yet y = y.real y_in = y # y before scaling / quantizing #====================================================================== # (2) : INPUT SCALING # Multiply by `scale` factor before requantization and saturation # when `scaling=='mult'`or 'multdiv' #====================================================================== if scaling in {'mult', 'multdiv'}: y = y * self.scale #====================================================================== # (3) : QUANTIZATION # Divide by LSB to obtain an intermediate format where the # quantization step size = 1. # Next, apply selected quantization method to convert # floating point inputs to "fixpoint integers". # Finally, multiply by LSB to restore original scale. #===================================================================== y = y / self.LSB if self.quant == 'floor': yq = np.floor(y) # largest integer i, such that i <= x (= binary truncation) elif self.quant == 'round': yq = np.round(y) # rounding, also = binary rounding elif self.quant == 'fix': yq = np.fix(y) # round to nearest integer towards zero ("Betragsschneiden") elif self.quant == 'ceil': yq = np.ceil(y) # smallest integer i, such that i >= x elif self.quant == 'rint': yq = np.rint(y) # round towards nearest int elif self.quant == 'dsm': if DS: # Synthesize DSM loop filter, # TODO: parameters should be adjustable via quantizer dict H = synthesizeNTF(order=3, osr=64, opt=1) # Calculate DSM stream and shift/scale it from -1 ... +1 to # 0 ... 1 sequence yq = (simulateDSM(y * self.LSB, H)[0] + 1) / (2 * self.LSB) # returns four ndarrays: # v: quantizer output (-1 or 1) # xn: modulator states. # xmax: maximum value that each state reached during simulation # y: The quantizer input (ie the modulator output). else: raise Exception( '"deltasigma" Toolbox not found.\n' 'Try installing it with "pip install deltasigma".') elif self.quant == 'none': yq = y # return unquantized value else: raise Exception('Unknown Requantization type "%s"!' % (self.quant)) yq = yq * self.LSB # logger.debug("y_in={0} | y={1} | yq={2}".format(y_in, y, yq)) #====================================================================== # (4) : Handle Overflow / saturation w.r.t. to the MSB, returning a # result in the range MIN = -2*MSB ... + 2*MSB-LSB = MAX #===================================================================== if self.ovfl == 'none': pass else: # Bool. vectors with '1' for every neg./pos overflow: over_neg = (yq < self.MIN) over_pos = (yq > self.MAX) # create flag / array of flags for pos. / neg. overflows self.ovr_flag = over_pos.astype(int) - over_neg.astype(int) # No. of pos. / neg. / all overflows occured since last reset: self.N_over_neg += np.sum(over_neg) self.N_over_pos += np.sum(over_pos) self.N_over = self.N_over_neg + self.N_over_pos # Replace overflows with Min/Max-Values (saturation): if self.ovfl == 'sat': yq = np.where(over_pos, self.MAX, yq) # (cond, true, false) yq = np.where(over_neg, self.MIN, yq) # Replace overflows by two's complement wraparound (wrap) elif self.ovfl == 'wrap': yq = np.where( over_pos | over_neg, yq - 4. * self.MSB * np.fix( (np.sign(yq) * 2 * self.MSB + yq) / (4 * self.MSB)), yq) else: raise Exception('Unknown overflow type "%s"!' % (self.ovfl)) return None #====================================================================== # (5) : OUTPUT SCALING # Divide result by `scale` factor when `scaling=='div'`or 'multdiv' # to obtain correct scaling for floats # - frmt2float() always returns float # - input_coeffs when quantizing the coefficients # float2frmt passes on the scaling argument #====================================================================== if scaling in {'div', 'multdiv'}: yq = yq / self.scale if SCALAR and isinstance(yq, np.ndarray): yq = yq.item() # convert singleton array to scalar return yq
def test_dsdemo3(self): """dsdemo3 test: Delta sigma modulator synthesis""" order = 5 R = 42 opt = 1 H = ds.synthesizeNTF(order, R, opt) # ## Evaluation of the coefficients for a CRFB topology a, g, b, c = ds.realizeNTF(H) # Use a single feed-in for the input # Lets check that stuffABCD understands that if b is scalar it means 1 feed-in. ABCD = ds.stuffABCD(a, g, b[0], c) # for passing the assertion below, we need b to have the trailing zeros b = np.concatenate((np.atleast_1d(b[0]), np.zeros((b.shape[0] - 1, )))) u = np.linspace(0, 0.6, 30) N = 1e4 T = np.ones((1, N)) maxima = np.zeros((order, len(u))) for i in range(len(u)): ui = u[i] v, xn, xmax, _ = ds.simulateDSM(ui * T, ABCD) maxima[:, i] = np.squeeze(xmax) if any(xmax > 1e2): umax = ui u = u[:i + 1] maxima = maxima[:, :i] break # save the maxima prescale_maxima = np.copy(maxima) # ## Scaled modulator # ### Calculate the scaled coefficients ABCDs, umax, _ = ds.scaleABCD(ABCD, N_sim=1e5) as_, gs, bs, cs = ds.mapABCD(ABCDs) # ### Calculate the state maxima u = np.linspace(0, umax, 30) N = 1e4 T = np.ones((N, )) maxima = np.zeros((order, len(u))) for i in range(len(u)): ui = u[i] v, xn, xmax, _ = ds.simulateDSM(ui * T, ABCDs) maxima[:, i] = xmax.squeeze() if any(xmax > 1e2): umax = ui u = u[:i] maxima = maxima[:, :i] break self.assertTrue(np.allclose(ABCD, self.data['ABCD'])) self.assertTrue( np.allclose(ABCDs, self.data['ABCDs'], atol=1e-2, rtol=5e-2)) self.assertTrue(np.allclose(a, self.data['a'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(b, self.data['b'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(g, self.data['g'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(c, self.data['c'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(as_, self.data['as'], atol=1e-2, rtol=5e-3)) self.assertTrue(np.allclose(bs, self.data['bs'], atol=5e-3, rtol=5e-3)) self.assertTrue(np.allclose(gs, self.data['gs'], atol=5e-3, rtol=5e-3)) self.assertTrue(np.allclose(cs, self.data['cs'], atol=3e-2, rtol=3e-2)) self.assertTrue( np.allclose(umax, self.data['umax'], atol=5e-3, rtol=5e-3)) self.assertTrue( np.allclose(maxima, self.data['maxima'], atol=2e-2, rtol=5e-2))
def test_dsdemo3(self): """dsdemo3 test: Delta sigma modulator synthesis""" order = 5 R = 42 opt = 1 H = ds.synthesizeNTF(order, R, opt) # ## Evaluation of the coefficients for a CRFB topology a, g, b, c = ds.realizeNTF(H) # Use a single feed-in for the input # Lets check that stuffABCD understands that if b is scalar it means 1 feed-in. ABCD = ds.stuffABCD(a, g, b[0], c) # for passing the assertion below, we need b to have the trailing zeros b = np.concatenate((np.atleast_1d(b[0]), np.zeros((b.shape[0] - 1,)))) u = np.linspace(0, 0.6, 30) N = 1e4 T = np.ones((1, N)) maxima = np.zeros((order, len(u))) for i in range(len(u)): ui = u[i] v, xn, xmax, _ = ds.simulateDSM(ui*T, ABCD); maxima[:, i] = np.squeeze(xmax) if any(xmax > 1e2): umax = ui u = u[:i+1] maxima = maxima[:, :i] break # save the maxima prescale_maxima = np.copy(maxima) # ## Scaled modulator # ### Calculate the scaled coefficients ABCDs, umax, _ = ds.scaleABCD(ABCD, N_sim=1e5) as_, gs, bs, cs = ds.mapABCD(ABCDs) # ### Calculate the state maxima u = np.linspace(0, umax, 30) N = 1e4 T = np.ones((N,)) maxima = np.zeros((order, len(u))) for i in range(len(u)): ui = u[i] v, xn, xmax, _ = ds.simulateDSM(ui*T, ABCDs) maxima[:, i] = xmax.squeeze() if any(xmax > 1e2): umax = ui; u = u[:i] maxima = maxima[:, :i] break self.assertTrue(np.allclose(ABCD, self.data['ABCD'])) self.assertTrue(np.allclose(ABCDs, self.data['ABCDs'], atol=1e-2, rtol=5e-2)) self.assertTrue(np.allclose(a, self.data['a'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(b, self.data['b'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(g, self.data['g'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(c, self.data['c'], atol=1e-5, rtol=1e-3)) self.assertTrue(np.allclose(as_, self.data['as'], atol=1e-2, rtol=5e-3)) self.assertTrue(np.allclose(bs, self.data['bs'], atol=5e-3, rtol=5e-3)) self.assertTrue(np.allclose(gs, self.data['gs'], atol=5e-3, rtol=5e-3)) self.assertTrue(np.allclose(cs, self.data['cs'], atol=3e-2, rtol=3e-2)) self.assertTrue(np.allclose(umax, self.data['umax'], atol=5e-3, rtol=5e-3)) self.assertTrue(np.allclose(maxima, self.data['maxima'], atol=2e-2, rtol=5e-2))
def test_sim_noiseshaper(self): fmt = Q(8, 18) input = fmt.Signal() dut = Noiseshaper(input, order=8, n_lev=64) sim = Simulator(dut) sim.add_clock(1 / 100e6) input_hist = [] output_hist = [] integrators_hist = [[] for _ in dut.stages] n = 8192 f_nyquist = int(np.ceil(n / (2. * dut.osr))) f_test = np.floor(2. / 3. * f_nyquist) u = dut.n_lev * 0.5 * np.sin(2 * np.pi * f_test / n * np.arange(n)) def testbench(): for x in u: yield input.eq(x) input_hist.append(fmt.to_float((yield input.value))) output_hist.append( fmt.to_float((yield dut.quantized_value.value))) for i, integrator in enumerate(dut.stages): integrators_hist[i].append( fmt.to_float((yield integrator.value))) yield sim.add_sync_process(testbench) sim.run() from matplotlib import pyplot as plt plt.plot(np.arange(n), output_hist, linewidth=1, label="output") plt.plot(np.arange(n), input_hist, label="input") plt.legend() plt.show() for i, integrator_hist in reversed(list(enumerate(integrators_hist))): plt.plot(np.arange(n), integrator_hist, linewidth=1, label="integrator {}".format(i)) plt.legend() plt.show() import deltasigma as ds f = np.linspace(0, 0.5, int(n / 2. + 1)) v, xn, xmax, y = ds.simulateDSM(u, dut.h, nlev=len(dut.quantization_values)) spec = np.fft.fft(v * ds.ds_hann(n)) / (n / 4) plt.plot(f, ds.dbv(spec[:int(n / 2. + 1)]), 'b', label='Simulation DS') spec = np.fft.fft(output_hist * ds.ds_hann(n)) / (n / 4) plt.plot(f, ds.dbv(spec[:int(n / 2. + 1)]), 'g', label='Simulation HW', alpha=0.7) ds.figureMagic([0, 0.5], 0.05, None, [-160, 0], 20, None, (16, 6), 'Output Spectrum') plt.xlabel('Normalized Frequency') plt.ylabel('dBFS') snr = ds.calculateSNR(spec[2:f_nyquist + 1], f_test - 2) plt.text(0.05, -10, 'SNR = %4.1fdB @ OSR = %d' % (snr, dut.osr), verticalalignment='center') NBW = 1.5 / n Sqq = 4 * ds.evalTF(dut.h, np.exp(2j * np.pi * f))**2 / 3. plt.plot(f, ds.dbp(Sqq * NBW), 'm', linewidth=2, label='Expected PSD') plt.text(0.49, -90, 'NBW = %4.1E x $f_s$' % NBW, horizontalalignment='right') plt.legend(loc=4) plt.show() pwm_out = py_pwm.modulate(np.array(output_hist) + 32, n_bits=6, oversampling_ratio=1) n = n * 64 f = np.linspace(0, 0.5, int(n / 2. + 1)) spec = np.fft.fft(pwm_out * ds.ds_hann(n)) / (n / 4) plt.plot(f, ds.dbv(spec[:int(n / 2. + 1)]), 'b', label='PWM') ds.figureMagic([0, 0.5], 0.05, None, [-160, 0], 20, None, (16, 6), 'Output Spectrum') plt.xlabel('Normalized Frequency') plt.ylabel('dBFS') snr = ds.calculateSNR(spec[2:f_nyquist + 1], f_test - 2) plt.text(0.05, -10, 'SNR = %4.1fdB @ OSR = %d' % (snr, dut.osr), verticalalignment='center') plt.legend(loc=4) plt.show()