Real-time Underwater Acoustic Simulator
The UnderwaterAcoustics.jl
project provides a unified interface to many underwater acoustic propagation models including such as PekerisRayModel
, RaySolver
, Bellhop, Kraken, etc. This project leverages these models to provide a real-time streaming ocean acoustic simulator for software-only or hardware-in-the-loop simulations. The data streams simulate analog-to-digital convertors (ADC) and digital-to-analog convertors (DAC) in acoustic systems.
Tip
If you only need offline acoustic simulations, you may want to consider using the acoustic simulation API in UnderwaterAcoustics.jl
directly instead.
julia> # press ]
pkg> add VirtualAcousticOcean
Setting up a simulation is simple. We first describe an environment and create a propagation model, as one would with the propagation modeling toolkit:
using UnderwaterAcoustics
env = UnderwaterEnvironment(
seabed = SandyClay, # sandy-clay seabed
bathymetry = ConstantDepth(40.0) # 40m water depth
)
pm = PekerisRayModel(env, 7) # 7-ray Pekeris ray model
We then define a simulation using that environment, adding acoustic nodes to it:
using VirtualAcousticOcean
sim = Simulation(pm, 25000.0) # operating at 25 kHz nominal frequency
addnode!(sim, (0.0, 0.0, -10.0), GroguUDP, 9809) # node 1 at 10 m depth
addnode!(sim, (1000.0, 0.0, -10.0), GroguUDP, 9819) # node 2 at 10 m depth, 1 km away
run(sim) # start simulation (non-blocking)
Any number of nodes may be added to a simulation. Both nodes above will be accessible over UDP ports (9809
and 9819
respectively) using the Grogu UDP real-time streaming protocol. UnetStack 4 based modems and software-defined model simulators support the Grogu protocol out-of-the-box.
Nodes may have an array of hydrophones, if desired. To define an array, each hydrophone location relative to the node location is specified using a keyword parameter relpos
. For example:
addnode!(sim, (500.0, 500.0, -15.0), GroguUDP, 9829; relpos=[
(0.0,0.0,0.0), # relative position of hydrophone 1
(0.0,0.0,-1.0), # relative position of hydrophone 2
(0.0,0.0,-2.0), # relative position of hydrophone 3
(0.0,0.0,-3.0) # relative position of hydrophone 4
])
To terminate the simulation, simply close the simulation:
close(sim)
Once the simulation is up and running, we can connect to the Virtual Acoustic Ocean and stream acoustic data from various nodes. For example, in the above simulation (with the Grogu UDP protocol), we will have the following UDP ports open once the simulator is running:
9809
– command port for node 1 (single channel data)9810
– DAC data port for node 1 (single output channel)9819
– command port for node 2 (single channel data)9820
– DAC data port for node 2 (single output channel)9829
– command port for node 3 (4-channel data stream)9830
– DAC data port for node 3 (single output channel)
ADC data can be streamed from any of the nodes by sending a istart
command and specifying the UDP port to stream the data to.
Tip
UnetStack 4 based modems and software-defined model simulators allow us to specify the UDP port to connect to in the modem.toml
. A minimal example modem.toml
is shown below:
[input]
analoginterface = "GroguDAQ" # use GroguUDP protocol
baseport = 9819 # with control port 9819, DAQ data port 9820
[bb]
fc = 24000 # carrier frequency of 24 kHz
While the Virtual Acoustic Ocean currently only supports the Grogu UDP real-time streaming protocol, the code is designed to easily allow users to implement their own streaming protocols. If you implement a standard protocol that you feel may be useful to others, please do consider contributing the implementation back to this repository via a pull request (PR).
To implement a new protocol, create a new data type (e.g. MyProtocol
) and support the following API:
MyProtocol(client)
VirtualAcousticOcean.run(conn::MyProtocol)
VirtualAcousticOcean.stream(conn::MyProtocol, timestamp::Int, seqno::Int, data::Matrix{Float32})
VirtualAcousticOcean.event(conn::MyProtcocol, timestamp::Int, event, id)
Base.close(conn::MyProtocol)
data
matrix contains samples scaled in the ±1 range, with each column containing data for one channel. timestamp
are in µs from an arbitrary time origin. seqno
is a running packet number for streaming data. id
is an opaque numeric ID identifying the tranmission for which an event (transmission start ostart
and transmission end ostop
) is sent.
For detailed documentation on what each API function should do, refer to the Grogu UDP protocol implementation.
The protocol implementation may call the following API:
Base.get(client, key::Symbol) # get parameter
VirtualAcousticOcean.set!(client, key::Symbol, value::Any) # set parameter
VirtualAcousticOcean.transmit(client, timestamp::Int, data::Matrix{Float32}, id) # transmit a signal
Supported parameters:
:time # current simulated time in µs
:iseqno # next block sequence number of ADC input data stream
:iblksize # ADC input data stream block size in samples
:irate # ADC input data sampling rate (Sa/s)
:irates # list of supported ADC input data sampling rate (Sa/s)
:ichannels # number of ADC input channels
:igain # gain (dB) of ADC channels
:orate # DAC output sampling rate (Sa/s)
:orates # list of supported DAC output sampling rate (Sa/s)
:ochannels # number of DAC output channels
:ogain # gain (dB) for DAC channels
:omute # mute flag indicating that the DAC is muted
Currently, the Virtual Acoustic Ocean makes a quasi-static assumption:
- Node reception time is computed at time of transmission, and therefore does not account for any node motion while the transmission is in flight. This is a reasonable simplification for slow moving nodes and short distances, but may not hold at very long range communication.
- Node motion does not induce any Doppler in the reception.