def _try_att(cart, photonics, lo_ghz, voltage, att, skip_servo_pa, lock_only, delay_secs, sleep, log): '''Helper function used by tune(); set a new attenuation and retune if needed.''' photonics.set_attenuation(att) sleep(delay_secs) photonics.update() cart.update_all() if cart.state['pll_unlock']: _try_tune(cart, lo_ghz, voltage, 'att %d'%(att), skip_servo_pa, lock_only, log)
def _try_dbm(cart, agilent, lo_ghz, voltage, dbm, skip_servo_pa, lock_only, delay_secs, sleep, log): '''Helper function used by tune(); set a new dbm and retune if needed.''' agilent.set_dbm(dbm) sleep(delay_secs) agilent.update() cart.update_all() if cart.state['pll_unlock']: _try_tune(cart, lo_ghz, voltage, 'dbm %.2f'%(dbm), skip_servo_pa, lock_only, log)
def _try_tune(cart, lo_ghz, voltage, msg, skip_servo_pa, lock_only, log): '''Helper function used by tune() to catch and log exceptions. NOTE: Any error except BadLock should set power to safe levels. ''' log.info('cart.tune %.3f ghz, %s', lo_ghz, msg) try: cart.tune(lo_ghz, voltage, skip_servo_pa=skip_servo_pa, lock_only=lock_only) except namakanui.cart.BadLock as e: log.error('tune failed at %.3f ghz, %s', lo_ghz, msg) cart.update_all()
def iv(target, rows): if target == 'hot': p_index = hot_p_index else: p_index = sky_p_index load.move('b%d_%s' % (band, target)) if target == 'hot': cart.tune(lo_ghz, 0.0, skip_servo_pa=True) cart._set_pa([pas[0], pas[1]]) cart.update_all() if namakanui.util.iftask_setup(2, 1000, 6, dcms): # level only return 1 sys.stderr.write('%s: ' % (target)) sys.stderr.flush() mult = 1.0 if band == 6: mult = -1.0 cart._ramp_sis_bias_voltages( [mult * mvs[0], mvs[0], mult * mvs[0], mvs[0]]) for i, mv in enumerate(mvs): if (i + 1) % 20 == 0: sys.stderr.write('%.2f%% ' % (0.0 + 50 * i / len(mvs))) sys.stderr.flush() cart.update_all() # for anyone monitoring for po in range(2): cart.femc.set_sis_voltage(cart.ca, po, 0, mult * mv) cart.femc.set_sis_voltage(cart.ca, po, 1, mv) rows[i][mv_index] = mv # start IFTASK action while we average the mixer current readings transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) # TODO: separate hot/cold mixer currents, or only calc hot for j in range(ua_n): for po in range(2): for sb in range(2): ua = cart.femc.get_sis_current(cart.ca, po, sb) * 1e3 rows[i][ua_avg_index + po * 2 + sb] += abs( ua) # for band 6 rows[i][ua_dev_index + po * 2 + sb] += ua * ua # 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 for j, dcm in enumerate(dcms): rows[i][p_index + j] = msg.arg['POWER%d' % (dcm)] sys.stderr.write('\n') sys.stderr.flush() return 0
def loop(): global i, prev_powers while i < 23: time.sleep(1) i += 1 if i < 0: sys.stderr.write('.') else: sys.stderr.write('%d ' % (i)) sys.stderr.flush() if i % 5 == 4: # retune the cart; make sure it loses the lock dbm = agilent.state['dbm'] while dbm > agilent.safe_dbm and not cart.state['pll_unlock']: agilent.set_dbm(dbm) cart.update_all() dbm -= 0.1 dbm = agilent.safe_dbm agilent.set_dbm(dbm) agilent.set_output(0) time.sleep(0.05) agilent.set_output(1) agilent.set_dbm(orig_dbm) time.sleep(0.05) cart.tune(lo_ghz, 0.0) time.sleep(0.05) transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) cart.update_all() 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 if cart.state['pll_unlock']: logging.error('failed to tune') return powers = [] for dcm in dcms: powers.append(msg.arg['POWER%d' % (dcm)]) for j, (prev, curr) in enumerate(zip(prev_powers, powers)): pdiff = abs((prev - curr) / min(prev, curr)) * 100.0 if pdiff > 1.5: logging.info('%.2f%% jump in DCM %d', pdiff, dcms[j]) # let's write to the output file too, might come in handy sys.stdout.write('# jump DCM %d, %.2f%%\n' % (dcms[j], pdiff)) if i < 0: i = 0 # collect a bit more data, then quit prev_powers = powers output(powers)
def print_dbm(i, dbm): #logging.info('%d dbm %.2f', i, dbm) transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) cart.update_all() logging.info('%d dbm %.2f, pll_if %.3f', i, dbm, cart.state['pll_if_power']) 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 False if cart.state['pll_unlock']: return False sys.stdout.write('%.2f %.3f'%(dbm, cart.state['pll_if_power'])) for dcm in dcms: sys.stdout.write(' %.3f'%(msg.arg['POWER%d'%(dcm)])) sys.stdout.write('\n') sys.stdout.flush() return True
def ip(target, rows, pas): if target == 'hot': p_index = hot_p_index else: p_index = sky_p_index load.move('b%d_%s' % (band, target)) sys.stderr.write('%s: ' % (target)) sys.stderr.flush() for i, pa in enumerate(pas): if (i + 1) % 20 == 0: sys.stderr.write('%.2f%% ' % (100.0 * i / len(pas))) sys.stderr.flush() cart.update_all() cart._set_pa([pa, pa]) rows[i][pa_index] = pa # start IFTASK action while we average the mixer current readings transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) for j in range(ua_n): for po in range(2): for sb in range(2): ua = cart.femc.get_sis_current(cart.ca, po, sb) * 1e3 rows[i][ua_avg_index + po * 2 + sb] += abs( ua) # for band 6 rows[i][ua_dev_index + po * 2 + sb] += ua * ua # 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 for j, dcm in enumerate(dcm_0 + dcm_1): rows[i][p_index + j] = msg.arg['POWER%d' % (dcm)] sys.stderr.write('\n') sys.stderr.flush() return 0
def main_loop(): random.seed() lock_only = 1 sys.stderr.write('starting %d iters\n' % (args.iters)) sys.stderr.flush() for i in range(args.iters): sys.stderr.write('%d ' % (i)) sys.stderr.flush() if i == args.iters // 2: lock_only = 0 sys.stderr.write('\nhalfway\n') sys.stderr.flush() lo_ghz = random.uniform(args.lo_ghz - args.off_ghz, args.lo_ghz + args.off_ghz) if not util.tune(cart, agilent, photonics, lo_ghz, lock_only=lock_only): logging.error('failed to tune to %.6f ghz', lo_ghz) return transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) cart.update_all() 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 if cart.state['pll_unlock']: logging.error('lost lock at %.6f GHz', lo_ghz) return sys.stdout.write('%.6f %d' % (lo_ghz, lock_only)) for key in state_keys: if key not in cart.state: key, sep, index = key.rpartition('_') index = int(index) sys.stdout.write(' %s' % (cart.state[key][index])) else: sys.stdout.write(' %s' % (cart.state[key])) for dcm in dcms: sys.stdout.write(' %.5f' % (msg.arg['POWER%d' % (dcm)])) sys.stdout.write('\n') sys.stdout.flush()
load = namakanui.load.Load(datapath+'load.ini', time.sleep, namakanui.nop) load.move('b%d_hot'%(band)) # tune cartridge, adjusting power as needed if not util.tune(cart, agilent, photonics, lo_ghz): logging.error('failed to tune to %.3f ghz', lo_ghz) sys.exit(1) # determine dbm limits dbm = agilent.state['dbm'] orig_dbm = dbm while dbm < agilent.max_dbm and cart.state['pll_if_power'] > -2.5 and not cart.state['pll_unlock']: dbm += 0.1 agilent.set_dbm(dbm) time.sleep(0.05) cart.update_all() logging.info('+ dbm %.2f, pll_if %.3f', dbm, cart.state['pll_if_power']) dbm = agilent.state['dbm'] hi_dbm = dbm while dbm > agilent.safe_dbm and cart.state['pll_if_power'] < -0.5 and not cart.state['pll_unlock']: dbm -= 0.1 agilent.set_dbm(dbm) time.sleep(0.05) cart.update_all() logging.info('- dbm %.2f, pll_if %.3f', dbm, cart.state['pll_if_power']) lo_dbm = dbm + 0.2 logging.info('dbm limits: [%.2f, %.2f]', lo_dbm, hi_dbm) if lo_dbm >= hi_dbm: logging.error('bad dbm limits, bailing out.') agilent.set_dbm(agilent.safe_dbm) photonics.set_attenuation(photonics.max_att) if photonics else None
def setup_script(band, lock_side, sleep=time.sleep, publish=namakanui.nop): ''' Perform common setup for a standalone script: - Create agilent and set to safe levels with output enabled. - Create photonics (if in namaknui.ini) and set max attenuation. - Create cart(band) and set given lock side. - Zero out pa/lna on unused bands. - Set ifswitch to given band. - Check reference (floog) power level. Returns cart, agilent, photonics. ''' import namakanui.ini import namakanui.cart import namakanui.agilent import namakanui.photonics import namakanui.ifswitch binpath, datapath = get_paths() agilent = namakanui.agilent.Agilent(datapath+'agilent.ini', sleep, publish) agilent.set_dbm(agilent.safe_dbm) agilent.set_output(1) photonics = None nconfig = namakanui.ini.IncludeParser(datapath+'namakanui.ini') if 'photonics_ini' in nconfig['namakanui']: pini = nconfig['namakanui']['photonics_ini'] photonics = namakanui.photonics.Photonics(datapath+pini, sleep, publish) photonics.set_attenuation(photonics.max_att) ifswitch = namakanui.ifswitch.IFSwitch(datapath+'ifswitch.ini', sleep, publish) ifswitch.set_band(band) ifswitch.close() # done with ifswitch cart = namakanui.cart.Cart(band, datapath+'band%d.ini'%(band), sleep, publish) cart.power(1) if lock_side is not None: lock_side = lock_side.lower() if hasattr(lock_side, 'lower') else lock_side lock_side = {0:0, 1:1, 'below':0, 'above':1}[lock_side] cart.femc.set_cartridge_lo_pll_sb_lock_polarity_select(cart.ca, lock_side) cart.state['pll_sb_lock'] = lock_side # cart never updates this # zero out unused carts femc = cart.femc for b in [3,6,7]: if b == band: continue ca = b-1 if not femc.get_pd_enable(ca): continue for po in range(2): femc.set_cartridge_lo_pa_pol_drain_voltage_scale(ca, po, 0) femc.set_cartridge_lo_pa_pol_gate_voltage(ca, po, 0) for sb in range(2): femc.set_lna_enable(ca, po, sb, 0) # this mainly checks that the IF switch really has this band selected cart.update_all() rp = cart.state['pll_ref_power'] if rp < -3.0: raise RuntimeError('PLL ref (FLOOG 31.5 MHz) power high (%.2fV), needs padding'%(rp)) if rp > -0.5: raise RuntimeError('PLL ref (FLOOG 31.5 MHz) power low (%.2fV), check IF switch'%(rp)) return cart, agilent, photonics
def iv(target, rows, pa): if target == 'hot': p_index = hot_p_index else: p_index = sky_p_index load.move('b%d_%s' % (band, target)) # TODO Maybe it's wrong to relevel for each PA; it makes it harder # to compare power between PAs if the leveling is slightly different. # Ambient temperature shouldn't be changing much compared to the # difference between hot load and sky, either. # at the start of a HOT row, re-tune and re-level the power meters # at the nominal values. do not relevel on SKY or y-factor won't work. # actually re-leveling makes it difficult to compare power levels # across sweeps, so skip it. retuning is fine though. # 20200221 but ACTUALLY we're having problems with saturating power levels, # so DO relevel the detectors here. we won't be able to see # relative power levels, but we mostly only do 2 PAs these days and care # more about Y-factor values anyway. if target == 'hot': # dbm/att should already be set from namakanui.util.tune cart.tune(lo_ghz, 0.0, skip_servo_pa=True) cart._set_pa([pa, pa]) cart.update_all() if namakanui.util.iftask_setup(2, 1000, 6, dcms): # level only return 1 sys.stderr.write('%s: ' % (target)) sys.stderr.flush() # NOTE: The two SIS mixers in each polarization module are not really USB and LSB. # Rather the input to one is phase-shifted relative to the other, and their # signals are then combined to produce USB and LSB outputs. So to check the # power output and Y-factor from each mixer individually, the other one needs # to have its output disabled by setting its bias voltage to zero. # Since we still need to smoothly ramp SIS bias voltage for large changes, # we therefore do two separate loops for sis1 and sis2. # TODO: Once we have the mixers optimized individually, we might still need # to optimize their combined outputs. This will require a 2D scan of # mixer bias voltage for each PA setting. # sis1 sb = 0 mult = 1.0 if band == 6: mult = -1.0 if args.zero: cart._ramp_sis_bias_voltages([mult * mvs[0], 0.0, mult * mvs[0], 0.0]) else: cart._ramp_sis_bias_voltages( [mult * mvs[0], nom_v[1], mult * mvs[0], nom_v[3]]) for i, mv in enumerate(mvs): if (i + 1) % 20 == 0: sys.stderr.write('%.2f%% ' % (0.0 + 50 * i / len(mvs))) sys.stderr.flush() cart.update_all() # for anyone monitoring for po in range(2): cart.femc.set_sis_voltage(cart.ca, po, sb, mult * mv) rows[i][mv_index] = mv # start IFTASK action while we average the mixer current readings transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) for j in range(ua_n): for po in range(2): ua = cart.femc.get_sis_current(cart.ca, po, sb) * 1e3 rows[i][ua_avg_index + po * 2 + sb] += abs(ua) # for band 6 rows[i][ua_dev_index + po * 2 + sb] += ua * ua # 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 for j, dcm in enumerate(dcm_0): rows[i][p_index + j + 0] = msg.arg['POWER%d' % (dcm)] for j, dcm in enumerate(dcm_1): rows[i][p_index + j + 16] = msg.arg['POWER%d' % (dcm)] # sis2 sb = 1 if args.zero: cart._ramp_sis_bias_voltages([0.0, mvs[0], 0.0, mvs[0]]) else: cart._ramp_sis_bias_voltages([nom_v[0], mvs[0], nom_v[2], mvs[0]]) for i, mv in enumerate(mvs): if (i + 1) % 20 == 0: sys.stderr.write('%.2f%% ' % (50.0 + 50 * i / len(mvs))) sys.stderr.flush() cart.update_all() # for anyone monitoring for po in range(2): cart.femc.set_sis_voltage(cart.ca, po, sb, mv) rows[i][mv_index] = mv # start IFTASK action while we average the mixer current readings transid = drama.obey("IFTASK@if-micro", "WRITE_TP2", FILE="NONE", ITIME=0.1) for j in range(ua_n): for po in range(2): ua = cart.femc.get_sis_current(cart.ca, po, sb) * 1e3 rows[i][ua_avg_index + po * 2 + sb] += ua rows[i][ua_dev_index + po * 2 + sb] += ua * ua # 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 for j, dcm in enumerate(dcm_0): rows[i][p_index + j + 8] = msg.arg['POWER%d' % (dcm)] for j, dcm in enumerate(dcm_1): rows[i][p_index + j + 24] = msg.arg['POWER%d' % (dcm)] sys.stderr.write('\n') sys.stderr.flush() return 0