Skip to content

Commit 05f9a6e

Browse files
authored
Merge pull request #5 from nav-solutions/develop
Small improvements, mark v0.1
2 parents f508904 + 090252a commit 05f9a6e

File tree

6 files changed

+161
-29
lines changed

6 files changed

+161
-29
lines changed

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[submodule "tools"]
22
path = tools
3-
url = https://github.com/rtk-rs/tools
3+
url = https://github.com/nav-solutions/tools

Cargo.toml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
[package]
22
name = "ntrip-client"
3-
version = "0.0.1"
3+
version = "0.1.0"
44
license = "MPL-2.0"
55
authors = [
66
"Guillaume W. Bres <guillaume.bressaix@gmail.com>",
77
"Ryan Kurte <ryan@kurte.nz>"
88
]
99
description = "NTRIP client"
10-
homepage = "https://github.com/rtk-rs"
11-
repository = "https://github.com/rtk-rs/ntrip-client"
10+
homepage = "https://github.com/nav-solutions"
11+
repository = "https://github.com/nav-solutions/ntrip-client"
1212
keywords = ["geo", "rtcm", "tcp", "tokio"]
1313
categories = ["science", "science::geo"]
1414
edition = "2021"
1515
readme = "README.md"
16+
exclude = ["tools/*"]
1617

1718
[package.metadata]
1819
msrv = "1.82"
@@ -22,13 +23,16 @@ all-features = true
2223
rustdoc-args = ["--cfg", "docrs", "--generate-link-to-definition"]
2324

2425
[features]
25-
default = [ "log", "clap", "serde", "anyhow" ]
26+
default = ["log", "clap", "serde"]
2627

2728
# Unlock client logs
2829
log = ["dep:tracing", "dep:tracing-subscriber"]
2930
clap = ["dep:clap"]
3031
serde = ["dep:serde", "rtcm-rs/serde", "geoutils/serde"]
3132

33+
[dev-dependencies]
34+
anyhow = "1"
35+
3236
[dependencies]
3337
thiserror = "2"
3438
base64 = "0.22"
@@ -48,9 +52,8 @@ tracing = { version = "0.1.41", optional = true, features = ["log"] }
4852
tracing-subscriber = { version = "0.3.17", optional = true, features = ["fmt", "env-filter"] }
4953
clap = { version = "4.5", optional = true, features = ["derive", "env"] }
5054
serde = { version = "1.0", optional = true, features = ["derive"] }
51-
anyhow = { version = "1.0.0", optional = true }
5255

5356
[[examples]]
5457
name = "simple-cli"
5558
path = "examples/simple-cli.rs"
56-
required-features = ["clap", "log", "anyhow"]
59+
required-features = ["clap", "log"]

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
NTRIP Client
22
============
33

4-
[![Rust](https://github.com/rtk-rs/ntrip-client/actions/workflows/rust.yml/badge.svg)](https://github.com/rtk-rs/ntrip-client/actions/workflows/rust.yml)
5-
[![Rust](https://github.com/rtk-rs/ntrip-client/actions/workflows/daily.yml/badge.svg)](https://github.com/rtk-rs/ntrip-client/actions/workflows/daily.yml)
4+
[![Rust](https://github.com/nav-solutions/ntrip-client/actions/workflows/rust.yml/badge.svg)](https://github.com/nav-solutions/ntrip-client/actions/workflows/rust.yml)
5+
[![Rust](https://github.com/nav-solutions/ntrip-client/actions/workflows/daily.yml/badge.svg)](https://github.com/nav-solutions/ntrip-client/actions/workflows/daily.yml)
66
[![crates.io](https://docs.rs/ntrip-client/badge.svg)](https://docs.rs/ntrip-client/)
77
[![crates.io](https://img.shields.io/crates/d/ntrip-client.svg)](https://crates.io/crates/ntrip-client)
88

99
[![MRSV](https://img.shields.io/badge/MSRV-1.78.0-orange?style=for-the-badge)](https://github.com/rust-lang/rust/releases/tag/1.78.0)
10-
[![License](https://img.shields.io/badge/license-MPL_2.0-orange?style=for-the-badge&logo=mozilla)](https://github.com/rtk-rs/ntrip-client/blob/main/LICENSE)
10+
[![License](https://img.shields.io/badge/license-MPL_2.0-orange?style=for-the-badge&logo=mozilla)](https://github.com/nav-solutions/ntrip-client/blob/main/LICENSE)
1111

1212
NTRIP client used by all our applications that require RTCM messaging (downlink), through NTRIP connection.
1313

@@ -68,5 +68,5 @@ loop {
6868
Licensing
6969
=========
7070

71-
This library is part of the [RTK-rs framework](https://github.com/rtk-rs) which
72-
is delivered under the [Mozilla V2 Public](https://www.mozilla.org/en-US/MPL/2.0) license.
71+
This library is part of the [NAV-solutions framework](https://github.com/nav-solutions)
72+
which is licensed under the [Mozilla V2 Public](https://www.mozilla.org/en-US/MPL/2.0) license.

src/client.rs

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,74 @@ use crate::{
2828
snip::ServerInfo,
2929
};
3030

31-
/// NTRIP Client, used to connect to an NTRIP (RTCM) service
31+
/// NTRIP Client, used to connect to an NTRIP (RTCM) service.
32+
/// When "mounted", the [NtripHandle] allows real-time messaging
33+
/// through a [Stream] channel.
34+
///
35+
/// ```
36+
/// use tokio::select;
37+
/// use tokio::sync; // broadcast channem
38+
/// use futures::StreamExt; // real-time channel
39+
///
40+
/// use ntrip_client::{
41+
/// NtripClient,
42+
/// NtripConfig,
43+
/// RtcmProvider,
44+
/// NtripCredentials,
45+
/// NtripClientError,
46+
/// };
47+
///
48+
/// async fn basic_listener() -> Result<(), anyhow::Error> {
49+
///
50+
/// // this network does not require SSL
51+
/// let config = NtripConfig::from_provider(RtcmProvider::Centipede);
52+
///
53+
/// // adapt your credentials to the network
54+
/// let creds = NtripCredentials::default()
55+
/// .with_username("user")
56+
/// .with_password("password");
57+
///
58+
/// // client definition
59+
/// let mut client = NtripClient::new(config, creds)
60+
/// .await?;
61+
///
62+
/// // this channel allows graceful exit
63+
/// let (exit_tx, mut exit_rx) = sync::broadcast::channel(1);
64+
///
65+
/// // subscribe to remote server
66+
/// let mut mountpoint = client.mount("192.168.1.1:1234", exit_tx).await?;
67+
///
68+
/// // listening
69+
/// loop {
70+
/// select! {
71+
/// message = mountpoint.next() => match message {
72+
/// Some(msg) => {
73+
/// println!("received RTCM message: {:?}", msg);
74+
/// },
75+
/// None => {
76+
/// println!("End of stream!");
77+
/// break;
78+
/// },
79+
/// },
80+
/// _ = exit_rx.recv() => {
81+
/// println!("graceful exit");
82+
/// break;
83+
/// },
84+
/// }
85+
/// }
86+
///
87+
/// Ok(())
88+
/// }
89+
///
90+
/// basic_listener();
91+
/// ```
3292
pub struct NtripClient {
3393
config: NtripConfig,
3494
creds: NtripCredentials,
3595
}
3696

37-
/// NTRIP Mount handle, used to stream RTCM messages from an NTRIP service
97+
/// [NtripHandle] is the Mount handle, it implements [Stream]
98+
/// which is how you can receiver messages in real-time.
3899
pub struct NtripHandle {
39100
_rx_handle: tokio::task::JoinHandle<()>,
40101
ntrip_rx: UnboundedReceiver<Message>,
@@ -105,18 +166,27 @@ impl NtripClient {
105166
Ok(snip_info)
106167
}
107168

169+
/// 'Mount' the [NtripClient] from remote $url/$mount service point.
170+
/// On success, you can then start listening to messages from the server.
171+
///
172+
/// ## Input
173+
/// - mount: readable remote mount point (server name)
174+
/// - exit_tx: [BroadcastSender] is passed to allow graceful exit on errors
175+
///
176+
/// ## Output
177+
/// - [NtripHandle] which implements [Stream] to receive messages in real-time.
108178
pub async fn mount(
109179
&mut self,
110180
mount: impl ToString,
111181
exit_tx: BroadcastSender<()>,
112182
) -> Result<NtripHandle, NtripClientError> {
113183
debug!(
114184
"Connecting to NTRIP server {}/{}",
115-
self.config.url(),
185+
self.config.to_url(),
116186
mount.to_string()
117187
);
118188

119-
let sock = TcpStream::connect(&self.config.url()).await?;
189+
let sock = TcpStream::connect(&self.config.to_url()).await?;
120190

121191
let (rx_handle, ntrip_rx) = match self.config.use_tls {
122192
true => {
@@ -199,7 +269,7 @@ impl NtripClient {
199269
debug!("Write HTTP request");
200270
sock.write_all(format!("GET /{} HTTP/1.0\r\n", mount).as_bytes())
201271
.await?;
202-
sock.write_all(format!("Host: {}\r\n", config.url()).as_bytes())
272+
sock.write_all(format!("Host: {}\r\n", config.to_url()).as_bytes())
203273
.await?;
204274

205275
// Write HTTP headers

src/config.rs

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,60 @@ pub struct NtripConfig {
3333
pub use_tls: bool,
3434
}
3535

36+
impl Default for NtripConfig {
37+
/// Builds a default [NtripConfig] ready to connect to [RtcmProvider::Centipede] network.
38+
/// The network does not requires SSL.
39+
fn default() -> Self {
40+
Self::from_provider(RtcmProvider::Centipede)
41+
}
42+
}
43+
44+
impl NtripConfig {
45+
/// Generate a connection URL ("host:port") from the NtripConfig
46+
pub fn to_url(&self) -> String {
47+
format!("{}:{}", self.host, self.port)
48+
}
49+
50+
/// Prepares an [NtripConfig] for one of our predefined [RtcmProvider]s
51+
pub fn from_provider(network: RtcmProvider) -> Self {
52+
Self {
53+
host: network.host().to_string(),
54+
port: network.port(),
55+
use_tls: network.uses_tls(),
56+
}
57+
}
58+
59+
/// Copies and returns [NtripConfig] with updated "host" IP address
60+
pub fn with_host(&self, address: &str) -> Self {
61+
let mut s = self.clone();
62+
s.host = address.to_string();
63+
s
64+
}
65+
66+
/// Copies and returns [NtripConfig] with updated port number
67+
pub fn with_port(&self, port: u16) -> Self {
68+
let mut s = self.clone();
69+
s.port = port;
70+
s
71+
}
72+
73+
/// Copies and returns [NtripConfig] with TLS/SSL active
74+
pub fn with_tls(&self) -> Self {
75+
let mut s = self.clone();
76+
s.use_tls = true;
77+
s
78+
}
79+
80+
/// Copies and returns [NtripConfig] without TLS/SSL active
81+
pub fn without_tls(&self) -> Self {
82+
let mut s = self.clone();
83+
s.use_tls = false;
84+
s
85+
}
86+
}
87+
3688
/// Credentials for an NTRIP (RTCM) service
37-
#[derive(Clone, PartialEq, Debug)]
89+
#[derive(Clone, Default, PartialEq, Debug)]
3890
#[cfg_attr(feature = "clap", derive(clap::Parser))]
3991
pub struct NtripCredentials {
4092
/// Username for the NTRIP service
@@ -49,10 +101,19 @@ pub struct NtripCredentials {
49101
pub pass: String,
50102
}
51103

52-
impl NtripConfig {
53-
/// Generate a connection URL ("host:port") from the NtripConfig
54-
pub fn url(&self) -> String {
55-
format!("{}:{}", self.host, self.port)
104+
impl NtripCredentials {
105+
/// Copies and returns updated [NtripCredentials]
106+
pub fn with_username(&self, username: &str) -> Self {
107+
let mut s = self.clone();
108+
s.user = username.to_string();
109+
s
110+
}
111+
112+
/// Copies and returns updated [NtripCredentials]
113+
pub fn with_password(&self, password: &str) -> Self {
114+
let mut s = self.clone();
115+
s.pass = password.to_string();
116+
s
56117
}
57118
}
58119

@@ -98,8 +159,8 @@ impl RtcmProvider {
98159
}
99160
}
100161

101-
/// Does the provider require TLS / SSL?
102-
pub fn use_tls(&self) -> bool {
162+
/// Returns true if this [RtcmProvider] requires TLS/SSL.
163+
pub fn uses_tls(&self) -> bool {
103164
match self {
104165
RtcmProvider::Linz => false,
105166
RtcmProvider::Rtk2Go => false,
@@ -139,11 +200,7 @@ impl FromStr for NtripConfig {
139200
fn from_str(s: &str) -> Result<Self, Self::Err> {
140201
// Match on known providers
141202
if let Ok(provider) = RtcmProvider::from_str(s) {
142-
return Ok(NtripConfig {
143-
host: provider.host().to_string(),
144-
port: provider.port(),
145-
use_tls: provider.use_tls(),
146-
});
203+
return Ok(NtripConfig::from_provider(provider));
147204
}
148205

149206
// Strip protocol if present

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
//! Provides an async NTRIP client for listing mounts and connecting to RTCM services
44
55
pub mod config;
6+
pub use config::*;
67

78
pub mod snip;
9+
pub use snip::*;
810

911
mod error;
1012
pub use error::NtripClientError;

0 commit comments

Comments
 (0)