Example

The following example uses example data that come with Wahoo.jl.

using Wahoo

import GeoArrays
using DelimitedFiles: readdlm

# This is optional. If *both* packages are imported,
# some computations use the GPU. Otherwise, the CPU is used for everything.
import CUDA
import cuDNN

1) Bathymetry

Load the bathymetry map which provides depth information for each grid cell.

pathdata = joinpath(pkgdir(Wahoo), "example_data")
bathymetry_map = GeoArrays.read(joinpath(pathdata, "bathymetry_200m.tif"))
GeoArrays.bbox(bathymetry_map)

2) Depth observations

Read the depth measurements and define the exponential likelihood model. This model implies the fish is more likely close to the seabed.

# read depth signals
depth_signals = readdlm(joinpath(pathdata, "depth_observations.csv"), ',', header=true)[1][:,2]

# define likelihood
function p_obs_depth_exponential(signals, t, waterdepth, dist; scale=30f0)
    signal = signals[t]
    if signal > waterdepth         # water is too shallow
        return zero(waterdepth)
    else
        # exponential
        Z = 1 - exp(-waterdepth/scale) # normalisation due to truncation
        exp(-(waterdepth - signal)/scale)/(scale * Z)
    end
end

3) Acoustic observations

Load acoustic detection data and specify the probability model for acoustic signals given the distance.

# read acoustic signals
acoustic_signals = readdlm(joinpath(pathdata, "acoustic_observations.csv"), ',', header=true)[1][:,2:3]
acoustic_signals = Int.(acoustic_signals')
acoustic_obs = [acoustic_signals[1,:], acoustic_signals[2,:]]

# read sensor positions
moorings = readdlm(joinpath(pathdata, "acoustic_moorings.csv"), ',', header=true)[1]
acoustic_pos = tuple.(moorings[:,2], moorings[:,3])

# define likelihood
function p_obs_acoustic(signals, t::Int, depth::Number, distance::Number)
    Wahoo.p_acoustic_sigmoid(signals[t], depth, distance)
end

acoustic_obs_models = [p_obs_acoustic, p_obs_acoustic]

4) Define parameters

We define the initial distribution of the fish location and configure the model parameters such as time steps, movement capabilities of the fish, and spatial resolution of the bathymetry.


# initial values: Matrix{Float64}
p0 = zeros(size(bathymetry_map)[1], size(bathymetry_map)[2])
idx = GeoArrays.indices(bathymetry_map, (709757.111649658, 6.26772603565296e6)) # last known location of the fish
p0[idx] = 1

tsave = 1:2:720             # time steps to save
movement_std = 140          # standard deviation of the fish movement for one time step [m]
spatial_resolution = 200    # spatial resolution [m]

5) Run inference

Finally, we run the model inference, combining all observations and assumptions.

res = track(pos_init = p0, bathymetry = bathymetry_map,
            tsave = tsave,
            spatial_resolution = spatial_resolution,
            movement_std = movement_std,
            observations = [depth_signals, acoustic_obs...],
            observation_models = [p_obs_depth_exponential, acoustic_obs_models...],
            sensor_positions = [nothing, acoustic_pos...],
            n_trajectories = 2)

# Resulting probabilities
# Array{Float32, 4}: Ny × Nx × 1 × time
res.pos_smoother     # Prob(s_t | y_{1...T}), only if `smoother = true` was used
res.pos_filter       # Prob(s_t | y_{1...t}), only if `filter = true`
res.residence_dist   # 1/T Σ Prob(s_t | y_{1...T})
res.trajectories     # Vector of trajectories sampled from Prob(s_{1...T} | y_{1...T})
res.log_p            # Prob(y_t)
res.tsave            # time points

The result of the smoother res.pos_smoother can be visualized:

Note, the code for visualization is not part of Wahoo.