Positions and units

Positions

We often need 1D, 2D or 3D positions to describe the location of a source, receiver, or other object in the underwater environment. The canonical representation for positions is a named 3-tuple of coordinates of the form (x=0.0, y=0.0, z=0.0). We define the generic data type XYZ to represent such a named tuple:

const XYZ = NamedTuple{(:x,:y,:z)}


It is sometimes more convenient to represent positions in simpler forms, with just a single or two-coordinates. In most places where positions are expected, we automatically convert various input representations to a XYZ. This allows the user to specify scalars, tuples, or named tuples as input. For example, the AcousticReceiver(⋯) accepts receiver position as input:

using UnderwaterAcoustics

# The following are all equivalent:
@info AcousticReceiver((x=0, y=0, z=-10)).pos
@info AcousticReceiver((x=0, z=-10)).pos
@info AcousticReceiver((z=-10,)).pos
@info AcousticReceiver((0, 0, -10)).pos
@info AcousticReceiver((0, -10)).pos
@info AcousticReceiver(-10).pos
@info AcousticReceiver(0, 0, -10).pos
@info AcousticReceiver(0, -10).pos
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)
[ Info: (x = 0.0, y = 0.0, z = -10.0)

The last two forms are only available in functions that accept no other positional arguments (other than position), and therefore do not cause any confusion as to the meaning of positional arguments.

Units

We also support units though Unitful.jl. Internally, all quantities are represented as floating point numbers without explicit units, with standard units (mostly SI units) assumed implicitly. Input quantities are automatically converted to the implicit units as necessary. For example, positions can be specified with units:

# The following are all equivalent:
@info AcousticReceiver(1000, -10).pos
@info AcousticReceiver(1000u"m", -10u"m").pos
@info AcousticReceiver(1u"km", -10u"m").pos
@info AcousticReceiver((1u"km", -10u"m")).pos
@info AcousticReceiver((x=1u"km", z=-1000u"cm")).pos
[ Info: (x = 1000.0, y = 0.0, z = -10.0)
[ Info: (x = 1000.0, y = 0.0, z = -10.0)
[ Info: (x = 1000.0, y = 0.0, z = -10.0)
[ Info: (x = 1000.0, y = 0.0, z = -10.0)
[ Info: (x = 1000.0, y = 0.0, z = -10.0)

We may also use units in other places:

@show soundspeed(0u"°C");
@show soundspeed(32u"°F");
@show reflection_coef(10°, 1.2, 1.5);   # const ° is defined as u"°"
@show absorption(10u"kHz");
soundspeed(0 * u"°C") = 1448.96
soundspeed(32 * u"°F") = 1448.96
reflection_coef(10°, 1.2, 1.5) = 0.294789169055958 + 0.0im
absorption(10 * u"kHz") = 0.9253039111184715

including environment descriptions:

env = UnderwaterEnvironment(
  soundspeed = 1.5u"km/s",
  temperature = 60u"°F",
  salinity = 35u"ppt",
  density = 1.025u"g/cm^3",
  bathymetry = 100u"m"
)
UnderwaterEnvironment(
  bathymetry = 100, 
  altimetry = 0.0, 
  temperature = 140//9, 
  salinity = 35, 
  soundspeed = 1500.0, 
  density = 1025.0, 
  seabed = RigidBoundary, 
  surface = PressureReleaseBoundary, 
)

Extending

While standard UnderwaterAcoustics.jl API functions automatically convert input values to the appropriate XYZ and units, you may wish to support these features in your own code when extending UnderwaterAcoustics.jl. To do so, you may use the unexported function xyz(⋯) to create a position from various different forms. You may also use the unexported in_units(⋯) function to convert input values to the appropriate units:

using UnderwaterAcoustics: XYZ, in_units

function myfunction(pos, temperature)
  pos = xyz(pos)
  temperature = in_units(u"°C", temperature)
  # ⋯
end

API reference

xyz(pos)
xyz(x, y, z)
xyz(x, z)
xyz(z)

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 units, they are converted to meters.

in_units(u, x)

Get the numerical value of x in units of u. If x is a Unitful.Quantity, it is converted to u. If x is a number, it is assumed to be in u and is returned as is.

If u is u"dB", the x may be specified as a number of a u"dB" quantity. In both cases, the numerical value of x in dB is returned.

Examples:
julia> in_units(u"Hz", 10u"kHz")
10000

julia> in_units(u"m", 10)
10

julia> in_units(u"m", 1u"cm")
1//100

julia> in_units(u"dB", 3)
3

julia> in_units(u"dB", 3u"dB")
3
distance(p1, p2)

Compute distance between two positions.