feat: underwater seabed reflection, ambient noise and ship-traffic source levels (#149)
* feat: underwater seabed reflection, ambient noise and ship-traffic source levels
Second half of the closed-form underwater propagation sub-phase (P1), building
on the transmission-loss/sound-speed/sonar core:
- seabed_reflection: fluid-fluid Rayleigh reflection coefficient (Medwin & Clay),
critical grazing angle arccos(c1/c2) and bottom loss BL = -20 lg|R|.
- ocean_ambient_noise: Wenz-framework spectrum levels, energy-summing wind noise
(rule of fives) and Mellen thermal noise, with an optional caller-supplied
shipping term.
- ship_traffic_noise: predicted ship source-level spectra from vessel class,
speed and length via JOMOPANS-ECHO (MacGillivray & de Jong 2021, default,
validated to the authors' File S1 reference calculator within 0.01 dB), plus
RANDI 3.1 and Wales & Heitmeyer (2002).
Each module exposes a frozen result dataclass with .plot(), a conformance check
with an independent oracle, one figure in four language/theme variants, and full
English docs plus EN/ES site guides. Conformance 179/179; full suite 2450 passed.
* refactor: address PR review feedback on underwater modules
- seabed_reflection: guard the c1 == c2 & grazing = 0 singular limit
(0/0 -> normal-incidence coefficient) so |R| never returns NaN; add test.
- ocean_ambient_noise: allow a calm sea (wind_speed_knots = 0) to return -inf
(zero energy contribution) rather than raising; add test.
- Migrate the three modules' input validation to the shared _validation helpers
(new require_positive_array enforces finite, positive, 1-D); removes
duplicated hand-rolled checks.
- Clarify units: source spectral-density is "dB re 1 µPa²/Hz at 1 m" (not the
ambiguous "/Hz m") in the plot label, figure, api-reference and docs; scope the
plane-wave reference note so it no longer claims to cover source levels; note
the wind-noise validity range (500 Hz-5 kHz) for the wide-range Wenz example.
- Label the wind/thermal component curves in the ambient-noise figure.
Kept the conformance thermal oracle's hardcoded physical constants (Boltzmann,
1 µPa) for clean-room independence from the module under test. Conformance
179/179; full suite 2452 passed.
* fix: SonarCloud float-equality and long-title docs-deploy failures
- seabed_reflection / ocean_ambient_noise: avoid float equality checks
(SonarCloud S1244). Detect the seabed 0/0 singular limit via np.isnan of the
result instead of comparing the denominator to 0.0; gate the calm-sea branch
with require_non_negative + a relational check.
- Shorten four site-guide titles so the rendered "<title> | phonometry" stays
within the 70-char html-validate limit (long-title), which was blocking the
docs deployment: the two underwater-propagation guides (EN/ES, lengthened in
this PR) and the two pre-existing wind-turbine-noise guides (EN/ES). The full
detail stays in each guide's description.
docs: restructure into docs/, slim PyPI-safe README, bilingual Starlight site with theme-aware figures (#61)
* docs: split README content into docs/ topic pages
11 topic pages under docs/ (guides + reference), including the new
levels/spectrogram documentation and a Why PyOctaveBand page distilled
from the issue #38 IEC 61672-1 compliance analysis.
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* docs: add auto-generated figures for levels/spectrogram/zero-phase docs
Four new generate_* functions (spectrogram example, LN levels over the
Fast envelope, zero-phase group-delay comparison, A-weighting HF accuracy),
a 'make graphs' target, and a CONTRIBUTING section formalizing that every
docs image must come from generate_graphs.py.
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* docs: slim README to hook + quick start with absolute links (PyPI-safe)
The README is the PyPI landing page and PyPI does not resolve relative
links, so every link (docs pages, website, CONTRIBUTING, LICENSE) is now
absolute. The Documentation section sits right after installation with a
per-page table. Full content lives in docs/ and on the website.
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* docs(site): scaffold Astro/Starlight with en/es i18n, KaTeX, and English content
Starlight site under site/ with base /PyOctaveBand for GitHub Pages,
locale-aware sidebar, KaTeX math rendering, and the 11 English pages
ported from docs/ (links rewritten to site routes, GitHub alerts
converted to Starlight asides).
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* docs: generate all figures in light and dark themes
generate_graphs.py now renders every figure twice (light + _dark suffix)
via a theme switch (dark_background style, foreground color swap).
- docs/ (GitHub): <picture> with prefers-color-scheme sources.
- site/ (Starlight): dual img.light-only/.dark-only following the theme
toggle via [data-theme] CSS.
- README keeps plain light images (PyPI sanitizes <picture>).
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* docs(site): add complete Spanish translation
Full natural translation of all 12 pages (landing, getting started,
7 guides, 3 reference pages) with theme-aware images and es/ routes.
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* ci: deploy Starlight docs site to GitHub Pages
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* ci: bump actions to latest, dependabot cooldown 0, refresh all dependencies
- Workflows: checkout v7; docs.yml to setup-node v6, pnpm/action-setup v6,
upload-pages-artifact v5, deploy-pages v5 (pnpm stays 11.x).
- Dependabot: cooldown 0 days on all ecosystems, stop ignoring pip
minor/patch updates (grouped), add npm ecosystem for site/.
- Python deps to latest: scipy 1.18, matplotlib 3.11, numba 0.66,
mypy 2.1, ruff 0.15.20, pytest 9.1. numpy capped <2.5 in the dev/CI
environment because numba 0.66 requires it (users without the [perf]
extra are unaffected).
- Site deps to latest: astro 7.0.6, starlight 0.41.3, katex 0.17, sharp 0.35.
- Repo configured via API: homepage -> docs site URL, GitHub Pages enabled
with build_type=workflow (no manual post-merge step needed anymore).
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf
* docs: address review feedback (dependabot schema, snippets, workflow hardening)
- dependabot: remove cooldown blocks (default-days minimum is 1; no
cooldown IS the zero-delay behavior).
- docs.yml: scope pages/id-token permissions to the deploy job only and
set persist-credentials: false on checkout (third-party install scripts
run in the build job).
- Snippets: add missing numpy import, define the bank in the zero-phase
example, linspace endpoint=False (docs + site EN/ES + README).
- tsconfig: exclude node_modules and .astro explicitly.
- A-weighting +2.00 constant kept: verified numerically that
20*log10(RA(1000)) = -1.99986, so +2.00 yields 0.0001 dB at 1 kHz;
the suggested +2.06 would introduce a +0.06 dB offset.
Claude-Session: https://claude.ai/code/session_013kkVt3nxi9svp1an28uHxf