sidescanpy.viz.tvg#
Plots for TVG and range-decay analysis (sidescanpy.analysis.tvg,
sidescanpy.analysis.tvg_fit).
Each function draws into an optional ax, returns (fig, ax) and
never saves a file. plot_tvg_fit() draws a
fit_tvg_models() result and, below it, a
check_tvg() result. The range-decay plots
(plot_candidate_fit(), plot_range_decay_fit(),
plot_range_decay_residuals(), set_fit_window_limits()) are
deprecated with fit_range_decay() and will be
removed in 0.12.
2026 rshomberg@uri.edu
Contents
Line plot of mean backscatter vs. range, with an optional shaded percentile band. |
|
Plot |
|
"Spaghetti plot": overlay |
|
Overlay |
|
Overlay |
|
Overlay one |
|
Observed loss-vs-range curve with every candidate model overlaid (one |
|
Set robust axis limits on a decay-fit axis from the fit window. |
|
Residual (model - observed) vs. range within each candidate's fit window. |
|
One column: the TVG model fit and, below it, the check after TVG. |
- sidescanpy.viz.tvg.plot_mean_vs_range(binned, ax=None, *, show_band=True, band=('p10', 'p90'), label=None, color=None, linewidth=1.5, zorder=3.0, title=None)#
Line plot of mean backscatter vs. range, with an optional shaded percentile band.
Call twice on the same
ax(raw vs. corrected) for a direct before/after overlay.- Parameters:
- binnedpd.DataFrame
Output of
backscatter_vs_range(orbackscatter_vs_sample— any DataFrame with a “mean” column and matching min/max range columns).- show_bandbool, default True
Shade the region between
band’s two percentile columns.- band(str, str), default (“p10”, “p90”)
- linewidthfloat, default 1.5
Matplotlib’s own default is 1.5, so this default is a no-op for existing callers – pass a larger value (e.g. when overlaying on top of
plot_per_ping_profiles’ dense low-alpha spaghetti, which can otherwise visually bury a thin mean line even with a distinct color) to make the mean line read clearly on top.- zorderfloat, default 3.0
Matplotlib draws in call order by default (zorder 1, 2, 3, … as each
ax.plot/fill_betweencall happens) – 3.0 is chosen to sit above a typical single priorplot_per_ping_profilescall (which doesn’t set its own zorder, so defaults to 1) even if draw order alone wouldn’t guarantee it. Raise further if overlaying on top of more layers.
- sidescanpy.viz.tvg.plot_tvg_gain_curve(ds, ax=None, *, range_var='ground_range_m', title='TVG gain vs. range')#
Plot
tvg_gain_dbvs. range fromcompare_before_after’s output.Shows how much gain TVG actually added at each range — useful alongside
plot_mean_vs_rangeto compare against the observed residual’s shape.
- sidescanpy.viz.tvg.plot_per_ping_profiles(ds, value_var, range_var='ground_range_m', n_pings=50, ax=None, alpha=0.08, *, channel_idx=0, seed=0, title=None, x_sign=1.0)#
“Spaghetti plot”: overlay
n_pingsindividual ping profiles (value_varvs.range_var, one channel) at low alpha.Visual density shows ping-to-ping consistency/variance directly — complementary to
plot_mean_vs_range’s percentile band, which summarizes spread but averages away periodic per-ping artifacts.- Parameters:
- channel_idxint, default 0
Which channel to plot (0 = port, 1 = starboard per this schema’s convention) — call twice with
channel_idx=0andchannel_idx=1on separate axes for a side-by-side port/ starboard comparison. Previously hardcoded to 0; the default preserves that exact behavior for existing callers.- x_signfloat, default 1.0
Multiplies every plotted
range_varvalue before drawing — pass-1.0(typically forchannel_idx=0/port) to mirror one channel onto the negative side of a shared axis, so calling this twice (once per channel, opposite signs) on the sameaxdraws one continuous full-swath profile instead of two independent one-sided ones.range_varitself (e.g.slant_range_m) is always unsigned magnitude — this is a display-only sign flip, not a different range computation. 1.0 is a no-op, preserving every existing caller’s output exactly.
- sidescanpy.viz.tvg.plot_multi_frequency_comparison(binned_by_freq, ax=None, *, title='Mean backscatter vs. range, by frequency')#
Overlay
backscatter_vs_rangecurves for multiple frequencies, color-coded by frequency.If multiple frequencies (different
absorption_for_frequency()coefficients) show a similarly-shaped residual, that’s evidence against “just retune absorption” (candidate 1).
- sidescanpy.viz.tvg.plot_altitude_bin_comparison(binned_by_altitude, ax=None, *, title='Mean backscatter vs. range, by altitude bin')#
Overlay
backscatter_vs_rangecurves for multiple altitude bins (frombin_by_altitude), color-coded by bin.If the residual’s shape changes with altitude (different near-nadir geometry), that’s evidence for a missing angular correction (candidate 2); roughly altitude-invariant leans toward candidate 1.
- sidescanpy.viz.tvg.plot_candidate_fit(fit, observed_range_m, observed_loss_db, ax=None, *, color=None, show_observed=True, title=None, use_fixed_scale=True, model_n_points=200)#
Overlay one
analysis.tvg.fit_tvg_curve()candidate’s fitted model against the observed raw-loss curve it was fit against.Call once per candidate on the same
ax(result.candidatesfromTVGCandidateFitResult) —show_observed=Trueon the first call only,Falseon the rest, so the observed curve is drawn once and each candidate’s fitted line overlays it. Mirrorsplot_mean_vs_range’s existing “call twice for before/after” idiom, generalized to “call once per candidate.”- Parameters:
- fitTVGCandidateFit
One candidate from
TVGCandidateFitResult.candidates(or.best).- observed_range_m, observed_loss_dbnp.ndarray
TVGCandidateFitResult.observed_range_m/.observed_loss_db— the same arrays for every candidate fit from onefit_tvg_curve()call, so passing a different candidate on repeated calls only changes the overlaid line.- show_observedbool, default True
Plot the observed curve itself. Set
Falseon repeat calls against the sameaxto avoid redrawing it once per candidate.- use_fixed_scalebool, default True
Draw the literal, uncalibrated model (scale pinned to 1.0) – matches what
fit_range_decayactually ranks candidates by (2026-08-24 change; seeTVGCandidateFit’s docstring).Falsedraws the old free-fit-scaled line instead, for diagnostic comparison against the fixed-scale one – e.g. to see by how much a candidate needed to cheat to look competitive.- model_n_pointsint, default 200
The model line is drawn over its own dense, evenly-spaced range array from 0 to
observed_range_m’s max — not just at the bin centersobserved_range_mhappens to have — so the curve’s shape reads smoothly all the way back to near-zero range for reference, including where no observed data exists (e.g. inside the excluded nadir gap). Purely a display domain; the fit itself was never evaluated there and this draws no new conclusion about that span.- .. deprecated:: 0.10
Removed in 0.12. Use
plot_tvg_fit()withfit_tvg_models().
- sidescanpy.viz.tvg.plot_range_decay_fit(result, ax=None)#
Observed loss-vs-range curve with every candidate model overlaid (one
plot_candidate_fit()call per candidate on a shared axis).Deprecated since version 0.10: Removed in 0.12. Use
plot_tvg_fit()withfit_tvg_models().
- sidescanpy.viz.tvg.set_fit_window_limits(ax, result, *, x_pad_fraction=0.12, y_pad_fraction=0.25, y_pad_min_db=1.0, x_pad_min_m=1.0)#
Set robust axis limits on a decay-fit axis from the fit window.
y-limits come from the observed values inside the fit window (
result.r_fit_min_m..``r_fit_max_m``) only, so an out-of-window outlier bin cannot compress the fit curves. x-limits deliberately span 0 to the far edge (padded), not the fit window, so a misplaced reference or window stays visible. With a shared x-axis, call this on one panel only. No-op if there is no fit or no recorded window.Deprecated since version 0.10: Removed in 0.12. Use
plot_tvg_fit()withfit_tvg_models().
- sidescanpy.viz.tvg.plot_range_decay_residuals(result, *, use_fixed_scale=True, ax=None, relative_x=True)#
Residual (model - observed) vs. range within each candidate’s fit window.
use_fixed_scale=True(default) draws the scale-1.0 residual and labels it withfixed_scale_rmse_db, the number candidates are ranked by;Falsedraws the free-fit-scaled residual for comparison. The reference uses the nearest bin tor_ref_m, matchingfit_tvg_curve’s own RMSE computation exactly.relative_x=Trueplots distance from each candidate’sr_ref_m; passFalsefor absolute range (to stack underplot_candidate_fit()on a shared x-axis).Deprecated since version 0.10: Removed in 0.12. Use
plot_tvg_fit()withfit_tvg_models().
- sidescanpy.viz.tvg.plot_tvg_fit(fit, check=None, ax=None, ax_check=None, *, title=None)#
One column: the TVG model fit and, below it, the check after TVG.
Fit panel: the median level against range (black), the fit window (shaded), the 20, 30 and 40 log R models with absorption (thin), the best model (thick; dashed when it has no absorption), each labelled with its RMS in the window. Check panel (when
checkis given): the median level after the TVG, relative to its mean in the window, with a zero line, limits of ±5 dB, and the spread and trend written in the corner. Flat means the model holds.- Parameters:
- fitTVGFitResult
From
fit_tvg_models().- checkTVGCheckResult, optional
From
check_tvg(), normally onfit.tvg_paramsandfit.window_m.- ax, ax_checkAxes, optional
Where to draw. Without
axa new figure is made, with a second row for the check whencheckis given. Withaxand acheck, passax_checktoo.- titlestr, optional
Title of the fit panel.
- Returns:
- (Figure, (Axes, Axes or None))
The figure, the fit axes and the check axes (
Nonewithout a check).