ExSrpPhat (ex_srp_phat v0.1.0)

Copy Markdown View Source

GCC-PHAT → SRP-PHAT acoustic source localization over a known microphone-array geometry, backed by a Rust NIF.

One solve fuses a single time-aligned frame-set (one PCM frame per emplacement) against the array's known WGS-84 ECEF geometry into zero or more localized ExSrpPhat.Source structs.

Coordinate frame & units

  • Positions are WGS-84 ECEF meters. Slant range is straight-line Euclidean (chord) distance — sound travels the chord, not a surface arc.
  • Speed of sound is 343.0 m/s (~20 °C).
  • Use ExSrpPhat.Geo to convert lat/lon/alt → ECEF when building geometry.

Example

iex> geometry = %{
...>   sample_rate_hz: 4_000,
...>   emplacements: [
...>     %{emplacement_id: :a, ecef: ExSrpPhat.Geo.latlon_to_ecef(35.0, -106.0, 1600.0)},
...>     %{emplacement_id: :b, ecef: ExSrpPhat.Geo.latlon_to_ecef(35.0006, -106.0, 1600.0)},
...>     %{emplacement_id: :c, ecef: ExSrpPhat.Geo.latlon_to_ecef(35.0, -106.0008, 1600.0)},
...>     %{emplacement_id: :d, ecef: ExSrpPhat.Geo.latlon_to_ecef(35.0004, -106.0004, 1670.0)}
...>   ]
...> }
iex> frames = Enum.map(geometry.emplacements, &%{emplacement_id: &1.emplacement_id, samples: List.duplicate(0.0, 16)})
iex> {:ok, sources} = ExSrpPhat.localize(frames, geometry)
iex> is_list(sources)
true

Summary

Functions

Localize acoustic sources from a time-aligned frame-set.

Types

emplacement()

@type emplacement() :: %{emplacement_id: term(), ecef: vec3()}

frame()

@type frame() :: %{emplacement_id: term(), samples: [number()]}

geometry()

@type geometry() :: %{sample_rate_hz: pos_integer(), emplacements: [emplacement()]}

vec3()

@type vec3() :: {number(), number(), number()}

Functions

localize(frames, geometry, opts \\ [])

@spec localize([frame()], geometry(), keyword()) ::
  {:ok, [ExSrpPhat.Source.t()]} | {:error, term()}

Localize acoustic sources from a time-aligned frame-set.

frames is a list of %{emplacement_id: id, samples: [float]}; every frame must share the same sample length. geometry carries the sample rate and one ECEF position per emplacement. Frames are matched to geometry by emplacement_id (order-independent); the two id sets must be identical.

opts (all optional) tune the search — see ExSrpPhat.Codec.pack_opts/1 for keys and defaults (:grid_extent_m, :coarse_res_m, :fine_res_m, :min_peak_ratio, :max_sources).

Returns {:ok, [%ExSrpPhat.Source{}]} or {:error, reason}. Input is fully validated in Elixir before crossing the NIF boundary.