Parser and lookup helpers for ANTEX 1.4 receiver and satellite antenna blocks.
The ANTEX parser, satellite validity lookup, and PCO/PCV interpolation live in the Rust GNSS core. A parsed product keeps every record ANTEX 1.4 defines and keeps absent records absent:
header-ANTEX VERSION / SYST, thePCV TYPE / REFANTcalibration type and reference antenna (so relative values can be told apart from absolute ones), the header comments, and whetherEND OF HEADERis present;blocks- every antenna block in file order, including each validity interval of oneTYPE / SERIAL NOid;antennas- the latest block for each id, the viewantenna/2reads;outer_comments- comments between and after the blocks, each placed by the number of blocks before it;skipped_records- the count of records the forgiving parse passed over or found inconsistent (a line outside any record, a# OF FREQUENCIEScount that disagrees with the sections read, a block or section not closed by its own end record, and similar); the blocks themselves are kept.
Each Sidereon.GNSS.Antex.Antenna keeps its comments (those before
TYPE / SERIAL NO apart from the rest), its METH / BY / # / DATE records,
DAZI and ZEN1 / ZEN2 / DZEN as nil when the block has no such record,
its validity bounds with their exact seconds (Sidereon.GNSS.Antex.Epoch),
and its frequency sections as a list in file order, each with its
START OF FREQ RMS section when it has one. A frequency label may repeat;
frequency/2, pco/2 and pcv/4 refuse a label whose sections differ as
{:ambiguous_frequency, fields}.
Refusals
Parsing, writing and lookups return {:error, reason} with the core's typed
reason and every field it carries:
{:invalid_field, %{antenna_id, record, field, value}}- a record field that is not a valid value, such as a blank, short or malformed validity seconds field or a second of 60, which GPS time does not have;{:repeated_record, %{antenna_id, record}}- a once-per-block record repeated with different content;{:degenerate_grid, %{antenna_id, frequency, reason}}- a PCV row that would put two values on one zenith;{:invalid_input, %{field, reason}}- a lookup argument, such as a non-finite zenith;{:unknown_frequency, %{antenna_id, frequency}},{:ambiguous_frequency, %{antenna_id, frequency, sections}},{:missing_pco, %{antenna_id, frequency}}and{:empty_pcv_grid, %{antenna_id, frequency}};{:unwritable, %{field, reason}}- a product the writer cannot state exactly;:invalid_datetime- an epoch outside the GPS calendar and clock.
Summary
Functions
Return the latest antenna block for a TYPE / SERIAL id, or nil.
Return the validity block of a TYPE / SERIAL id valid at epoch, or nil.
Return every validity block of a TYPE / SERIAL id, in file order.
Serialize a parsed ANTEX product back to ANTEX 1.4 text.
The frequency section a label selects.
Load and parse an ANTEX file from path.
Like load/1 but raises on failure.
Parse ANTEX text already in memory.
Frequency-dependent PCO (north/east/up in meters).
Like pco/2 but raises on a refused lookup.
Frequency-dependent phase-center variation in meters.
Like pcv/4 but raises on a refused lookup.
Return the satellite antenna block for PRN prn (e.g. "G05") valid at the
given epoch, or nil if none.
Return the number of records the forgiving parse skipped or found inconsistent.
Types
@type parse_error() :: {:error, term()}
@type t() :: %Sidereon.GNSS.Antex{ antennas: %{optional(String.t()) => Sidereon.GNSS.Antex.Antenna.t()}, blocks: [Sidereon.GNSS.Antex.Antenna.t()], handle: reference() | nil, header: Sidereon.GNSS.Antex.Header.t() | nil, outer_comments: [Sidereon.GNSS.Antex.OuterComment.t()], skipped_records: non_neg_integer() }
Functions
@spec antenna(t(), String.t()) :: Sidereon.GNSS.Antex.Antenna.t() | nil
Return the latest antenna block for a TYPE / SERIAL id, or nil.
@spec antenna_at(t(), String.t(), NaiveDateTime.t() | Sidereon.GNSS.Antex.Epoch.t()) :: Sidereon.GNSS.Antex.Antenna.t() | nil | {:error, term()}
Return the validity block of a TYPE / SERIAL id valid at epoch, or nil.
epoch is a NaiveDateTime or a Sidereon.GNSS.Antex.Epoch in GPS time;
bounds are inclusive and compared with their exact seconds.
@spec antenna_intervals(t(), String.t()) :: [Sidereon.GNSS.Antex.Antenna.t()]
Return every validity block of a TYPE / SERIAL id, in file order.
Serialize a parsed ANTEX product back to ANTEX 1.4 text.
The writer states every record from a retained value and writes no record
the source did not carry, apart from the start and end records of blocks and
sections, with antenna blocks and frequency sections in file order and
validity seconds exactly. Re-parsing the output yields an equal product. The
serializer works on the full parsed product held alongside the decoded
antennas, so every validity interval is re-emitted, not just the latest-wins
view exposed by antenna/2.
The writer refuses a product it cannot state exactly - a field that overflows
its columns, a value its precision cannot hold, a sample coordinate the reader
would not reconstruct, a frequency label that is not a system flag and a
two-column number, validity seconds no decimal form fits in 13 columns - as
{:error, {:unwritable, %{field: field, reason: reason}}} rather than
rounding or dropping it.
@spec frequency(Sidereon.GNSS.Antex.Antenna.t(), String.t()) :: {:ok, Sidereon.GNSS.Antex.Frequency.t()} | {:error, term()}
The frequency section a label selects.
When several sections carry the label they must be identical; otherwise the
lookup is refused as {:error, {:ambiguous_frequency, fields}}.
@spec load(String.t()) :: {:ok, t()} | parse_error()
Load and parse an ANTEX file from path.
Like load/1 but raises on failure.
@spec parse(binary()) :: {:ok, t()} | parse_error()
Parse ANTEX text already in memory.
Returns {:ok, %Sidereon.GNSS.Antex{}} with every retained record, or
{:error, reason} with the typed refusals listed in the module
documentation.
@spec pco(Sidereon.GNSS.Antex.Antenna.t(), String.t()) :: {:ok, {float(), float(), float()}} | {:error, term()}
Frequency-dependent PCO (north/east/up in meters).
Returns {:ok, {north, east, up}}, {:error, {:unknown_frequency, fields}},
or {:error, {:ambiguous_frequency, fields}}.
@spec pco!(Sidereon.GNSS.Antex.Antenna.t(), String.t()) :: {float(), float(), float()}
Like pco/2 but raises on a refused lookup.
@spec pcv(Sidereon.GNSS.Antex.Antenna.t(), String.t(), number(), number() | nil) :: {:ok, float()} | {:error, term()}
Frequency-dependent phase-center variation in meters.
Interpolation is linear in zenith and azimuth. Azimuth is optional: when not
given (or when the antenna has no azimuth-dependent rows), the NOAZI row is
used. A finite zenith outside the block's ZEN1..ZEN2 grid is clamped to it.
Returns {:ok, value_m} or {:error, reason} with the typed lookup
refusals (unknown or ambiguous frequency, a non-finite zenith as
{:invalid_input, fields}, an empty grid as {:empty_pcv_grid, fields}).
@spec pcv!(Sidereon.GNSS.Antex.Antenna.t(), String.t(), number(), number() | nil) :: float()
Like pcv/4 but raises on a refused lookup.
@spec satellite_antenna( t(), String.t(), NaiveDateTime.t() | Sidereon.GNSS.Antex.Epoch.t() ) :: Sidereon.GNSS.Antex.Antenna.t() | nil | {:error, term()}
Return the satellite antenna block for PRN prn (e.g. "G05") valid at the
given epoch, or nil if none.
Every validity interval in the file is searched, not only the latest block of
each id. epoch is a NaiveDateTime or a Sidereon.GNSS.Antex.Epoch in GPS
time. An epoch outside the GPS calendar and clock returns
{:error, :invalid_datetime}.
@spec skipped_records(t()) :: non_neg_integer()
Return the number of records the forgiving parse skipped or found inconsistent.