sidescanpy.io.xtf.reader#
Low-level XTF (eXtended Triton Format) reader.
Reads the file header, frames packets, and parses sonar packets into
per-ping header records and per-channel sample arrays. The converter to
the standard survey model is sidescanpy.io.xtf.converter;
read_xtf is the per-file entry point that feeds it.
File layout#
A file starts with a 1024-byte header holding up to six ChanInfo
channel descriptions; with more than six channels the remaining
ChanInfo entries follow contiguously in further 1024-byte blocks.
Packets follow. Each starts with the magic 0xFACE, a header type and
NumBytesThisRecord, the packet’s total size including any padding.
That byte count is authoritative: the reader steps from packet to packet
with it, never with sizes derived from the packet contents. A packet with
a bad magic triggers a forward scan for the next 0xFACE (counted, and
logged at WARNING); a truncated final packet is logged at WARNING and
ends the read.
A sonar packet (header type 0) is a 256-byte ping header followed, for
each channel, by a 64-byte channel header and NumSamples samples.
The sample type comes from the file header’s ChanInfo entry for that
channel (SampleFormat, else BytesPerSample; a SampleFormat
the specification does not define, such as 255, counts as unset).
Channel blocks are walked using the packet byte count#
NumChansToFollow in the ping header is treated as an upper bound.
Channel blocks are parsed while at least a full 64-byte channel header
remains inside the packet; a channel whose samples would run past the
packet end is clipped to the samples that fit. Each mismatch kind is
logged at WARNING once per file with a count.
Evidence for this rule (EdgeTech 4200 files recorded by Isis, four
channels declared in the file header: port/starboard at 120 kHz as
channels 0/1 and at 410 kHz as channels 2/3, BytesPerSample 2):
In two such files the first sonar packets (2976 of 25539 in one file, 1680 of 20475 in the other, a single contiguous run at the start) are 4480 bytes long while their ping headers say
NumChansToFollow = 4and the first two channel headers sayNumSamples = 1024. Normal packets are 8704 = 256 + 4 * (64 + 1024 * 2) bytes.4480 = 256 + 2 * (64 + 1024 * 2) exactly: the packet holds two complete channel blocks (channel numbers 0 and 1, the low-frequency pair) and nothing else. The “third channel header” a reader trusting
NumChansToFollowwould parse starts with0xFACE: it is the next packet.The samples are 16-bit, 1024 per channel as declared. Read as uint16, adjacent samples correlate (lag-1 r = 0.96 and 0.79 over the two runs, 0.86 and 0.80 in the same files’ normal pings), values stay within the normal pings’ 0..32767 range, and medians are comparable (1148 vs 1505, 285 vs 731). Read as 8-bit, adjacent samples do not correlate (r = -0.01 and -0.46). The first pings of each run are faint (high byte zero in 88-99.99 % of the first 200 pings), rising to the normal level. Ping numbers are continuous across the switch to four-channel packets, navigation is continuous, and the low-only pings repeat faster (0.08 s vs 0.14 s at 100 m range, 0.21 s vs 0.42 s at 300 m).
So those pings carry only the low-frequency pair; the high-frequency
channels are absent, not empty. pyxtf 1.5.0 trusts NumChansToFollow
and fails on such packets.
Sample arrays#
Samples keep their recorded dtype (uint8/uint16/uint32/float32), so
values are bit-identical to the file. A channel’s array is
(n_ping, max_samples) over all sonar pings of the file; pings where
the channel is absent or shorter are zero-padded on the right, and the
true length is in num_samples (0 where absent, with present
False). The converter decides whether padding becomes NaN.
2026 rshomberg@uri.edu
Contents
Parsed XTF file header. |
|
Return the file-header size in bytes for |
|
Read the file header of an XTF file. |
|
One framed packet. |
|
Counts gathered while framing packets. |
|
Stream the packets of an XTF file. |
|
Frame every packet of a file without parsing them. |
|
Samples and headers of one sonar channel over all pings. |
|
A port/starboard channel pair sharing one frequency. |
|
Mismatches between sonar packets' declared and actual channel blocks. |
|
Where a run of pings sits in its source file. |
|
Sonar pings of one XTF file, as recorded. |
|
Compute ping times from ping header records. |
|
Read every sonar ping of an XTF file. |
|
Read one XTF file for conversion. |
- class sidescanpy.io.xtf.reader.XtfFileHeader(record, chan_info, size)#
Bases:
objectParsed XTF file header.
- Attributes:
- recordnumpy.void
The first 1024-byte header block as a
FILE_HEADER_DTYPErecord (itsChanInfofield holds the first six slots).- chan_infonumpy.ndarray
CHAN_INFO_DTYPEarray with one entry per declared channel (the sum of the header’s channel counts), including entries from extra header blocks.- sizeint
Header size in bytes, i.e. the offset of the first packet.
- property n_channels#
Number of channels declared in the header.
- sidescanpy.io.xtf.reader.header_size_for_channels(n_channels)#
Return the file-header size in bytes for
n_channelschannels.The first 1024-byte block holds six
ChanInfoentries; further entries follow contiguously from byte 1024, padded to whole 1024-byte blocks.- Parameters:
- n_channelsint
Declared channel count.
- Returns:
- int
- sidescanpy.io.xtf.reader.read_xtf_header(path)#
Read the file header of an XTF file.
- Parameters:
- pathstr or Path
XTF file.
- Returns:
- XtfFileHeader
- Raises:
- FileNotFoundError
If the file does not exist.
- ValueError
If the file is too short or its first byte is not the XTF
FileFormatvalue 0x7B.
- class sidescanpy.io.xtf.reader.XtfPacket(header_type, offset, data)#
Bases:
objectOne framed packet.
- Attributes:
- header_typeint
HeaderTypefrom the packet start (seestructs.HEADER_TYPE_NAMES).- offsetint
Byte offset of the packet in the file.
- databytes
The whole packet, starting with its 14 packet-start bytes, of length
NumBytesThisRecord.
- property start#
The packet start fields as a
PACKET_START_DTYPErecord.
- class sidescanpy.io.xtf.reader.PacketStats(counts=<factory>, skipped=<factory>, unknown=<factory>, resyncs=0, resync_bytes=0, truncated=False)#
Bases:
objectCounts gathered while framing packets.
- Attributes:
- countscollections.Counter
Packets framed, by header type (yielded or skipped).
- skippedcollections.Counter
Packets not yielded because of the
header_typesfilter.- unknowncollections.Counter
Packets whose header type the specification does not define.
- resyncsint
Bad-magic events that needed a forward scan.
- resync_bytesint
Bytes skipped by those scans.
- truncatedbool
True if the file ended inside a packet.
- sidescanpy.io.xtf.reader.iter_packets(path, header_types=None, stats=None)#
Stream the packets of an XTF file.
- Parameters:
- pathstr or Path
XTF file.
- header_typesiterable of int, optional
Header types to yield. Others are skipped by their byte count without reading their body. Default: yield every packet.
- statsPacketStats, optional
Filled in place with counts as packets are framed (useful because a generator has no return value to the caller).
- Yields:
- XtfPacket
Packets in file order.
Notes
Packets whose header type is not defined by the specification are counted in
stats.unknownand logged at DEBUG; they are yielded like any other when no filter is given. A bad magic number or a byte count smaller than the packet start triggers a forward scan for the next0xFACE(logged at WARNING). A packet extending past the end of the file is logged at WARNING and ends iteration.
- sidescanpy.io.xtf.reader.count_packets(path)#
Frame every packet of a file without parsing them.
- Parameters:
- pathstr or Path
XTF file.
- Returns:
- PacketStats
Counts by header type, plus resync and truncation information.
- class sidescanpy.io.xtf.reader.XtfChannel(channel_number, chan_info, name, side, frequency, samples, num_samples, present, headers)#
Bases:
objectSamples and headers of one sonar channel over all pings.
- Attributes:
- channel_numberint
ChannelNumberfrom the channel headers (index into the file header’sChanInfo).- chan_infonumpy.void
The
ChanInforecord for this channel.- namestr
ChanInfo.ChannelName.- sidestr
"port","stbd"or"other"(fromTypeOfChannel).- frequencyfloat
ChanInfo.Frequency, or the most common nonzero per-ping channel-headerFrequencywhen that is 0, else NaN. The unit is whatever the recorder wrote (kHz in the files seen so far).- samplesnumpy.ndarray
(n_ping, max_samples)in the recorded sample dtype, zero-padded on the right.- num_samplesnumpy.ndarray
(n_ping,)uint32, samples stored for each ping (0 where absent).- presentnumpy.ndarray
(n_ping,)bool, whether the ping held this channel.- headersnumpy.ndarray
(n_ping,)PING_CHAN_HEADER_DTYPErecords; all zero where the channel is absent.
- class sidescanpy.io.xtf.reader.XtfFrequencyGroup(frequency, port=None, stbd=None)#
Bases:
objectA port/starboard channel pair sharing one frequency.
- Attributes:
- frequencyfloat
Frequency as recorded (see
XtfChannel.frequency); NaN when unknown, in which case channels were paired in channel order.- port, stbdXtfChannel or None
The two sides; one may be missing.
- class sidescanpy.io.xtf.reader.ChannelBlockStats(fewer_channels=0, declared=<factory>, found=<factory>, clipped=0, no_chan_info=0, legacy_count=0)#
Bases:
objectMismatches between sonar packets’ declared and actual channel blocks.
- Attributes:
- fewer_channelsint
Sonar packets holding fewer complete channel blocks than their
NumChansToFollow.- declared, foundcollections.Counter
For those packets: declared channel counts, and the number of blocks actually found.
- clippedint
Channel blocks declaring more samples than fit in their packet (clipped to what fits).
- no_chan_infoint
Channel blocks with no usable
ChanInfoentry (rest of the packet skipped).- legacy_countint
Channel blocks whose sample count came from
ChanInfobecauseNumSampleswas 0 (pre-X41 files).
- class sidescanpy.io.xtf.reader.XtfSegmentInfo(index, count, ping_start, ping_stop, n_pings_file, n_runs_dropped, n_pings_dropped, rtol)#
Bases:
objectWhere a run of pings sits in its source file.
Set by
sidescanpy.io.xtf.split_by_sample_rate().- Attributes:
- indexint
1-based position among the kept runs.
- countint
Number of kept runs of the file.
- ping_start, ping_stopint
The run’s pings are
[ping_start, ping_stop)in the file’s sonar-ping order.- n_pings_fileint
Sonar pings in the whole file.
- n_runs_droppedint
Runs of the file shorter than
min_pings, dropped.- n_pings_droppedint
Pings in those dropped runs.
- rtolfloat
Relative tolerance used for the cuts.
- class sidescanpy.io.xtf.reader.XtfPingData(path, file_header, ping_headers, packet_offsets, channels, frequency_groups, stats, block_stats=<factory>, segment=None)#
Bases:
objectSonar pings of one XTF file, as recorded.
- Attributes:
- pathPath
Source file.
- file_headerXtfFileHeader
Parsed file header.
- ping_headersnumpy.ndarray
(n_ping,)PING_HEADER_DTYPErecords, in file order.- packet_offsetsnumpy.ndarray
(n_ping,)int64 file offset of each sonar packet.- channelsdict[int, XtfChannel]
Channels keyed by
ChannelNumber.- frequency_groupslist[XtfFrequencyGroup]
Port/starboard channels grouped by frequency, ascending (unknown frequencies last).
- statsPacketStats
Packet counts from the read.
- block_statsChannelBlockStats
Channel-block mismatches found while parsing sonar packets.
- segmentXtfSegmentInfo or None
Set when these pings are one run of a file split at sample-rate changes (
sidescanpy.io.xtf.split_by_sample_rate()); None for a whole file.
- property n_pings#
Number of sonar pings.
- property times#
Ping times as
datetime64[ns](seeping_times()).
- sidescanpy.io.xtf.reader.ping_times(ping_headers)#
Compute ping times from ping header records.
Uses
Year,Month,Day,Hour,Minute,SecondandHSeconds(hundredths). The time base is whatever the recorder used (normally UTC). Records with an invalid date giveNaT.- Parameters:
- ping_headersnumpy.ndarray
PING_HEADER_DTYPErecords.
- Returns:
- numpy.ndarray
datetime64[ns], one per record.
- sidescanpy.io.xtf.reader.read_xtf_pings(path)#
Read every sonar ping of an XTF file.
Two passes over the file: the first frames sonar packets and records the ping and channel headers and where each channel’s samples are; the second reads the samples straight into preallocated arrays. Other packet types are counted and skipped.
- Parameters:
- pathstr or Path
XTF file.
- Returns:
- XtfPingData
Ping headers, per-channel sample arrays (recorded dtype, zero-padded) and port/starboard frequency groups.
- Raises:
- ValueError
If the file header is invalid or a channel uses an unsupported sample format.
- sidescanpy.io.xtf.reader.read_xtf(path)#
Read one XTF file for conversion.
The XTF counterpart of
read_jsf/read_sdf: returns whatsidescanpy.io.xtf.convert()takes. Equivalent toread_xtf_pings().- Parameters:
- pathstr or Path
XTF file.
- Returns:
- XtfPingData