mth5.io.phoenix.readers.mtu
MTU_utils - Utility to read the Canadian Phoenix MTU-5A instrument time series binary files in Matlab (and Python)
A bunch of simple scripts to read the legacy Phoenix MTU-5A binary format files … including the time series (.TSN) and table (.TBL) formats.
Original files by:
DONG Hao donghao@cugb.edu.cn China University of Geosciences, Beijing
Updated and adapted to Python by: Peacock, J.R. (2025-12-31)
Submodules
Classes
Reader for legacy Phoenix MTU-5A instrument time series binary files. |
Package Contents
- class mth5.io.phoenix.readers.mtu.MTUTable(file_path: str | pathlib.Path | None = None, **kwargs)[source]
The Phoenix TBL file is a series of 25-byte blocks containing key-value pairs: - Bytes 0-11: Tag name (4-character string, null-padded) - Bytes 12-24: Value (13 bytes, mixed data types)
Values can be decoded as follows: 1. INT (4 bytes): struct.unpack(‘<i’, bytes[0:4]) - Little-endian signed int 2. DOUBLE (8 bytes): struct.unpack(‘<d’, bytes[0:8]) - Little-endian double 3. CHAR (variable): bytes.decode(‘latin-1’).strip() - Null-terminated string 4. BYTE (1 byte): struct.unpack(‘<B’, bytes[0:1]) - Unsigned byte 5. TIME (6 bytes): [sec, min, hour, day, month, year-2000] format
The TBL_TAG_TYPES dictionary maps each known tag to its data type, enabling automatic decoding via decode_tbl_value() function. Unknown tags return raw bytes.
- Example usage:
# Automatic decoding: tbl_dict = get_dictionary_from_tbl(‘file.TBL’, decode_values=True)
# Manual decoding with read_tbl (legacy): info = read_tbl(‘/path’, ‘file.TBL’)
read_tbl - reads a (binary) TBL table file of the legacy Phoenix format (MTU-5A) and output the “info” metadata dictionary.
- Parameters:
fpath – path to the tbl
fname – name of the tbl file (including extensions)
- Returns:
output dict of the TBL metadata
- Return type:
info
SITE: site name SNUM: serial number (of the box) FILE: file name recorded CMPY: company/institute of the survey SRVY: survey project name EXLN: Ex channel dipole length EYLN: Ey channel dipole length NREF: North reference (true, or magnetic north) LNGG: longitude in degree-minute format (DDD MM.MM) LATG: latitude in degree-minute format (DD MM.MM) ELEV: elevation (in metres) HXSN: Hx channel coil serial number HYSN: Hy channel coil serial number HZSN: Hz channel coil serial number STIM: starting time (UTC) ETIM: ending time (UTC) LFRQ: powerline frequency for filtering (can only be 50 or 60 Hz) HGN: final H-channel gain HGNC: H-channel gain control: HGN = PA * 2^HGNC (note: PA =
PreAmplifier gain)
EGN: final E-channel gain EGNC: E-channel gain control: HGN = PA * 2^HGNC (note: PA =
PreAmplifier gain)
- HSMP: L3 and L4 time slot in second (MTU-5A) or minute (MTU-5P),
this means the instrument will record L3NS seconds for L3 and L4NS seconds for L4, for every HSMP time slot.
L3NS: L3 sample time (in second) L4NS: L4 sample time (in second) SRL3: L3 sample rate SRL4: L4 sample rate SRL5: L5 sample rate HATT: H channel attenuation (1/4.3 for MTU-5A) HNOM: H channel normalization (mA/nT) TCMB: Type of comb filter (probably used to suppress the harmonics of the
powerline noise.
TALS: Type of anti-aliasing filter LPFR: Parameter of Low-pass/VLF filter. this is a quite complicated
part as the low-pass filter is simply an R-C circuit with a switch to connect to different capacitors. To ensure enough bandwidth (proportion to 1/RC), one should use smaller capacitors with larger ground resistance.
ACDC: AC/DC coupling (DC = 0, AC = 1; MT should always be DC) FSCV: full scaling A-D converter voltage (in unit of V) ======================================================================= note: Phoenix Legacy TBL is a straight-forward parameter-value metadata file, stored in a bizarre format. The parameter tag and value are stored in a series of 25-byte data blocks, in mixed data type: the first 12 bytes are reserved for the tag name (first 4 bytes as char). The values are stored in the 13 bytes afterwards, in various formats (char, int, float, etc.).
So a good practice is to read in those blocks one by one and extract all of them. However, not every thing is useful for the metadata, so I only extract a few of them, for now.
Original author: Hao 2012.07.04 Beijing
Translated to Python and enhanced by: J. Peacock (2025-12-31)
Main changes:
Encapsulated in MTUTable class
Automatic type detection and decoding based on TBL_TAG_TYPES
Added properties to extract metadata as mt_metadata objects
- file_path
- tbl_dict: dict[str, int | float | str | bytes]
- TBL_TAG_TYPES
- decode_tbl_value(value_bytes: bytes, data_type: str) int | float | str | bytes[source]
Decode TBL value bytes based on the specified data type.
- Parameters:
value_bytes (bytes) – 13 bytes from position 12-24 in the 25-byte block containing the value.
data_type (str) – Type of the data: ‘int’, ‘double’, ‘char’, ‘byte’, or ‘time’.
- Returns:
Decoded value in appropriate Python type. Returns raw bytes if decoding fails or data_type is unrecognized.
- Return type:
int or float or str or bytes
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> value = tbl.decode_tbl_value(b'...', 'int') >>> print(value) 1690
- read_tbl() None[source]
Read and decode the TBL file, populating the tbl_dict attribute.
This method reads the TBL file specified during initialization and decodes all tag-value pairs according to their known types. The results are stored in self.tbl_dict.
- Returns:
Results are stored in the tbl_dict attribute.
- Return type:
None
Examples
>>> tbl = MTUTable('/data/phoenix', '1690C16C.TBL') >>> tbl.read_tbl() >>> print(tbl.tbl_dict['SITE']) '10441W10' >>> print(tbl.tbl_dict['SNUM']) 1690
- property channel_keys: dict[str, int]
Get list of channel keys present in the TBL metadata.
- Returns:
Dictionary of channel keys and their corresponding values found in tbl_dict (e.g., ‘CHEX’, ‘CHEY’, ‘CHHX’, etc.).
- Return type:
dict[str, int]
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> keys = tbl.channel_keys >>> print(keys) {'ex': 1, 'ey': 2, 'hx': 3, 'hy': 4, 'hz': 5}
- property survey_metadata: mt_metadata.timeseries.Survey
Extract survey metadata from TBL file.
- Returns:
mt_metadata Survey object populated with survey-level information from the TBL file (survey ID, company/author).
- Return type:
Survey
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Survey object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> survey = tbl.survey_metadata >>> print(survey.id) 'MT_Survey_2024'
- property station_metadata: mt_metadata.timeseries.Station
Extract station metadata from TBL file.
- Returns:
mt_metadata Station object populated with station-level information including location (latitude, longitude, elevation, declination) and time period.
- Return type:
Station
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Station object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> station = tbl.station_metadata >>> print(station.id) '10441W10' >>> print(f"{station.location.latitude:.6f}") 41.006467
- property run_metadata: mt_metadata.timeseries.Run
Extract run metadata from TBL file.
- Returns:
mt_metadata Run object populated with data logger information and channel metadata.
- Return type:
Run
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Run object with a warning.
The run includes all channel metadata (ex, ey, hx, hy, hz) obtained from their respective property methods.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> run = tbl.run_metadata >>> print(run.id) 'run_1690' >>> print(run.data_logger.id) 'MTU_1690'
- property ex_metadata: mt_metadata.timeseries.Electric
Extract Ex electric channel metadata from TBL file.
- Returns:
mt_metadata Electric object for Ex component with dipole length, azimuth, AC/DC start values, and channel number.
- Return type:
Electric
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Electric object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> ex = tbl.ex_metadata >>> print(ex.dipole_length) 100.0
- property ey_metadata: mt_metadata.timeseries.Electric
Extract Ey electric channel metadata from TBL file.
- Returns:
mt_metadata Electric object for Ey component with dipole length, azimuth (Ex azimuth + 90°), AC/DC start values, and channel number.
- Return type:
Electric
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Electric object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> ey = tbl.ey_metadata >>> print(ey.dipole_length) 100.0
- property hx_metadata: mt_metadata.timeseries.Magnetic
Extract Hx magnetic channel metadata from TBL file.
- Returns:
mt_metadata Magnetic object for Hx component with maximum field, channel number, azimuth, and sensor serial number.
- Return type:
Magnetic
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Magnetic object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> hx = tbl.hx_metadata >>> print(hx.sensor.id) 'coil1693'
- property hy_metadata: mt_metadata.timeseries.Magnetic
Extract Hy magnetic channel metadata from TBL file.
- Returns:
mt_metadata Magnetic object for Hy component with maximum field, channel number, azimuth (Hx azimuth + 90°), and sensor serial number.
- Return type:
Magnetic
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Magnetic object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> hy = tbl.hy_metadata >>> print(hy.sensor.id) 'coil1694'
- property hz_metadata: mt_metadata.timeseries.Magnetic
Extract Hz magnetic channel metadata from TBL file.
- Returns:
mt_metadata Magnetic object for Hz component with maximum field, channel number, and sensor serial number.
- Return type:
Magnetic
Notes
If TBL metadata has not been loaded (via read_tbl()), returns an empty Magnetic object with a warning.
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> hz = tbl.hz_metadata >>> print(hz.sensor.id) 'coil1695'
- property ex_calibration: float | None
Calculate Ex channel calibration factor.
- Returns:
Calibration factor to convert raw ADC values to mV/km. Returns None if TBL metadata has not been loaded.
- Return type:
float or None
Notes
The calibration factor is calculated as:

where:
FSCV: Full-scale converter voltage
EGN: Electric channel gain
EXLN: Ex dipole length in meters
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> cal = tbl.ex_calibration >>> print(f"{cal:.6f}") 0.000762
- property ey_calibration: float | None
Calculate Ey channel calibration factor.
- Returns:
Calibration factor to convert raw ADC values to mV/km. Returns None if TBL metadata has not been loaded.
- Return type:
float or None
Notes
The calibration factor is calculated as:

where:
FSCV: Full-scale converter voltage
EGN: Electric channel gain
EYLN: Ey dipole length in meters
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> cal = tbl.ey_calibration >>> print(f"{cal:.6f}") 0.000762
- property magnetic_calibration: float | None
Calculate magnetic channel calibration factor.
- Returns:
Calibration factor to convert raw ADC values to nT. Returns None if TBL metadata has not been loaded.
- Return type:
float or None
Notes
The calibration factor is calculated as:

where:
FSCV: Full-scale converter voltage
HGN: Magnetic channel gain
HATT: Magnetic channel attenuation
HNOM: Magnetic channel normalization (mA/nT)
This calibration applies to all magnetic channels (Hx, Hy, Hz).
Examples
>>> tbl = MTUTable('/data', 'file.TBL') >>> tbl.read_tbl() >>> cal = tbl.magnetic_calibration >>> print(f"{cal:.9f}") 0.000000229
- class mth5.io.phoenix.readers.mtu.MTUTSN(file_path: str | pathlib.Path | None = None, **kwargs)[source]
Reader for legacy Phoenix MTU-5A instrument time series binary files.
Reads time series data from Phoenix MTU-5A (.TS2, .TS3, .TS4, .TS5) and V5-2000 system (.TSL, .TSH) binary files. The data consists of 24-bit signed integers organized in data blocks with headers.
- Parameters:
file_path (str or Path or None, optional) – Path to the TSN file to read. If None, the reader is created without loading data. Default is None.
- file_path
Path to the currently loaded TSN file.
- Type:
Path or None
- ts
Time series data array with shape (n_channels, n_samples).
- Type:
ndarray or None
- tag
Metadata dictionary containing file information.
- Type:
dict
Examples
Read a TS3 file:
>>> from pathlib import Path >>> reader = MTUTSN('data/1690C16C.TS3') >>> print(reader.ts.shape) (3, 86400) >>> print(reader.tag['sample_rate']) 24
Create reader without loading data:
>>> reader = MTUTSN() >>> reader.read('data/1690C16C.TS3')
Access metadata:
>>> reader = MTUTSN('data/1690C16C.TS4') >>> reader.read() >>> print(f"Channels: {reader.tag['n_ch']}") Channels: 4 >>> print(f"Blocks: {reader.tag['n_block']}") Blocks: 48
- property file_path: pathlib.Path | None
Get the TSN file path.
- ts = None
- ts_metadata = None
- get_sign24(x: numpy.ndarray | list | int) numpy.ndarray[source]
Convert unsigned 24-bit integers to signed integers.
Converts unsigned 24-bit values (0 to 16777215) to their signed equivalents (-8388608 to 8388607) by applying two’s complement.
- Parameters:
x (ndarray or list or int) – Unsigned 24-bit integer value(s) to convert.
- Returns:
Signed 24-bit integer value(s) as int32 array.
- Return type:
ndarray
Examples
Convert a single positive value:
>>> reader = MTUTSN() >>> reader.get_sign24(100) array([100], dtype=int32)
Convert a single negative value (unsigned representation):
>>> reader.get_sign24(16777215) # -1 in 24-bit signed array([-1], dtype=int32)
Convert an array:
>>> values = np.array([0, 8388607, 8388608, 16777215]) >>> reader.get_sign24(values) array([ 0, 8388607, -8388608, -1], dtype=int32)
- read(file_path: str | pathlib.Path | None = None) None[source]
Read and parse a Phoenix MTU time series binary file.
Reads complete time series data from legacy Phoenix MTU-5A instrument files (.TS2, .TS3, .TS4, .TS5) or V5-2000 system files (.TSL, .TSH). Each file contains multiple data blocks with 24-bit signed integer samples organized by channel.
- Parameters:
file_path (str or Path or None, optional) – Path to the TSN file to read. If None, uses the current file_path attribute. Default is None.
- Returns:
ts (ndarray) – Time series data array with shape (n_channels, total_samples). Data type is float64. Each row represents one channel, and each column is a time sample.
tag (dict) – Metadata dictionary containing file information with keys:
’box_number’ (int): Instrument serial number
’ts_type’ (str): Instrument type (‘MTU-5’ or ‘V5-2000’)
’sample_rate’ (int): Sampling frequency in Hz
’n_ch’ (int): Number of channels
’n_scan’ (int): Number of scans per data block
’start’ (MTime): UTC timestamp of first sample
’ts_length’ (float): Duration of each block in seconds
’n_block’ (int): Total number of data blocks in file
- Raises:
EOFError – If the file is empty or cannot be read.
ValueError – If the file has an unsupported extension or channel count.
FileNotFoundError – If the specified file does not exist.
Examples
Read a 3-channel TS3 file:
>>> reader = MTUTSN() >>> ts, tag = reader.read('data/1690C16C.TS3') >>> print(f"Shape: {ts.shape}") Shape: (3, 86400) >>> print(f"Sample rate: {tag['sample_rate']} Hz") Sample rate: 24 Hz >>> print(f"Duration: {ts.shape[1] / tag['sample_rate']:.1f} seconds") Duration: 3600.0 seconds
Read a 4-channel TS4 file:
>>> reader = MTUTSN('data/1690C16C.TS4') >>> print(f"Channels: {reader.tag['n_ch']}") Channels: 4 >>> print(f"Start time: {reader.tag['start'].isoformat()}") Start time: 2016-07-16T00:00:00+00:00
Read and process data:
>>> ts, tag = MTUTSN().read('data/station.TS5') >>> # Calculate statistics for each channel >>> for i in range(tag['n_ch']): ... print(f"Ch{i} mean: {ts[i].mean():.2f}, std: {ts[i].std():.2f}") Ch0 mean: 123.45, std: 456.78 Ch1 mean: -234.56, std: 567.89 ...
- to_runts(table_filepath: str | pathlib.Path | None = None, calibrate=True) mth5.timeseries.RunTS[source]
Create an MTUTable object from the TSN file and associated TBL file.
- Parameters:
table_filepath (str or Path) – Path to the corresponding TBL file.
- Returns:
An MTUTable object containing metadata from the TBL file.
- Return type:
Examples
>>> reader = MTUTSN('data/1690C16C.TS3') >>> mtu_table = reader.to_runts('data/1690C16C.TBL') >>> print(mtu_table.metadata) {...}