def drift_scan(session, ref_antenna, target, duration=60.0, nd_period=None, lead_time=None): """Drift scan observation. Parameters ---------- session: `CaptureSession` ref_antenna: katpoint.Antenna target: katpoint.Target duration: float scan duration nd_period: float noisediode period lead_time: float noisediode trigger lead time """ # trigger noise diode if set trigger(session.kat, duration=nd_period, lead_time=lead_time) target = drift_pointing_offset(ref_antenna, target, duration=duration) user_logger.info("Drift_scan observation for {} sec".format(duration)) return session.track(target, duration=duration)
def off(kat, timestamp=None, lead_time=None, allow_ts_err=False): """Switch noise-source pattern off. Parameters ---------- kat : session kat container-like object Container for accessing KATCP resources allocated to schedule block. timestamp : float, optional (default = None) Time since the epoch as a floating point number [sec] lead_time : float, optional (default = system default lead time) Lead time before the noisediode is switched off [sec] allow_ts_err: boolean, optional (default = False) Allow ND set failures to pass by ignoring NaN timestamps Returns ------- timestamp : float Linux timestamp reported by digitiser """ if timestamp is None: timestamp = _get_nd_timestamp_(lead_time) true_timestamp = _switch_on_off_(kat, timestamp) continue_ = (np.isfinite(true_timestamp) or allow_ts_err) if not continue_: msg = ('Failed to switch ND off, timestamp = {}, observation aborted'. format(true_timestamp)) raise RuntimeError(msg) msg = ('Report: noise-diode off at {}'.format(true_timestamp)) user_logger.info(msg) return true_timestamp
def log_message(msg, level='info', boldtype=False, colourtext='black'): bold = boldtype colour = colourtext if level == 'debug': user_logger.debug(str(msg)) elif level == 'info': user_logger.info(str(msg)) elif level == 'warn': user_logger.warn(str(msg)) colour = 'orange' bold = True elif level == 'error': user_logger.error(str(msg)) colour = 'red' bold = True if not bold and colour == 'black': email_msg.append(timestamp() + level.upper() + ' ' + str(msg)) elif colour != 'black' and not(bold): email_msg.append('<font color="{colour}">{timestamp} {level} {msg}</font>'.format( colour=colour, timestamp=timestamp(), level=level.upper(), msg=str(msg))) else: email_msg.append('<font color="{colour}"><b>{timestamp} {level} {msg}</b></font>'.format( colour=colour, timestamp=timestamp(), level=level.upper(), msg=str(msg)))
def off(kat, timestamp=None, lead_time=_DEFAULT_LEAD_TIME): """Switch noise-source pattern off. Parameters ---------- kat : session kat container-like object Container for accessing KATCP resources allocated to schedule block. timestamp : float, optional (default = None) Time since the epoch as a floating point number [sec] lead_time : float, optional (default = system default lead time) Lead time before the noisediode is switched off [sec] Returns ------- timestamp : float Linux timestamp reported by digitiser """ if timestamp is None: timestamp = _get_nd_timestamp_(lead_time) true_timestamp = _switch_on_off_(kat, timestamp) msg = ('Report: noise-diode off at {}' .format(true_timestamp)) user_logger.info(msg) return true_timestamp
def phase_up(cbf, weights, ants=None, bf='bf0', phase_only=True, scramble=False): """Phase up a group of antennas using latest gain corrections.""" for inp in bf_inputs(cbf, bf): status = 'beamformer input ' + inp + ':' if (ants is None or inp in ants) and inp in weights and weights[inp]: weights_str = weights[inp] if phase_only: f = StringIO.StringIO(weights_str) weights_arr = np.loadtxt(f, dtype=np.complex, delimiter=' ') norm_weights = weights_arr / np.abs(weights_arr) status += ' normed' if scramble: norm_weights *= np.exp(2j * np.pi * np.random.rand(len(norm_weights))) status += ' scrambled' weights_str = ' '.join([('%+5.3f%+5.3fj' % (w.real, w.imag)) for w in norm_weights]) else: weights_str = ' '.join(1024 * ['0']) status += ' zeroed' cbf.req.dbe_k7_beam_weights(bf, inp, weights_str) user_logger.info(status)
def return_scan(session, target, nd_period=None, lead_time=None, **kwargs): """Return scan observation. A temporary fix until raster scan can be fixed Parameters ---------- session: `CaptureSession` target: katpoint.Target nd_period: float noisediode period lead_time: float noisediode trigger lead time """ # set up 2way scan user_logger.info("Forward scan over target") target_visible = scan(session, target, nd_period=nd_period, lead_time=lead_time, **kwargs) user_logger.info("Reverse scan over target") returnscan = dict(kwargs) returnscan["start"] = kwargs["end"] returnscan["end"] = kwargs["start"] target_visible &= scan(session, target, nd_period=nd_period, lead_time=lead_time, **returnscan) return target_visible
def set_fengines(session, requant_gains=None, fft_shift=None): """Set the f-engine gains. Parameters ---------- session: `SessionCBF()` Simplify and normalise access to a CBF stream within session requant_gains: int F-engine gain fft_shift: int Fast Fourier Transform shift """ if not session.cbf.fengine.inputs: msg = "Cannot set the F-engine gains" raise RuntimeError(msg) for inp in session.cbf.fengine.inputs: # Set the gain to a single non complex number if needed if requant_gains is not None: # TODO: read and store values before assignment session.cbf.fengine.req.gain(inp, requant_gains) msg = "F-engine {} gain set to {}".format(str(inp), requant_gains) user_logger.info(msg) # Set the FFT-shift schedule if fft_shift is not None: # TODO: read and store values before assignment session.cbf.fengine.req.fft_shift(fft_shift) msg = "F-engine FFT shift schedule set to {}".format(fft_shift) user_logger.info(msg)
def raster_scan(ants,target, num_scans=3, scan_duration=30.0, scan_extent=6.0, scan_spacing=0.5, scan_in_azimuth=True, projection=('ARC', 0., 0.), dry_run=False): # Create references to allow easy copy-and-pasting from this function # Convert description string to target object, or keep object as is if not isinstance(target, katpoint.Target): target = katpoint.Target(target) user_logger.info("Initiating raster scan (%d %g-second scans extending %g degrees) on target '%s'" % (num_scans, scan_duration, scan_extent, target.name)) # Create start and end positions of each scan, based on scan parameters scan_levels = np.arange(-(num_scans // 2), num_scans // 2 + 1) scanning_coord = (scan_extent / 2.0) * (-1) ** scan_levels stepping_coord = scan_spacing * scan_levels # Flip sign of elevation offsets to ensure that the first scan always # starts at the top left of target scan_starts = zip(scanning_coord, -stepping_coord) if scan_in_azimuth else zip(stepping_coord, -scanning_coord) scan_ends = zip(-scanning_coord, -stepping_coord) if scan_in_azimuth else zip(stepping_coord, scanning_coord) # Perform multiple scans across the target for scan_index, (start, end) in enumerate(zip(scan_starts, scan_ends)): scan(ants,target, duration=scan_duration, start=start, end=end, index=scan_index, projection=projection,) return True
def capture_start(self): """Enter the data capturing session, starting capture.""" user_logger.info('Starting correlator (used for signal displays)') # Starting streams will issue metadata for capture # Allow long 10sec intervals to allow enough time to initiate data capture and to capture metadata # Else there will be collisions between the 2 beams for beam in self.beams: # Initialise receiver and setup server for data capture user_logger.info('Initialising receiver and stream for beam %r' % (beam.name,)) if not beam.rx_init(beam.data_drive, beam.obs_meta['half_band'], beam.obs_meta['transpose']): raise RuntimeError('Could not initialise %r receiver' % (beam.name,)) # Start metadata receiver before starting data transmit beam.rx_meta_init(beam.meta_port) # port self.instrument_start(beam.name) user_logger.info('beamformer metadata') beam.rx_meta(beam.obs_meta) # additional obs related info user_logger.info('waiting 10s to write metadata for beam %r' % (beam.name,)) time.sleep(10) # Start transmitting data user_logger.info('beamformer data for beam %r' % (beam.name,)) beam.rx_beam(pol=beam.pol, port=beam.data_port) time.sleep(1)
def mv_idx(kat, band): stop_ants(kat) user_logger.info("Moving Receiver Indexer to position %s" % string.upper(band)) #try: if not kat.dry_run: kat.ants.req.ap_set_indexer_position('l', timeout=60) # string.lower(band))
def set_gains(kat, value): ants = kat.ants for ant in ants: for pol in ['h', 'v']: user_logger.info("Setting gain %d to antenna %s" % (int(value), '%s%s' % (ant.name, pol))) kat.data_rts.req.cbf_gain('%s%s' % (ant.name, pol), int(value)) time.sleep(1)
def mv_idx(kat, band): stop_ants(kat) user_logger.info("Moving Receiver Indexer to position %s" % string.upper(band)) try: if not kat.dry_run: kat.ants.req.ap_set_indexer_position(string.lower(band)) time.sleep(60) except: raise RuntimeError('Unknown indexer %s' % string.upper(band))
def __exit__(self, type, value, traceback): """Return telescope to its startup state.""" user_logger.info("Observation clean up") user_logger.info("Returning telescope to startup state") # Ensure known exit state before quitting # TODO: Return correlator settings to entry values # switch noise-source pattern off (ensure this after each observation) noisediode.off(self.array) self.array.disconnect()
def select_ant(cbf, input, bf='bf0'): """Only use one antenna in specified beamformer.""" # Iterate over *all* inputs going into the given beam for inp in bf_inputs(cbf, bf): status = 'beamformer input ' + inp + ':' weight = '1' if inp == input else '0' status += ' kept' if inp == input else ' zeroed' cbf.req.dbe_k7_beam_weights(bf, inp, *(1024 * [weight])) user_logger.info(status)
def start_ants(ants, dry_run=False): if not dry_run: user_logger.info("Setting Antennae Mode to 'STOP', Powering on Antenna Drives.") ants.set_sampling_strategy('mode', 'event') ants.req.mode('STOP') try: ants.wait('mode', 'STOP', timeout=12) except Exception as err: #user_logger.warn(err) user_logger.warn("not all antennas were in 'STOP' mode.")
def stop_ants(kat): if not kat.dry_run and kat.ants.req.mode('STOP') : user_logger.info("Setting Antenna Mode to 'STOP', Powering on Antenna Drives.") cntr = 60 for ant in kat.ants: while ant.sensor.mode.get_value() not in ['STOP']: time.sleep(1) cntr -= 1 if cntr < 0: break else: user_logger.error("Unable to set Antenna mode to 'STOP'.")
def test_sequence(ants, disable_corrections=False, dry_run=False): # Disable ACU pointing corrections if disable_corrections: ants.req.ap_enable_point_error_systematic(False) ants.req.ap_enable_point_error_tiltmeter(False) if not dry_run: try: ants.wait('ap.enable-point-error-systematic', False, timeout=1) ants.wait('ap.enable-point-error-tiltmeter', False, timeout=1) except: user_logger.error( "Failed to disable ACU pointing corrections.") raise user_logger.warning("ACU pointing corrections have been disabled.") for azim in [-45, 45, 135, 225]: # Start at low elevation elev = 20 # Set this target for the receptor target sensor target = katpoint.Target('Name, azel, %s, %s' % (azim, elev)) user_logger.info("Starting position: '%s' ", target.description) if not dry_run: move_antennas(ants, azim, elev) # Move to high elevation elev = 80 # Set this target for the receptor target sensor target = katpoint.Target('Name, azel, %s, %s' % (azim, elev)) user_logger.info("Move to high elevation: '%s' ", target.description) if not dry_run: move_antennas(ants, azim, elev) # Wait for 10 minutes before moving to next position user_logger.info( "Dwell at high elevation for 10 minutes to check if indexer slips." ) time.sleep(600) # Return to low elevation elev = 20 # Set this target for the receptor target sensor target = katpoint.Target('Name, azel, %s, %s' % (azim, elev)) user_logger.info("Return to low elevation: '%s' ", target.description) if not dry_run: move_antennas(ants, azim, elev) user_logger.info("Sequence Completed!")
def move_antennas(ants, azim, elev): if (-185.0 > azim) or (azim > 275.0): raise ParametersExceedTravelRange("Cannot perform requested slew, " "azimuth travel range exceeded.") if (14.65 > elev) or (elev > 92.0): raise ParametersExceedTravelRange("Cannot perform requested slew, " "elevation travel range exceeded.") # Use slew to bypass pointing model ants.req.ap_slew(azim, elev) # Since we are not using the proxy request we will have to explicitly # wait for the servo brakes to open and on-target sensor to update. time.sleep(4) try: ants.wait('ap.on-target', True, timeout=300) time.sleep(2) # Track for a bit to allow deacceleration except: user_logger.error( "Timed out while waiting for AP to reach starting position.") raise user_logger.info("AP has reached start position.") ants.req.mode('STOP') time.sleep(3) current_az = ants.req.sensor_value('ap.actual-azim') current_el = ants.req.sensor_value('ap.actual-elev') current_ridx = ants.req.sensor_value('ap.indexer-position-raw') user_logger.info("Azimuth readings: %s", current_az) user_logger.info("Elevation readings: %s", current_el) user_logger.info("Indexer position readings: %s", current_ridx)
def __enter__(self): """Verify subarray setup correct for observation before doing any work.""" user_logger.info("Observation start up") user_logger.info("Running astrokat version - %s", astrokat.__version__) obs_plan_params = self.opts.obs_plan_params if "instrument" in obs_plan_params: self.subarray_setup(obs_plan_params["instrument"]) # TODO: noise diode implementations should be moved to sessions # switch noise-source pattern off (known setup starting observation) noisediode.off(self.array) # TODO: update correlator settings # TODO: names of antennas to use for beamformer if not all is desirable return self
def __exit__(self, exc_type, exc_value, traceback): """Exit the data capturing session, stopping the capture.""" if exc_value is not None: exc_msg = str(exc_value) msg = "Session interrupted by exception (%s%s)" % \ (exc_value.__class__.__name__, (": '%s'" % (exc_msg,)) if exc_msg else '') if exc_type is KeyboardInterrupt: user_logger.warning(msg) else: user_logger.error(msg, exc_info=True) self.cbf.req.dbe_capture_stop(self.instrument) user_logger.info('beamformer stopped') # Suppress KeyboardInterrupt so as not to scare the lay user, # but allow other exceptions that occurred in the body of with-statement return exc_type is KeyboardInterrupt
def __init__(self, opts, correlator=None): user_logger.info("Setting up telescope for observation") self.opts = opts # unpack user specified correlator setup values if correlator is not None: correlator_config = read_yaml(correlator) self.feng = correlator_config["Fengine"] self.xeng = correlator_config["Xengine"] self.beng = correlator_config["Bengine"] else: self.feng = self.xeng = self.beng = None # Check options and build KAT configuration, # connecting to proxies and devices # create single kat object, cannot repeatedly recreate self.array = verify_and_connect(opts)
def scan(ants, target, duration=30.0, start=(-3.0, 0.0), end=(3.0, 0.0), index=-1, dry_run=False,projection = ('ARC', 0., 0.)): # Convert description string to target object, or keep object as is if not isinstance(target, katpoint.Target): target = katpoint.Target(target) scan_name = 'scan' if index < 0 else 'scan %d' % (index,) user_logger.info("Initiating %g-second scan across target '%s'" % (duration, target.name)) # This already sets antennas in motion if in mode POINT set_target(ants, target) user_logger.info('slewing to start of %s', scan_name) # Move each antenna to the start position of the scan ants.req.scan_asym(start[0], start[1], end[0], end[1],duration, projection) ants.req.mode('POINT') # Wait until they are all in position (with 5 minute timeout) locks = 0 if not dry_run : for ant_x in ants: if ant_x.wait('lock', True, timeout=300): locks +=1 print "locks status:", ant_x.name , locks,len(ants) else: locks = len(ants) if len(ants)==locks: user_logger.info('start of %s reached' % (scan_name,)) if not dry_run : #time.sleep(duration) user_logger.info('performing %s' % (scan_name,)) # Start scanning the antennas ants.req.mode('SCAN') # Wait until they are all finished scanning (with 5 minute timeout) scanc = 0 for ant_x in ants: if ant_x.wait('scan_status', 'after', timeout=300): scanc +=1 print "scan status:", ant_x.name , scanc,len(ants) user_logger.info('%s complete' % (scan_name,)) return True else: user_logger.warning("Unable to Scan Target : %r Check %s sensors " % (target,','.join([ant.name for ant in ants]))) return False return True
def point(ants, target, timeout=300): # send this target to the antenna. ants.req.target(target) ants.req.mode("POINT") user_logger.info("Slewing to target : %s" % (target, )) # wait for antennas to lock onto target ants.set_sampling_strategy("lock", "period 1.0") success = ants.wait("lock", True, timeout) if success: user_logger.info("Tracking Target : %s " % (target, )) else: failed = [client for client in success if not success[client]] msg = "Waiting for sensor 'lock' == True " # Report failure to user (including list of clients that failed) msg += "reached timeout of %.1f seconds. " % (timeout, ) msg += "Clients %r failed." % (sorted(failed), ) user_logger.error(msg) return success
def move_to(ants, az, el): user_logger.info("Slewing to Az: '%s' El: '%s'", az, el) # Use slew to bypass pointing model ants.req.ap_slew(az, el) # Since we are not using the proxy mode we will have to explicitly wait # for the servo brakes to open and on-target sensor to update. time.sleep(4) try: ants.wait('ap.on-target', True, timeout=300) time.sleep(2) # Track for a bit to allow deacceleration user_logger.info("Position reached...") except: user_logger.error("Timed out while waiting for AP to reach " "requested position.") raise # Arbitrary pause before next movement time.sleep(10)
def sample_bits(ant, pol, band='l'): tmp_data = np.zeros((5, 4096, 4)) for i in range(5): snap = ant.req.dig_adc_snap_shot(pol) tmp_data[i, :, :] = np.array( [snip.arguments[1:] for snip in snap.messages[1:]]).astype('float') data = tmp_data.flatten() std = (data * plus_minus(data.shape[0])).std() color_d = color_code(std, 12, 8) sensor = 'dig_%s_band_rfcu_%spol_attenuation' % (band, pol) atten = ant.sensor[sensor].get_value() windowed_data = data.reshape(-1, 256) voltage = np.abs(np.fft.fft(windowed_data) [:, 67:89]).mean() # 67:89 corresponds to 1300 to 1450 MHz # channels 67:89 correspond to a RFI free section of band (1300 to 1450 MHz). string = "%s ADC rms %s: %s%-4.1f %s vlevel: %-4.1f Attenuation : %-2i " % ( ant.name, pol, color_d, std, colors.Normal, voltage, atten) user_logger.info(string) return std, atten, voltage
def move_ri(ants, ridx_pos, dry_run=False): if ridx_pos not in ['u', 'l', 's', 'x']: raise ValueError("indexer position {} does not exist. active bands are 'u', 'l', 's', and 'x'".format(ridx_pos)) user_logger.info("Moving receiver indexers to '{}' position".format(ridx_pos)) ants.set_sampling_strategy("ap.ridx-brakes-released","period 0.5") ants.req.ap_set_indexer_position(ridx_pos) time.sleep(10) if not dry_run: try: # Wait for indexer brakes to engage again ants.wait('ap.ridx-brakes-released', False, timeout=60) user_logger.info('{} position reached'.format(ridx_pos)) except Exception: not_on_pos = [] not_on_pos = [ant.name for ant in ants if ant.sensor.ap_indexer_position.get_value() != ridx_pos] user_logger.error("Indexer brakes did not engage on: {}".format(', '.join(not_on_pos))) finally: # allow brakes to engage and put antennas to stop time.sleep(3) ants.req.mode('STOP') time.sleep(2)
def track(ants, target, duration=10, dry_run=False): print "Target :", target # send this target to the antenna. ants.req.target(target.description) print "Target desc. : ", target.description ant_names = ','.join([ant.name for ant in ants]) ants.req.mode("POINT") user_logger.info("Slewing %s to target : %r" % (ant_names, target)) #Wait for antenna to lock onto target locks = 0 if not dry_run: for ant_x in ants: if ant_x.wait('lock', True, timeout=300): locks += 1 print "locks status:", ant_x.name, locks, len(ants) else: locks = len(ants) if len(ants) == locks: user_logger.info( "Target reached : %r wait for %d seconds before slewing to the next target" % (target, duration)) if not dry_run: time.sleep(duration) user_logger.info("Test complete : %r" % (target, )) return True else: user_logger.warning("Unable to track Target : %r Check %s sensors " % (target, ant_names)) return False
def measure_atten(ant, pol, atten_ref=None, band='l'): """ This function returns the attenuation of an antenna and colors the logging if this number differs from the reference value Example: $ measure_atten('m064', 'h',atten_ref=5) returns 4 with log message: <<date time>> band l: m064 h Attenuation : <yellow> 4 <default color> " ant is an katcp antenna object pol is a string value and test are the antenna name and the polorisation and atten_ref is the expected values. """ sensor = "dig_%s_band_rfcu_%spol_attenuation" % (band, pol) atten = ant.sensor[sensor].get_value() color_d = color_code_eq(atten, atten_ref) string = "'%s' band: %s %s Attenuation : %s %-2i %s " % ( band, ant.name, pol, color_d, atten, colors.Normal) user_logger.info(string) return atten
def on(kat, timestamp=None, lead_time=_DEFAULT_LEAD_TIME): """Switch noise-source pattern on. Parameters ---------- kat : session kat container-like object Container for accessing KATCP resources allocated to schedule block. timestamp : float, optional (default = None) Time since the epoch as a floating point number [sec] lead_time : float, optional (default = system default lead time) Lead time before the noisediode is switched on [sec] Returns ------- timestamp : float Linux timestamp reported by digitiser """ if timestamp is None: timestamp = _get_nd_timestamp_(lead_time) true_timestamp = _switch_on_off_(kat, timestamp, switch=1) # on sleeptime = true_timestamp - time.time() user_logger.debug('DEBUG: now {}, sleep {}' .format(time.time(), sleeptime)) time.sleep(sleeptime) # default sleep to see for signal to get through user_logger.debug('DEBUG: now {}, slept {}' .format(time.time(), sleeptime)) msg = ('Report: noise-diode on at {}' .format(true_timestamp)) user_logger.info(msg) return true_timestamp
def scan(session, target, nd_period=None, lead_time=None, **kwargs): """Run basic scan observation. Parameters ---------- session: `CaptureSession` target: katpoint.Target nd_period: float noisediode period lead_time: float noisediode trigger lead time """ # trigger noise diode if set trigger(session.kat, duration=nd_period, lead_time=lead_time) try: timestamp = session.time except AttributeError: timestamp = time.time() user_logger.debug( "DEBUG: Starting scan across target: {}".format(timestamp)) user_logger.info("Scan target: {}".format(target)) return session.scan(target, **kwargs)