Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ cggtts = [
]

[dependencies.rinex]
version = "0.20"
git = "https://github.com/nav-solutions/rinex"
rev = "a10b41abb0ccd6b804dff6acd560992cdcd4c639"
features = [
"qc",
"processing",
Expand All @@ -50,26 +51,25 @@ features = [
"ut1",
"meteo",
"clock",
"ionex",
"antex",
"serde",
]

[dependencies.gnss-qc]
version = "0.4"
git = "https://github.com/nav-solutions/gnss-qc"
rev = "3ee5918a37bf2b18371bd18d9a40b2742004865f"
features = [
"sp3",
"navigation",
"flate2",
]

[dependencies.gnss-rtk]
git = "https://github.com/rtk-rs/gnss-rtk"
rev = "5eb681c3f6f123d36b1c5b46cf27429bcf45f3f7"
git = "https://github.com/nav-solutions/gnss-rtk"
rev = "bf0914fb9134eef35a3ef73bc105d5ec28fa783b"
optional = true
features = [
"serde",
"embed_ephem"
]

[dependencies]
Expand All @@ -89,6 +89,6 @@ csv = { version = "1.3", optional = true }
gnss-rs = { version = "2.4", features = ["serde"] }
clap = { version = "4.4.13", features = ["derive", "color"] }
hifitime = { version = "4.1", features = ["serde", "std"] }
anise = { version = "0.6", features = ["embed_ephem"] }
anise = { version = "0.5.3", features = ["embed_ephem"] }
serde = { version = "1.0", default-features = false, features = ["derive"] }
cggtts = { version = "4.3", features = ["serde", "scheduler", "tracker"], optional = true }
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RINEX-Cli
[![MRSV](https://img.shields.io/badge/MSRV-1.82.0-orange?style=for-the-badge)](https://github.com/rust-lang/rust/releases/tag/1.82.0)
[![License](https://img.shields.io/badge/license-MPL_2.0-orange?style=for-the-badge&logo=mozilla)](https://github.com/rtk-rs/rinex-cli/blob/main/LICENSE)

`rinex-cli` is a command line tool to post process RINEX and SP3 files.
`rinex-cli` is a command line tool to post-process RINEX and SP3 files.

Because RINEX and SP3 cover many applications, `rinex-cli` can be used for many applications.
The most important being:
Expand All @@ -22,8 +22,10 @@ The most important being:
- generate high level reports
- Synthesis
- generate RINEX (and soon SP3) from provided products
- Post processed navigation (`ppp` mode) because it integrates a complete
PVT solver (on `ppp` feature only)
- post-processed navigation (on `ppp` feature only) because it integrates a complete
- RINEX-Cli integrates a complete [P.V.T solution solver](https://github.com/rtk-rs/gnss-rtk)
- absolute navigation with `ppp` command line
- differential navigation with `rtk` command line, using one ground station
- CGGTTS solutions solver (`ppp --cggtts` mode) by combining the `ppp` **and** `cggtts` options

<div align="center">
Expand Down
31 changes: 2 additions & 29 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ pub struct Context {
pub workspace: Workspace,

#[cfg(feature = "ppp")]
/// (RX) [Orbit] to use, whether is was automatically picked up,
/// or manually overwritten.
/// (RX) [Orbit] to be used in several proceeses.
/// Either automatically picked up, manually assigned or overwritten, or non-existing.
pub rx_orbit: Option<Orbit>,
}

Expand Down Expand Up @@ -330,33 +330,6 @@ Otherwise it gets automatically picked up."))
}
}

/// Returns individual input BASE STATION -d
pub fn base_station_directories(&self) -> Vec<&String> {
match self.matches.subcommand() {
Some(("rtk", submatches)) => {
if let Some(dir) = submatches.get_many::<String>("dir") {
dir.collect()
} else {
Vec::new()
}
},
_ => Vec::new(),
}
}
/// Returns individual input BASE STATION -fp
pub fn base_station_files(&self) -> Vec<&String> {
match self.matches.subcommand() {
Some(("rtk", submatches)) => {
if let Some(fp) = submatches.get_many::<String>("fp") {
fp.collect()
} else {
Vec::new()
}
},
_ => Vec::new(),
}
}

/// Returns list of preprocessing operations
pub fn preprocessing(&self) -> Vec<&String> {
if let Some(filters) = self.matches.get_many::<String>("preprocessing") {
Expand Down
152 changes: 92 additions & 60 deletions src/cli/positioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,53 @@ fn shared_args(cmd: Command) -> Command {
.action(ArgAction::Append)
.help("Position Solver configuration file (JSON). See --help.")
.long_help("
Refer to all our navigation demos (subfolder).
[https://docs.rs/gnss-rtk/latest/gnss_rtk/prelude/struct.Config.html] is the structure to represent in JSON.
Refer to all our navigation demos (subfolder) and example scripts.
[https://docs.rs/gnss-rtk/latest/gnss_rtk/prelude/struct.Config.html] is the structure to be descrined.
"));

let cmd = cmd.next_help_heading("User / Rover Profile")
.arg(
Arg::new("static")
.long("static")
.action(ArgAction::SetTrue)
.help("Define that the user (rover) is static. Antenna was held static for the entire session."))
.arg(
Arg::new("clock-sigma")
.long("clock-sigma")
.action(ArgAction::Set)
.value_parser(value_parser!(f64))
.required(false)
.help("Define the uncertainty/bias over next clock state prediction (in seconds).
Default value is 10ms (=low quality clock).")
.help("Define the rover as static, meaning, its antenna was held static for the entire session.
The default profile is \"pedestrian\" (very low velocity), which is not suited for very fast moving rovers."))
.arg(
Arg::new("car")
.long("car")
.action(ArgAction::SetTrue)
.help("Define car profile (low velocity).
The default profile is \"pedestrian\" (very low velocity), which is not suited for very fast moving rovers."))
.arg(
Arg::new("airplane")
.long("airplane")
.action(ArgAction::SetTrue)
.help("Define airplane profile (high velocity).
The default profile is \"pedestrian\" (very low velocity), which is not suited for very fast moving rovers."))
.arg(
Arg::new("rocket")
.long("rocket")
.action(ArgAction::SetTrue)
.help("Define rocket profile (very high velocity).
The default profile is \"pedestrian\" (very low velocity), which is not suited for very fast moving rovers."))
.arg(
Arg::new("quartz")
.long("quartz")
.action(ArgAction::SetTrue)
.help("Define quartz (rover clock) profile (very poor quality).
The default profile is Oscillator/OCXO."))
.arg(
Arg::new("atomic")
.long("quartz")
.action(ArgAction::SetTrue)
.help("Define atomic (rover clock) profile (high quality, at the scale of a GNSS constellation).
The default profile is Oscillator/OCXO."))
.arg(
Arg::new("h-maser")
.long("h-maser")
.action(ArgAction::SetTrue)
.help("Define Hydrogen MASER (rover clock) profile (ultra high quality, better than GNSS constellation).
The default profile is Oscillator/OCXO.")
);

let cmd = cmd.next_help_heading("Solutions formating");
Expand Down Expand Up @@ -66,94 +95,97 @@ Default value is 10ms (=low quality clock).")
)
};

let cmd = cmd.next_help_heading("CGGTTS Post FIT");

let cmd = if cfg!(not(feature = "cggtts")) {
cmd.arg(
Arg::new("cggtts")
.long("cggtts")
.action(ArgAction::SetTrue)
.help("[NOT AVAILABLE] requires cggtts compilation option"),
)
} else {
cmd
.arg(Arg::new("cggtts")
.long("cggtts")
.action(ArgAction::SetTrue)
.help("Activate CGGTTS special Post FIT"))
.arg(Arg::new("tracking")
.long("trk")
.value_parser(value_parser!(Duration))
.action(ArgAction::Set)
.help("CGGTTS custom tracking duration.
Otherwise, the default tracking duration is used. Refer to [https://docs.rs/cggtts/latest/cggtts/track/struct.Scheduler.html]."))
.arg(Arg::new("lab")
.long("lab")
.action(ArgAction::Set)
.help("Define the name of your station or laboratory here."))
.arg(Arg::new("utck")
.long("utck")
.action(ArgAction::Set)
.conflicts_with("clock")
.help("If the local clock tracks a local UTC replica, you can define the name
of this replica here."))
.arg(Arg::new("clock")
.long("clk")
.action(ArgAction::Set)
.conflicts_with("utck")
.help("If the local clock is not a UTC replica and has a specific name, you
can define it here."))
};

cmd
}

pub fn ppp_subcommand() -> Command {
let cmd = Command::new("ppp")
.arg_required_else_help(false)
.about(
"Post Processed Positioning. Use this mode to deploy the precise position solver.
"Post-processed PPP (absolute navigation).
The solutions are added to the final report as an extra chapter. See --help",
)
.long_about(
"Post Processed Positioning (ppp) opmode resolves
PVT solutions from RINEX data sampled by a single receiver (! This is not RTK!).
The solutions are presented in the analysis report (post processed results chapter).
Use --cggtts to convert solutions to CGGTTS special format.",
);
)
.next_help_heading("CGGTTS Post FIT");

let cmd = if cfg!(not(feature = "cggtts")) {
cmd.arg(
Arg::new("cggtts")
.long("cggtts")
.action(ArgAction::SetTrue)
.help("[NOT AVAILABLE] requires cggtts compilation option"),
)
} else {
cmd
.arg(Arg::new("cggtts")
.long("cggtts")
.action(ArgAction::SetTrue)
.help("Activate CGGTTS special Post FIT"))
.arg(Arg::new("tracking")
.long("trk")
.value_parser(value_parser!(Duration))
.action(ArgAction::Set)
.help("CGGTTS custom tracking duration.
Otherwise, the default tracking duration is used. Refer to [https://docs.rs/cggtts/latest/cggtts/track/struct.Scheduler.html]."))
.arg(Arg::new("lab")
.long("lab")
.action(ArgAction::Set)
.help("Define the name of your station or laboratory here."))
.arg(Arg::new("utck")
.long("utck")
.action(ArgAction::Set)
.conflicts_with("clock")
.help("If the local clock tracks a local UTC replica, you can define the name
of this replica here."))
.arg(Arg::new("clock")
.long("clk")
.action(ArgAction::Set)
.conflicts_with("utck")
.help("If the local clock is not a UTC replica and has a specific name, you
can define it here."))
};

shared_args(cmd)
}

pub fn rtk_subcommand() -> Command {
let cmd = Command::new("rtk")
.arg_required_else_help(true)
.about(
"Post Processed RTK. Use this mode to deploy the precise differential positioning.
The initial context describes the Rover context. rtk accepts `-f` and `-d` once again, to describe the remote Station.
Other positioning flags still apply (like -c). See --help.",
"Post-processed RTK (differential navigation).
The initial context describes the rover context.
Use rtk --fp,-f to describe the base station, with at least one RINEX file (mandatory).
The RINEX file must describe the station position.
RINEX-Cli is limited to static base! moving base is not feasible.
The RTK solutions are added to the final report, as an extra chapter. See --help.",
)
.long_about(
"RTK post opmode resolves PVT solutions by (post processed) differential navigation.
The initial context (-f, -d) describes the ROVER.
`rtk` also accepts -f and -d and you need to use those to describe the BASE (mandatory).
Other than that, `rtk` is stricly identical to `ppp` and is presented similarly.
CGGTTS and other options still apply."
`rtk` also accepts -f and -d and you need to use those to describe the base station (mandatory).",
)
.arg(
Arg::new("fp")
.long("fp")
.value_name("FILE")
.action(ArgAction::Append)
.required_unless_present("dir")
.help("Pass any RINEX file for remote base station"),
.help("Base station Observation RINEX file(s), one at a time, as many as needed."),
)
.arg(
Arg::new("dir")
.short('d')
.value_name("DIR")
.action(ArgAction::Append)
.required_unless_present("fp")
.help("Pass any directory for remote base station"),
.help(
"Base station Observation RINEX directory, one at a time, as many as needed.",
),
);
shared_args(cmd)
}
2 changes: 0 additions & 2 deletions src/fops/filegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,11 @@ pub fn filegen(ctx: &Context, matches: &ArgMatches, submatches: &ArgMatches) ->
fn write(ctx: &Context, matches: &ArgMatches, submatches: &ArgMatches) -> Result<(), Error> {
let ctx_data = &ctx.data;
for (product, dir) in [
(ProductType::DORIS, "DORIS"),
(ProductType::Observation, "OBSERVATIONS"),
(ProductType::MeteoObservation, "METEO"),
(ProductType::BroadcastNavigation, "BRDC"),
(ProductType::HighPrecisionClock, "CLOCK"),
(ProductType::HighPrecisionOrbit, "SP3"),
(ProductType::IONEX, "IONEX"),
(ProductType::ANTEX, "ANTEX"),
] {
if let Some(rinex) = ctx_data.rinex(product) {
Expand Down
8 changes: 0 additions & 8 deletions src/fops/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,6 @@ pub fn merge(ctx: &Context, cli: &Cli, submatches: &ArgMatches) -> Result<(), Er
rinex_a.merge(&rinex_b)?,
)
},
RinexType::IonosphereMaps => {
let rinex_a = ctx_data.ionex().ok_or(Error::MissingIONEX)?;

(
rinex_a.standard_filename(short_v2_name, None, None),
rinex_a.merge(&rinex_b)?,
)
},
RinexType::ClockData => {
let rinex_a = ctx_data.clock().ok_or(Error::MissingClockRinex)?;

Expand Down
1 change: 0 additions & 1 deletion src/fops/split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub fn split(ctx: &Context, submatches: &ArgMatches) -> Result<(), Error> {
ProductType::MeteoObservation,
ProductType::BroadcastNavigation,
ProductType::HighPrecisionClock,
ProductType::IONEX,
] {
if let Some(rinex) = ctx_data.rinex(product) {
let (rinex_a, rinex_b) = rinex.split(*split_instant);
Expand Down
2 changes: 0 additions & 2 deletions src/fops/tbin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ pub fn time_binning(
ctx.workspace.create_subdir("BATCH");

for product in [
ProductType::IONEX,
ProductType::DORIS,
ProductType::Observation,
ProductType::MeteoObservation,
ProductType::BroadcastNavigation,
Expand Down
Loading