chandra_aca.maude_decom

Classes and functions to help fetching ACA telemetry data using MAUDE.

Telemetry Specification

Aspect telemetry is described in chapter 5 of the User’s Manual. This module deals mostly with Aspect telemetry, specified in section 5.3 of the user’s manual and section 3.2.1.15.12 of the ACA specification document EQ7-278 F.

In general, the telemetry data is specified in the following documents (available on the Aspect Twiki page):

  • MSFC-STD-1274B. MSFC HOSC Telemetry Format Standard

  • MSFC-DOC-1949. MSFC HOSC Database Definitions

Timing

Timing in MAUDE Telemetry

What follows is a summary from the user’s guide section 6, and EQ7-278 F section 3.2.1.7. Check there for more details, especially Figures 6-1 to 6-4 in the user’s manual and Figure 7 in EQ7-278 F.

The ACA updates its output in regular 1.025 second periods that either begin or end at the time of an RCTU science header pulse (which occur every 2.05 seconds). These are called update periods, following the convention in the user manual section 6.1.

The ACA CCD operating cycle starts with a flush of charge from the CCD, followed by CCD integration, CCD readout, and ends with an idle period. The start/end of the update period does not coincide with the start/end of the CCD cycle. Instead, the end of the integration coincides with the start/end of the update period. This is accomplished by adjusting the idle period.

The data from and integration period is available to the OBC at the end of the following update period (EQ7-278 F Figure 7). That is 1.025 sec after the end of integration. This is the VCDU time seen in MAUDE telemetry:

TIME = END_INTEG_TIME + 1.025
_images/aca_timing_manual.png

In the case of 6x6 and 8x8 images, the entire image cannot be updated in a single update period, because Aspect pixel telemetry contains only eight pixels per update. 6x6 images take two update periods, and 8x8 images take four. The end of the integration interval is the same for all the sub-images, and corresponds to:

END_INTEG_TIME = TIME - 1.025

When the choice of integration time causes the CCD cycle to last longer than the time it takes to update a full image (1.025 seconds for a 4x4 image, 2.05 seconds for a 6x6 image or 4.1 seconds for an 8x8 image) the most recent image is repeated until new pixel data is available.

Timing in level0 Data Products

The times for pixel telemetry in level0 data products is adjusted to coincide with the middle of the integration interval:

TIME = END_INTEG_TIME - INTEG / 2

This means that the difference between the time in telemetry and the time in the level0 data products is:

TIME<telem> - TIME<l0> = 1.025 + INTEG / 2

Global variables in this module

These include the following global variables

  • MAX_VCDU: the maximum possible VCDU frame counter value

  • MAX_MJF: the maximum possible major frame counter value

  • MAX_MNF: the maximum possible minor frame counter value

  • PIXEL_MAP: dict of np.array, with values mapping integer pixel indices to pixel string ID

  • PIXEL_MAP_INV: dict of dict, with values mapping pixel string ID to integer pixel indices.

  • PIXEL_MASK: dict of np.array. Values are boolean masks that apply to images of different sizes

  • ACA_MSID_LIST: dictionary of commonly-used ACA telemetry MSIDs.

  • ACA_SLOT_MSID_LIST: dictionary of ACA image telemetry MSIDs.

PIXEL_MAP contains maps between pixel indices and pixel IDm depending on the image size. In the following tables, column index increases to the right and row index increases to the top (c.f. ACA User Manual Figs 1.8 and 1.9 ):

- Size 4X41:

  -----------------------------------------
  | -- | -- | -- | -- | -- | -- | -- | -- |
  -----------------------------------------
  | -- | -- | -- | -- | -- | -- | -- | -- |
  -----------------------------------------
  | -- | -- | D1 | H1 | L1 | P1 | -- | -- |
  -----------------------------------------
  | -- | -- | C1 | G1 | K1 | O1 | -- | -- |
  -----------------------------------------
  | -- | -- | B1 | F1 | J1 | N1 | -- | -- |
  -----------------------------------------
  | -- | -- | A1 | E1 | I1 | M1 | -- | -- |
  -----------------------------------------
  | -- | -- | -- | -- | -- | -- | -- | -- |
  -----------------------------------------
  | -- | -- | -- | -- | -- | -- | -- | -- |
  -----------------------------------------

- Size 6X61 or 6X62:

  -----------------------------------------
  | -- | -- | -- | -- | -- | -- | -- | -- |
  -----------------------------------------
  | -- | -- | E2 | F2 | G2 | H2 | -- | -- |
  -----------------------------------------
  | -- | D2 | D1 | H1 | L1 | P1 | I2 | -- |
  -----------------------------------------
  | -- | C2 | C1 | G1 | K1 | O1 | J2 | -- |
  -----------------------------------------
  | -- | B2 | B1 | F1 | J1 | N1 | K2 | -- |
  -----------------------------------------
  | -- | A2 | A1 | E1 | I1 | M1 | L2 | -- |
  -----------------------------------------
  | -- | -- | P2 | O2 | N2 | M2 | -- | -- |
  -----------------------------------------
  | -- | -- | -- | -- | -- | -- | -- | -- |
  -----------------------------------------


- Size 8X81, 8X82, 8X83 or 8X84:

  -----------------------------------------
  | H1 | P1 | H2 | P2 | H3 | P3 | H4 | P4 |
  -----------------------------------------
  | G1 | O1 | G2 | O2 | G3 | O3 | G4 | O4 |
  -----------------------------------------
  | F1 | N1 | F2 | N2 | F3 | N3 | F4 | N4 |
  -----------------------------------------
  | E1 | M1 | E2 | M2 | E3 | M3 | E4 | M4 |
  -----------------------------------------
  | D1 | L1 | D2 | L2 | D3 | L3 | D4 | L4 |
  -----------------------------------------
  | C1 | K1 | C2 | K2 | C3 | K3 | C4 | K4 |
  -----------------------------------------
  | B1 | J1 | B2 | J2 | B3 | J3 | B4 | J4 |
  -----------------------------------------
  | A1 | I1 | A2 | I2 | A3 | I3 | A4 | I4 |
  -----------------------------------------
chandra_aca.maude_decom.blob_to_aca_image_dict(blob, imgnum, pea=1)[source]

Assemble ACA image MSIDs from a blob into a dictionary.

This does to blobs what unpack_aca_telemetry does to frames, but for a single image.

Parameters:
blob
imgnum
pea
Returns:
chandra_aca.maude_decom.filter_vcdu_jumps(vcdu_counters)[source]

Return a boolean mask to filter VCDU counters that are not continuous.

The returned mask ensures that:

- VCDU counters are a strictly monotonic sequence.
- VCDU counters come in packets of four, with each group starting with a multiple of 4.
chandra_aca.maude_decom.get_aca_images(start, stop, **maude_kwargs)[source]

Fetch ACA image telemetry

Parameters:
start

timestamp interpreted as a Chandra.Time.DateTime

stop

timestamp interpreted as a Chandra.Time.DateTime

maude_kwargs

keyword args passed to maude

Returns:
astropy.table.Table
chandra_aca.maude_decom.get_aca_packets(start, stop, level0=False, combine=False, adjust_time=False, calibrate=False, blobs=None, frames=None, dtype=None, **maude_kwargs)[source]

Fetch VCDU 1025-byte frames, extract ACA packets, unpack them and store them in a table.

Incomplete ACA packets (if there is a minor frame missing) can be combined or not into records with complete ACA telemetry. Compare these to calls to the function:

>>> from chandra_aca import maude_decom
>>> img = maude_decom.get_aca_packets(684089000, 684089016, combine=True)
>>> img = img[img['IMGNUM'] == 0]
>>> img['TIME', 'MJF', 'MNF', 'COMMCNT', 'GLBSTAT', 'IMGTYPE', 'IMGROW0', 'IMGCOL0',
>>>     'TEMPCCD', 'TEMPHOUS']
<Table masked=True length=4>
     TIME      MJF    MNF   COMMCNT GLBSTAT IMGTYPE IMGROW0 IMGCOL0 TEMPCCD TEMPHOUS
   float64    uint32 uint32  uint8   uint8   uint8   int16   int16   int16   int16
------------- ------ ------ ------- ------- ------- ------- ------- ------- --------
684089001.869  78006     32       0       0       4     469    -332     -20       83
684089005.969  78006     48       0       0       4     469    -332     -20       83
684089010.069  78006     64       0       0       4     469    -332     -20       83
684089014.169  78006     80       0       0       4     469    -332     -20       83

Using combined=False, results in records with incomplete images. In this case, data can be missing from some records. For example, with 8X8 images, IMGROW0 and IMGCOL0 are present in the first ACA packet (image type 4) while the temperature is present in the second (image type 5):

>>> from chandra_aca import maude_decom
>>> img = maude_decom.get_aca_packets(684089000, 684089016, combine=False)
>>> img = img[img['IMGNUM'] == 0]
>>> img['TIME', 'MJF', 'MNF', 'COMMCNT', 'GLBSTAT', 'IMGTYPE', 'IMGROW0', 'IMGCOL0',
>>>     'TEMPCCD', 'TEMPHOUS']
    <Table masked=True length=15>
         TIME      MJF    MNF   COMMCNT GLBSTAT IMGTYPE IMGROW0 IMGCOL0 TEMPCCD TEMPHOUS
       float64    uint32 uint32  uint8   uint8   uint8   int16   int16   int16   int16
    ------------- ------ ------ ------- ------- ------- ------- ------- ------- --------
    684089000.844  78006     28       0       0       7      --      --      --       --
    684089001.869  78006     32       0       0       4     469    -332      --       --
    684089002.894  78006     36       0       0       5      --      --     -20       83
    684089003.919  78006     40       0       0       6      --      --      --       --
    684089004.944  78006     44       0       0       7      --      --      --       --
    684089005.969  78006     48       0       0       4     469    -332      --       --
    684089006.994  78006     52       0       0       5      --      --     -20       83
    684089008.019  78006     56       0       0       6      --      --      --       --
    684089009.044  78006     60       0       0       7      --      --      --       --
    684089010.069  78006     64       0       0       4     469    -332      --       --
    684089011.094  78006     68       0       0       5      --      --     -20       83
    684089012.119  78006     72       0       0       6      --      --      --       --
    684089013.144  78006     76       0       0       7      --      --      --       --
    684089014.169  78006     80       0       0       4     469    -332      --       --
    684089015.194  78006     84       0       0       5      --      --     -20       83
>>> img['IMG'].data[1]
masked_BaseColumn(data =
 [[60.0 97.0 70.0 120.0 74.0 111.0 103.0 108.0]
 [67.0 90.0 144.0 96.0 88.0 306.0 82.0 67.0]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]],
                  mask =
 [[False False False False False False False False]
 [False False False False False False False False]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]],
            fill_value = 1e+20)
>>> img['IMG'].data[2]
masked_BaseColumn(data =
 [[-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [76.0 81.0 160.0 486.0 449.0 215.0 88.0 156.0]
 [68.0 91.0 539.0 483.0 619.0 412.0 105.0 77.0]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]],
                  mask =
 [[ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [False False False False False False False False]
 [False False False False False False False False]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]],
            fill_value = 1e+20)
>>> img['IMG'].data[3]
masked_BaseColumn(data =
 [[-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [86.0 101.0 408.0 344.0 556.0 343.0 122.0 67.0]
 [196.0 195.0 114.0 321.0 386.0 115.0 69.0 189.0]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]],
                  mask =
 [[ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [False False False False False False False False]
 [False False False False False False False False]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]],
            fill_value = 1e+20)
>>> img['IMG'].data[4]
Out[10]:
masked_BaseColumn(data =
 [[-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [-- -- -- -- -- -- -- --]
 [67.0 61.0 67.0 176.0 99.0 72.0 79.0 88.0]
 [70.0 62.0 101.0 149.0 163.0 89.0 60.0 76.0]],
                  mask =
 [[ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [ True  True  True  True  True  True  True  True]
 [False False False False False False False False]
 [False False False False False False False False]],
            fill_value = 1e+20)
Parameters:
start

timestamp interpreted as a Chandra.Time.DateTime

stop

timestamp interpreted as a Chandra.Time.DateTime

level0bool.

Implies combine=True, adjust_time=True, calibrate=True

combinebool.

If True, ACA subimages are combined to form a full image (depending on size), If False, ACA subimages are not combined, resulting in multiple rows for 6x6 and 8x8 images.

adjust_timebool

If True, TIME is at the middle of the integration window. If False, TIME is the VCDU time in telemetry of the packet frame (combine=False) or the VCDU time of the first sub-image of the combined image (combine=True).

calibratebool

If True, pixel values will be ‘value * imgscale / 32 - 50’ and temperature values will be: 0.4 * value + 273.15

blobsbool or dict

If set, data is assembled from MAUDE blobs. If it is a dictionary, it must be the output of maude.get_blobs ({‘blobs’: … }).

framesbool or dict

If set, data is assembled from MAUDE frames. If it is a dictionary, it must be the output of maude.get_frames ({‘data’: … }).

dtypenp.dtype. Optional.

the dtype to use when creating the resulting table. This is useful to add columns including MSIDs that are present in blobs. If used with frames, most probably you will get and empty column. This option is intended to augment the default dtype. If a more restrictive dtype is used, a KeyError can be raised.

maude_kwargs

keyword args passed to maude

Returns:
astropy.table.Table
chandra_aca.maude_decom.get_raw_aca_blobs(start, stop, maude_result=None, **maude_kwargs)[source]

Fetch MAUDE blobs and group them according to the underlying 225-byte ACA packets.

If the first minor frame in a group of four ACA packets is within (start, stop), the three following minor frames are included if present.

returns a dictionary with keys [‘TIME’, ‘MNF’, ‘MJF’, ‘packets’, ‘flags’]. These correspond to the minor frame time, minor frame count, major frame count, the list of packets, and flags returned by MAUDE respectively.

This is to blobs what get_raw_aca_packets is to frames.

Parameters:
start

timestamp interpreted as a Chandra.Time.DateTime

stop

timestamp interpreted as a Chandra.Time.DateTime

maude_result

the result of calling maude.get_blobs. Optional.

maude_kwargs

keyword args passed to maude.get_frames()

Returns:
dict
{‘blobs’: [], ‘names’: np.array([]), ‘types’: np.array([])}
chandra_aca.maude_decom.get_raw_aca_packets(start, stop, maude_result=None, **maude_kwargs)[source]

Fetch 1025-byte VCDU frames using MAUDE and extract a list of 225-byte ACA packets.

If the first minor frame in a group of four ACA packets is within (start, stop), the three following minor frames are included if present.

returns a dictionary with keys [‘TIME’, ‘MNF’, ‘MJF’, ‘packets’, ‘flags’]. These correspond to the minor frame time, minor frame count, major frame count, the list of packets, and flags returned by MAUDE respectively.

This function raises an exception if the VCDU frames in maude_result are not contiguous, which can also happen if the frames are corrupted in some way.

Parameters:
start

timestamp interpreted as a Chandra.Time.DateTime

stop

timestamp interpreted as a Chandra.Time.DateTime

maude_result

the result of calling maude.get_frames. Optional.

maude_kwargs

keyword args passed to maude.get_frames()

Returns:
dict
{‘flags’: int, ‘packets’: [],
‘TIME’: np.array([]), ‘MNF’: np.array([]), ‘MJF’: np.array([])}
chandra_aca.maude_decom.unpack_aca_telemetry(packet)[source]

Unpack ACA telemetry encoded in 225-byte packets.

Parameters:
packet

bytes

Returns:
list of dict
A list of length 8, one entry per slot, where each entry is a dictionary.