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 way to go.
RINEX
filesMany RINEX file types exist,
the rinex::types::Type
enum
describes the types of RINEX currently supported:
Type::NavigationMessage
(NAV) messagesType::ObservationData
(OBS) dataType::MeteoData
(Meteo) dataRINEX
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
This parser does not check whether the provided local file follows the RINEX naming convention or not. You can parse anything and behavior adapts to the actual file content.
Compressed RINEX (CRINEX)
this lib is not able to decompress CRINEX files at the moment.
you should manually decompress your RINEX files prior attempting
parsing.
In that scenario, rinex.record
is set to None, and
the header is fully parsed.
Weird RINEX content:
this parser is not able to parse the RINEX body if the provided
RINEX content only has a single epoch.
In that scenario, rinex.record
is set to None, and
the header is fully parsed.
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.
Comments
are currently discarded and not exposed by the parser.
rust
println!("{:#?}", rinex.header);
This includes Rinex
:
* revision number
* GNSS constellation
* possible file compression infos
* recorder & station infos
* hardware, RF infos
* 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.
The record is optionnal at the moment to handle CRINEX
Observation Data that we are not able to parse directly.
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.
record
is always first indexed by an epoch
,
that is, data is sorted by the sampling timestamp.
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
.unwrap() // option<record> unwrapping, None in case of CRINEX
.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();
according to hashmap
documentation: .keys()
are randomly exposed (not sorted):
rust
epochs = [ // example
2021-01-01T14:00:00,
2021-01-01T10:00:00,
2021-01-01T05:00:00,
2021-01-01T22:00:00,
...
]
You can use itertools
to enhance the iterator methods and sort them easily:
```rust use itertools::Itertools; let epochs: Vec<_> = record .keys() .sort() // unlocked .collect();
epochs = [ // example 2021-01-01T00:00:00, 2021-01-01T01:00:00, 2021-01-01T03:59:44, 2021-01-01T04:00:00, 2021-01-01T05:00:00, ... ] ```
unique()
filter is not available to hashmaps,
due to the hashmap.insert()
behavior which always overwrites
a previous value for a given key.
It is not needed in our case because, because epochs
are unique,
we will always have a single set of data per epoch.
Keep in mind epochs
are fixed to EpochFlag::Ok
in NAV files.
EpochFlags are provided in OBS files for examples.
Refer to specific
Observation files documentation
to see how filtering using epoch flags is powerful and relevant.
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.
Observation Data documentation