Esempio n. 1
0
 def __init__(self, address, handler=BasicRequestHandler,
              correlator=BEE2CorrelationProvider, reference=6,
              fstop=0.256, antennas=[6, 1, 2, 3, 4, 5, 7, 8],
              correlator_lags=16, include_baselines='*-*', initial_int_time=16, 
              analog_bandwidth=512000000.0, antenna_diameter=3,
              bee2_host='b02.ata.pvt', bee2_port=7147,
              correlator_bitstream='bee2_calib_corr.bof',
              ipa_hosts=('169.254.128.3', '169.254.128.2'),
              dbe_host='169.254.128.0', dds_host='128.171.116.189',
              correlator_client_port=8332, phase_tracker_port=9453):
     """ SubmillimeterArrayTCPServer(address, handler, correlator, lags, baselines)
     This subclasses the BasicTCPServer and adds some methods needed for
     controlling and reading data from the BEE2CorrelationProvider. Please see 
     the BasicTCPServer documentation for more detailed information."""
     BasicTCPServer.__init__(self, address, handler=handler, 
                             correlator=correlator, correlator_lags=correlator_lags, 
                             antennas=antennas, initial_int_time=initial_int_time,
                             antenna_diameter=antenna_diameter, analog_bandwidth=analog_bandwidth, 
                             include_baselines=include_baselines)
     self._correlator = correlator(self, self._include_baselines, bee2_host, bee2_port, 
                                   lags=correlator_lags, bof=correlator_bitstream)
     self._correlator_client = BEE2CorrelatorClient('0.0.0.0', correlator_client_port)
     self.bee2_host, self.bee2_port, self.bee2_bitstream = bee2_host, bee2_port, correlator_bitstream
     self._delay_tracker_thread = Thread(target=self._delay_tracker)
     self._delay_tracker_stopevent = Event()
     self._checks_thread = Thread(target=self._checks_loop)
     self._checks_stopevent = Event()
     self._bee2 = BEE2Client(bee2_host, port=bee2_port)
     self._bee2._connected.wait()
     self._dds = DDSClient(dds_host)
     self._ipa0 = IBOBClient(ipa_hosts[0], port=23)
     self._ipa1 = IBOBClient(ipa_hosts[1], port=23)
     self._dbe = IBOBClient(dbe_host, port=23)
     self._reference_antenna = reference
     self._phase_tracker_port = phase_tracker_port
     self._fstop = fstop # GHz, fringe stopping
     self._ipas = {'ipa0': self._ipa0, 'ipa1': self._ipa1}
     self._ibobs = {'ipa0': self._ipa0, 'ipa1': self._ipa1, 'dbe': self._dbe}
     self._boards = {'ipa0': self._ipa0, 'ipa1': self._ipa1, 'dbe': self._dbe, 'bee2': self._bee2}
     self._mapping = dict((a, i) for i, a in enumerate(self._antennas))#{6:0, 1:1, 2:2, 3:3, 4:4, 5:5, 7:6, 8:7}
     self._input_ibob_map = {0: [self._ipa0, 0], 1: [self._ipa0, 1],
                             2: [self._ipa0, 2], 3: [self._ipa0, 3],
                             4: [self._ipa1, 0], 5: [self._ipa1, 1],
                             6: [self._ipa1, 2], 7: [self._ipa1, 3]}
     self._param_handlers = {'_thresholds' : self._thresh_handler,
                             '_phases': self._phase_handler,
                             '_phase_offsets': self._phase_offset_handler,
                             '_delays': self._delay_handler,
                             '_delay_offsets': self._delay_offset_handler,
                             '_gains': self._gain_handler}
     self._command_set.update({2 : self.get_mapping,
                               3 : self.set_mapping,
                               5 : self.load_walsh_table,
                               6 : self.clear_walsh_table,
                               7 : self.delay_tracker,
                               12 : self.reset_xaui,
                               13 : self.arm_sync,
                               14 : self.noise_mode,
                               15 : self._board,
                               16 : self.get_reference,
                               17 : self.setup_fstopping,
                               18 : self.start_fstopping,
                               19 : self.stop_fstopping,
                               36 : self.get_delays,
                               37 : self.set_delays,
                               38 : self.get_phases,
                               39 : self.set_phases,
                               40 : self.get_gains,
                               41 : self.set_gains,
                               42 : self.get_thresholds,
                               43 : self.set_thresholds,
                               64 : self.get_dbe_gains,
                               65 : self.set_dbe_gains,
                               96 : self.operations_log,
                               128 : self.get_correlation})
     self.setup()
     #self.sync_all()
     self.start_checks_loop(30.0)
     #self.start_delay_tracker(4.0)
     self.start_phase_tracker(1)
Esempio n. 2
0
class SubmillimeterArrayTCPServer(BasicTCPServer):

    def __init__(self, address, handler=BasicRequestHandler,
                 correlator=BEE2CorrelationProvider, reference=6,
                 fstop=0.256, antennas=[6, 1, 2, 3, 4, 5, 7, 8],
                 correlator_lags=16, include_baselines='*-*', initial_int_time=16, 
                 analog_bandwidth=512000000.0, antenna_diameter=3,
                 bee2_host='b02.ata.pvt', bee2_port=7147,
                 correlator_bitstream='bee2_calib_corr.bof',
                 ipa_hosts=('169.254.128.3', '169.254.128.2'),
                 dbe_host='169.254.128.0', dds_host='128.171.116.189',
                 correlator_client_port=8332, phase_tracker_port=9453):
        """ SubmillimeterArrayTCPServer(address, handler, correlator, lags, baselines)
        This subclasses the BasicTCPServer and adds some methods needed for
        controlling and reading data from the BEE2CorrelationProvider. Please see 
        the BasicTCPServer documentation for more detailed information."""
        BasicTCPServer.__init__(self, address, handler=handler, 
                                correlator=correlator, correlator_lags=correlator_lags, 
                                antennas=antennas, initial_int_time=initial_int_time,
                                antenna_diameter=antenna_diameter, analog_bandwidth=analog_bandwidth, 
                                include_baselines=include_baselines)
        self._correlator = correlator(self, self._include_baselines, bee2_host, bee2_port, 
                                      lags=correlator_lags, bof=correlator_bitstream)
        self._correlator_client = BEE2CorrelatorClient('0.0.0.0', correlator_client_port)
        self.bee2_host, self.bee2_port, self.bee2_bitstream = bee2_host, bee2_port, correlator_bitstream
        self._delay_tracker_thread = Thread(target=self._delay_tracker)
        self._delay_tracker_stopevent = Event()
        self._checks_thread = Thread(target=self._checks_loop)
        self._checks_stopevent = Event()
        self._bee2 = BEE2Client(bee2_host, port=bee2_port)
        self._bee2._connected.wait()
        self._dds = DDSClient(dds_host)
        self._ipa0 = IBOBClient(ipa_hosts[0], port=23)
        self._ipa1 = IBOBClient(ipa_hosts[1], port=23)
        self._dbe = IBOBClient(dbe_host, port=23)
        self._reference_antenna = reference
        self._phase_tracker_port = phase_tracker_port
        self._fstop = fstop # GHz, fringe stopping
        self._ipas = {'ipa0': self._ipa0, 'ipa1': self._ipa1}
        self._ibobs = {'ipa0': self._ipa0, 'ipa1': self._ipa1, 'dbe': self._dbe}
        self._boards = {'ipa0': self._ipa0, 'ipa1': self._ipa1, 'dbe': self._dbe, 'bee2': self._bee2}
        self._mapping = dict((a, i) for i, a in enumerate(self._antennas))#{6:0, 1:1, 2:2, 3:3, 4:4, 5:5, 7:6, 8:7}
        self._input_ibob_map = {0: [self._ipa0, 0], 1: [self._ipa0, 1],
                                2: [self._ipa0, 2], 3: [self._ipa0, 3],
                                4: [self._ipa1, 0], 5: [self._ipa1, 1],
                                6: [self._ipa1, 2], 7: [self._ipa1, 3]}
        self._param_handlers = {'_thresholds' : self._thresh_handler,
                                '_phases': self._phase_handler,
                                '_phase_offsets': self._phase_offset_handler,
                                '_delays': self._delay_handler,
                                '_delay_offsets': self._delay_offset_handler,
                                '_gains': self._gain_handler}
        self._command_set.update({2 : self.get_mapping,
                                  3 : self.set_mapping,
                                  5 : self.load_walsh_table,
                                  6 : self.clear_walsh_table,
                                  7 : self.delay_tracker,
                                  12 : self.reset_xaui,
                                  13 : self.arm_sync,
                                  14 : self.noise_mode,
                                  15 : self._board,
                                  16 : self.get_reference,
                                  17 : self.setup_fstopping,
                                  18 : self.start_fstopping,
                                  19 : self.stop_fstopping,
                                  36 : self.get_delays,
                                  37 : self.set_delays,
                                  38 : self.get_phases,
                                  39 : self.set_phases,
                                  40 : self.get_gains,
                                  41 : self.set_gains,
                                  42 : self.get_thresholds,
                                  43 : self.set_thresholds,
                                  64 : self.get_dbe_gains,
                                  65 : self.set_dbe_gains,
                                  96 : self.operations_log,
                                  128 : self.get_correlation})
        self.setup()
        #self.sync_all()
        self.start_checks_loop(30.0)
        #self.start_delay_tracker(4.0)
        self.start_phase_tracker(1)

    def shutdown(self, args):
        self.stop_checks_loop()
        self.stop_delay_tracker()
        self.stop_phase_tracker()
        return BasicTCPServer.shutdown(self, args)

    @info
    def reset_xaui(self, args):
        lev = BYTE.unpack(args[0])[0]
        for board in [self._dbe, self._bee2]:
            board.regwrite('xaui_rst', lev)
            board.logger.info('xaui_rst=%d' % board.regread('xaui_rst'))
            board.regwrite('xaui_rst', 0)
            board.logger.info('xaui_rst=%d' % board.regread('xaui_rst'))
        return BYTE.pack(0)

    @info
    def arm_sync(self, args):
        self.sync_all()
        return BYTE.pack(0)

    @debug
    def _setup_IPA(self, ipanum):
        ipa = self._ibobs['ipa%d'%ipanum]
        ipa.regwrite('insel', 0)
        ipa.regwrite('smasel', 0)
        ipa.regwrite('monsel', 2)
        ipa.regwrite('walsh/colsel', 31 | 30*2**8 | 29*2**16 | 28*2**24)
        ipa.regwrite('walsh/syncsel', 1)
        ipa.regwrite('walsh/sign90', 1)
        #ipa.regwrite('start_xaui', 1)

    @debug
    def _setup_DBE(self):
        self._dbe.regwrite('insel', 0)

    @debug
    def _setup_BEE2(self):
        #self._bee2.progdev('bee2_complex_corr.bof')
        self._bee2.regwrite('refant', self._mapping[self._reference_antenna])
        self._bee2.regwrite('syncsel', 2)

    @info
    def _setup_fringe_stopping(self):
        with RLock():
            queues = {}
            self._dds.query_dds(None)
            query = self._dds.query.copy()
            for name, ibob in self._ipas.iteritems():
                sync_time = time() + 1 # second from now
                H = self._dds.get_hour_angle(query['rA'],
                                             query['refLong'],
                                             sync_time)
                h_cmd = 'sync_hour_angle {} 0'.format(int(H*10**5))
                while time()<sync_time:
                    pass
                ibob.tinysh(h_cmd)
                ibob.logger.info(h_cmd)
                fstop_cmd = 'set_fstop {} 0'.format(int(self._fstop*10**5))
                ibob.tinysh(fstop_cmd)
                ibob.logger.info(fstop_cmd)
            for ant in self._antennas:
                B = -query['b'][ant] * 10**9
                C = -query['c'][ant] * 10**9
                A = 4000.0 - query['a'][ant] * 10**9
                ibob, ibob_input = self._input_ibob_map[self._mapping[ant]]
                cmd = 'set_delay_triplet {input} {A} {B} {C}'.format(
                    input=ibob_input, A=int(A*10**5), B=int(B*10**5), C=int(C*10**5)
                    )
                queue = ibob.tinysh(cmd)
                queue.get()
                ibob.logger.info(cmd)
                
    @debug
    def _sync_1pps(self):
        with RLock():
            queues = {}
            for name, ibob in self._ibobs.iteritems():
                queues[ibob] = ibob.tinysh('arm1pps')
            for ibob, queue in queues.iteritems():
                last_line = queue.get().splitlines()[-2]
                # should check if they were actually sync'ed
                # here instead of just logging about it
                ibob.logger.info(last_line)

    @debug
    def _sync_sowf(self):
        with RLock():
            queues = {}
            for name, ibob in self._ipas.iteritems():
                queues[ibob] = ibob.tinysh('armsowf')
            for ibob, queue in queues.iteritems():
                last_line = queue.get().splitlines()[-2]
                # should check if they were actually sync'ed
                # here instead of just logging about it
                ibob.logger.info(last_line)

    @debug
    def _check_XAUI(self):
        boards = {'DBE': (self._dbe, '/'),
                  'BEE': (self._bee2, '_')}
        for board_name, info in boards.iteritems():
            board, regsep = info
            msg = "{board}/{0}: (period {1})(syncs {2})(errors: {3})(linkdowns: {4})"
            for xaui in ['xaui0', 'xaui1']:
                prefix = xaui+regsep
                with RLock():
                    linkdown = board.regread(prefix+'rx_linkdown')
                    period = board.regread(prefix+'period')
                    sync_cnt = board.regread(prefix+'sync_cnt')
                    period_err_cnt = board.regread(prefix+'period_err_cnt')
                    linkdown_cnt = board.regread(prefix+'linkdown_cnt')
                if linkdown:
                    self.logger.error('{board} {0} link is down!'.format(xaui, board=board_name))
                board.logger.info(msg.format(xaui, period, sync_cnt, period_err_cnt,
                                            linkdown_cnt, board=board_name))

    @info
    def setup(self):
        self._setup_IPA(0)
        self._setup_IPA(1)
        self._setup_DBE()
        self._setup_BEE2()

    @debug
    def sync_all(self):
        self._sync_1pps()
        self._sync_sowf()

    @info
    def run_checks(self):
        self._check_XAUI()

    @debug
    def run_delay_tracker(self, delays):
        for a in self._antennas:
            self.set_value('_delays', a, delays[a])

    @debug
    def run_fringe_stopper(self, phases):
        for a in self._antennas:
            self.set_value('_phases', a, phases[a])

    @debug
    def _checks_loop(self):
        while not self._checks_stopevent.isSet():
            with RLock():
                checks_period = self._checks_period
            self.run_checks()
            self._checks_stopevent.wait(checks_period)

    @debug
    def _delay_tracker(self):
        count = 0
        logger = logging.getLogger("DelayTracker")
        while not self._delay_tracker_stopevent.isSet():
            start = time()
            if count%20 == 0:
                try:
                    self._dds.reconnect()
                    self._dds.query_dds(None)
                except:
                    logger.error("Problem communicating with the DDS!")
                    self._delay_tracker_stopevent.wait(20)
                    continue
            with RLock():
                fstop = self._fstop
                period = self._delay_tracker_period
                delays = self._dds.get_delays(start+period)
            phases = dict((a, sign(fstop)*(360*d*abs(fstop) % 360)) for a, d in delays.iteritems())
            count += 1
            while time() < start+period:
                self._delay_tracker_stopevent.wait(period/10.)
            with RLock():
                self.run_delay_tracker(delays)
                self.run_fringe_stopper(phases)
            logger.info('|'.join('%d:%.2f'%(a, d) for a, d in delays.iteritems() if a in self._antennas))
            #logger.info('|'.join('%d:%.2f'%(a, p) for a, p in phases.iteritems() if a in self._antennas))
            
    @debug
    def start_checks_loop(self, period):
        self.logger.info('starting check loop at %s (period %.2f)' % (asctime(), period))
        self._checks_period = period
        self._checks_thread.start()

    @debug
    def start_delay_tracker(self, period):
        self.logger.info('starting delay tracker at %s (period %.2f)' % (asctime(), period))
        self._delay_tracker_thread = Thread(target=self._delay_tracker)
        self._delay_tracker_stopevent.clear()
        self._delay_tracker_period = period
        self._delay_tracker_thread.start()

    @debug
    def start_phase_tracker(self, period):
        self.logger.info('starting phase tracker at %s (period %.2f)' % (asctime(), period))
        self._phase_tracker = PhaseTracker(self, '0.0.0.0', self._phase_tracker_port)
        self._correlator.add_subscriber(('0.0.0.0', self._phase_tracker_port))
        self._phase_tracker.start(period)

    @debug
    def stop_checks_loop(self):
        self._checks_stopevent.set()
        self._checks_thread.join()

    @debug
    def stop_delay_tracker(self):
        self._delay_tracker_stopevent.set()
        if self._delay_tracker_thread.isAlive():
            self._delay_tracker_thread.join()

    @debug
    def stop_phase_tracker(self):
        self._phase_tracker.stop()
        self._correlator.remove_subscriber(('0.0.0.0', self._phase_tracker_port))

    @info
    def delay_tracker(self, args):
        """ inst.noise_mode(bool)
        If bool=True, selects internally generated noise.
        If bool=False, selects external ADC data (normal)."""
        on = unpack('!B', args[0])[0]
        if self._delay_tracker_thread.isAlive():
            if on:
                self.logger.warning("delay tracker already started!")
                return SBYTE.pack(-1)
            else:
                self.stop_delay_tracker()
                self.logger.info("delay tracker stopped")
        else:
            if not on:
                self.logger.warning("delay tracker has not been started!")
                return SBYTE.pack(-2)
            else:
                self.start_delay_tracker(1)
                self.logger.info("delay tracker started")
        return SBYTE.pack(0)

    @info
    def clear_walsh_table(self, args):
        for name, ibob in self._ipas.iteritems():
            for step in range(64):
                ibob.bramwrite('walsh/table/90', 0, location=step)
                ibob.bramwrite('walsh/table/180', 0, location=step)
        return SBYTE.pack(0)

    @info
    def load_walsh_table(self, args):
        try:
            walsh_table = self._dds.get_walsh_pattern()
        except:
            self.logger.error("problem communicating with the DDS!")
            return SBYTE.pack(-1)
        for step in range(64):
            cur90 = dict.fromkeys(self._ipas.values(), 0)
            cur180 = dict.fromkeys(self._ipas.values(), 0)
            for antenna, steps in walsh_table.items():
                try:
                    ibob, col = self._input_ibob_map[self._mapping[antenna]]
                except KeyError:
                    self.logger.warning("antenna %d not in array" % antenna)
                    walsh_table.pop(antenna)
                    continue
                bit90 = steps[step] & 1 # extract bottom bit
                bit180 = (steps[step] >> 1) & 1 # extract top bit
                cur90[ibob] = cur90[ibob] | (bit90 << col)
                cur180[ibob] = cur180[ibob] | (bit180 << col)
            for ibob in self._ipas.values():
                ibob.bramwrite('walsh/table/90', cur90[ibob], location=step)
                ibob.bramwrite('walsh/table/180', cur180[ibob], location=step)
        return SBYTE.pack(0)

    @debug
    def _delay_handler(self, mode, antenna, ibob, ibob_input, value=None):
        adc_per_ns = 1.024
        regname = 'delay%d' % ibob_input
        if mode=='get':
            regvalue = ibob.regread(regname)
            if regvalue < 64:
                regvalue += 2**17
            return ((regvalue-64)/(16*adc_per_ns)) % (2**17)
        elif mode=='set':
            total = value + self._delay_offsets[antenna]
            regvalue = (round(16*adc_per_ns*total)+64) % (2**17)
            ibob.regwrite(regname, int(regvalue))
            return self._delay_handler('get', antenna, ibob, ibob_input)

    @debug
    def _delay_offset_handler(self, mode, antenna, ibob, ibob_input, value=None):
        if mode=='get':
            return ibob.get_delay_offset(ibob_input)
        elif mode=='set':
            ibob.set_delay_offset(ibob_input, value)
            return self._delay_offset_handler('get', antenna, ibob, ibob_input)

    @debug
    def _phase_handler(self, mode, antenna, ibob, ibob_input, value=None):
        deg_per_step = 360./2**12
        regname = 'phase%d' % ibob_input
        if mode=='get':
            regvalue = ibob.regread(regname)
            return regvalue * deg_per_step
        elif mode=='set':
            total = value + self._phase_offsets[antenna]
            regvalue = round(total/deg_per_step)
            ibob.regwrite(regname, int(regvalue))
            return self._phase_handler('get', antenna, ibob, ibob_input)

    @debug
    def _phase_offset_handler(self, mode, antenna, ibob, ibob_input, value=None):
        if mode=='get':
            return ibob.get_phase_offset(ibob_input)
        elif mode=='set':
            ibob.set_phase_offset(ibob_input, value)
            return self._phase_offset_handler('get', antenna, ibob, ibob_input)

    @debug
    def _gain_handler(self, mode, antenna, ibob, ibob_input, value=None):
        regname = 'gain%d' % ibob_input
        if mode=='get':
            regvalue = ibob.regread(regname)
            return (regvalue % 256) * 2**-7
        elif mode=='set':
            regvalue = round(value * 2**7) % 256
            ibob.regwrite(regname, int(regvalue))
            return self._gain_handler('get', antenna, ibob, ibob_input)

    @debug
    def _thresh_handler(self, mode, antenna, ibob, ibob_input, value=None):
        regname = 'quant/thresh%d' % ibob_input
        if mode=='get':
            return ibob.regread(regname)
        elif mode=='set':
            ibob.regwrite(regname, value)
            return self._thresh_handler('get', antenna, ibob, ibob_input)

    def get_value(self, param, antenna):
        try:
            ibob, ibob_input = self._input_ibob_map[self._mapping[antenna]]
            return self._param_handlers[param]('get', antenna, ibob, ibob_input)
        except KeyError:
            return BasicTCPServer.get_value(self, param, antenna)

    def set_value(self, param, antenna, value):
        ibob, ibob_input = self._input_ibob_map[self._mapping[antenna]]
        try:
            return self._param_handlers[param]('set', antenna, ibob, ibob_input, value)
        except KeyError:
            return BasicTCPServer.set_value(self, param, antenna, value)

    @info
    def get_delays(self, args):
        """ inst.get_delays(ant=[1,2,3,4,...]) -> values=[100.0, 100.0, 100.0,...]
        Get the current delays (with delay offsets included). """
        return self.get_values('delays', args, type='f')

    @info
    def set_delays(self, args):
        """ inst.set_delays(ant_val=[1,1.0,2,1.0,3,1.0,...]) -> values=[0,1,2,...]
        Set the delays, this may be overriden by the delay tracking loop.
        If you're looking to just add a delay offset use set_delay_offsets instead
        and wait for the delay loop to iterate."""
        return self.set_values('delays', args, type='f')

    @info
    def get_phases(self, args):
        """ inst.get_phases(ant=[1,2,3,4,...]) -> values=[100.0, 100.0, 100.0,...]
        Get the current delays (with delay offsets included). """
        return self.get_values('phases', args, type='f')

    @info
    def set_phases(self, args):
        """ inst.set_phases(ant_val=[1,1.0,2,1.0,3,1.0,...]) -> values=[0,1,2,...]
        Set the phases, this may be overriden by the delay tracking loop.
        If you're looking to just add a phase offset use set_phase_offsets instead
        and wait for the delay loop to iterate."""
        return self.set_values('phases', args, type='f')

    @info
    def get_mapping(self, args):
        """ inst.get_mapping(ant=[1,2,3,4,...]) -> values=[4,5,6,7,...]
        Get mapping of antennas to input numbers. """
        return self.get_values('mapping', args, type='B')

    @info
    def set_mapping(self, args):
        """ inst.set_delay_offsets(ant_val=[1,0,2,1,3,2,...]) -> values=[0,1,2,...]
        Set the mapping of antennas to input numbers. """
        return self.set_values('mapping', args, type='B')

    @info
    def get_gains(self, args):
        """ inst.get_gains(ant=[1,2,3,4,...]) -> values=[1.0, 1.0, 1.0,...]
        Get the pre-sum gains. """
        return self.get_values('gains', args, type='f')

    @info
    def set_gains(self, args):
        """ inst.set_gains(ant_val=[1,1.0,2,1.0,3,1.0,...]) -> values=[0,1,2,...]
        Set the pre-sum gains. """
        return self.set_values('gains', args, type='f')

    @info
    def get_dbe_gains(self, args):
        """ inst.get_dbe_gains(ant=[0,1,2,3,4,...,15]) -> values=[1.0, 1.0, 1.0,...]
        Get the DBE channelizer gains. """
        gainctrl0 = self._dbe.bramdump('pol0/gainctrl0', 8)
        gainctrl1 = self._dbe.bramdump('pol0/gainctrl1', 8)
        changains = [None] * 16
        changains[::2] = gainctrl0
        changains[1::2] = gainctrl1
        return pack('!B16I', 0, *changains)

    @info
    def set_dbe_gains(self, args):
        """ inst.set_dbe_gains(ant_val=[0,1.0,1,1.0,2,1.0,...]) -> values=[0,1,2,...]
        Set the DBE channelizer gains. """
        changains = unpack('!16I', args)
        for chan, gain in enumerate(changains):
            self._dbe.bramwrite('pol0/gainctrl%d' %(chan%2), gain, int(chan/2.))
        return self.get_dbe_gains('')

    @debug
    def get_correlation(self, args):
        """ inst.get_correlation() -> single_udp_packet
        Gets the next correlation from the correlator client, returns -1 if there is no
        correlation ready. Note: it is preferable to use a direcy UDP client instead of
        this function but it is provided to enable secure remote operations."""
        try:
            pkt = self._correlator_client._request('')
            return pack('!B%ds' % len(pkt), 0, pkt)
        except NoCorrelations:
            return SBYTE.pack(-1)

    @info
    def get_thresholds(self, args):
        """ inst.get_thresholds(ant=[1,2,3,4,...]) -> values=[16, 16, 16,...]
        Get the threshold values used by the 2-bit quantizers. """
        return self.get_values('thresholds', args, type='B')

    @info
    def set_thresholds(self, args):
        """ inst.set_thresholds(ant_val=[1,16,2,16,3,16,...]) -> values=[0,1,2,...]
        Set the 2-bit quantization thresholds. """
        return self.set_values('thresholds', args, type='B')

    @info
    def noise_mode(self, args):
        """ inst.noise_mode(bool)
        If bool=True, selects internally generated noise.
        If bool=False, selects external ADC data (normal)."""
        insel = unpack('!B', args[0])[0]
        seed = (randint(0, 2**16-1) << 16) + randint(0, 2**16-1)
        for name, ibob in self._ipas.iteritems():
            ibob.regwrite('noise/seed/0', seed)
            ibob.regwrite('noise/seed/1', seed)
            ibob.regwrite('noise/seed/2', seed)
            ibob.regwrite('noise/seed/3', seed)
        for name, ibob in self._ipas.iteritems():
            ibob.regwrite('noise/arm', 0)
        for name, ibob in self._ipas.iteritems():
            ibob.regwrite('noise/arm', 0x1111)
        for name, ibob in self._ipas.iteritems():
            ibob.regwrite('insel', insel*0x55555555)
        return SBYTE.pack(0)

    @debug
    def _board(self, args):
        """ inst._board(board, cmd)
        This allows the client to send commands and receive
        responses from the server's underlying iBOBs. Note: this
        should be used cautiously, if you find yourself using this often
        you should just write a server command."""
        queues = {}
        board_re, sep, cmd = args.partition(' ')
        for name, board in self._boards.iteritems():
            if re.match(board_re, name):
                queues[name] = board.tinysh(cmd)
        response = ''
        for name, queue in queues.iteritems():
            response += queue.get(20)
            response += "\r### {0} {1} @({2})\n\r".format(name, cmd, asctime()) 
        return SBYTE.pack(0) + response

    @debug
    def get_reference(self, args):
        """ inst.get_reference() -> err_code
        Returns the current reference antenna. """
        return pack('!bB', 0, self._reference_antenna)

    @debug
    def setup_fstopping(self, args):
        """ inst.setup_fstopping() -> err_code
        Setup the IPA iBOBs to perform fringe stopping.
        The only argument is the fringe stopping rate in GHz."""
        self._fstop = unpack('!f', args)[0]
        try:
            self._setup_fringe_stopping()
            return pack('!b', 0)
        except:
            self.logger.error('Error setting up fringe stopping!')
            return pack('!b', -1)

    @debug
    def start_fstopping(self, args):
        """ inst.start_fstopping() -> err_code
        Enable fringe stopping in the IPA iBOBs."""
        for name, ibob in self._ipas.iteritems():
            fstop_cmd = 'set_fstop {} 1'.format(int(self._fstop*10**5))
            ibob.tinysh(fstop_cmd)
            ibob.logger.info(fstop_cmd)
        return pack('!b', 0)

    @debug
    def stop_fstopping(self, args):
        """ inst.stop_fstopping() -> err_code
        Disable fringe stopping in the IPA iBOBs."""
        for name, ibob in self._ipas.iteritems():
            fstop_cmd = 'set_fstop {} 0'.format(int(self._fstop*10**5))
            ibob.tinysh(fstop_cmd)
            ibob.logger.info(fstop_cmd)
        return pack('!b', 0)

    def get_integration_time(self, args):
        """ inst.get_integration_time() -> err_code
        Overloaded method to get integration time on the BEE2."""
        itime = self._bee2.regread('integ_time')
        PERIOD = PERIOD_SYNCSEL[self._bee2.regread('syncsel')]
        self._integration_time = itime * PERIOD
        return pack('!bf', 0, self._integration_time)

    def set_integration_time(self, args):
        """ inst.set_integration_time(time) -> err_code
        Overloaded method to set the integration time on the BEE2."""
        itime = unpack('!f', args)[0]
        PERIOD = PERIOD_SYNCSEL[self._bee2.regread('syncsel')]
        integ_time = round(itime / PERIOD)
        self._bee2.regwrite('integ_time', integ_time)
        self._integration_time = integ_time
        return SBYTE.pack(0)

    @debug
    def operations_log(self, args):
        """ inst.operations_log(level, logger_name, msg)
        This allows any client to send log messages to the
        given logger at any level. """
        level, logger_msg = unpack('!B%ds' %(len(args)-1), args)
        logger_name, msg = logger_msg.split('\r')
        logger = logging.getLogger(logger_name)
        logger.log(level, msg)
        return SBYTE.pack(0)