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.
| Code | Center | Protocol | Host |
|---|---|---|---|
:gfz | GFZ Potsdam (operational rapid) | HTTPS | isdc-data.gfz.de |
:cod | CODE / University of Bern (MGEX) | FTP | gssc.esa.int |
:grg | CNES/CLS (MGEX, final) | FTP | gssc.esa.int |
:wum | Wuhan University (MGEX, final) | FTP | gssc.esa.int |
:igs | IGS combined (broadcast / IONEX) | FTP | gssc.esa.int |
:*_ult | IGS/COD/ESA/GFZ/GRG ultra-rapid | FTP | gssc.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 final), and the ultra-rapid center aliases:igs_ult,:cod_ult,:esa_ult,:gfz_ult,:grg_ult. The GFZ rapid token isGFZ0OPSRAP; the MGEX final tokens areCOD0MGXFIN,GRG0MGXFIN,WUM0MGXFIN; the ultra-rapid tokens areIGS0OPSULT,COD0OPSULT,ESA0OPSULT,GFZ0OPSULT, andGRG0OPSULT.:nav— the IGS merged multi-GNSS broadcast navigation file (BRDC00IGS_R_..._MN.rnx). Only:igspublishes it.:ionex— the global ionosphere TEC map (..._GIM.INX).:igsserves the combinedIGS0OPSFINmap;:codservesCOD0OPSFIN. IONEX cadence is sub-daily, so the default sampling is01H/02H, not01D.
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). Ultra-rapid SP3 uses the same
shape with a sub-daily issue time and a two-day span, e.g.
IGS0OPSULT_20242470600_02D_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 full, compressed (.gz) archive URL for a product with an optional
issue time.
Build the canonical IGS long-name filename for a product.
Build the canonical filename for a product with an optional sub-daily issue.
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 (001–366) for a calendar date.
The catalog default sampling code for a center/content pair, when one is known.
The GPS day-of-week for a calendar date (0 = Sunday … 6 = Saturday).
The GPS week number for a calendar date.
Resolve the latest available ultra-rapid issue at or before target.
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).
Return candidate ultra-rapid issues at or before a target epoch, newest first.
Functions
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.
@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"}
@spec archive_url(atom(), atom(), Date.t(), String.t(), String.t() | nil) :: {:ok, String.t()} | {:error, {:unsupported_product, term()}}
Build the full, compressed (.gz) archive URL for a product with an optional
issue time.
Examples
iex> Orbis.GNSS.Data.Catalog.archive_url(:cod_ult, :sp3, ~D[2024-09-03], "05M", "0600")
{:ok, "ftp://gssc.esa.int/gnss/products/2330/COD0OPSULT_20242470600_02D_05M_ORB.SP3.gz"}
@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"}
@spec canonical_filename(atom(), atom(), Date.t(), String.t(), String.t() | nil) :: {:ok, String.t()} | {:error, {:unsupported_product, term()}}
Build the canonical filename for a product with an optional sub-daily issue.
issue is nil for daily products and an HHMM string for ultra-rapid
products. Ultra-rapid centers reject unsupported issue times instead of
silently rounding.
Examples
iex> Orbis.GNSS.Data.Catalog.canonical_filename(:igs_ult, :sp3, ~D[2024-09-03], "15M", "0600")
{:ok, "IGS0OPSULT_20242470600_02D_15M_ORB.SP3"}
Look up a center's static definition.
Returns {:ok, map} or {:error, {:unsupported_product, {:center, code}}}.
Human-readable center name, or nil if the code is unknown.
@spec centers() :: [atom()]
All supported analysis-center codes.
The content-type descriptor (%{code:, ext:, kind:}) for a content type.
Returns {:ok, map} or {:error, {:unsupported_product, {:content, type}}}.
@spec content_types() :: [atom()]
All supported content-type codes.
@spec day_of_year(Date.t()) :: 1..366
The day-of-year (001–366) for a calendar date.
Examples
iex> Orbis.GNSS.Data.Catalog.day_of_year(~D[2020-06-24])
176
@spec default_sample(atom(), atom()) :: {:ok, String.t()} | {:error, {:unsupported_product, term()}}
The catalog default sampling code for a center/content pair, when one is known.
Ultra-rapid centers publish different native orbit cadences (IGS/ESA at
15 minutes, several analysis-center products at 5 minutes), so callers should
prefer this over a global default when building live-latency products.
Examples
iex> Orbis.GNSS.Data.Catalog.default_sample(:igs_ult, :sp3)
{:ok, "15M"}
iex> Orbis.GNSS.Data.Catalog.default_sample(:cod_ult, :sp3)
{:ok, "05M"}
@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
@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
@spec latest_ultra_issue( atom(), NaiveDateTime.t() | DateTime.t(), nil | [%{date: Date.t(), issue: String.t()} | {Date.t(), String.t()}] ) :: {:ok, %{date: Date.t(), issue: String.t()}} | {:error, {:unsupported_product, term()}}
Resolve the latest available ultra-rapid issue at or before target.
available is optional. When provided, it is a list of %{date:, issue:} maps
or {date, issue} tuples representing archive entries known to exist; the
resolver picks the newest candidate present in that set. This keeps network
probing outside the pure catalog while letting the fetch layer fall back from
a missing latest issue to an older one.
Examples
iex> available = [{~D[2024-09-03], "0000"}, {~D[2024-09-03], "0600"}]
iex> Orbis.GNSS.Data.Catalog.latest_ultra_issue(:igs_ult, ~N[2024-09-03 13:00:00], available)
{:ok, %{date: ~D[2024-09-03], issue: "0600"}}
The transfer protocol (:https or :ftp) for a center.
@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"}
@spec station_obs_protocol() :: :ftp
The transfer protocol for the daily station observation archive (:ftp on the
ESA GSSC mirror).
@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"}
@spec ultra_issue_candidates(atom(), NaiveDateTime.t() | DateTime.t()) :: [%{date: Date.t(), issue: String.t()}] | {:error, {:unsupported_product, term()}}
Return candidate ultra-rapid issues at or before a target epoch, newest first.
The returned entries are maps with :date and :issue keys. A previous-day
issue is included when it is the newest product not after the target; this is
required for early UTC hours when the current-day file has not landed yet.
Examples
iex> Orbis.GNSS.Data.Catalog.ultra_issue_candidates(:igs_ult, ~N[2024-09-03 13:15:00]) |> Enum.take(3)
[%{date: ~D[2024-09-03], issue: "1200"}, %{date: ~D[2024-09-03], issue: "0600"}, %{date: ~D[2024-09-03], issue: "0000"}]