Fork PyRPL on GitHub

Source code for pyrpl.test.test_hardware_modules.test_na_iir

import logging
logger = logging.getLogger(name=__name__)
from pyrpl.attributes import *
from pyrpl import CurveDB
from pyrpl.test.test_base import TestPyrpl


[docs]class TestIir(TestPyrpl):
[docs] def setup(self): self.extradelay = 0.6 * 8e-9 # no idea where this delay comes from # shortcuts self.pyrpl.na = self.pyrpl.networkanalyzer self.na = self.pyrpl.networkanalyzer
[docs] def teardown(self): self.na.stop()
[docs] def test_pz_interface(self): """ tests that poles and real/comples_poles remain sync'ed""" iir = self.pyrpl.rp.iir p = iir.poles = [-1000j-2032, -34343j-3424, -1221, -43254.4] rp = iir.real_poles cp = iir.complex_poles assert iir.real_poles == [-1221, -43254.4], iir.real_poles assert iir.complex_poles == [1000j-2032, 34343j-3424], \ iir.complex_poles # attention: imaginary part is positivized iir.real_poles = [] assert iir.complex_poles == [1000j-2032, 34343j-3424], \ iir.complex_poles # attention: imaginary part is positivized assert iir.poles == iir.complex_poles, iir.poles iir.complex_poles = [] assert iir.poles == [] assert iir.real_poles == [] assert iir.complex_poles == [] p = iir.zeros = [-1000j - 2032, -34343j - 3424, -1221, -43254.4] rp = iir.real_zeros cp = iir.complex_zeros assert iir.real_zeros == [-1221, -43254.4], iir.real_zeros assert iir.complex_zeros == [1000j - 2032, 34343j - 3424], \ iir.complex_zeros # attention: imaginary part is positivized iir.real_zeros = [] assert iir.complex_zeros == [1000j - 2032, 34343j - 3424], \ iir.complex_zeros # attention: imaginary part is positivized assert iir.zeros == iir.complex_zeros , iir.zeros iir.complex_zeros = [] assert iir.zeros == [] assert iir.real_zeros == [] assert iir.complex_zeros == []
[docs] def test_iirsimple_na_generator(self): # this test defines a simple transfer function that occupies 2 # biquads in the iir filter. It then shifts the coefficients through # all available biquad spots and verifies that the transfer # function, as obtained from a na measurement, is in agreement with # the expected one. If something fails, the curves are saved to # CurveDB. extradelay = 0 error_threshold = 0.25 # this value is mainly so high because of # ringing effects since we sweep over a resonance of the IIR filter # over a timescale comparable to its bandwidth. We should implement # another filter with very slow scan to test for model accuracy. # This test is only to confirm that all of the biquads are working. # setup na na = self.pyrpl.networkanalyzer iir = self.pyrpl.rp.iir na.setup(start_freq=3e3, stop_freq=1e6, points=301, rbw=[500, 500], average_per_point=1, running_state='stopped', trace_average=1, amplitude=0.005, input=iir, output_direct='off', logscale=True) # setup a simple iir transfer function zeros = [1e5j - 3e3] poles = [5e4j - 3e3] gain = 1.0 iir.setup(zeros=zeros, poles=poles, gain=gain, loops=35, input=na.iq, output_direct='off') for setting in range(iir._IIRSTAGES): iir.on = False # shift coefficients into next pair of biquads (each biquad has # 6 coefficients) iir.coefficients = np.roll(iir.coefficients, 6) iir.iirfilter._fcoefficients = iir.coefficients iir.on = True # self.na_assertion(setting=setting, # module=iir, # error_threshold=error_threshold, # extradelay=extradelay, # relative=True) yield self.na_assertion, \ setting, iir, error_threshold, extradelay, True
[docs] def test_iircomplicated_na_generator(self): """ This test defines a number of complicated IIR transfer functions and tests whether the NA response of the filter corresponds to what's expected. sorry for the ugly code - the test works though if there is a problem, no need to try to understand what the code does at first (rather read the iir module code): Just check the latest new CurveDB curves and for each failed test you should find a set of curves whose names indicate the failed test, whose parameters show the error between measurement and theory, and by comparing the measurement and theory curve you should be able to figure out what went wrong in the iir filter... """ extradelay = 0 error_threshold = 0.005 # mean relative error over the whole curve, # values will be redifined individually if self.r is None: return else: pyrpl = self.pyrpl # setup na na = self.pyrpl.networkanalyzer self.pyrpl.na = na iir = pyrpl.rp.iir params = [] # setting 0 z, p, g, loops = (np.array([-1510.0000001 + 10101.36145285j, -2100.0000001 + 21828.90817759j, -1000.0000001 + 30156.73583005j, -1000.0000001 + 32063.2533145j -6100.0000001 + 44654.63524562j]), np.array([-151.00000010 + 16271.51686739j, -51.00000010 + 22342.54324816j, -10.00000010 + 30884.30406145j, -41.00000010 + 32732.52445066j, -51.00000010 + 46953.00496993j]), 0.03, 400) naset = dict(start_freq=3e3, stop_freq=50e3, points=501, rbw=[500, 500], average_per_point=1, running_state='stopped', trace_average=1, amplitude=0.05, input=iir, output_direct='off', logscale=True) error_threshold = 0.08 # [0.01, 0.03] works if average_per_point=10 in naset params.append((z, p, g, loops, naset, "0 - low_sampling", error_threshold, ['final', 'continuous'])) # setting 1 - minimum number of loops z = [1e5j - 3e3] p = [5e4j - 3e3] g = 0.5 loops = None naset = dict(start_freq=3e3, stop_freq=10e6, points=301, rbw=[500, 500], average_per_point=1, running_state='stopped', trace_average=1, amplitude=0.05, input=iir, output_direct='off', logscale=True) error_threshold = 0.05 # large because of phase error at high freq params.append((z, p, g, loops, naset, "1 - loops_None", error_threshold, ['final', 'continuous'])) # setting 2 - complicated with well-defined loops (similar to 1) z, p, g = ( np.array([-151.0000001 + 10101.36145285j, -210.0000001 + 21828.90817759j, -100.0000001 + 30156.73583005j, -100.0000001 + 32063.2533145j, -610.0000001 + 44654.63524562j]), np.array([-151.00000010 + 16271.51686739j, -51.00000010 + 22342.54324816j, -50.00000010 + 30884.30406145j, -41.00000010 + 32732.52445066j, -51.00000010 + 46953.00496993j]), 0.5) loops = 80 naset = dict(start_freq=3e3, stop_freq=50e3, points=2501, rbw=[1000, 1000], average_per_point=5, running_state='stopped', trace_average=1, amplitude=0.02, input=iir, output_direct='off', logscale=True) error_threshold = 0.03 params.append((z, p, g, loops, naset, "2 - loops=80", error_threshold, ['final', 'continuous'])) # setting 3, medium complex transfer function z = [+4e4j - 300, +2e5j - 3000] p = [+5e4j - 300, +1e5j - 3000, +1e6j - 30000, -5e5] g = 1.0 loops = None naset = dict(start_freq=1e4, stop_freq=500e3, points=301, rbw=1000, average_per_point=1, running_state='stopped', trace_average=1, amplitude=0.01, input='iir', output_direct='off', logscale=True) error_threshold = [0.04, 0.04] params.append((z, p, g, loops, naset, "3 - medium", error_threshold, ['final', 'continuous'])) # config na and iir and launch the na assertions for param in params[2:3]: print("\nComplex Iir test with the following params: %s\n" % str(params)) z, p, g, loops, naset, name, maxerror, kinds = param self.pyrpl.na.setup(**naset) iir.setup(zeros=z, poles=p, gain=g, loops=loops, input=na.iq, output_direct='off') yield self.na_assertion, name, iir, maxerror, 0, True, True, kinds
# default arguments of na_assertion: # setting, module, error_threshold=0.1, # extradelay=0, relative=False, mean=False, kinds=[]
[docs] def na_assertion(self, setting, module, error_threshold=0.1, extradelay=0, relative=False, mean=False, kinds=None): """ helper function: tests if module.transfer_function is within error_threshold of the measured transfer function of the module """ na = self.pyrpl.na na.input = module na._logger.info("Starting NA acquisition...") data = na.curve() na._logger.info("NA acquisition finished...") f = na.data_x extrastring = str(setting) if not kinds: kinds = [None] for kind in kinds: if kind is None: theory = module.transfer_function(f, extradelay=extradelay) eth = error_threshold else: extrastring += '_' + kind + '_' theory = module.transfer_function(f, extradelay=extradelay, kind=kind) try: eth = error_threshold[kinds.index(kind)] except: eth = error_threshold if relative: error = np.abs((data - theory) / theory) else: error = np.abs(data - theory) if mean: maxerror = np.mean(error) else: maxerror = np.max(error) if maxerror > eth: c = CurveDB.create(f, data, name='test_' + module.name + '_' + extrastring + '_na-failed-data') c.params["unittest_relative"] = relative c.params["unittest_maxerror"] = maxerror c.params["unittest_error_threshold"] = eth c.params["unittest_setting"] = setting c.save() c.add_child(CurveDB.create(f, theory, name='test_' + module.name + '_na-failed-theory')) c.add_child(CurveDB.create(f, error, name='test_' + module.name + '_na-failed-error')) assert False, (maxerror, setting)