sidescanpy.processing.radiometric#
Radiometric correction for sidescan sonar data.
This module implements Time-Varying Gain (TVG) correction to compensate for transmission losses in the water column. The raw backscatter_intensity values represent echo levels received by the transducer but do not account for:
Geometric spreading: sound spreads in 3D, so intensity falls as 1/R² each way. Over the two-way path that is 40*log10(R) dB (samples are amplitude, converted with 20*log10, so dB values are comparable to intensity dB). Some authors use 30*log10(R) for sidescan, because the insonified seafloor patch grows in proportion to R and adds back 10*log10(R) (see the note on
TVGParams); the coefficient is a parameter, not fixed.Absorption: water absorbs acoustic energy proportional to distance traveled (in dB: 2*alpha*R dB loss for two-way path)
TVG correction adds these losses back, so equal backscatter at different ranges produces equal corrected values. This is essential for radiometric calibration and mosaic generation.
The default absorption coefficient comes from the Francois-Garrison seawater
absorption model (francois_garrison_absorption_db_per_m()):
Francois and G. R. Garrison, “Sound absorption based on ocean measurements: Part I: Pure water and magnesium sulfate contributions,” J. Acoust. Soc. Am. 72(3), 896-907 (1982).
Francois and G. R. Garrison, “Sound absorption based on ocean measurements: Part II: Boric acid contribution and equation for total absorption,” J. Acoust. Soc. Am. 72(6), 1879-1890 (1982).
Input backscatter is assumed to be linear amplitude; this module converts to
dB, applies TVG, and outputs dB. The step is recorded in processing_history
with its resolved parameters so it can be inverted exactly.
2026 rshomberg@uri.edu
Contents
Convert a string or number to a floating-point number, if possible. |
|
Convert a string or number to a floating-point number, if possible. |
|
Convert a string or number to a floating-point number, if possible. |
|
Convert a string or number to a floating-point number, if possible. |
|
Default TVG spreading coefficient (dB per decade of range): 30 log R. |
|
Parameters for time-varying gain correction. |
|
Seawater sound absorption (dB/m, one-way) after Francois & Garrison (1982). |
|
Acoustic center frequency of a channel (Hz), for frequency-dependent physics. |
|
Default absorption coefficient (dB/m, one-way) for a sonar frequency. |
|
Per-range TVG gain (dB): spreading loss + absorption loss. |
|
Apply TVG correction to a plain linear-amplitude array — no xarray |
|
Apply time-varying gain (TVG) correction to |
|
Fill a TVGParams' unset fields from |
|
Per-sample TVG gain (dB, float64, shape |
|
History-record params for a resolved TVGParams. |
|
TVGParams from a |
- sidescanpy.processing.radiometric.DEFAULT_TEMPERATURE_C = 10.0#
Convert a string or number to a floating-point number, if possible.
- sidescanpy.processing.radiometric.DEFAULT_SALINITY_PSU = 35.0#
Convert a string or number to a floating-point number, if possible.
- sidescanpy.processing.radiometric.DEFAULT_DEPTH_M = 10.0#
Convert a string or number to a floating-point number, if possible.
- sidescanpy.processing.radiometric.DEFAULT_PH = 8.0#
Convert a string or number to a floating-point number, if possible.
- sidescanpy.processing.radiometric.DEFAULT_SPREADING_LOSS_DB = 30.0#
Default TVG spreading coefficient (dB per decade of range): 30 log R.
Used by
TVGParamsand the TVG curve fits. 30 log R models the insonified seafloor area growing with range; 40 log R (two-way spherical spreading) and 20 log R (one-way) remain available by passingspreading_loss_db.
- class sidescanpy.processing.radiometric.TVGParams(spreading_loss_db=30.0, absorption_db_per_m=None, min_range_m=0.5, sound_velocity_ms=None, temperature_c=None, salinity_psu=None, depth_m=None, ph=None)#
Bases:
objectParameters for time-varying gain correction.
- Attributes:
- spreading_loss_dbfloat, default=30.0
Spreading loss coefficient (dB per decade of range): the gain is
spreading_loss_db * log10(R). 30.0 (the default,DEFAULT_SPREADING_LOSS_DB) models the insonified seafloor area growing with range (e.g. J. Mar. Sci. Eng. 2017, 5(4), 54, https://www.mdpi.com/2077-1312/5/4/54). 40.0 is two-way spherical spreading (the default in 0.7.0-0.7.5); 20.0 is one-way spherical spreading (the default before 0.7.0). Which fits real data varies, so measure withsidescanpy.analysis.fit_range_decay()per survey. 0.0 disables the term (hardware already compensates spreading).- absorption_db_per_mfloat, optional
Absorption coefficient (dB per meter). If None, derived from the channel frequency with
francois_garrison_absorption_db_per_m()(Francois & Garrison, J. Acoust. Soc. Am. 72(3) 896-907 and 72(6) 1879-1890, 1982) and the four water parameters below (typical-coastal defaults where None).- min_range_mfloat, default=0.5
Minimum range threshold (meters). Ranges below this are clipped to avoid log(0) singularity. Typical value 0.5 m accounts for transducer blanking.
- sound_velocity_msfloat, optional
Sound velocity (m/s) used to turn sample index into slant range. If None, the mean of the dataset’s per-ping
sound_vel_ms.- temperature_c, salinity_psu, depth_m, phfloat, optional
Water conditions for the Francois-Garrison absorption; used only when
absorption_db_per_mis None (defaults 10 C, 35 psu, 10 m, pH 8 – pass real values when known).
- sidescanpy.processing.radiometric.francois_garrison_absorption_db_per_m(frequency_hz, *, temperature_c=10.0, salinity_psu=35.0, depth_m=10.0, ph=8.0, sound_velocity_ms=None)#
Seawater sound absorption (dB/m, one-way) after Francois & Garrison (1982).
Sum of three relaxation / viscosity terms (
fin kHz, result in dB/km before conversion):alpha = A1 P1 f1 f^2 / (f1^2 + f^2) boric acid + A2 P2 f2 f^2 / (f2^2 + f^2) magnesium sulfate + A3 P3 f^2 pure water (viscosity)
with the paper’s coefficients:
f1 = 2.8 sqrt(S/35) 10^(4 - 1245/(T+273)),f2 = 8.17 10^(8 - 1990/(T+273)) / (1 + 0.0018 (S - 35)),A3having separate polynomial branches forT <= 20 CandT > 20 C, and depth factorsP2,P3. Valid roughly 0.4 - 1000 kHz, -1.8 to 30 C, S 30-35 psu, depth 0 - 6 km.- Parameters:
- frequency_hzfloat or array_like
Frequency (Hz). Vectorised.
- temperature_c, salinity_psu, depth_m, phfloat
Water conditions (degrees C, practical salinity units, metres, pH).
- sound_velocity_msfloat, optional
Sound speed (m/s) in the relaxation-amplitude terms. Default: the paper’s own
c = 1412 + 3.21 T + 1.19 S + 0.0167 D.
- Returns:
- float or np.ndarray
Absorption coefficient in dB per metre (the paper’s dB/km / 1000), a float for scalar input.
References
[1]R. E. Francois and G. R. Garrison, “Sound absorption based on ocean measurements: Part I: Pure water and magnesium sulfate contributions,” J. Acoust. Soc. Am. 72(3), 896-907 (1982) (pure water and magnesium sulfate terms).
[2]R. E. Francois and G. R. Garrison, “Sound absorption based on ocean measurements: Part II: Boric acid contribution and equation for total absorption,” J. Acoust. Soc. Am. 72(6), 1879-1890 (1982) (boric acid term and the equation for total absorption).
The defaults are “typical coastal” values (10 C, 35 psu, 10 m, pH 8), not a measurement: callers with real conditions should pass their own.
- sidescanpy.processing.radiometric.center_frequency_hz(ds)#
Acoustic center frequency of a channel (Hz), for frequency-dependent physics.
The midpoint of
freq_hz_startandfreq_hz_endfor a chirp (a 1495-1605 kHz sweep is centered at 1550 kHz);freq_hz_startwhen the end is missing, non-positive or equal to the start. The channel label (hz_<freq_hz_start>) is the start frequency and is not affected.
- sidescanpy.processing.radiometric.absorption_for_frequency(frequency_hz, **env)#
Default absorption coefficient (dB/m, one-way) for a sonar frequency.
Thin wrapper over
francois_garrison_absorption_db_per_m();env(temperature_c, salinity_psu, depth_m, ph, sound_velocity_ms) is passed through, defaulting to its typical-coastal-water values.
- sidescanpy.processing.radiometric.tvg_gain_db(range_m, params)#
Per-range TVG gain (dB): spreading loss + absorption loss.
spreading_loss (dB) = spreading_loss_db * log10(range_m)absorption_loss (dB) = 2 * absorption_db_per_m * range_m(factor of 2 for the two-way path)Shared by
apply_tvg()(xarray survey pipeline) andapply_tvg_to_amplitude()(plain-array path, e.g. target crop windows) so both apply the identical formula rather than maintaining it twice. Caller is responsible for clippingrange_mtoparams.min_range_mfirst (as both callers here do) — this function doesn’t clip on its own since it has no opinion on what a caller’s minimum range should be.
- sidescanpy.processing.radiometric.apply_tvg_to_amplitude(amplitude, meters_per_sample, *, params=None, freq_hz=None, sample_idx=None)#
Apply TVG correction to a plain linear-amplitude array — no xarray
Datasetrequired.For use on data that isn’t a full survey channel (e.g. a target’s raw ping-sample window decoded straight from its catalog SQLite via
sidescanpy.io.jsf.targets.decode_target_window), where constructing a fullSideScanSurvey/Datasetjust to TVG-correct a small crop isn’t warranted. Same formula and defaults asapply_tvg()— see that function for the physical justification.- Parameters:
- amplitudenp.ndarray
Linear amplitude, sample axis last (e.g.
(n_pings, n_samples)or(n_samples,)).- meters_per_samplefloat
Range covered by one sample (two-way), e.g. a target catalog’s own
PingMetersPerSample— the per-sample analog ofapply_tvg()’ssound_velocity / (2 * sample_rate).- paramsTVGParams, optional
If
None, built from defaults withabsorption_db_per_mderived fromfreq_hz(required in that case, matchingapply_tvg()’s own default-resolution pattern).- freq_hzfloat, optional
Used only when
paramsisNone(or has noabsorption_db_per_mset), to derive a default viaabsorption_for_frequency().- sample_idxnp.ndarray, optional
Absolute sample index of each position on the last axis. Defaults to
arange(n_samples)(the array starts at the ping’s first recorded sample); pass it when leading samples were cropped off.
- Returns:
- np.ndarray
TVG-corrected values in dB, same shape as
amplitude.
- sidescanpy.processing.radiometric.apply_tvg(ds, params=None, *, spreading_loss_db=None, absorption_db_per_m=None, min_range_m=None, sound_velocity_ms=None, temperature_c=None, salinity_psu=None, depth_m=None, ph=None)#
Apply time-varying gain (TVG) correction to
backscatter_intensity.Compensates for geometric spreading and acoustic absorption losses. Converts linear amplitude to dB (
20*log10(clip(x, 1e-6)), seesidescanpy.core.units), adds the TVG gain for each sample’s slant range, and returns corrected dB values in place of the input.Input
backscatter_intensityis assumed to be linear amplitude; the library does not check this (what units the input data is in is the caller’s knowledge). Exact-zero amplitude maps to the -120 dB floor and gets no gain, so it stays exactly atFLOOR_DBafter TVG andmask_floor_clip()still recognizes it (uncorrected_backscatter()maps it back to 0).The step is recorded in
processing_historyas"tvg"with the parameters actually used (defaults resolved), which is whatsidescanpy.processing.inversion.invert()reads to undo it.- Parameters:
- dsxr.Dataset or SideScanSurvey
Frequency channel dataset; needs
backscatter_intensity,sound_vel_msand thefreq_hz_start/sample_rate_hzattrs. ASideScanSurveyis corrected frequency by frequency with the same arguments; a new survey is returned and the input is not modified.- paramsTVGParams, optional
If None, defaults with
absorption_db_per_mderived fromfreq_hz_startandsound_velocity_msfrom the mean per-pingsound_vel_ms. The caller’s object is never modified.- spreading_loss_db, absorption_db_per_m, min_range_m, sound_velocity_msfloat, optional
Override the matching
TVGParamsfield (ofparams, or of the defaults), soapply_tvg(ds, spreading_loss_db=40)needs noTVGParams. SeeTVGParamsfor their meaning.- temperature_c, salinity_psu, depth_m, phfloat, optional
Francois-Garrison water conditions, used only when
absorption_db_per_mis not set; override the matching fields like the arguments above.
- Returns:
- xr.Dataset or SideScanSurvey
Same type as
ds: a new dataset with TVG-corrected dBbackscatter_intensityand the"tvg"history record appended.
- Raises:
- ValueError
If TVG has already been applied (prevents double application).
- sidescanpy.processing.radiometric.resolve_tvg_params(ds, params)#
Fill a TVGParams’ unset fields from
ds(new object; input untouched).absorption_db_per_m=None-> Francois-Garrison absorption at the channel’s center frequency (center_frequency_hz());sound_velocity_ms=None-> mean of the per-pingsound_vel_ms(NaN or non-positive mean falls back to 1500 m/s).
- sidescanpy.processing.radiometric.tvg_gain_for_dataset(ds, params)#
Per-sample TVG gain (dB, float64, shape
(n_samples,)) fords.Slant range of each sample is
sample_index * c / (2 * fs)(absolute sample index, so cropped datasets get the same gain per sample), clipped toparams.min_range_m.paramsmust be fully resolved (seeresolve_tvg_params()).
- sidescanpy.processing.radiometric.tvg_params_record(params)#
History-record params for a resolved TVGParams.
- sidescanpy.processing.radiometric.tvg_params_from_record(record_params)#
TVGParams from a
"tvg"history record (current or legacy key names).