|
FhSim
3.1.0
Marine systems simulation
|
Single-beam multi-frequency echosounder: an altimeter, sa and the Integrated biomass on its ports, and the echogram in a file.
Collaboration diagram for Echosounder:The instrument has three parts.
The altimeter is on the output ports. One ray is cast along the beam with marenv::EnvironmentFacade::ComputeRayIntersection to the first seabed crossing along the beam (owner ruling R132), the slant range is perturbed with a bias and two noise terms, and the result is published as SeafloorDistance, the inferred bottom depth SeafloorDepth, the height above the bottom Altitude, a Valid flag and the PingTime the reading belongs to. That is what a dive controller consumes.
The Ping columns, one per frequency, are formed from the Species Concentration fields along the beam (feature echosounder-beam; the sensor model is the echosounder sensor repository, doc/fhsim-port.md and its ADR 0006). Their Detections are on the ports: Sa<f>, the area backscattering coefficient sa per frequency, and IntegratedBiomass_<field>, the true kg/m^2 of each field along the beam, over the same Integration window.
The echogram is a file, because it does not fit a port: one ping is a row of up to a few hundred Sv values per frequency. See SensorEchosounder_file below.
Auv already publishes a true Altitude, computed from GetSeaDepth at the vehicle's own position. This class publishes a measured Altitude with the same name, the same sign and the same unit, so putting the sensor in the control loop is a one line rewire in XML and nothing downstream changes:
The two differ by the range bias, the range noise, the ping interval and, over sloping ground, by the beam geometry: this class reports the vertical projection of the slant range, \(R\cos\theta\), which is the true vertical clearance only where the bottom is locally flat. On a slope it reads the clearance to the point the beam actually hits, which is what a real altimeter does too. \(\theta\) is the beam's angle from NED down, so it includes the carrier's pitch and roll. The two ports are interchangeable only for a nadir beam on a level vehicle, or over flat ground; a controller that needs the true vertical clearance should use Auv.Altitude.
The beam axis is the SensorFrame MountDirection, rotated into NED by the carrier attitude and normalised. It is not defaulted for you: SensorFrame defaults MountDirection to the body x axis, which for a nadir sounder is wrong by 90 degrees, so a sounder configuration must set MountDirection = "0, 0, 1". One warning is given at FinalSetup if the mounted beam does not point downwards in the body frame at all.
There is one beam. An upward looking sounder is a second instance with MountDirection = "0, 0, -1"; MaxRangeUp of earlier versions is refused.
38, 120 and 200 kHz by default; the Parameter library has data at 18, 38, 70, 120 and 200 kHz, and a frequency it has no data at is an error. 38 kHz is the de facto fish biomass and bottom detection standard; 120 kHz separates a mackerel layer from a gadoid one; 200 kHz is where a 2.8 mm Calanus becomes visible.
NoiseSeed = 0 is the perfect instrument: no range bias, no range noise, no acoustic noise floor and no speckle. The echogram is then the pure forward model and can be compared against a closed form, which is exactly what the unit tests do. Any other seed enables all four. See SensorNoise.
As with the other AUV sensors there are deliberately no TimeDelay, LossProbability or OutlierProbability parameters. Chain Altitude into a fhsim_base Signal/Samplifier if that is wanted:
The beam is binned at BinSize, bin \(k\) covering the slant range \([k\,\Delta, (k+1)\Delta)\) and evaluated at its centre. A beam ending at range \(R\), the seafloor or MaxRangeDown, has \(\lceil R/\Delta \rceil\) bins; its last bin may be partial and is evaluated at the centre of its wet part, at least RayTolerance short of the seabed hit (FISH-0064).
Every species field is sampled once per bin and beam direction, not once per frequency. BeamSamples = 1 (the default) samples the beam axis only. A larger value adds a ring of BeamSamples - 1 directions at half the widest BeamWidth off the axis, and each frequency weights the samples by its own two-way beam pattern, \(B = (2J_1(u)/u)^4\) with \(u = 1.6163\sin\theta / \sin(\texttt{BeamWidth}/2)\): one on the axis and a quarter on the ring of a beam of that width. That is the fast approximation the sensor model allows (its ADR 0006), in place of its adaptive sampler.
Per frequency \(f\), with \(\rho_i\) the beam-weighted kg/m^3 of field \(i\):
\[ s_v = \sum_i \rho_i\,\langle\sigma_{bs}\rangle_i(f) / \langle W\rangle_i \]
where \(\langle\sigma_{bs}\rangle/\langle W\rangle\), sigma_bs per kg, is averaged over the species' normal length spread around the field's LengthM (ParameterLibrary). In the bottom bin a bottom echo \(10^{B(f)/10} / \Delta\) is added, the surface backscattering strength BottomBackscatterDb spread over the bin that contains it.
With a non-zero seed a range dependent noise floor is added to every bin,
\[ s_{noise} = 10^{(N(f) + 20\log_{10} r + 2\alpha(f) r/1000)/10}, \]
and the bin is multiplied by a Gamma draw of shape IndependentSamplesPerBin and unit mean: Rayleigh speckle averaged over that many independent samples, about 1.5 dB at the default of 8, multiplicative in linear units. Finally SvBiasDb is applied.
The absorption \(\alpha(f)\) is the environment's (EnvironmentProvider::GetAcousticAbsorption, Francois and Garrison) at the midpoint of the beam, once per ping, unless AbsorptionDbPerKm overrides it.
\(S_v\) is by definition a TVG compensated quantity: a real sounder has already multiplied the received echo by \(r^2 10^{2\alpha r/1000}\) before reporting \(S_v\), so applying absorption to the modelled \(S_v\) here would double count it. The noise floor, on the other hand, is referred to the received signal, so it grows with range through exactly that same TVG.
The bottom Detection is the first bin, evaluated at least MinRange out, whose Sv at the first frequency reaches BottomThresholdDb (-30 dB). The Integration window runs from MinRange to the start of that bin less BottomOffset, or to the end of the beam when no bottom is detected; there is one window per ping. Over it:
Sa<f> is sa, the integral of the linear sv of frequency f, m^2/m^2. NASC is \(4\pi\,1852^2\,s_a\). Noise is in it when the seed is non-zero, as in a real one.IntegratedBiomass_<field> is the integral of the field's kg/m^3 sampled on the beam axis, kg/m^2: the truth sa is compared against, not a Detection. With the noise off, Sa<f> is the sum over the fields of their Integrated biomass times sigma_bs per kg.Both are held between pings. IntegratedBiomass_<field> exists for each name in SpeciesFieldNames; with AutoDiscoverSpecies the fields are only known after the ports are declared, so there are none.
A Species Concentration field enters the acoustic model when it is named in SpeciesFieldNames (default none), or every one with AutoDiscoverSpecies. It must carry ScalarField.<Name>.Species, a key of the Parameter library, and the property LengthM, the mean animal length in metres. A named field that is missing, names an unknown species, lacks LengthM, or whose length spread leaves a species' tabulated lengths is a fatal error. The properties MassKg, TargetStrengthRefDb, RefFreqkHz and TsSlopeDbPerOctave of earlier versions are fatal too, with a migration message. A discovered field that names no species is excluded with one warning: a plankton field that only the SilCam cares about must not stop the sounder.
The Parameter library is found as described for ParameterLibrary::CandidatePaths: ParameterLibrary relative to the input file, or the copy installed with fhsim_fishery.
Written only when WriteFile is non-zero. The ofstream is opened and the header written in FinalSetup, one row per frequency is appended in AcceptedStep, and the stream is flushed and closed in the destructor. Nothing is ever written in OdeFcn, which is const and is re-entered at rejected trial states.
Each row carries its own NumBins and exactly that many Sv values, so a row is as long as the beam is, rather than padded to a fixed width with NaN. FileName is kept relative** on purpose, so that an FMU host controls where the file lands.
StepMax, and well below PingPeriod. SampleClock warns once if a ping is taken more than a quarter of a period late.| Name | Width | Description @addoption{Pos, 3, The carrier body origin in NED, m. Held by the SensorFrame. } @addoption{Quater, 4, The carrier body attitude, scalar first [q0 q1 q2 q3]. Held by the SensorFrame. } |
|---|
| Name | Width | Description @addoption{SeafloorDistance, 1, The measured slant range along the beam to the seafloor, m. Held between pings. A transducer at the seafloor (within RayTolerance above it) or inside it reads a valid 0 (owner ruling R133). When no bottom was found this is MaxRangeDown, which is a lower bound rather than a measurement, and Valid is zero. } @addoption{SeafloorDepth, 1, The inferred depth of the seafloor below the mean water level at the point the beam hit, m. } @addoption{Altitude, 1, The measured height of the sensor above the seafloor, m, that is the vertical projection of the slant range. The measured twin of the true Auv.Altitude. } @addoption{Valid, 1, One when the last ping found a bottom within MaxRangeDown, zero otherwise. A dive controller must check this. } @addoption{PingTime, 1, The simulation time of the ping the held reading came from, s. } @addoption{Sa<f>, 1, sa at the frequency f in kHz as FrequencieskHz writes it (Sa38\, Sa120\, ...), over the Integration window, m^2/m^2. Held between pings. } |
|---|---|---|
| IntegratedBiomass_<field> | 1 | The kg/m^2 of the named species field along the beam axis over the Integration window. One per name in SpeciesFieldNames; none with AutoDiscoverSpecies. Held between pings. |
| Name | Width | Description @addoption{MountOffset, 3, The transducer position in the body frame, m. Default 0 0 0. } @addoption{MountDirection, 3, The beam axis in the body frame. Need not be unit. Default 1 0 0 from the shared SensorFrame, which is wrong for a sounder: set it to 0 0 1. } @addoption{PingPeriod, 1, The time between pings, s. Default 0.5. } @addoption{MaxRangeDown, 1, The longest slant range searched and binned along the beam, m. Default 150. Beyond it the bottom counts as not found. Must be positive and finite (FISH-0101). } @addoption{RayStepSize, 1, The search step of ComputeRayIntersection, m. Default 1. The search marches from the transducer and reports the first seabed crossing along the beam (owner ruling R132); a seabed feature that the beam enters and leaves within one step can be missed. Must be finite and at least RayTolerance (FISH-0100). } @addoption{RayTolerance, 1, The vertical convergence tolerance of ComputeRayIntersection, m. Default 0.01. Must be positive and finite. It bounds how well SeafloorDepth can agree with the bathymetry\, and a beam that ends on the bottom keeps its samples this far (vertically) above the hit (owner ruling R131). } @addoption{RangeBias, 1, A constant offset added to every measured slant range, m. Default 0.02. } @addoption{RangeNoiseStd, 1, The standard deviation of the absolute range noise, m. Default 0.02. } @addoption{RangeNoiseRelative, 1, The standard deviation of the range noise proportional to the range. Default 0.0005, that is 0.05 per cent. } |
|---|---|---|
| SpeciesFieldNames | n | The comma separated names of the Species Concentration fields entering the acoustic model, each with an IntegratedBiomass port. Default none. A name that is not registered is a fatal error. @addoption{AutoDiscoverSpecies, 1, When non-zero, every registered field tagged SpeciesConcentration is used and SpeciesFieldNames is ignored; there are then no IntegratedBiomass ports. Default 0. } @addoption{ParameterLibrary, 1, The echosounder Parameter library, relative to the input file. Default empty: the copy installed with fhsim_fishery (resources/acoustics/parameter_library.json). } @addoption{FrequencieskHz, n, The operating frequencies, kHz, each one the Parameter library has data at. Default 38, 120, 200. The per-frequency arrays below must all have this same length. } @addoption{BeamWidth, 1 or n, The full -3 dB beam width, degrees: one value for every frequency or one per frequency. Default 7. It shapes the beam pattern with BeamSamples above 1\, and the drawn cone, at the widest. } @addoption{BeamSamples, 1, The samples across the beam per range bin: the axis\, then a ring of BeamSamples - 1 at half the widest BeamWidth. Default 1, the axis only. } @addoption{AbsorptionDbPerKm, n, Overrides the absorption at each frequency, dB/km. Default: the environment's at the midpoint of the beam, once per ping. Applied to the noise floor only; see above. } @addoption{BottomBackscatterDb, n, The seafloor backscattering strength at each frequency, dB. Default -25, -23, -22. } @addoption{NoiseSvAt1mDb, n, The noise floor referred to one metre range at each frequency, dB. Default -125, -128, -130. } @addoption{SvBiasDb, n, A calibration offset added to the reported Sv at each frequency, dB. Default 0 at every frequency. } @addoption{BinSize, 1, The range bin size, m. Default 1. } @addoption{MinRange, 1, The start of the Integration window and the range before which no bottom is detected, m. Default 1. } @addoption{BottomOffset, 1, The Integration window ends this far short of the detected bottom, m. Default 0.5. } @addoption{BottomThresholdDb, 1, The Sv at the first frequency that detects the bottom, dB. Default -30. } @addoption{SvFloorDb, 1, The lowest Sv the file ever reports, dB. Default -140. } @addoption{IndependentSamplesPerBin, 1, The Gamma shape of the multiplicative speckle, that is the number of independent samples averaged in a bin. Default 8, about 1.5 dB. } @addoption{WriteFile, 1, One to write the echogram file, zero for the ports only. Default 1. } @addoption{FileName, 1, The echogram file, relative so that an FMU host controls placement. Default the object name followed by _echogram.csv. } |
| FlushEveryNPings | 1 | How often the echogram stream is flushed. Default 20. Zero never flushes before the destructor. |
| SvPrecision | 1 | The number of decimals written for each Sv value. Default 2. @addoption{NoiseSeed, 1, The random seed. Default 0, which means a perfect instrument: no range bias or noise, no acoustic noise floor and no speckle. } |
This SimObject declares no states, so it takes no initial conditions.
This SimObject is referred to as Auv/Echosounder