Documentation

How SAS works

Aperture synthesis from first principles, and the sizing rules that follow from it.

A working reference for synthetic aperture sonar geometry, array sizing, and the design rules the simulator's presets follow. Aimed at someone who knows time-domain radar/sonar processing but wants a quick handle on what each SonarCfg field controls and why the canonical values are what they are.

1. Core formulas

Quantity Formula Notes
Wavelength λ = sos / fc sos = 1500 m/s typical
Range resolution δ_r = sos / (2 · BW) independent of range
1-way 3 dB beamwidth (uniform aperture) θ_3dB ≈ 0.886 · λ / d radians, full angle
1-way first-null half-angle sin θ_null = λ / d "FN" = first null
Synthetic-aperture length on a target L_synth = R · θ_tx_bw ≈ R · λ / d_tx_az far-field
SAS azimuth resolution δ_az = λ · R / (2 · L_synth) = d_tx_az / 2 independent of R and λ

2. The two design rules

These two rules together determine every meaningful element dimension for a multi-channel SAS array. The simulator's preset registry (appcore/sas_presets.py) enforces both.

2.1 Azimuth resolution: δ_az = d_tx_az / 2

The famous SAS result. Wider TX → finer resolution, because a wider TX is illuminated over a longer along-track span and thus contributes to a longer synthetic aperture. Resolution is independent of range and wavelength.

To set a target resolution (e.g. 2 cm), pick d_tx_az = 0.04 m and stop. The synthetic aperture forms over R · λ / d_tx_az of platform travel at slant range R.

2.2 Azimuth ghost suppression: d_tx_az = channel_spacing

A multi-channel SAS samples along-track at the phase-center pitch, pc_spacing = channel_spacing / 2. Strict Nyquist would require pc_spacing ≤ λ/4, but real arrays have pc_spacing ≫ λ/4. The TX directivity is what saves you: it acts as the anti-alias filter.

The condition for no visible ghosts is:

azimuth alias angle:   sin θ_amb  = λ / channel_spacing
TX first-null angle:   sin θ_null = λ / d_tx_az

         require:      d_tx_az  ≥  channel_spacing

Setting d_tx_az = channel_spacing puts the TX first-null exactly on the alias angle → alias falls in the null → no ghost. Smaller d_tx_az opens the TX beam past the alias and produces visible ghost copies of every target at ±arcsin(λ / channel_spacing) from broadside.

The two rules collapse to: d_tx_az = d_rx_az = channel_spacing (RX elements tile contiguously). Resolution is then channel_spacing / 2.

3. Elevation aperture: keep small

The vertical (elevation) dimension does not benefit from synthetic-aperture processing: the platform doesn't traverse vertically. The simulator uses 5 mm for tx/rx_element_elevation across every preset because:

  • At λ ≥ 5 mm (300 kHz or lower), λ / d_el ≥ 1 → the sinc pattern has no zero at any real angle → no elevation null bands in the image.
  • The 3 dB beamwidth is wide (≈ 51° at 300 kHz, even wider at 100 kHz) so the entire imaged ground range is illuminated.

If you make d_el larger, you get a tighter vertical beam but the first null arcsin(λ / d_el) falls inside the imaged range and stamps a dark horizontal band in the image. Don't change it unless you are deliberately modelling a different vertical aperture.

4. PRF and range ambiguity

Two-way travel time to the far edge plus the pulse length sets the PRF ceiling:

PRF_max = sos / (2 · (R_far + pulse_length · sos))

The wizard ships PRF = 0.95 · PRF_max (5 % margin). The factor matches appcore/validation/rules._max_range (mirrored by simulator/design_rules.py for the CLI/cloud/studio paths), which is what gates the Run-Simulation button: go any higher and rule R2 fires.

4.1 Platform speed: cap it at what the vehicle can do

The along-track sampling rule ties speed to PRF: the platform may advance at most half a receive array per ping, speed ≤ ping_advance · PRF with ping_advance = (n_ch − overlap) · channel_spacing / 2 (0.56 m for HISAS). The wizard derives the fastest legal speed at the range-limited PRF, which runs away at short range: 50 m of range gives 11.5 Hz and 6.4 m/s (12.5 kn) for HISAS, twice a HUGIN's 3.1 m/s maximum. Real operating points (Kongsberg HISAS 1030 datasheet; Hansen, FFI): 200 m per side at 2 m/s (4 kn), 275 m at 1.5 m/s, altitude ≈ range / 10, about 2 to 2.7 km²/h. Both pairs fall out of the sampling rule with a 1.2 m array, so the physics is right and only the speed needs a ceiling.

appcore.sas_presets.SAS_SYSTEM_PLATFORM_SPEED holds that ceiling (HISAS: 2.0 m/s) and derive_sas_geometry(speed_max=...) applies it: when the derived speed exceeds it, speed is held at the cap and the PRF is lowered to speed / ping_advance, so the ping advance, the ping count and the image are unchanged and only the time base becomes realistic. At 200 m the derivation already lands on 1.9 m/s, so the cap only bites below about 190 m of range. The HISAS default geometry is 200 m × 80 m, the datasheet operating point.

5. Canonical preset table

What's in appcore/sas_presets.py today:

System fc (kHz) BW (kHz) n_ch ch_spc (mm) d_tx_az / d_rx_az (mm) d_tx_el / d_rx_el (mm) δ_az (mm)
MUSCLE 300 kHz 300 60 36 30 30 / 30 5 / 5 15
HISAS 1030 100 kHz 100 30 32 40 40 / 40 5 / 5 20
Kraken MINSAS 120 (≈337 kHz) 337 40 32 37.5 37.5 / 37.5 5 / 5 18.75
Klein 5900 600 kHz 600 40 28 65 65 / 65 5 / 5 32.5

Every row satisfies d_tx_az = channel_spacing (rule 2.2 → no ghosts) and δ_az = channel_spacing / 2 (rule 2.1).

6. Failure modes if the rules are violated

Symptom Likely cause Fix
Bright ghost copies of each target offset ±X m in along-track d_tx_az < channel_spacing → alias falls inside TX beam Set d_tx_az = channel_spacing
Coarser resolution than expected (azimuth blur) TX element narrower or wider than rule 2.1 dictates Set d_tx_az = 2 · δ_az_target
Horizontal dark bands across the image d_el too large → elevation first-null inside imaged range Use d_el = 5 mm for the canonical no-null configuration
Run-Simulation button greyed out / R2 fires PRF over the range-unambiguous budget Lower PRF to ≤ 0.95 · sos / (2 · (R_far + τ · sos))
Image is mostly empty with bright targets squeezed at top/bottom range_offset_m mismatch for SAR scenes sim.range_offset_m = slant_min; BF reads it back as t0 = 2 · range_offset / sos

7. Where this is encoded

  • Element sizes per system: appcore/sas_presets.py (preset registry)
  • Aperture-footprint derivation: appcore/scene_defaults.py::derive_sas_geometry (uses 0.886·λ/channel_spacing for the synthetic-aperture footprint at the far range)
  • Beam-pattern interactive viewer: Tools → Beam Pattern Viewer (gui_imgui/dialogs/beam_pattern.py); sweep element sizes live, plots 1-way TX, 1-way RX, and 2-way TX·RX in azimuth and elevation, with metrics (3 dB BW, first-null) per element

8. References

  • Hayes & Gough, Synthetic Aperture Sonar: A Review of Current Status, IEEE J. Ocean. Eng. 34(3), 2009: the textbook treatment of multi-channel SAS Nyquist + resolution.
  • Bellettini & Pinto, Design and Experimental Results of a 300-kHz SAS Optimized for Shallow-Water Operations, IEEE JOE 34(3), 2009: MUSCLE/CMRE design rationale.
  • Brown et al., PoSSM, POMA 36, 2019: the simulator's point-scattering model.