def SET_SG_OUT(msg): '''Set Agilent output on (1) or off (0).''' log.debug('SET_SG_OUT(%s)', msg.arg) if not initialised: raise drama.BadStatus(drama.APP_ERROR, 'task needs INITIALISE') args, kwargs = drama.parse_argument(msg.arg) out = set_sg_out_args(*args, **kwargs) log.info('setting agilent output to %d', out) agilent.set_output(out) agilent.update(publish_only=True)
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)
parser.add_argument('--note', nargs='?', default='', help='note for file header') args = parser.parse_args() 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.flush() agilent = namakanui.agilent.Agilent(datapath + 'agilent.ini', time.sleep, namakanui.nop) agilent.log.setLevel(logging.INFO) agilent.set_dbm(agilent.safe_dbm) agilent.set_output(1) # TODO: is it worth making a class for the N1913A? pmeter = socket.socket() pmeter.settimeout(1) pmeter.connect((args.ip, 5025)) pmeter.send(b'*idn?\n') idn = pmeter.recv(256) if b'N1913A' not in idn: logging.error('expected power meter model N1913A, but got %s', idn) sys.exit(1) # just assume all this succeeds pmeter.send(b'*cls\n') # clear errors pmeter.send(b'unit:power dbm\n') # dBm readings pmeter.send(b'init:cont on\n') # free run mode pmeter.send(b'mrate normal\n') # 20 reads/sec
import os import sys import logging logging.root.addHandler(logging.StreamHandler()) logging.root.setLevel(logging.INFO) binpath, datapath = namakanui.util.get_paths() def mypub(n, s): pass logging.info('\ndisabling agilent output') try: agilent = namakanui.agilent.Agilent(datapath + 'agilent.ini', time.sleep, namakanui.nop) agilent.set_dbm(agilent.safe_dbm) agilent.set_output(0) except socket.timeout as e: logging.warning('*** WARNING: socket.timeout, skipping agilent. ***') for band in [3, 6, 7]: logging.info('\nband %d:', band) cart = namakanui.cart.Cart(band, datapath + 'band%d.ini' % (band), time.sleep, mypub) cart.power(0) logging.info('\nall cartridges powered down.')
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 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)