sidescanpy.analysis.usable_range#

Usable range: where the signal falls into the far-range noise floor.

  • find_noise_knee() — on a level-vs-range profile in dB, the peak, the lowest level past it and the noise knee: the first range past the peak within knee_db of that lowest level (NoiseKnee).

  • estimate_usable_range() — bins backscatter by range and reports where the signal ends: at the noise knee (method="knee", the default), or at the last bin whose SNR against the far-range floor meets a threshold (method="snr").

Contents

NoiseKnee

Peak, noise floor and noise knee of a level-vs-range profile.

find_noise_knee

Find the profile peak, the noise floor and the noise knee.

UsableRangeResult

Usable-range estimate and the binned profile it came from.

estimate_usable_range

Estimate the usable range of value_var along range_var.

class sidescanpy.analysis.usable_range.NoiseKnee(peak_m=None, peak_db=None, lowest_m=None, lowest_db=None, knee_m=None, knee_found=False, window_m=None, contrast_db=None, low_contrast=False, rises_at_far_range=False, knee_db=None, start_margin_m=None, min_window_m=None, notes=<factory>)#

Bases:

Peak, noise floor and noise knee of a level-vs-range profile.

peak_m is the highest level in the near half of the profile; lowest_m the lowest level past it; knee_m the first range past the peak whose level is within knee_db of the lowest. The fit window runs from peak_m + start_margin_m to the knee. knee_found is False when that window would be shorter than min_window_m (the level does not fall to a floor, as on data the recorder has already levelled); knee_m and the window then run to the end of the record. contrast_db is the fall from the peak to the lowest level, low_contrast flags it under the threshold, and rises_at_far_range flags a far-half level above the peak. Ranges are bin centers; all fields are None when the profile has no finite level.

sidescanpy.analysis.usable_range.find_noise_knee(range_m, level_db, knee_db=3.0, *, start_margin_m=5.0, min_window_m=8.0, low_contrast_db=6.0, peak_search_fraction=0.5, drop_last_bins=2)#

Find the profile peak, the noise floor and the noise knee.

The peak is searched in the near peak_search_fraction of the finite profile, so a level that rises again at far range (recorder gain on noise) cannot pull it out. The last drop_last_bins finite bins are left out (end-of-record artifacts). Beyond the knee the level is noise, which no spreading law describes.

Parameters:
range_mnp.ndarray

Bin centers (m), increasing.

level_dbnp.ndarray

Level per bin (dB); NaN for empty bins.

knee_dbfloat, default 3.0

The knee is the first range past the peak within this many dB of the lowest level.

start_margin_mfloat, default 5.0

The fit window starts this far past the peak (inside it the beam pattern dominates).

min_window_mfloat, default 8.0

A window shorter than this counts as no knee: the window then runs to the end of the record and knee_found is False.

low_contrast_dbfloat, default 6.0

low_contrast is set when the peak is less than this above the lowest level.

peak_search_fractionfloat, default 0.5
drop_last_binsint, default 2
Returns:
NoiseKnee
class sidescanpy.analysis.usable_range.UsableRangeResult(usable_range_m=None, method=None, knee_db=None, knee=None, bin_width_m=None, snr_threshold=None, noise_region_fraction=None, noise_floor=None, noise_std=None, far_range_median=None, max_range_m=None, range_var=None, channel=None, water_column_n_excluded=0, nadir_gap_n_excluded=0, reject_outliers=False, outlier_mad_k=None, outlier_n_rejected=0, notes=<factory>, binned=None, snr=None)#

Bases:

Usable-range estimate and the binned profile it came from.

method is "knee" or "snr". With "knee", usable_range_m is the noise knee of the binned median level in dB (knee holds the details; the end of the record when no knee is found). With "snr" it is the far edge of the last bin whose SNR meets the threshold, None when none does. binned is the backscatter_vs_range() table and snr the per-bin SNR (both serialized as column lists).

sidescanpy.analysis.usable_range.estimate_usable_range(ds, value_var='backscatter_intensity', range_var='slant_range_m', *, method='knee', bin_width_m=2.0, knee_db=3.0, already_db=False, snr_threshold=3.0, noise_region_fraction=0.1, exclude_before_sample=None, reject_outliers=False, outlier_mad_k=3.5, outlier_min_bin_count=5, channel=None)#

Estimate the usable range of value_var along range_var.

method="knee" (default): the per-bin medians are converted to dB (20 log10, unless already_db) and the usable range is their noise knee (find_noise_knee()): the first range past the near-range peak within knee_db of the lowest level. When the level never falls to a floor, the end of the record, with a note.

method="snr": the noise floor is the median of the per-bin medians over the farthest noise_region_fraction of bins; noise std is the mean of their stds (falling back to the median nonzero bin std). SNR is |bin median - floor| / noise std; the usable range is the far edge of the last bin with SNR >= snr_threshold, None when no bin reaches it (common on data the recorder has levelled).

Water-column samples are always excluded using ground_range_m’s NaN pattern (when present), independent of range_var.

Parameters:
range_varstr, default “slant_range_m”

Range axis to bin by (slant range matches the TVG model’s axis).

method{“knee”, “snr”}, default “knee”
bin_width_mfloat, default 2.0
knee_dbfloat, default 3.0

method="knee" only.

already_dbbool, default False

method="knee" only: value_var is already in dB, so the bin medians are used as they are.

snr_thresholdfloat, default 3.0

method="snr" only.

noise_region_fractionfloat, default 0.1

method="snr" only.

exclude_before_samplenp.ndarray, optional

(ping, channel) sample index; samples before it are dropped before binning, e.g. a per-ping first-bottom-return boundary.

reject_outliersbool, default False

Run reject_range_outliers() first (with outlier_mad_k / outlier_min_bin_count), so the floor reflects a flat-bottom-like population.

channelint or str, optional

Restrict to one physical side (index, or channel_id value such as "port"). None pools both sides. Applied by masking.