RINEX 3 and RINEX 2 reader and batch conversion to NetCDF4 / HDF5 in Python or Matlab. Batch converts NAV and OBS GPS RINEX (including Hatanaka compressed OBS) data into xarray.Dataset for easy use in analysis and plotting. This gives remarkable speed vs. legacy iterative methods, and allows for HPC / out-of-core operations on massive amounts of GNSS data. GeoRinex has over 125 unit tests driven by Pytest.
Pure compiled language RINEX processors such as within Fortran NAPEOS give perhaps 2x faster performance than this Python program--that's pretty good for a scripted language like Python! However, the initial goal of this Python program was to be for one-time offline conversion of ASCII (and compressed ASCII) RINEX to HDF5/NetCDF4, where ease of cross-platform install and correctness are primary goals.
io.StringIOtext stream RINEX'
zlibcompression. This yields orders of magnitude speedup in reading/converting RINEX data and allows filtering/processing of gigantic files too large to fit into RAM.
Latest stable release:
pip install georinex
Current development version:
git clone https://github.com/geospace-code/georinex cd georinex python -m pip install -e .
The Hatanaka CRINEX converter automatically compiles if needed and a C compiler is available.
It can be useful to check the setup of your system with:
python -m pytest
158 passed, 1 skipped
The simplest command-line use is through the top-level
Normally you'd use the
-p option with single files to plot, if not converting.
in this example, the suffix
rnx2hdf5 ~/data "*o" -o ~/data
.ncis appended to the original RINEX filename:
By default all plots and status messages are off, unless using the
-p option to save processing time.
It's suggested to save the GNSS data to NetCDF4 (a subset of HDF5) with the
as NetCDF4 is also human-readable, yet say 1000x faster to load than RINEX.
You can also of course use the package as a python imported module as in the following examples. Each example assumes you have first done:
import georinex as gr
Uses speculative time preallocation
gr.load(..., fast=True) by default.
georinex_read -strict to fall back to double-read strict (slow) preallocation.
Please open a GitHub issue if this is a problem.
Time bounds can be set for reading -- load only data between those time bounds:
--tlim start stop
stop are formatted like
dat = gr.load('my.rnx', tlim=['2017-02-23T12:59', '2017-02-23T13:13'])
Further speed increase can arise from reading only wanted measurements:
--meas C1C L1C
dat = gr.load('my.rnx', meas=['C1C', 'L1C'])
By default, the SSI and LLI (loss of lock indicators) are not loaded to speed up the program and save memory.
If you need them, the
-useindicators option loads SSI and LLI for OBS 2/3 files.
This convenience function reads any possible format (including compressed, Hatanaka) RINEX 2/3 OBS/NAV or
obs = gr.load('tests/demo.10o')
Print start, stop times and measurement interval in a RINEX OBS or NAV file:
Print start, stop times and measurement interval for all files in a directory:
TimeRinex ~/data *.rnx
Get vector of
datetime.datetime in RINEX file:
times = gr.gettimes('~/my.rnx')
If you desire to specifically read a RINEX 2 or 3 OBS file:
obs = gr.load('tests/demo_MO.rnx')
This returns an xarray.Dataset of data within the .XXo observation file.
NaN is used as a filler value, so the commands typically end with .dropna(dim='time',how='all') to eliminate the non-observable data vs time. As per pg. 15-20 of RINEX 3.03 specification, only certain fields are valid for particular satellite systems. Not every receiver receives every type of GNSS system. Most Android devices in the Americas receive at least GPS and GLONASS.
To get a
dict() of the RINEX file header:
hdr = gr.rinexheader('myfile.rnx')
assume the OBS data from a file is loaded in variable
Select satellite(s) (here,
Pick any parameter (say,
L1) across all satellites and time (or index via
.sel() by time and/or satellite too) by:
Indexing only a particular satellite system (here, Galileo) using Boolean indexing.
import georinex as gr obs = gr.load('myfile.o', use='E')
would load only Galileo data by the parameter E.
ReadRinex allow this to be specified as the -use command line parameter.
If however you want to do this after loading all the data anyway, you can make a Boolean indexer
Eind = obs.sv.to_index().str.startswith('E') # returns a simple Numpy Boolean 1-D array Edata = obs.isel(sv=Eind) # any combination of other indices at same time or before/after also possible
Plot for all satellites L1C:
from matplotlib.pyplot import figure, show ax = figure().gca() ax.plot(obs.time, obs['L1C']) show()
Suppose L1C pseudorange plot is desired for
If you desire to specifically read a RINEX 2 or 3 NAV file:
nav = gr.load('tests/demo_MN.rnx')
This returns an
xarray.Dataset of the data within the RINEX 3 or RINEX 2 Navigation file.
Indexed by time x quantity
assume the NAV data from a file is loaded in variable
Select satellite(s) (here,
Pick any parameter (say,
M0) across all satellites and time (or index by that first) by:
A significant reason for using
xarray as the base class of GeoRinex is that big data operations are fast, easy and efficient.
It's suggested to load the original RINEX files with the
use= option to greatly speed loading and conserve memory.
A copy of the processed data can be saved to NetCDF4 for fast reloading and out-of-core processing by:
georinex.__init.py__ shows examples of using compression and other options if desired.
Please see documentation for
xarray.merge for more details.
Assuming you loaded OBS data from one file into
obs1 and data from another file into
obs2, and the data needs to be concatenated in time:
obs = xarray.concat((obs1, obs2), dim='time')
xarray.concatoperation may fail if there are different SV observation types in the files.
you can try the more general:
obs = xarray.merge((obs1, obs2))
APPROX LOCATION XYZ gives ECEF location in RINEX OBS files, this is OPTIONAL for moving platforms.
If available, the
location is written to the NetCDF4 / HDF5 output file on conversion.
To convert ECEF to Latitude, Longitude, Altitude or other coordinate systems, use
Read location from NetCDF4 / HDF5 file can be accomplished in a few ways:
georinex_locscript, which loads and plots all RINEX and .nc files in a directory
obs = xarray.open_dataset('my.nc) ecef = obs.position latlon = obs.position_geodetic # only if pymap3d was used
with h5py.File('my.nc') as f: ecef = h['OBS'].attrs['position'] latlon = h['OBS'].attrs['position_geodetic']
Although Pandas DataFrames are 2-D, using say
df = nav.to_dataframe() will result in a reshaped 2-D DataFrame.
Satellites can be selected like
df.loc['G12'].dropna(0, 'all') using the usual
Pandas Multiindexing methods.
An Intel Haswell i7-3770 CPU with plain uncompressed RINEX 2 OBS processes in about:
This processing speed is about within a factor of 2 of compiled RINEX parsers, with the convenience of Python, Xarray, Pandas and HDF5 / NetCDF4.
OBS2 and NAV2 currently have the fast pure Python read that has C-like speed.
OBS3 / NAV3 are not yet updated to new fast pure Python method.
Done on 5 year old Haswell laptop:
time georinex_read tests/CEDA00USA_R_20182100000_23H_15S_MO.rnx.gz -u E
real 48.6 s
time georinex_read tests/CEDA00USA_R_20182100000_23H_15S_MO.rnx.gz -u E -m C1C
real 17.6 s
conda install line_profiler
%load_ext line_profiler %lprun -f gr.obs3._epoch gr.load('tests/CEDA00USA_R_20182100000_23H_15S_MO.rnx.gz', use='E', meas='C1C')
np.genfromtxt() is consuming about 30% of processing time, and
xarray.concat and xarray.Dataset
nested insideconcat` takes over 60% of time.
With the GNSS constellations in 2018, per the Trimble Planner the min/max visible SV would be about:
UNAVCO site map: identify the 4-letter callsign of a station, and look in the FTP sites below for data from a site.
UNAVCO RINEX 3 data:
UNAVCO RINEX 2 data:
The compressed Hatanaka
.crx.gz files are supported seamlessly via
crx2rnx as noted in the Install section.
There are distinct from the supported
.zip RINEX files.
Hatanaka, Y. (2008), A Compression Format and Tools for GNSS Observation Data, Bulletin of the Geospatioal Information Authority of Japan, 55, 21-30. (available at http://www.gsi.go.jp/ENGLISH/Bulletin55.html)