rinex 0.0.12

Package to parse and analyze RINEX data
Documentation
# RINEX 
Rust package to parse and analyze Rinex files

[![Rust](https://github.com/gwbres/rinex/actions/workflows/rust.yml/badge.svg)](https://github.com/gwbres/rinex/actions/workflows/rust.yml)
[![crates.io](https://img.shields.io/crates/v/rinex.svg)](https://crates.io/crates/rinex)
[![crates.io](https://img.shields.io/crates/d/rinex.svg)](https://crates.io/crates/rinex)
[![codecov](https://codecov.io/gh/gwbres/rinex/branch/main/graph/badge.svg)](https://codecov.io/gh/gwbres/rinex)

Many RINEX file types exist, 
the `RinexType` enum (refer to API) 
describes the types of RINEX currently supported:

* `RinexType::NavigationMessage` (NAV) messages
* `RinexType::ObservationData` (OBS) data
* `RinexType::MeteoData` (Meteo) data

`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](https://docs.rs/rinex/0.0.10/rinex/)

### Supported RINEX revisions

* 2.00 ⩽ v < 4.0    Tested 
*             v = 4.0    should work, not garanteed at the moment

## Getting started 

Use ``Rinex::from_file`` to parse a local `RINEX` file:

```rust
let path = std::path::PathBuf::from("amel0010.21g");
let rinex = rinex::Rinex::from_file(&path).unwrap();
```

The `data/` folder contains a bunch of `RINEX` files, spanning almost all revisions
and all supported file types, mainly
for CI purposes: you can refer to them.

For data analysis and manipulation, you must refer to the
[official RINEX definition](https://files.igs.org/pub/data/format/)

This [interactive portal](https://gage.upc.edu/gFD/) 
is also a nice interface to
discover or figure things out. 

### Header & general information

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);
```

## RINEX record

The `Rinex` structure comprises the header previously defined,
and the `Record` which contains the payload data.

The `Record` is optionnal at the moment and 
set to _None_ in case of CRINEX Observation Data,
as this lib is not able to decompress the file content, therefore
unable to fully parse it. The header is still parsed though.

RINEX records uses hashmap collections (_dictionnaries_) 
to expose the data,
firstly indexed by `epoch` and secondly indexed by `sv` (Satellite Vehicule)
in case of NAV and OBS data.

Refer to the `Record` enum signature in the API, for the type of record you are
interested in.

`epoch` is simply an alias of the `chrono::NaiveDateTime` structure,
thefore all of its methods are available.

To grab the rinex record from a parsed file, in the example
of a NAV file, one can do:

```rust
let rinex = rinex::Rinex::from_file("navigation-file.rnx")
  .unwrap();
let record = rinex.record
  .unwrap() // option<record>, to still work in CRINEX context
    .as_nav() // NAV record example
    .unwrap(); // as_nav() expected to succeed
```

`epochs` serve as keys of the first hashmap. 
The `keys()` iterator is then the easiest way to to determine
which _epochs_ were idenfitied:

```rust
   let epochs: Vec<_> = record
    .keys() // keys interator
    .collect();
```

according to `hashmap` documentation: `.keys()` are exposed randomly/unsorted:

```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 hashmap,
due to `hashmap.insert()` behavior which always overwrites
a previous value for a given key.
 
It is not needed in our case because:
* `epochs` are unique, we only have one set of data per epoch
* `sv` is tied to an epoch, therefore a previous set of data for that
particular vehicule is stored at another epoch 

## Data payload

Data payload are described in the specific documentation pages down below,
for each supported RINEX files.
In any case, they are encapsulated in the `ComplexEnum` enum,
which wraps:

* "f32": unscaled float value
* "f64": unscaled double precision
* "str": raw string value
* "u8": 8 bit value

Refer to the `ComplexEnum` API and following detailed examples.

## Navigation Data

Refer to related API and
[Navigation Data documentation](https://github.com/gwbres/rinex/blob/main/doc/navigation.md)

## Observation Data

Refer to related API and
[Observation Data documentation](https://github.com/gwbres/rinex/blob/main/doc/observation.md)

## GNSS Time specific operations

wip

## Meteo Data

wip

## Clocks data

wip