1pub mod core;
85pub mod protocols;
86pub mod sources;
87
88pub mod gps;
90pub mod network;
91
92pub mod drift;
94pub mod health;
95pub mod quality;
96pub mod statistics;
97
98pub use core::{
100 ClockOffset, ClockSynchronizationConfig, ClockSynchronizationManager,
101 ClockSynchronizationState, ClockSynchronizationStatus, ClockSynchronizer, SynchronizationEvent,
102 SynchronizationResult,
103};
104
105pub use protocols::{
107 BerkeleyConfig, ClockSyncProtocol, CristianConfig, CustomProtocolConfig, NtpConfig, NtpPeer,
108 NtpSynchronizer, NtpTimestamps, ProtocolError, ProtocolManager, PtpConfig, PtpSynchronizer,
109 SntpConfig, SntpSynchronizer,
110};
111
112pub use sources::{
114 AtomicClockType, ClockSource, RadioTimeStation, SourceSelectionAlgorithm,
115 SourceSelectionCriteria, SourceValidation, SystemClockConfig, TimeSource, TimeSourceConfig,
116 TimeSourceManager,
117};
118
119pub use gps::{
121 AntennaConfig, GpsConfig, GpsError, GpsErrorCorrection, GpsReceiverType, GpsSignalProcessing,
122 GpsSynchronizationManager, GpsTime, IonosphericCorrection, SatelliteClockCorrection,
123 TroposphericCorrection,
124};
125
126pub use network::{
128 LoadBalancingAlgorithm, MessagePassingConfig, MessagePriority, NetworkFaultTolerance,
129 NetworkLoadBalancing, NetworkSyncConfig, NetworkSyncError, NetworkSynchronizationManager,
130 NetworkTopology, SyncMessageType,
131};
132
133pub use quality::{
135 ClockAccuracyRequirements, ClockQualityMonitor, QualityAssessment, QualityGrade, QualityMetric,
136 QualityMonitoringConfig, QualityRequirements, QualitySnapshot, QualityThresholds,
137 SourceQualityMonitoring,
138};
139
140pub use drift::{
142 DriftCompensationAlgorithm, DriftCompensationConfig, DriftCompensationError,
143 DriftCompensationStatus, DriftCompensator, DriftMeasurement, DriftMeasurementConfig,
144 DriftModel, DriftPredictionConfig, DriftPredictionEngine,
145};
146
147pub use health::{
149 AlertConfiguration, AlertSeverity, HealthAlert, HealthCheck, HealthCheckType,
150 HealthMonitorConfig, HealthMonitorError, HealthStatus, HealthThresholds, RecoveryConfiguration,
151 SourceFailoverConfig, SourceHealthMonitor,
152};
153
154pub use statistics::{
156 ClockStatistics, PerformanceHistory, PerformanceMeasurement, PerformanceReport,
157 PerformanceTracking, QualityReporting, ReliabilityStatistics, ReportGeneration,
158 StatisticsCollector, StatisticsError, TrendDirection,
159};
160
161pub type Result<T> = std::result::Result<T, ClockSynchronizationError>;
163pub type Duration = std::time::Duration;
164pub type Instant = std::time::Instant;
165
166#[derive(Debug)]
168pub enum ClockSynchronizationError {
169 CoreError(core::ClockSynchronizationError),
171 ProtocolError(protocols::ProtocolError),
173 SourceError(sources::SourceManagementError),
175 GpsError(gps::GpsError),
177 NetworkError(network::NetworkSyncError),
179 QualityError(quality::QualityMonitorError),
181 DriftError(drift::DriftCompensationError),
183 HealthError(health::HealthMonitorError),
185 StatisticsError(statistics::StatisticsError),
187 ConfigurationError(String),
189 SystemError(String),
191}
192
193impl std::fmt::Display for ClockSynchronizationError {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 match self {
196 ClockSynchronizationError::CoreError(e) => {
197 write!(f, "Core synchronization error: {}", e)
198 }
199 ClockSynchronizationError::ProtocolError(e) => write!(f, "{}", e),
205 ClockSynchronizationError::SourceError(e) => {
206 write!(f, "Source management error: {}", e)
207 }
208 ClockSynchronizationError::GpsError(e) => write!(f, "GPS synchronization error: {}", e),
209 ClockSynchronizationError::NetworkError(e) => {
210 write!(f, "Network synchronization error: {}", e)
211 }
212 ClockSynchronizationError::QualityError(e) => {
213 write!(f, "Quality monitoring error: {}", e)
214 }
215 ClockSynchronizationError::DriftError(e) => {
216 write!(f, "Drift compensation error: {}", e)
217 }
218 ClockSynchronizationError::HealthError(e) => {
219 write!(f, "Health monitoring error: {}", e)
220 }
221 ClockSynchronizationError::StatisticsError(e) => write!(f, "Statistics error: {}", e),
222 ClockSynchronizationError::ConfigurationError(msg) => {
223 write!(f, "Configuration error: {}", msg)
224 }
225 ClockSynchronizationError::SystemError(msg) => write!(f, "System error: {}", msg),
226 }
227 }
228}
229
230impl std::error::Error for ClockSynchronizationError {}
231
232impl From<core::ClockSynchronizationError> for ClockSynchronizationError {
234 fn from(err: core::ClockSynchronizationError) -> Self {
235 ClockSynchronizationError::CoreError(err)
236 }
237}
238
239impl From<protocols::ProtocolError> for ClockSynchronizationError {
240 fn from(err: protocols::ProtocolError) -> Self {
241 ClockSynchronizationError::ProtocolError(err)
242 }
243}
244
245impl From<sources::SourceManagementError> for ClockSynchronizationError {
246 fn from(err: sources::SourceManagementError) -> Self {
247 ClockSynchronizationError::SourceError(err)
248 }
249}
250
251impl From<gps::GpsError> for ClockSynchronizationError {
252 fn from(err: gps::GpsError) -> Self {
253 ClockSynchronizationError::GpsError(err)
254 }
255}
256
257impl From<network::NetworkSyncError> for ClockSynchronizationError {
258 fn from(err: network::NetworkSyncError) -> Self {
259 ClockSynchronizationError::NetworkError(err)
260 }
261}
262
263impl From<quality::QualityMonitorError> for ClockSynchronizationError {
264 fn from(err: quality::QualityMonitorError) -> Self {
265 ClockSynchronizationError::QualityError(err)
266 }
267}
268
269impl From<drift::DriftCompensationError> for ClockSynchronizationError {
270 fn from(err: drift::DriftCompensationError) -> Self {
271 ClockSynchronizationError::DriftError(err)
272 }
273}
274
275impl From<health::HealthMonitorError> for ClockSynchronizationError {
276 fn from(err: health::HealthMonitorError) -> Self {
277 ClockSynchronizationError::HealthError(err)
278 }
279}
280
281impl From<statistics::StatisticsError> for ClockSynchronizationError {
282 fn from(err: statistics::StatisticsError) -> Self {
283 ClockSynchronizationError::StatisticsError(err)
284 }
285}
286
287impl From<scirs2_core::CoreError> for ClockSynchronizationError {
288 fn from(err: scirs2_core::CoreError) -> Self {
289 ClockSynchronizationError::SystemError(err.to_string())
290 }
291}
292
293#[derive(Debug)]
298pub struct ClockSynchronizationBuilder {
299 core_config: Option<core::ClockSynchronizationConfig>,
300 protocol_configs: Vec<protocols::ClockSyncProtocol>,
301 source_configs: Vec<sources::TimeSource>,
302 gps_config: Option<gps::GpsConfig>,
303 network_config: Option<network::NetworkSyncConfig>,
304 quality_config: Option<quality::QualityMonitoringConfig>,
305 drift_config: Option<drift::DriftCompensationConfig>,
306 health_config: Option<health::HealthMonitorConfig>,
307 statistics_config: Option<statistics::StatisticsCollectionConfig>,
308}
309
310impl ClockSynchronizationBuilder {
311 pub fn new() -> Self {
313 Self {
314 core_config: None,
315 protocol_configs: Vec::new(),
316 source_configs: Vec::new(),
317 gps_config: None,
318 network_config: None,
319 quality_config: None,
320 drift_config: None,
321 health_config: None,
322 statistics_config: None,
323 }
324 }
325
326 pub fn with_core_config(mut self, config: core::ClockSynchronizationConfig) -> Self {
328 self.core_config = Some(config);
329 self
330 }
331
332 pub fn with_protocol(mut self, protocol: protocols::ClockSyncProtocol) -> Self {
334 self.protocol_configs.push(protocol);
335 self
336 }
337
338 pub fn with_source(mut self, source: sources::TimeSource) -> Self {
340 self.source_configs.push(source);
341 self
342 }
343
344 pub fn with_gps_config(mut self, config: gps::GpsConfig) -> Self {
346 self.gps_config = Some(config);
347 self
348 }
349
350 pub fn with_network_config(mut self, config: network::NetworkSyncConfig) -> Self {
352 self.network_config = Some(config);
353 self
354 }
355
356 pub fn with_quality_config(mut self, config: quality::QualityMonitoringConfig) -> Self {
358 self.quality_config = Some(config);
359 self
360 }
361
362 pub fn with_drift_config(mut self, config: drift::DriftCompensationConfig) -> Self {
364 self.drift_config = Some(config);
365 self
366 }
367
368 pub fn with_health_config(mut self, config: health::HealthMonitorConfig) -> Self {
370 self.health_config = Some(config);
371 self
372 }
373
374 pub fn with_statistics_config(
376 mut self,
377 config: statistics::StatisticsCollectionConfig,
378 ) -> Self {
379 self.statistics_config = Some(config);
380 self
381 }
382
383 pub fn build(self) -> Result<ClockSynchronizationManager> {
385 let core_config = self.core_config.unwrap_or_default();
386
387 let mut manager = ClockSynchronizationManager::new();
389 manager.config = core_config;
390
391 for protocol in self.protocol_configs {
393 manager.add_protocol(protocol)?;
394 }
395
396 for source in self.source_configs {
398 manager.add_time_source(source)?;
399 }
400
401 if let Some(gps_config) = self.gps_config {
403 manager.configure_gps(gps_config)?;
404 }
405
406 if let Some(network_config) = self.network_config {
407 manager.configure_network(network_config)?;
408 }
409
410 if let Some(quality_config) = self.quality_config {
411 manager.configure_quality_monitoring(quality_config)?;
412 }
413
414 if let Some(drift_config) = self.drift_config {
415 manager.configure_drift_compensation(drift_config)?;
416 }
417
418 if let Some(health_config) = self.health_config {
419 manager.configure_health_monitoring(health_config)?;
420 }
421
422 if let Some(statistics_config) = self.statistics_config {
423 manager.configure_statistics(statistics_config)?;
424 }
425
426 Ok(manager)
427 }
428}
429
430impl Default for ClockSynchronizationBuilder {
431 fn default() -> Self {
432 Self::new()
433 }
434}
435
436pub mod utils {
438 use super::*;
439
440 pub fn create_ntp_sync_manager(
449 ntp_servers: Vec<String>,
450 ) -> Result<ClockSynchronizationManager> {
451 if ntp_servers.is_empty() {
452 return Err(ClockSynchronizationError::ConfigurationError(
453 "an NTP synchronization manager needs at least one server address".to_string(),
454 ));
455 }
456
457 let mut builder = ClockSynchronizationBuilder::new();
458
459 builder = builder.with_protocol(protocols::ClockSyncProtocol::NTP);
461
462 for server in ntp_servers {
464 builder = builder.with_source(sources::TimeSource {
465 source_type: sources::ClockSource::NTP,
466 address: Some(server),
467 });
468 }
469
470 builder = builder.with_quality_config(quality::QualityMonitoringConfig::default());
472 builder = builder.with_health_config(health::HealthMonitorConfig::default());
473
474 builder.build()
475 }
476
477 pub fn create_gps_sync_manager(
479 gps_config: gps::GpsConfig,
480 ) -> Result<ClockSynchronizationManager> {
481 let mut builder = ClockSynchronizationBuilder::new();
482
483 builder = builder.with_gps_config(gps_config.clone());
485
486 let source = sources::TimeSource {
489 source_type: sources::ClockSource::GPS,
490 address: None,
491 };
492 builder = builder.with_source(source);
493
494 builder = builder.with_quality_config(quality::QualityMonitoringConfig::default());
496 builder = builder.with_drift_config(drift::DriftCompensationConfig::default());
497 builder = builder.with_health_config(health::HealthMonitorConfig::default());
498
499 builder.build()
500 }
501
502 pub fn create_precision_sync_manager() -> Result<ClockSynchronizationManager> {
504 let mut builder = ClockSynchronizationBuilder::new();
505
506 builder = builder.with_protocol(protocols::ClockSyncProtocol::PTP);
508
509 let source = sources::TimeSource {
512 source_type: sources::ClockSource::Atomic,
513 address: None,
514 };
515 builder = builder.with_source(source);
516
517 builder = builder.with_quality_config(quality::QualityMonitoringConfig::default());
519 builder = builder.with_drift_config(drift::DriftCompensationConfig::default());
520 builder = builder.with_health_config(health::HealthMonitorConfig::default());
521 builder = builder.with_statistics_config(statistics::StatisticsCollectionConfig::default());
522
523 builder.build()
524 }
525
526 pub fn duration_to_string(duration: Duration) -> String {
528 let total_seconds = duration.as_secs();
529 let days = total_seconds / 86400;
530 let hours = (total_seconds % 86400) / 3600;
531 let minutes = (total_seconds % 3600) / 60;
532 let seconds = total_seconds % 60;
533 let millis = duration.subsec_millis();
534 let micros = duration.subsec_micros() % 1000;
535 let nanos = duration.subsec_nanos() % 1000;
536
537 if days > 0 {
538 format!("{}d {}h {}m {}s", days, hours, minutes, seconds)
539 } else if hours > 0 {
540 format!("{}h {}m {}s", hours, minutes, seconds)
541 } else if minutes > 0 {
542 format!("{}m {}s", minutes, seconds)
543 } else if seconds > 0 {
544 format!("{}.{:03}s", seconds, millis)
545 } else if millis > 0 {
546 format!("{}.{:03}ms", millis, micros)
547 } else if micros > 0 {
548 format!("{}.{:03}μs", micros, nanos)
549 } else {
550 format!("{}ns", nanos)
551 }
552 }
553
554 pub fn validate_clock_offset(
556 offset: ClockOffset,
557 requirements: &quality::ClockAccuracyRequirements,
558 ) -> bool {
559 offset.offset_ns.abs() as f64 <= requirements.max_drift_ppm
560 }
561
562 pub fn calculate_quality_score(metrics: &std::collections::HashMap<String, f64>) -> f64 {
564 if metrics.is_empty() {
565 return 0.0;
566 }
567
568 let sum: f64 = metrics.values().sum();
569 sum / metrics.len() as f64
570 }
571
572 pub fn process_uptime() -> Duration {
589 static PROCESS_START: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
590 let start = *PROCESS_START.get_or_init(Instant::now);
591 start.elapsed()
592 }
593}
594
595pub mod prelude {}
597
598#[cfg(test)]
600mod tests {
601 use super::*;
602
603 #[test]
604 fn test_builder_pattern() {
605 let builder =
606 ClockSynchronizationBuilder::new().with_protocol(protocols::ClockSyncProtocol::NTP);
607
608 assert!(builder.protocol_configs.len() == 1);
610 }
611
612 #[test]
613 fn test_error_conversions() {
614 let core_error = core::ClockSynchronizationError;
615 let sync_error: ClockSynchronizationError = core_error.into();
616
617 match sync_error {
619 ClockSynchronizationError::CoreError(_) => {}
620 _ => panic!("Error conversion failed"),
621 }
622 }
623
624 #[test]
625 fn test_utility_functions() {
626 let duration = Duration::from_millis(1500);
628 let formatted = utils::duration_to_string(duration);
629 assert!(formatted.contains("s"));
630
631 let mut metrics = std::collections::HashMap::new();
633 metrics.insert("accuracy".to_string(), 0.9);
634 metrics.insert("stability".to_string(), 0.8);
635 let score = utils::calculate_quality_score(&metrics);
636 assert!((score - 0.85).abs() < 1e-10);
637 }
638
639 #[test]
645 fn process_uptime_reflects_real_elapsed_time_not_a_fixed_constant() {
646 let first = utils::process_uptime();
647 std::thread::sleep(Duration::from_millis(30));
648 let second = utils::process_uptime();
649
650 assert!(
651 second > first,
652 "process_uptime must strictly increase with real elapsed time \
653 (first={first:?}, second={second:?})"
654 );
655 assert!(
656 second - first >= Duration::from_millis(20),
657 "process_uptime delta should reflect the real 30ms sleep, got {:?}",
658 second - first
659 );
660 assert_ne!(
661 second,
662 Duration::from_secs(86400),
663 "must not be the old hardcoded fabricated constant"
664 );
665 }
666}