Tracking a drifting transmitter
This tutorial is adapted from Example A
presented in:
- Mandar Chitre, “Differentiable Ocean Acoustic Propagation Modeling,” in OCEANS 2023 IEEE/MTS – Limerick, 5-8 June 2023.
A version of this example was also presented in the UComms 2020 webinar:
- Mandar Chitre, “Underwater Acoustics in the age of differentiable and probabilistic programming”, UComms 2020 webinar, 3 December 2020.
Problem statement
Let us consider a scenario where a drifting probe acoustically transmits its sensor data periodically to a static receiver. The initial position of the sensor is perfectly known, and so is the environment. But the path of the sensor as it drifts is not known, but we’d like to get an estimate of it from the received acoustic signal. Due to the high data rate requirements, the receiver uses an equalization technique that requires an accurate estimate of the channel impulse response. We want to generate that using a propagation model and an accurate estimate of the location of the probe.
The environment is an iso-velocity channel with a constant depth of 20 m and known seabed parameters (density ρ
= 1500 kg/m³, sound speed c
= 1850 m/s, and attenuation δ
= 0.001). The probe uses a 1-2 kHz band for data transmission, and includes 101 pilots at 10 Hz spacing to aid with channel estimation. The transmission loss can be accurately measured at those pilot frequencies, since the transmit source level is assumed to be known, but phase information is assumed to be unavailable at each pilot.
Dataset
To illustrate the idea, we generate a 60-transmission dataset with a linearly drifting path for the transmitter. Since we have an range-independent iso-velocity environment, we can use the PekerisRayTracer
(otherwise we could use the RaySolver
):
using UnderwaterAcoustics
using DataFrames
function 𝒴((r, d, f, ρ, c, δ))
= UnderwaterEnvironment(
env = 20.0,
bathymetry = FluidBoundary(ρ, c, δ)
seabed
)= AcousticSource(r, -d, f)
tx = AcousticReceiver(0.0, -5.0)
rx = PekerisRayTracer(env)
pm transmission_loss(pm, tx, rx)
end
= DataFrame([(
data =100.0 + 0.5t,
range=6.0 + 0.01t,
depth=[𝒴([100.0 + 0.5t, 6.0 + 0.01t, f, 1500.0, 1850.0, 0.001]) for f ∈ 1000.0:10.0:2000.0]
pilots∈ 0.0:1.0:59.0]) ) for t
Gradient descent
In order to recover the drift path of the probe, we build a simple error model for the measured pilots. We initialize the model with the known starting location of the probe, and track the probe by minimizing the error through gradient descent.
Since our propagation model is differentiable, the gradient of the error can be automatically computed during the optimization using ForwardDiff.jl
.
using ForwardDiff
# channel model for pilots
pilots(r, d) = [𝒴([r, d, f, 1500.0, 1850.0, 0.001]) for f ∈ 1000.0:10.0:2000.0]
# gradient descent optimization
function chparams(data)
= []
history = [100.0, 6.0] # known initial location
θ = [1e-4, 1e-6] # learning rate
η for row ∈ eachrow(data)
err(θ) = sum(abs2, pilots(θ[1], θ[2]) .- row.pilots) # error model
for i ∈ 1:100 # iterations of simple gradient descent
.-= η .* ForwardDiff.gradient(err, θ)
θ end
push!(history, (range=θ[1], depth=θ[2]))
end
DataFrame(history)
end
= chparams(data) p
Now that we have a path estimate, let’s check it against the ground truth:
using Plots
plot(data.range, -data.depth; linewidth=2, xlabel="Range (m)", ylabel="Depth (m)", label="Ground truth")
scatter!(p.range, -p.depth; markersize=2, label="Estimated")
We have a pretty good match!