RINEX 4.02 Plan¶
This plan scoped the RINEX 4 work before implementation. The original iter1 intent was to record parser state, isolate the RINEX 4 surface area, and add a compile-time skeleton; the completed Phase 1 and Phase 2 slices now add the documented reader behavior incrementally.
Reference Sources¶
- IGS RINEX 4.02 specification: https://files.igs.org/pub/data/format/rinex_4.02.pdf
- IGS release note for RINEX 4.02: https://igs.org/news/rinex-4-02/
- IGS formats page: https://igs.org/formats-and-standards/
- GSI RNXCMP / CompactRINEX note: https://terras.gsi.go.jp/ja/crx2rnx.html
Current Parser State¶
The active reader/writer lives in:
include/libgnss++/io/rinex.hppsrc/io/rinex.cpp
Current behavior is mostly RINEX 2.x and 3.x:
RINEXReader::readHeader()reads fixed 80-column header lines untilEND OF HEADER.parseHeaderLine()detects the file version withstd::stod(line.substr(0, 9)).- File type is inferred from
line[20], with observation, navigation, meteorological, and clock mapped toFileType. - Observation headers support RINEX 2
# / TYPES OF OBSERVand RINEX 3SYS / # / OBS TYPES. - The RINEX 3 observation type parser currently reads only the first line for a system, up to 13 observation types. Continuation lines are not accumulated.
readObservationEpoch()dispatchesversion < 3.0to the RINEX 2 epoch parser and everyversion >= 3.0to the RINEX 3 epoch parser.parseObservationEpochV3()assumes fixed epoch fields: year at columns 2-5, seconds insubstr(18, 13), flag at columns 31-32, and satellite count at columns 33-35.- RINEX 3 satellite observation rows are parsed as one line per satellite, with 16-character observation fields starting at column 4.
readNavigationData()treats everyversion >= 3.0navigation file as a RINEX 3 navigation file.- Navigation record detection expects the first character of an ephemeris data line to be a constellation identifier.
supportsBroadcastNavigationSystem()accepts GPS, GLONASS, Galileo, BeiDou, and QZSS for broadcast navigation parsing. SBAS and NavIC are not accepted.parseNavigationMessage()handles RINEX 2 GPS-style records and RINEX 3 GPS/Galileo/BeiDou/QZSS eight-line Kepler records, plus four-line GLONASS FDMA records.- Header-level RINEX 3
IONOSPHERIC CORRparsing is limited to GPSA/GPSB. RINEXWritercan write simple RINEX 3-style headers and broadcast navigation records, but it has no RINEX 4 data-record header support.
The important conclusion is that RINEX 4 observation files may look close to
RINEX 3, but RINEX 4 navigation files do not fit the current navigation parser.
The current version >= 3.0 branch is not a sufficient RINEX 4 compatibility
story.
RINEX 4.02 Deltas¶
The RINEX 4 line starts at RINEX 4.00:
- Navigation files use a new data record header line beginning with
>. - Navigation record types include
EPH,STO,EOP, andION. - The header line carries a data source and a navigation message type, for
example
LNAV,FDMA,FNAV,INAV,CNAV,CNV1,CNV2,CNV3,L1NV,L1OC, andL3OC. - System records such as
STO,EOP, andIONare first-class data records, not just legacy header fields. - RINEX 4.00 made system-dependent observation code lists required for observation files.
The RINEX 4.02 update adds or clarifies:
- Observation epoch time tagging may carry extra second digits up to picosecond resolution.
- The observation epoch record can include an optional receiver clock offset estimate.
- NavIC L1
L1NVnavigation messages are included. - GLONASS L1OC and L3OC CDMA navigation messages are included.
- Navigation message subtypes are present in the navigation data record header.
- ION subtypes are defined for QZSS and NavIC dual ionosphere models:
QZSS
CNVX WIDE, QZSSCNVX JAPN, NavICL1NV KLOB, and NavICL1NV NEQN.
CompactRINEX / CRINEX is separate from the RINEX syntax itself:
- Hatanaka CompactRINEX is a compressed representation for RINEX observation files.
- GSI RNXCMP advertises conversion for RINEX 2.xx, 3.xx, and 4.xx observation files.
- This repo currently has no native CRINEX reader. Iter2 should treat CRINEX as an input-preparation concern unless a native decompressor is explicitly chosen.
Impact On Current Code¶
Observation file impact:
- Current RINEX 3 epoch parsing will not safely parse picosecond-width seconds, because flag and satellite-count columns are fixed after a 13-character seconds field.
- The parser needs token-based epoch parsing after the
>marker while still preserving optional receiver clock offset parsing. - System observation type continuation lines must be supported before claiming robust RINEX 4 observation support.
- Time precision should be stored as
doublefor now becauseGNSSTime::towis adouble; picosecond text may be parsed but not represented losslessly.
Navigation file impact:
- Current RINEX 4 navigation files will begin records with
> EPH ...,> STO ...,> EOP ..., or> ION ...; the current parser will not treat those lines as ephemeris starts. - Record metadata must be parsed before record-body dispatch.
- Existing ephemeris parsing can be reused for selected
EPHbodies after the RINEX 4 header line is consumed, but the selected message type must be kept in the resulting object or at least respected for line count and time-system interpretation. - STO/EOP/ION require new data containers or an explicit skip policy. Silent misparse is not acceptable.
- NavIC and GLONASS CDMA navigation messages need either new
Ephemerisfields or a generic navigation-message container.
Writer impact:
- RINEX 4 navigation writing must emit data record header lines.
- Observation writing must preserve the expanded epoch second precision and the optional receiver clock offset field.
- Writer support can lag reader support; it should not block initial reader acceptance.
Proposed Skeleton Ownership¶
The new files introduced in iter1 are:
include/libgnss++/io/rinex4.hppsrc/io/rinex4.cpp
They should remain small and separate until the compatibility boundary is
clear. The existing RINEXReader should remain the production path for
RINEX 2/3. RINEX 4 additions can later be folded into RINEXReader once the
tests prove that no RINEX 2/3 behavior regressed.
Roadmap¶
Phase 0, iter1 foundation:
- Add empty RINEX 4 skeleton files and compile them.
- Document current parser assumptions.
- Do not change behavior in
src/io/rinex.cpp.
Phase 1, reader detection and safe dispatch:
- Add explicit
isRinex4()checks rather than relying onversion >= 3.0. - Add tests with minimal RINEX 4.02 observation and navigation fixtures.
- For RINEX 4 navigation records, parse the
>data record header and route unsupported record types to a deliberate skip path. - Keep RINEX 2/3 tests unchanged.
Phase 2, observation support:
- Replace fixed-column epoch parsing with token-based parsing for RINEX 4 epoch lines.
- Preserve optional receiver clock offset estimate when present.
- Handle expanded second precision text without shifting flag/count parsing.
- Accumulate
SYS / # / OBS TYPEScontinuation lines. - Add CRINEX policy tests that verify
.crx/.crx.gzis rejected with a clear message or preprocessed through an external converter.
DONE in the Phase 2 observation slice. RINEX 4 epoch metadata is parsed
token-wise, including the Table A3 receiver-clock and optional five-digit
picosecond extension fields. GPS and QZSS rows reuse the RINEX 3 selection
policy, continuation headers are covered, and event/cycle-slip records are
consumed before scanning for the next normal epoch. Event records do not
produce ObservationData epochs, while a valid flag 0/1 epoch with zero
satellites is returned as an intentionally empty epoch; malformed or
truncated normal records fail instead of returning partial data. Native
CRINEX decompression remains out of scope and matching suffixes are rejected
explicitly.
Phase 3, EPH navigation metadata and compatible-body support (DONE):
- Parse RINEX 4
EPHdata record headers and preserve typed message provenance onEphemeris. DONE in the Phase 3 slice. - Reuse current GPS/Galileo/BeiDou/QZSS/GLONASS FDMA bodies where compatible. DONE for the compatible message types; global ephemeris selection is unchanged.
- Reject nonblank EPH subtypes and trailing header tokens, skip unsupported
EPH bodies at the next
>boundary, and validate Galileo FNAV/INAV clock source bits. DONE in the Phase 3 slice.
Phase 4, system data records (DONE):
- Add internal containers for
STO,EOP, andION. DONE in the Phase 4 slice as a RINEX-specificRINEXReadersidecar, leaving the existingNavigationDataselection and legacy ionosphere model unchanged. - Parse the Table A33/A34
STO/EOPrecords and the A35--A40 Klobuchar, NeQuick-G, BDGIM, NavIC KLOB/NEQN, and GLONASS CDMA ION models with strict fixed-field validation. DONE in the Phase 4 slice. - Reject unsupported message/type/subtype combinations and malformed bodies
transactionally while preserving the next
>boundary. TheLEGSTO spelling is retained only for the explicit RINEX 4.02 Table A41 example; Table 21 remains the normative message-type matrix.
Phase 5, new navigation messages:
- Implement GPS/QZSS CNAV/CNV2, BeiDou CNV1/CNV2/CNV3, and SBAS navigation body models.
- Implement NavIC L1
L1NVparsing. - Add conformance fixtures from public RINEX 4.02 files before enabling these records in production flows.
Phase 5a, GLONASS CDMA navigation (DONE):
- Parse
> EPH R.. L1OC/L3OCA16/A17 bodies with a strict, transactional nine-line parser, including required integer/range checks and the required 19-character blank spare field (A19 format). - Preserve the typed message provenance and optional CDMA payload on
Ephemeris; map the broadcast state vector from km to metres and convert UTCtoc/t_tmto GPST with nearest-week handling. - Keep the existing GLONASS FDMA path and ephemeris selection policy unchanged;
malformed CDMA records recover at the next explicit
>boundary.
The remaining Phase 5 work is GPS/QZSS CNAV/CNV2, BeiDou CNV1/CNV2/CNV3, SBAS,
and NavIC L1NV body models. Those records remain deliberately unsupported.
Phase 6, writer support:
- Emit RINEX 4 navigation data record headers.
- Add writer round-trip tests only after reader behavior is stable.
End-State Acceptance Gates¶
These are end-state gates for the complete roadmap, not just the current safe-dispatch and observation slices. The reader gates completed by Phases 1 and 2 are marked by the corresponding status sections below.
- Existing RINEX 2/3 tests continue to pass.
- RINEX 4 observation fixture parses header, epoch time, satellite count, and at least GPS/QZSS observation rows. MET by Phase 2.
- RINEX 4 navigation fixture with
> EPH G.. LNAVparses at parity with the equivalent RINEX 3 record. - Supported RINEX 4
STO/EOP/IONrecords are retained in the reader sidecar; unsupported records fail or skip deterministically with diagnostics. - CRINEX input policy is explicit and tested.
Safe-Dispatch, Observation, and System-Record Slice Non-Goals¶
- No complete RINEX 4 observation model: event metadata is intentionally not
retained in
ObservationData, and cycle-slip records are consumed rather than exposed. - No native CRINEX decompressor.
- No GPS/QZSS CNAV/CNV2, BeiDou CNV1/CNV2/CNV3, SBAS, or NavIC L1NV EPH body implementation; GLONASS CDMA L1OC/L3OC is covered by Phase 5a.
- No writer behavior changes.
- No conversion of system-record epochs into a common GPS time scale or application of the parsed system data to navigation selection.
Phase 1 Implementation Status: DONE¶
The safe-dispatch slice is implemented in the reader:
RINEXReader::isRinex4()identifies versions 4.00 through 4.99 explicitly; RINEX 2 and 3 continue through their existing paths.- RINEX 4 observation epochs are dispatched to the dedicated Phase 2 parser; RINEX 2 and 3 continue through their existing paths.
- RINEX 4 navigation data-record headers are parsed in
rinex4.hpp/cpp. Compatible GPS/QZSS LNAV, GLONASS FDMA, Galileo FNAV/INAV, and BeiDou D1/D2 EPH bodies reuse the existing ephemeris parser; GLONASS CDMA L1OC/L3OC uses the dedicated Phase 5a parser. - STO/EOP/ION records and unsupported EPH message types are skipped at the
next
>record boundary with diagnostics, preventing silent body misparse.
Phase 2 Implementation Status: DONE¶
The observation slice now handles token-based RINEX 4 epoch fields, fixed Table A3 receiver-clock offsets, optional extra second digits, GPS/QZSS observation rows, continuation headers, deterministic event/cycle-slip skipping (with zero-satellite normal epochs preserved), strict truncation checks, and explicit CRINEX suffix rejection. Event metadata retention, richer cycle-slip data models, and the remaining new EPH navigation message models remain deferred as listed above.
Phase 4 Implementation Status: DONE¶
RINEX 4 STO, EOP, and ION records are parsed into the
RINEXReader::rinex4SystemData() sidecar. The implementation covers the
Table A33/A34 fixed records and the A35--A40 ION payload models, validates
system/message/subtype combinations and numeric/date fields, and appends only
complete records. Unsupported or malformed records are diagnosed and
discarded at the next explicit > boundary without changing RINEX 2/3 or
legacy NavigationData behavior.