예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
 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
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
 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
예제 #10
0
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)
예제 #11
0
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)
예제 #12
0
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