def init_for_roach(self, roach_name): "Inits the helper classes we use to interact w/ the given roach name" # connect to roach tmsg = 'Connecting to %s' % roach_name logger.info(tmsg) if not self.test: self.roach = corr.katcp_wrapper.FpgaClient(roach_name) time.sleep(1) if not self.roach.is_connected(): raise Exception("Cannot connect to %s" % roach_name) # we'll need this to change the frequency valonSerial = "/dev/ttyS1" # this should never change if not self.test: self.valon = ValonKATCP(self.roach, valonSerial) # this is the object that will find the MMCM value self.cal = ADCCalibrate(dir=self.data_dir, roach_name=roach_name, gpib_addr=self.gpibaddr, roach=self.roach, now=self.now, test=self.test) # read the config file and find the mmcm through each mode fn = self.get_adc_config_filename(roach_name) self.adcConf = ADCConfFile(fn)
def init_for_roach(self, roach_name): "Inits the helper classes we use to interact w/ the given roach name" # connect to roach tmsg = 'Connecting to %s'%roach_name logger.info(tmsg) if not self.test: self.roach = corr.katcp_wrapper.FpgaClient(roach_name) time.sleep(1) if not self.roach.is_connected(): raise Exception("Cannot connect to %s" % roach_name) # we'll need this to change the frequency valonSerial = "/dev/ttyS1" # this should never change if not self.test: self.valon = ValonKATCP(self.roach, valonSerial) # this is the object that will find the MMCM value self.cal = ADCCalibrate(dir = self.data_dir , roach_name = roach_name , gpib_addr = self.gpibaddr , roach = self.roach , now = self.now , test = self.test) # read the config file and find the mmcm through each mode fn = self.get_adc_config_filename(roach_name) self.adcConf = ADCConfFile(fn)
def create_adccalibrate_object(self, zdok, snapshot): """ Create the appropate ADCCalibrate object with the parameters from the config file, and personal deafaults to perform calibration and data loading. :param zdok: ZDOK port number of the ADC (0 or 1). :param snapshot: snapshot block name from where extraxt the data. :return: adc5g_devel ADCCalibrate object. """ adccal = ADCCalibrate(roach=self.fpga.fpga, roach_name="", zdok=zdok, snapshot=snapshot, dir=self.caldir, now=self.now, clockrate=self.bw) return adccal
def main(): p = OptionParser() p.set_usage('%prog [options]') p.set_description(__doc__) p.add_option('-v', '--verbosity', dest='verbosity',type='int', default=1, help='Verbosity level. Default: 1') p.add_option('-p', '--skip_prog', dest='prog_fpga',action='store_false', default=True, help='Skip FPGA programming (assumes already programmed). Default: program the FPGAs') p.add_option('-r', '--roach', dest='roach',type='str', default='srbsr2-1', help='ROACH IP address or hostname. Default: srbsr2-1') p.add_option('-z', '--zdok', dest='zdok', type='int', default=2, help='ZDOK, 0 or 1, if input is 2, then refers to both. Default = 2') p.add_option('-t', '--types', dest='types', type='str', default='all', help='Which type of calibrations to load: [all, ogp, inl]') p.add_option('-l', '--caldir', dest='caldir', type='str', default=None, help='What directory to find the calibration files in') p.add_option('-f', '--file', dest='file', type='str', default=None, help='What specific file to use? -type & -zdok options must be used as well') p.add_option('-b', '--boffile', dest='boffile',type='str', default='h1k_ver105_2013_Dec_02_1551.bof', help='Boffile to program. Default: h1k_ver105_2013_Dec_02_1551.bof') p.add_option('-g', '--gpibaddr', dest='gpibaddr', type='str', default='10.16.96.174', help='IP Address of the GPIB. Current default is set to tape room machine. Default = 10.16.96.174') p.add_option('-d', '--directory', dest='dir', type='str', default='.', help='name of directory to put all files') p.add_option('-u', '--use_conifg', dest='use_conifg', action='store_true', default=False, help='Load calibrations found in <roachname>-adc.conf file?') p.add_option('-c', '--clockrate', dest='clockrate', type='float', default=1500.0, help='Clock rate in MHz; must be specified if --use_config option is specified') opts, args = p.parse_args(sys.argv[1:]) # setup log file name: current_time = datetime.datetime.now().strftime('%Y-%m%d-%H%M%S') timestamp = "_%s_z%d_%s"%(opts.roach, opts.zdok, current_time) AdcCalLoggingFileHandler.timestamp = timestamp # load log config file var = "YGOR_TELESCOPE" confdir = '.' if not os.environ.has_key(var) else os.path.join(os.environ[var], "etc/config") conffile = "%s/%s" % (confdir, 'adc_cal_logging.conf') if not os.path.isfile(conffile): print "Cannot find config file for logging: %s" % conffile sys.exit(0) logging.config.fileConfig(conffile) logger = logging.getLogger('adc5gLogging') logger.info("Started") if not opts.verbosity: logger.setLevel(logging.INFO) logger.info("opts :\n\t" + str(opts)) logger.info("args :\n\t" + str(args)) logger.info("log file name:\n\t" + AdcCalLoggingFileHandler.logfilename) tmsg = 'Connecting to %s'%opts.roach logger.info(tmsg) r = corr.katcp_wrapper.FpgaClient(opts.roach) time.sleep(0.2) tmsg = 'ROACH is connected? ' + str(r.is_connected()) logger.info(tmsg) if opts.prog_fpga: tmsg = 'Programming ROACH with boffile %s'%opts.boffile r.progdev(opts.boffile) time.sleep(0.5) logger.info(tmsg) # Time to make our worker class cal = ADCCalibrate(dir = opts.dir , gpib_addr = opts.gpibaddr , roach_name = opts.roach , roach = r) # First, check out the noise before we load logger.info("Checking initial state of spectrum.") fn = cal.get_check_filename("inital_spec", opts.zdok) cal.check_spec(opts.zdok, filename = fn) # TBF: we have to do this, or some subset, after the roach has # has been rebooted. WHY? if opts.prog_fpga: cal.do_mmcm(opts.zdok) logger.info("Checking of spectrum after MMCM calibration.") fn = cal.get_check_filename("after_mmcm_spec", opts.zdok) cal.check_spec(opts.zdok, filename = fn) if opts.types == 'all': types = None else: types = [opts.types] if opts.file is not None: if opts.types is 'all': raise Exception, "Must specify --types when specifiying --file" if opts.zdok == 2: raise Exception, "Must specify --zdok as 0 or 1 when specifiying --file" if opts.types == 'ogp': cal.ogp.load_from_file(opts.file) logger.info("Loading calibration file %s" % opts.file) elif opts.types == 'inl': cal.inl.load_from_file(opts.file) logger.info("Loading calibration file %s" % opts.file) else: msg = "--type value unsupported (all, ogp, inl): ", opts.types logger.debug(msg) raise Exception, msg else: cal.load_calibrations(indir = opts.caldir , use_conf = opts.use_conf , freq = opts.clockrate , zdoks = opts.zdok , types = types) # Finally, check out the noise logger.info("Checking final state of spectrum.") fn = cal.get_check_filename("after_load_cals_spec", opts.zdok) cal.check_spec(opts.zdok, filename = fn)
class ADCCalibrations: def __init__(self, test=False, conf_dir=None, data_dir=None, roaches=None, mmcm_trials=None, ogp_trials=None, test_tone=None, ampl=None, now=None, manual=False, do_ogps=True, do_mmcms=True, gpib_addr=None): self.test = test self.now = now self.conf_dir = conf_dir if conf_dir is not None else '.' self.data_dir = data_dir if data_dir is not None else '.' self.roaches = roaches if roaches is not None else self.get_roach_names_from_config( ) self.banks = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] self.manual = manual self.do_mmcms = do_mmcms self.do_ogps = do_ogps if not self.do_ogps and not self.do_mmcms: raise Exception("One of do_ogps and do_mmcms must be True") # mmcms self.mmcm_trials = mmcm_trials if mmcm_trials is not None else 5 self.mmcm_tolerance = 4 # ogps/inls self.ogp_bof = 'h1k_ver106_2014_Apr_11_1612.bof' self.testfreq = test_tone if test_tone is not None else 18.3105 # MHz self.ampl = ampl if ampl is not None else -3 self.ogp_trials = ogp_trials if ogp_trials is not None else 10 self.gpibaddr = gpib_addr if gpib_addr is not None else '10.16.96.174' # tape room # helper classes self.cp = ConfigParser.ConfigParser() self.roach = None self.valon = None self.cal = None self.adcConf = None def find_all_calibrations(self): if self.do_mmcms: self.find_all_mmcms() if self.do_ogps: self.find_all_ogps() def find_all_ogps(self): for r in self.roaches: self.find_ogps(r) def find_all_mmcms(self): for r in self.roaches: self.find_mmcms(r) def get_roach_names_from_config(self): "Determines what roaches to connect to from the vegas.conf file" fn = "%s/%s" % (self.conf_dir, "vegas.conf") r = cp.read(fn) if len(r) == 0: print "Could not find roach names from: ", fn return [] # what banks to read? sec = "DEFAULTS" subsys = self.cp.get(sec, "subsystems") subsys = [int(s) for s in subsys.split(',')] # what roaches corresopond to those banks? roaches = [] for s in subsys: bank = self.banks[s - 1] sec = "BANK%s" % bank # roach_host = vegasr2-1.gb.nrao.edu roaches.append(cp.get(sec, "roach_host").split('.')[0]) return roaches def get_adc_config_filename(self, roach_name): fn = "%s/%s-adc.conf" % (self.conf_dir, roach_name) logger.info("MMCM config file: %s" % fn) return fn def init_for_roach(self, roach_name): "Inits the helper classes we use to interact w/ the given roach name" # connect to roach tmsg = 'Connecting to %s' % roach_name logger.info(tmsg) if not self.test: self.roach = corr.katcp_wrapper.FpgaClient(roach_name) time.sleep(1) if not self.roach.is_connected(): raise Exception("Cannot connect to %s" % roach_name) # we'll need this to change the frequency valonSerial = "/dev/ttyS1" # this should never change if not self.test: self.valon = ValonKATCP(self.roach, valonSerial) # this is the object that will find the MMCM value self.cal = ADCCalibrate(dir=self.data_dir, roach_name=roach_name, gpib_addr=self.gpibaddr, roach=self.roach, now=self.now, test=self.test) # read the config file and find the mmcm through each mode fn = self.get_adc_config_filename(roach_name) self.adcConf = ADCConfFile(fn) def find_ogps(self, roach_name): self.init_for_roach(roach_name) for frq, value in self.adcConf.ogp_info.items(): #i, ogp0, ogp1 = value # determine the OGP values for this clockrate tmsg = "Finding OGPs for clockrate: %s" % frq logger.info(tmsg) ogp0, ogp1 = self.find_this_ogp(frq) if ogp0 is not None and ogp1 is not None: self.adcConf.write_ogps(frq, 0, ogp0) self.adcConf.write_ogps(frq, 1, ogp1) self.adcConf.write_to_file() # the INLs don't change w/ either bof, or clockrate, # so now that this roach is in a suitable state (it's # MMCM and OGP calibrations are done), let's do one INL for z in range(2): self.cal.do_inl(z) self.adcConf.write_inls(z, self.cal.inl.inls) self.adcConf.write_to_file() def change_bof(self, bof): if self.test: return # switch to the given bof file self.roach.progdev(bof) tmsg = "Roach BOF file set to: %s" % bof logger.info(tmsg) time.sleep(2) def change_frequency(self, freq): "If necessary, use the Valon Synth to change the Roach board clockrate." if self.test: return # we also need to switch to the given frequency valonSynth = 0 # neither should this (0: A, 8: B) current_clkrate = self.valon.get_frequency(valonSynth) tmsg = "Valon Synth set to frequency: %f MHz" % current_clkrate clkrate = freq / 1e6 # Hz -> MHz logger.info(tmsg) if abs(current_clkrate - clkrate) > 0.001: self.valon.set_frequency(valonSynth, clkrate) time.sleep(1) current_clkrate = self.valon.get_frequency(valonSynth) tmsg = "Valon Synth changed to frequency: %f MHz" % current_clkrate logger.info(tmsg) # in addition, this class uses clockrate for ogps self.cal.set_clockrate(clkrate) def find_this_ogp(self, freq): # we may not need to do this, but its probably safer. # OGPs don't change w/ bof file, so this is arbitrary self.change_bof(self.ogp_bof) # but OGPs *do* change with clock rate self.change_frequency(freq) # since we reprogrammed the roach, mmcm calibrate self.cal.do_mmcm(2) # TBF: do this once manually at the beginning? if not self.cal.gpib_test( 2, self.testfreq, self.ampl, manual=self.manual): logger.info("canceling find_this_ogp for frequency %s" % freq) return None, None # now find the ogps self.cal.do_ogp(0, self.testfreq, self.ogp_trials) ogp0 = self.cal.ogp.ogps self.cal.do_ogp(1, self.testfreq, self.ogp_trials) ogp1 = self.cal.ogp.ogps return ogp0, ogp1 def find_mmcms(self, roach_name): """ For the given roach, determine all the MMCM optimal phase values for all the different combinations of bof file and frequency. """ self.init_for_roach(roach_name) for key, value in self.adcConf.mmcm_info.items(): bof, frq = key i, adc0, adc1, _ = value # determine the MMCM optimal phase values for this combo # of bof file and frequency adc0s = [] adc1s = [] for trial in range(self.mmcm_trials): adc0, adc1 = self.find_this_mmcm( bof, frq) #v, cal, roach, bof, frq) tmsg = "Found ADC mmcm's for trial %d: %s, %s" % (trial, adc0, adc1) print tmsg logger.info(tmsg) if self.has_adc_difference(adc0, adc1, adc0s, adc1s): adc0s.append(adc0) adc1s.append(adc1) self.adcConf.write_mmcms(bof, frq, 0, adc0s) self.adcConf.write_mmcms(bof, frq, 1, adc1s) self.adcConf.write_to_file() def has_adc_difference(self, adc0, adc1, adc0s, adc1s): assert len(adc0s) == len(adc1s) if len(adc0s) == 0: return True has_diff = True adcs = zip(adc0s, adc1s) t = self.mmcm_tolerance for i in range(len(adc0s)): #if ((abs(adc0s[i] - adc0) < tolerance) and (abs(adc1s[i] - adc1) < tolerance)): if self.is_within_tolerance(adc0, adc0s[i], t) \ and self.is_within_tolerance(adc1, adc1s[i], t): has_diff = False return has_diff def is_within_tolerance(self, x, y, tolerance): # they are both None if x is None and y is None: return True # one of them is None and the other isn't if x is None or y is None: return True # if this were False we'd get None's written to .conf! # none of them are none return abs(x - y) < tolerance def find_this_mmcm(self, bof, freq): #valon, adcCal, roach, bof, freq): """ Sets the given bof file and frequency (Hz) for a roach board using the given valon and ADCCalibration objects, returns the ADCs' MMCM optimal phase results. """ # switch to the given bof file self.change_bof(bof) # we also need to switch to the given frequency self.change_frequency(freq) # Now actually find the MMCM optimal phase for each ADC set_phase = False # TBF? self.cal.set_zdok(0) adc0, g = self.cal.mmcm.calibrate_mmcm_phase(set_phase=set_phase) tmsg = "MMCM (Opt. Phase, Glitches) for zdok %d: %s, %s" % (0, adc0, g) logger.info(tmsg) self.cal.set_zdok(1) adc1, g = self.cal.mmcm.calibrate_mmcm_phase(set_phase=set_phase) tmsg = "MMCM (Opt. Phase, Glitches) for zdok %d: %s, %s" % (1, adc1, g) logger.info(tmsg) return adc0, adc1
def main(): p = OptionParser() p.set_usage('%prog [options]') p.set_description(__doc__) p.add_option('-v', '--verbosity', dest='verbosity',type='int', default=1, help='Verbosity level. Default: 1') p.add_option('-r', '--roach', dest='roach',type='str', default='srbsr2-1', help='ROACH IP address or hostname. Default: srbsr2-1') p.add_option('-z', '--zdok', dest='zdok', type='int', default=2, help='ZDOK, 0 or 1, if input is 2, then refers to both. Default = 2') p.add_option('-d', '--directory', dest='dir', type='str', default='.', help='name of directory to put all files') p.add_option('-o', '--read_ogp', dest='read_ogp', action='store_true', default=False, help='Read the OGP values from the hardware') p.add_option('-i', '--read_inl', dest='read_inl', action='store_true', default=False, help='Read the INL values from the hardware') opts, args = p.parse_args(sys.argv[1:]) # setup log file name: current_time = datetime.datetime.now().strftime('%Y-%m%d-%H%M%S') timestamp = "_%s_z%d_%s"%(opts.roach, opts.zdok, current_time) AdcCalLoggingFileHandler.timestamp = timestamp # load log config file import os var = "YGOR_TELESCOPE" confdir = '.' if not os.environ.has_key(var) else os.path.join(os.environ[var], "etc/config") conffile = "%s/%s" % (confdir, 'adc_cal_logging.conf') if not os.path.isfile(conffile): print "Cannot find config file for logging: %s" % conffile sys.exit(0) logging.config.fileConfig(conffile) logger = logging.getLogger('adc5gLogging') logger.info("Started") if not opts.verbosity: logger.setLevel(logging.INFO) logger.info("opts :\n\t" + str(opts)) logger.info("args :\n\t" + str(args)) logger.info("log file name:\n\t" + AdcCalLoggingFileHandler.logfilename) tmsg = 'Connecting to %s'%opts.roach logger.info(tmsg) r = corr.katcp_wrapper.FpgaClient(opts.roach) time.sleep(0.2) tmsg = 'ROACH is connected? ' + str(r.is_connected()) logger.info(tmsg) # Time to make our worker class cal = ADCCalibrate(dir = opts.dir , gpib_addr = None #opts.gpibaddr , roach_name = opts.roach , roach = r) zdoks = [opts.zdok] if opts.zdok != 2 else [0,1] # What are the currently loaded OGP values in the Card? chs = range(1,5) if opts.read_ogp: for z in zdoks: cal.set_zdok(z) os = [cal.spi.get_offset(c) for c in chs] gs = [cal.spi.get_gain(c) for c in chs] ps = [cal.spi.get_phase(c) for c in chs] logger.info("OGPs for zdok %s" % z) logger.info("Offsets: %s" % os) logger.info("Gains: %s" % gs) logger.info("Phases: %s" % ps) if opts.read_inl: for z in zdoks: cal.set_zdok(z) logger.info("INLs for zdok %s" % z) a = cal.inl.get_inl_array() logger.debug( "lvl A B C D") for level in range(17): logger.debug( "%3d %5.2f %5.2f %5.2f %5.2f" % tuple(a[level])) i = 0 while cal.user_input("Check ADC output?"): fn = cal.get_check_filename("raw_%d" % i, opts.zdok) cal.check_raw(opts.zdok, filename = fn) fn = cal.get_check_filename("spec_%d" % i, opts.zdok) cal.check_spec(opts.zdok, filename = fn) i += 1
class ADCCalibrateTest(unittest.TestCase): "Unit tests for ." def setUp(self): # setup this class for unit tests now = datetime(2014, 4, 24, 9, 8, 38) self.adc = ADCCalibrate(dir="testdata", now=now, roach_name="noroach", test=True) # Uncomment this code if you want the logs to stdout # logging.config.fileConfig('adc5g_logging_config.conf') # logger = logging.getLogger('adc5gLogging') # logger.info("Started") def tearDown(self): # clean up all the generated files files = [] basename = "%s/snapshot_raw_%s_z%d_%s.dat.%s" exts = ["fit", "ogp", "a", "b", "c", "d", "res"] for zdok in [0, 1]: for i in range(10): for ext in exts: e = "%d.%s" % (i, ext) f = basename % (self.adc.dir, self.adc.roach_name, zdok, self.adc.current_time, e) files.append(f) for f in files: if os.path.isfile(f): os.remove(f) # clean up .png's fs = os.listdir(self.adc.dir) def test_load_calibrations(self): indir = "testdata" self.adc.load_calibrations(indir=indir) # did we load the right files? exp = ["%s/ogp_noroach_z%d_2014-04-24-090838" % (indir, i) for i in range(2)] exp.extend(["%s/inl_noroach_z%d_2014-04-24-090838.meas" % (indir, i) for i in range(2)]) self.assertEquals(exp, self.adc.loaded_files) # probe the lower level objects to make sure commands were sent self.assertEqual(138, len(self.adc.spi.regs)) # test filters self.adc.load_calibrations(indir=indir, zdoks=1, types=["ogp"]) exp = ["%s/ogp_noroach_z%d_2014-04-24-090838" % (indir, 1)] self.assertEquals(exp, self.adc.loaded_files) def test_load_calibrations_2(self): indir = "testdata" self.adc.load_calibrations(indir=indir, use_conf=True, freq=1500.0) self.assertEquals([], self.adc.loaded_files) # probe the lower level objects to make sure commands were sent self.assertEqual(138, len(self.adc.spi.regs)) # *************** The below tests are using dummy input data, so checking their # results is of limited value. Here we basically make sure theres no failures. def test_check_ramp(self): # setup zdok = 0 fn1 = self.adc.get_check_filename(self.adc.post_mmcm_ramp_check_name, zdok) + ".png" fn2 = self.adc.get_check_filename(self.adc.post_ramp_check_raw_name, zdok) + ".png" for f in [fn1, fn2]: if os.path.isfile(f): os.remove(f) self.adc.check_ramp(zdok, save=True, view=False) # test & cleanup for f in [fn1, fn2]: self.assertTrue(os.path.isfile(f)) if os.path.isfile(f): os.remove(f) def test_check_raw(self): # setup zdok = 0 fn = self.adc.get_check_filename(self.adc.raw_startup_name, zdok) + ".png" if os.path.isfile(fn): os.remove(fn) self.adc.check_raw(zdok, save=True, view=False) self.assertTrue(os.path.isfile(fn)) if os.path.isfile(fn): os.remove(fn) # skipping this for now ... def skip_test_check_spec(self): # setup zdok = 0 fn = self.adc.get_check_filename("spec", zdok) + ".png" if os.path.isfile(fn): os.remove(fn) self.adc.gpib.set_freq(1.0) self.adc.gpib.set_ampl(1.0) # not saving because the FFT causes NaN's, which the figure has # problems with self.adc.check_spec(zdok, save=False, view=False) self.assertTrue(not os.path.isfile(fn)) if os.path.isfile(fn): os.remove(fn)
def setUp(self): # setup this class for unit tests now = datetime(2014, 4, 24, 9, 8, 38) self.adc = ADCCalibrate(dir="testdata", now=now, roach_name="noroach", test=True)
def main(): p = OptionParser() p.set_usage('%prog [options]') p.set_description(__doc__) p.add_option('-v', '--verbosity', dest='verbosity', type='int', default=1, help='Verbosity level. Default: 1') p.add_option('-r', '--roach', dest='roach', type='str', default='srbsr2-1', help='ROACH IP address or hostname. Default: srbsr2-1') p.add_option( '-z', '--zdok', dest='zdok', type='int', default=2, help='ZDOK, 0 or 1, if input is 2, then refers to both. Default = 2') p.add_option('-d', '--directory', dest='dir', type='str', default='.', help='name of directory to put all files') p.add_option('-o', '--read_ogp', dest='read_ogp', action='store_true', default=False, help='Read the OGP values from the hardware') p.add_option('-i', '--read_inl', dest='read_inl', action='store_true', default=False, help='Read the INL values from the hardware') opts, args = p.parse_args(sys.argv[1:]) # setup log file name: current_time = datetime.datetime.now().strftime('%Y-%m%d-%H%M%S') timestamp = "_%s_z%d_%s" % (opts.roach, opts.zdok, current_time) AdcCalLoggingFileHandler.timestamp = timestamp # load log config file import os var = "YGOR_TELESCOPE" confdir = '.' if not os.environ.has_key(var) else os.path.join( os.environ[var], "etc/config") conffile = "%s/%s" % (confdir, 'adc_cal_logging.conf') if not os.path.isfile(conffile): print "Cannot find config file for logging: %s" % conffile sys.exit(0) logging.config.fileConfig(conffile) logger = logging.getLogger('adc5gLogging') logger.info("Started") if not opts.verbosity: logger.setLevel(logging.INFO) logger.info("opts :\n\t" + str(opts)) logger.info("args :\n\t" + str(args)) logger.info("log file name:\n\t" + AdcCalLoggingFileHandler.logfilename) tmsg = 'Connecting to %s' % opts.roach logger.info(tmsg) r = corr.katcp_wrapper.FpgaClient(opts.roach) time.sleep(0.2) tmsg = 'ROACH is connected? ' + str(r.is_connected()) logger.info(tmsg) # Time to make our worker class cal = ADCCalibrate( dir=opts.dir, gpib_addr=None #opts.gpibaddr , roach_name=opts.roach, roach=r) zdoks = [opts.zdok] if opts.zdok != 2 else [0, 1] # What are the currently loaded OGP values in the Card? chs = range(1, 5) if opts.read_ogp: for z in zdoks: cal.set_zdok(z) os = [cal.spi.get_offset(c) for c in chs] gs = [cal.spi.get_gain(c) for c in chs] ps = [cal.spi.get_phase(c) for c in chs] logger.info("OGPs for zdok %s" % z) logger.info("Offsets: %s" % os) logger.info("Gains: %s" % gs) logger.info("Phases: %s" % ps) if opts.read_inl: for z in zdoks: cal.set_zdok(z) logger.info("INLs for zdok %s" % z) a = cal.inl.get_inl_array() logger.debug("lvl A B C D") for level in range(17): logger.debug("%3d %5.2f %5.2f %5.2f %5.2f" % tuple(a[level])) i = 0 while cal.user_input("Check ADC output?"): fn = cal.get_check_filename("raw_%d" % i, opts.zdok) cal.check_raw(opts.zdok, filename=fn) fn = cal.get_check_filename("spec_%d" % i, opts.zdok) cal.check_spec(opts.zdok, filename=fn) i += 1
def main(): p = OptionParser() p.set_usage('%prog [options]') p.set_description(__doc__) p.add_option('-p', '--skip_prog', dest='prog_fpga',action='store_false', default=True, help='Skip FPGA programming (assumes already programmed). Default: program the FPGAs') p.add_option('-v', '--verbosity', dest='verbosity',type='int', default=1, help='Verbosity level. Default: 1') p.add_option('-r', '--roach', dest='roach',type='str', default='srbsr2-1', help='ROACH IP address or hostname. Default: srbsr2-1') p.add_option('-b', '--boffile', dest='boffile',type='str', default='h1k_ver105_2013_Dec_02_1551.bof', help='Boffile to program. Default: h1k_ver105_2013_Dec_02_1551.bof') p.add_option('-N', '--n_trials', dest='n_trials',type='int', default=10, help='Number of snap/fit trials. Default: 10') p.add_option('-c', '--clockrate', dest='clockrate', type='float', default=1500.0, help='Clock rate in MHz, for use when plotting frequency axes. If none is given, rate will be estimated from FPGA clock') p.add_option('-f', '--testfreq', dest='testfreq', type='float', default=18.3105, help='sine wave test frequency input in MHz. Default = 18.3105') p.add_option('-l', '--ampl', dest='ampl', type='float', default=3.0, help='Power level of test tone input in dBm. Default = 3.0') p.add_option('-g', '--gpibaddr', dest='gpibaddr', type='str', default='10.16.96.174', help='IP Address of the GPIB. Current default is set to tape room machine. Default = 10.16.96.174') p.add_option('-s', '--snapname', dest='snapname', type='str', default='adcsnap', help='snapname. Default = adcsnap') p.add_option('-z', '--zdok', dest='zdok', type='int', default=2, help='ZDOK, 0 or 1, if input is 2, then refers to both. Default = 2') p.add_option('-d', '--directory', dest='dir', type='str', default='.', help='name of directory to put all files') p.add_option('-o', '--ogp', dest='do_ogp', type='int', default=1, help='Do OGP calibration? Default = 1') p.add_option('-i', '--inl', dest='do_inl', type='int', default=1, help='Do INL calibration (OGP must be completed first)? Default = 1') p.add_option('-t', '--test', dest='test', type='int', default=1, help='Test after calibration is completed. Default=1') p.add_option('-m', '--manual', dest='manual', type='int', default=1, help='Manual control of the calibration process. Default=1') p.add_option('-S', '--save', dest='save', type='int', default=1, help='To save the plots. Default=1 (save)') p.add_option('-V', '--view', dest='view', type='int', default=1, help='To show the plots interactively (will be forced to 0 if manual is off). Default=1 (show)') p.add_option('-u', '--update_conf', dest='update_conf', action='store_false', default=True, help='Update the <roach_name>-adc.conf file?') opts, args = p.parse_args(sys.argv[1:]) # setup log file name: current_time = datetime.datetime.now().strftime('%Y-%m%d-%H%M%S') timestamp = "_%s_z%d_%s"%(opts.roach, opts.zdok, current_time) AdcCalLoggingFileHandler.timestamp = timestamp # load log config file var = "YGOR_TELESCOPE" confdir = '.' if not os.environ.has_key(var) else os.path.join(os.environ[var], "etc/config") conffile = "%s/%s" % (confdir, 'adc_cal_logging.conf') if not os.path.isfile(conffile): print "Cannot find config file for logging: %s" % conffile sys.exit(0) logging.config.fileConfig(conffile) logger = logging.getLogger('adc5gLogging') logger.info("Started") if not opts.verbosity: logger.setLevel(logging.INFO) logger.info("opts :\n\t" + str(opts)) logger.info("args :\n\t" + str(args)) logger.info("log file name:\n\t" + AdcCalLoggingFileHandler.logfilename) tmsg = 'Connecting to %s'%opts.roach logger.info(tmsg) r = corr.katcp_wrapper.FpgaClient(opts.roach) time.sleep(0.2) tmsg = 'ROACH is connected? ' + str(r.is_connected()) logger.info(tmsg) if opts.prog_fpga: tmsg = 'Programming ROACH with boffile %s'%opts.boffile r.progdev(opts.boffile) time.sleep(0.5) logger.info(tmsg) tmsg = 'Estimating clock speed...' logger.info(tmsg) clk_est = r.est_brd_clk() tmsg = 'Clock estimated speed is %d MHz'%clk_est logger.info(tmsg) if opts.clockrate is None: clkrate = clk_est*16 print "SETTING clkrate to: ", clkrate else: clkrate = opts.clockrate # Before progressing furth, check that the Valon Synth # is actually set to the clkrate. # ValonKATCP is not a worker class belonging to ADCCalibrate # since ADCCalibrate is inherited code, while Valon is ours. valonSerial = "/dev/ttyS1" # this should never change valonSynth = 0 # neither should this (0: A, 8: B) v = ValonKATCP(r, valonSerial) current_clkrate = v.get_frequency(valonSynth) tmsg = "Valon Synth set to frequency: %f MHz" % current_clkrate logger.info(tmsg) if abs(current_clkrate - clkrate) > 0.001: v.set_frequency(valonSynth, clkrate) time.sleep(1) current_clkrate = v.get_frequency(valonSynth) tmsg = "Valon Synth changed to frequency: %f MHz" % current_clkrate logger.info(tmsg) # Time to make our worker class cal = ADCCalibrate(dir = opts.dir , roach_name = opts.roach , gpib_addr = opts.gpibaddr , clockrate = clkrate , bof = opts.boffile , config = opts.update_conf , roach = r) cal.set_freq(opts.testfreq) cal.set_ampl(opts.ampl) if opts.prog_fpga: tmsg = 'Calibrating ADCs (MMCM)' logger.info(tmsg) cal.do_mmcm(opts.zdok) if opts.manual: if cal.user_input("Check the test ramps now?"): cal.check_ramp(opts.zdok, save=opts.save, view=opts.view) #, filename = fn) if opts.do_ogp or opts.test: if cal.gpib_test(opts.zdok, opts.testfreq, opts.ampl, manual=opts.manual): tmsg = 'Current test tone power level: %.4f'%opts.ampl logger.debug(tmsg) tmsg ='Current test tone frequency: %.4f'%opts.testfreq logger.debug(tmsg) cal.check_raw(opts.zdok, save=opts.save, view=(opts.manual and opts.view)) if opts.manual: if cal.user_input("Adjust power level now?"): cal.ampl_setup(opts.zdok, manual = True) else: tmsg = "Problem with synthesizer, aborting OGP calibration & testing..." logger.warning(tmsg) opts.do_ogp = 0 opts.test = 0 if opts.do_ogp: cal.do_ogp(opts.zdok, opts.testfreq, opts.n_trials) if opts.do_inl: cal.do_inl(opts.zdok) if opts.test: if opts.manual: logger.info("Startinging manual testing...") check_spec = cal.user_input("Check spectrum?") while(check_spec): cal.freq_setup(opts.zdok, manual=True) cal.ampl_setup(opts.zdok, manual=True) fn = cal.get_check_filename("post_adjustment_test_%.4fMHz" % cal.gpib.freq, opts.zdok) cal.check_spec(opts.zdok, save=opts.save, view=opts.view, filename=fn) #, filename=fn) check_spec = cal.user_input("Check spectrum?") if cal.user_input("Do frequency scan?"): cal.freq_scan(save=opts.save, view=opts.view) #, filename=fn) else: logger.info("Starting automatic testing...") for i in range(0, 5): test_freq = random.random()*cal.clockrate cal.freq_setup(manual=False, freq = test_freq) fn = "post_adjustment_test_%.4fMHz"%cal.gpib.freq + cal.file_label cal.check_spec(save=opts.save, view=False, filename=fn) fn = 'freq_scan' + cal.file_label #timestamp cal.freq_scan(save=opts.save, view=False, filename=fn)
def main(): p = OptionParser() p.set_usage('%prog [options]') p.set_description(__doc__) p.add_option( '-p', '--skip_prog', dest='prog_fpga', action='store_false', default=True, help= 'Skip FPGA programming (assumes already programmed). Default: program the FPGAs' ) p.add_option('-v', '--verbosity', dest='verbosity', type='int', default=1, help='Verbosity level. Default: 1') p.add_option('-r', '--roach', dest='roach', type='str', default='srbsr2-1', help='ROACH IP address or hostname. Default: srbsr2-1') p.add_option( '-b', '--boffile', dest='boffile', type='str', default='h1k_ver105_2013_Dec_02_1551.bof', help='Boffile to program. Default: h1k_ver105_2013_Dec_02_1551.bof') p.add_option('-N', '--n_trials', dest='n_trials', type='int', default=10, help='Number of snap/fit trials. Default: 10') p.add_option( '-c', '--clockrate', dest='clockrate', type='float', default=1500.0, help= 'Clock rate in MHz, for use when plotting frequency axes. If none is given, rate will be estimated from FPGA clock' ) p.add_option( '-f', '--testfreq', dest='testfreq', type='float', default=18.3105, help='sine wave test frequency input in MHz. Default = 18.3105') p.add_option('-l', '--ampl', dest='ampl', type='float', default=3.0, help='Power level of test tone input in dBm. Default = 3.0') p.add_option( '-g', '--gpibaddr', dest='gpibaddr', type='str', default='10.16.96.174', help= 'IP Address of the GPIB. Current default is set to tape room machine. Default = 10.16.96.174' ) p.add_option('-s', '--snapname', dest='snapname', type='str', default='adcsnap', help='snapname. Default = adcsnap') p.add_option( '-z', '--zdok', dest='zdok', type='int', default=2, help='ZDOK, 0 or 1, if input is 2, then refers to both. Default = 2') p.add_option('-d', '--directory', dest='dir', type='str', default='.', help='name of directory to put all files') p.add_option('-o', '--ogp', dest='do_ogp', type='int', default=1, help='Do OGP calibration? Default = 1') p.add_option( '-i', '--inl', dest='do_inl', type='int', default=1, help='Do INL calibration (OGP must be completed first)? Default = 1') p.add_option('-t', '--test', dest='test', type='int', default=1, help='Test after calibration is completed. Default=1') p.add_option('-m', '--manual', dest='manual', type='int', default=1, help='Manual control of the calibration process. Default=1') p.add_option('-S', '--save', dest='save', type='int', default=1, help='To save the plots. Default=1 (save)') p.add_option( '-V', '--view', dest='view', type='int', default=1, help= 'To show the plots interactively (will be forced to 0 if manual is off). Default=1 (show)' ) p.add_option('-u', '--update_conf', dest='update_conf', action='store_false', default=True, help='Update the <roach_name>-adc.conf file?') opts, args = p.parse_args(sys.argv[1:]) # setup log file name: current_time = datetime.datetime.now().strftime('%Y-%m%d-%H%M%S') timestamp = "_%s_z%d_%s" % (opts.roach, opts.zdok, current_time) AdcCalLoggingFileHandler.timestamp = timestamp # load log config file var = "YGOR_TELESCOPE" confdir = '.' if not os.environ.has_key(var) else os.path.join( os.environ[var], "etc/config") conffile = "%s/%s" % (confdir, 'adc_cal_logging.conf') if not os.path.isfile(conffile): print "Cannot find config file for logging: %s" % conffile sys.exit(0) logging.config.fileConfig(conffile) logger = logging.getLogger('adc5gLogging') logger.info("Started") if not opts.verbosity: logger.setLevel(logging.INFO) logger.info("opts :\n\t" + str(opts)) logger.info("args :\n\t" + str(args)) logger.info("log file name:\n\t" + AdcCalLoggingFileHandler.logfilename) tmsg = 'Connecting to %s' % opts.roach logger.info(tmsg) r = corr.katcp_wrapper.FpgaClient(opts.roach) time.sleep(0.2) tmsg = 'ROACH is connected? ' + str(r.is_connected()) logger.info(tmsg) if opts.prog_fpga: tmsg = 'Programming ROACH with boffile %s' % opts.boffile r.progdev(opts.boffile) time.sleep(0.5) logger.info(tmsg) tmsg = 'Estimating clock speed...' logger.info(tmsg) clk_est = r.est_brd_clk() tmsg = 'Clock estimated speed is %d MHz' % clk_est logger.info(tmsg) if opts.clockrate is None: clkrate = clk_est * 16 print "SETTING clkrate to: ", clkrate else: clkrate = opts.clockrate # Before progressing furth, check that the Valon Synth # is actually set to the clkrate. # ValonKATCP is not a worker class belonging to ADCCalibrate # since ADCCalibrate is inherited code, while Valon is ours. valonSerial = "/dev/ttyS1" # this should never change valonSynth = 0 # neither should this (0: A, 8: B) v = ValonKATCP(r, valonSerial) current_clkrate = v.get_frequency(valonSynth) tmsg = "Valon Synth set to frequency: %f MHz" % current_clkrate logger.info(tmsg) if abs(current_clkrate - clkrate) > 0.001: v.set_frequency(valonSynth, clkrate) time.sleep(1) current_clkrate = v.get_frequency(valonSynth) tmsg = "Valon Synth changed to frequency: %f MHz" % current_clkrate logger.info(tmsg) # Time to make our worker class cal = ADCCalibrate(dir=opts.dir, roach_name=opts.roach, gpib_addr=opts.gpibaddr, clockrate=clkrate, bof=opts.boffile, config=opts.update_conf, roach=r) cal.set_freq(opts.testfreq) cal.set_ampl(opts.ampl) if opts.prog_fpga: tmsg = 'Calibrating ADCs (MMCM)' logger.info(tmsg) cal.do_mmcm(opts.zdok) if opts.manual: if cal.user_input("Check the test ramps now?"): cal.check_ramp(opts.zdok, save=opts.save, view=opts.view) #, filename = fn) if opts.do_ogp or opts.test: if cal.gpib_test(opts.zdok, opts.testfreq, opts.ampl, manual=opts.manual): tmsg = 'Current test tone power level: %.4f' % opts.ampl logger.debug(tmsg) tmsg = 'Current test tone frequency: %.4f' % opts.testfreq logger.debug(tmsg) cal.check_raw(opts.zdok, save=opts.save, view=(opts.manual and opts.view)) if opts.manual: if cal.user_input("Adjust power level now?"): cal.ampl_setup(opts.zdok, manual=True) else: tmsg = "Problem with synthesizer, aborting OGP calibration & testing..." logger.warning(tmsg) opts.do_ogp = 0 opts.test = 0 if opts.do_ogp: cal.do_ogp(opts.zdok, opts.testfreq, opts.n_trials) if opts.do_inl: cal.do_inl(opts.zdok) if opts.test: if opts.manual: logger.info("Startinging manual testing...") check_spec = cal.user_input("Check spectrum?") while (check_spec): cal.freq_setup(opts.zdok, manual=True) cal.ampl_setup(opts.zdok, manual=True) fn = cal.get_check_filename( "post_adjustment_test_%.4fMHz" % cal.gpib.freq, opts.zdok) cal.check_spec(opts.zdok, save=opts.save, view=opts.view, filename=fn) #, filename=fn) check_spec = cal.user_input("Check spectrum?") if cal.user_input("Do frequency scan?"): cal.freq_scan(save=opts.save, view=opts.view) #, filename=fn) else: logger.info("Starting automatic testing...") for i in range(0, 5): test_freq = random.random() * cal.clockrate cal.freq_setup(manual=False, freq=test_freq) fn = "post_adjustment_test_%.4fMHz" % cal.gpib.freq + cal.file_label cal.check_spec(save=opts.save, view=False, filename=fn) fn = 'freq_scan' + cal.file_label #timestamp cal.freq_scan(save=opts.save, view=False, filename=fn)
class ADCCalibrations: def __init__(self , test = False , conf_dir = None , data_dir = None , roaches = None , mmcm_trials = None , ogp_trials = None , test_tone = None , ampl = None , now = None , manual = False , do_ogps = True , do_mmcms = True , gpib_addr = None): self.test = test self.now = now self.conf_dir = conf_dir if conf_dir is not None else '.' self.data_dir = data_dir if data_dir is not None else '.' self.roaches = roaches if roaches is not None else self.get_roach_names_from_config() self.banks = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'] self.manual = manual self.do_mmcms = do_mmcms self.do_ogps = do_ogps if not self.do_ogps and not self.do_mmcms: raise Exception("One of do_ogps and do_mmcms must be True") # mmcms self.mmcm_trials = mmcm_trials if mmcm_trials is not None else 5 self.mmcm_tolerance = 4 # ogps/inls self.ogp_bof = 'h1k_ver106_2014_Apr_11_1612.bof' self.testfreq = test_tone if test_tone is not None else 18.3105 # MHz self.ampl = ampl if ampl is not None else -3 self.ogp_trials = ogp_trials if ogp_trials is not None else 10 self.gpibaddr = gpib_addr if gpib_addr is not None else '10.16.96.174' # tape room # helper classes self.cp = ConfigParser.ConfigParser() self.roach = None self.valon = None self.cal = None self.adcConf = None def find_all_calibrations(self): if self.do_mmcms: self.find_all_mmcms() if self.do_ogps: self.find_all_ogps() def find_all_ogps(self): for r in self.roaches: self.find_ogps(r) def find_all_mmcms(self): for r in self.roaches: self.find_mmcms(r) def get_roach_names_from_config(self): "Determines what roaches to connect to from the vegas.conf file" fn = "%s/%s" % (self.conf_dir, "vegas.conf") r = cp.read(fn) if len(r)==0: print "Could not find roach names from: ", fn return [] # what banks to read? sec = "DEFAULTS" subsys = self.cp.get(sec, "subsystems") subsys = [int(s) for s in subsys.split(',')] # what roaches corresopond to those banks? roaches = [] for s in subsys: bank = self.banks[s-1] sec = "BANK%s" % bank # roach_host = vegasr2-1.gb.nrao.edu roaches.append(cp.get(sec, "roach_host").split('.')[0]) return roaches def get_adc_config_filename(self, roach_name): fn = "%s/%s-adc.conf" % (self.conf_dir, roach_name) logger.info("MMCM config file: %s" % fn) return fn def init_for_roach(self, roach_name): "Inits the helper classes we use to interact w/ the given roach name" # connect to roach tmsg = 'Connecting to %s'%roach_name logger.info(tmsg) if not self.test: self.roach = corr.katcp_wrapper.FpgaClient(roach_name) time.sleep(1) if not self.roach.is_connected(): raise Exception("Cannot connect to %s" % roach_name) # we'll need this to change the frequency valonSerial = "/dev/ttyS1" # this should never change if not self.test: self.valon = ValonKATCP(self.roach, valonSerial) # this is the object that will find the MMCM value self.cal = ADCCalibrate(dir = self.data_dir , roach_name = roach_name , gpib_addr = self.gpibaddr , roach = self.roach , now = self.now , test = self.test) # read the config file and find the mmcm through each mode fn = self.get_adc_config_filename(roach_name) self.adcConf = ADCConfFile(fn) def find_ogps(self, roach_name): self.init_for_roach(roach_name) for frq, value in self.adcConf.ogp_info.items(): #i, ogp0, ogp1 = value # determine the OGP values for this clockrate tmsg = "Finding OGPs for clockrate: %s" % frq logger.info(tmsg) ogp0, ogp1 = self.find_this_ogp(frq) if ogp0 is not None and ogp1 is not None: self.adcConf.write_ogps(frq, 0, ogp0) self.adcConf.write_ogps(frq, 1, ogp1) self.adcConf.write_to_file() # the INLs don't change w/ either bof, or clockrate, # so now that this roach is in a suitable state (it's # MMCM and OGP calibrations are done), let's do one INL for z in range(2): self.cal.do_inl(z) self.adcConf.write_inls(z, self.cal.inl.inls) self.adcConf.write_to_file() def change_bof(self, bof): if self.test: return # switch to the given bof file self.roach.progdev(bof) tmsg = "Roach BOF file set to: %s" % bof logger.info(tmsg) time.sleep(2) def change_frequency(self, freq): "If necessary, use the Valon Synth to change the Roach board clockrate." if self.test: return # we also need to switch to the given frequency valonSynth = 0 # neither should this (0: A, 8: B) current_clkrate = self.valon.get_frequency(valonSynth) tmsg = "Valon Synth set to frequency: %f MHz" % current_clkrate clkrate = freq / 1e6 # Hz -> MHz logger.info(tmsg) if abs(current_clkrate - clkrate) > 0.001: self.valon.set_frequency(valonSynth, clkrate) time.sleep(1) current_clkrate = self.valon.get_frequency(valonSynth) tmsg = "Valon Synth changed to frequency: %f MHz" % current_clkrate logger.info(tmsg) # in addition, this class uses clockrate for ogps self.cal.set_clockrate(clkrate) def find_this_ogp(self, freq): # we may not need to do this, but its probably safer. # OGPs don't change w/ bof file, so this is arbitrary self.change_bof(self.ogp_bof) # but OGPs *do* change with clock rate self.change_frequency(freq) # since we reprogrammed the roach, mmcm calibrate self.cal.do_mmcm(2) # TBF: do this once manually at the beginning? if not self.cal.gpib_test(2, self.testfreq, self.ampl, manual=self.manual): logger.info("canceling find_this_ogp for frequency %s" % freq) return None, None # now find the ogps self.cal.do_ogp(0, self.testfreq, self.ogp_trials) ogp0 = self.cal.ogp.ogps self.cal.do_ogp(1, self.testfreq, self.ogp_trials) ogp1 = self.cal.ogp.ogps return ogp0, ogp1 def find_mmcms(self, roach_name): """ For the given roach, determine all the MMCM optimal phase values for all the different combinations of bof file and frequency. """ self.init_for_roach(roach_name) for key, value in self.adcConf.mmcm_info.items(): bof, frq = key i, adc0, adc1, _ = value # determine the MMCM optimal phase values for this combo # of bof file and frequency adc0s = [] adc1s = [] for trial in range(self.mmcm_trials): adc0, adc1 = self.find_this_mmcm(bof, frq) #v, cal, roach, bof, frq) tmsg = "Found ADC mmcm's for trial %d: %s, %s" % (trial, adc0, adc1) print tmsg logger.info(tmsg) if self.has_adc_difference(adc0, adc1, adc0s, adc1s): adc0s.append(adc0) adc1s.append(adc1) self.adcConf.write_mmcms(bof, frq, 0, adc0s) self.adcConf.write_mmcms(bof, frq, 1, adc1s) self.adcConf.write_to_file() def has_adc_difference(self, adc0, adc1, adc0s, adc1s): assert len(adc0s) == len(adc1s) if len(adc0s) == 0: return True has_diff = True adcs = zip(adc0s, adc1s) t = self.mmcm_tolerance for i in range(len(adc0s)): #if ((abs(adc0s[i] - adc0) < tolerance) and (abs(adc1s[i] - adc1) < tolerance)): if self.is_within_tolerance(adc0, adc0s[i], t) \ and self.is_within_tolerance(adc1, adc1s[i], t): has_diff = False return has_diff def is_within_tolerance(self, x, y, tolerance): # they are both None if x is None and y is None: return True # one of them is None and the other isn't if x is None or y is None: return True # if this were False we'd get None's written to .conf! # none of them are none return abs(x - y) < tolerance def find_this_mmcm(self, bof, freq): #valon, adcCal, roach, bof, freq): """ Sets the given bof file and frequency (Hz) for a roach board using the given valon and ADCCalibration objects, returns the ADCs' MMCM optimal phase results. """ # switch to the given bof file self.change_bof(bof) # we also need to switch to the given frequency self.change_frequency(freq) # Now actually find the MMCM optimal phase for each ADC set_phase = False # TBF? self.cal.set_zdok(0) adc0, g = self.cal.mmcm.calibrate_mmcm_phase(set_phase = set_phase) tmsg = "MMCM (Opt. Phase, Glitches) for zdok %d: %s, %s" % (0, adc0, g) logger.info(tmsg) self.cal.set_zdok(1) adc1, g = self.cal.mmcm.calibrate_mmcm_phase(set_phase = set_phase) tmsg = "MMCM (Opt. Phase, Glitches) for zdok %d: %s, %s" % (1, adc1, g) logger.info(tmsg) return adc0, adc1