sidescanpy.processing.farrange#

Far-range noise detection and cropping.

Some surveys record a channel’s sample window well past where real backscatter decays into noise — a per-survey acquisition setting, not fixed by frequency. Past that point, raw amplitude doesn’t stay flat — it climbs back up, new evidence for a hardware TVG/AGC artifact baked into the raw signal — which apply_tvg()’s uncapped, monotonically-increasing gain formula then massively over-amplifies (a ~57 dB overshoot measured on real data), saturating percentile-based color scales and burying the real near-nadir signal. This module detects that decay-then-rise pattern directly from the uncorrected signal (and crops it out — intentionally conservative: a channel with no detected rise is left untouched, not cropped by a guessed constant, since the recorded window length varies per survey.

Binning is done by raw sample index, not real range — this needs no geometric computation (no aux interpolation, no altitude), so it can run immediately after conversion, before any other correction step.

2026 rshomberg@uri.edu

Contents

detect_far_range_cutoff_sample

Detect the sample index beyond which backscatter climbs back up after decaying into its real noise floor.

crop_far_range

Crop one channel's far-range noise (the "crop_farrange" step).

crop_far_range_noise

Apply crop_far_range() to every frequency channel in survey.

sidescanpy.processing.farrange.detect_far_range_cutoff_sample(ds, *, n_bins=40, min_valid_fraction=0.5, rise_threshold_db=3.0, margin_bins=1)#

Detect the sample index beyond which backscatter climbs back up after decaying into its real noise floor.

Works on the uncorrected signal: any recorded radiometric steps are inverted first (sidescanpy.processing.inversion.uncorrected_backscatter()), so it can run before or after TVG. Bin statistics are accumulated in ping blocks, so memory stays bounded on large (or dask-backed) datasets.

Bins the uncorrected signal (converted to dB) by sample index across n_bins roughly-equal bins, finds the peak bin (the strongest mean return) among bins with enough finite samples to be statistically reliable, then finds the minimum-mean bin after that peak (the real far-range floor — searched only post-peak so a legitimate near-nadir ramp-up is never mistaken for the floor). If any later, equally-reliable bin’s mean then rises rise_threshold_db or more above that floor, returns a cropped sample count (the floor bin’s end, plus margin_bins of slack). If no such rise is found, returns None — nothing should be cropped.

A cutoff whose slant range is not beyond the median altitude_m is rejected with a WARNING and None is returned: on recordings that include the transmit pulse and the water column, the “floor” found is the water column and the “rise” is the seafloor return.

Parameters:
dsxr.Dataset

A single frequency channel dataset.

n_binsint, default 40

Number of sample-index bins to divide the sample axis into.

min_valid_fractionfloat, default 0.5

A bin is “reliable” only if at least this fraction of its n_pings * n_channels * bin_size cells are finite.

rise_threshold_dbfloat, default 3.0

How far above the detected floor a later, reliable bin’s mean must climb to count as “the noise climbing back up”.

margin_binsint, default 1

Extra bins kept past the detected floor bin.

Returns:
int or None

Sample count to crop to (ds.isel(sample=slice(0, result))), or None if no crop is warranted.

sidescanpy.processing.farrange.crop_far_range(ds, *, cutoff_sample=None, n_bins=40, min_valid_fraction=0.5, rise_threshold_db=3.0, margin_bins=1)#

Crop one channel’s far-range noise (the "crop_farrange" step).

Detects the cutoff with detect_far_range_cutoff_sample() (the detection keyword arguments are passed through) unless cutoff_sample is given. A channel with no detected artifact is left untouched. Either way a "crop_farrange" history record is appended (with the resolved cutoff_sample, None when nothing was cropped), so a later run knows detection already happened.

Not invertible: cropped samples are gone (re-convert from the source file to get them back).

ds may also be a SideScanSurvey: each frequency is cropped at its own detected cutoff and a new survey is returned (the input is not modified).

sidescanpy.processing.farrange.crop_far_range_noise(survey, **detect_kwargs)#

Apply crop_far_range() to every frequency channel in survey.

Parameters:
surveySideScanSurvey

Mutated in place (frequency channels replaced) and returned.

**detect_kwargs

Passed through to crop_far_range().

Returns:
(survey, cutoffs)

cutoffs: freq_key -> cropped sample count, or None for channels left unchanged.