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 the archive protocol, host, product token it publishes, and directory layout for each content type. Only centers and paths that resolve against a live, anonymous HTTP(S) 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 | HTTP | ftp.aiub.unibe.ch |
:esa | ESA Navigation Office final products | HTTPS | navigation-office.esa.int |
:igs | IGS combined broadcast navigation | HTTPS | igs.bkg.bund.de |
:igs_ult | IGS combined ultra-rapid | HTTPS | igs.bkg.bund.de |
:cod_ult | CODE ultra-rapid | HTTP | ftp.aiub.unibe.ch |
:esa_ult | ESA ultra-rapid | HTTPS | navigation-office.esa.int |
:gfz_ult | GFZ ultra-rapid | HTTPS | isdc-data.gfz.de |
:cod_rap | CODE rapid global ionosphere map | HTTP | ftp.aiub.unibe.ch |
:cod_prd1 | CODE 1-day predicted ionosphere map | HTTP | ftp.aiub.unibe.ch |
:cod_prd2 | CODE 2-day predicted ionosphere map | HTTP | ftp.aiub.unibe.ch |
AIUB's public CODE archive does not offer HTTPS. CODE products are public data, but fresh products do not have published checksums; transport integrity therefore relies on the plain-HTTP channel.
Products that were only reachable through the deprecated ESA GSSC anonymous
archive and have no verified open HTTP(S) mirror are intentionally not
listed. Requests for those former products return
{:error, {:no_open_mirror, {center, content}}}.
Content types and what each center serves
:sp3,:clk— precise orbits and clocks.:gfz(operational rapid),:codand:esa(final products), and the ultra-rapid center aliases:igs_ult,:cod_ult,:esa_ult, and:gfz_ult. The GFZ rapid token isGFZ0OPSRAP; the CODE MGEX final token isCOD0MGXFIN; the ESA final token isESA0MGNFIN; the ultra-rapid tokens areIGS0OPSULT,COD0OPSULT,ESA0OPSULT, andGFZ0OPSULT.:nav— the IGS merged multi-GNSS broadcast navigation file (BRDC00WRD_R_..._MN.rnx). Only:igspublishes it.:ionex— the global ionosphere TEC map (..._GIM.INX).:codserves the finalCOD0OPSFINover HTTP and:esaservesESA0OPSFINover HTTPS. Lower-latency CODE GIMs live in the AIUB/CODEroot::cod_rapserves the rapid mapCOD0OPSRAP(short nameCORG<ddd>0.<yy>I), and the predicted aliases:cod_prd1(1-day-ahead) and:cod_prd2(2-day-ahead) both serveCOD0OPSPRD(short nameCOPG<ddd>0.<yy>I). The predicted product is published in real time: the 1-day map for a UTC day exists before that day starts, so:cod_prd1resolves for the current/near-future UTC day and:cod_prd2for the day after. AIUB encodes the prediction horizon only in the file'sCOMMENTheader, not the filename; the catalog distinguishes the horizons by the target date the convenience builders offset to. The IGS combined rapid mapIGS0OPSRAPhas no verified open HTTP(S) mirror and stays in@no_open_mirrors. 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
form with a sub-daily issue time and a two-day span, e.g.
IGS0OPSULT_20242470600_02D_15M_ORB.SP3. CODE ultra-rapid SP3 on AIUB is
published as a daily, uncompressed 01D file. Broadcast navigation uses the
RINEX long-name SSSSMRCCC_R_YYYYDDDHHMM_LEN_CNT.fmt with no sampling
field and a lowercase extension (e.g. BRDC00WRD_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 archive compression used for a center/content pair.
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.
Candidate UTC dates for a daily GIM (rapid or predicted) at or before target,
newest first.
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.
Day offset, relative to a target UTC date, that a predicted IONEX alias maps to.
The transfer protocol (:https or :http) 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.
Build the full, compressed (.gz) archive URL for a daily station observation
product on the public BKG IGS observation tree.
Return candidate ultra-rapid issues at or before a target epoch, newest first.
Types
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.
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 a tagged error 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, "https://igs.bkg.bund.de/root_ftp/IGS/BRDC/2020/177/BRDC00WRD_R_20201770000_01D_MN.rnx.gz"}
@spec archive_url(atom(), atom(), Date.t(), String.t(), String.t() | nil) :: {:ok, String.t()} | error()
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[2026-06-11], "05M", "0000")
{:ok, "http://ftp.aiub.unibe.ch/CODE/COD0OPSULT_20261620000_01D_05M_ORB.SP3"}
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 a tagged error 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, "BRDC00WRD_R_20201770000_01D_MN.rnx"}
@spec canonical_filename(atom(), atom(), Date.t(), String.t(), String.t() | nil) :: {:ok, String.t()} | error()
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.
@spec compression(atom(), atom()) :: {:ok, compression()} | error()
The archive compression used for a center/content pair.
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
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(:gfz_ult, :sp3)
{:ok, "05M"}
@spec gim_date_candidates( atom(), Date.t() | NaiveDateTime.t() | DateTime.t(), non_neg_integer() ) :: [Date.t()] | error()
Candidate UTC dates for a daily GIM (rapid or predicted) at or before target,
newest first.
Unlike ultra-rapid SP3, the CODE rapid and predicted GIMs are daily files with no sub-daily issue time, so the latest-available fallback walks the calendar day backward instead of the issue clock. The rapid map lands a day or two late and the predicted map is published ahead of its target day; in both cases the freshest file present may be for a slightly earlier day than first requested, so the fetch layer tries these candidates newest first.
Returns a list of Date values (at most lookback + 1 entries, default
lookback 2), or a tagged error for an unsupported center/content.
Examples
iex> Orbis.GNSS.Data.Catalog.gim_date_candidates(:cod_rap, ~D[2026-06-14])
[~D[2026-06-14], ~D[2026-06-13], ~D[2026-06-12]]
iex> Orbis.GNSS.Data.Catalog.gim_date_candidates(:cod_prd1, ~D[2026-06-14], 1)
[~D[2026-06-14], ~D[2026-06-13]]
@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()
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"}}
Day offset, relative to a target UTC date, that a predicted IONEX alias maps to.
CODE publishes a single predicted product (COD0OPSPRD); the prediction
horizon ("1-DAY", "2-DAY", ... PREDICTED) appears only in the file's COMMENT
header, never the filename. The catalog therefore distinguishes the horizons by
the calendar day each alias targets: :cod_prd1 is the current/near-future day
(offset 0) and :cod_prd2 is the day after (offset +1). Non-predicted
centers return 0.
Examples
iex> Orbis.GNSS.Data.Catalog.predicted_day_offset(:cod_prd1)
0
iex> Orbis.GNSS.Data.Catalog.predicted_day_offset(:cod_prd2)
1
iex> Orbis.GNSS.Data.Catalog.predicted_day_offset(:cod_rap)
0
The transfer protocol (:https or :http) 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() :: :https
The transfer protocol for the daily station observation archive.
@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 public BKG IGS observation tree.
Examples
iex> Orbis.GNSS.Data.Catalog.station_obs_url("WTZR00DEU", ~D[2020-06-25], "30S")
{:ok, "https://igs.bkg.bund.de/root_ftp/IGS/obs/2020/177/WTZR00DEU_R_20201770000_01D_30S_MO.crx.gz"}
@spec ultra_issue_candidates(atom(), NaiveDateTime.t() | DateTime.t()) :: [%{date: Date.t(), issue: String.t()}] | error()
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"}]