Sidereon.GNSS.QC (Sidereon v0.26.1)

Copy Markdown View Source

Measurement-quality control for single-point positioning.

The numerical modeling and FDE orchestration live in the sidereon-core Rust core. This module keeps the Elixir API, normalizes options and epochs for the NIF, maps errors, and decodes the unchanged public result maps.

Summary

Types

The result of raim/2.

A {satellite_id, elevation_deg} or {satellite_id, elevation_deg, cn0_dbhz} entry.

Functions

Chi-square inverse CDF (quantile).

Fault detection and exclusion: solve, run RAIM, exclude the worst satellite, and repeat until the measurement set is self-consistent or the exclusion budget is exhausted.

Lint RINEX NAV text.

Lint a parsed RINEX OBS file.

Lint RINEX OBS or CRINEX text.

Observation completeness and signal-quality rollup for a parsed RINEX OBS file.

Pseudorange measurement variance (m^2) from satellite elevation.

Residual-based RAIM: a chi-square goodness-of-fit test on a positioning solution.

Standalone range RAIM/FDE over a caller-supplied linearized measurement set, independent of any full positioning solve.

Residual-based RAIM for an existing SPP solution.

Render an observation QC report as HTML.

Render an observation QC report as fixed-width text.

Mechanically repair RINEX NAV text.

Mechanically repair RINEX OBS or CRINEX text.

Core robust-reweighted SPP under the RAIM/FDE exclusion loop.

Build a satellite => sigma_m map for a list of weight entries.

Serialize an observation QC report as JSON.

Build a satellite => inverse_variance_weight map for a list of weight entries.

Types

raim_result()

@type raim_result() :: %{
  fault_detected?: boolean(),
  fault_detected: boolean(),
  test_statistic: float(),
  threshold: float() | nil,
  reduced_chi_square: float() | nil,
  dof: integer(),
  testable?: boolean(),
  testable: boolean(),
  normalized_residuals: %{required(String.t()) => float()},
  rms_m: float(),
  worst_sat: String.t() | nil
}

The result of raim/2.

weight_entry()

@type weight_entry() :: {String.t(), number()} | {String.t(), number(), number()}

A {satellite_id, elevation_deg} or {satellite_id, elevation_deg, cn0_dbhz} entry.

Functions

chi2_inv(p, k)

@spec chi2_inv(float(), pos_integer()) :: float()

Chi-square inverse CDF (quantile).

fde(source, observations, epoch, opts \\ [])

@spec fde(
  term(),
  [Sidereon.GNSS.Positioning.observation()],
  Sidereon.GNSS.Positioning.epoch(),
  keyword()
) ::
  {:ok,
   %{
     solution: Sidereon.GNSS.Positioning.Solution.t(),
     excluded: [{String.t(), :raim_excluded}],
     iterations: non_neg_integer()
   }}
  | {:error, {:fault_unresolved, float()}}
  | {:error, term()}

Fault detection and exclusion: solve, run RAIM, exclude the worst satellite, and repeat until the measurement set is self-consistent or the exclusion budget is exhausted.

Malformed FDE options are returned as tagged errors, including {:invalid_option, :p_fa}, {:invalid_option, :weights}, and {:invalid_option, :max_iterations}.

lint_nav_text(text)

@spec lint_nav_text(String.t()) :: {:ok, map()} | {:error, term()}

Lint RINEX NAV text.

lint_obs(observations)

@spec lint_obs(Sidereon.GNSS.RINEX.Observations.t()) ::
  {:ok, map()} | {:error, term()}

Lint a parsed RINEX OBS file.

lint_obs_text(text)

@spec lint_obs_text(String.t()) :: {:ok, map()} | {:error, term()}

Lint RINEX OBS or CRINEX text.

observation_report(observations, opts \\ [])

@spec observation_report(
  Sidereon.GNSS.RINEX.Observations.t(),
  keyword()
) :: {:ok, Sidereon.GNSS.QC.ObservationReport.t()} | {:error, term()}

Observation completeness and signal-quality rollup for a parsed RINEX OBS file.

pseudorange_variance(elevation_deg, opts \\ [])

@spec pseudorange_variance(
  number(),
  keyword()
) :: float() | {:error, :invalid_elevation | :missing_cn0}

Pseudorange measurement variance (m^2) from satellite elevation.

Returns a float, {:error, :invalid_elevation} for elevations at or below the horizon, or {:error, :missing_cn0} when model: :elevation_cn0 is selected without :cn0.

raim(input, opts \\ [])

Residual-based RAIM: a chi-square goodness-of-fit test on a positioning solution.

Pass inverse-variance weights derived from per-satellite residual variances, either as a %{sat => weight} map or as weight entries consumed by weight_vector/2. Unit weights with metre-scale residuals make fault_detected saturate near 100%.

entries = [
  %{satellite_id: "G01", elevation_deg: 72.0},
  %{satellite_id: "G02", elevation_deg: 42.0}
]

weights = Sidereon.GNSS.QC.weight_vector(entries, a_m: 0.8, b_m: 0.8)
Sidereon.GNSS.QC.raim(input, weights: weights)

raim_fde_design(rows, opts \\ [])

@spec raim_fde_design(
  [map()],
  keyword()
) :: {:ok, map()} | {:error, term()}

Standalone range RAIM/FDE over a caller-supplied linearized measurement set, independent of any full positioning solve.

Each row of rows is a map describing one linearized range measurement:

  • :id - stable measurement identifier (e.g. a satellite token "G01")
  • :residual_m - observed-minus-computed range residual, metres
  • :design_row - the measurement's row of the design matrix (a list of the partials of the predicted range with respect to each estimated state parameter); every row must carry the same length
  • :weight - inverse-variance weight 1 / sigma^2, strictly positive

Options:

  • :p_fa - false-alarm probability for the global chi-square test (default 0.001)
  • :max_exclusions - maximum measurements the exclusion loop may remove (default: the row count)
  • :min_redundancy - minimum redundancy an exclusion must leave behind (default 1)

Returns {:ok, result} where result carries the protected :state_correction, :state_covariance, the :global_test chi-square map, the :excluded ids, per-measurement :diagnostics, and the exclusion :iterations; or {:error, reason} for a malformed or rank-deficient input.

raim_for_solution(solution, opts \\ [])

@spec raim_for_solution(
  Sidereon.GNSS.Positioning.Solution.t(),
  keyword()
) :: raim_result()

Residual-based RAIM for an existing SPP solution.

This is the direct post-solve variant matching the Rust and C raim_for_solution surface. It uses the solution's used satellites and post-fit residuals, with the same options accepted by raim/2.

render_html(observation_report)

@spec render_html(Sidereon.GNSS.QC.ObservationReport.t()) ::
  {:ok, String.t()} | {:error, term()}

Render an observation QC report as HTML.

render_text(observation_report)

@spec render_text(Sidereon.GNSS.QC.ObservationReport.t()) ::
  {:ok, String.t()} | {:error, term()}

Render an observation QC report as fixed-width text.

repair_nav_text(text, opts \\ [])

@spec repair_nav_text(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Mechanically repair RINEX NAV text.

repair_obs_text(text, opts \\ [])

@spec repair_obs_text(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Mechanically repair RINEX OBS or CRINEX text.

robust_fde(source, observations, epoch, opts \\ [])

@spec robust_fde(
  term(),
  [Sidereon.GNSS.Positioning.observation()],
  Sidereon.GNSS.Positioning.epoch(),
  keyword()
) ::
  {:ok,
   %{
     solution: Sidereon.GNSS.Positioning.Solution.t(),
     excluded: [{String.t(), :raim_excluded}],
     iterations: non_neg_integer()
   }}
  | {:error, term()}

Core robust-reweighted SPP under the RAIM/FDE exclusion loop.

sigmas(entries, opts \\ [])

@spec sigmas(
  [weight_entry()],
  keyword()
) :: %{required(String.t()) => float()}

Build a satellite => sigma_m map for a list of weight entries.

to_json(observation_report)

@spec to_json(Sidereon.GNSS.QC.ObservationReport.t()) ::
  {:ok, String.t()} | {:error, term()}

Serialize an observation QC report as JSON.

weight_vector(entries, opts \\ [])

@spec weight_vector(
  [weight_entry()],
  keyword()
) :: %{required(String.t()) => float()}

Build a satellite => inverse_variance_weight map for a list of weight entries.