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, **kwargs)[source]

Fetch ACA image telemetry

Fetch ACA image telemetry from MAUDE and return it as an astropy Table. With the default settings and no additional kwargs, this calls get_aca_packets() in a configuration that uses MAUDE frames, combines image data, and sets the TIME associated with each image to the midpoint of the integration time during which that pixel data was collected (matches CXC L0 times). See get_aca_packets().

The ‘IMG’ column is always Nx8x8 and masked, where the mask is a per-pixel mask that indicates missing data for 4x4 or 6x6 images. The units of ‘IMG’ are DN.

For queries including 4x4 data, the ‘BGDRMS’, ‘TEMPCCD’, ‘TEMPHOUS’, ‘TEMPPRIM’, ‘TEMPSEC’, and ‘BGDSTAT’ columns will be masked since they are not present in the 4x4 image data.

There are three different specifiers of the image row/col location: - IMGROW0_8x8/IMGCOL0_8x8: the row/col of the lower-left pixel of the 8x8 masked

image. This is generally the most useful.

  • IMGROW0/IMGCOL0: the row/col of the lower-left pixel of the actual 4x4, 6x6, or

    8x8 image data. For 6x6 this corresponds to the mouse-bitten corner pixel.

  • IMGROW_A1/IMGCOL_A1: the row/col of the A1 pixel in telemetry (see ACA EQ-spec).

The full list of columns is:

         name          dtype  unit
--------------------- ------- -----------
                 TIME float64 CXC seconds
              VCDUCTR  uint32
                  MJF  uint32
                  MNF  uint32
               IMGNUM  uint32
              COMMCNT   uint8
             COMMPROG   uint8
              GLBSTAT   uint8
              IMGFUNC  uint32
              IMGTYPE   uint8
             IMGSCALE  uint16
              IMGROW0   int16
              IMGCOL0   int16
                INTEG float64 s
               BGDAVG  uint16 DN
               BGDRMS  uint16 DN
              TEMPCCD float32 degC
             TEMPHOUS float32 degC
             TEMPPRIM float32 degC
              TEMPSEC float32 degC
              BGDSTAT   uint8
             HIGH_BGD    bool
             RAM_FAIL    bool
             ROM_FAIL    bool
           POWER_FAIL    bool
             CAL_FAIL    bool
   COMM_CHECKSUM_FAIL    bool
                RESET    bool
         SYNTAX_ERROR    bool
 COMMCNT_SYNTAX_ERROR    bool
COMMCNT_CHECKSUM_FAIL    bool
      COMMPROG_REPEAT   uint8
               IMGFID    bool
              IMGSTAT   uint8
            SAT_PIXEL    bool
            DEF_PIXEL    bool
           QUAD_BOUND    bool
           COMMON_COL    bool
           MULTI_STAR    bool
              ION_RAD    bool
            IMGROW_A1   int16
            IMGCOL_A1   int16
          IMGROW0_8X8   int16
          IMGCOL0_8X8   int16
       END_INTEG_TIME float64
             AAPIXTLM    str4
             AABGDTYP    str4
                  IMG float64 DN
          IMG_VCDUCTR   int64

This function can be used to fetch up to 5 days of ACA image telemetry at a time. If more is needed, you can set the module variable MAUDE_FETCH_LIMIT to a larger value. Internally, this function will fetch the data in intervals of MAUDE_SINGLE_FETCH_LIMIT.

Parameters:
start

timestamp, CxoTimeLike

stop

timestamp, CxoTimeLike. stop - start cannot be greater than MAUDE_FETCH_LIMIT

kwargs

keyword args passed to get_aca_packets

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:
startCxoTimeLike

Start time for the ACA packets

stopCxoTimeLike

Stop time for the ACA packets

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:
startCxoTimeLike

Start time for the ACA blobs

stopCxoTimeLike

Stop time for the ACA blobs

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:
startCxoTimeLike

Start time for packets

stopCxoTimeLike

Stop time for packets

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.