Skip to content

Commit

Permalink
doc(pm): add docs for developing new models
Browse files Browse the repository at this point in the history
  • Loading branch information
mchitre committed Jan 4, 2025
1 parent eecf0e6 commit d2ad6cb
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ makedocs(
"Manual" => Any[
"uw_basic.md",
"pm_api.md",
"pm_ext.md",
"utils.md"
],
# "Propagation models" => Any[
Expand Down
1 change: 1 addition & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pkg> add UnderwaterAcoustics

- [Propagation modeling toolkit]() quickstart guide.
- [Underwater acoustics]() utility functions.
- [Developing your own propagation or channel models](@ref).

## Talks & publications

Expand Down
1 change: 1 addition & 0 deletions docs/src/pm_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CurrentModule = UnderwaterAcoustics

```@docs
AbstractPropagationModel
AcousticArrival
transmission_loss
acoustic_field
arrivals
Expand Down
72 changes: 72 additions & 0 deletions docs/src/pm_ext.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Developing your own propagation or channel models

```@meta
CurrentModule = UnderwaterAcoustics
```

`UnderwaterAcoustics.jl` is designed to allow the community to extend it by adding propagation models and channel models.

## Propagation models

A new propagation model (let's call it `MyPropagationModel` for illustration) should define a type that extends:
```@docs; canonical=false
AbstractPropagationModel
```

The constructor for `MyPropagationModel` usually will take in an environmental description and optionally, keyword options that control the model:
```julia
MyPropagationModel(env::UnderwaterEnvironment; kwargs...)
```
However, for data-driven models, the constructor might take in data or partial environmental information and data:
```julia
MyPropagationModel(data; kwargs...)
MyPropagationModel(env, data; kwargs...)
```

The following methods for `MyPropagationModel`:
```@docs; canonical=false
acoustic_field
```
The acoustic field is represented by complex numbers with amplitude that is related to the source level (`spl`) and transmission loss, and angle that is related to the acoustic phase at the source frequency.

!!! tip "Additional options"
The `acoustic_field()`, `transmission_loss()`, `arrivals()` and `impulse_response()` methods may support propagation model specific keyword arguments (options) to control finer details of the propagation model.

A `transmission_loss()` method may be optionally defined for `MyPropagationModel`:
```@docs; canonical=false
transmission_loss
```
If it is not defined, the transmission loss is automatically computed as `20 * log10(acoustic_field(...))`.

A propagation model should also typically define:
```@docs; canonical=false
arrivals
```
The returned arrivals should be an array of arrivals that extend:
```@docs; canonical=false
AcousticArrival
```
The information held in an arrival is propagation model dependent. For example, ray models may return arrivals that contain ray information such as time of arrival, angle of arrival, amplitude and phase of arrival, eigenpath, etc. On the other hand, models based on normal modes may return arrivals containing mode information such as mode number, horizontal and vertical wavenumber, etc.

Some propagation models may be able to generate an impulse response. If so, they should define:
```@docs; canonical=false
impulse_response
```
If defined, the impulse response may be used to generate a channel model automatically (by calling `channel()`).

## Channel model

If a propagation model can estimate a received signal from a transmit signal without having to compute an impulse response and convolve it, it may wish to implement the channel modeling API directly:
```@docs; canonical=false
channel
```
The returned channel model must extend:
```@docs; canonical=false
AbstractChannelModel
```
and support:
```@docs; canonical=false
transmit
```

In some cases (e.g. channel replay techniques), a channel model may be defined without the need to derive it from a propagation model. In such a case, one may extend `UnderwaterAcoustics.jl` by directly defining the channel model.
21 changes: 17 additions & 4 deletions src/pm_api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,32 @@ Superclass for all propagation models.
"""
abstract type AbstractPropagationModel end

"""
Superclass for all acoustic arrivals.
"""
abstract type AcousticArrival end

"""
transmission_loss(pm, tx, rxs)
Compute the transmission loss from the source `tx` to the receivers `rxs`
using propagation model `pm`. If `rxs` denotes a single receiver, the result is a
scalar. If `rxs` is an `AbstractArray`, the result is an array of transmission
losses with the same shape as `rxs`.
losses (in dB) with the same shape as `rxs`.
"""
function transmission_loss end
function transmission_loss(pm, tx, rxs; kwargs...)
20 * log10.(abs.(acoustic_field(pm, tx, rxs; kwargs...))) .- spl(tx)
end

"""
acoustic_field(pm, txs, rxs)
Compute the acoustic field at the receivers `rxs` due to the sources `txs`
using propagation model `pm`. If `rxs` denotes a single receiver, the result is a
scalar. If `rxs` is an `AbstractArray`, the result is an array of acoustic
fields with the same shape as `rxs`.
complex scalar. If `rxs` is an `AbstractArray`, the result is an array of
complex numbers with the same shape as `rxs`. The amplitude of the field is
related to the transmission loss, and the angle is related to the acoustic phase
at the source frequency.
"""
function acoustic_field end

Expand Down Expand Up @@ -287,6 +296,10 @@ end
Narrowband source at position `pos` with specified `frequency` and source level
`spl` (dB re 1 µPa @ 1 m).
If the position of the source is unknown, it may be specified as `nothing`. This
is useful when the propagation model does not require the source position (e.g.,
data-driven models).
"""
struct NarrowbandAcousticSource{T1,T2,T3} <: AbstractAcousticSource
pos::T1
Expand Down
6 changes: 3 additions & 3 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ in_units(u::typeof(u"dB"), x::typeof(1u"dB")) = ustrip(x)
Position(x, y, z)
Position(x, z)
Position(z)
Position()
Convert a position to a named tuple with fields `x`, `y`, and `z`. If any of the
coordinates is not provided, they are assumed to be zero. If the coordinates have
Expand All @@ -59,7 +58,8 @@ Position(pos::NamedTuple{(:z,)}) = Position(pos.z)
Position(x::Number, y::Number, z::Number) = Position((x, y, z))
Position(x::Number, z::Number) = Position((x, 0, z))
Position(z::Number) = Position((0, 0, z))
Position() = Position((0, 0, 0))
Position(::Nothing) = nothing
Position(::Missing) = missing

################################################################################
# fields - types and utilities for quantities that may depend on position
Expand Down Expand Up @@ -114,4 +114,4 @@ may be specified as a `(x, y, z)` tuple, a `(x, z)` tuple, a `z` value.
"""
value(q::Number, pos) = q
value(q, pos) = q(Position(pos))
value(q) = value(q, Position())
value(q) = value(q, nothing)

0 comments on commit d2ad6cb

Please sign in to comment.