Skip to content

Commit f508904

Browse files
authored
Merge pull request #4 from ryankurte/rebuild-client
rework client + config, add tls support
2 parents 8ed6bc6 + 94ff90e commit f508904

File tree

11 files changed

+1168
-245
lines changed

11 files changed

+1168
-245
lines changed

.github/workflows/daily.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- uses: actions-rs/toolchain@v1
2323
name: Install Rust
2424
with:
25-
toolchain: 1.78.0
25+
toolchain: 1.82.0
2626
override: true
2727

2828
- uses: actions-rs/cargo@v1

.github/workflows/rust.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
- uses: actions-rs/toolchain@v1
3434
name: Install Rust
3535
with:
36-
toolchain: 1.78.0
36+
toolchain: 1.82.0
3737
override: true
3838

3939
- uses: actions-rs/cargo@v1

Cargo.toml

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
name = "ntrip-client"
33
version = "0.0.1"
44
license = "MPL-2.0"
5-
authors = ["Guillaume W. Bres <guillaume.bressaix@gmail.com>"]
5+
authors = [
6+
"Guillaume W. Bres <guillaume.bressaix@gmail.com>",
7+
"Ryan Kurte <ryan@kurte.nz>"
8+
]
69
description = "NTRIP client"
710
homepage = "https://github.com/rtk-rs"
811
repository = "https://github.com/rtk-rs/ntrip-client"
@@ -12,21 +15,42 @@ edition = "2021"
1215
readme = "README.md"
1316

1417
[package.metadata]
15-
msrv = "1.78"
18+
msrv = "1.82"
1619

1720
[package.metadata.docs.rs]
1821
all-features = true
1922
rustdoc-args = ["--cfg", "docrs", "--generate-link-to-definition"]
2023

2124
[features]
22-
default = []
25+
default = [ "log", "clap", "serde", "anyhow" ]
2326

2427
# Unlock client logs
25-
log = ["dep:log"]
28+
log = ["dep:tracing", "dep:tracing-subscriber"]
29+
clap = ["dep:clap"]
30+
serde = ["dep:serde", "rtcm-rs/serde", "geoutils/serde"]
2631

2732
[dependencies]
2833
thiserror = "2"
2934
base64 = "0.22"
3035
rtcm-rs = "0.11"
31-
log = { version = "0.4", optional = true }
36+
futures = "0.3"
3237
tokio = { version = "1.45.0", features = ["full"] }
38+
strum = { version = "0.27.2", features = ["derive"] }
39+
reqwest = { version = "0.12", features = ["rustls-tls"] }
40+
http = "1.3.1"
41+
tokio-rustls = "0.26.2"
42+
rustls = "0.23.31"
43+
webpki-roots = "1.0.2"
44+
geoutils = "0.5.1"
45+
isocountry = "0.3.2"
46+
47+
tracing = { version = "0.1.41", optional = true, features = ["log"] }
48+
tracing-subscriber = { version = "0.3.17", optional = true, features = ["fmt", "env-filter"] }
49+
clap = { version = "4.5", optional = true, features = ["derive", "env"] }
50+
serde = { version = "1.0", optional = true, features = ["derive"] }
51+
anyhow = { version = "1.0.0", optional = true }
52+
53+
[[examples]]
54+
name = "simple-cli"
55+
path = "examples/simple-cli.rs"
56+
required-features = ["clap", "log", "anyhow"]

README.md

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,46 @@ Getting started
2323
ntrip-client = "0.0.1"
2424
```
2525

26+
Refer to the [provided example](examples/) for a complete demo.
27+
2628
```rust
27-
let mut client = NTRIPClient::new("caster.centipede.fr", 2101, "ENSMM")
28-
.with_credentials("centipede", "centipede");
29-
30-
// deploy using 'tokio' framework
31-
client.run()
32-
.await
33-
.unwrap_or_else(|e| {
34-
panic!("Failed to deploy NTRIP client: {}", e);
35-
});
29+
// Configure server
30+
let ntrip_config = "centipede".parse::<NtripConfig>();
31+
let ntrip_creds = NtripCredentials{
32+
user: "centipede".to_string(),
33+
pass: "centipede".to_string(),
34+
}
35+
36+
// Setup client
37+
let mut client = NtripClient::new(ntrip_config, ntrip_creds).await.unwrap();
38+
39+
// List mounts
40+
let server_info = client.list_mounts().await.unwrap();
41+
for m in server_info.mounts {
42+
println!("{} - {}", m.name, m.details);
43+
}
44+
45+
// Subscribe to a mount
46+
let (exit_tx, exit_rx) = tokio::sync::broadcast(1);
47+
let handle = client.mount("VALDM", exit_tx.clone());
48+
49+
loop {
50+
select!{
51+
m = client.next() => match m {
52+
Some(m) => {
53+
info!("Received RTCM message: {:?}", m);
54+
},
55+
None => {
56+
error!("NTRIP client stream ended");
57+
break;
58+
}
59+
},
60+
_ = exit_rx.recv() => {
61+
info!("Exiting on signal");
62+
break;
63+
}
64+
}
65+
}
3666
```
3767

3868
Licensing

examples/simple-cli.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use clap::Parser;
2+
use futures::StreamExt;
3+
use geoutils::Location;
4+
use ntrip_client::{
5+
config::{NtripConfig, NtripCredentials},
6+
NtripClient,
7+
};
8+
use tokio::select;
9+
use tracing::{debug, error, info, level_filters::LevelFilter};
10+
use tracing_subscriber::{fmt::Subscriber as FmtSubscriber, EnvFilter};
11+
12+
/// NTRIP command line tool
13+
#[derive(Clone, PartialEq, Debug, Parser)]
14+
struct Args {
15+
#[clap()]
16+
/// NTRIP server identifier or URI ("rtk2go", "linz" etc., or "[ntrip|http|https]://host:port")
17+
pub ntrip_host: NtripConfig,
18+
19+
#[clap(flatten)]
20+
pub ntrip_creds: NtripCredentials,
21+
22+
#[clap(subcommand)]
23+
pub command: Commands,
24+
25+
#[clap(long, default_value = "info")]
26+
/// Set log level
27+
pub log_level: LevelFilter,
28+
}
29+
30+
#[derive(Clone, PartialEq, Debug, Parser)]
31+
pub enum Commands {
32+
/// List mount points on an NTRIP server
33+
List,
34+
/// Find the nearest mount point to a specified location
35+
FindNearest {
36+
#[clap()]
37+
lat: f64,
38+
#[clap()]
39+
lon: f64,
40+
},
41+
/// Subscribe to a specified mount point and print received RTCM messages
42+
Subscribe {
43+
#[clap()]
44+
mount: String,
45+
},
46+
}
47+
48+
#[tokio::main]
49+
async fn main() -> Result<(), anyhow::Error> {
50+
// Parse command line arguments
51+
let args = Args::parse();
52+
53+
// Setup logging
54+
let filter = EnvFilter::from_default_env().add_directive(args.log_level.into());
55+
let _ = FmtSubscriber::builder()
56+
.compact()
57+
.without_time()
58+
.with_max_level(args.log_level)
59+
.with_env_filter(filter)
60+
.try_init();
61+
62+
info!("Start NTRIP/RTMP tool");
63+
64+
debug!("Args {args:?}");
65+
66+
// Setup interrupt / exit handler
67+
let (exit_tx, mut exit_rx) = tokio::sync::broadcast::channel(1);
68+
let e = exit_tx.clone();
69+
tokio::task::spawn(async move {
70+
tokio::signal::ctrl_c().await.unwrap();
71+
debug!("Received Ctrl-C, shutting down...");
72+
e.send(()).unwrap();
73+
});
74+
75+
let mut client = NtripClient::new(args.ntrip_host.clone(), args.ntrip_creds.clone()).await?;
76+
77+
match args.command {
78+
Commands::List => {
79+
// List available NTRIP mounts using SNIP
80+
info!("Listing NTRIP mounts");
81+
82+
let info = client.list_mounts().await.unwrap();
83+
84+
for s in info.services {
85+
info!(
86+
"{} - {} ({:.3}, {:.3})",
87+
s.name,
88+
s.details,
89+
s.location.latitude(),
90+
s.location.longitude()
91+
);
92+
}
93+
},
94+
Commands::FindNearest { lat, lon } => {
95+
// Find the nearest NTRIP mount to the specified location
96+
info!("Finding nearest NTRIP mount to ({}, {})", lat, lon);
97+
98+
let info = client.list_mounts().await.unwrap();
99+
100+
let target_location = Location::new(lat, lon);
101+
102+
match info.find_nearest(&target_location) {
103+
Some((s, d)) => {
104+
info!(
105+
"Nearest mount: {} - {} ({:.3}, {:.3}), {:.3} km away",
106+
s.name,
107+
s.details,
108+
s.location.latitude(),
109+
s.location.longitude(),
110+
d / 1000.0
111+
);
112+
},
113+
None => {
114+
info!("No mounts found");
115+
},
116+
}
117+
},
118+
Commands::Subscribe { mount } => {
119+
// Subscribe to the specified NTRIP mount
120+
debug!("Connecting to NTRIP server");
121+
122+
// Setup the NTRIP client
123+
let mut client = client.mount(mount, exit_tx.clone()).await?;
124+
125+
// Process incoming RTCM messages
126+
loop {
127+
select! {
128+
m = client.next() => match m {
129+
Some(m) => {
130+
info!("Received RTCM message: {:?}", m);
131+
},
132+
None => {
133+
error!("NTRIP client stream ended");
134+
break;
135+
}
136+
},
137+
_ = exit_rx.recv() => {
138+
info!("Exiting on signal");
139+
break;
140+
}
141+
}
142+
}
143+
},
144+
}
145+
146+
debug!("Exiting");
147+
148+
Ok(())
149+
}

0 commit comments

Comments
 (0)