sidescanpy.io.sdf.pages#

Klein SDF page types and parser.

A Klein SDF file is a sequence of pages. Each page has: - A 4-byte sync marker (always 0xFFFFFFFF) - A 512-byte typed header (described by SDFPageHeader) - Variable-length data section (sonar samples for sonar page versions)

The header is a binary structure of 128 uint32-equivalent slots. Most fields are uint32 or float32 (4 bytes); seven fields are float64 (8 bytes, taking 2 slots). All values are little-endian.

Spec source: Klein SDF/SDFX Data Page Definitions Specification.

Conventions encoded here:

  • Latitude/longitude stored as float64 in radians; converted to degrees via @property

  • Heading/pitch/roll stored as float32 in degrees; no conversion

  • speedFish stored as cm/s; converted to m/s via @property

  • speedSound stored as cm/s; converted to m/s via @property

  • sonarFreq stored in kHz; converted to Hz via @property

  • Year normally 4-digit; older firmware uses offset-from-1900, auto-detected via < 200 check

  • Time fractional component (fseconds field 51) preferred over hSecond field 24 when both are valid

2026 rshomberg@uri.edu

Contents

MARKER

int([x]) -> integer int(x, base=10) -> integer

MARKER_BYTES

int([x]) -> integer int(x, base=10) -> integer

PAGE_HEADER_BYTES

int([x]) -> integer int(x, base=10) -> integer

HEADER_TOTAL_BYTES

int([x]) -> integer int(x, base=10) -> integer

PAGE_VERSIONS_SONAR

frozenset() -> empty frozenset object frozenset(iterable) -> frozenset object

PAGE_VERSIONS_TOWFISH_TYPE

SDFPageHeader

Klein SDF page header (512 bytes after the 4-byte marker).

SDFSonarPage

A parsed Klein SDF sonar data page (versions 3501/3502).

SDFUnknownPage

A parsed Klein SDF page of unknown/unsupported version.

sidescanpy.io.sdf.pages.MARKER = 4294967295#

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

sidescanpy.io.sdf.pages.MARKER_BYTES = 4#

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

sidescanpy.io.sdf.pages.PAGE_HEADER_BYTES = 512#

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

sidescanpy.io.sdf.pages.HEADER_TOTAL_BYTES = 516#

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

sidescanpy.io.sdf.pages.PAGE_VERSIONS_SONAR = frozenset({3501, 3502})#

frozenset() -> empty frozenset object frozenset(iterable) -> frozenset object

Build an immutable unordered collection of unique elements.

sidescanpy.io.sdf.pages.PAGE_VERSIONS_TOWFISH_TYPE#
class sidescanpy.io.sdf.pages.SDFPageHeader(_page_size, _page_version, _configuration, ping_number, num_samples, _beams_to_display, _error_flags, range_m, _speed_cms, _sound_speed_cms, _res_mode, _tx_waveform, _responder_divisor, _responder_frequency, _manual_speed_switch, _despeckle_switch, _speed_filter_switch, _year, _month, _day, _hour, _minute, _second, _hsecond, _fix_time_hour, _fix_time_minute, _fix_time_second, heading_deg, pitch_deg, roll_deg, depth_volts, altitude_m, temperature_c, _ship_speed_ms, _ship_heading_deg, _ship_magnetic_variation, _ship_latitude_rad, _ship_longitude_rad, _fish_latitude_rad, _fish_longitude_rad, _tvg_page, _header_size, fix_time_year, fix_time_month, fix_time_day, aux_pitch, aux_roll, aux_depth, aux_altitude, cable_out_m, _fseconds, altimeter_on, sample_freq_hz, depressor_type, cable_type, shieve_x_offset_m, shieve_y_offset_m, shieve_z_offset_m, gps_height_m, _raw_data_config, _header_3_extension_size, sbp_tx_waveform, sbp_preamp_gain, sbp_data_raw, sbp_samples, sbp_sample_frequency, sbp_tx_waveform_version, wing_angle_deg, emergency_switch_state, layback_method, _layback_fish_latitude_rad, _layback_fish_longitude_rad, fish_heading_offset_deg, pressure_sensor_offset_psi, tpu_sw_version, capability_mask, tx_version, num_samples_extra, post_processing_version, motion_sensor_type, ping_time_ref_count, ext_trig_time_ref_count, one_pps_time_ref_count, time_ref_count_weight, altitude_bathy_m, ping_interval_s, sdf_extension_size, seconds_of_week, sound_speed_source, pressure_sensor_max, pressure_sensor_voltage_min, pressure_sensor_voltage_max, temperature_ambient_c, saturation_detect_threshold, _sonar_freq_khz, _reserved=<factory>)#

Bases: object

Klein SDF page header (512 bytes after the 4-byte marker).

All fields are little-endian. The dataclass holds the raw spec values; transformed (unit-converted) versions are exposed via @property.

Field numbers in comments correspond to the Klein spec table (1-100 named, 101-126 reserved). Fields prefixed with underscore are internal raw forms with corresponding public properties.

Note on uint16 fields (80-83): the spec says U16 but Klein stores them in 32-bit slots with values fitting in the lower 16 bits. They’re parsed as uint32 here for layout purity.

classmethod from_bytes(data)#

Parse 512 bytes of header data into an SDFPageHeader.

The 4-byte marker is NOT included; caller should validate and consume it separately.

to_bytes()#

Pack the dataclass back into 512 bytes.

property page_size#

Page size in bytes (excludes the 4-byte marker).

property speed_ms#

Towfish speed in m/s (converted from cm/s).

property sound_speed_ms#

Speed of sound at transducer in m/s (converted from cm/s).

property ship_speed_ms#

Ship speed from NMEA in m/s (already in m/s in spec).

property ship_heading_deg#

Ship Course Over Ground from NMEA, degrees.

property ship_magnetic_variation_deg#

Magnetic variation from NMEA $–RMC, degrees.

property ship_latitude_deg#

Ship latitude in degrees (converted from radians).

property ship_longitude_deg#

Ship longitude in degrees (converted from radians).

property fish_latitude_deg#

Towfish latitude in degrees (converted from radians).

property fish_longitude_deg#

Towfish longitude in degrees (converted from radians).

property layback_fish_latitude_deg#

Layback-corrected towfish latitude in degrees.

property layback_fish_longitude_deg#

Layback-corrected towfish longitude in degrees.

property sonar_freq_hz#

Operating frequency in Hz (converted from kHz).

property sonar_freq_khz#

Operating frequency in kHz (raw spec value).

property fix_time_second#

NMEA fix second.

property hsecond#

Hundredths of second from hSecond field (legacy; prefer fseconds).

property fseconds#

TPU fractional seconds (preferred over hSecond).

property error_flags#

Raw error flags bit field.

has_error(bit)#

Check if a specific error flag bit is set.

Bits per spec:

0: Invalid speed 1: GPS data error 2: Telemetry error 3: Sensor checksum error 4: Towfish leak 5: Bad data 6: Watchdog 7: Compass error 8: No GPS Lat/Lon sentence input 9: No GPS speed sentence input 10: No GPS ZDA sentence input 11: No data from MRU or MRU error 12: No 1 PPS input

class sidescanpy.io.sdf.pages.SDFSonarPage(header, port_data=None, stbd_data=None, extension_bytes=b'')#

Bases: object

A parsed Klein SDF sonar data page (versions 3501/3502).

Wraps the SDFPageHeader plus the per-channel sample arrays. The reader populates port_data, stbd_data, and extension_bytes after parsing the header.

property latitude#

Towfish latitude (degrees), preferring fish_latitude over ship.

property longitude#

Towfish longitude (degrees).

property depth_m#

Depth in meters. Note: spec stores as Volts; needs INI calibration.

property fathometer_reading_m#

Raw altimeter reading from towfish.

class sidescanpy.io.sdf.pages.SDFUnknownPage(header, body_bytes=b'')#

Bases: object

A parsed Klein SDF page of unknown/unsupported version.

The header is fully parsed (since all SDF pages share the same header structure), but the body is preserved as raw bytes for later analysis. Use the page version to identify what kind of page it is and decide whether to add typed parsing.

Attributes:
headerSDFPageHeader

Parsed page header (always valid).

body_bytesbytes

Raw body content (size = header.page_size - PAGE_HEADER_BYTES). Includes everything between the header and the next page’s marker.