Ejemplo n.º 1
0
def main_loop():
    for bw_mhz in bws:
        if set_bw(bw_mhz):
            return

        for if_ghz in ifs:
            lo2_mhz = if_ghz * 1e3 + 2500
            if bw_mhz == 250:
                lo2_mhz += 125
            if set_lo2(lo2_mhz):
                return

            agilent.set_hz_dbm(if_ghz * 1e9, dbms[0])
            if if_setup(2, bw_mhz,
                        if_ghz):  # level only (won't actually set bw/lo2)
                return

            for dbm in dbms:
                agilent.set_dbm(dbm)
                time.sleep(0.05)
                # start IFTASK action
                transid = drama.obey("IFTASK@if-micro",
                                     "WRITE_TP2",
                                     FILE="NONE",
                                     ITIME=0.1)
                # get IFTASK reply
                msg = transid.wait(5)
                if msg.reason != drama.REA_COMPLETE or msg.status != 0:
                    logging.error('bad reply from IFTASK.WRITE_TP2: %s', msg)
                    return 1
                sys.stdout.write('%.2f %g %g' % (dbm, if_ghz, bw_mhz))
                for j, dcm in enumerate(dcms):
                    sys.stdout.write(' %g' % (msg.arg['POWER%d' % (dcm)]))
                sys.stdout.write('\n')
                sys.stdout.flush()
Ejemplo n.º 2
0
def do_ghz(ghz):
    '''Optimize power output for given frequency.'''
    sys.stderr.write('%.6f: ' % (ghz))
    sys.stderr.flush()
    delay = 0.1  # generous sleep since pmeter takes 50ms/read
    dbm = args.start_dbm
    if dbm_table:
        interp_row = namakanui.ini.interp_table(dbm_table, ghz)
        dbm = interp_row.dbm
    agilent.set_hz_dbm(ghz * 1e9, dbm)
    pmeter.send(b'freq %gGHz\n' % (ghz))  # for power sensor calibration tables
    # assuming meter and generator are both reasonably accurate,
    # we only need to iterate a few times to get close to optimal setting.
    #
    # RMB 20200930: add some basic gain estimation to speed convergence.
    # we don't really trust the estimate, so bias toward 1.0
    # by multiplying its logarithm with a scale factor <1.
    prev_dbm = dbm
    prev_power = 0.0
    gain_bias = 0.5  # multiplied with log(gain) to skew toward 1
    for i in range(5):
        time.sleep(delay)
        power = read_power()
        err = args.target_dbm - power
        sys.stderr.write('(%.2f, %.3f, ' % (dbm, power))
        #sys.stderr.flush()
        if abs(err) <= deadband:
            break
        dout = dbm - prev_dbm
        dpow = power - prev_power
        prev_dbm = dbm
        prev_power = power
        gain = 1.0
        if dout != 0.0 and dpow != 0.0 and sign(dout) == sign(dpow):
            gain = dpow / dout
            gain = 10**(math.log10(gain) * gain_bias)
        sys.stderr.write('%.3f)' % (gain))
        sys.stderr.flush()
        dbm += err / gain
        # NOTE: even if we rail, don't bail out early -- might have overshot
        if dbm < agilent.safe_dbm:
            dbm = agilent.safe_dbm
        if dbm > agilent.max_dbm:
            dbm = agilent.max_dbm
        agilent.set_dbm(dbm)
    time.sleep(delay)
    power = read_power()
    sys.stderr.write('(%.2f, %.3f)\n' % (dbm, power))
    sys.stderr.flush()
    print('%.3f %.2f %.3f' % (ghz, dbm, power))
    sys.stdout.flush()
Ejemplo n.º 3
0
    floog = agilent.floog * [1.0, -1.0][lock_polarity]  # [below, above]
    sys.stdout.write('#lo_ghz sig_ghz sig_dbm pmeter_dbm\n')
    sys.stdout.flush()
    logging.info('looping over entries in %s', args.table)
    for line in open(args.table):
        line = line.strip()
        if not line:
            continue
        if line.startswith('#'):
            continue
        vals = [float(x) for x in line.split()]
        lo_ghz = vals[0]
        dbm = vals[1]
        fyig = lo_ghz / (cold_mult * warm_mult)
        fsig = (fyig * warm_mult + floog) / agilent.harmonic
        agilent.set_hz_dbm(fsig * 1e9, dbm)
        pmeter.send(b'freq %gGHz\n' %
                    (fsig))  # for power sensor calibration tables
        time.sleep(0.1)  # generous sleep since pmeter takes 50ms/read
        power = read_power()
        sys.stdout.write('%.3f %.6f %.2f %.3f\n' % (lo_ghz, fsig, dbm, power))
        sys.stdout.flush()
else:
    # loop over ghz range for each dbm
    ghzs = namakanui.util.parse_range(args.ghz)
    dbms = namakanui.util.parse_range(args.dbm)
    sys.stdout.write('#sig_ghz sig_dbm pmeter_dbm\n')
    sys.stdout.flush()
    logging.info('looping over ghz range %s for each dbm in %s', args.ghz,
                 args.dbm)
    for dbm in dbms:
Ejemplo n.º 4
0
# output file header
sys.stdout.write(time.strftime('# %Y%m%d %H:%M:%S HST\n', time.localtime()))
sys.stdout.write('# %s\n' % (sys.argv))
sys.stdout.write('#\n')
sys.stdout.write('#att pow\n')
sys.stdout.flush()


def read_power():
    '''Return power reading in dBm.'''
    #raise RuntimeError('TODO')
    pmeter.send(b'fetch?\n')
    return float(pmeter.recv(256))


agilent.set_hz_dbm(args.ghz * 1e9, args.dbm)
att = photonics.max_att + 1
pmeter.send(b'freq %gGHz\n' % (args.ghz))  # for power sensor cal tables

while att > 0:
    att -= 1
    delay = 0.1  # generous sleep since pmeter takes 50ms/read
    photonics.set_attenuation(att)
    time.sleep(delay)
    power = read_power()
    sys.stdout.write('%d %.2f\n' % (att, power))
    sys.stdout.flush()

att = -1
while att < photonics.max_att:
    att += 1
Ejemplo n.º 5
0
def CART_TUNE(msg):
    '''Tune a cartridge, after setting reference frequency.  Arguments:
            BAND:      One of 3,6,7
            LO_GHZ:    Local oscillator frequency in gigahertz
            VOLTAGE:   Desired PLL control voltage, [-10,10].
                       If not given, voltage will not be adjusted
                       following the initial lock.
            LOCK_ONLY: if True, bias voltage, PA, LNA, and magnets will not
                       be adjusted after locking the receiver.
       
       TODO: lock polarity (below or above reference) could be a parameter.
             for now we just read back from the cartridge task.
       
       TODO: save dbm offset and use it for close frequencies.
    '''
    log.debug('CART_TUNE(%s)', msg.arg)
    if not initialised:
        raise drama.BadStatus(drama.APP_ERROR, 'task needs INITIALISE')
    args, kwargs = drama.parse_argument(msg.arg)
    band, lo_ghz, voltage, lock_only = cart_tune_args(*args, **kwargs)
    if band not in [3, 6, 7]:
        raise drama.BadStatus(drama.INVARG,
                              'BAND %d not one of [3,6,7]' % (band))
    if not 70 <= lo_ghz <= 400:  # TODO be more specific
        raise drama.BadStatus(drama.INVARG,
                              'LO_GHZ %g not in [70,400]' % (lo_ghz))
    if voltage and not -10 <= voltage <= 10:
        raise drama.BadStatus(drama.INVARG,
                              'VOLTAGE %g not in [-10,10]' % (voltage))

    if ifswitch.get_band() != band:
        log.info('setting IF switch to band %d', band)
        # reduce power first
        agilent.set_dbm(agilent.safe_dbm)
        if photonics:
            photonics.set_attenuation(photonics.max_att)
        ifswitch.set_band(band)

    cartname = cartridge_tasknames[band]

    # TODO don't assume pubname is DYN_STATE
    dyn_state = drama.get(cartname, "DYN_STATE").wait().arg["DYN_STATE"]
    lock_polarity = dyn_state['pll_sb_lock']  # 0=below_ref, 1=above_ref
    lock_polarity = -2.0 * lock_polarity + 1.0

    fyig = lo_ghz / (cold_mult[band] * warm_mult[band])
    fsig = (fyig * warm_mult[band] +
            agilent.floog * lock_polarity) / agilent.harmonic
    if photonics:
        dbm = agilent.interp_dbm(0, fsig)
        att = photonics.interp_attenuation(band, lo_ghz)
        log.info('setting photonics attenuator to %d counts', att)
    else:
        dbm = agilent.interp_dbm(band, lo_ghz)
    dbm = min(dbm, agilent.max_dbm)
    log.info('setting agilent to %g GHz, %g dBm', fsig, dbm)
    # set power safely while maintaining lock, if possible.
    hz = fsig * 1e9
    if photonics:
        if att < photonics.state['attenuation']:
            agilent.set_hz_dbm(hz, dbm)
            photonics.set_attenuation(att)
        else:
            photonics.set_attenuation(att)
            agilent.set_hz_dbm(hz, dbm)
    else:
        agilent.set_hz_dbm(hz, dbm)
    agilent.set_output(1)
    agilent.update(publish_only=True)
    time.sleep(0.05)  # wait 50ms; for small changes PLL might hold lock
    vstr = ''
    band_kwargs = {"LO_GHZ": lo_ghz}
    if voltage is not None:
        vstr += ', %g V' % (voltage)
        band_kwargs["VOLTAGE"] = voltage
    if lock_only:
        vstr += ', LOCK_ONLY'
        band_kwargs["LOCK_ONLY"] = lock_only
    log.info('band %d tuning to LO %g GHz%s...', band, lo_ghz, vstr)

    pll_if_power = 0.0

    # tune in a loop, adjusting signal to get pll_if_power in proper range.
    # adjust attenuator if present, then adjust output dBm.
    if photonics:
        orig_att = att
        att_min = max(0, att - 24)  # limit 2x nominal power
        tries = 0
        max_tries = 5
        while True:
            tries += 1
            msg = drama.obey(cartname, 'TUNE', **band_kwargs).wait()
            if msg.reason != drama.REA_COMPLETE:
                raise drama.BadStatus(drama.UNEXPMSG,
                                      '%s bad TUNE msg: %s' % (cartname, msg))
            elif msg.status == drama.INVARG:
                # frequency out of range
                agilent.set_dbm(agilent.safe_dbm)
                photonics.set_attenuation(photonics.max_att)
                raise drama.BadStatus(msg.status,
                                      '%s TUNE failed' % (cartname))
            elif msg.status != 0:
                # tune failure, raise the power and try again
                old_att = att
                att -= 8
                if att < att_min:
                    att = att_min
                if att == old_att or tries > max_tries:  # stuck at the limit, time to give up
                    photonics.set_attenuation(orig_att)
                    raise drama.BadStatus(msg.status,
                                          '%s TUNE failed' % (cartname))
                log.warning(
                    'band %d tune failed, retuning at %d attenuator counts...',
                    band, att)
                photonics.set_attenuation(att)
                time.sleep(0.05)
                continue
            # we got a lock.  check the pll_if_power level.
            # TODO don't assume pubname is DYN_STATE
            dyn_state = drama.get(cartname,
                                  "DYN_STATE").wait().arg["DYN_STATE"]
            pll_if_power = dyn_state['pll_if_power']
            if tries > max_tries:  # avoid bouncing around forever
                break
            old_att = att
            if pll_if_power < -2.5:  # power too high
                att += 4
            elif pll_if_power > -0.7:  # power too low
                att -= 4
            else:  # power is fine
                break
            if att < att_min:
                att = att_min
            elif att > photonics.max_att:
                att = photonics.max_att
            if att == old_att:  # stuck at the limit, so it'll have to do
                break
            log.warning(
                'band %d bad pll_if_power %.2f; retuning at %d attenuator counts...',
                band, pll_if_power, att)
            photonics.set_attenuation(att)
            time.sleep(0.05)
        log.info(
            'band %d tuned to LO %g GHz, pll_if_power %.2f at %.2f dBm, %d attenuator counts',
            band, lo_ghz, pll_if_power, dbm, att)
    #else:
    if not (-2.5 <= pll_if_power <=
            -0.7):  # adjust dbm after photonics if needed
        orig_dbm = dbm
        orig_att = att if photonics else 0
        dbm_max = min(agilent.max_dbm, dbm + 3.0)  # limit to 2x nominal power
        att_min = max(0, att - 6) if photonics else 0  # ditto
        tries = 0
        max_tries = 5
        while True:
            tries += 1
            msg = drama.obey(cartname, 'TUNE', **band_kwargs).wait()
            if msg.reason != drama.REA_COMPLETE:
                raise drama.BadStatus(drama.UNEXPMSG,
                                      '%s bad TUNE msg: %s' % (cartname, msg))
            elif msg.status == drama.INVARG:
                # frequency out of range
                agilent.set_dbm(agilent.safe_dbm)
                raise drama.BadStatus(msg.status,
                                      '%s TUNE failed' % (cartname))
            elif msg.status != 0:
                # tune failure, raise the power and try again
                old_dbm = dbm
                dbm += 1.0
                if dbm > dbm_max:
                    dbm = dbm_max
                if dbm == old_dbm or tries > max_tries:  # stuck at the limit, time to give up
                    agilent.set_dbm(orig_dbm)
                    raise drama.BadStatus(msg.status,
                                          '%s TUNE failed' % (cartname))
                log.warning('band %d tune failed, retuning at %.2f dBm...',
                            band, dbm)
                agilent.set_dbm(dbm)
                time.sleep(0.05)
                continue
            # we got a lock.  check the pll_if_power level.
            # TODO don't assume pubname is DYN_STATE
            dyn_state = drama.get(cartname,
                                  "DYN_STATE").wait().arg["DYN_STATE"]
            pll_if_power = dyn_state['pll_if_power']
            if tries > max_tries:  # avoid bouncing around forever
                break
            old_dbm = dbm
            if pll_if_power < -2.5:  # power too high
                dbm -= 0.5
            elif pll_if_power > -0.7:  # power too low
                dbm += 0.5
            else:  # power is fine
                break
            if dbm > dbm_max:
                dbm = dbm_max
            elif dbm < -20.0:
                dbm = -20.0
            if dbm == old_dbm:  # stuck at the limit, so it'll have to do
                break
            log.warning(
                'band %d bad pll_if_power %.2f; retuning at %.2f dBm...', band,
                pll_if_power, dbm)
            agilent.set_dbm(dbm)
            time.sleep(0.05)
        log.info('band %d tuned to LO %g GHz, pll_if_power %.2f at %.2f dBm.',
                 band, lo_ghz, pll_if_power, dbm)