Measurement Set Reading

Everything that knows the Measurement Set format. Named for the format rather than generically, so that a second input format becomes a sibling module with its own name.

Reading declarations rather than assuming them

An MS records several properties that are easy to assume and expensive to get wrong. TABASCAL reads them from the file:

Correlations. The POLARIZATION subtable’s CORR_TYPE lists which correlations the MS actually holds, as CASA Stokes codes. resolve_correlation() matches the configured data.corr against it by identity, not by position, so yy selects YY whether the MS holds all four correlations or only that one. A fixed {xx: 0, xy: 1, yx: 2, yy: 3} table only works for a full four-correlation MS: a single-correlation MS has a length-1 correlation axis whatever polarisation it holds, and a two-correlation (XX, YY) MS puts YY at index 1. Requesting a correlation the MS does not hold is an error naming what it does hold, rather than an index error or a silent read of the wrong polarisation.

Time scale. The TIME column’s MEASINFO record declares the scale its values are on, almost always UTC. read_time_scale() returns it, and it is carried in the read_ms result as time_scale. The scales differ by enough to matter — reading a UTC epoch as TAI shifts it by 32 leap seconds, roughly 240 km along a LEO satellite’s ground track — and none of these mismatches raise.

Note

The declared scale is currently reported, not yet honoured: satellite trajectories are computed as if every observation were UTC, and a non-UTC MS produces a warning. Wiring it through is tracked by issue #133.

Reading Measurement Sets.

Everything that knows the MS format lives here. Named for the format rather than generically (io.py) so that a second input format becomes a sibling module with its own name, instead of accreting into one file the way MS reading accreted into tab_tools.py.

tabascal.ms.CORR_TYPES = {'i': 1, 'll': 8, 'lr': 7, 'q': 2, 'rl': 6, 'rr': 5, 'u': 3, 'v': 4, 'xx': 9, 'xy': 10, 'yx': 11, 'yy': 12}

CASA Stokes enumeration (casacore Stokes.h), for the correlations that can be selected by name. The MS records which of these it holds in POLARIZATION::CORR_TYPE, so a correlation is identified by its code rather than by where it sits on the data axis.

tabascal.ms.DEFAULT_TIME_SCALE = 'utc'

Time scale assumed when an MS does not say which one its TIME column uses.

tabascal.ms.read_time_scale(column_keywords: dict, column: str = 'TIME') str[source]

Time scale declared by an MS column, from its MEASINFO record.

A Measurement Set records the scale its times are on rather than leaving it to convention: the TIME column carries MEASINFO {'type': 'epoch', 'Ref': 'UTC'}. UTC is overwhelmingly the common case, but it is a declaration to be read, not a property to be assumed – an MS may legitimately declare TAI or another scale, and the difference is 32 s of leap seconds, which is ~240 km along a LEO satellite’s ground track.

Parameters:
  • column_keywords (dict) – Per-column keyword mapping, as returned by xds_from_ms(path, column_keywords=True)[1].

  • column (str, optional) – Column to read the scale from. Defaults to "TIME".

Returns:

The declared scale, lower-cased, or DEFAULT_TIME_SCALE when the MS does not declare one.

Return type:

str

tabascal.ms.resolve_correlation(ms_path: str, corr: str, pol_id: int = 0) int[source]

Index of corr on the MS’s correlation axis.

Resolved by identity, not by position: the requested correlation is mapped to its CASA Stokes code and located in POLARIZATION::CORR_TYPE.

A full 4-correlation MS lays its correlations out in the conventional order, so a fixed {xx: 0, xy: 1, yx: 2, yy: 3} table happens to work there. It does not generalise: an MS written with a single polarisation holds only that one, so its correlation axis has length 1 whatever the polarisation is, and yy means index 0 rather than 3. A 2-correlation (XX, YY) MS breaks the same table in a different way. Reading CORR_TYPE covers all three, and turns a request for an absent correlation into an error rather than either an index error or a silent read of the wrong polarisation.

Parameters:
  • ms_path (str) – Path to the Measurement Set.

  • corr (str) – Correlation name, e.g. "xx". Case-insensitive.

  • pol_id (int, optional) – Row of POLARIZATION describing the data being read, from resolve_data_description(). Defaults to 0.

Returns:

Position of corr on the data’s correlation axis.

Return type:

int

Raises:

ValueError – If corr is not a recognised name, or the MS does not contain it.

tabascal.ms.resolve_data_description(ms_path: str, data_desc_id: int = 0)[source]

(spectral_window_id, polarization_id) for a DATA_DESC_ID.

An MS does not tie its data to row 0 of SPECTRAL_WINDOW and POLARIZATION. It carries a DATA_DESC_ID per row, and the DATA_DESCRIPTION subtable maps that to the spectral window and polarization setups the data actually uses. Those ids are 0 in the common single-setup case, which is why assuming 0 usually works – and why an MS with several setups would silently read another one’s channel frequencies or correlation layout.

xds_from_ms partitions by (FIELD_ID, DATA_DESC_ID) and records the id in each partition’s attrs, so the caller can say which partition it is reading.

Falls back to (0, 0) with a warning if DATA_DESCRIPTION cannot be read, which keeps a malformed store loadable.