Rust library to parse and analyze RINEX files
This library lets you access complex RINEX data easily and build your application around it.
If you are only interested in RINEX data extraction and visualization,
you should rather look into the
RINEX tool that I am working
on, based off this library.
If you are only interested in RINEX file compression and decompression,
you should use the
Hatanaka tool to perform such operations quickly.
For more information on the compression algorithm, the
Hatanaka documentation page
is the where to look at.
RINEX
formatMany RINEX file types exist,
the rinex::types::Type
enum
describes the types of RINEX currently supported:
Type::NavigationMessage
(NAV) messagesType::ObservationData
(OBS) data: compressed
or notType::MeteoData
(Meteo) dataCRINEX V1 and V3 are fully supported.
You can pass compressed OBS RINEX directly to this parser.
Uncompressed record is extracted and can be analyzed directly.
RINEX
files contain a lot of data and this library is capable of parsing all of it.
To fully understand how to operate this lib, refer to the RinexType
section you are interested in.
Link to the official API
RINEX
file production (writer) is work in progress and will be supported
in next release
This parser does not care for the RINEX file name, it is possible to parse a file
that does not respect standard naming conventions.
The Rinex::from_file
method parses a local RINEX
file:
rust
let path = std::path::PathBuf::from("data/NAV/V2/amel0010.21g");
let rinex = rinex::Rinex::from_file(&path).unwrap();
The data/
folder contains short but relevant RINEX files,
spanning almost all revisions and supported types, mainly for CI purposes.
For data analysis and manipulation, you must refer to the official RINEX definition
This interactive portal is also a nice interface to discover or figure things out.
The header
contains high level information.
rust
println!("{:#?}", rinex.header);
This includes Rinex
:
* revision number
* GNSS constellation
* possible file compression infos
* recorder & station infos
* hardware, RF infos
* comments
are exposed in a string array, by order of appearance
* and much more
rust
println!("{:#?}", rinex.header.version);
assert_eq!(rinex.header.constellation, Constellation::Glonass)
println!("{:#?}", rinex.header.crinex)
println!("pgm: \"{}\"", rinex.header.program);
println!("run by: \"{}\"", rinex.header.run_by);
println!("station: \"{}\"", rinex.header.station);
println!("observer: \"{}\"", rinex.header.observer);
println!("{:#?}", rinex.header.leap);
println!("{:#?}", rinex.header.coords);
The Rinex
structure comprises the header
previously defined,
and the record
which contains the data payload.
Record data are always classified by epoch
that means, by sampling timestamps.
The record is naturally sorted from oldest epochs to newest.
A RINEX
file usually spans 24h at a steady sampling interval.
The record
is a complex structure of HashMap (dictionaries)
whose definition varies with the type of RINEX file.
Refer to its definition in the API and the specific documentation down below,
for the type of file you are interested in.
comments
identified in the record are currently dropped out
because they are not considered as epoch
data directly.
In the next release, recods
will be able to expose comments
sorted by epochs
. This will allow complex post-processing,
and also allow the user to determine what happened in case of special
epoch events.
Epoch
objectepoch
is a chrono::NaiveDateTime
object validated by an
EpochFlag
.
A valid epoch is validated with EpochFlag::Ok
, refer to specific API.
To demonstrate how to operate the epoch
API, we'll take
a Navigation Rinex file as an example.
First, let's grab the record:
rust
let rinex = rinex::Rinex::from_file("data/amel0010.21g")
.unwrap();
let record = rinex.record
.as_nav() // NAV record unwrapping
.unwrap();
epochs
serve as keys of the first hashmap.
The keys()
iterator is the easiest way to to determine
which epochs were idenfitied.
Here we are only interested in the .date
field of an epoch
, to determine
the encountered timestamps:
```rust let epochs: Vec<_> = record .keys() // keys interator .map(|k| k.date) // building a key.date vector .collect();
epochs = [ // For RINEX files that do not expose an Epoch Refer to specific
Observation files documentation
to see how filtering using epoch flags is powerful and relevant. Advanced operations like file Merging will be available in next releases. Documentation and example of use for specific Topics to be unlocked by next releasesunique()
to filter epochs
makes no sense - and is not available,
because a valid RINEX exposes a unique data set per epoch
. flag
, like Navigation or Meteo data,
we fix it to Ok
(valid epoch
) by default.Sv
objectSv
for Satellite Vehicule, is also
used to sort and idenfity datasets.
For instance, in a NAV file,
we have one set of NAV data per Sv
per epoch
.Sv
is tied to a rinex::constellation
and comprises an 8 bit
identification number.Useful high level methods
interval
: returns the nominal sampling interval - nominal time difference
between two successive epochs in this record
. See API for more informationsampling_dead_time
: returns a list of epochs
for which time difference
between epoch and previous epoch exceeded the nominal sampling interval.Advanced
RINEX
file operations
Merged RINEX
files will be parsed properly: meaning the record
is built correctly.
Informations on the merging
operation will be exposed either in the .comments
structure,
if "standard" comments were provided.Specific documentation
RINEX
formats Work in progress
to_file
methods
to produce supported RINEX formatsmerging
when producing a new fileContribute