Skip to content

Commit

Permalink
Develop (#194)
Browse files Browse the repository at this point in the history
* plot brdc clock corrections
* take sp3 into account in clock state too
* bump rtk to v0.4.0
* run linter
* bump cggtts to v4.1.0
* antex: defining support of ATX files

  * differentiate between SV and RX antennas
  * correct possible reference antenna
  * improved PCV description and API
  * introduce dedicated browsing and data extraction methods
    by means of a dedicated crate features

* ignore patch files
* improved rtk and positioning definitions
* high level atx methods
* working on apc coordinates
* remove rinex from crate dependencies
* calibration validity parsing
* test new features
* prepare rinex 0.15.2
* clippy
* linter
* Introduce Channel Number Pseudo Observable

  * although we do not propose functionnalities
    tied to this Pseudo Observable, this will allow
    tolerating their presence and not crash

* run linter
* RINEX3: IONOSPHERIC CORR header support

  * support the special header, in RINEX3
   to describe the ionospheric correction to apply over a 24H time frame

* advanced in iono corr visualization
* iono corr visualization solution
* post processing -p: fix default configuration to be used
* post processing -p: possible ionod correction (RINEX3) case
* simplify code combinations
* update documentation
* add new test resource

---------

Signed-off-by: Guillaume W. Bres <[email protected]>
  • Loading branch information
gwbres authored Dec 26, 2023
1 parent d5a3401 commit 980cdf3
Show file tree
Hide file tree
Showing 56 changed files with 1,714 additions and 1,014 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Cargo.lock
*.html
*.swp
*.swo
*.patch
**/*.rs.bk
.DS_Store

Expand Down
67 changes: 36 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,50 @@ RINEX

Rust tool suites to parse, analyze and process [RINEX Data](https://en.wikipedia.org/wiki/RINEX).

Our Wiki contains [several tutorials and applications](https://github.com/georust/rinex/wiki): it will get you started quickly.
The [Wiki pages](https://github.com/georust/rinex/wiki) contain all the documentation of this project, including several examples spanning different applications of GNSS.

For any question or problems you may experience:

- open a new issue
- drop us a message [on Discord](https://discord.gg/Fp2aape)
If you have any question or experience any problems, feel free to open an issue on Github.
You can also contact us [on our Discord channel](https://discord.gg/Fp2aape)

## Advantages :rocket:

- Fast
- Fast :crab:
- Open sources
- Native Hatanaka decompression and compression
- Seamless .gzip decompression with `flate2` compilation feature
- RINEX V4 full support, that includes modern Navigation messages
- Seamless Hatanaka compression and decompression
- Seamless Gzip decompression with `flate2` build option
- RINEX V4 full support
- Meteo RINEX full support
- IONEX (2D) support, partial 3D support
- IONEX 2D support. Partial IONEX 3D support.
- Clock RINEX partial support: to be concluded soon
- File merging, splitting and pre processing
- Modern constellations like BeiDou, Galileo and IRNSS
- Supported time scales are GPST, BDT, GST, UTC
- Several pre processing operations:
- File merging
- Time beaning
- Filtering..
- Several post processing operations
- All modern GNSS constellations
- Modern GNSS codes and signals
- Time scales: GPST, BDT, GST, UTC
- Supports many SBAS, refer to online documentation
- Full support of Military codes : if you're working with such signals you can
at least run a -qc analysis, and possibly the position solver once it is merged
- Supports high precision RINEX (scaled phase data with micro cycle precision)
- RINEX post processing like SNR, DCB analysis, Broadcast ephemeris interpolation,
high precision orbit interpolation (SP3)..
- RINEX-qc: statistical analysis that you can request in the "cli" application directly.
Analysis can run on modern GNSS signals and SP3 high precision data.
Emulates "teqc" historical application.
- An SPP/PPP position solver (under development), in the form of the "gnss-rtk" library that you can
summon from the "cli" application directly.

## Known weaknesses :warning:

- QZNSST is represented as GPST at the moment
- GLONASST and IRNSST are not supported : calculations (mostly orbits) will not be accurate
- The command line tool does not accept BINEX or other proprietary formats
- File production is not fully concluded to this day, some formats are still not correctly supported
(mostly NAV).
- High precision RINEX (carrier phase micro cycle precision)
- High precision orbit support (SP3)
- Quality Check (QC): file quality and statistical analysis to help precise positioning
(historical `teqc` function).
- SPP: Single Point Positioning
- PPP: Precise Point Positioning is work in progress :warning:

## Disadvantages :warning:

- QZNSST is represented as GPST at the moment.
- We're waiting for Hifitime V4 to support GLONASST and IRNSST.
Until then, orbital calculations on these systems are not feasible.
In other term, positioning is not feasible and you're limited to basic analysis.
- These tools are oriented towards the latest revisions of the RINEX format.
RINEX4 is out and we already support it.
Some minor features in the RINEX2 or 3 revisions may not be supported.
- Our command line applications do not accept BINEX or other proprietary formats
- File production is not fully concluded to this day. We're currently focused
on RINEX post processing rather than RINEX data production. Do not hesitate to fork and submit
your improvements

## Architecture

Expand Down
4 changes: 2 additions & 2 deletions crx2rnx/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "crx2rnx"
version = "2.2.0"
version = "2.2.1"
license = "MIT OR Apache-2.0"
authors = ["Guillaume W. Bres <[email protected]>"]
description = "RINEX data decompressor"
Expand All @@ -12,4 +12,4 @@ readme = "README.md"

[dependencies]
clap = { version = "4.4.10", features = ["derive", "color"] }
rinex = { path = "../rinex", version = "=0.15.1", features = ["serde"] }
rinex = { path = "../rinex", version = "=0.15.2", features = ["serde"] }
16 changes: 7 additions & 9 deletions crx2rnx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn workspace(cli: &Cli) -> PathBuf {
}

fn create_workspace(path: &PathBuf) {
std::fs::create_dir_all(&path).unwrap_or_else(|_| {
std::fs::create_dir_all(path).unwrap_or_else(|_| {
panic!(
"failed to create workspace \"{}\": permission denied",
path.to_string_lossy(),
Expand All @@ -39,7 +39,7 @@ fn input_name(path: &PathBuf) -> String {
}

// deduce output name, from input name
fn output_filename<'a>(stem: &'a str, path: &PathBuf) -> String {
fn output_filename(stem: &str, path: &PathBuf) -> String {
let filename = path
.file_name()
.expect("failed to determine input file name")
Expand All @@ -52,14 +52,12 @@ fn output_filename<'a>(stem: &'a str, path: &PathBuf) -> String {
.expect("failed to determine output file name")
.replace("crx", "rnx")
.to_string()
} else if filename.ends_with('d') {
filename.replace('d', "o").to_string()
} else if filename.ends_with('D') {
filename.replace('D', "O").to_string()
} else {
if filename.ends_with('d') {
filename.replace('d', "o").to_string()
} else if filename.ends_with('D') {
filename.replace('D', "O").to_string()
} else {
format!("{}.rnx", stem)
}
format!("{}.rnx", stem)
}
}

Expand Down
6 changes: 3 additions & 3 deletions rinex-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ horrorshow = "0.8"
clap = { version = "4.4.10", features = ["derive", "color"] }
hifitime = { version = "3.8.4", features = ["serde", "std"] }
gnss-rs = { version = "2.1.2" , features = ["serde"] }
rinex = { path = "../rinex", version = "=0.15.1", features = ["full"] }
rinex-qc = { path = "../rinex-qc", version = "=0.1.6", features = ["serde"] }
rinex = { path = "../rinex", version = "=0.15.2", features = ["full"] }
rinex-qc = { path = "../rinex-qc", version = "=0.1.7", features = ["serde"] }
sp3 = { path = "../sp3", version = "=1.0.6", features = ["serde", "flate2"] }
serde = { version = "1.0", default-features = false, features = ["derive"] }

Expand All @@ -41,6 +41,6 @@ plotly = "0.8.4"
# plotly = { git = "https://github.com/gwbres/plotly", branch = "density-mapbox" }

# solver
gnss-rtk = { version = "0.4.0", features = ["serde"] }
gnss-rtk = { version = "0.4.1", features = ["serde"] }
# gnss-rtk = { git = "https://github.com/rtk-rs/gnss-rtk", branch = "develop", features = ["serde"] }
# gnss-rtk = { path = "../../rtk-rs/gnss-rtk", features = ["serde"] }
3 changes: 3 additions & 0 deletions rinex-cli/config/rtk/gpst_10sv_basic.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"max_sv": 10,
"min_sv_elev": 20.0,
"min_snr": 20.0,
"solver": {
"gdop_threshold": 3.0
},
"modeling": {
"sv_clock_bias": true,
"sv_total_group_delay": true,
Expand Down
2 changes: 1 addition & 1 deletion rinex-cli/src/analysis/sampling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rinex::prelude::RnxContext;
* Sampling histogram
*/
pub fn histogram(ctx: &RnxContext, plot_ctx: &mut PlotContext) {
plot_ctx.add_cartesian2d_plot("Sampling Histogram", "Count");
plot_ctx.add_timedomain_plot("Sampling Histogram", "Count");
if let Some(data) = ctx.obs_data() {
let histogram = data.sampling_histogram().sorted();
let durations: Vec<_> = histogram.clone().map(|(dt, _)| dt.to_string()).collect();
Expand Down
57 changes: 11 additions & 46 deletions rinex-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ use std::str::FromStr;
use rinex::prelude::*;
use rinex_qc::QcOpts;

use gnss_rtk::prelude::{Config, Mode as SolverMode};
use gnss_rtk::prelude::Config;

pub struct Cli {
/// Arguments passed by user
matches: ArgMatches,
}

impl Default for Cli {
fn default() -> Self {
Self::new()
}
}

impl Cli {
/// Build new command line interface
pub fn new() -> Self {
Expand Down Expand Up @@ -238,36 +244,12 @@ The summary report by default is integrated to the global HTML report."))
.action(ArgAction::SetTrue)
.help("Activates QC mode and disables all other features: quickest qc rendition."))
.next_help_heading("Positioning")
.arg(Arg::new("spp")
.long("spp")
.conflicts_with("ppp")
.conflicts_with("lsqspp")
.action(ArgAction::SetTrue)
.help("Enable Single Point Positioning.
Use with ${RUST_LOG} env logger for more information.
Refer to the positioning documentation."))
.arg(Arg::new("lsqspp")
.long("lsqspp")
.conflicts_with("ppp")
.conflicts_with("spp")
.action(ArgAction::SetTrue)
.help("Recursive Weighted Least Square SPP strategy.
Use with ${RUST_LOG} env logger for more information.
Refer to the positioning documentation."))
.arg(Arg::new("ppp")
.long("ppp")
.conflicts_with("spp")
.conflicts_with("lsqspp")
.arg(Arg::new("positioning")
.short('p')
.action(ArgAction::SetTrue)
.help("Enable Precise Point Positioning.
.help("Activate positioning mode. Disables all other modes.
Use with ${RUST_LOG} env logger for more information.
Refer to the positioning documentation."))
.arg(Arg::new("pos-only")
.long("pos-only")
.short('p')
.action(ArgAction::SetTrue)
.help("Disable context analysis and run position solver only.
This is the most performant mode to solve a position."))
.arg(Arg::new("config")
.long("cfg")
.short('c')
Expand Down Expand Up @@ -453,25 +435,8 @@ Primary RINEX was either loaded with `-f`, or is Observation RINEX loaded with `
pub fn quiet(&self) -> bool {
self.matches.get_flag("quiet")
}
/* returns RTK solver mode to implement */
pub fn solver_mode(&self) -> Option<SolverMode> {
if self.matches.get_flag("spp") {
Some(SolverMode::SPP)
} else if self.matches.get_flag("lsqspp") {
Some(SolverMode::LSQSPP)
} else if self.matches.get_flag("ppp") {
Some(SolverMode::PPP)
} else {
None
}
}
pub fn positioning(&self) -> bool {
self.matches.get_flag("spp")
|| self.matches.get_flag("lsqspp")
|| self.matches.get_flag("ppp")
}
pub fn positioning_only(&self) -> bool {
self.matches.get_flag("pos-only")
self.matches.get_flag("positioning")
}
pub fn gpx(&self) -> bool {
self.matches.get_flag("gpx")
Expand Down
2 changes: 1 addition & 1 deletion rinex-cli/src/identification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ struct SSIReport {

fn report_sampling_histogram(data: &Vec<(Duration, usize)>) {
let data: HashMap<String, usize> = data
.into_iter()
.iter()
.map(|(dt, pop)| (dt.to_string(), *pop))
.collect();
println!("{:#?}", data);
Expand Down
23 changes: 8 additions & 15 deletions rinex-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ pub(crate) fn context_stem(ctx: &RnxContext) -> String {
*/
pub fn workspace_path(ctx: &RnxContext, cli: &Cli) -> PathBuf {
match cli.workspace() {
Some(w) => Path::new(w).join(&context_stem(ctx)),
Some(w) => Path::new(w).join(context_stem(ctx)),
None => Path::new(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join("WORKSPACE")
.join(&context_stem(ctx)),
.join(context_stem(ctx)),
}
}

Expand Down Expand Up @@ -133,7 +133,7 @@ fn build_context(cli: &Cli) -> RnxContext {
* Returns true if Skyplot view if feasible and allowed
*/
fn skyplot_allowed(ctx: &RnxContext, cli: &Cli) -> bool {
if cli.quality_check_only() || cli.positioning_only() {
if cli.quality_check_only() || cli.positioning() {
/*
* Special modes: no plots allowed
*/
Expand Down Expand Up @@ -240,12 +240,7 @@ pub fn main() -> Result<(), Error> {
let qc_only = cli.quality_check_only();
let qc = cli.quality_check() || qc_only;

let positioning_only = cli.positioning_only();
let positioning = cli.positioning() || positioning_only;

if !positioning {
warn!("position solver currently turned off");
}
let positioning = cli.positioning();

// Initiate plot context
let mut plot_ctx = PlotContext::new();
Expand Down Expand Up @@ -337,7 +332,7 @@ pub fn main() -> Result<(), Error> {
None => String::from("merged.rnx"),
};

let path = workspace.clone().join(&filename);
let path = workspace.clone().join(filename);

let path = path
.as_path()
Expand Down Expand Up @@ -406,10 +401,8 @@ pub fn main() -> Result<(), Error> {
let ground_pos = ctx.ground_position().unwrap(); // infaillible
plot::skyplot(nav, ground_pos, &mut plot_ctx);
info!("skyplot view generated");
} else {
if !no_graph {
info!("skyplot view is not feasible");
}
} else if !no_graph {
info!("skyplot view is not feasible");
}
}
/*
Expand All @@ -431,7 +424,7 @@ pub fn main() -> Result<(), Error> {
* Record analysis / visualization
* analysis depends on the provided record type
*/
if !qc_only && !positioning_only && !no_graph {
if !qc_only && !positioning && !no_graph {
info!("entering record analysis");
plot::plot_record(&ctx, &mut plot_ctx);

Expand Down
4 changes: 2 additions & 2 deletions rinex-cli/src/plot/combination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub fn plot_gnss_combination(
y_title: &str,
) {
// add a plot
plot_context.add_cartesian2d_plot(plot_title, y_title);
plot_context.add_timedomain_plot(plot_title, y_title);

// generate 1 marker per OP
let markers = generate_markers(data.len());
Expand Down Expand Up @@ -49,7 +49,7 @@ pub fn plot_gnss_dcb_mp(
y_title: &str,
) {
// add a plot
plot_context.add_cartesian2d_plot(plot_title, y_title);
plot_context.add_timedomain_plot(plot_title, y_title);
// generate 1 marker per OP
let markers = generate_markers(data.len());
// plot all ops
Expand Down
16 changes: 8 additions & 8 deletions rinex-cli/src/plot/context.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
build_default_2y_plot, build_default_3d_plot, build_default_plot, build_default_polar_plot,
build_world_map, Plot,
build_default_3d_plot, build_default_polar_plot, build_timedomain_2y_plot,
build_timedomain_plot, build_world_map, Plot,
};
//use log::trace;
use plotly::{layout::MapboxStyle, Trace};
Expand All @@ -21,8 +21,12 @@ impl PlotContext {
let len = self.plots.len() - 1;
self.plots.get_mut(len)
}*/
pub fn add_cartesian2d_plot(&mut self, title: &str, y_label: &str) {
self.plots.push(build_default_plot(title, y_label));
pub fn add_timedomain_plot(&mut self, title: &str, y_label: &str) {
self.plots.push(build_timedomain_plot(title, y_label));
}
pub fn add_timedomain_2y_plot(&mut self, title: &str, y1_label: &str, y2_label: &str) {
self.plots
.push(build_timedomain_2y_plot(title, y1_label, y2_label));
}
pub fn add_cartesian3d_plot(
&mut self,
Expand All @@ -34,10 +38,6 @@ impl PlotContext {
self.plots
.push(build_default_3d_plot(title, x_label, y_label, z_label));
}
pub fn add_cartesian2d_2y_plot(&mut self, title: &str, y1_label: &str, y2_label: &str) {
self.plots
.push(build_default_2y_plot(title, y1_label, y2_label));
}
pub fn add_polar2d_plot(&mut self, title: &str) {
self.plots.push(build_default_polar_plot(title));
}
Expand Down
Loading

0 comments on commit 980cdf3

Please sign in to comment.