Skip to content

Commit

Permalink
Develop (#200)
Browse files Browse the repository at this point in the history
* improve obsrinex test infra
* working on substract ci
* test more state of the art decompression
* filename conventions
* fix rad2deg
* bump version
* clippy
* follow standard naming conventions, in data we generate
* fix a tiny error in substraction ops
* update doc
* export data as csv if desired

---------

Signed-off-by: Guillaume W. Bres <[email protected]>
  • Loading branch information
gwbres authored Jan 1, 2024
1 parent 9c36f03 commit c8b56c9
Show file tree
Hide file tree
Showing 38 changed files with 2,024 additions and 573 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ Cargo.lock

rinex/merge.rnx
rinex/test.crx
rinex/test.rnx
DATA/
WORKSPACE/
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.1"
version = "2.3.0"
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.3", features = ["serde"] }
rinex = { path = "../rinex", version = "=0.15.4", features = ["serde"] }
18 changes: 16 additions & 2 deletions crx2rnx/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clap::{Arg, ArgMatches, ColorChoice, Command};
use clap::{Arg, ArgAction, ArgMatches, ColorChoice, Command};
use std::path::{Path, PathBuf};

pub struct Cli {
Expand All @@ -12,7 +12,7 @@ impl Cli {
matches: {
Command::new("crx2rnx")
.author("Guillaume W. Bres <[email protected]>")
.version("2.0")
.version(env!("CARGO_PKG_VERSION"))
.about("Compact RINEX decompression tool")
.arg_required_else_help(true)
.color(ColorChoice::Always)
Expand All @@ -23,10 +23,24 @@ impl Cli {
.help("Input RINEX file")
.required(true),
)
.arg(
Arg::new("short")
.short('s')
.long("short")
.action(ArgAction::SetTrue)
.conflicts_with("output")
.help(
"Prefer shortened filename convention.
Otherwise, we default to modern (V3+) long filenames.
Both will not work well if your input does not follow standard conventions at all.",
),
)
.arg(
Arg::new("output")
.short('o')
.long("output")
.action(ArgAction::Set)
.conflicts_with_all(["short"])
.help("Custom output file name"),
)
.arg(
Expand Down
40 changes: 12 additions & 28 deletions crx2rnx/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,6 @@ fn input_name(path: &PathBuf) -> String {
}
}

// deduce output name, from input name
fn output_filename(stem: &str, path: &PathBuf) -> String {
let filename = path
.file_name()
.expect("failed to determine input file name")
.to_str()
.expect("failed to determine input file name");

if filename.ends_with("gz") {
filename
.strip_suffix(".gz")
.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 {
format!("{}.rnx", stem)
}
}

fn main() -> Result<(), rinex::Error> {
let cli = Cli::new();

Expand All @@ -72,16 +49,23 @@ fn main() -> Result<(), rinex::Error> {

create_workspace(&workspace_path);

let output_name = match cli.output_name() {
Some(name) => name.clone(),
_ => output_filename(&input_name, &input_path),
};

let filepath = input_path.to_string_lossy();

let mut rinex = Rinex::from_file(&filepath)?;
rinex.crnx2rnx_mut(); // convert to RINEX

// if input was gzip'ed: preserve it
let suffix = if input_name.ends_with(".gz") {
Some(".gz")
} else {
None
};

let output_name = match cli.output_name() {
Some(name) => name.clone(),
_ => rinex.standard_filename(cli.matches.get_flag("short"), suffix, None),
};

let outputpath = format!("{}/{}", workspace_path.to_string_lossy(), output_name);

rinex.to_file(&outputpath)?; // dump
Expand Down
2 changes: 1 addition & 1 deletion rinex-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ horrorshow = "0.8"
clap = { version = "4.4.11", 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.3", features = ["full"] }
rinex = { path = "../rinex", version = "=0.15.4", features = ["full"] }
rinex-qc = { path = "../rinex-qc", version = "=0.1.8", features = ["serde"] }
sp3 = { path = "../sp3", version = "=1.0.6", features = ["serde", "flate2"] }
serde = { version = "1.0", default-features = false, features = ["derive"] }
Expand Down
8 changes: 7 additions & 1 deletion rinex-cli/src/cli/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ pub fn subcommand() -> Command {
.long_flag("graph")
.arg_required_else_help(true)
.about(
"RINEX dataset visualization (signals, orbits..), rendered as HTML in the workspace.",
"RINEX data visualization (signals, orbits..), rendered as HTML or CSV in the workspace.",
)
.arg(
Arg::new("csv")
.long("csv")
.action(ArgAction::SetTrue)
.help("Generate CSV files along HTML plots.")
)
.next_help_heading(
"RINEX dependent visualizations.
Expand Down
72 changes: 58 additions & 14 deletions rinex-cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
use clap::{value_parser, Arg, ArgAction, ArgMatches, ColorChoice, Command};
use log::info;
use map_3d::{ecef2geodetic, geodetic2ecef, Ellipsoid};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{
fs::create_dir_all,
io::Write,
path::{Path, PathBuf},
str::FromStr,
};

use crate::Error;
use clap::{value_parser, Arg, ArgAction, ArgMatches, ColorChoice, Command};
use map_3d::{ecef2geodetic, geodetic2ecef, rad2deg, Ellipsoid};
use rinex::prelude::*;
use walkdir::WalkDir;

use crate::{fops::open_with_web_browser, Error};

// identification mode
mod identify;
// graph mode
Expand Down Expand Up @@ -74,6 +79,36 @@ impl Context {
let primary_stem: Vec<&str> = ctx_major_stem.split('.').collect();
primary_stem[0].to_string()
}
/*
* Utility to prepare subdirectories in the session workspace
*/
pub fn create_subdir(&self, suffix: &str) {
create_dir_all(self.workspace.join(suffix))
.unwrap_or_else(|e| panic!("failed to generate session dir {}: {:?}", suffix, e));
}
/*
* Utility to create a file in this session
*/
fn create_file(&self, path: &Path) -> std::fs::File {
std::fs::File::create(path).unwrap_or_else(|e| {
panic!("failed to create {}: {:?}", path.display(), e);
})
}
/*
* Save HTML content, auto opens it if quiet (-q) is not turned on
*/
pub fn render_html(&self, filename: &str, html: String) {
let path = self.workspace.join(filename);
let mut fd = self.create_file(&path);
write!(fd, "{}", html).unwrap_or_else(|e| {
panic!("failed to render HTML content: {:?}", e);
});
info!("html rendered in \"{}\"", path.display());

if !self.quiet {
open_with_web_browser(path.to_string_lossy().as_ref());
}
}
/*
* Creates File/Data context defined by user.
* Regroups all provided files/folders,
Expand All @@ -90,17 +125,21 @@ impl Context {
let walkdir = WalkDir::new(dir).max_depth(max_depth);
for entry in walkdir.into_iter().filter_map(|e| e.ok()) {
if !entry.path().is_dir() {
let filepath = entry.path().to_string_lossy().to_string();
let ret = data.load(&filepath);
let path = entry.path();
let ret = data.load(&path.to_path_buf());
if ret.is_err() {
warn!("failed to load \"{}\": {}", filepath, ret.err().unwrap());
warn!(
"failed to load \"{}\": {}",
path.display(),
ret.err().unwrap()
);
}
}
}
}
// load individual files, if any
for filepath in cli.input_files() {
let ret = data.load(filepath);
let ret = data.load(&Path::new(filepath).to_path_buf());
if ret.is_err() {
warn!("failed to load \"{}\": {}", filepath, ret.err().unwrap());
}
Expand All @@ -119,10 +158,11 @@ impl Context {
},
};
// make sure the workspace is viable and exists, otherwise panic
std::fs::create_dir_all(&path).unwrap_or_else(|_| {
create_dir_all(&path).unwrap_or_else(|e| {
panic!(
"failed to create session workspace \"{}\": permission denied!",
path.to_string_lossy()
"failed to create session workspace \"{}\": {:?}",
path.display(),
e
)
});
info!("session workspace is \"{}\"", path.to_string_lossy());
Expand All @@ -131,7 +171,9 @@ impl Context {
rx_ecef: {
match cli.manual_position() {
Some((x, y, z)) => {
let (lat, lon, _) = ecef2geodetic(x, y, z, Ellipsoid::WGS84);
let (mut lat, mut lon, _) = ecef2geodetic(x, y, z, Ellipsoid::WGS84);
lat = rad2deg(lat);
lon = rad2deg(lon);
info!(
"using manually defined position: {:?} [ECEF] (lat={:.5}°, lon={:.5}°",
(x, y, z),
Expand All @@ -143,7 +185,9 @@ impl Context {
None => {
if let Some(data_pos) = data_position {
let (x, y, z) = data_pos.to_ecef_wgs84();
let (lat, lon, _) = ecef2geodetic(x, y, z, Ellipsoid::WGS84);
let (mut lat, mut lon, _) = ecef2geodetic(x, y, z, Ellipsoid::WGS84);
lat = rad2deg(lat);
lon = rad2deg(lon);
info!(
"position defined in dataset: {:?} [ECEF] (lat={:.5}°, lon={:.5}°",
(x, y, z),
Expand Down
2 changes: 1 addition & 1 deletion rinex-cli/src/fops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ pub fn substract(ctx: &Context, matches: &ArgMatches) -> Result<(), Error> {
.crnx2rnx() //TODO remove this in future please
.substract(
&rinex_b.crnx2rnx(), //TODO: remove this in future please
)?
)
},
t => panic!("operation not feasible for {}", t),
};
Expand Down
41 changes: 41 additions & 0 deletions rinex-cli/src/graph/csv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! helpers to export to CSV if desired,
//! and not only generate HTML plots.
use hifitime::Epoch;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
#[error("failed to write csv file")]
IoError(#[from] std::io::Error),
}

/*
* Use this to export Time domain plots (most widely used plot type)
*/
pub fn csv_export_timedomain<T: std::fmt::UpperExp>(
path: &Path,
title: &str,
labels: &str,
x: &Vec<Epoch>,
y: &Vec<T>,
) -> Result<(), Error> {
let mut fd = File::create(path)?;
writeln!(fd, "================================================")?;
writeln!(fd, "title : {}", title)?;
writeln!(fd, "labels : {}", labels)?;
writeln!(
fd,
"version: rinex-cli v{} - https://georust.org",
env!("CARGO_PKG_VERSION")
)?;
writeln!(fd, "================================================")?;
for (x, y) in x.iter().zip(y.iter()) {
writeln!(fd, "{:?}, {:.6E}", x, y)?;
}
writeln!(fd, "================================================")?;
Ok(())
}
Loading

0 comments on commit c8b56c9

Please sign in to comment.