ACU Agent
The Antenna Control Unit (ACU) is an industrial PC with VxWorks installed. It is used for readout of encoder measurements and control of telescope platforms.
usage: python3 agent.py [-h] [--acu-config ACU_CONFIG] [--no-processes]
[--ignore-axes {el,az,third,none} [{el,az,third,none} ...]]
[--disable-idle-reset] [--min-el MIN_EL]
[--max-el MAX_EL] [--disable-sun-avoidance]
[--disable-hwp-interlocks]
Agent Options
- --acu-config
- --no-processes
Default: False
- --ignore-axes
Possible choices: el, az, third, none
One or more axes to ignore.
- --disable-idle-reset
Disable idle_reset, even for LAT.
Default: False
- --min-el
Override the minimum el defined in platform config.
- --max-el
Override the maximum el defined in platform config.
- --disable-sun-avoidance
Disable Sun Avoidance before startup.
Default: False
- --disable-hwp-interlocks
Disable HWP interlocks before startup.
Default: False
Dependencies
The soaculib package must be installed to use this Agent. This can be installed via:
$ pip install 'soaculib @ git+https://github.com/simonsobs/soaculib.git@master'
Additionally, socs should be installed with the acu group:
$ pip install -U socs[acu]
Configuration File Examples
Below are configuration examples for the ocs config file and for soaculib.
OCS Site Config
To configure the ACU Agent we need to add a block to the ocs configuration file. An example configuration block using all availabile arguments is below:
{'agent-class': 'ACUAgent',
'instance-id': 'acu-satp1',
'arguments': [['--acu-config', 'satp1']],
}
soaculib
The ACU configuration file is parsed by soaculib. A template is
included within that library; it can be copied into the OCS
configuration area and modified. soaculib will use the config
file pointed to by environment variable ACU_CONFIG. The
--acu_config agent instance argument must correspond to a key in
the devices section of the ACU configuration file.
Here is an example of a device definition for a SATP:
'satp1': {
'base_url': 'http://192.168.1.111:8100',
'readonly_url': 'http://192.168.1.111:8110',
'dev_url': 'http://192.168.1.111:8080',
'interface_ip': '192.168.1.110',
'streams': {
'main': {
'acu_name': 'PositionBroadcast',
'port': 10004,
'schema': 'v5'
},
'ext': {
'acu_name': 'PositionBroadcastExt',
'port': 10005,
'active': False,
},
},
'platform': 'satp',
'motion_limits': {
'azimuth': {
'lower': -90.0,
'upper': 450.0,
'accel': 4.0,
},
'elevation': {
'lower': 18.5,
'upper': 90.0,
},
'boresight': {
'lower': -55.0,
'upper': 55.0,
},
'axes_sequential': False,
},
'ignore_axes': [],
'named_positions': {
'home': [180, 60]
},
'scan_params': {},
'sun_avoidance': {},
'hwp_interlocks': {},
}
See next section for some details of the configurable parameters.
Configuration Notes
Hostnames and port numbers
The base_url, readonly_url, and dev_url should all point
to the IP address of the ACU. The port numbers refer to different
servers or server configurations and probably don’t need to be
altered.
The interface_ip is the address of the system where the Agent is
running – this is used to direct UDP frames from the ACU to the
Agent.
Motion limits and restrictions
Settings that affect motion limits, scan parameters, and axis ignorance:
motion_limits:Axis limits (
azimuth,elevation,boresight): These are software limits enforced in the Agent. They are distinct from the ACU software limits, and from the hardware limits (enforced by limit switches and a PLC). Generally these will be set equal to, or sub-ranges of, the ACU software limits. Note the identifier “boresight” is used for both SATP and the LAT co-rotator. Thelowerlimit should be numerically less than theupperlimit. If specified, theaccelparameter (in deg/s/s) is used to limit the minimum turn-around time in ProgramTrack mode.axes_sequential: If True, then (az, el) moves are not performed simultaneously. First one axis is moved, and then the next. The Sun Avoidance code is made aware of this restriction and optimizes paths with that constraint in mind.
ignore_axes: If set, should be a list containing any combination of “az”, “el”, “third”, and “none”. See further explanation inACUAgent.scan_params: Default scan parameters; currentlyaz_speed(float, deg/s) andaz_accel(float, deg/s/s). If not specfied, these are given default values depending on the platform type.
Other agent functions
The named_positions is a dict mapping a position name
(e.g. “home”, “stow”) to a list [az, el]. These are made
available to the go_to_named task. Here’s an example (in YAML
style):
named_positions:
home: [180, 60]
stow: [180, 20]
The sun_avoidance block controls Sun avoidance parameters, most
importantly the default enable/disable of Sun Avoidance and the
exclusion radius. Any of the “user-defined policy” parameters can be
included in this block. Additionally, the key enabled (boolean)
determines whether active Sun Avoidance is enabled on agent startup.
Here are are some examples:
# Enable Sun avoidance, with 39 deg radius.
sun_avoidance:
enabled: true
exclusion_radius: 39
# Do not enable Sun avoidance by default, but extend the danger
# zone to 2 hours, for information purposes.
sun_avoidance:
enabled: false
min_sun_time: 7200
When Sun avoidance is configured, but not enabled on startup, it can
be enabled during the run through the
update_sun Task.
Sun Avoidance
The Sun’s position, and the potential danger of the Sun to the
equipment, is monitored and reported by the monitor_sun Process.
If enabled to do so, this Process can trigger the escape_sun_now
Task, which will cause the platform to move to a Sun-safe position.
The parameters used by an Agent instance for Sun Avoidance are determined like this:
Default parameters for each platform (LAT and SATP) are in the Agent code.
On start-up of the Agent, the ACU config file
sun_avoidanceblock is parsed, and that modifies the platform default parameters.Command-line parameters can modify Sun Safety, through the
--disable-sun-avoidance,--min-el, and--max-elarguments.
The avoidance policy is defined by a few key parameters and concepts;
please see the descriptions of sun_dist, sun_time,
exclusion_radius, and more in the socs.agents.acu.avoidance
module documentation.
When Sun Avoidance is active (active_avoidance is True), the
following will be enforced:
When a user initiates the
go_toTask, the target point of the motion will be checked. If it is not Sun-safe, the Task will exit immediately with an error. If the Task cannot find a set of moves that are Sun-safe and that do not violate other requirements (azimuth and elevation limits; theel_dodgingpolicy), then the Task will exit with error. The move may be executed as a series of separate legs (e.g. the Task may move first to an intermediate elevation, then slew in azimuth, then continue to the final elevation) rather than simulataneously commanding az and el motion.When a user starts the
generate_scanProcess, the sweep of the scan will be checked for Sun-safety, and the Process will exit with error if it is not. Furthermore, any movement required prior to starting the scan will be checked in the same way as for thego_toTask.If the platform, at any time, enters a position that is not Sun-safe, then an Escape will be Initiated. During an Escape, any running
go_toorgenerate_scanoperations will be cancelled, and further motions are blocked. The platform will be driven to a position at due North or due South. The current elevation of the platform will be preserved, unless that is not Sun-safe (in which case lower elevations will be attempted). The Escape feature is active, even when motions are not in progress, as long as themonitor_sunProcess is running. However – the Escape operation requires that the platform be in Remote operation mode, with no persistent faults.
When HWP Interlocks are active (see next section), then HWP state may affect the elevations available for Sun escape.
HWP Interlocks
The ACU Agent can be configured to block certain kinds of motion, depending on the state of the Half-Wave Plate. When configured, the ACU will monitor the session data of an HWPSupervisor agent instance to determine:
the “grip_state”: this can be one of “ungripped”, “warm”, “cold”, or “unknown”,
the “spin_state”: this can be one of “spinning”, “not_spinning”, “unknown”.
These two HWP state strings are used, along with the current elevation (or the elevation range of a move) to determine whether a given move should be allowed.
The Agent determines what kinds of moves are permitted by finding rules in the configuration table that match with the current spin and grip states, and that also overlap with the required range of elevation. If any matching rule grants motion on an axis, then that is sufficient for the motion to be allowed.
Here is an example hwp_interlocks configuration block:
hwp_interlocks:
enabled: true
limit_sun_avoidance: false
rules:
- el_range: [20, 90]
grip_states: ['warm', 'cold']
spin_states: ['*']
allow_moves:
el: true
az: true
third: false
- el_range: [40, 70]
grip_states: ['*']
spin_states: ['*']
allow_moves:
el: true
az: true
third: false
- el_range: [40, 70]
grip_states: ['ungripped']
spin_states: ['not_spinning']
allow_moves:
el: false
az: false
third: true
In words, the above example says the following:
If the elevation is in (40, 70), any az and el moves are permitted, regardless of the HWP state.
In that same elevation range, third axis (boresight) moves are permitted only if the HWP is not gripped and not spinning.
To get down to el of 20 or up to el of 90, you must be in “warm” or “cold” gripped states. In that extended range, you can move in az and el freely, even if spin state is not known.
Ignore these restrictions when a motion needs to be made for Sun Avoidance purposes.
If HWP motion constraints should also apply to Sun avoidance
“escapes”, then the setting limit_sun_avoidance should be set to
true (this is the default). Only the limits associated with
elevation axis motion are considered here – i.e. the allowed
elevation movement range, for the current HWP state, is used as the
allowable elevation range for escapes. Sun escape azimuth motion is
not restricted by HWP state.
For more syntax details see HWPInterlocks, MotionRule.
Exercisor Mode
The agent can run itself through various motion patterns, using the
Process exercise. This process is only visible if the agent is
invoked with the --exercise-plan argument and a path to the
exercise plan config file. Here is an example config file:
satp1:
settings:
use_boresight: false
steps:
- type: 'elnod'
repeat: 2
- type: 'grid'
duration: 3600
- type: 'schedule'
files:
- /path/to/schedule1.txt
- /path/to/schedule2.txt
duration: 3600
dwell_time: 600
- type: 'grease'
duration: 900
Note that the root level may contain multiple entries; the key
corresponds to the ACU config block name, which would correspond to
the ACU agent --acu-config argument.
The exercisor writes some diagnostic and timing information to a feed
called activity.
Agent API
- class socs.agents.acu.agent.ACUAgent(agent, acu_config='guess', startup=False, ignore_axes=None, disable_idle_reset=False, disable_sun_avoidance=False, disable_hwp_interlocks=False, min_el=None, max_el=None)[source]
Agent to acquire data from an ACU and control telescope pointing with the ACU.
- Parameters:
acu_config (str) – The configuration for the ACU, as referenced in aculib.configs. Default value is ‘guess’.
startup (bool) – If True, immediately start the main monitoring processes for status and UDP data.
ignore_axes (list of str) – List of axes to “ignore”. “ignore” means that the axis will not be commanded. If a user requests an action that would otherwise move the axis, it is not moved but the action is assumed to have succeeded. The values in this list should be drawn from “az”, “el”, “third”, and “none”. This argument replaces the setting from the config file. (“none” entries will simply be ignored.)
disable_idle_reset (bool) – If True, don’t auto-start idle_reset process for LAT.
disable_sun_avoidance (bool) – If set, start up with Sun Avoidance completely disabled.
disable_hwp_interlocks (bool) – If set, start up with HWP Interlocks disabled.
min_el (float) – If not None, override the default configured elevation lower limit.
max_el (float) – If not None, override the default configured elevation upper limit.
- idle_reset()[source]
Process - To prevent LAT from going into Survival mode, do something on the command interface every so often. (The default inactivity timeout is 1 minute.)
- monitor()[source]
Process - Refresh the cache of SATP ACU status information and report it on the ‘acu_status’ and ‘acu_status_influx’ HK feeds.
Summary parameters are ACU-provided time code, Azimuth mode, Azimuth position, Azimuth velocity, Elevation mode, Elevation position, Elevation velocity, Boresight mode, and Boresight position.
The session.data of this process is a nested dictionary. Here’s an example:
{ "StatusDetailed": { "Time": 81.661170959322, "Year": 2023, "Azimuth mode": "Stop", "Azimuth commanded position": -20.0012, "Azimuth current position": -20.0012, "Azimuth current velocity": 0.0002, "Azimuth average position error": 0, "Azimuth peak position error": 0, "Azimuth computer disabled": false, ... }, "Status3rdAxis": { "3rd axis Mode": "Stop", "3rd axis commanded position": 77, "3rd axis current position": 77, "3rd axis computer disabled": "No Fault", ... }, "StatusShutter": { "Shutter Closed": false, ... }, "Hvac": { "Booster EL Housing Failure": false, "Booster EL Housing on": false, ... }, "StatusResponseRate": 19.237531827325963, "PlatformType": "satp", "IgnoredAxes": [], "NamedPositions": { "home": [ 180, 40 ] }, "DefaultScanParams": { "az_speed": 2.0, "az_accel": 1.0, }, "connected": True, }Differences between SATP and LAT structures:
The PlatformType reports “satp” for SATP and “ccat” for LAT.
In the case of an SATP, the Status3rdAxis is not populated; the Boresight info can be found in StatusDetailed. In the case of the LAT, the corotator info is queried separately and stored under Status3rdAxis.
The StatusShutter and Hvac entries will be populated for the LAT, but empty for SATP.
- broadcast(auto_enable=True)[source]
Process - Read UDP data from the port specified by self.acu_config, decode it, and publish to HK feeds. Full resolution (200 Hz) data are written to feed “acu_udp_stream” while 1 Hz decimated are written to “acu_broadcast_influx”. The 1 Hz decimated output are also stored in session.data.
- Parameters:
auto_enable (bool) – If True, the Process will try to configure and (re-)enable the UDP stream if at any point the stream seems to drop out.
Notes
The session.data looks like this (this is for a SATP running with servo details in the UDP output):
{ "Time": 1679499948.8234625, "Corrected_Azimuth": -20.00112176010607, "Corrected_Elevation": 50.011521050839434, "Corrected_Boresight": 29.998428712246067, "Raw_Azimuth": -20.00112176010607, "Raw_Elevation": 50.011521050839434, "Raw_Boresight": 29.998428712246067, "Azimuth_Current_1": -0.000384521484375, "Azimuth_Current_2": -0.0008331298828125, "Elevation_Current_1": 0.003397979736328125, "Boresight_Current_1": -0.000483856201171875, "Boresight_Current_2": -0.000105743408203125, "Azimuth_Vel_1": -0.000002288818359375, "Azimuth_Vel_2": 0, "Az_Vel_Act": -0.0000011444091796875, "Az_Vel_Des": 0, "Az_Vffw": 0, "Az_Pos_Des": -20.00112176010607, "Az_Pos_Err": 0 }
- broadcast_ext(auto_enable=True)[source]
Process - Read UDP data from the “ext” UDP stream, as defined in self.acu_config. Like the broadcast process, this will write full rate data to an aggregator feed and downsampled data to an influx feed.
- Parameters:
auto_enable (bool) – If True, the Process will try to configure and (re-)enable the UDP stream if at any point the stream seems to drop out.
Notes
The session.data is as you would find for the broadcast process with a “Time” field and a bunch of readings, updated about once per second.
- go_to(az, el, end_stop=False)[source]
Task - Move the telescope to a particular point (azimuth, elevation) in Preset mode. When motion has ended and the telescope reaches the preset point, the function returns.
- Parameters:
az (float) – destination angle for the azimuth axis
el (float) – destination angle for the elevation axis
end_stop (bool) – put the commanded axes in Stop mode at the end of the motion
Notes
If az or el is unspecified (None), the axis will not be commanded to a new position and will not be put in Preset mode, and will not be put in Stop (if end_stop) after motion.
When omitting el, and if Sun Avoidance path-finding decides an elevation change is required to travel from the current position to the implicit target position, the task will exit with error.
- set_boresight(target, end_stop=False)[source]
Task - Move the telescope to a particular third-axis angle.
- Parameters:
target (float) – destination angle for boresight rotation
end_stop (bool) – put axes in Stop mode after motion
- go_to_named(target, end_stop=True)[source]
Task - Move the telescope to a named position, e.g. “home”, that has been configured through command line args.
- Parameters:
target (str) – name of the target position.
end_stop (bool) – put axes in Stop mode after motion
- set_speed_mode(speed_mode)[source]
Task - Set the ACU Speed Mode. This affects motion when in Preset mode, such as when using go_to in this Agent. It should not affect the speed of scans done in ProgramTrack mode.
- Parameters:
speed_mode (str) – ‘high’ or ‘low’.
Notes
The axes must be in Stop mode for this to work. This task will return an error if the command appears to have failed.
The actual speed and acceleration settings for the “high” and “low” (perhaps called “aux”) settings must be configured on the ACU front panel.
- set_scan_params(az_speed=None, az_accel=None, reset=False))[source]
Task - Update the default scan parameters, used by generate_scan if not passed explicitly.
- Parameters:
az_speed (float, optional) – The azimuth scan speed.
az_accel (float, optional) – The (average) azimuth acceleration at turn-around.
reset (bool, optional) – If True, reset all params to default values before applying any updates passed explicitly here.
- stop_and_clear(all_axes=False)[source]
Task - Change the azimuth, elevation, and 3rd axis modes to Stop; also clear the ProgramTrack stack.
- Parameters:
all_axes (bool) – Send Stop to all axes, even ones user has requested to be ignored.
- special_action(action, force=False, elsync_ref=None)[source]
Task - Perform a special action or set a special mode.
- Parameters:
action (str) – Action to perform. See notes.
force (bool) – Perform the action even if conditions suggest it need not or should not be run.
elsync_ref (float) – For action=’elsync’, sets the reference elevation for the locked co-rotator mode. (This is the negative of the ACU offset parameter.)
Notes
‘unstow’: Set the el and az axis modes to “UnStow”. This is used to recover the LAT from “maintenance stow” position, where el=-90 and pins inserted. The Task returns after setting the mode; transition to Stop will normally occur after a few seconds.
‘elsync’: Put the LAT corotator into ElSync mode. If elsync_ref is provided, that is sent to the ACU first. Otherwise the offset is left unchanged. Unless force=True, the corotator axis should be in Stop before when this is called.
- fromfile_scan(filename, absolute_times=False, azonly=True)[source]
Process - Upload and execute a scan pattern from a file.
- Parameters:
filename (str) – full path to the track file.
absolute_times (bool) – If True, the track timestamps are taken at face value. Otherwise, the timestamps are treated as relative to the track start time, which will be a few seconds in the future from when this function is called.
azonly (bool) – If True, the elevation part of the track will be uploaded but the el axis won’t be put in ProgramTrack mode. It might be put in Stop mode though.
Notes
See
drivers.from_filefor discussion of the file structure.
- generate_scan(az_endpoint1, az_endpoint2, az_speed=None, az_accel=None, el_endpoint1=None, el_endpoint2=None, el_speed=None, num_scans=None, start_time=None, wait_to_start=None, step_time=None, az_start='end', az_drift=None, az_only=True, scan_upload_length=None)[source]
Process - Scan generator, currently only works for constant-velocity az scans with fixed elevation.
- Parameters:
az_endpoint1 (float) – first endpoint of a linear azimuth scan
az_endpoint2 (float) – second endpoint of a linear azimuth scan
az_speed (float) – azimuth speed for constant-velocity scan
az_accel (float) – turnaround acceleration for a constant-velocity scan
el_endpoint1 (float) – first endpoint of elevation motion. In the present implementation, this will be the constant elevation declared at every point in the track.
el_endpoint2 (float) – this is ignored.
el_speed (float) – this is ignored.
num_scans (int or None) – if not None, limits the scan to the specified number of constant velocity legs. The process will exit without error once that has completed.
start_time (float or None) – a unix timestamp giving the time at which the scan should begin. The default is None, which means the scan will start immediately (but taking into account the value of wait_to_start).
wait_to_start (float) – number of seconds to wait before starting a scan, in the case that start_time is None. The default is to compute a minimum time based on the scan parameters and the ACU ramp-up algorithm; this is typically 5-10 seconds.
step_time (float) – time, in seconds, between points on the constant-velocity parts of the motion. The default is None, which will cause an appropriate value to be chosen automatically (typically 0.1 to 1.0).
az_start (str) – part of the scan to start at. To start at one of the extremes, use ‘az_endpoint1’, ‘az_endpoint2’, or ‘end’ (same as ‘az_endpoint1’). To start in the midpoint of the scan use ‘mid_inc’ (for first half-leg to have positive az velocity), ‘mid_dec’ (negative az velocity), or ‘mid’ (velocity oriented towards endpoint2).
az_drift (float) – if set, this should be a drift velocity in deg/s. The scan extrema will move accordingly. This can be used to better follow compact sources as they rise or set through the focal plane.
az_only (bool) – if True (the default), then only the Azimuth axis is put in ProgramTrack mode, and the El axis is put in Stop mode.
scan_upload_length (float) – number of seconds for each set of uploaded points. If this is not specified, the track manager will try to use as short a time as is reasonable.
Notes
Note that all parameters are optional except for az_endpoint1 and az_endpoint2. If only those two parameters are passed, the Process will scan between those endpoints, with the elevation axis held in Stop, indefinitely (until Process .stop method is called)..
- monitor_sun()[source]
Process - Monitors and reports the position of the Sun; maintains a Sun Safety Map for verifying that moves and scans are Sun-safe; triggers a “Sun escape” if the boresight enters an unsafe position.
The monitoring functions are always active (as long as this process is running). But the escape functionality must be explicitly enabled (through the default platform configuration, command line arguments, or the update_sun task).
Session data looks like this:
{ "timestamp": 1698848292.5579932, "active_avoidance": false, "disable_until": 0, "block_motion": false, "recompute_req": false, "next_drill": null, "safety_map_kw": { "sun_time_shift": 0 }, "policy": { "exclusion_radius": 20, "el_horizon": 10, "min_sun_time": 1800, "response_time": 7200, "min_az": -90, "max_az": 450, "min_el": 18.5, "max_el": 90 }, "sun_pos": { "map_exists": true, "map_is_old": false, "map_ref_time": 1698848179.1123455, "platform_azel": [ 90.0158, 20.0022 ], "sun_radec": [ 216.50815789438036, -14.461844389380719 ], "sun_azel": [ 78.24269024936028, 60.919554369324096 ], "sun_down": false, "sun_dist": 41.75087242151837, "sun_safe_time": 71760, "platform_down": false }, "avoidance": { "safety_unknown": false, "warning_zone": false, "danger_zone": false, "escape_triggered": false, "escape_active": false, "last_escape_time": 0, "sun_is_real": true, "platform_is_moveable": true } }In debugging, the Sun position might be falsified. In that case the “sun_pos” subtree will contain an entry like this:
"WARNING": "Fake Sun Position is in use!",
and “avoidance”: “sun_is_real” will be set to false. (No other functionality is changed when using a falsified Sun position; flags are computed and actions decided based on the false position.)
- update_sun(reset=None, enable=None, temporary_disable=None, escape=None, exclusion_radius=None, shift_sun_hours=None)[source]
Task - Update Sun monitoring and avoidance parameters.
All arguments are optional.
- Parameters:
reset (bool) – If True, reset all sun_params to the platform defaults. (The “defaults” includes any overrides specified on Agent command line.)
enable (bool) – If True, enable active Sun avoidance. If avoidance was temporarily disabled it is re-enabled. If False, disable active Sun avoidance (non-temporarily).
temporary_disable (float) – If set, disable Sun avoidance for this number of seconds.
escape (bool) – If True, schedule an escape drill for 10 seconds from now.
exclusion_radius (float) – If set, change the FOV radius (degrees), for Sun avoidance purposes, to this number.
shift_sun_hours (float) – If set, compute the Sun position as though it were this many hours in the future. This is for debugging, testing, and work-arounds. Pass zero to cancel.
- escape_sun_now()[source]
Task - Take control of the platform, and move it to a Sun-Safe position. This will abort/stop any current go_to or generate_scan, identify the safest possible path to North or South (without changing elevation, if possible), and perform the moves to get there.
- monitor_hwp()[source]
Process - Monitors the state of a HWP, by querying a HWPSupervisor’s
monitorProcess session data. Assesses what motions are permitted, given the HWP state.session.data example:
{ "interlocks_config": { "configured": true, "enabled": true, "instance_id": "hwp-supervisor", "limit_sun_avoidance": true, "tolerance": 0.1, }, "supervisor_data": { "timestamp": 1744692973.185377, "ok": true, "err_msg": "", "_grip_brakes": [1, 1, 1], "_grip_state": "ungripped", "_is_spinning": true, "_target_freq": 2.1, "grip_state": "ungripped", "spin_state": "spinning", "request_block_motion": null, "request_block_motion_timestamp": null }, "allowed": { "el": [true, [40, 70], [ [40, 70], ], "az": [true, [40, 70], [ [40, 70], ], "third": [false, null, []], } }
- update_hwp(enable=None)[source]
Task - Update HWP state monitoring and safety parameters.
All arguments are optional.
- Parameters:
enable (bool) – If True, enable HWP state checks. If False, disable HWP state checks (non-temporarily).
Example Clients
Below is an example client demonstrating a go-to task followed by a scan. Note that all tasks and the generate_scan process can be run while the data acquisition processes are running:
from ocs.matched_client import MatchedClient
def upload_track(scantype, testing, azpts, el, azvel, acc, ntimes):
acu_client = MatchedClient('acu1')
acu_client.go_to.start(az=azpts[0], el=el, wait=1)
acu_client.go_to.wait()
acu_client.run_specified_scan.start(scantype=scantype, testing=testing, azpts=azpts, el=el, azvel=azvel, acc=acc, ntimes=ntimes)
acu_client.run_specified_scan.wait()
if __name__ == '__main__':
upload_track('linear_turnaround_sameends', True, (120., 160.), 35., 1., 4, 3)
Supporting APIs
drivers (Scanning support)
- socs.agents.acu.drivers.DAY = 86400
The number of seconds in a day.
- class socs.agents.acu.drivers.TrackPoint(timestamp: float, az: float, el: float, az_vel: float, el_vel: float, az_flag: int = 0, el_flag: int = 0, group_flag: int = 0)[source]
- timestamp: float
Timestamp of the point (unix timestamp)
- az: float
Azimuth (deg).
- el: float
Elevation (deg).
- az_vel: float
Azimuth velocity (deg/s).
- el_vel: float
Elevation velocity (deg/s).
- az_flag: int = 0
0 if stationary, 1 if non-final point of const-vel scan segment; 2 if final point of const-vel segment.
- Type:
Az flag
- el_flag: int = 0
like az_flag but for el.
- Type:
El flag
- group_flag: int = 0
If 1, indicates that once this point is uploaded the next point in sequence also needs to be soon uploaded. Used at start of a new const-vel scan segment.
- socs.agents.acu.drivers.get_track_points_text(tpl, timestamp_offset=None, with_group_flag=False, text_block=False)[source]
Get a list of ProgramTrack lines for upload to ACU.
- Parameters:
tpl (list) – list of TrackPoint to convert.
timestamp_offset (float) – offset to add to all timestamps before rendering (defaults to 0).
with_group_flag (bool) – If True return each line as (group_flag, text).
text_block (bool) – If True, return all lines joined together into a single string.
- socs.agents.acu.drivers.from_file(filename, fmt=None)[source]
Load a ProgramTrack trajectory from a file. This function supports two formats. The modern format is a pickle file. The older numpy format is also supported.
- Parameters:
filename (str) – Full path to the file.
fmt (str) – Optional, one of “pickle” or “numpy”. If this is unspecified, the code will assume pickle format unless the filename ends in “npy”.
- Returns:
- Object containing the loaded points and
supporting config for ProgramTrack mode.
- Return type:
FromFileScan
Notes
For the pickle-based file format, the file must encode a single dict. Here is a minimal example:
{ 'timestamp': [1747233498, 1747233499, ..., 1747233523], 'az': [180, 181, ..., 160], 'el': [60, 60, ..., 60], }Those 3 required entries are “vectors”, with the same length. The vectors may be any iterable type but please use lists or ndarrays. Optionally, the user may specify these other vectors:
‘az_vel’: the az velocity at each point.
‘el_vel’: the el velocity at each point.
‘az_flag’: the az flag.
‘el_flag’: the el flag.
‘group_flag’: the point grouping flag.
If not provided, the “vel” vectors will be computed from the gradient of the position vectors. The “flag” vectors will default to all 0. See
TrackPointfor purpose of the various flags.The following settings (stated with their default values) may also be included in the dict:
‘free_form’ (bool, False): Specifies whether the track should be run with the turn-around profiler disabled and spline (rather than linear) interpolation enabled. When false, the ACU will do linear interpolation and automatic profiling of turn-arounds, between constant velocity segments. Setting free_form=False is appropriate for constant elevation, constant az speed scans. Set free_form=True for more complex scans.
‘loopable’ (bool, False): Specifies whether the track should be repeated, forever. When this is the case, the final point of the track (i.e. the last point in all the vectors) is ignored except for the timestamp, which is used to set the time at which the next loop iteration is started.
‘preamble_count’ (int, 0): If loopable, this specifies the number of points in the track (i.e. in each vector) that are not included in the loopable portion. This permits the track to have a ramp-up, or partial initial scan segment, prior to entering the repetable template.
The older numpy-based format does not support the additional settings. The numpy file must contain an iterable with 5 or 7 entries, where all entries are 1-d arrays of the same length. The first 5 arrays will correspond to ‘timestamp’, ‘az’, ‘el’, ‘az_vel’, ‘el_vel’. The 2 optional arrays are ‘az_flag’ and ‘el_flag’.
- socs.agents.acu.drivers.timecode(acutime, now=None)[source]
Takes the time code produced by the ACU status stream and returns a unix timestamp.
- Parameters:
acutime (float) – The time recorded by the ACU status stream, corresponding to the fractional day of the year.
now (float) – The time, as unix timestamp, to assume it is now. This is for testing, it defaults to time.time().
- socs.agents.acu.drivers.generate_constant_velocity_scan(az_endpoint1, az_endpoint2, az_speed, acc, el_endpoint1, el_endpoint2, el_speed=0, num_batches=None, num_scans=None, start_time=None, wait_to_start=10.0, step_time=1.0, batch_size=500, az_start='mid_inc', az_first_pos=None, az_drift=None)[source]
Python generator to produce times, azimuth and elevation positions, azimuth and elevation velocities, azimuth and elevation flags for arbitrarily long constant-velocity azimuth scans.
- Parameters:
az_endpoint1 (float) – azimuth endpoint for the scan start
az_endpoint2 (float) – second azimuth endpoint of the scan
az_speed (float) – speed of the constant-velocity azimuth motion
acc (float) – turnaround acceleration for the azimuth motion at the endpoints
el_endpoint1 (float) – elevation endpoint for the scan start
el_endpoint2 (float) – second elevation endpoint of the scan. For constant az scans, this must be equal to el_endpoint1.
el_speed (float) – speed of the elevation motion. For constant az scans, set to 0.0
num_batches (int or None) – sets the number of batches for the generator to create. Default value is None (interpreted as infinite batches).
num_scans (int or None) – if not None, limits the points returned to the specified number of constant velocity legs.
start_time (float or None) – a ctime at which to start the scan. Default is None, which is interpreted as starting now + wait_to_start.
wait_to_start (float) – number of seconds to wait between start_time and when the scan actually starts. Default is 10 seconds.
step_time (float) – time between points on the constant-velocity parts of the motion. Default value is 1.0 seconds. Minimum value is 0.05 seconds.
batch_size (int) – number of values to produce in each iteration. Default is 500. Batch size is reset to the length of one leg of the motion if num_batches is not None.
az_start (str) – part of the scan to start at. To start at one of the extremes, use ‘az_endpoint1’, ‘az_endpoint2’, or ‘end’ (same as ‘az_endpoint1’). To start in the midpoint of the scan use ‘mid_inc’ (for first half-leg to have positive az velocity), ‘mid_dec’ (negative az velocity), or ‘mid’ (velocity oriented towards endpoint2).
az_first_pos (float) – If not None, the first az scan will start at this position (but otherwise proceed in the same starting direction).
az_drift (float) – The rate (deg / s) at which to shift the scan endpoints in time. This can be used to better track celestial sources in targeted scans.
- Yields:
points (list) –
- a list of TrackPoint objects. Raises
StopIteration once exit condition, if defined, is met.
- socs.agents.acu.drivers.plan_scan(az_end1, az_end2, el, v_az=1, a_az=1, az_start=None)[source]
Determine some important parameters for running a ProgramTrack scan with the desired end points, velocity, and mean turn-around acceleration.
These get complicated in the limit of high velocity and narrow scan.
- Returns:
A dict with outputs of the calculations. The following items must be considered when generating and posting the track points:
’step_time’: The recommended track point separation, in seconds.
’wait_to_start’: The minimum time (s) between initiating ProgramTrack mode and the first uploaded point’s timestamp.
’init_az’: The az (deg) at which to position the telescope before beginning the scan. This takes into account any “ramp up” that needs to occur and the fact that such ramp up needs to be finished before the ACU starts profiling the first turn-around.
The following dict items provide additional detail / intermediate results:
’scan_start_buffer’: Minimum amount (deg of az) by which to shift the start of the first scan leg in order to satisfy the requirements for az_prep and az_rampup. This ultimately is what can make init_az different from the natural first leg starting point. This parameter is always non-negative.
’turnprep_buffer’: Minimum azimuth travel required for ProgramTrack to prepare a turn-around.
’rampup_buffer’: Minimum azimuth travel required for ProgramTrack to ramp up to the first leg velocity. Degrees, positive.
’rampup_time’: Number of seconds before the first track point where the platform could start moving (as part of smooth acceleration into the initial velocity).
avoidance (Sun Avoidance)
When considering Sun Safety of a boresight pointing, we consider an
exclusion zone around the Sun with a user-specified radius. This is
called the exclusion_radius or the field-of-view radius.
The Safety of the instrument at any given moment is parametrized by two numbers and a boolean:
sun_distThe separation between the Sun and the boresight (az, el), in degrees. This is defined without regards for the horizon – i.e. even if the Sun and the boresight are both below “the horizon”, the sun distance can be small.
sun_timeThe minimum time, in seconds, which must elapse before the current (az, el) pointing of the boresight will lie within the exclusion radius of the Sun. Although some positions will “never” see the Sun, the sun_time for such positions is set to about 2 days, as a place-holder for “the future”. When the boresight is well below the horizon, the sun_time is set to “never”, i.e. 2 days. When the sun is below the horizon, then the sun_time will always be at least the time until next sun-rise.
sun_downA boolean indicating whether the sun is below the horizon elevation (which is a parameter of the instrument).
While the sun_dist and sun_down are important indicators of
whether the instrument is currently in immediate danger, the
sun_time is useful for looking forward in order to avoid positions
that will soon be dangerous.
The user-defined policy for Sun Safety is captured in the following settings:
exclusion_radiusThe radius, in degres, of a disk centered on the Sun that must be avoided by the boresight.
min_sun_timeAn (az, el) position is considered unsafe (danger zone) if the
sun_timeis less than themin_sun_time. (Expressed in seconds.)response_timeAn (az, el) position is considered vulnerable (warning zone) if the
sun_timeis less than theresponse_time. This is intended to represent the maximum amount of time it could take an operator to reach the instrument and secure it, were motion to stall unexpectedly. (Expressed in seconds.)el_horizonThe elevation (in degrees) below which the Sun may be considered as invisible to the instrument.
el_dodgingThis setting affects how point-to-point motions are executed, with respect to what elevations may be used in intermediate legs of the trajectory. When this is False, the platform is restricted to travel only at elevations that lie between the initial and the target elevation. When True, the platform is permitted to travel at other elevations, all the way up to the limits of the platform. Using True is helpful to find Sun-safe trajectories in some circumstances. But False is helpful if excess elevation changes are potentially disturbing to the cryogenics. This setting only affects point-to-point motions; “escape” paths will always consider all available elevations.
axes_sequentialIf True, a point-to-point motion will only be accepted if each leg is a constant el or constant az move. When this setting is False, legs that move simultaneously in az and el are permitted (and probably preferred) as long as they are safe.
A “Sun-safe” position is a pointing of the boresight that currently
has a sun_time that meets or exceeds the min_sun_time
parameter.
- socs.agents.acu.avoidance.DEFAULT_POLICY = {'axes_sequential': False, 'el_dodging': False, 'el_horizon': 0, 'exclusion_radius': 20, 'max_az': 405, 'max_el': 90, 'min_az': -45, 'min_el': 0, 'min_sun_time': 3600, 'response_time': 14400}
Default policy to apply when evaluating Sun-safety and planning trajectories. Note the Agent code may apply different defaults, based on known platform details.
- class socs.agents.acu.avoidance.SunTracker(policy=None, site=None, map_res=0.5, sun_time_shift=None, fake_now=None, compute=True, base_time=None)[source]
Provide guidance on what horizion coordinate positions and trajectories are sun-safe.
- Parameters:
policy (dict) – Exclusion policy parameters. See module docstring, and DEFAULT_POLICY. The policy should also include {min,max}_{el,az}, giving the limits supported by those axes.
site (EarthlySite or None) – Site to use; default is the SO LAT. If not None, pass an so3g.proj.EarthlySite or compatible.
map_res (float, deg) – resolution to use for the Sun Safety Map.
sun_time_shift (float, seconds) – For debugging and testing, compute the Sun’s position as though it were this many seconds in the future. If None or zero, this feature is disabled.
fake_now (float, seconds) – For debugging and testing, replace the tracker’s computation of the current time (time.time()) with this value. If None, this testing feature is disabled.
compute (bool) – If True, immediately compute the Sun Safety Map by calling .reset().
base_time (unix timestamp) – Store this base_time and, if compute is True, pass it to .reset().
- reset(base_time=None)[source]
Compute and store the Sun Safety Map for a specific timestamp.
This basic computation is required prior to calling other functions that use the Sun Safety Map.
- check_trajectory(az, el, t=None, raw=False)[source]
For a telescope trajectory (vectors az, el, in deg), assumed to occur at time t (defaults to now), get the minimum value of the Sun Safety Map traversed by that trajectory. Also get the minimum value of the Sun Distance map.
This requires the Sun Safety Map to have been computed with a base_time in the 24 hours before t.
Returns a dict with entries:
'sun_time': Minimum Sun Safety Time on the traj.'sun_time_start': Sun Safety Time at first point.'sun_time_stop': Sun Safety Time at last point.'sun_dist_min': Minimum distance to Sun, in degrees.'sun_dist_mean': Mean distance to Sun.'sun_dist_start': Distance to Sun, at first point.'sun_dist_stop': Distance to Sun, at last point.
- get_sun_pos(az=None, el=None, t=None)[source]
Get info on the Sun’s location at time t. If (az, el) are also specified, returns the angular separation between that pointing and Sun’s center.
- show_map(axes=None, show=True)[source]
Plot the Sun Safety Map and Sun Distance Map on the provided axes (a list).
- analyze_paths(az0, el0, az1, el1, t=None, plot_file=None, dodging=True)[source]
Design and analyze a number of different paths between (az0, el0) and (az1, el1). Return the list, for further processing and choice.
- find_escape_paths(az0, el0, t=None, debug=False)[source]
Design and analyze a number of different paths that move from (az0, el0) to a sun safe position. Return the list, for further processing and choice.
- select_move(moves, raw=False)[source]
Given a list of possible “moves”, select the best one. The “moves” should be like the ones returned by
analyze_paths.The best move is determined by first screening out dangerous paths (ones that pass close to Sun, move closer to Sun unnecessarily, violate axis limits, etc.) and then identifying paths that minimize danger (distance to Sun; Sun time) and path length.
If raw=True, a debugging output is returned; see code.
- Returns:
(best_move, decisions)
best_move– the element of moves that is safest. If no safe move was found, None is returned.decisions- List of dicts, in one-to-one correspondence withmoves. Each decision dict has entries ‘rejected’ (True or False) and ‘reason’ (string description of why the move was rejected outright).- Return type:
(dict, list)
hwp_iface (HWP Interlocks)
Interface to HWPSupervisor.
- class socs.agents.acu.hwp_iface.MotionRule(el_range: tuple, grip_states: list, spin_states: list, allow_moves: dict)[source]
- el_range: tuple
Tuple giving the elevation range to which this rule applies.
- grip_states: list
List of grip_state values to which this rule applies. Note “*” matches any grip_state.
- spin_states: list
List of spin_state values to which this rule applies. Note “*” matches any spin_state.
- allow_moves: dict
Dict mapping each axis to a bool that indicates whether moves in that axis are permitted.
- class socs.agents.acu.hwp_iface.HWPInterlocks(configured: bool = False, enabled: bool = False, instance_id: str = 'hwp-supervisor', tolerance: float = 0.1, limit_sun_avoidance: bool = True, rules: list = <factory>)[source]
- configured: bool = False
bool indicating whether the config is valid (and thus eligible to be enabled).
- enabled: bool = False
bool indicating whether to bother with HWP interlocks.
- instance_id: str = 'hwp-supervisor'
instance_id of HWPSupervisor agent to query.
- tolerance: float = 0.1
amount of leeway in el (deg) to grant when checking rules.
- limit_sun_avoidance: bool = True
whether HWP state-based elevation limits should be applied to Sun avoidance escape movements
- rules: list
list of MotionRule objects, each of which grants move privs on some axis based on elevation range and HWP state.
- Type:
rules
- test_range(el_range, grip_state, spin_state)[source]
Determine what motion types are permitted, given the current HWP state and the elevation (range) of the planned motion.
- Parameters:
el_range (tuple[float] or None) – The starting and ending elevations of the move.
grip_state (str) – The HWP gripper state.
spin_state (str) – The HWP spinning state.
The rules are evaluated in the context of grip_state and spin_state, to obtain the ranges of els over which moves in each axis is permitted. Then the el_range, if provided is tested against those ranges.
E.g for a call like:
test_range((40, 40), 'ungripped', 'not_spinning')
the returned structure looks like this:
{'el': (True, (30, 70), [(30, 70)]), 'az': (True, (30, 70), [(30, 70)]), 'third': (False, None, [(30, 50), (60, 70)]), }I.e. for each axis, a tuple is returned where the third value is the full list of permitted elevation ranges for this state; the first value is whether the requested el_range, specifically, overlaps with the elevation ranges, and the second value is the particular range that overlaps with requested el_range (or None if not the case).
- class socs.agents.acu.hwp_iface.HWPSupervisorClient(instance_id)[source]
OCSClient wrapper to query and interpret the state of the HWPSupervisor. Instantiate with the instance_id of the HWPSupervisor to be monitored.
- update()[source]
Analyze HWPSupervisor state info and make determinations about hwp state. Returns a dict with the results.
When everything works well, the useful results in the dict are:
timestamp: current timeok: True if state determination went reasonably wellerr_msg: populated if not ok.grip_state: one of “unknown”, “cold”, “warm”, “ungripped”spin_state: one of “unknown”, “spinning”, “not_spinning”
The following raw data from monitor process are copied into the output dict:
hwp_state: is_spinning -> _is_spinning pid_target_freq -> _target_freq gripper: grip_state -> _grip_state brake -> _grip_brakes acu: request_block_motion -> request_block_motion request_block_motion_timestamp -> request_block_motion_timestamp