mirror of
https://github.com/cjfranko/NTP-Timeturner.git
synced 2025-11-08 18:32:02 +00:00
feat: add PTP support with PHC probe
Co-authored-by: aider (openai/gpt-5) <aider@aider.chat>
This commit is contained in:
parent
2e8bc9ac5e
commit
5ba0421f76
3 changed files with 128 additions and 0 deletions
|
|
@ -21,5 +21,9 @@ log = { version = "0.4", features = ["std"] }
|
||||||
daemonize = "0.5.0"
|
daemonize = "0.5.0"
|
||||||
num-rational = "0.4"
|
num-rational = "0.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "ptp_probe"
|
||||||
|
path = "src/bin/ptp_probe.rs"
|
||||||
|
|
||||||
|
|
|
||||||
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod ptp;
|
||||||
123
src/ptp.rs
Normal file
123
src/ptp.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PtpError {
|
||||||
|
Io(std::io::Error),
|
||||||
|
ChronoOutOfRange,
|
||||||
|
Unsupported,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for PtpError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PtpError::Io(e) => write!(f, "I/O error: {}", e),
|
||||||
|
PtpError::ChronoOutOfRange => write!(f, "Chrono out-of-range error"),
|
||||||
|
PtpError::Unsupported => write!(f, "PTP is unsupported on this platform"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for PtpError {}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for PtpError {
|
||||||
|
fn from(e: std::io::Error) -> Self {
|
||||||
|
PtpError::Io(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod linux {
|
||||||
|
use super::{DateTime, PtpError, TimeZone, Utc};
|
||||||
|
use libc::{clockid_t, timespec, CLOCK_REALTIME};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
|
|
||||||
|
pub struct PtpClock {
|
||||||
|
file: File,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PtpClock {
|
||||||
|
pub fn open(path: &str) -> Result<Self, PtpError> {
|
||||||
|
Ok(Self { file: File::open(path)? })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn fd_to_clockid(fd: i32) -> clockid_t {
|
||||||
|
// Linux CLOCKFD encoding:
|
||||||
|
// #define CLOCKFD 3
|
||||||
|
// #define FD_TO_CLOCKID(fd) ((~(clockid_t)(fd) << 3) | CLOCKFD)
|
||||||
|
((!(fd as clockid_t)) << 3) | 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_timespec(&self) -> Result<timespec, PtpError> {
|
||||||
|
let mut ts = timespec { tv_sec: 0, tv_nsec: 0 };
|
||||||
|
let clk_id = Self::fd_to_clockid(self.file.as_raw_fd());
|
||||||
|
let rc = unsafe { libc::clock_gettime(clk_id, &mut ts as *mut timespec) };
|
||||||
|
if rc != 0 {
|
||||||
|
return Err(PtpError::Io(std::io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
Ok(ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now_datetime(&self) -> Result<DateTime<Utc>, PtpError> {
|
||||||
|
let ts = self.read_timespec()?;
|
||||||
|
let dt = Utc
|
||||||
|
.timestamp_opt(ts.tv_sec as i64, ts.tv_nsec as u32)
|
||||||
|
.single()
|
||||||
|
.ok_or(PtpError::ChronoOutOfRange)?;
|
||||||
|
Ok(dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset_from_system_ns(&self) -> Result<i128, PtpError> {
|
||||||
|
// PTP Hardware Clock (PHC)
|
||||||
|
let phc = self.read_timespec()?;
|
||||||
|
// System realtime clock
|
||||||
|
let mut sys_ts = timespec { tv_sec: 0, tv_nsec: 0 };
|
||||||
|
let rc = unsafe { libc::clock_gettime(CLOCK_REALTIME, &mut sys_ts as *mut timespec) };
|
||||||
|
if rc != 0 {
|
||||||
|
return Err(PtpError::Io(std::io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let phc_ns = (phc.tv_sec as i128) * 1_000_000_000 + (phc.tv_nsec as i128);
|
||||||
|
let sys_ns = (sys_ts.tv_sec as i128) * 1_000_000_000 + (sys_ts.tv_nsec as i128);
|
||||||
|
Ok(phc_ns - sys_ns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_supported() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use PtpClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
mod non_linux {
|
||||||
|
use super::{DateTime, PtpError, Utc};
|
||||||
|
|
||||||
|
pub struct PtpClock;
|
||||||
|
|
||||||
|
impl PtpClock {
|
||||||
|
pub fn open(_path: &str) -> Result<Self, PtpError> {
|
||||||
|
Err(PtpError::Unsupported)
|
||||||
|
}
|
||||||
|
pub fn now_datetime(&self) -> Result<DateTime<Utc>, PtpError> {
|
||||||
|
Err(PtpError::Unsupported)
|
||||||
|
}
|
||||||
|
pub fn offset_from_system_ns(&self) -> Result<i128, PtpError> {
|
||||||
|
Err(PtpError::Unsupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_supported() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use PtpClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use linux::{is_supported, PtpClock};
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub use non_linux::{is_supported, PtpClock};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue