ntrip_core/lib.rs
1//! # ntrip-core
2//!
3//! An async NTRIP client library for Rust.
4//!
5//! [NTRIP](https://en.wikipedia.org/wiki/Networked_Transport_of_RTCM_via_Internet_Protocol)
6//! (Networked Transport of RTCM via Internet Protocol) is a protocol for
7//! streaming GNSS correction data over the internet. This library provides
8//! a client implementation for receiving RTK corrections from NTRIP casters.
9//!
10//! ## Features
11//!
12//! - **Protocol Support**: NTRIP v1 (ICY) and v2 (HTTP/1.1 chunked)
13//! - **Security**: TLS/HTTPS via rustls (no OpenSSL dependency)
14//! - **Discovery**: Sourcetable retrieval and nearest mountpoint selection
15//! - **Proxy**: HTTP proxy support via CONNECT tunneling
16//! - **Async**: Built on Tokio for efficient async I/O
17//! - **Robust**: Read timeouts, automatic reconnection, and proper error handling
18//!
19//! ## Reconnection Behavior
20//!
21//! By default, [`NtripClient::read_chunk()`] will automatically attempt to reconnect
22//! on timeout or disconnect (up to 3 attempts with 1 second delay). This means the
23//! method may block for longer than the read timeout while reconnecting.
24//!
25//! To disable automatic reconnection, use [`NtripConfig::without_reconnect()`].
26//!
27//! ## TLS Support
28//!
29//! TLS/HTTPS capability is always compiled in via `tokio-rustls` (no OpenSSL dependency).
30//! TLS is **not** used by default - you must explicitly enable it per-connection:
31//!
32//! ```rust,no_run
33//! # use ntrip_core::NtripConfig;
34//! let config = NtripConfig::new("secure-caster.example.com", 443, "MOUNT")
35//! .with_tls(); // Enable TLS for this connection
36//! ```
37//!
38//! ## HTTP Proxy Support
39//!
40//! Connect through an HTTP proxy using the CONNECT method:
41//!
42//! ```rust,no_run
43//! use ntrip_core::{NtripConfig, ProxyConfig};
44//!
45//! // Explicit proxy configuration
46//! let proxy = ProxyConfig::new("proxy.example.com", 8080)
47//! .with_credentials("proxy_user", "proxy_pass");
48//!
49//! let config = NtripConfig::new("caster.example.com", 2101, "MOUNT")
50//! .with_proxy(proxy);
51//!
52//! // Or read from $HTTP_PROXY environment variable
53//! let config = NtripConfig::new("caster.example.com", 2101, "MOUNT")
54//! .with_proxy_from_env();
55//! ```
56//!
57//! ## Cancellation
58//!
59//! To cancel a pending operation, use `tokio::select!`:
60//!
61//! ```rust,no_run
62//! use ntrip_core::{NtripClient, NtripConfig};
63//! use tokio::sync::oneshot;
64//!
65//! # async fn example() -> Result<(), ntrip_core::Error> {
66//! let config = NtripConfig::new("rtk2go.com", 2101, "MOUNT");
67//! let mut client = NtripClient::new(config)?;
68//! client.connect().await?;
69//!
70//! let (cancel_tx, cancel_rx) = oneshot::channel::<()>();
71//!
72//! let mut buf = [0u8; 4096];
73//! tokio::select! {
74//! result = client.read_chunk(&mut buf) => {
75//! match result {
76//! Ok(n) => println!("Received {} bytes", n),
77//! Err(e) => eprintln!("Error: {}", e),
78//! }
79//! }
80//! _ = cancel_rx => {
81//! println!("Operation cancelled");
82//! client.disconnect();
83//! }
84//! }
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! ## Custom Timeout Handling
90//!
91//! For fine-grained timeout control beyond the built-in read timeout:
92//!
93//! ```rust,no_run
94//! use ntrip_core::{NtripClient, NtripConfig};
95//! use std::time::Duration;
96//!
97//! # async fn example() -> Result<(), ntrip_core::Error> {
98//! // Disable built-in reconnection for manual control
99//! let config = NtripConfig::new("rtk2go.com", 2101, "MOUNT")
100//! .with_read_timeout(0) // Disable built-in read timeout
101//! .without_reconnect(); // Disable auto-reconnection
102//!
103//! let mut client = NtripClient::new(config)?;
104//! client.connect().await?;
105//!
106//! let mut buf = [0u8; 4096];
107//!
108//! // Apply your own timeout
109//! match tokio::time::timeout(Duration::from_secs(5), client.read_chunk(&mut buf)).await {
110//! Ok(Ok(n)) => println!("Received {} bytes", n),
111//! Ok(Err(e)) => eprintln!("Read error: {}", e),
112//! Err(_) => eprintln!("Custom timeout expired"),
113//! }
114//! # Ok(())
115//! # }
116//! ```
117//!
118//! ## Quick Start
119//!
120//! ```rust,no_run
121//! use ntrip_core::{NtripClient, NtripConfig};
122//!
123//! #[tokio::main]
124//! async fn main() -> Result<(), ntrip_core::Error> {
125//! let config = NtripConfig::new("rtk2go.com", 2101, "MOUNTPOINT")
126//! .with_credentials("user@example.com", "none");
127//!
128//! let mut client = NtripClient::new(config)?;
129//! client.connect().await?;
130//!
131//! let mut buf = [0u8; 4096];
132//! let n = client.read_chunk(&mut buf).await?;
133//! println!("Received {} bytes of RTCM data", n);
134//! Ok(())
135//! }
136//! ```
137//!
138//! ## Sourcetable Discovery
139//!
140//! ```rust,no_run
141//! use ntrip_core::{NtripClient, NtripConfig};
142//!
143//! # async fn example() -> Result<(), ntrip_core::Error> {
144//! let config = NtripConfig::new("rtk2go.com", 2101, "");
145//! let table = NtripClient::get_sourcetable(&config).await?;
146//!
147//! // Find nearest mountpoint
148//! if let Some((stream, distance_km)) = table.nearest_rtcm_stream(-27.47, 153.02) {
149//! println!("Nearest: {} at {:.1} km", stream.mountpoint, distance_km);
150//! }
151//! # Ok(())
152//! # }
153//! ```
154
155mod client;
156mod config;
157mod error;
158mod gga;
159mod sourcetable;
160mod stream;
161
162// Re-export public API
163pub use client::NtripClient;
164pub use config::{ConnectionConfig, NtripConfig, NtripVersion, ProxyConfig};
165pub use error::Error;
166pub use gga::GgaSentence;
167pub use sourcetable::{CasterEntry, NetworkEntry, Sourcetable, StreamEntry};