The .ss.h5 file#
The data format on disk: a whole survey (every frequency
and every aux record) in one HDF5 file with the extension .ss.h5.
Each group is a NetCDF-4 group written with h5netcdf, so any HDF5 or
NetCDF tool can read it, and xarray can open each group without
sidescanpy.
Format version 0.1, a draft
The layout below may change in any release before 1.0, and a file written by one version may not read in a later one. Keep the raw source files and re-convert after an upgrade. Comments on the layout are welcome; see Data format.
from sidescanpy.io.hdf5 import read_survey, read_survey_pings, write_survey
write_survey(survey, "line_001.ss.h5")
survey = read_survey("line_001.ss.h5")
survey = read_survey("line_001.ss.h5", chunks={"ping": 256}) # lazy, dask-backed
survey = read_survey_pings("line_001.ss.h5", start=0, stop=500) # a run of pings
write_survey refuses to overwrite an existing file unless
overwrite=True. processing.correct_file corrects a file in place,
writing to a temporary file and renaming it so an interrupted run never
leaves a half-written survey.
Layout#
line_001.ss.h5
├── / root attributes (below)
├── frequencies/
│ ├── hz_100000/ one group per frequency: a SideScanFrequencyChannel
│ └── hz_400000/
└── aux_streams/
├── navigation/ always written, empty if the file had none
├── attitude/ always written, empty if the file had none
├── dvl/ always written, empty if the file had none
└── sensors/ only if the survey has sensor records
└── pressure/
Root attributes:
Attribute |
Content |
|---|---|
|
|
|
version of this layout, separate from the package version: |
|
version that wrote the file (absent before 0.8.0) |
|
when the file was written, ISO 8601 UTC (absent before 0.9.0) |
|
defaults to the first source file’s stem |
|
|
|
JSON list of the source file paths |
Frequency groups#
Each frequencies/hz_<Hz> group holds dimensions ping, channel
(always 2: port, starboard) and sample. A freshly converted file
has:
Variable |
Dimensions |
Type |
Meaning |
|---|---|---|---|
|
ping, channel, sample |
float32 |
amplitude as recorded (scaled by the format’s weighting); corrected in place by radiometric steps |
|
ping, channel |
int32 |
samples actually recorded in each ping; the rest is NaN padding |
|
ping |
datetime64 |
ping time as recorded |
|
ping |
int32 |
ping number from the file |
|
sample |
int64 |
absolute sample index; slicing keeps it, so ranges stay right |
|
channel |
string |
|
|
ping |
float64 |
position, degrees (WGS84) |
|
ping |
float64 |
heading, degrees clockwise from north |
|
ping |
float64 |
attitude, degrees |
|
ping |
float64 |
heave, metres |
|
ping |
float32 |
sensor depth below the surface, metres |
|
ping |
float32 |
height above the seabed, metres |
|
ping |
float32 |
altimeter reading as recorded, metres |
|
ping |
float32 |
sound velocity, m/s (NaN or 0 when not recorded) |
The geometric and georef correction steps add per-sample variables
on (ping, channel, sample), each with units and long_name
attributes:
Variable |
Added by |
Meaning |
|---|---|---|
|
|
slant range from the transducer |
|
|
range across the seabed; NaN in the water column |
|
|
signed across-track distance, port negative |
|
|
along-track offset from nadir (pitch) |
|
|
vertical distance from the transducer to the sample |
|
|
water depth at the sample |
|
|
position of the sample, degrees (float64) |
Group attributes:
Attribute |
Content |
|---|---|
|
transmit frequency band, Hz |
|
sample rate, Hz |
|
pulse length, µs (0 when the format does not record it) |
|
JSON list of applied correction steps (Processing) |
|
reader provenance: recording program, sonar name, channel numbers, counts of skipped or repaired packets |
Per-ping variables carry their units in the name suffix (_deg, _m,
_ms) rather than a units attribute.
Aux groups#
Aux records have their own time on an obs dimension, independent of
the pings, because sensors update on their own schedules.
Group |
Variables |
|---|---|
|
|
|
|
|
|
|
sensor-specific; |
Each group has a source attribute naming where its values came from
(for example extracted_from_xtf_ping_headers).
Encoding#
Variables with two or more dimensions are gzip level 4 with shuffle, chunked 256 pings at a time. Per-ping variables are uncompressed.
float64 is stored as float32, except latitude and longitude, where float32 resolves only about 0.4 m.
read_survey(path, chunks={"ping": 256})matches the on-disk chunks; other chunk sizes work but read more than they need. Aux records always load into memory (they are small).Files from 0.5.0 may also carry
backscatter_raw; they still read, andcorrect_filedrops that variable when it rewrites the file.
Reading without sidescanpy#
import xarray as xr
ds = xr.open_dataset("line_001.ss.h5", group="frequencies/hz_400000", engine="h5netcdf")
nav = xr.open_dataset("line_001.ss.h5", group="aux_streams/navigation", engine="h5netcdf")
From the shell, h5ls -r line_001.ss.h5 lists every group and variable
and h5dump -A line_001.ss.h5 prints the attributes.
For one frequency on its own, as a plain NetCDF file, see NetCDF, one frequency.