use crate::formats::timestamp::{ShortFormat, TimestampFormat};
use crate::formats::{LeapIndicator, Mode, PrimarySource, ReferenceIdentifier, Stratum, Version};
use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Packet {
pub li: LeapIndicator,
pub vn: Version,
pub mode: Mode,
pub stratum: Stratum,
pub poll: i8, pub precision: i8,
pub delay: ShortFormat,
pub dispersion: ShortFormat,
pub ref_id: ReferenceIdentifier,
pub ref_time: TimestampFormat,
pub orig_time: TimestampFormat,
pub recv_time: TimestampFormat,
pub transmit_time: TimestampFormat,
}
impl Packet {
pub fn new_client() -> Packet {
Packet {
mode: Mode::Client,
vn: Version::Ver2,
transmit_time: TimestampFormat::now(),
..Default::default()
}
}
pub fn try_from<T: ReadBytesExt>(mut rdr: T) -> e_utils::AnyResult<Packet> {
let li_vn_mode = rdr.read_u8()?;
let li = LeapIndicator::from_repr(li_vn_mode >> 6).ok_or("From LeapIndicator")?;
let vn = Version::from_repr((li_vn_mode >> 3) & 0b111).ok_or("From Version")?;
let mode = Mode::from_repr(li_vn_mode & 0b111).ok_or("From Mode")?;
let stratum = Stratum::new(rdr.read_u8()?);
let poll = rdr.read_i8()?;
let precision = rdr.read_i8()?;
let delay = rdr.read_u32::<NetworkEndian>()?.into();
let dispersion = rdr.read_u32::<NetworkEndian>()?.into();
let ref_id_raw = rdr.read_u32::<NetworkEndian>()?.into();
let ref_id = if stratum.primary() {
let source = PrimarySource::from_repr(ref_id_raw).ok_or("From PrimarySource")?;
ReferenceIdentifier::Primary(source)
} else if stratum.secondary() {
ReferenceIdentifier::Secondary(ref_id_raw)
} else {
return Err(format!("Unsupported stratum: {}", stratum.get_value()).into());
};
let ref_time = rdr.read_u64::<NetworkEndian>()?.into();
let orig_time = rdr.read_u64::<NetworkEndian>()?.into();
let recv_time = rdr.read_u64::<NetworkEndian>()?.into();
let transmit_time = rdr.read_u64::<NetworkEndian>()?.into();
Ok(Packet {
li: li,
vn: vn,
mode: mode,
stratum: stratum,
poll: poll,
precision: precision,
delay: delay,
dispersion: dispersion,
ref_id: ref_id,
ref_time: ref_time,
orig_time: orig_time,
recv_time: recv_time,
transmit_time: transmit_time,
})
}
}
impl From<Packet> for Vec<u8> {
fn from(p: Packet) -> Vec<u8> {
let mut buf = Vec::with_capacity(48);
let mut li_vn_mode = 0;
li_vn_mode |= (p.li as u8) << 6;
li_vn_mode |= (p.vn as u8) << 3;
li_vn_mode |= p.mode as u8;
buf.push(li_vn_mode);
buf.push(p.stratum.get_value());
buf.push(p.poll as u8);
buf.push(p.precision as u8);
buf.write_u32::<NetworkEndian>(p.delay.into())
.expect("can't fail");
buf.write_u32::<NetworkEndian>(p.dispersion.into())
.expect("can't fail");
buf.write_u32::<NetworkEndian>(p.ref_id.into())
.expect("can't fail");
buf.write_u64::<NetworkEndian>(p.ref_time.into())
.expect("can't fail");
buf.write_u64::<NetworkEndian>(p.orig_time.into())
.expect("can't fail");
buf.write_u64::<NetworkEndian>(p.recv_time.into())
.expect("can't fail");
buf.write_u64::<NetworkEndian>(p.transmit_time.into())
.expect("can't fail");
buf
}
}
#[cfg(test)]
mod tests {
use super::Packet;
use crate::formats::timestamp::{ShortFormat, TimestampFormat};
use crate::formats::{
LeapIndicator, Mode, PrimarySource, ReferenceIdentifier, Stratum, Version,
};
use std::io::Cursor;
#[test]
fn packet_from_bytes() {
let input = vec![
20u8, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
188, 128, 113, 46, 35, 158, 108,
];
let expected_output = Packet {
li: LeapIndicator::NoWarning,
vn: Version::Ver2,
mode: Mode::Server,
stratum: Stratum::new(1),
poll: 3,
precision: -16,
delay: ShortFormat { sec: 0, frac: 0 },
dispersion: ShortFormat { sec: 0, frac: 24 },
ref_id: ReferenceIdentifier::Primary(PrimarySource::CDMA),
ref_time: TimestampFormat {
sec: 3619455081,
frac: 3332976227,
},
orig_time: TimestampFormat {
sec: 3619402178,
frac: 2670688256,
},
recv_time: TimestampFormat {
sec: 3619455089,
frac: 770500141,
},
transmit_time: TimestampFormat {
sec: 3619455089,
frac: 774086252,
},
};
let rdr = Cursor::new(input);
assert_eq!(expected_output, Packet::try_from(rdr).unwrap());
}
#[test]
fn packet_to_bytes() {
let expected_output = vec![
20, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
188, 128, 113, 46, 35, 158, 108,
];
let input = Packet {
li: LeapIndicator::NoWarning,
vn: Version::Ver2,
mode: Mode::Server,
stratum: Stratum::new(1),
poll: 3,
precision: -16,
delay: ShortFormat { sec: 0, frac: 0 },
dispersion: ShortFormat { sec: 0, frac: 24 },
ref_id: ReferenceIdentifier::Primary(PrimarySource::CDMA),
ref_time: TimestampFormat {
sec: 3619455081,
frac: 3332976227,
},
orig_time: TimestampFormat {
sec: 3619402178,
frac: 2670688256,
},
recv_time: TimestampFormat {
sec: 3619455089,
frac: 770500141,
},
transmit_time: TimestampFormat {
sec: 3619455089,
frac: 774086252,
},
};
let output: Vec<u8> = input.into();
assert_eq!(output, expected_output);
}
#[test]
fn packet_conversion_roundtrip() {
let input = vec![
20, 1, 3, 240, 0, 0, 0, 0, 0, 0, 0, 24, 67, 68, 77, 65, 215, 188, 128, 105, 198, 169,
46, 99, 215, 187, 177, 194, 159, 47, 120, 0, 215, 188, 128, 113, 45, 236, 230, 45, 215,
188, 128, 113, 46, 35, 158, 108,
];
let rdr = Cursor::new(&input);
let output: Vec<u8> = Packet::try_from(rdr).unwrap().into();
assert_eq!(input.as_slice(), output.as_slice());
}
}