Reading a RINEX observation file

An annotated RINEX 3.03 observation file, field by field.

RINEX is a fixed-column format: what a value means depends on which columns it sits in, so the alignment below is not decoration. Everything here is copied byte for byte from a real file — a static occupation recorded on the Washington University campus with an Emlid Reach RS2, in RINEX version 3.03.

The file is named …23O, the short filename convention inherited from RINEX 2.11: 23 is the two-digit year and O is the file type, observation — a capital letter O, not a zero. The same receiver in 2026 writes .26O. RINEX 3.02 and later recommend a longer, self-describing name ending in .rnx, but many receivers still emit the short form.

The header

Everything before END OF HEADER. Columns 61–80 hold a label naming each line. Line numbers at the left are the file's own.

  |         1         2         3         4         5         6         7         8
  |12345678901234567890123456789012345678901234567890123456789012345678901234567890
 1|     3.03           OBSERVATION DATA    M: Mixed            RINEX VERSION / TYPE
 2|Emlid RGL 1.2.5                         20230914 144917 UTC PGM / RUN BY / DATE
 3|format: u-blox, option: -TADJ=0.1                           COMMENT
 4|                                                            MARKER NAME
 5|                                                            MARKER NUMBER
 6|                                                            MARKER TYPE
 7|                                                            OBSERVER / AGENCY
 8|                    EMLID REACH RS2                         REC # / TYPE / VERS
 9|                    EML_REACH_RS2       NONE                ANT # / TYPE
10|   -26509.4860 -4987782.3281  3962072.6564                  APPROX POSITION XYZ
11|        0.0000        0.0000        0.0000                  ANTENNA: DELTA H/E/N
12|G    8 C1C L1C D1C S1C C2X L2X D2X S2X                      SYS / # / OBS TYPES
13|R    8 C1C L1C D1C S1C C2C L2C D2C S2C                      SYS / # / OBS TYPES
14|E    8 C1X L1X D1X S1X C7X L7X D7X S7X                      SYS / # / OBS TYPES
15|J    8 C1C L1C D1C S1C C2X L2X D2X S2X                      SYS / # / OBS TYPES
16|C    8 C2I L2I D2I S2I C7I L7I D7I S7I                      SYS / # / OBS TYPES
17|  2023     9    13    17     7   51.2000000     GPS         TIME OF FIRST OBS
18|  2023     9    14    14    49   35.0000000     GPS         TIME OF LAST OBS
  |...  SYS / PHASE SHIFT (5 lines), GLONASS SLOT / FRQ #, GLONASS COD/PHS/BIS
26|                                                            END OF HEADER
1
Format version and content. M means several constellations in one file.
2
Which program wrote the file, and when — in UTC. Line 18 gives the last observation in GPS time, which since 2017 has run 18 seconds ahead of UTC. So 14:49:35 GPS and 14:49:17 UTC are the same instant: the file was written the moment logging stopped. Mixing the two time systems is one of the easiest ways to lose 18 seconds in a processing chain.
3
Free text. Here it records a u-blox source and -TADJ=0.1, meaning timestamps were snapped to 0.1 s.
4–7
Blank. MARKER NAME and OBSERVER / AGENCY are required by the specification, which asterisks only the optional records; the receiver writes the labels and leaves the values empty. So nothing in the file says what was occupied, or by whom. MARKER TYPE is required only for markers that are neither GEODETIC nor NON_GEODETIC, so leaving it blank on a static monument is legitimate.
8–9
Receiver and antenna model. NONE is the radome code, not a missing value — but note where it sits. ANT # / TYPE is two 20-column fields, antenna number then antenna type, so the radome belongs inside columns 21–40; the specification's own example reads LEIAR25.R4      NONE. This receiver writes it at column 41, outside the field, so a parser reading the columns strictly finds no radome.
10
The receiver's own rough position, in ECEF meters. A PPP service such as NRCan CSRS-PPP calls this the a priori and reports how far its solution moved from it.
11
Height of the antenna reference point above the mark, then east and north offsets, in meters. This is the line you edit before submitting for postprocessing.
12–16
One line per constellation, eight observation types each: G GPS, R GLONASS, E Galileo, J QZSS, C BeiDou.
17–18
Claimed first and last observation. Check both against the first and last epoch records in the file. A header can be stale; the records cannot.
26
Nothing above this line is measurement. Everything below it is.

An epoch record

One per measurement instant, always beginning with >.

> 2023  9 13 17  7 51.4000000  0 18
^ |--- year month day hour min sec ---|  |  ||

> the record identifier, which marks the start of an epoch  ·  0 the epoch flag: zero is a normal epoch, 1 means a power failure since the previous one, and higher values introduce a special event  ·  18 satellites reported at this instant.

Consecutive epochs here are 0.200 s apart, so the receiver logged at 5 Hz. Postprocessing services commonly decimate to one epoch per 30 s before solving.

One satellite's observations

One record per satellite, immediately after its epoch line. This is a single line in the file, 131 columns wide, wrapped here after column 67. Every field is 16 columns: 14 for the value, 2 for flags.

G 5  21704463.402 6 114057724.418 6     -3546.598          39.000
^^^|----C1C-----|f |----L1C-----|f |----D1C-----|f |----S1C-----|f
  21704463.063 6  88876147.817 6     -2762.884          36.000
|----C2X-----|f |----L2X-----|f |----D2X-----|f |----S2X-----|f

^^^ GPS satellite 5.   f two flag columns: loss-of-lock, then a signal-strength indicator from 1 (worst) to 9 (best). A blank field means that observation is missing for this satellite, which is normal.

L1 band

C1C 21 704 463.402 m
L1C 114 057 724.418 cyc
D1C −3 546.598 Hz
S1C 39.000 dB-Hz

L2 band

C2X 21 704 463.063 m
L2X 88 876 147.817 cyc
D2X −2 762.884 Hz
S2X 36.000 dB-Hz

Decoding any code

C pseudorange m
L carrier phase cyc
D Doppler Hz
S strength, C/N0 dB-Hz

First letter: the quantity, above right. Digit: the band — 1 L1, 2 L2, 7 Galileo E5b or BeiDou B2. Final letter: the tracking channel (C, X, I, P). So C1C is a pseudorange on L1, and S1C is signal strength on L1.

Compare C1C with C2X: same satellite, same instant, two frequencies, ranges differing by 0.339 m. Two effects separate them. The ionosphere delays the bands unequally — group delay scales as 1/f², so L2 at 1227.60 MHz is delayed about 1.65 times as much as L1 at 1575.42 MHz — and each signal also carries its own hardware bias in the satellite and the receiver, on top of multipath and noise.

Recording both frequencies is what lets a receiver separate those effects and remove the ionospheric part. A single difference is not itself an ionospheric measurement: across the nine GPS satellites in this one epoch the same quantity runs from −3.2 m to +9.3 m, and the biases, not the ionosphere, set that spread.

Full specification, both from the IGS: rinex303.pdf, the version this file declares, and rinex_4.02.pdf, the current one. Column positions are counted from 1.

GNSS surveying at the lab

The lab runs GNSS control surveys, base-station occupations, and PPK workflows, and postprocesses the results.