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 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 compare_sensors(sensors1, sensors2, num): """ return True if sensors2 - sensors1 > num""" return_value = False for sen in sensors1.keys(): if sensors2[sen] - sensors1[sen] > num: return_value = True user_logger.error('%s has changed by %g from %g to %g') return return_value
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 stow_test(ants, taz, tel, dry_run=False): # send this target to the antenna. target = katpoint.Target('Name, azel, %s, %s' % (taz, tel)) if not dry_run: # Use slew to bypass pointing model ants.req.ap_slew(taz, tel) # 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) user_logger.info("Starting target description: '%s' ", target.description) if not dry_run: 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, beginning stow test") user_logger.info("Activating receptor windstows") ants.req.set_windstow(1) ants.wait('ap.mode', 'stowed', timeout=120) # Wait a bit before clearing user_logger.info("Stow position reached. Monitoring for 20 seconds.") time.sleep(20) user_logger.info("Clearing receptor windstow condition") ants.req.set_windstow(0) ants.wait('mode', 'STOP', timeout=120) # Wait a bit and issue the windstow again to simulate wind picking up user_logger.info("Waiting 30 seconds before next 'gust'.") time.sleep(30) user_logger.info("Triggering 2nd windstow event.") ants.req.set_windstow(1) ants.wait('mode', 'STOW', timeout=120) user_logger.info("Montitoring for 20 seconds") # Reduced this time since we should already be up at stow position time.sleep(20) user_logger.info("Test concluded. Clearing windstow.") # Restore system to normal ants.req.set_windstow(0) ants.wait('mode', 'STOP', timeout=120)
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 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 __exit__(self, exc_type, exc_value, traceback): """Exit the data capturing session, closing all streams.""" 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.capture_stop() # Suppress KeyboardInterrupt so as not to scare the lay user, # but allow other exceptions that occurred in the body of with-statement if exc_type is KeyboardInterrupt: report_compact_traceback(traceback) return True else: return False
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 report_sensors(kat, filter, status): user_logger.info("Requesting sensor values... (filter={}, status={})" "".format(filter, status)) sensors = kat.list_sensors(filter=filter) for s in sensors: try: sensor_obj = getattr(kat.sensor, escape_name(s[1])) sensor_obj.get_value() except: user_logger.error('Could not get the sensor value for {}'.format( s.name)) user_logger.info("Waiting...") for i in range(0, len(sensors) / 20 + 1): time.sleep(0.1) sensors = kat.list_sensors(filter=filter, status=status) for s in sensors: name = s.name reading = s.reading val = str(reading.value) valTime = reading.received_timestamp updateTime = reading.timestamp stat = reading.status colour = get_sensor_colour(stat) # Print status with stratchar prefix - indicates strategy has been set val = val if len( val ) <= 45 else val[:42] + "..." # truncate value to first 45 character val = r"\n".join(val.splitlines()) user_logger.info("{} {} {} {} {}" "".format( col(colour) + name.ljust(45), str(stat).ljust(15), get_time_str(valTime).ljust(15), get_time_str(updateTime).ljust(15), str(val).ljust(45) + col('normal')))
) else: # Start capture session, which creates HDF5 file with start_session(kat, **vars(opts)) as session: user_logger.info("Setting up the signal Generator ip:port %s:%i." % (siggen_ip, siggen_port)) if not kat.dry_run or opts.force_siggen: # prevent verifiing script from messing with things and failing to connect sigconn = SCPI.SCPI(siggen_ip, siggen_port) testcon = sigconn.testConnect() else: sigconn = Temp() testcon = False with sigconn as sig: if testcon == False: user_logger.error( 'Test connection to signal generator failed.') else: user_logger.info("Connected to Signal Generator:%s" % (testcon)) sig.reset() user_logger.info("Signal Generator reset") sig.outputOn() user_logger.info("Signal Generator output on") sig.setFrequency((siggen_freq + siggen_freq_minor) * 1.0e6) user_logger.info( "Signal Generator frequency is set to %7.3f MHz" % (sig.getFrequency() * 1.0e-6)) siggen_freq = sig.getFrequency() sig.setPower(siggen_power) user_logger.info( "Signal Generator Power is set to %f dBm" %
if atten < 32 and (voltage > adc_volt + 20): # Up user_logger.info( "'%s' band %s %s: Changing attenuation from %idB to %idB " % (band, ant.name, pol, atten, atten + 1)) ant.req.dig_attenuation(pol, atten + 1) ant_update[i] = True if atten > 0 and (voltage < adc_volt or std < adc_std_in): user_logger.info( "'%s' band %s %s: Changing attenuation from %idB to %idB " % (band, ant.name, pol, atten, atten - 1)) ant.req.dig_attenuation(pol, atten - 1) ant_update[i] = True else: user_logger.error( "'%s' band %s %s band is not in the list of valid bands " % (band, ant.name, pol)) lines = [] summary = [] atten_ref = {} ant_list = [] lines.append('Changing attenuation , report of refine_attenuation.py') for ant in kat.ants: band = get_ant_band(ant) if band in bandlist: ant_list.append(ant.name) for pol in {'h', 'v'}: std, atten, voltage = sample_bits(ant, pol, band=band) lines.append("'%s' band %s %s: ,%i # std:%f vol:%f" % (band, ant.name, pol, atten, std, voltage)) atten_ref['%s_%s' % (ant.name, pol)] = [
for ant, value_h, value_v in tmp_data: try: atten_ref['%s_%s_%s' % (band, ant, 'h')] = np.int(value_h) atten_ref['%s_%s_%s' % (band, ant, 'v')] = np.int(value_v) except ValueError: user_logger.warning( "'%s' band %s: attenuation value '%s','%s' is not an integer " % (band, ant, value_h, value_v)) if not kat.dry_run: for pol in {'h', 'v'}: for ant in kat.ants: # note ant is an katcp antenna object band = get_ant_band(ant) key_lookup = '%s_%s_%s' % (band, ant.name, pol) if key_lookup not in atten_ref: user_logger.error( "'%s' band %s %s: Has no attenuation value in the file " % (band, ant.name, pol)) continue atten = measure_atten(ant, pol, atten_ref=atten_ref[key_lookup], band=band) if atten != atten_ref[key_lookup]: user_logger.info( "'%s' band %s %s: Changing attenuation from %idB to %idB " % (band, ant.name, pol, atten, atten_ref[key_lookup])) ant.req.dig_attenuation(pol, atten_ref[key_lookup]) user_logger.info("Sleeping for 30 seconds ") time.sleep(30) # The sleep is because there is a potential +/-30sec loop in the # state machine in the digitiser and sending a second command
kat.ants.set_sampling_strategy("ap.enable-point-error-tiltmeter", "event") if not kat.ants.req.mode('STOP'): user_logger.info("Setting antennas to mode 'STOP'") time.sleep(2) else: raise RuntimeError("Unable to set antennas to mode 'STOP'!") try: test_sequence(kat.ants, disable_corrections=opts.no_corrections, dry_run=kat.dry_run) finally: # Restore ACU pointing corrections kat.ants.req.ap_enable_point_error_systematic(False) kat.ants.req.ap_enable_point_error_tiltmeter(True) if not kat.dry_run: try: kat.ants.wait('ap.enable-point-error-systematic', False, timeout=1) kat.ants.wait('ap.enable-point-error-tiltmeter', True, timeout=1) except: user_logger.error("Failed to reset ACU pointing corrections.") raise kat.ants.req.mode('STOP') user_logger.info("Stopping antennas")
#if len(args) == 0: # raise ValueError("Please specify the sources to observe as arguments, either as " # "description strings or catalogue filenames") fl = '/../2.1-Tipping_Curve/TBGAL_CONVL.FITS' hdulist = pyfits.open(fl) Data = np.flipud(np.fliplr( hdulist[0].data)) # data is in the first element of the fits file ra = lambda x: int(x / 0.25) # helper functions dec = lambda x: int((-x + 90) / 0.25) with verify_and_connect(opts) as kat: if not kat.dry_run and kat.ants.req.mode('STOP'): user_logger.info( "Setting Antenna Mode to 'STOP', Powering on Antenna Drives.") else: user_logger.error("Unable to set Antenna mode to 'STOP'.") moon = kat.sources.lookup['moon'] with start_session(kat, **vars(opts)) as session: if not opts.no_delays and not kat.dry_run: if session.dbe.req.auto_delay('on'): user_logger.info("Turning on delay tracking.") else: user_logger.error('Unable to turn on delay tracking.') elif opts.no_delays and not kat.dry_run: if session.dbe.req.auto_delay('off'): user_logger.info("Turning off delay tracking.") else: user_logger.error('Unable to turn off delay tracking.') if session.dbe.req.zero_delay(): user_logger.info("Zeroed the delay values.") else:
def _set_dig_nd_(kat, timestamp, nd_setup=None, switch=0, cycle=False): """Setting and implementing digitiser noise diode command Parameters ---------- kat : session kat container-like object Container for accessing KATCP resources allocated to schedule block. timestamp : float Linux timestamp in seconds at which to switch noise diode nd_setup : dict, optional (default = None, no pattern set) Noise diode pattern setup, with keys: 'antennas': options are 'all', or 'm062', or ...., 'cycle_len': the cycle length [sec], - must be less than 20 sec for L-band, etc., etc. switch : 0 or 1, optional (default = 0) Switch all noise diodes off (0) or on (1) Returns ------- timestamp : float Linux timestamp reported by digitiser """ if nd_setup is not None: # selected antennas for nd pattern nd_antennas = sorted(nd_setup['antennas'].split(",")) # nd pattern length [sec] cycle_length = nd_setup['cycle_len'] # on fraction of pattern length [%] on_fraction = nd_setup['on_frac'] msg = ('Repeat noise diode pattern every {} sec, ' 'with {} sec on and apply pattern to {}'.format( cycle_length, float(cycle_length) * float(on_fraction), nd_antennas)) user_logger.info(msg) else: nd_antennas = sorted(ant.name for ant in kat.ants) cycle_length = 1. on_fraction = switch # Noise diodes trigger is evaluated per antenna replies = {} for ant in nd_antennas: ped = getattr(kat, ant) # The digitiser master controller takes about 15-50 ms per request, # so start panicking just before the deadline. if time.time() > timestamp - 0.02: user_logger.error( 'Requested noise diode timestamp %sZ will probably ' 'be in the past - please increase lead time', katpoint.Timestamp(timestamp)) skipped = ','.join(nd_antennas[len(replies):]) user_logger.error('Skipped setting these noise diodes: %s', skipped) break replies[ant] = ped.req.dig_noise_source(timestamp, on_fraction, cycle_length) if kat.dry_run: msg = ('Dry-run: Set noise diode for antenna {} at ' 'timestamp {}'.format(ant, timestamp)) user_logger.debug(msg) if cycle: # add time [sec] to ensure all digitisers set at the same time timestamp += cycle_length * on_fraction # assuming ND for all antennas must be the same # only display single timestamp if not kat.dry_run: timestamp = _katcp_reply_(replies) # test incorrect reply check if len(replies) < len(nd_antennas): user_logger.error('Noise diode activation not in sync') if np.isfinite(timestamp): msg = ( 'Set successful noise diodes with average timestamp {:.0f} ({}Z)'. format(timestamp, katpoint.Timestamp(timestamp))) user_logger.debug('DEBUG: {}'.format(msg)) return timestamp
def track(ant, taz, tel, total=1, dry_run=False): # Direction variable ie. up/down, clockwise/anti-clockwise h_direction = 1 v_direction = 1 # Number of cycles done cycle_count = 0 # Total number of degrees travelled in each axis az_total_angle = 0 el_total_angle = 0 #Total number of each type of slew completed az_7_deg_slews = 0 el_7_deg_slews = 0 az_26_deg_slews = 0 el_23_deg_slews = 0 # send this target to the antenna. target = katpoint.Target('Name, azel, %s, %s' % (taz, tel)) if not dry_run: # Use slew to bypass pointing model ant.req.ap_slew(taz, tel) # 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) user_logger.info("Starting target description: '%s' ", target.description) if not dry_run: try: ant.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, beginning endurance cycle") if not dry_run: last_az = ant.sensor.ap_actual_azim.get_value() last_el = ant.sensor.ap_actual_elev.get_value() else: last_az = -135 last_el = 15 while cycle_count <= total: # Cycle loop # Azimuth cycle is 7, 26, 7 degrees # Elevation cycle is 7, 23.5, 7 degrees # Azel = [(7,7),(26,23.5),(7,7)] for az_offset, el_offset in [(7, 7), (26, 23), (7, 7)]: taz += az_offset * h_direction # Find new coordinate based on current position tel += el_offset * v_direction # send this target to the antenna. target = katpoint.Target('Name, azel, %s, %s' % (taz, tel)) if not dry_run: # Use slew to bypass pointing model ant.req.ap_slew(taz, tel) # 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) user_logger.info("Target description: '%s' ( %s degree slew )", target.description, str(az_offset)) if not dry_run: try: ant.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 complete a slew.") user_logger.info("7 deg slews: az - '%s', el - '%s' ", az_7_deg_slews, el_7_deg_slews) user_logger.info("26/23 deg slews: az - '%s', el - '%s' ", az_26_deg_slews, el_23_deg_slews) user_logger.info( "Total degrees travelled: az - '%s', el - '%s' ", az_total_angle, el_total_angle) raise # Get the current position if not dry_run: current_az = ant.sensor.ap_actual_azim.get_value() current_el = ant.sensor.ap_actual_elev.get_value() else: current_az = -135 current_el = 15 # Add the angle travelled to the accumulated value az_total_angle += abs(current_az - last_az) el_total_angle += abs(current_el - last_el) # Update last_<axis> values last_az = current_az last_el = current_el if az_offset == 7: az_7_deg_slews += 1 else: az_26_deg_slews += 1 if el_offset == 7: el_7_deg_slews += 1 else: el_23_deg_slews += 1 # Update cycle counter cycle_count += 1 # If we have done 2 elevation cycles then reverse direction if cycle_count % 2 == 0: v_direction = v_direction * -1 # If we have done 9 azimuth cycles then reverse direction if cycle_count % 9 == 0: h_direction = h_direction * -1 # Only need this bit if we are not doing indexer test #if not dry_run: # time.sleep(228) if not dry_run: ant.req.mode('STOP') # Add a sleep since the indexer portion of the script # does not use the waiting functionality that is part # of the receptor proxy mode requests time.sleep(3) # NOTE: Using the indexer positioning time spec as we are more # concerned with repetitions than accuracy for this test. indexer_timeout = 60 # Position raw changed after indexer configurations ridx_angle = {'s': -0.618, 'l': 39.248, 'x': 79.143, 'u': 119.405} ridx_sequence = ['s', 'l', 'x', 'u'] # TODO: Revisit this once we have more information from AP team # about indexer drift issues if not dry_run: # If we are closer to 'u' than 's', then start from 'u' instead if ant.sensor.ap_indexer_position_raw.get_value() > 60: ridx_sequence.reverse() ridx_last_position = ant.sensor.ap_indexer_position.get_value() else: ridx_last_position = None # NOTE: we skip the first position in the list if we are already # there because the indexer seems to end up in a 'moving' # state after a few cycles of operating the drive. Revisit # this after feedback from Nico if ridx_last_position == ridx_sequence[0]: ridx_sequence = ridx_sequence[1:] if not dry_run: for pos in ridx_sequence: ridx_movement_start_time = time.time() user_logger.info("--- Moving RI to position: '%s' ---", pos.upper()) ant.req.ap_set_indexer_position(pos) # TODO: Add a promximity check for S position in case this increased # sleep still does not help # Wait for indexer brakes to open time.sleep(3) try: # Wait for indexer brakes to engage again ant.wait('ap.ridx-brakes-released', False, timeout=indexer_timeout) except: user_logger.error("Indexer brakes did not engage " "(hunting issue). Giving up and " "trying the next position.") ant.req.mode('STOP') time.sleep(3) # NOTE: We do not raise the timeout exception as we want # to try to continue. # Wait for power to encoder to switch off time.sleep(5) ridx_current_position = ant.sensors.ap_indexer_position.get_value( ) time_to_index = time.time() - ridx_movement_start_time ridx_position_raw = ant.sensor.ap_indexer_position_raw.get_value( ) ridx_brakes_released = ant.sensor.ap_ridx_brakes_released.get_value( ) if ridx_current_position != pos: user_logger.error( "Indexer error after %s seconds! " "Requested position: '%s'. " "Current position: '%s'. " "Encoder reading is %s degrees. " "Position error: %.6f degrees. " "Brakes released: '%s'. ", time_to_index, pos.upper(), ridx_current_position, ridx_position_raw, abs(ridx_angle[pos] - ridx_position_raw), ridx_brakes_released) # NOTE: We ignore the failed check here for now and # continue the script by not raising an exception. else: user_logger.info( "Brake engaged. The offset from the requested position: " "'%s' is %.6f degree(s)", pos.upper(), abs(ridx_angle[pos] - ridx_position_raw)) user_logger.info( "Request for angle '%s' to final angle '%s' " "took '%s' seconds.", ridx_angle[pos], ridx_position_raw, time_to_index) # Wait a little before requesting next indexer position time.sleep(5) # Print out slew numbers once all cycles are completed user_logger.info("7 deg slews: az - '%s', el - '%s' ", az_7_deg_slews, el_7_deg_slews) user_logger.info("26/23 deg slews: az - '%s', el - '%s' ", az_26_deg_slews, el_23_deg_slews) user_logger.info("Total degrees travelled: az - '%s', el - '%s' ", az_total_angle, el_total_angle)
def reversescan(session, target, nd_period=None, lead_time=None, **kwargs): """Reverse scan observation. This scan is done in "Reverse" This means that it is given an area to scan (via kwargs) rather that a target and parameters 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) if 'radec_p1' in kwargs and 'radec_p2' in kwargs: # means that there is a target area # find lowest setting part or # highest rising part antenna = copy.copy(target.antenna) target_list = [] for i in range(1, _MAX_POINTS_IN_SCAN_AREA_POLYGON + 1): key = 'radec_p%i' % i if key in kwargs: target_list.append( katpoint.Target( 't%i,radec,%s' % (i, kwargs[key]), antenna=target.antenna, )) else: user_logger.error( "No scan area defined - require radec_p1 and radec_p2") return False direction = kwargs.get("direction", False) scan_speed = kwargs.get("scan_speed", _DEFAULT_SCAN_SPEED_ARCMIN_PER_SEC) obs_start_ts = katpoint.Timestamp(time.time()).to_ephem_date() # use 1 deg offset to pre-position >4 min in the future to take into account slewing el, az_min, az_max, t_start, t_end = _get_scan_area_extents(target_list, antenna, obs_start_ts, offset_deg=1) # TODO: get horizon limit from observation - may want to pass limits of # the "acceptable elevation extent" into _get_scan_area_extents. if _MIN_HORIZON_EL_FOR_SCAN_DEG > np.degrees(el): user_logger.warning( "Source and scan below horizon: %s < %s", np.degrees(el), _MIN_HORIZON_EL_FOR_SCAN_DEG, ) return False scan_target = katpoint.construct_azel_target(katpoint.wrap_angle(az_min), el) scan_target.name = target.name # katpoint destructively set dates and times during calculation # restore datetime before continuing scan_target.antenna = antenna scan_target.antenna.observer.date = obs_start_ts user_logger.info("Slew to scan start") # slew to target. target_visible = session.track(scan_target, duration=0.0, announce=False) if not target_visible: user_logger.warning( "Start of scan is not visible! Elevation: %.1f deg.", np.degrees(el)) return False # This is the real scan obs_start_ts = katpoint.Timestamp(time.time()).to_ephem_date() el, az_min, az_max, t_start, t_end = _get_scan_area_extents( target_list, antenna, obs_start_ts) scan_target = katpoint.construct_azel_target( katpoint.wrap_angle((az_min + az_max) / 2.), el) scan_target.name = target.name # katpoint destructively set dates and times during calculation # restore datetime before continuing scan_target.antenna = antenna scan_target.antenna.observer.date = obs_start_ts scan_start = np.degrees(az_min - (az_min + az_max) / 2.) scan_end = np.degrees(az_max - (az_min + az_max) / 2.) scanargs = {} if "projection" in kwargs: scanargs["projection"] = kwargs["projection"] # take into account projection effects of the sky and convert to degrees per second # E.g., 5 arcmin/s should translate to 5/60/cos(el) deg/s scan_speed = (scan_speed / 60.0) / np.cos(el) scanargs["duration"] = abs(scan_start - scan_end) / scan_speed # Duration in seconds user_logger.info("Scan duration is %.2f and scan speed is %.2f deg/s", scanargs["duration"], scan_speed) user_logger.info("Start Time: %s", t_start) user_logger.info("End Time: %s", t_end) num_scan_lines = 0 while time.time() <= t_end.secs: if direction: scanargs["start"] = scan_start, 0.0 scanargs["end"] = scan_end, 0.0 else: scanargs["start"] = scan_end, 0.0 scanargs["end"] = scan_start, 0.0 user_logger.info("Azimuth scan extent [%.1f, %.1f]" % (scanargs["start"][0], scanargs["end"][0])) target_visible = scan(session, scan_target, nd_period=nd_period, lead_time=lead_time, **scanargs) direction = not direction if target_visible: num_scan_lines += 1 user_logger.info("Scan completed - %s scan lines", num_scan_lines) return num_scan_lines > 0
def pattern(kat, nd_setup, lead_time=_DEFAULT_LEAD_TIME, ): """Start background noise diode pattern controlled by digitiser hardware. Parameters ---------- kat : session kat container-like object Container for accessing KATCP resources allocated to schedule block. nd_setup : dict Noise diode pattern setup, with keys: 'antennas': options are 'all', or 'm062', or ...., 'cycle_len': the cycle length [sec], - must be less than 20 sec for L-band, etc., etc. lead_time : float, optional (default = system default lead time) Lead time before digitisers pattern is set [sec] Returns ------- timestamp : float Linux timestamp reported by digitiser """ # nd pattern length [sec] max_cycle_len = _get_max_cycle_len(kat) if float(nd_setup['cycle_len']) > max_cycle_len: msg = 'Maximum cycle length is {} seconds'.format(max_cycle_len) raise RuntimeError(msg) user_logger.trace('TRACE: max cycle len {}' .format(max_cycle_len)) # Try to trigger noise diodes on specified antennas in array simultaneously. # - add a default lead time to ensure enough time for all digitisers # to be set up if lead_time >= max_cycle_len: user_logger.error('Nonstandard ND usage: lead time > max cycle len') raise RuntimeError('ND pattern setting cannot be achieved') start_time = _get_nd_timestamp_(lead_time) user_logger.trace('TRACE: desired start_time {} ({})' .format(start_time, time.ctime(start_time))) msg = ('Request: Set noise diode pattern to activate at {} ' '(includes {} sec lead time)' .format(start_time, lead_time)) user_logger.warning(msg) nd_antennas = nd_setup['antennas'] sb_ants = ",".join(str(ant.name) for ant in kat.ants) nd_setup['antennas'] = sb_ants if nd_antennas == 'all': cycle = False elif nd_antennas == 'cycle': cycle = True else: cycle = False nd_setup['antennas'] = ",".join( ant.strip() for ant in nd_antennas.split(",") if ant.strip() in sb_ants ) user_logger.info('Antennas found in subarray, setting ND: {}' .format(nd_setup['antennas'])) # Noise Diodes are triggered simultaneously # on specified antennas in the array timestamp = _set_dig_nd_(kat, start_time, nd_setup=nd_setup, cycle=cycle) user_logger.trace('TRACE: now {} ({})' .format(time.time(), time.ctime(time.time()))) user_logger.trace('TRACE: timestamp {} ({})' .format(timestamp, time.ctime(timestamp))) wait_time = timestamp - time.time() user_logger.trace('TRACE: delta {}' .format(wait_time)) time.sleep(wait_time) user_logger.trace('TRACE: set nd pattern at {}, slept {}' .format(time.time(), wait_time)) msg = ('Report: Switch noise-diode pattern on at {}' .format(timestamp)) user_logger.info(msg) return timestamp
kind=opts.kind, mirrorx=opts.mirrorx) timeperstep = opts.sampletime if len(args) == 0: raise ValueError( "Please specify a target argument via name ('Ori A'), " "description ('azel, 20, 30') or catalogue file name ('sources.csv')") # Check basic command-line options and obtain a kat object connected to the appropriate system with verify_and_connect(opts) as kat: if not kat.dry_run and kat.ants.req.mode('STOP'): user_logger.info( "Setting Antenna Mode to 'STOP', Powering on Antenna Drives.") else: user_logger.error("Unable to set Antenna mode to 'STOP'.") catalogue = collect_targets(kat, args) targets = catalogue.targets if len(targets) == 0: raise ValueError( "Please specify a target argument via name ('Ori A'), " "description ('azel, 20, 30') or catalogue file name ('sources.csv')" ) target = targets[0] #only use first target lasttargetel = target.azel()[1] * 180.0 / np.pi # Initialise a capturing session (which typically opens an HDF5 file) with start_session(kat, **vars(opts)) as session: # Use the command-line options to set up the system session.standard_setup(**vars(opts)) if not opts.no_delays and not kat.dry_run:
def rate_slew(ants, azim, elev, speed=0.5, reverse=False, disable_corrections=False, dry_run=False): # Scale timeout with requested test speed. Full speed timeout is 5 min rate_timeout = (float(2) / speed) * 300 # Only testing movement in azimuth, not elevation azim_speed = speed elev_speed = 0.0 sensor_name = "ap.actual-azim" # Only testing 365 degrees, not full travel range (460 degrees) expected_azim = (azim + 365.0 if speed > 0 else azim - 365.0) # Position threshold 2 degrees to catch it at 0.5 second polling # period at full speed (2 deg/sec). threshold = 2 if (-185.0 < expected_azim < 275.0): user_logger.info("Antennas will perform a rate slew to azimuth %s", expected_azim) else: raise ParametersExceedTravelRange("Cannot perform 365 degree slew " "within the AP azim travel range " "from the given start position.") # Set this target for the receptor target sensor target = katpoint.Target('Name, azel, %s, %s' % (azim, elev)) if not dry_run: # 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) user_logger.info("Starting target description: '%s' ", target.description) if not dry_run: 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.") if not dry_run: if disable_corrections: SPEM_state = False # np.any([a.sensors.ap_point_error_systematic_enabled.get_value() for a in ants]) # TODO finalize the commented-out snippet TILT_state = True # np.any([a.sensors.ap_point_error_tiltmeter_enabled.get_value() for a in ants]) ants.req.ap_enable_point_error_systematic(False) ants.req.ap_enable_point_error_tiltmeter(False) ants.req.mode('STOP') time.sleep(3) ants.req.ap_rate(azim_speed, elev_speed) user_logger.info("Performing rate slew to azimuth %s at %s deg/sec.", expected_azim, azim_speed) # Wait until we are within a threshold of the expected ending azimuth ants.wait(sensor_name, lambda c: abs(c.value - expected_azim) < threshold, timeout=rate_timeout) if reverse: user_logger.info("Reverse slew selected...") ants.req.mode('STOP') time.sleep(8) ants.req.ap_rate(-1 * azim_speed, elev_speed) user_logger.info( "Performing rate slew to azimuth %s at %s deg/sec.", azim, azim_speed) # Head back to the starting azim that was passed into the function ants.wait(sensor_name, lambda c: abs(c.value - azim) < threshold, timeout=rate_timeout) if disable_corrections: ants.req.ap_enable_point_error_systematic(SPEM_state) ants.req.ap_enable_point_error_tiltmeter(TILT_state) user_logger.info("Sequence Completed!")
off1.name = 'off1' off2_azel = katpoint.construct_azel_target(wrap_angle(moon.azel()[0] - np.radians(10)), moon.azel()[1]) off2_azel.antenna = antenna off2 = katpoint.construct_radec_target(off2_azel.radec()[0], off2_azel.radec()[1]) off2.antenna = antenna off2.name = 'off2' sources = katpoint.Catalogue(add_specials=False) sources.add(moon) sources.add(off2) sources.add(off1) txtlist = ', '.join(["'%s'" % (target.name,) for target in sources]) user_logger.info("Calibration targets are [%s]", txtlist) for target in sources: session.nd_params = nd_off for nd in [nd_coupler]: session.nd_params = nd_off session.track(target, duration=0) # get onto the source user_logger.info("Now capturing data - diode %s on", nd['diode']) session.label('%s' % (nd['diode'],)) if not session.fire_noise_diode(announce=True, **nd): user_logger.error("Noise diode %s did not fire", nd['diode']) session.nd_params = nd_off user_logger.info("Now capturing data - noise diode off") session.label('track') session.track(target, duration=opts.track_duration) if opts.max_duration and time.time() > start_time + opts.max_duration: user_logger.info('Maximum script duration (%d s) exceeded, stopping script', opts.max_duration)
def _set_dig_nd_(kat, timestamp, nd_setup=None, switch=0, cycle=False): """Setting and implementing digitiser noise diode command Parameters ---------- kat : session kat container-like object Container for accessing KATCP resources allocated to schedule block. timestamp : float Linux timestamp in seconds at which to switch noise diode nd_setup : dict, optional (default = None, no pattern set) Noise diode pattern setup, with keys: 'antennas': options are 'all', or 'm062', or ...., 'cycle_len': the cycle length [sec], - must be less than 20 sec for L-band, etc., etc. switch : 0 or 1, optional (default = 0) Switch all noise diodes off (0) or on (1) Returns ------- timestamp : float Linux timestamp reported by digitiser """ if nd_setup is not None: # selected antennas for nd pattern nd_antennas = sorted(nd_setup['antennas'].split(",")) # nd pattern length [sec] cycle_length = nd_setup['cycle_len'] # on fraction of pattern length [%] on_fraction = nd_setup['on_frac'] msg = ('Repeat noise diode pattern every {} sec, ' 'with {} sec on and apply pattern to {}' .format(cycle_length, float(cycle_length) * float(on_fraction), nd_antennas)) user_logger.info(msg) else: nd_antennas = sorted(ant.name for ant in kat.ants) cycle_length = 1. on_fraction = switch # Noise diodes trigger is evaluated per antenna timestamps = [] for ant in nd_antennas: ped = getattr(kat, ant) reply = ped.req.dig_noise_source(timestamp, on_fraction, cycle_length) if not kat.dry_run: timestamps.append(_katcp_reply_({ant: reply})) else: msg = ('Dry-run: Set noise diode for antenna {} at ' 'timestamp {}'.format(ant, timestamp)) user_logger.debug(msg) if cycle: # add time [sec] to ensure all digitisers set at the same time timestamp += cycle_length * on_fraction # assuming ND for all antennas must be the same # only display single timestamp if not kat.dry_run: # test incorrect reply check if len(timestamps) < len(nd_antennas): err_msg = 'Noise diode activation not in sync' user_logger.error(err_msg) timestamp = np.mean(timestamps) msg = ('Set all noise diodes with timestamp {} ({})' .format(int(timestamp), time.ctime(timestamp))) user_logger.debug('DEBUG: {}'.format(msg)) return timestamp
off2_azel.radec()[1]) off2.antenna = antenna off2.name = 'off2' sources = katpoint.Catalogue(add_specials=False) sources.add(moon) sources.add(off2) sources.add(off1) txtlist = ', '.join( ["'%s'" % (target.name, ) for target in sources]) user_logger.info("Calibration targets are [%s]" % (txtlist)) for target in sources: session.nd_params = nd_off for nd in [nd_coupler]: session.nd_params = nd_off session.track(target, duration=0) # get onto the source user_logger.info("Now capturing data - diode %s on" % nd['diode']) session.label('%s' % (nd['diode'])) if not session.fire_noise_diode(announce=True, **nd): user_logger.error( "Noise Diode did not Fire , (%s did not fire)" % nd['diode']) session.nd_params = nd_off user_logger.info("Now capturing data - noise diode off") session.label('track') session.track(target, duration=opts.track_duration) if opts.max_duration and time.time() > start_time + opts.max_duration: user_logger.info( 'Maximum script duration (%d s) exceeded, stopping script' % (opts.max_duration, ))
def run_observation(opts, kat): """Extract control and observation information provided in observation file.""" obs_plan_params = opts.obs_plan_params # remove observation specific instructions housed in YAML file del opts.obs_plan_params # set up duration periods for observation control obs_duration = -1 if "durations" in obs_plan_params: if "obs_duration" in obs_plan_params["durations"]: obs_duration = obs_plan_params["durations"]["obs_duration"] # check for nonsensical observation duration setting if abs(obs_duration) < 1e-5: user_logger.error( "Unexpected value: obs_duration: {}".format(obs_duration)) return # TODO: the description requirement in sessions should be re-evaluated # since the schedule block has the description # Description argument in instruction_set should be retired, but is # needed by sessions # Assign proposal_description if available, else create a dummy if "description" not in vars(opts): session_opts = vars(opts) description = "Observation run" if "proposal_description" in vars(opts): description = opts.proposal_description session_opts["description"] = description nr_obs_loops = len(obs_plan_params["observation_loop"]) with start_session(kat.array, **vars(opts)) as session: session.standard_setup(**vars(opts)) # Each observation loop contains a number of observation cycles over LST ranges # For a single observation loop, only a start LST and duration is required # Target observation loop observation_timer = time.time() for obs_cntr, observation_cycle in enumerate( obs_plan_params["observation_loop"]): if nr_obs_loops > 1: user_logger.info("Observation loop {} of {}.".format( obs_cntr + 1, nr_obs_loops)) user_logger.info("Loop LST range {}.".format( observation_cycle["LST"])) # Unpack all target information if not ("target_list" in observation_cycle.keys()): user_logger.error( "No targets provided - stopping script instead of hanging around" ) continue obs_targets = observation_cycle["target_list"] target_list = obs_targets["target"].tolist() # build katpoint catalogues for tidy handling of targets catalogue = collect_targets(kat.array, target_list) obs_tags = [] for tgt in obs_targets: # catalogue names are no longer unique name = tgt["name"] # add tag evaluation to identify catalogue targets tags = tgt["target"].split(",")[1].strip() for cat_tgt in catalogue: if name == cat_tgt.name: if ("special" in cat_tgt.tags or "xephem" in cat_tgt.tags or tags == " ".join(cat_tgt.tags)): tgt["target"] = cat_tgt obs_tags.extend(cat_tgt.tags) break obs_tags = list(set(obs_tags)) cal_tags = [tag for tag in obs_tags if tag[-3:] == "cal"] # observer object handle to track the observation timing in a more user # friendly way # observer = catalogue._antenna.observer ref_antenna = catalogue.antenna observer = ref_antenna.observer start_datetime = timestamp2datetime(time.time()) observer.date = ephem.Date(start_datetime) user_logger.trace("TRACE: requested start time " "({}) {}".format( datetime2timestamp(start_datetime), start_datetime)) user_logger.trace("TRACE: observer at start\n {}".format(observer)) # Only observe targets in valid LST range if nr_obs_loops > 1 and obs_cntr < nr_obs_loops - 1: [start_lst, end_lst] = get_lst(observation_cycle["LST"], multi_loop=True) if end_lst is None: # for multi loop the end lst is required raise RuntimeError( 'Multi-loop observations require end LST times') next_obs_plan = obs_plan_params["observation_loop"][obs_cntr + 1] [next_start_lst, next_end_lst] = get_lst(next_obs_plan["LST"]) user_logger.trace("TRACE: current LST range {}-{}".format( ephem.hours(str(start_lst)), ephem.hours(str(end_lst)))) user_logger.trace("TRACE: next LST range {}-{}".format( ephem.hours(str(next_start_lst)), ephem.hours(str(next_end_lst)))) else: next_start_lst = None next_end_lst = None [start_lst, end_lst] = get_lst(observation_cycle["LST"]) # Verify the observation is in a valid LST range # and that it is worth while continuing with the observation # Do not use float() values, ephem.hours does not convert as # expected local_lst = observer.sidereal_time() user_logger.trace("TRACE: Local LST {}".format( ephem.hours(local_lst))) # Only observe targets in current LST range log_msg = "Local LST outside LST range {}-{}".format( ephem.hours(str(start_lst)), ephem.hours(str(end_lst))) if float(start_lst) < end_lst: # lst ends before midnight if not _same_day(start_lst, end_lst, local_lst): if obs_cntr < nr_obs_loops - 1: user_logger.info(log_msg) else: user_logger.error(log_msg) continue else: # lst ends after midnight if _next_day(start_lst, end_lst, local_lst): if obs_cntr < nr_obs_loops - 1: user_logger.info(log_msg) else: user_logger.error(log_msg) continue # Verify that it is worth while continuing with the observation # The filter functions uses the current time as timestamps # and thus incorrectly set the simulation timestamp if not kat.array.dry_run: # Quit early if there are no sources to observe if len(catalogue.filter(el_limit_deg=opts.horizon)) == 0: raise NoTargetsUpError( "No targets are currently visible - " "please re-run the script later") # Quit early if the observation requires all targets to be visible if opts.all_up and (len( catalogue.filter(el_limit_deg=opts.horizon)) != len(catalogue)): raise NotAllTargetsUpError( "Not all targets are currently visible - please re-run the script" "with --visibility for information") # List sources and their associated functions from observation tags not_cals_filter_list = [] for cal_type in cal_tags: not_cals_filter_list.append("~{}".format(cal_type)) cal_array = [cal.name for cal in catalogue.filter(cal_type)] if len(cal_array) < 1: continue # do not display empty tags user_logger.info("{} calibrators are {}".format( str.upper(cal_type[:-3]), cal_array)) user_logger.info("Observation targets are [{}]".format(", ".join([ repr(target.name) for target in catalogue.filter(not_cals_filter_list) ]))) # TODO: setup of noise diode pattern should be moved to sessions # so it happens in the line above if "noise_diode" in obs_plan_params: nd_setup = obs_plan_params["noise_diode"] nd_lead = nd_setup.get('lead_time') # Set noise diode period to multiple of correlator integration time. if not kat.array.dry_run: cbf_corr = session.cbf.correlator dump_period = cbf_corr.sensor.int_time.get_value() else: dump_period = 0.5 # sec user_logger.debug( 'DEBUG: Correlator integration time {} [sec]'.format( dump_period)) if "cycle_len" in nd_setup: if (nd_setup['cycle_len'] >= dump_period): cycle_len_frac = nd_setup['cycle_len'] // dump_period nd_setup['cycle_len'] = cycle_len_frac * dump_period msg = ('Set noise diode period ' 'to multiple of correlator dump period: ' 'cycle length = {} [sec]'.format( nd_setup['cycle_len'])) else: msg = ('Requested cycle length {}s ' '< correlator dump period {}s, ' 'ND not synchronised with dump edge'.format( nd_setup['cycle_len'], dump_period)) user_logger.warning(msg) noisediode.pattern( kat.array, nd_setup, lead_time=nd_lead, ) # Adding explicit init after "Capture-init failed" exception was # encountered session.capture_init() user_logger.debug("DEBUG: Initialise capture start with timestamp " "{} ({})".format(int(time.time()), timestamp2datetime( time.time()))) # Go to first target before starting capture user_logger.info("Slewing to first target") observe(session, ref_antenna, obs_targets[0], slewonly=True) # Only start capturing once we are on target session.capture_start() user_logger.trace("TRACE: capture start time after slew " "({}) {}".format(time.time(), timestamp2datetime( time.time()))) user_logger.trace( "TRACE: observer after slew\n {}".format(observer)) done = False sanity_cntr = 0 while not done: # small errors can cause an infinite loop here # preventing infinite loops sanity_cntr += 1 if sanity_cntr > 100000: user_logger.error("While limit counter has reached {}, " "exiting".format(sanity_cntr)) break # Cycle through target list in order listed targets_visible = False time_remaining = obs_duration observation_timer = time.time() for tgt_cntr, target in enumerate(obs_targets): katpt_target = target["target"] user_logger.debug("DEBUG: {} {}".format(tgt_cntr, target)) user_logger.trace( "TRACE: initial observer for target\n {}".format( observer)) # check target visible before doing anything # make sure the target would be visible for the entire duration target_duration = target['duration'] visible = True if type(katpt_target.body) is ephem.FixedBody: visible = above_horizon( target=katpt_target.body.copy(), observer=observer.copy(), horizon=opts.horizon, duration=target_duration) if not visible: show_horizon_status = True # warning for cadence targets only when they are due if (target["cadence"] > 0 and target["last_observed"] is not None): delta_time = time.time() - target["last_observed"] show_horizon_status = delta_time >= target[ "cadence"] if show_horizon_status: user_logger.warn("Target {} below {} deg horizon, " "continuing".format( target["name"], opts.horizon)) continue user_logger.trace( "TRACE: observer after horizon check\n {}".format( observer)) # check and observe all targets with cadences while_cntr = 0 cadence_targets = list(obs_targets) while True: tgt = cadence_target(cadence_targets) if not tgt: break # check enough time remaining to continue if obs_duration > 0 and time_remaining < tgt[ "duration"]: done = True break # check target visible before doing anything user_logger.trace("TRACE: cadence" "target\n{}\n {}".format( tgt, catalogue[tgt["name"]])) user_logger.trace("TRACE: initial observer for cadence" "target\n {}".format(observer)) user_logger.trace( "TRACE: observer before track\n {}".format( observer)) user_logger.trace( "TRACE: target observation # {} last observed " "{}".format(tgt["obs_cntr"], tgt["last_observed"])) cat_target = catalogue[tgt["name"]] if above_horizon( target=cat_target.body, observer=cat_target.antenna.observer.copy(), horizon=opts.horizon, duration=tgt["duration"]): if observe(session, ref_antenna, tgt, **obs_plan_params): targets_visible += True tgt["obs_cntr"] += 1 tgt["last_observed"] = time.time() else: # target not visibile to sessions anymore cadence_targets.remove(tgt) user_logger.trace( "TRACE: observer after track\n {}".format( observer)) user_logger.trace( "TRACE: target observation # {} last observed " "{}".format(tgt["obs_cntr"], tgt["last_observed"])) else: cadence_targets.remove(tgt) while_cntr += 1 if while_cntr > len(obs_targets): break if done: break user_logger.trace( "TRACE: observer after cadence\n {}".format(observer)) # observe non cadence target if target["cadence"] < 0: user_logger.trace( "TRACE: normal target\n {}".format(target)) user_logger.trace( "TRACE: observer before track\n {}".format( observer)) user_logger.trace("TRACE: ts before observe {}".format( time.time())) user_logger.trace("TRACE: target last " "observed {}".format( target["last_observed"])) targets_visible += observe(session, ref_antenna, target, **obs_plan_params) user_logger.trace( "TRACE: observer after track\n {}".format( observer)) user_logger.trace("TRACE: ts after observe {}".format( time.time())) if targets_visible: target["obs_cntr"] += 1 target["last_observed"] = time.time() user_logger.trace( "TRACE: target observation # {} last observed " "{}".format(target["obs_cntr"], target["last_observed"])) user_logger.trace( "TRACE: observer after track\n {}".format( observer)) # loop continuation checks delta_time = time.time() - session.start_time user_logger.trace( "TRACE: time elapsed {} sec".format(delta_time)) user_logger.trace( "TRACE: total obs duration {} sec".format( obs_duration)) if obs_duration > 0: time_remaining = obs_duration - delta_time user_logger.trace( "TRACE: time remaining {} sec".format( time_remaining)) next_target = obs_targets[(tgt_cntr + 1) % len(obs_targets)] user_logger.trace("TRACE: next target before cadence " "check:\n{}".format(next_target)) # check if there is a cadence target that must be run # instead of next target for next_cadence_tgt_idx in range( tgt_cntr + 1, len(obs_targets)): next_cadence_target = obs_targets[ next_cadence_tgt_idx % len(obs_targets)] if next_cadence_target["cadence"] > 0: user_logger.trace( "TRACE: time needed for next obs " "{} sec".format( next_cadence_target["cadence"])) next_target = obs_targets[next_cadence_tgt_idx % len(obs_targets)] continue user_logger.trace("TRACE: next target after cadence " "check:\n{}".format(next_target)) user_logger.trace("TRACE: time needed for next obs " "{} sec".format( next_target["duration"])) if (time_remaining < 1.0 or time_remaining < next_target["duration"]): user_logger.info( "Scheduled observation time lapsed - ending observation" ) done = True break # during dry-run when sessions exit time is reset so will be incorrect # outside the loop observation_timer = time.time() if obs_duration < 0: user_logger.info( "Observation list completed - ending observation") done = True # for multiple loop, check start lst of next loop if next_start_lst is not None: check_local_lst = observer.sidereal_time() if (check_local_lst > next_start_lst) or (not _next_day( next_start_lst, next_end_lst, check_local_lst)): user_logger.info("Moving to next LST loop") done = True # End if there is nothing to do if not targets_visible: user_logger.warning( "No more targets to observe - stopping script " "instead of hanging around") done = True user_logger.trace("TRACE: observer at end\n {}".format(observer)) # display observation cycle statistics # currently only available for single LST range observations if nr_obs_loops < 2: print user_logger.info("Observation loop statistics") total_obs_time = observation_timer - session.start_time if obs_duration < 0: user_logger.info("Single run through observation target list") else: user_logger.info("Desired observation time {:.2f} sec " "({:.2f} min)".format(obs_duration, obs_duration / 60.0)) user_logger.info("Total observation time {:.2f} sec " "({:.2f} min)".format(total_obs_time, total_obs_time / 60.0)) if len(obs_targets) > 0: user_logger.info("Targets observed :") for unique_target in np.unique(obs_targets["name"]): cntrs = obs_targets[obs_targets["name"] == unique_target]["obs_cntr"] durations = obs_targets[obs_targets["name"] == unique_target]["duration"] if np.isnan(durations).any(): user_logger.info("{} observed {} times".format( unique_target, np.sum(cntrs))) else: user_logger.info("{} observed for {} sec".format( unique_target, np.sum(cntrs * durations))) print
if opts.ap in [ant.name]: receptor = ant if receptor is None: raise RuntimeError("Receptor under test is not in controlled array") # Set sensor strategies" kat.ants.set_sampling_strategy("lock", "event") kat.ants.set_sampling_strategy("ap.indexer-position", "event") if not kat.dry_run and receptor.req.mode('STOP'): user_logger.info("Setting Antenna Mode to 'STOP', " "Powering on Antenna Drives.") time.sleep(2) else: if not kat.dry_run: user_logger.error("Unable to set Antenna mode to 'STOP'.") for taz, tel, band in targetlist: for i in range(int(opts.num_repeat)): target = katpoint.Target('Name, azel, %s, %s' % (taz, tel)) track(receptor, target, ridx_position=band, duration=opts.dwell_time, dry_run=kat.dry_run) user_logger.info("Elevation: '%s' " "Patterns attempted: '%s' " "Contractual cycles: '%s'." % (tel, i + 1, 11 * (i + 1)))
"Please specify at least one target argument via name ('Cygnus A'), " "description ('azel, 20, 30') or catalogue file name ('sources.csv')") # Check basic command-line options and obtain a kat object connected to the appropriate system with verify_and_connect(opts) as kat: targets = collect_targets(kat, args) # Initialise a capturing session (which typically opens an HDF5 file) with start_session(kat, **vars(opts)) as session: # Use the command-line options to set up the system session.standard_setup(**vars(opts)) if not opts.no_delays and not kat.dry_run: if session.dbe.req.auto_delay('on'): user_logger.info("Turning on delay tracking.") else: user_logger.error('Unable to turn on delay tracking.') elif opts.no_delays and not kat.dry_run: if session.dbe.req.auto_delay('off'): user_logger.info("Turning off delay tracking.") else: user_logger.error('Unable to turn off delay tracking.') if session.dbe.req.zero_delay(): user_logger.info("Zeroed the delay values.") else: user_logger.error('Unable to zero delay values.') all_ants = session.ants # Form scanning antenna subarray (or pick the first antenna as the default scanning antenna) scan_ants = ant_array( kat, opts.scan_ants if opts.scan_ants else session.ants[0], 'scan_ants')
def track(ant, target, ridx_position='l', duration=10, dry_run=False): # TODO: make the indexer timeout configurable parameter indexer_timeout = 120 ant.req.mode('STOP') if not dry_run: # Added sleep to wait for AP brakes to engage time.sleep(2) result = wait_until_sensor_equals(5.0, ant.name + '_mode', 'STOP') if result[0] == False: user_logger.error("Failed to set AP to 'STOP' mode. " "Indexer commands will not be processed.") return user_logger.info("Setting initial RI on %s to position : %s " % (ant.name, ridx_position.upper())) ant.req.ap_set_indexer_position(ridx_position) if not dry_run: result = wait_until_sensor_equals(indexer_timeout, ant.name + '_ap_indexer_position', ridx_position) else: result = (True, ridx_position) if result[0] == False: ridx_position_raw = kat.sensors.get( ant.name + '_ap_indexer_position_raw', None).get_value() user_logger.error( "Timed out while waiting %s seconds for " "indexer to reach '%s' position. " "Last position reading was %s degrees." % (indexer_timeout, ridx_position.upper(), ridx_position_raw)) # TODO: make this sequence easier to configure ridx_sequence = [ 'u', 'l', 'x', 's', 'l', 'u', 's', 'x', 'u', 'l', 's', 'x', 'l', 'u', 'x', 's', 'u', 'x', 'l', 's', 'l', 's' ] # Cycling indexer positions if not dry_run: for pos in ridx_sequence: ridx_last_position = kat.sensors.get( ant.name + '_ap_indexer_position', None).get_value() ridx_movement_start_time = time.time() user_logger.info("--- Moving RI to position: '%s' ---" % pos.upper()) ant.req.ap_set_indexer_position(pos) result = wait_until_sensor_equals( indexer_timeout, ant.name + '_ap_indexer_position', pos) user_logger.debug("Request result: '%s', " "last sensor reading: '%s'" % (result[0], result[1])) if result[0] == False: ridx_position_raw = kat.sensors.get( ant.name + '_ap_indexer_position_raw', None).get_value() ridx_brakes_released = kat.sensors.get( ant.name + '_ap_ridx_brakes_released', None).get_value() user_logger.error("Timed out while waiting %s seconds " "for indexer to reach '%s' position. " "Last position reading was %s degrees. " "Brakes released: '%s'. " % (indexer_timeout, pos.upper(), ridx_position_raw, ridx_brakes_released)) ridx_current_position = kat.sensors.get( ant.name + '_ap_indexer_position', None).get_value() time_to_index = time.time() - ridx_movement_start_time if ridx_current_position in ['undefined']: user_logger.warning("Movement ended in undefined position") else: user_logger.info( "RIDX from '%s' to '%s' position " "took '%s' seconds." % (ridx_last_position.upper(), ridx_current_position.upper(), time_to_index)) # 60 seconds comes from the antenna specification if (time_to_index > 60.0): user_logger.warning("Indexer took longer than 60 seconds!") user_logger.info("Dwell for %s seconds before " "going to next position." % duration) time.sleep(duration) user_logger.info("Pattern complete. Heading to next sky target.")