mirror of
https://github.com/cjfranko/NTP-Timeturner.git
synced 2025-11-08 10:22: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"
|
||||
num-rational = "0.4"
|
||||
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