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