Design overview¶
Motivation¶
The annie package is focused on modeling the ACA behavior while guiding on stars. The key feature of the design is that it is modular and extensible, allowing flexibility in supporting variations of simulation parameters or algorithms such as:
- Algorithm to simulate star image data:
Gaussian PSF
ACA PSF image data
- Dark CCD background
Background from flight ACA dark current calibration
Time-history of arbitrarily defined background
Constant background
- Background subtraction
Flight algorithm based on the eight corner pixels
Dynamic background using median-filterd edge pixels
Warm pixel detection within each image
- Simulation strategy
Pure simulation
Plug in flight data for select components such as ACA images, background, or attitude
Centroiding algorithms
Code overview¶
Spacecraft¶
At the top level of the simulator there is a single Spacecraft
class
which serves as master controller for an annie simulation. It performs the following key functions:
Serves as a container for each of the Subsystems classes
Provides the
track_stars_setup()
convenience method to set up most of the supported configuration options for typical simulations of tracking guide stars.Provides the
run()
method to actually run a simulation once everything is set up.
Running the simulation consists of a loop in which the spacecraft clock is
incremented by one “tick”, and then the main process()
method of each subsystem is
called. One tick is a minor cycle (1.025 sec / 64 = 16.015625 msec). The subsystem is
responsible for deciding if any action is needed on that tick. The order of subsystem
processing does matter and thus the details of subsystems are somewhat coupled. For example it
is assumed that PCAD runs first in order that the attitude is correct for other
subsystems.
Subsystems¶
The core of annie
is the concept of a subsystem. This is the object which does the
actual work of simulating a subsytem such as the ACA or PCAD in a modular fashion. A
subsystem can handle the scheduling and processing of regularly recurring tasks, external
commands, and internally generated events. For details see the Subsystem details
section.
Subsystems contain attributes which may be constants required for runtime processing or dynamically updated values.
All subsystems are based on the SubSystem
: base class. One
important feature of this class is that it provides access to all of the other subsystems
(and corresponding attributes) via a dynamically generated attribute with the name of the
other subsystem. For instance within the ACA
subsystem class one could access the
current true yaw using self.pcad.att_record.yaw_true
. This cross-communication
facilitates writing simulation code while still maintaining modularity. Note that there
is a “trust” model in place and it is assumed each subsystem treats other subsystem
attributes as read-only.
The implemented subsystems are:
Data storage¶
In order to analyze simulation data, the data need to be collected and made available for
use post-simulation. This is done via the Telem
subsystem, which
puts data into lists of Python records (class objects). This is not a direct simulation
of actual Chandra telemetry, but instead “pseudo-telemetry” that is convenient for
analysis.
StarDataRecord
: ACA telemetry for each slot, collected each frameSlotRecord
: PCAD telemetry for each slot, collected each frameAttitudeRecord
: attitude telemetry, collected each minor frame
Subsystem details¶
The SubSystem
base class is used by each of the subsystem
classes and provides methods that are responsible for scheduling and processing the tasks,
commands and events that are executed by each subsystem. The key method of this
class that deals with generic processing is called
process()
and it is executed at each clock tick for each
subsystem. This is done via the following code in the run()
method:
while self.clock.tick():
self.pcad.process()
self.sky.process()
self.aca.process()
self.ccd.process()
self.telem.process()
Tasks¶
Actions that are executed regularly, usually at the begining
of each frame, or each minor frame. They are defined in the subsystem’s
task
attribute which is a list of tuples containing a method, time unit,
and number of clock ticks within this unit at which the task is to be executed. For example,
there are two tasks defined for the telemetry subsystem:
main_processing()
called at the begining of each
frame with the goal of collecting the ACA star data record telemetry,
and attitude_processing()
called at the begining
of each minor frame with the goal of updating the spacecraft attitude
Telemetry:
>>> from annie.annie import Spacecraft
>>> sc = Spacecraft()
>>> sc.telem.tasks
[('main_processing', 'frame', 0), ('attitude_processing', 'mnf', 0)]
Execution of tasks happens at their scheduled time as part of
generic subsystem processing that is called every clock tick
(process()
). The workflow of
regularly executed tasks is illustrated below: PCAD,
Sky, ACA, CCD
,
Telemetry.
Commands¶
Commands are actions that are scheduled to be executed one time
as part of the generic subsystem processing. Commands are
scheduled using the subsystem’s command()
method. The parameters of the command()
method include method to be called, time, and method’s
**kwargs. The time can be given in units that
are absolute (parameter absolute = True) or relative (time
unit within the current frame; parameter absolute=False,
default).
When the internal clock reaches the specified time, the subsystem’s
processing transfers the command from the
commands
queue to the
pending_commands
list, where
it awaits its execution.
Execution of the pending commands happens as part of the subsystem’s main processing,
which is usually called at the beginning of each frame. For a simple example see the PCAD
annie.pcad.PCAD.main_processing()
which first calls
annie.subsystem.SubSystem.execute_pending_commands()
and then does ACA processing.
A more complicated example is annie.aca.ACA.main_processing()
, which sets
up the full CCD processing cycle along with executing pending commands.
Because commands are queued in the pending_commands
buffer, the time specified when
using the command()
method does not have to be fine tuned
and equal to the time at which the command needs to be executed. The command will be
executed at the first appropriate opportunity (as determined by the subsystem) after the
specified command time. For example:
>>> from annie.annie import Spacecraft
>>> import astropy.units as u
>>> # Define the Spacecraft and set the clock at tick 61
>>> # (near the end if the 1st frame). Set the simulation
>>> # to end at 1.05 seconds (just after 1st frame).
>>> sc = Spacecraft()
>>> c = sc.clock
>>> c.start = 61 # ticks
>>> c.stop = 1.05 * u.s
>>> # Command setting the commanded attitude at absolute clock tick = 62
>>> sc.pcad.command('set_att_cmd', 62, absolute=True, att=[0., 0., 0.])
>>> print(sc.pcad.commands)
defaultdict(list,
{<ClockTime secs=0.993 ticks=62>: [('set_att_cmd',
{'att': [0.0, 0.0, 0.0]})]})
>>> while c.tick():
>>> sc.pcad.process()
>>> print('Clock ticks: ', c.ticks)
>>> print('Pending commands: ', sc.pcad.pending_commands)
>>> print('Cmd attitude: ', sc.pcad.att_record.att_cmd)
>>> print()
Clock ticks: 61
Pending commands: []
Cmd attitude: None
Clock ticks: 62
Pending commands: [('set_att_cmd', {'att': [0.0, 0.0, 0.0]})]
Cmd attitude: None
Clock ticks: 63
Pending commands: [('set_att_cmd', {'att': [0.0, 0.0, 0.0]})]
Cmd attitude: None
Clock ticks: 64
Pending commands: []
Cmd attitude: <Quat q1=0.00000000 q2=-0.00000000 q3=0.00000000 q4=1.00000000>
Clock ticks: 65
Pending commands: []
Cmd attitude: <Quat q1=0.00000000 q2=-0.00000000 q3=0.00000000 q4=1.00000000>
In the current implementation commanding is used to set up the commanded,
estimated and true attitudes at the start of a simulation, and command
an ACA search()
from within the PCAD
class either at the start of the simulation or when a star is lost in the
course of a simulation.
Events¶
Events are actions that are scheduled to be executed one time, at a strictly
specified time. They are added to the subsystem’s
events queue (events
) using the
add_event()
method with parameters:
method to be executed, time of execution (absolute ClockTime
),
method’s **kwargs.
Events in the events queue are executed as part of generic subsystem
processing called every clock tick (process()
)
as soon as the clock reaches the specified time. For example:
>>> from annie.annie import Spacecraft
>>> import astropy.units as u
>>> # Define the Spacecraft and set the clock at tick 61
>>> # (near the end if the 1st frame)
>>> sc = Spacecraft()
>>> c = sc.clock
>>> c.start = 61
>>> c.stop = 1.05 * u.s
>>> # Add flush event to be executed in 2 minor cycles (= 2 ticks)
>>> t_flush = c + 2 * u.mnc
>>> sc.ccd.add_event('flush', t_flush)
>>> print(sc.ccd.events)
defaultdict(<class 'list'>, {<ClockTime secs=1.009 ticks=63>: [('flush', {})]})
>>> while c.tick():
>>> sc.ccd.process()
>>> print('Clock ticks: ', c.ticks)
>>> print('CCD status: ', sc.ccd.status)
>>> print()
Clock ticks: 61
CCD status: idle
Clock ticks: 62
CCD status: idle
Clock ticks: 63
CCD status: flush
Clock ticks: 64
CCD status: flush
Clock ticks: 65
CCD status: flush
In the current implementation, events functionality is used within the
ACA
class to schedule ACA and CCD events related
to image processing that are to be executed in the next two frames
providing that the CCD status is idle (see ACA).
Summary¶
Characteristic |
Tasks |
Commands |
Events |
Executed regularly |
Yes |
No |
No |
Get buffered and await execution |
No |
Yes |
No |
Requested time matches the execution time |
Yes |
No |
Yes |
Workflow of regularly executed tasks¶
PCAD¶
The workflow of tasks executed regularly by the PCAD subsystem is as follows:
at the start of each minor frame the PCAD subsystem performs a task to
update the attitude; see In addition, the PCAD system performs main processing at the start of every frame. This includes execution of pending commands (e.g. setting the initial commanded attitude or issuing the ACA search command for lost stars), and ACA processing. There are two actions performed as part of ACA processing. The first one is
to keep track of the The second action performed by the PCAD subsystem as part of the regular ACA
processing is to identify the guide stars, i.e. to assign a value (either
|
Sky¶
The workflow of tasks executed regularly by the Sky subsystem is as follows:
at the start of each frame the Sky subsystem checks if the current S/C attitude
( |
ACA¶
The workflow of tasks executed regularly by the ACA subsystem is as follows:
every frame the ACA subsystem waits 1 minor cycle (1 clock tick) and then starts its
The subsequent workflow depends on the CCD status. If CCD status is ‘idle’ then
the |
Telemetry¶
The workflow of tasks executed regularly by the telemetry subsystem is as follows:
at the start of each minor frame, following the attitude updates performed by the
PCAD subsystem (see the PCAD workflow) the telemetry subsystem
executes In addition, the telemetry subsystem executes |
Attitude control law¶
The subsections below summarize the concepts of the commanded, true, sky and estimated attitudes, as well as the attitude control law adopted by the simulator. Attitude control law is a pdf document that presents these concepts in more detail. An illustration of the adopted control law can be found in this Jupyter notebook.
Commanded attitude¶
The commanded attitude is either fetched from the archive if
the user provides ‘obsid’ while setting up an annie
simulation, or it is passed directly by the user
during the setup together with an astropy Table containing
a custom star catalog (see Setting up an annie simulation).
It is representative of the center of the dither pattern.
It is stored in att_record
as
att_cmd
.
True attitude¶
The true attitude is the actual simulated spacecraft attitude
and is used (for example) to compute star positions in the ACA field of view and for
post-facto analysis of attitude errors. The true attitude is
found by integrating the true rates.
The true rates are computed as a sum of the commanded
dither rates and control rates that are proportional to the
attitude errors, R_control = att_error * RATE_SCALE
.
The adopted control law assumes that a 2 arcsec error
results in a 0.1 arcsec/sec adjustment to the rate.
The control rate is allowed to take values in the range
-RATE_MAX
< R_control < RATE_MAX
.
The true attitude is stored in
att_record
as
att_true
.
Sky attitude¶
The sky attitude is set at the start of a simulation to match
the current commanded attitude (see update_stars()
).
It is stored as att_sky
.
The sky attitude is used to get stars available in ACA field of view
(get_stars()
, stars
)
if stars_source
equals 'agasc'
(Setting up an annie simulation).
If the true attitude is found to differ from the commanded attitude by more
than 0.1 deg in any of the yaw, pitch, roll axes (typically after a new
commanded attitude has been provided, e.g. in the case of simulating
a set of observations), the sky attitude is overwritten with
the commanded attitude and the ACA FOV stars are fetched again.
Estimated attitude¶
In the absence of new star data, the estimated attitude is computed
in the same way as the true attitute, by integrating the estimated
rates assumed to be equal to the true rates. However, every frame
with new star data (i.e. frames that start with
annie.aca.CCD.status
equal ‘idle’ and have
annie.aca.ACA.repeat_count
equal 0), the estimated attitude
is found from the guide stars using the fast attitude solution
algorithm. The estimated attitude is stored in
att_record
as
att_est
. It is used to derive
attitude errors as the deviations between the estimated and commanded
attitudes, given the dither pattern.