Orbis.GNSS.Data.Catalog (Orbis v0.9.0)

Copy Markdown View Source

Static, pure catalog of GNSS analysis centers and the rules that turn a product specification into a canonical filename and a full archive URL.

Everything here is deterministic and network-free: GPS-week and day-of-year arithmetic, the IGS long-name filename convention, and the per-center archive layout. The fetch pipeline derives all network hosts and cache filenames from this module, which is what keeps the layer safe against SSRF (only known hosts are ever contacted) and path traversal (cache names come only from a validated canonical filename).

Analysis centers

Each center maps to a transfer protocol (:https or :ftp), an archive host, the product token it publishes, and the directory layout for each content type. Only centers and paths that resolve against a live, anonymous archive are listed; every entry below has been checked against its server.

CodeCenterProtocolHost
:gfzGFZ Potsdam (operational rapid)HTTPSisdc-data.gfz.de
:codCODE / University of Bern (MGEX)FTPgssc.esa.int
:grgCNES/CLS (MGEX, final)FTPgssc.esa.int
:wumWuhan University (MGEX, final)FTPgssc.esa.int
:igsIGS combined (broadcast / IONEX)FTPgssc.esa.int

The ESA Galileo Science Support Centre (gssc.esa.int) mirrors the IGS/MGEX archive over anonymous FTP and is the source for the MGEX precise products, the merged broadcast navigation file, and the global ionosphere maps. GFZ's own HTTPS server (isdc-data.gfz.de) carries its operational rapid SP3/CLK.

Content types and what each center serves

  • :sp3, :clk — precise orbits and clocks. :gfz (operational rapid), :cod, :grg, :wum (MGEX). The GFZ token is GFZ0OPSRAP; the MGEX tokens are COD0MGXFIN, GRG0MGXFIN, WUM0MGXFIN.
  • :nav — the IGS merged multi-GNSS broadcast navigation file (BRDC00IGS_R_..._MN.rnx). Only :igs publishes it.
  • :ionex — the global ionosphere TEC map (..._GIM.INX). :igs serves the combined IGS0OPSFIN map; :cod serves COD0OPSFIN. IONEX cadence is sub-daily, so the default sampling is 01H/02H, not 01D.

Filename conventions

Precise products and IONEX follow the IGS long-name convention AAAVPPPTTT_YYYYDDDHHMM_LEN_SMP_CNT.EXT (e.g. GFZ0OPSRAP_20201760000_01D_15M_ORB.SP3). Broadcast navigation uses the RINEX long-name SSSSMRCCC_R_YYYYDDDHHMM_LEN_CNT.fmt with no sampling field and a lowercase extension (e.g. BRDC00IGS_R_20201770000_01D_MN.rnx).

Summary

Functions

The set of hosts the layer is permitted to contact.

Build the full, compressed (.gz) archive URL for a product.

Build the canonical IGS long-name filename for a product.

Look up a center's static definition.

Human-readable center name, or nil if the code is unknown.

All supported analysis-center codes.

The content-type descriptor (%{code:, ext:, kind:}) for a content type.

All supported content-type codes.

The day-of-year (001366) for a calendar date.

The GPS day-of-week for a calendar date (0 = Sunday … 6 = Saturday).

The GPS week number for a calendar date.

The transfer protocol (:https or :ftp) for a center.

Build the canonical IGS long-name filename for a daily station observation product (RINEX 3 CRINEX), e.g. ESBC00DNK_R_20201770000_01D_30S_MO.crx.

The transfer protocol for the daily station observation archive (:ftp on the ESA GSSC mirror).

Build the full, compressed (.gz) archive URL for a daily station observation product on the ESA GSSC anonymous archive (the same daily data tree the broadcast navigation file uses).

Functions

allowed_hosts()

@spec allowed_hosts() :: MapSet.t(String.t())

The set of hosts the layer is permitted to contact.

Used by the download path as an allow-list so a malformed or unexpected URL can never cause a request to an off-catalog host.

archive_url(center, content, date, sample)

@spec archive_url(atom(), atom(), Date.t(), String.t()) ::
  {:ok, String.t()} | {:error, {:unsupported_product, term()}}

Build the full, compressed (.gz) archive URL for a product.

The directory follows the center/content layout; the filename is the canonical long-name plus a .gz suffix. The host is always one of the catalog hosts, never caller-supplied input.

Returns {:ok, url} or an {:error, {:unsupported_product, _}} tuple.

Examples

iex> Orbis.GNSS.Data.Catalog.archive_url(:gfz, :sp3, ~D[2020-06-24], "15M")
{:ok, "https://isdc-data.gfz.de/gnss/products/rapid/w2111/GFZ0OPSRAP_20201760000_01D_15M_ORB.SP3.gz"}

iex> Orbis.GNSS.Data.Catalog.archive_url(:igs, :nav, ~D[2020-06-25], "01D")
{:ok, "ftp://gssc.esa.int/gnss/data/daily/2020/177/BRDC00IGS_R_20201770000_01D_MN.rnx.gz"}

canonical_filename(center, content, date, sample)

@spec canonical_filename(atom(), atom(), Date.t(), String.t()) ::
  {:ok, String.t()} | {:error, {:unsupported_product, term()}}

Build the canonical IGS long-name filename for a product.

Precise products and IONEX use AAAVPPPTTT_YYYYDDDHHMM_LEN_SMP_CNT.EXT; broadcast navigation uses the no-sampling RINEX form SSSSMRCCC_R_YYYYDDDHHMM_LEN_CNT.ext. The center must actually publish the requested content type.

Returns {:ok, filename} or an {:error, {:unsupported_product, _}} tuple.

Examples

iex> Orbis.GNSS.Data.Catalog.canonical_filename(:gfz, :sp3, ~D[2020-06-24], "15M")
{:ok, "GFZ0OPSRAP_20201760000_01D_15M_ORB.SP3"}

iex> Orbis.GNSS.Data.Catalog.canonical_filename(:igs, :nav, ~D[2020-06-25], "01D")
{:ok, "BRDC00IGS_R_20201770000_01D_MN.rnx"}

center(code)

@spec center(atom()) :: {:ok, map()} | {:error, {:unsupported_product, term()}}

Look up a center's static definition.

Returns {:ok, map} or {:error, {:unsupported_product, {:center, code}}}.

center_name(code)

@spec center_name(atom()) :: String.t() | nil

Human-readable center name, or nil if the code is unknown.

centers()

@spec centers() :: [atom()]

All supported analysis-center codes.

content(type)

@spec content(atom()) :: {:ok, map()} | {:error, {:unsupported_product, term()}}

The content-type descriptor (%{code:, ext:, kind:}) for a content type.

Returns {:ok, map} or {:error, {:unsupported_product, {:content, type}}}.

content_types()

@spec content_types() :: [atom()]

All supported content-type codes.

day_of_year(date)

@spec day_of_year(Date.t()) :: 1..366

The day-of-year (001366) for a calendar date.

Examples

iex> Orbis.GNSS.Data.Catalog.day_of_year(~D[2020-06-24])
176

gps_day_of_week(date)

@spec gps_day_of_week(Date.t()) :: 0..6

The GPS day-of-week for a calendar date (0 = Sunday … 6 = Saturday).

Examples

iex> Orbis.GNSS.Data.Catalog.gps_day_of_week(~D[2020-06-24])
3

gps_week(date)

@spec gps_week(Date.t()) :: non_neg_integer()

The GPS week number for a calendar date.

GPS week 0 began on 1980-01-06. Uses exact integer day arithmetic, so it is leap-second-agnostic (week numbering is a calendar count, not a clock count).

Examples

iex> Orbis.GNSS.Data.Catalog.gps_week(~D[2020-06-24])
2111

protocol(center)

@spec protocol(atom()) ::
  {:ok, :https | :ftp} | {:error, {:unsupported_product, term()}}

The transfer protocol (:https or :ftp) for a center.

station_obs_filename(station, date, sample)

@spec station_obs_filename(String.t(), Date.t(), String.t()) ::
  {:ok, String.t()} | {:error, {:unsupported_product, term()}}

Build the canonical IGS long-name filename for a daily station observation product (RINEX 3 CRINEX), e.g. ESBC00DNK_R_20201770000_01D_30S_MO.crx.

Station observation files are keyed by a 9-character site id, not an analysis-center token, so they have their own builder.

Examples

iex> Orbis.GNSS.Data.Catalog.station_obs_filename("ESBC00DNK", ~D[2020-06-25], "30S")
{:ok, "ESBC00DNK_R_20201770000_01D_30S_MO.crx"}

station_obs_protocol()

@spec station_obs_protocol() :: :ftp

The transfer protocol for the daily station observation archive (:ftp on the ESA GSSC mirror).

station_obs_url(station, date, sample)

@spec station_obs_url(String.t(), Date.t(), String.t()) ::
  {:ok, String.t()} | {:error, {:unsupported_product, term()}}

Build the full, compressed (.gz) archive URL for a daily station observation product on the ESA GSSC anonymous archive (the same daily data tree the broadcast navigation file uses).

Examples

iex> Orbis.GNSS.Data.Catalog.station_obs_url("ESBC00DNK", ~D[2020-06-25], "30S")
{:ok, "ftp://gssc.esa.int/gnss/data/daily/2020/177/ESBC00DNK_R_20201770000_01D_30S_MO.crx.gz"}