├── examples └── .git-keep ├── statime-linux ├── docs ├── COPYRIGHT ├── README.md ├── CHANGELOG.md ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── metrics │ │ └── mod.rs │ ├── lib.rs │ ├── tracing.rs │ ├── tlvforwarder.rs │ ├── socket.rs │ └── observer.rs ├── bin │ └── statime-metrics-exporter.rs ├── build.rs └── Cargo.toml ├── statime ├── COPYRIGHT ├── LICENSE-MIT ├── README.md ├── LICENSE-APACHE ├── src │ ├── datastructures │ │ ├── datasets │ │ │ ├── current.rs │ │ │ ├── mod.rs │ │ │ ├── path_trace.rs │ │ │ ├── parent.rs │ │ │ ├── default.rs │ │ │ └── time_properties.rs │ │ ├── common │ │ │ ├── leap_indicator.rs │ │ │ ├── mod.rs │ │ │ ├── port_identity.rs │ │ │ ├── time_source.rs │ │ │ ├── timestamp.rs │ │ │ ├── clock_identity.rs │ │ │ ├── clock_quality.rs │ │ │ ├── time_interval.rs │ │ │ └── clock_accuracy.rs │ │ ├── messages │ │ │ ├── signalling.rs │ │ │ ├── control_field.rs │ │ │ ├── delay_req.rs │ │ │ ├── sync.rs │ │ │ ├── p_delay_req.rs │ │ │ ├── management.rs │ │ │ ├── follow_up.rs │ │ │ ├── delay_resp.rs │ │ │ ├── p_delay_resp.rs │ │ │ └── p_delay_resp_follow_up.rs │ │ └── mod.rs │ ├── bmc │ │ ├── mod.rs │ │ └── acceptable_master.rs │ ├── time │ │ ├── mod.rs │ │ └── interval.rs │ ├── port │ │ ├── sequence_id.rs │ │ ├── measurement.rs │ │ └── state.rs │ ├── observability │ │ ├── mod.rs │ │ ├── current.rs │ │ ├── parent.rs │ │ ├── default.rs │ │ └── port.rs │ ├── config │ │ ├── mod.rs │ │ ├── instance.rs │ │ └── port.rs │ ├── float_polyfill.rs │ ├── shared_clock.rs │ ├── filters │ │ └── mod.rs │ ├── overlay_clock.rs │ ├── clock.rs │ └── lib.rs └── Cargo.toml ├── statime-stm32 ├── COPYRIGHT ├── LICENSE-MIT ├── LICENSE-APACHE ├── rust-toolchain.toml ├── README.md ├── .cargo │ └── config.toml ├── src │ ├── ptp_state_mutex.rs │ └── ptp_clock.rs └── Cargo.toml ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── message_sound.rs ├── docs ├── examples │ └── conf │ │ ├── statime.preset │ │ ├── 41-statime.rules │ │ ├── statime.toml.default │ │ ├── statime-metrics-exporter.service │ │ ├── statime.service │ │ └── statime-61850.toml ├── index.md ├── development │ ├── networking.md │ ├── diagrams │ │ └── measurement.puml │ └── development-process.md ├── man │ ├── statime.8.md │ └── statime-metrics-exporter.8.md ├── precompiled │ └── man │ │ ├── statime.8 │ │ └── statime-metrics-exporter.8 └── guide │ └── getting-started.md ├── utils ├── release.sh ├── pandoc.sh ├── precompiled-man-diff.sh ├── mkdocs.sh ├── generate-man.sh ├── update-version.sh ├── build-release.sh └── prepare-artifacts.sh ├── .github ├── codecov.yml ├── dependabot.yml └── workflows │ ├── docs.yml │ └── rust.yml ├── validation ├── 08-04-2022 │ ├── gm_ref_offset.pdf │ ├── gm_statime_offset.pdf │ ├── measurement_report.pdf │ ├── gm_clocks_overtime_ref.pdf │ ├── gm_clocks_per_sec_ref.pdf │ ├── gm_ref_offset_overtime.pdf │ ├── gm_clocks_per_sec_statime.pdf │ ├── gm_clocks_overtime_statime.pdf │ ├── gm_statime_offset_overtime.pdf │ ├── sys_statime_offset_overtime.gnuplot │ ├── sys_ref_offset_overtime.gnuplot │ ├── gmsecs_overtime_ref.gnuplot │ ├── sys_ref_offset.gnuplot │ ├── sys_statime_offset.gnuplot │ ├── gmsecs_overtime_statime.gnuplot │ ├── gmsecs_stat_ref.gnuplot │ └── gm_secs_stat_statime.gnuplot ├── 24-11-2023-boundary-clock │ ├── i210-bc-offset.png │ ├── i210-direct-offset.png │ ├── i210-bc-offset.gnuplot │ ├── i210-direct-offset.gnuplot │ └── report.md └── 24-05-2022-hardware-timestamping │ ├── gm_ref_offset.pdf │ ├── gm_secs_stat_ref.pdf │ ├── gm_statime_offset.pdf │ ├── measurement_report.pdf │ ├── gm_secs_overtime_ref.pdf │ ├── gm_secs_stat_statime.pdf │ ├── gm_ref_offset_overtime.pdf │ ├── gm_secs_overtime_statime.pdf │ ├── gm_statime_offset_overtime.pdf │ ├── gm_ref_offset_overtime.gnuplot │ ├── gm_statime_offset_overtime.gnuplot │ ├── gm_secs_overtime_statime.gnuplot │ ├── gm_secs_overtime_ref.gnuplot │ ├── gm_ref_offset.gnuplot │ ├── gm_statime_offset.gnuplot │ ├── gm_secs_stat_ref.gnuplot │ └── gm_secs_stat_statime.gnuplot ├── pkg ├── deb │ ├── prerm │ ├── COPYRIGHT-debian │ ├── postrm │ └── postinst ├── test-scripts │ └── test-statime.sh └── rpm │ └── scriptlets.toml ├── .rustfmt.toml ├── COPYRIGHT ├── .gitignore ├── statime.toml ├── mkdocs.yml ├── LICENSE-MIT ├── Cargo.toml ├── testing-rpi ├── README.md ├── 0002-add-DT-overlay-for-pps-gen-gpio-generator.patch └── 0003-Updated-gpio-pps-to-work-with-timespec64.patch ├── CHANGELOG.md └── README.md /examples/.git-keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /statime-linux/docs: -------------------------------------------------------------------------------- 1 | ../docs -------------------------------------------------------------------------------- /statime/COPYRIGHT: -------------------------------------------------------------------------------- 1 | ../COPYRIGHT -------------------------------------------------------------------------------- /statime/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /statime/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /statime-linux/COPYRIGHT: -------------------------------------------------------------------------------- 1 | ../COPYRIGHT -------------------------------------------------------------------------------- /statime-linux/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /statime-stm32/COPYRIGHT: -------------------------------------------------------------------------------- 1 | ../COPYRIGHT -------------------------------------------------------------------------------- /statime-linux/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ../CHANGELOG.md -------------------------------------------------------------------------------- /statime-linux/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /statime-stm32/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /statime/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /statime-linux/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /statime-stm32/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | -------------------------------------------------------------------------------- /docs/examples/conf/statime.preset: -------------------------------------------------------------------------------- 1 | disable statime.service 2 | -------------------------------------------------------------------------------- /statime-linux/src/metrics/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod exporter; 2 | mod format; 3 | -------------------------------------------------------------------------------- /docs/examples/conf/41-statime.rules: -------------------------------------------------------------------------------- 1 | KERNEL=="ptp[0-9]*", SUBSYSTEM=="ptp", GROUP="statime", MODE="0660" 2 | -------------------------------------------------------------------------------- /utils/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cargo publish -p statime 4 | cargo publish -p statime-linux 5 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | patch: off 4 | project: off 5 | ignore: 6 | - "test-binaries" 7 | -------------------------------------------------------------------------------- /utils/pandoc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | exec docker run --rm -v "$(pwd):/data" -u "$(id -u):$(id -g)" pandoc/core "$@" 4 | -------------------------------------------------------------------------------- /validation/08-04-2022/gm_ref_offset.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_ref_offset.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/gm_statime_offset.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_statime_offset.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/measurement_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/measurement_report.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/gm_clocks_overtime_ref.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_clocks_overtime_ref.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/gm_clocks_per_sec_ref.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_clocks_per_sec_ref.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/gm_ref_offset_overtime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_ref_offset_overtime.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/gm_clocks_per_sec_statime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_clocks_per_sec_statime.pdf -------------------------------------------------------------------------------- /statime-stm32/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = ["rustfmt", "clippy", "llvm-tools"] 4 | targets = ["thumbv7em-none-eabihf"] 5 | -------------------------------------------------------------------------------- /validation/08-04-2022/gm_clocks_overtime_statime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_clocks_overtime_statime.pdf -------------------------------------------------------------------------------- /validation/08-04-2022/gm_statime_offset_overtime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/08-04-2022/gm_statime_offset_overtime.pdf -------------------------------------------------------------------------------- /validation/24-11-2023-boundary-clock/i210-bc-offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-11-2023-boundary-clock/i210-bc-offset.png -------------------------------------------------------------------------------- /validation/24-11-2023-boundary-clock/i210-direct-offset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-11-2023-boundary-clock/i210-direct-offset.png -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_ref_offset.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_ref_offset.pdf -------------------------------------------------------------------------------- /statime-linux/bin/statime-metrics-exporter.rs: -------------------------------------------------------------------------------- 1 | #[tokio::main] 2 | async fn main() -> Result<(), Box> { 3 | statime_linux::metrics_exporter_main().await 4 | } 5 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_stat_ref.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_secs_stat_ref.pdf -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_statime_offset.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_statime_offset.pdf -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/measurement_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/measurement_report.pdf -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_overtime_ref.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_secs_overtime_ref.pdf -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_stat_statime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_secs_stat_statime.pdf -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_ref_offset_overtime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_ref_offset_overtime.pdf -------------------------------------------------------------------------------- /statime/src/datastructures/datasets/current.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] 2 | pub(crate) struct InternalCurrentDS { 3 | pub(crate) steps_removed: u16, 4 | } 5 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_overtime_statime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_secs_overtime_statime.pdf -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_statime_offset_overtime.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pendulum-project/statime/HEAD/validation/24-05-2022-hardware-timestamping/gm_statime_offset_overtime.pdf -------------------------------------------------------------------------------- /statime/src/bmc/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the best master clock datastructures and logic 2 | 3 | pub mod acceptable_master; 4 | pub mod bmca; 5 | pub mod dataset_comparison; 6 | pub mod foreign_master; 7 | -------------------------------------------------------------------------------- /utils/precompiled-man-diff.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | rm -rf target/docs/man 6 | utils/generate-man.sh target/docs/man 7 | 8 | exec diff -r -s --color "docs/precompiled/man" "target/docs/man" 9 | -------------------------------------------------------------------------------- /validation/24-11-2023-boundary-clock/i210-bc-offset.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-6000:3000] 2 | set xrange [0:7200] 3 | set xlabel "time (s)" 4 | set ylabel "offset (ns)" 5 | plot 'i210-bc-offset.dat' using ($1*10) pt 7 black title "Offset to GM" 6 | -------------------------------------------------------------------------------- /validation/24-11-2023-boundary-clock/i210-direct-offset.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-6000:3000] 2 | set xrange [0:7200] 3 | set xlabel "time (s)" 4 | set ylabel "offset (ns)" 5 | plot 'i210-direct-offset.dat' using ($1*10) pt 7 black title "Offset to GM" 6 | -------------------------------------------------------------------------------- /pkg/deb/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ -d /run/systemd/system ] && [ "$1" = remove ]; then 5 | deb-systemd-invoke stop statime.service >/dev/null || true 6 | deb-systemd-invoke stop statime-metrics-exporter.service >/dev/null || true 7 | fi 8 | -------------------------------------------------------------------------------- /validation/08-04-2022/sys_statime_offset_overtime.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-95000:45000] 2 | set xtics (0) 3 | set xlabel "time (arbitrary)" 4 | set ylabel "offset (ns)" 5 | plot '../meetlogboek/statime-soft-31-03-2022-0928-analysis/sys-offset.dat' using ($1*10.00003620) pt 7 black title "Offset of system clock to GM" 6 | -------------------------------------------------------------------------------- /validation/08-04-2022/sys_ref_offset_overtime.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-4250:-3150] 2 | set xtics (0, "glitch" 4095) 3 | set xlabel "time (arbitrary)" 4 | set ylabel "offset (ns)" 5 | plot '../meetlogboek/ref-18-03-2022-1446-analysis/sys-offset.dat' using ($1*10.00004010) pt 7 black title "Offset of system clock to GM" 6 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_ref_offset_overtime.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-3600:-3300] 2 | set xlabel "time (arbitrary)" 3 | set ylabel "offset (ns)" 4 | set xtics (0) 5 | plot '../meetlogboek/ref-10-05-2022-1342-analysis/nic-offset.dat' using ($1*10.00004270) pt 7 black title "Offset of system clock to GM" 6 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_statime_offset_overtime.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-6000:2000] 2 | set xtics (0) 3 | set xlabel "time (arbitrary)" 4 | set ylabel "offset (ns)" 5 | plot '../meetlogboek/statime-hard-10-05-2022-1456-analysis/nic-offset.dat' using ($1*10.00004300) pt 7 black title "Offset of clock to GM" 6 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | unstable_features = true 2 | format_macro_matchers = true 3 | format_strings = true 4 | hex_literal_case = "Lower" 5 | imports_granularity = "Crate" 6 | normalize_comments = true 7 | normalize_doc_attributes = true 8 | group_imports = "StdExternalCrate" 9 | use_try_shorthand = true 10 | wrap_comments = true 11 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Statime 2 | 3 | Statime is an implementation of PTP for use under linux and [as a library](https://docs.rs/statime/latest/statime/). It is dual-licensed under the APACHE and MIT licenses. To start using statime to synchronize the time of your linux machine, see our [getting started guide](guide/getting-started.md). 4 | -------------------------------------------------------------------------------- /statime-stm32/README.md: -------------------------------------------------------------------------------- 1 | # Statime STM32 2 | 3 | This is an example program for running the statime PTP stack on a microcontroller using hardware timestamping. 4 | 5 | It uses RTIC as its executor and runs various tasks for running statime and smoltcp. 6 | Additionally it enables a PPS pin that runs on the clock that is being synchronized. 7 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-2023 Tweede Golf and Contributors 2 | 3 | Except as otherwise noted (below and/or in individual files), statime is 4 | licensed under the Apache License, Version 2.0 or 5 | or the MIT license 6 | or , at your option. 7 | -------------------------------------------------------------------------------- /utils/mkdocs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | bind_port_arg=(-p "127.0.0.1:8000:8000") 4 | if [ "$#" -gt 0 ]; then 5 | if [ "$1" == "--no-bind-port" ]; then 6 | shift 7 | bind_port_arg=() 8 | fi 9 | fi 10 | 11 | exec docker run --rm "${bind_port_arg[@]}" -v "${PWD}:/docs" -u "$(id -u):$(id -g)" squidfunk/mkdocs-material "$@" 12 | -------------------------------------------------------------------------------- /validation/08-04-2022/gmsecs_overtime_ref.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-407:-393] 2 | set xtics (0, "glitch" 4095) 3 | set ytics ("10^8-400" -400, -405, -395) 4 | set xlabel "time (arbitrary)" 5 | set ylabel "clock cycles per second" 6 | plot '../meetlogboek/ref-18-03-2022-1446-analysis/gm-interval.dat' using ($1-100000000) pt 7 black title "Period of GM clock in cycles" 7 | -------------------------------------------------------------------------------- /docs/development/networking.md: -------------------------------------------------------------------------------- 1 | # Timestamping 2 | - https://www.kernel.org/doc/html/latest/networking/timestamping.html 3 | - https://github.com/nix-rust/nix/pull/1547 4 | - https://linux.die.net/man/2/recvmsg 5 | - https://github.com/ptpd/ptpd/blob/1ec9e650b03e6bd75dd3179fb5f09862ebdc54bf/src/dep/net.c 6 | - Wireshark filter: `ip.addr >= 224.0.0.0 and (igmp or ptp)` 7 | -------------------------------------------------------------------------------- /statime/src/datastructures/datasets/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use current::InternalCurrentDS; 2 | pub(crate) use default::InternalDefaultDS; 3 | pub(crate) use parent::InternalParentDS; 4 | pub use path_trace::PathTraceDS; 5 | pub use time_properties::TimePropertiesDS; 6 | 7 | mod current; 8 | mod default; 9 | mod parent; 10 | mod path_trace; 11 | mod time_properties; 12 | -------------------------------------------------------------------------------- /validation/08-04-2022/sys_ref_offset.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 10.00004010 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set xrange [-4250:-3150] 5 | set xlabel "offset (ns)" 6 | set ytics (0) 7 | set yrange [0:500] 8 | plot '../meetlogboek/ref-18-03-2022-1446-analysis/sys-offset.dat' using ($1*10.00004010) smooth freq with boxes fillcolor black title "# of measurements" 9 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_overtime_statime.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-440:-370] 2 | set xtics (0) 3 | set ytics ("10^8-400" -400, -380, -390, -410, -420, -430) 4 | set xlabel "time (arbitrary)" 5 | set ylabel "clock cycles per second" 6 | plot '../meetlogboek/statime-hard-10-05-2022-1456-analysis/gm-interval.dat' using ($1-100000000) pt 7 black title "Period of GM clock in cycles" 7 | -------------------------------------------------------------------------------- /validation/08-04-2022/sys_statime_offset.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 10.00003620 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set xrange [-95000:35000] 5 | set xlabel "offset (ns)" 6 | set ytics (0) 7 | set yrange [0:20] 8 | plot '../meetlogboek/statime-soft-31-03-2022-0928-analysis/sys-offset.dat' using ($1*10.00003620) smooth freq with boxes fillcolor black title "# of measurements" 9 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_overtime_ref.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-440:-370] 2 | set xtics (0, "glitch" 4095) 3 | set ytics ("10^8-400" -400, -410, -390, -380, -420, -430) 4 | set xlabel "time (arbitrary)" 5 | set ylabel "clock cycles per second" 6 | plot '../meetlogboek/ref-10-05-2022-1342-analysis/gm-interval.dat' using ($1-100000000) pt 7 black title "Period of GM clock in cycles" 7 | -------------------------------------------------------------------------------- /validation/08-04-2022/gmsecs_overtime_statime.gnuplot: -------------------------------------------------------------------------------- 1 | set yrange [-375:-325] 2 | set xtics (0) 3 | set ytics ("10^8-350" -350, -325, -330, -335, -340, -345, -355, -360, -365, -370, -375) 4 | set xlabel "time (arbitrary)" 5 | set ylabel "clock cycles per second" 6 | plot '../meetlogboek/statime-soft-31-03-2022-0928-analysis/gm-interval.dat' using ($1-100000000) pt 7 black title "Period of GM clock in cycles" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | /docs/book 3 | validation/measurement_report.aux 4 | validation/measurement_report.fdb_latexmk 5 | validation/measurement_report.fls 6 | validation/measurement_report.log 7 | validation-ht/measurement_report.aux 8 | validation-ht/measurement_report.fdb_latexmk 9 | validation-ht/measurement_report.fls 10 | validation-ht/measurement_report.log 11 | *swp 12 | **/.idea 13 | **/.zed 14 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_ref_offset.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 10.00004270 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set xrange [-3550:-3350] 5 | set xlabel "offset (ns)" 6 | set ytics (0) 7 | set yrange [0:600] 8 | plot '../meetlogboek/ref-10-05-2022-1342-analysis/nic-offset.dat' using ($1*10.00004270) smooth freq with boxes fillcolor black title "# of measurements" 9 | -------------------------------------------------------------------------------- /pkg/deb/COPYRIGHT-debian: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-2023 Tweede Golf and Contributors 2 | 3 | Except as otherwise noted (below and/or in individual files), statime is 4 | licensed under the Apache License, Version 2.0 or 5 | or 6 | or the MIT license or 7 | , at your option. 8 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_statime_offset.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 10.00004300 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set xrange [-6000:2000] 5 | set xlabel "offset (ns)" 6 | set ytics (0) 7 | set yrange [0:35] 8 | plot '../meetlogboek/statime-hard-10-05-2022-1456-analysis/nic-offset.dat' using ($1*10.00004300) smooth freq with boxes fillcolor black title "# of measurements" 9 | -------------------------------------------------------------------------------- /validation/08-04-2022/gmsecs_stat_ref.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 1 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set ytics (0) 5 | set xtics ("10^8-400" -400, -405, -395) scale 0 6 | set xlabel "Clock cycles per second" 7 | set xrange [-408:-392] 8 | set yrange [0:2700] 9 | plot '../meetlogboek/ref-18-03-2022-1446-analysis/gm-interval.dat' using ($1-100000000) smooth freq with boxes fillcolor black title "# of measurements" 10 | -------------------------------------------------------------------------------- /statime.toml: -------------------------------------------------------------------------------- 1 | loglevel = "info" # Other values include trace, debug, warn and error 2 | 3 | # which ptp network to participate in? 4 | sdo-id = 0 5 | domain = 0 6 | 7 | # Add one of these per network interface in your system 8 | #[[port]] 9 | #interface = "" 10 | #network-mode = "ipv4" #can also be ipv6 or ethernet 11 | # If the network interface has hardware clock support, you can enable it with 12 | #hardware-clock = 13 | -------------------------------------------------------------------------------- /statime/src/time/mod.rs: -------------------------------------------------------------------------------- 1 | //! Types that describe points in time ([`Time`]), and durations between two 2 | //! instants ([`Duration`], [`Interval`]) 3 | //! 4 | //! These are used throughout `statime` instead of types from [`std::time`] as 5 | //! they fit closer with the on the wire representation of time in PTP. 6 | 7 | mod duration; 8 | mod instant; 9 | mod interval; 10 | 11 | pub use duration::Duration; 12 | pub use instant::Time; 13 | pub use interval::Interval; 14 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_stat_ref.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 1 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set ytics (0) 5 | set xtics ("10^8-400" -400, -410, -420, -430, -390) scale 0 6 | set xlabel "Clock cycles per second" 7 | set xrange [-437:-380] 8 | set yrange [0:500] 9 | plot '../meetlogboek/ref-10-05-2022-1342-analysis/gm-interval.dat' using ($1-100000000) smooth freq with boxes fillcolor black title "# of measurements" 10 | -------------------------------------------------------------------------------- /docs/examples/conf/statime.toml.default: -------------------------------------------------------------------------------- 1 | loglevel = "info" # Other values include trace, debug, warn and error 2 | 3 | # which ptp network to participate in? 4 | sdo-id = 0 5 | domain = 0 6 | 7 | # Add one of these per network interface in your system 8 | #[[port]] 9 | #interface = "" 10 | #network-mode = "ipv4" #can also be ipv6 or ethernet 11 | # If the network interface has hardware clock support, you can enable it with 12 | #hardware-clock = 13 | -------------------------------------------------------------------------------- /statime/src/port/sequence_id.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] 2 | pub(crate) struct SequenceIdGenerator { 3 | current: u16, 4 | } 5 | 6 | impl SequenceIdGenerator { 7 | pub(crate) fn new() -> Self { 8 | SequenceIdGenerator { current: 0 } 9 | } 10 | 11 | pub(crate) fn generate(&mut self) -> u16 { 12 | let id = self.current; 13 | self.current = self.current.wrapping_add(1); 14 | id 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /validation/24-05-2022-hardware-timestamping/gm_secs_stat_statime.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 1 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set ytics (0) 5 | set xtics (-430, -420, -410, "10^8-400" -400, -390) scale 0 6 | set xlabel "Clock cycles per second" 7 | set xrange [-440:-380] 8 | set yrange [0:1000] 9 | plot '../meetlogboek/statime-hard-10-05-2022-1456-analysis/gm-interval.dat' using ($1-100000000) smooth freq with boxes fillcolor black title "# of measurements" 10 | -------------------------------------------------------------------------------- /docs/examples/conf/statime-metrics-exporter.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Statime metrics exporter 3 | Documentation=https://github.com/pendulum-project/statime 4 | After=statime.service 5 | Requires=statime.service 6 | Conflicts= 7 | 8 | [Service] 9 | Type=simple 10 | Restart=always 11 | ExecStart=/usr/bin/statime-metrics-exporter 12 | Environment="RUST_LOG=info" 13 | RuntimeDirectory=statime-observe 14 | User=statime-observe 15 | Group=statime-observe 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /validation/08-04-2022/gm_secs_stat_statime.gnuplot: -------------------------------------------------------------------------------- 1 | set boxwidth 1 2 | set style fill solid 1 border lt -1 3 | set style rectangle black 4 | set ytics (0) 5 | set xtics ("10^8-350" -350, -325, -330, -335, -340, -345, -355, -360, -365, -370, -375) scale 0 6 | set xlabel "Clock cycles per second" 7 | set xrange [-375:-325] 8 | set yrange [0:800] 9 | plot '../meetlogboek/statime-soft-31-03-2022-0928-analysis/gm-interval.dat' using ($1-100000000) smooth freq with boxes fillcolor black title "# of measurements" 10 | -------------------------------------------------------------------------------- /docs/examples/conf/statime.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Statime linux 3 | Documentation=https://github.com/pendulum-project/statime 4 | After=network-online.target 5 | Wants=network-online.target 6 | Conflicts= 7 | 8 | [Service] 9 | Type=simple 10 | Restart=no 11 | ExecStart=/usr/bin/statime 12 | Environment="RUST_LOG=info" 13 | RuntimeDirectory=statime 14 | User=statime 15 | Group=statime 16 | AmbientCapabilities=CAP_SYS_TIME CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_NET_ADMIN 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /docs/examples/conf/statime-61850.toml: -------------------------------------------------------------------------------- 1 | # Sample configuration for the IEC/IEEE 61580 power utility profile. 2 | loglevel = "info" # Other values include trace, debug, warn and error 3 | 4 | sdo-id = 0 5 | domain = 0 6 | priority1 = 128 7 | priority2 = 128 8 | 9 | # Per network interface, include the lines below: 10 | # [[port]] 11 | # interface = "" 12 | # network-mode = "ethernet" 13 | # delay-mechanism = "P2P" 14 | # delay-interval = 0 15 | # announce-interval = 0 16 | # sync-interval = 0 17 | # announce-receipt-timeout = 3 18 | -------------------------------------------------------------------------------- /statime/src/datastructures/common/leap_indicator.rs: -------------------------------------------------------------------------------- 1 | /// Describes upcoming leap seconds. 2 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 3 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 4 | pub enum LeapIndicator { 5 | #[default] 6 | /// No leap seconds will be added or removed on this UTC day. 7 | NoLeap, 8 | /// The last minute of the current UTC day contains 61 seconds. 9 | Leap61, 10 | /// The last minute of the current UTC day contains 59 seconds. 11 | Leap59, 12 | } 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | groups: 9 | github-actions: 10 | patterns: ["*"] 11 | 12 | - package-ecosystem: cargo 13 | directories: ["/", "/fuzz/", "/statime-stm32/"] 14 | allow: 15 | - dependency-type: all 16 | schedule: 17 | interval: daily 18 | versioning-strategy: lockfile-only 19 | open-pull-requests-limit: 10 20 | groups: 21 | cargo: 22 | patterns: ["*"] 23 | -------------------------------------------------------------------------------- /statime-stm32/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 2 | runner = "probe-rs run --chip STM32F767ZITx" 3 | rustflags = [ 4 | "-C", 5 | "link-arg=-Tlink.x", 6 | "-C", 7 | "link-arg=-Tdefmt.x", 8 | # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x 9 | # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 10 | "-C", 11 | "link-arg=--nmagic", 12 | ] 13 | 14 | [build] 15 | target = "thumbv7em-none-eabihf" 16 | 17 | [env] 18 | DEFMT_LOG = "info,stm32_ptp=trace" 19 | -------------------------------------------------------------------------------- /statime/src/datastructures/common/mod.rs: -------------------------------------------------------------------------------- 1 | //! Common data structures that are used throughout the protocol 2 | 3 | mod clock_accuracy; 4 | mod clock_identity; 5 | mod clock_quality; 6 | mod leap_indicator; 7 | mod port_identity; 8 | mod time_interval; 9 | mod time_source; 10 | mod timestamp; 11 | mod tlv; 12 | 13 | pub use clock_accuracy::*; 14 | pub use clock_identity::*; 15 | pub use clock_quality::*; 16 | pub use leap_indicator::*; 17 | pub(crate) use port_identity::*; 18 | pub use time_interval::*; 19 | pub use time_source::*; 20 | pub use timestamp::*; 21 | pub use tlv::*; 22 | -------------------------------------------------------------------------------- /docs/development/diagrams/measurement.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | agent ptp_gm [ 3 | PTP Grand Master 4 | (Endrun Ninja with PTP and PPS) 5 | ] 6 | agent switch [ 7 | ethernet switch (dumb) 8 | ] 9 | agent dut [ 10 | device under test 11 | (raspberry pi 4 with ptp-enabled networking) 12 | ] 13 | 14 | agent scope [ 15 | oscilloscope (Keysight DSOX1204G) 16 | measuring leading edge time delta 17 | ] 18 | 19 | switch <--> ptp_gm : ethernet (ptp) 20 | switch <--> dut : ethernet (ptp) 21 | dut --> scope : pps (gpio) 22 | ptp_gm --> scope : pulse signal\n(1Hz to 10Mhz) 23 | @enduml 24 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "statime-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | edition = "2018" 6 | publish = false 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies.libfuzzer-sys] 12 | version = "0.4" 13 | features = ["arbitrary-derive"] 14 | 15 | [dependencies.statime] 16 | path = "../statime" 17 | features = ["fuzz"] 18 | 19 | 20 | # Prevent this from interfering with workspaces 21 | [workspace] 22 | members = ["."] 23 | 24 | [[bin]] 25 | name = "message_sound" 26 | path = "fuzz_targets/message_sound.rs" 27 | test = false 28 | doc = false 29 | -------------------------------------------------------------------------------- /statime/src/observability/mod.rs: -------------------------------------------------------------------------------- 1 | //! Serializable implementations of datastructures to be used for observability 2 | /// A concrete implementation of the PTP Current dataset (IEEE1588-2019 section 3 | /// 8.2.2) 4 | pub mod current; 5 | /// A concrete implementation of the PTP Default dataset (IEEE1588-2019 section 6 | /// 8.2.1) 7 | pub mod default; 8 | /// A concrete implementation of the PTP Parent dataset (IEEE1588-2019 section 9 | /// 8.2.3) 10 | pub mod parent; 11 | /// A concrete implementation of the PTP Port dataset (IEEE1588-2019 section 12 | /// 8.2.15) 13 | pub mod port; 14 | 15 | pub use crate::datastructures::datasets::PathTraceDS; 16 | -------------------------------------------------------------------------------- /utils/generate-man.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | docs_dir="docs/man" 6 | output_dir="${1:-"docs/precompiled/man"}" 7 | files=("statime.8" "statime-metrics-exporter.8" "statime.toml.5") 8 | 9 | mkdir -p "$output_dir" 10 | 11 | for f in "${files[@]}"; do 12 | origin_file="$docs_dir/$f.md" 13 | tmp_file="$output_dir/$f.md" 14 | target_file="$output_dir/$f" 15 | 16 | echo "Generating man page for $f from '$origin_file' to '$target_file'" 17 | sed '//s/--- -->/---/' > "$tmp_file" 18 | utils/pandoc.sh -s -t man "$tmp_file" -o "$target_file" 19 | rm "$tmp_file" 20 | done 21 | -------------------------------------------------------------------------------- /pkg/test-scripts/test-statime.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | set -x 5 | 6 | case $1 in 7 | post-install|post-upgrade) 8 | # Ensure users are created 9 | id statime 10 | 11 | # Ensure deamon is present 12 | echo -e "\nSTATIME HELP OUTPUT:" 13 | /usr/bin/statime --help 14 | 15 | # Ensure deamon is present 16 | echo -e "\nSTATIME METRICS EXPORTER HELP OUTPUT:" 17 | /usr/bin/statime-metrics-exporter --help 18 | 19 | # # Ensure that the systemd service is not running by default 20 | # ! systemctl is-active statime.service --quiet 21 | # ! systemctl is-active statime-metrics-exporter.service --quiet 22 | ;; 23 | esac 24 | -------------------------------------------------------------------------------- /docs/man/statime.8.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # NAME 6 | 7 | `statime` - The Statime PTP daemon for linux 8 | 9 | # SYNOPSIS 10 | `statime` [`-c` *path*] \ 11 | `statime` `-h` \ 12 | `statime` `-V` 13 | 14 | # DESCRIPTION 15 | 16 | ... 17 | 18 | # OPTIONS 19 | `-c` *path*, `--config`=*path* 20 | : Path to the configuration file for the statime daemon. If not specified this 21 | defaults to `/etc/statime/statime.toml`. 22 | 23 | `-h`, `--help` 24 | : Display usage instructions. 25 | 26 | `-V`, `--version` 27 | : Display version information. 28 | 29 | # SEE ALSO 30 | 31 | [statime-metrics-exporter(8)](statime-metrics-exporter.8.md), [statime.toml(5)](statime.toml.5.md) -------------------------------------------------------------------------------- /statime-stm32/src/ptp_state_mutex.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use critical_section::Mutex; 4 | use statime::{PtpInstanceState, PtpInstanceStateMutex}; 5 | 6 | pub struct PtpStateMutex(Mutex>); 7 | 8 | impl PtpInstanceStateMutex for PtpStateMutex { 9 | fn new(state: PtpInstanceState) -> Self { 10 | Self(Mutex::new(RefCell::new(state))) 11 | } 12 | 13 | fn with_ref R>(&self, f: F) -> R { 14 | critical_section::with(|cs| f(&self.0.borrow_ref(cs))) 15 | } 16 | 17 | fn with_mut R>(&self, f: F) -> R { 18 | critical_section::with(|cs| f(&mut self.0.borrow_ref_mut(cs))) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /statime/src/config/mod.rs: -------------------------------------------------------------------------------- 1 | //! Configuration structures 2 | //! 3 | //! Configurations for a [`PtpInstance`](`crate::PtpInstance`): 4 | //! * [`InstanceConfig`] 5 | //! * [`TimePropertiesDS`] 6 | //! 7 | //! Configurations for a [`Port`](`crate::port::Port`): 8 | //! * [`PortConfig`] 9 | //! 10 | //! And types used within those configurations. 11 | 12 | mod instance; 13 | mod port; 14 | 15 | pub use instance::InstanceConfig; 16 | pub use port::{DelayMechanism, PortConfig, PtpMinorVersion}; 17 | 18 | pub use crate::{ 19 | bmc::acceptable_master::{AcceptAnyMaster, AcceptableMasterList}, 20 | datastructures::{ 21 | common::{ClockAccuracy, ClockIdentity, ClockQuality, LeapIndicator, TimeSource}, 22 | datasets::TimePropertiesDS, 23 | messages::SdoId, 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /docs/man/statime-metrics-exporter.8.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # NAME 6 | 7 | `statime-metrics-exporter` - Prometheus/OpenMetrics exporter for the Statime PTP daemon 8 | 9 | # SYNOPSIS 10 | `statime-metrics-exporter` [`-c` *path*] \ 11 | `statime-metrics-exporter` `-h` \ 12 | `statime-metrics-exporter` `-V` 13 | 14 | # DESCRIPTION 15 | 16 | ... 17 | 18 | # OPTIONS 19 | `-c` *path*, `--config`=*path* 20 | : Path to the configuration file for the statime daemon. If not specified this 21 | defaults to `/etc/statime/statime.toml`. 22 | 23 | `-h`, `--help` 24 | : Display usage instructions. 25 | 26 | `-V`, `--version` 27 | : Display version information. 28 | 29 | # SEE ALSO 30 | 31 | [statime(8)](statime.8.md), [statime.toml(5)](statime.toml.5.md) -------------------------------------------------------------------------------- /statime/src/datastructures/datasets/path_trace.rs: -------------------------------------------------------------------------------- 1 | use arrayvec::ArrayVec; 2 | 3 | use crate::datastructures::{common::ClockIdentity, messages::MAX_DATA_LEN}; 4 | 5 | /// A concrete implementation of the PTP Path Trace dataset 6 | /// (IEEE1588-2019 section 16.2.2) 7 | #[derive(Clone, Debug, Eq, PartialEq)] 8 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 9 | pub struct PathTraceDS { 10 | /// See *IEEE1588-2019 section 16.2.2.2.1*. 11 | pub list: ArrayVec, 12 | /// See *IEEE1588-2019 section 16.2.2.3.1*. 13 | pub enable: bool, 14 | } 15 | 16 | impl PathTraceDS { 17 | pub(crate) fn new(enable: bool) -> Self { 18 | PathTraceDS { 19 | list: Default::default(), 20 | enable, 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /statime/src/port/measurement.rs: -------------------------------------------------------------------------------- 1 | use crate::time::{Duration, Time}; 2 | 3 | /// A single measurement as produced by a PTP port. 4 | /// Depending on what trigerred the measurements, not 5 | /// all fields will be populated 6 | #[derive(Default, Clone, Copy, Debug, Eq, PartialEq)] 7 | pub struct Measurement { 8 | /// Time this measurement was made. 9 | pub event_time: Time, 10 | /// Offset to the remote PTP node. 11 | pub offset: Option, 12 | /// Delay to the remote PTP node. 13 | pub delay: Option, 14 | /// Peer delay on the port, 15 | pub peer_delay: Option, 16 | /// Raw offset calculated from a sync message 17 | pub raw_sync_offset: Option, 18 | /// Raw offset calculated from a delay message 19 | pub raw_delay_offset: Option, 20 | } 21 | -------------------------------------------------------------------------------- /statime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "statime" 3 | readme = "README.md" 4 | description = "Precision Time Protocol implementation library for embedded and desktops" 5 | authors.workspace = true 6 | version.workspace = true 7 | edition.workspace = true 8 | license.workspace = true 9 | repository.workspace = true 10 | homepage.workspace = true 11 | publish.workspace = true 12 | rust-version.workspace = true 13 | 14 | [features] 15 | default = ["std", "serde"] 16 | std = [] 17 | fuzz = ["std"] 18 | serde = ["dep:serde", "arrayvec/serde"] 19 | 20 | [dependencies] 21 | arrayvec.workspace = true 22 | az.workspace = true 23 | fixed.workspace = true 24 | libm.workspace = true 25 | log = { workspace = true, default-features = false} 26 | rand = { workspace = true, default-features = false } 27 | serde = { workspace = true, optional = true } 28 | 29 | [dev-dependencies] 30 | serde_test.workspace = true 31 | -------------------------------------------------------------------------------- /docs/precompiled/man/statime.8: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 3.4 2 | .\" 3 | .TH "STATIME" "8" "" "statime 0.4.0" "statime" 4 | .SH NAME 5 | \f[CR]statime\f[R] \- The Statime PTP daemon for linux 6 | .SH SYNOPSIS 7 | \f[CR]statime\f[R] [\f[CR]\-c\f[R] \f[I]path\f[R]] 8 | .PD 0 9 | .P 10 | .PD 11 | \f[CR]statime\f[R] \f[CR]\-h\f[R] 12 | .PD 0 13 | .P 14 | .PD 15 | \f[CR]statime\f[R] \f[CR]\-V\f[R] 16 | .SH DESCRIPTION 17 | \&... 18 | .SH OPTIONS 19 | .TP 20 | \f[CR]\-c\f[R] \f[I]path\f[R], \f[CR]\-\-config\f[R]=\f[I]path\f[R] 21 | Path to the configuration file for the statime daemon. 22 | If not specified this defaults to \f[CR]/etc/statime/statime.toml\f[R]. 23 | .TP 24 | \f[CR]\-h\f[R], \f[CR]\-\-help\f[R] 25 | Display usage instructions. 26 | .TP 27 | \f[CR]\-V\f[R], \f[CR]\-\-version\f[R] 28 | Display version information. 29 | .SH SEE ALSO 30 | statime\-metrics\-exporter(8), statime.toml(5) 31 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/message_sound.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use libfuzzer_sys::fuzz_target; 4 | use statime::fuzz::FuzzMessage; 5 | 6 | fuzz_target!(|data: Vec| { 7 | // the default maximum size a fuzzed Vec<_> will be, as per 8 | // 9 | // > INFO: -max_len is not provided; libFuzzer will not generate inputs larger 10 | // than 4096 bytes 11 | let mut buf1 = [0u8; 4096]; 12 | let mut buf2 = [0u8; 4096]; 13 | 14 | assert!(data.len() <= buf1.len()); 15 | 16 | if let Ok(a) = FuzzMessage::deserialize(&data) { 17 | // verify that the tlv's are parsed without panics 18 | for tlv in a.tlv() { 19 | let _ = tlv; 20 | } 21 | 22 | let written = a.serialize(&mut buf1).unwrap(); 23 | assert!(data.len() >= written); 24 | 25 | let b = FuzzMessage::deserialize(&data).unwrap(); 26 | b.serialize(&mut buf2).unwrap(); 27 | 28 | assert_eq!(buf1, buf2); 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /docs/precompiled/man/statime-metrics-exporter.8: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 3.4 2 | .\" 3 | .TH "STATIME\-METRICS\-EXPORTER" "8" "" "statime 0.2.2" "statime" 4 | .SH NAME 5 | \f[CR]statime\-metrics\-exporter\f[R] \- Prometheus/OpenMetrics exporter 6 | for the Statime PTP daemon 7 | .SH SYNOPSIS 8 | \f[CR]statime\-metrics\-exporter\f[R] [\f[CR]\-c\f[R] \f[I]path\f[R]] 9 | .PD 0 10 | .P 11 | .PD 12 | \f[CR]statime\-metrics\-exporter\f[R] \f[CR]\-h\f[R] 13 | .PD 0 14 | .P 15 | .PD 16 | \f[CR]statime\-metrics\-exporter\f[R] \f[CR]\-V\f[R] 17 | .SH DESCRIPTION 18 | \&... 19 | .SH OPTIONS 20 | .TP 21 | \f[CR]\-c\f[R] \f[I]path\f[R], \f[CR]\-\-config\f[R]=\f[I]path\f[R] 22 | Path to the configuration file for the statime daemon. 23 | If not specified this defaults to \f[CR]/etc/statime/statime.toml\f[R]. 24 | .TP 25 | \f[CR]\-h\f[R], \f[CR]\-\-help\f[R] 26 | Display usage instructions. 27 | .TP 28 | \f[CR]\-V\f[R], \f[CR]\-\-version\f[R] 29 | Display version information. 30 | .SH SEE ALSO 31 | statime(8), statime.toml(5) 32 | -------------------------------------------------------------------------------- /statime-linux/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate core; 2 | 3 | pub mod clock; 4 | pub mod config; 5 | pub mod metrics; 6 | pub mod observer; 7 | pub mod socket; 8 | pub mod tlvforwarder; 9 | pub mod tracing; 10 | 11 | use std::path::Path; 12 | 13 | use config::Config; 14 | pub use metrics::exporter::main as metrics_exporter_main; 15 | use tracing::LogLevel; 16 | use tracing_log::LogTracer; 17 | use tracing_subscriber::util::SubscriberInitExt; 18 | 19 | pub fn initialize_logging_parse_config(path: &Path) -> Config { 20 | LogTracer::init().expect("Internal error: could not attach logger"); 21 | 22 | // Early setup for logging 23 | let startup_tracing = crate::tracing::tracing_init(LogLevel::default()); 24 | 25 | let config = ::tracing::subscriber::with_default(startup_tracing, || { 26 | Config::from_file(path).unwrap_or_else(|e| { 27 | eprintln!("{e}"); 28 | std::process::exit(1); 29 | }) 30 | }); 31 | 32 | crate::tracing::tracing_init(config.loglevel).init(); 33 | config 34 | } 35 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | theme: 2 | name: material 3 | features: 4 | - content.tooltips 5 | - content.code.copy 6 | - content.code.select 7 | - navigation.sections 8 | docs_dir: docs 9 | site_name: Statime documentation 10 | site_dir: target/docs/site 11 | exclude_docs: | 12 | /precompiled # Precompiled assets 13 | /includes # Only included indirectly 14 | markdown_extensions: 15 | - def_list 16 | - abbr 17 | - attr_list 18 | - admonition 19 | - pymdownx.snippets: 20 | auto_append: [./docs/includes/glossary.md] 21 | - pymdownx.escapeall: 22 | hardbreak: true 23 | - pymdownx.highlight: 24 | anchor_linenums: true 25 | line_spans: __span 26 | pygments_lang_class: true 27 | - pymdownx.inlinehilite 28 | - pymdownx.details 29 | - pymdownx.superfences 30 | nav: 31 | - Guide: 32 | - guide/getting-started.md 33 | - guide/exporting-metrics.md 34 | - Man Pages: 35 | - statime(8): man/statime.8.md 36 | - statime.toml(5): man/statime.toml.5.md 37 | - statime-metrics-exporter(8): man/statime-metrics-exporter.8.md 38 | -------------------------------------------------------------------------------- /statime/src/datastructures/messages/signalling.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::{common::PortIdentity, WireFormat, WireFormatError}; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | pub(crate) struct SignalingMessage { 5 | pub(super) target_port_identity: PortIdentity, 6 | } 7 | 8 | impl SignalingMessage { 9 | pub(crate) fn content_size(&self) -> usize { 10 | 10 11 | } 12 | 13 | pub(crate) fn serialize_content(&self, buffer: &mut [u8]) -> Result<(), WireFormatError> { 14 | if buffer.len() < 10 { 15 | return Err(WireFormatError::BufferTooShort); 16 | } 17 | 18 | let (left, _) = buffer.split_at_mut(10); 19 | 20 | self.target_port_identity.serialize(left)?; 21 | 22 | Ok(()) 23 | } 24 | 25 | pub(crate) fn deserialize_content(buffer: &[u8]) -> Result { 26 | let identity_bytes = buffer.get(0..10).ok_or(WireFormatError::BufferTooShort)?; 27 | let target_port_identity = PortIdentity::deserialize(identity_bytes)?; 28 | 29 | Ok(Self { 30 | target_port_identity, 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022-2023 Tweede Golf and Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /statime/src/datastructures/datasets/parent.rs: -------------------------------------------------------------------------------- 1 | use super::InternalDefaultDS; 2 | use crate::datastructures::common::{ClockIdentity, ClockQuality, PortIdentity}; 3 | 4 | // TODO: Discuss moving this (and TimePropertiesDS, ...) to slave? 5 | #[derive(Clone, Debug, Eq, PartialEq)] 6 | pub(crate) struct InternalParentDS { 7 | pub(crate) parent_port_identity: PortIdentity, 8 | pub(crate) grandmaster_identity: ClockIdentity, 9 | pub(crate) grandmaster_clock_quality: ClockQuality, 10 | pub(crate) grandmaster_priority_1: u8, 11 | pub(crate) grandmaster_priority_2: u8, 12 | } 13 | 14 | impl InternalParentDS { 15 | pub(crate) fn new(default_ds: InternalDefaultDS) -> Self { 16 | InternalParentDS { 17 | parent_port_identity: PortIdentity { 18 | clock_identity: default_ds.clock_identity, 19 | port_number: 0, 20 | }, 21 | grandmaster_identity: default_ds.clock_identity, 22 | grandmaster_clock_quality: default_ds.clock_quality, 23 | grandmaster_priority_1: default_ds.priority_1, 24 | grandmaster_priority_2: default_ds.priority_2, 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /statime/src/datastructures/messages/control_field.rs: -------------------------------------------------------------------------------- 1 | use super::MessageType; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub(super) enum ControlField { 5 | Sync, 6 | DelayReq, 7 | FollowUp, 8 | DelayResp, 9 | Management, 10 | AllOthers, 11 | } 12 | 13 | impl ControlField { 14 | pub fn to_primitive(self) -> u8 { 15 | match self { 16 | ControlField::Sync => 0x00, 17 | ControlField::DelayReq => 0x01, 18 | ControlField::FollowUp => 0x02, 19 | ControlField::DelayResp => 0x03, 20 | ControlField::Management => 0x04, 21 | ControlField::AllOthers => 0x05, 22 | } 23 | } 24 | } 25 | 26 | impl From for ControlField { 27 | fn from(message_type: MessageType) -> Self { 28 | match message_type { 29 | MessageType::Sync => ControlField::Sync, 30 | MessageType::DelayReq => ControlField::DelayReq, 31 | MessageType::FollowUp => ControlField::FollowUp, 32 | MessageType::DelayResp => ControlField::DelayResp, 33 | MessageType::Management => ControlField::Management, 34 | _ => ControlField::AllOthers, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /statime/src/float_polyfill.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] // clippy will inaccurately mark this as unused on platforms with std 2 | pub(crate) trait FloatPolyfill { 3 | #[cfg(not(feature = "std"))] 4 | fn abs(self) -> Self; 5 | #[cfg(not(feature = "std"))] 6 | fn signum(self) -> Self; 7 | #[cfg(not(feature = "std"))] 8 | fn sqrt(self) -> Self; 9 | #[cfg(not(feature = "std"))] 10 | fn powi(self, n: i32) -> Self; 11 | #[cfg(not(feature = "std"))] 12 | fn exp(self) -> Self; 13 | } 14 | 15 | impl FloatPolyfill for f64 { 16 | #[cfg(not(feature = "std"))] 17 | fn abs(self) -> Self { 18 | libm::fabs(self) 19 | } 20 | 21 | #[cfg(not(feature = "std"))] 22 | fn signum(self) -> Self { 23 | if self < 0.0 { 24 | -1.0 25 | } else if self > 0.0 { 26 | 1.0 27 | } else { 28 | 0.0 29 | } 30 | } 31 | 32 | #[cfg(not(feature = "std"))] 33 | fn sqrt(self) -> Self { 34 | libm::sqrt(self) 35 | } 36 | 37 | #[cfg(not(feature = "std"))] 38 | fn powi(self, n: i32) -> Self { 39 | libm::pow(self, n as f64) 40 | } 41 | 42 | #[cfg(not(feature = "std"))] 43 | fn exp(self) -> Self { 44 | libm::exp(self) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/deb/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | STATIME_CONF_DIR="/etc/statime" 5 | 6 | case "$1" in 7 | purge) 8 | # Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior 9 | # "configuration files must be preserved when the package is removed, and 10 | # only deleted when the package is purged." 11 | if [ -d ${STATIME_CONF_DIR} ]; then 12 | rm -r ${STATIME_CONF_DIR} 13 | fi 14 | ;; 15 | esac 16 | 17 | if [ -d /run/systemd/system ]; then 18 | systemctl --system daemon-reload >/dev/null || true 19 | fi 20 | 21 | if [ "$1" = "remove" ]; then 22 | if [ -x "/usr/bin/deb-systemd-helper" ]; then 23 | deb-systemd-helper mask statime.service >/dev/null || true 24 | deb-systemd-helper mask statime-metrics-exporter.service >/dev/null || true 25 | fi 26 | 27 | if [ -d /run/udev ]; then 28 | udevadm control -R 29 | udevadm trigger 30 | fi 31 | fi 32 | 33 | if [ "$1" = "purge" ]; then 34 | if [ -x "/usr/bin/deb-systemd-helper" ]; then 35 | deb-systemd-helper purge statime.service >/dev/null || true 36 | deb-systemd-helper unmask statime.service >/dev/null || true 37 | deb-systemd-helper purge statime-metrics-exporter.service >/dev/null || true 38 | deb-systemd-helper unmask statime-metrics-exporter.service >/dev/null || true 39 | fi 40 | 41 | if [ -d /run/udev ]; then 42 | udevadm control -R 43 | udevadm trigger 44 | fi 45 | fi 46 | 47 | #DEBHELPER# 48 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | permissions: 4 | actions: read 5 | contents: read 6 | pages: write 7 | id-token: write 8 | 9 | on: 10 | push: 11 | branches: 12 | - main 13 | 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: false 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout sources 23 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 24 | 25 | - name: Install rust 26 | uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: stable 29 | override: true 30 | 31 | - name: Build docs 32 | run: cargo doc -p statime --lib --no-deps 33 | 34 | - name: Build site 35 | run: utils/mkdocs.sh --no-bind-port build 36 | 37 | - name: Copy API docs 38 | run: cp -R target/doc target/docs/site/api 39 | 40 | - name: Remove the lockfile 41 | run: rm -rf target/docs/site/api/.lock 42 | 43 | - name: Upload artifact 44 | uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b 45 | with: 46 | path: target/docs/site 47 | 48 | deploy: 49 | environment: 50 | name: github-pages 51 | url: ${{ steps.deployment.outputs.page_url }} 52 | runs-on: ubuntu-latest 53 | needs: build 54 | steps: 55 | - name: Deploy to GitHub Pages 56 | id: deployment 57 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e 58 | -------------------------------------------------------------------------------- /statime/src/datastructures/datasets/default.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | config::InstanceConfig, 3 | datastructures::{ 4 | common::{ClockIdentity, ClockQuality}, 5 | messages::SdoId, 6 | }, 7 | }; 8 | 9 | /// A concrete implementation of the PTP Default dataset (IEEE1588-2019 section 10 | /// 8.2.1) 11 | /// 12 | /// This dataset describes the properties of the PTP instance. Most 13 | /// instance-wide configuration options are found here, with the exception of 14 | /// those related to timebase, which is contained in the 15 | /// [TimePropertiesDS](crate::TimePropertiesDS) dataset. 16 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 17 | pub(crate) struct InternalDefaultDS { 18 | pub(crate) clock_identity: ClockIdentity, 19 | pub(crate) number_ports: u16, 20 | pub(crate) clock_quality: ClockQuality, 21 | pub(crate) priority_1: u8, 22 | pub(crate) priority_2: u8, 23 | pub(crate) domain_number: u8, 24 | pub(crate) slave_only: bool, 25 | pub(crate) sdo_id: SdoId, 26 | } 27 | 28 | impl InternalDefaultDS { 29 | pub(crate) fn new(config: InstanceConfig) -> Self { 30 | Self { 31 | clock_identity: config.clock_identity, 32 | number_ports: 0, 33 | clock_quality: config.clock_quality, 34 | priority_1: config.priority_1, 35 | priority_2: config.priority_2, 36 | domain_number: config.domain_number, 37 | slave_only: config.slave_only, 38 | sdo_id: config.sdo_id, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /statime/src/observability/current.rs: -------------------------------------------------------------------------------- 1 | use crate::{datastructures::datasets::InternalCurrentDS, filters::FilterEstimate, time::Duration}; 2 | 3 | /// A concrete implementation of the PTP Current dataset (IEEE1588-2019 section 4 | /// 8.2.2) 5 | /// 6 | /// Note that the `meanDelay` field from IEEE1588-2019 section 8.2.2.4 is 7 | /// missing since this field can be constructed from the portDS. 8 | #[derive(Debug, Default, Copy, Clone)] 9 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 10 | pub struct CurrentDS { 11 | /// See *IEEE1588-2019 section 8.2.2.2*. 12 | pub steps_removed: u16, 13 | /// See *IEEE1588-2019 section 8.2.2.3*. 14 | pub offset_from_master: Duration, 15 | /// See *IEEE1588-2019 section 8.2.2.3*. 16 | pub mean_delay: Duration, 17 | } 18 | 19 | impl CurrentDS { 20 | pub(crate) fn from_state( 21 | current_ds: &InternalCurrentDS, 22 | port_contribution: Option, 23 | ) -> Self { 24 | match port_contribution { 25 | Some(port_contribution) => Self { 26 | steps_removed: current_ds.steps_removed, 27 | offset_from_master: port_contribution.offset_from_master, 28 | mean_delay: port_contribution.mean_delay, 29 | }, 30 | None => Self { 31 | steps_removed: current_ds.steps_removed, 32 | offset_from_master: Duration::ZERO, 33 | mean_delay: Duration::ZERO, 34 | }, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /statime/src/shared_clock.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Arc, Mutex}; 2 | 3 | use crate::{ 4 | time::{Duration, Time}, 5 | Clock, 6 | }; 7 | 8 | /// A wrapper for stateful `statime::Clock` implementations to make them behave 9 | /// like e.g. `statime_linux::LinuxClock` - clones share state with each other 10 | #[derive(Debug)] 11 | pub struct SharedClock(pub Arc>) 12 | where 13 | C: Clock; 14 | 15 | impl SharedClock { 16 | /// Take given clock and make it a `SharedClock` 17 | pub fn new(clock: C) -> Self { 18 | Self(Arc::new(Mutex::new(clock))) 19 | } 20 | } 21 | 22 | impl Clone for SharedClock { 23 | /// Clone the shared reference to the clock (behaviour consistent with 24 | /// `statime_linux::LinuxClock`) 25 | fn clone(&self) -> Self { 26 | Self(self.0.clone()) 27 | } 28 | } 29 | 30 | impl Clock for SharedClock { 31 | type Error = C::Error; 32 | fn now(&self) -> Time { 33 | self.0.lock().unwrap().now() 34 | } 35 | fn set_frequency(&mut self, ppm: f64) -> Result { 36 | self.0.lock().unwrap().set_frequency(ppm) 37 | } 38 | fn step_clock(&mut self, offset: Duration) -> Result { 39 | self.0.lock().unwrap().step_clock(offset) 40 | } 41 | fn set_properties( 42 | &mut self, 43 | time_properties_ds: &crate::config::TimePropertiesDS, 44 | ) -> Result<(), Self::Error> { 45 | self.0.lock().unwrap().set_properties(time_properties_ds) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /statime/src/observability/parent.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | config::{ClockIdentity, ClockQuality}, 3 | datastructures::{common::PortIdentity, datasets::InternalParentDS}, 4 | }; 5 | 6 | /// A concrete implementation of the PTP Parent dataset (IEEE1588-2019 section 7 | /// 8.2.3) 8 | /// 9 | /// These fields aren't implemented, because they are currently unused: 10 | /// - parentStats 11 | /// - observedParentOffsetScaledLogVariance 12 | /// - observedParentClockPhaseChangeRate 13 | 14 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 15 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 16 | pub struct ParentDS { 17 | /// See *IEEE1588-2019 section 8.2.3.2*. 18 | pub parent_port_identity: PortIdentity, 19 | /// See *IEEE1588-2019 section 8.2.3.6*. 20 | pub grandmaster_identity: ClockIdentity, 21 | /// See *IEEE1588-2019 section 8.2.3.7*. 22 | pub grandmaster_clock_quality: ClockQuality, 23 | /// See *IEEE1588-2019 section 8.2.3.8*. 24 | pub grandmaster_priority_1: u8, 25 | /// See *IEEE1588-2019 section 8.2.3.9*. 26 | pub grandmaster_priority_2: u8, 27 | } 28 | 29 | impl From<&InternalParentDS> for ParentDS { 30 | fn from(v: &InternalParentDS) -> Self { 31 | Self { 32 | parent_port_identity: v.parent_port_identity, 33 | grandmaster_identity: v.grandmaster_identity, 34 | grandmaster_clock_quality: v.grandmaster_clock_quality, 35 | grandmaster_priority_1: v.grandmaster_priority_1, 36 | grandmaster_priority_2: v.grandmaster_priority_2, 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /utils/update-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ "$#" -lt 1 ]; then 4 | echo "Missing new version specifier" 5 | exit 1 6 | fi 7 | 8 | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) 9 | PROJECT_DIR=$(dirname "$SCRIPT_DIR") 10 | NEW_VERSION="$1" 11 | NOTICE_LINE="# NOTE: keep this part at the bottom of the file, do not change this line" 12 | 13 | echo "Updating version in Cargo.toml" 14 | sed -i 's/^version\s*=\s*".*"/version = "'"$NEW_VERSION"'"/' "$PROJECT_DIR/Cargo.toml" 15 | 16 | echo "Updating workspace crate versions in Cargo.toml" 17 | tmp_file="$PROJECT_DIR/Cargo.toml.tmp" 18 | replace_flag=0 19 | while IFS= read -r line; do 20 | if [ $replace_flag -eq 1 ]; then 21 | line=$(echo "$line" | sed 's/version\s*=\s*"[^"]*"/version = "'"$NEW_VERSION"'"/g') 22 | fi 23 | 24 | if [ "$line" = "$NOTICE_LINE" ]; then 25 | replace_flag=1 26 | fi 27 | 28 | echo "$line" >> "$tmp_file" 29 | done < "$PROJECT_DIR/Cargo.toml" 30 | mv "$tmp_file" "$PROJECT_DIR/Cargo.toml" 31 | 32 | echo "Updating version in man pages" 33 | sed -i 's/^title: STATIME(8) statime .*/title: STATIME(8) statime '"$NEW_VERSION"' | statime/' "$PROJECT_DIR"/docs/man/statime.8.md 34 | sed -i 's/^title: STATIME.TOML(5) statime .*/title: STATIME.TOML(5) statime '"$NEW_VERSION"' | statime/' "$PROJECT_DIR"/docs/man/statime.toml.5.md 35 | 36 | echo "Rebuilding precompiled man pages" 37 | utils/generate-man.sh 38 | 39 | echo "Rebuilding project" 40 | (cd $PROJECT_DIR && cargo clean && cargo build --release) 41 | 42 | echo "!!! Version changes complete, make sure that the changelog is synchronized" 43 | -------------------------------------------------------------------------------- /statime-stm32/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "statime-stm32" 4 | version = "0.1.0" 5 | license = "MIT OR Apache-2.0" 6 | 7 | [dependencies] 8 | rtic = { version = "2.1.2", features = ["thumbv7-backend"] } 9 | rtic-monotonics = { version = "2.0.3", features = [ 10 | "cortex-m-systick", 11 | "systick-64bit", 12 | ] } 13 | rtic-sync = "1.3.2" 14 | embassy-sync = "0.7.0" 15 | 16 | cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] } 17 | critical-section = "1.2.0" 18 | stm32f7xx-hal = { version = "0.8.0", features = ["stm32f767", "rt"] } 19 | stm32-eth = { version = "0.8.0", features = [ 20 | "smoltcp-phy", 21 | "async-await", 22 | "stm32f767", 23 | ] } 24 | ieee802_3_miim = "0.8.0" 25 | smoltcp = { version = "0.12.0", default-features = false, features = [ 26 | "defmt", 27 | "medium-ethernet", 28 | "proto-ipv4", 29 | "proto-ipv6", 30 | "multicast", 31 | "socket-dhcpv4", 32 | "socket-udp", 33 | "async", 34 | ] } 35 | 36 | defmt = "1.0.1" 37 | defmt-rtt = "1.0.0" 38 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } 39 | 40 | futures = { version = "0.3.31", default-features = false, features = [ 41 | "async-await", 42 | ] } 43 | 44 | statime = { path = "../statime", default-features = false } 45 | fixed = "1.29.0" 46 | az = "1.2.1" 47 | static_cell = "2.1.0" 48 | log-to-defmt = "0.1.0" 49 | adler = { version = "1.0.2", default-features = false } 50 | 51 | [profile.release] 52 | debug = 2 53 | lto = "fat" 54 | 55 | [profile.dev] 56 | opt-level = 2 57 | lto = "fat" 58 | 59 | [[bin]] 60 | name = "main" 61 | path = "src/main.rs" 62 | test = false 63 | bench = false 64 | -------------------------------------------------------------------------------- /statime/src/observability/default.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::datasets::InternalDefaultDS; 2 | 3 | /// A concrete implementation of the PTP Current dataset (IEEE1588-2019 section 4 | /// 8.2.1) 5 | /// 6 | /// See [InternalDefaultDS](crate::datastructures::datasets::InternalDefaultDS). 7 | #[derive(Debug, Copy, Clone)] 8 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 9 | pub struct DefaultDS { 10 | /// The identity of a PTP node. 11 | /// See *IEEE1588-2019 section 8.2.1.2.2*. 12 | pub clock_identity: crate::config::ClockIdentity, 13 | /// The amount of PTP ports on this PTP instance. 14 | /// See *IEEE1588-2019 section 8.2.1.2.3*. 15 | pub number_ports: u16, 16 | /// A description of the accuracy and type of a clock. 17 | pub clock_quality: crate::config::ClockQuality, 18 | /// See *IEEE1588-2019 section 8.2.1.4.1*. 19 | pub priority_1: u8, 20 | /// See *IEEE1588-2019 section 8.2.1.4.2*. 21 | pub priority_2: u8, 22 | /// See *IEEE1588-2019 section 8.2.1.4.3*. 23 | pub domain_number: u8, 24 | /// See *IEEE1588-2019 section 8.2.1.4.4*. 25 | pub slave_only: bool, 26 | /// See *IEEE1588-2019 section 7.1.4 table 2*. 27 | pub sdo_id: crate::config::SdoId, 28 | } 29 | 30 | impl From<&InternalDefaultDS> for DefaultDS { 31 | fn from(v: &InternalDefaultDS) -> Self { 32 | Self { 33 | clock_identity: v.clock_identity, 34 | number_ports: v.number_ports, 35 | clock_quality: v.clock_quality, 36 | priority_1: v.priority_1, 37 | priority_2: v.priority_2, 38 | domain_number: v.domain_number, 39 | slave_only: v.slave_only, 40 | sdo_id: v.sdo_id, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "statime", 4 | "statime-linux", 5 | ] 6 | exclude = [ 7 | "statime-stm32" 8 | ] 9 | resolver = "2" 10 | 11 | [workspace.package] 12 | version = "0.4.0" 13 | edition = "2021" 14 | license = "Apache-2.0 OR MIT" 15 | repository = "https://github.com/pendulum-project/statime" 16 | homepage = "https://github.com/pendulum-project/statime" 17 | publish = true 18 | authors = [ 19 | "David Venhoek ", 20 | "Folkert de Vries ", 21 | "Dion Dokter ", 22 | "Ruben Nijveld ", 23 | "Bram Bruines ", 24 | "Marlon Peeters ", 25 | "Rick van der Wal " 26 | ] 27 | rust-version = "1.69" # MSRV 28 | 29 | [workspace.dependencies] 30 | arrayvec = { version = "0.7.4", default-features = false } 31 | clap = { version = "4.4.7", features = ["derive"] } 32 | tracing = "0.1.21" 33 | tracing-log = ">=0.1.0, <0.3.0" 34 | tracing-subscriber = { version = "0.3.0", default-features = false, features = ["std", "fmt", "ansi"] } 35 | hex = "0.4.3" 36 | libc = { version = "0.2.150", features = ["extra_traits"] } 37 | log = { version = "0.4.27", default-features = false } 38 | pin-project-lite = "0.2.13" 39 | toml = ">=0.5.0, <0.9.0" 40 | tokio = "1.33" 41 | rand = { version = "0.8.5", default-features = false } 42 | serde = { version = "1.0.192", features = ["derive"] } 43 | serde_json = { version = "1.0.111" } 44 | serde_test = { version = "1.0.176" } 45 | az = "1.2.1" 46 | fixed = "1.24" 47 | libm = "0.2.15" 48 | 49 | clock-steering = "0.2.0" 50 | timestamped-socket = "0.2.4" 51 | 52 | 53 | # our own crates used as dependencies, same version as the workspace version 54 | # NOTE: keep this part at the bottom of the file, do not change this line 55 | statime = { version = "0.4.0", path = "./statime" } 56 | 57 | [profile.release] 58 | debug = 2 59 | -------------------------------------------------------------------------------- /statime/src/datastructures/mod.rs: -------------------------------------------------------------------------------- 1 | //! General datastructures as defined by the ptp spec 2 | 3 | use core::fmt::Debug; 4 | 5 | use self::messages::EnumConversionError; 6 | 7 | pub mod common; 8 | pub mod datasets; 9 | pub mod messages; 10 | 11 | #[derive(Clone, Debug)] 12 | pub(crate) enum WireFormatError { 13 | EnumConversionError, 14 | BufferTooShort, 15 | CapacityError, 16 | Invalid, 17 | } 18 | 19 | impl core::fmt::Display for WireFormatError { 20 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 21 | match self { 22 | WireFormatError::EnumConversionError => f.write_str("enum conversion failed"), 23 | WireFormatError::BufferTooShort => f.write_str("a buffer is too short"), 24 | WireFormatError::CapacityError => f.write_str("a container has insufficient capacity"), 25 | WireFormatError::Invalid => f.write_str("an invariant was violated"), 26 | } 27 | } 28 | } 29 | 30 | #[cfg(feature = "std")] 31 | impl std::error::Error for WireFormatError {} 32 | 33 | impl From for WireFormatError { 34 | fn from(_: arrayvec::CapacityError) -> Self { 35 | WireFormatError::CapacityError 36 | } 37 | } 38 | 39 | impl From for WireFormatError { 40 | fn from(_: EnumConversionError) -> Self { 41 | Self::EnumConversionError 42 | } 43 | } 44 | 45 | trait WireFormat: Debug + Clone + Eq { 46 | /// Serializes the object into the PTP wire format. 47 | /// 48 | /// Returns the used buffer size that contains the message or an error. 49 | fn serialize(&self, buffer: &mut [u8]) -> Result<(), WireFormatError>; 50 | 51 | /// Deserializes the object from the PTP wire format. 52 | /// 53 | /// Returns the object and the size in the buffer that it takes up or an 54 | /// error. 55 | fn deserialize(buffer: &[u8]) -> Result; 56 | } 57 | -------------------------------------------------------------------------------- /statime/src/config/instance.rs: -------------------------------------------------------------------------------- 1 | use crate::config::{ClockIdentity, ClockQuality, SdoId}; 2 | #[cfg(doc)] 3 | use crate::PtpInstance; 4 | 5 | /// Configuration for a [`PtpInstance`] 6 | /// 7 | /// # Example 8 | /// A configuration with common default values: 9 | /// ``` 10 | /// # use statime::config::{ClockIdentity, InstanceConfig, SdoId, ClockQuality}; 11 | /// let config = InstanceConfig { 12 | /// clock_identity: ClockIdentity::from_mac_address([1,2,3,4,5,6]), 13 | /// priority_1: 128, 14 | /// priority_2: 128, 15 | /// domain_number: 0, 16 | /// sdo_id: SdoId::default(), 17 | /// slave_only: false, 18 | /// path_trace: false, 19 | /// clock_quality: ClockQuality::default(), 20 | /// }; 21 | /// ``` 22 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] 23 | pub struct InstanceConfig { 24 | /// The unique identifier for this device within the PTP network. 25 | pub clock_identity: ClockIdentity, 26 | 27 | /// Priority of this clock while selecting a master clock. 28 | /// 29 | /// Lower values assign a higher priority. 30 | pub priority_1: u8, 31 | 32 | /// Tie-breaker priority during master clock selection on otherwise 33 | /// identical instances. 34 | /// 35 | /// Lower values assign a higher priority. 36 | pub priority_2: u8, 37 | 38 | /// This and [`InstanceConfig::sdo_id`] together identify which domain a 39 | /// [`PtpInstance`] belongs to. 40 | /// 41 | /// In general nodes will only communicate within their domain. See *IEEE 42 | /// 1588-2019 table 2* for permitted combinations. 43 | pub domain_number: u8, 44 | 45 | /// See [`InstanceConfig::domain_number`]. 46 | pub sdo_id: SdoId, 47 | 48 | /// Whether this node may never become a master in the network 49 | pub slave_only: bool, 50 | 51 | /// Whether the path trace option is enabled 52 | pub path_trace: bool, 53 | 54 | /// A description of the accuracy and type of the local clock. 55 | pub clock_quality: ClockQuality, 56 | } 57 | -------------------------------------------------------------------------------- /statime/src/bmc/acceptable_master.rs: -------------------------------------------------------------------------------- 1 | use crate::config::ClockIdentity; 2 | 3 | /// A list of [`ClockIdentity`]s a [`Port`](`crate::port::Port`) may accept as a 4 | /// master clock. 5 | pub trait AcceptableMasterList { 6 | /// Return whether the clock with `identity` may be a master to this `Port` 7 | fn is_acceptable(&self, identity: ClockIdentity) -> bool; 8 | } 9 | 10 | /// An [`AcceptableMasterList`] that accepts any [`ClockIdentity`] as a master 11 | /// clock. 12 | pub struct AcceptAnyMaster; 13 | impl AcceptableMasterList for AcceptAnyMaster { 14 | fn is_acceptable(&self, _identity: ClockIdentity) -> bool { 15 | true 16 | } 17 | } 18 | 19 | impl AcceptableMasterList for &[ClockIdentity] { 20 | fn is_acceptable(&self, identity: ClockIdentity) -> bool { 21 | self.contains(&identity) 22 | } 23 | } 24 | 25 | impl AcceptableMasterList for arrayvec::ArrayVec { 26 | fn is_acceptable(&self, identity: ClockIdentity) -> bool { 27 | self.contains(&identity) 28 | } 29 | } 30 | 31 | #[cfg(feature = "std")] 32 | impl AcceptableMasterList for std::vec::Vec { 33 | fn is_acceptable(&self, identity: ClockIdentity) -> bool { 34 | self.contains(&identity) 35 | } 36 | } 37 | 38 | #[cfg(feature = "std")] 39 | impl AcceptableMasterList for std::collections::BTreeSet { 40 | fn is_acceptable(&self, identity: ClockIdentity) -> bool { 41 | self.contains(&identity) 42 | } 43 | } 44 | 45 | #[cfg(feature = "std")] 46 | impl AcceptableMasterList for std::collections::HashSet { 47 | fn is_acceptable(&self, identity: ClockIdentity) -> bool { 48 | self.contains(&identity) 49 | } 50 | } 51 | 52 | impl AcceptableMasterList for Option { 53 | fn is_acceptable(&self, identity: ClockIdentity) -> bool { 54 | match self { 55 | Some(list) => list.is_acceptable(identity), 56 | None => true, 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /statime/src/datastructures/messages/delay_req.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::{common::WireTimestamp, WireFormat, WireFormatError}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub(crate) struct DelayReqMessage { 5 | pub(crate) origin_timestamp: WireTimestamp, 6 | } 7 | 8 | impl DelayReqMessage { 9 | pub(crate) fn content_size(&self) -> usize { 10 | 10 11 | } 12 | 13 | pub(crate) fn serialize_content(&self, buffer: &mut [u8]) -> Result<(), WireFormatError> { 14 | self.origin_timestamp.serialize(&mut buffer[0..10])?; 15 | 16 | Ok(()) 17 | } 18 | 19 | pub(crate) fn deserialize_content(buffer: &[u8]) -> Result { 20 | let slice = buffer.get(0..10).ok_or(WireFormatError::BufferTooShort)?; 21 | let origin_timestamp = WireTimestamp::deserialize(slice)?; 22 | 23 | Ok(Self { origin_timestamp }) 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | 31 | #[test] 32 | fn timestamp_wireformat() { 33 | let representations = [( 34 | [0x00, 0x00, 0x45, 0xb1, 0x11, 0x5a, 0x0a, 0x64, 0xfa, 0xb0], 35 | DelayReqMessage { 36 | origin_timestamp: WireTimestamp { 37 | seconds: 1169232218, 38 | nanos: 174389936, 39 | }, 40 | }, 41 | )]; 42 | 43 | for (byte_representation, object_representation) in representations { 44 | // Test the serialization output 45 | let mut serialization_buffer = [0; 10]; 46 | object_representation 47 | .serialize_content(&mut serialization_buffer) 48 | .unwrap(); 49 | assert_eq!(serialization_buffer, byte_representation); 50 | 51 | // Test the deserialization output 52 | let deserialized_data = 53 | DelayReqMessage::deserialize_content(&byte_representation).unwrap(); 54 | assert_eq!(deserialized_data, object_representation); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /statime/src/datastructures/messages/sync.rs: -------------------------------------------------------------------------------- 1 | use crate::datastructures::{common::WireTimestamp, WireFormat, WireFormatError}; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 4 | pub(crate) struct SyncMessage { 5 | pub(crate) origin_timestamp: WireTimestamp, 6 | } 7 | 8 | impl SyncMessage { 9 | pub(crate) fn content_size(&self) -> usize { 10 | 10 11 | } 12 | 13 | pub(crate) fn serialize_content(&self, buffer: &mut [u8]) -> Result<(), WireFormatError> { 14 | self.origin_timestamp.serialize(&mut buffer[0..10])?; 15 | Ok(()) 16 | } 17 | 18 | pub(crate) fn deserialize_content(buffer: &[u8]) -> Result { 19 | match buffer.get(0..10) { 20 | None => Err(WireFormatError::BufferTooShort), 21 | Some(slice) => Ok(Self { 22 | origin_timestamp: WireTimestamp::deserialize(slice)?, 23 | }), 24 | } 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn timestamp_wireformat() { 34 | let representations = [( 35 | [0x00, 0x00, 0x45, 0xb1, 0x11, 0x5a, 0x0a, 0x64, 0xfa, 0xb0], 36 | SyncMessage { 37 | origin_timestamp: WireTimestamp { 38 | seconds: 1169232218, 39 | nanos: 174389936, 40 | }, 41 | }, 42 | )]; 43 | 44 | for (byte_representation, object_representation) in representations { 45 | // Test the serialization output 46 | let mut serialization_buffer = [0; 10]; 47 | object_representation 48 | .serialize_content(&mut serialization_buffer) 49 | .unwrap(); 50 | assert_eq!(serialization_buffer, byte_representation); 51 | 52 | // Test the deserialization output 53 | let deserialized_data = SyncMessage::deserialize_content(&byte_representation).unwrap(); 54 | assert_eq!(deserialized_data, object_representation); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /statime/src/port/state.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{Display, Formatter}; 2 | 3 | use crate::{ 4 | datastructures::common::PortIdentity, 5 | time::{Duration, Time}, 6 | }; 7 | 8 | #[derive(Debug, Default)] 9 | #[allow(private_interfaces)] 10 | #[allow(clippy::large_enum_variant)] 11 | pub(crate) enum PortState { 12 | #[default] 13 | Faulty, 14 | Listening, 15 | Master, 16 | Passive, 17 | Slave(SlaveState), 18 | } 19 | 20 | impl Display for PortState { 21 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 22 | match self { 23 | PortState::Listening => write!(f, "Listening"), 24 | PortState::Master => write!(f, "Master"), 25 | PortState::Passive => write!(f, "Passive"), 26 | PortState::Slave(_) => write!(f, "Slave"), 27 | PortState::Faulty => write!(f, "Faulty"), 28 | } 29 | } 30 | } 31 | 32 | #[derive(Debug)] 33 | pub(crate) struct SlaveState { 34 | pub(super) remote_master: PortIdentity, 35 | 36 | pub(super) sync_state: SyncState, 37 | pub(super) delay_state: DelayState, 38 | 39 | pub(super) last_raw_sync_offset: Option, 40 | } 41 | 42 | impl SlaveState { 43 | pub(crate) fn remote_master(&self) -> PortIdentity { 44 | self.remote_master 45 | } 46 | } 47 | 48 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 49 | pub(super) enum SyncState { 50 | Empty, 51 | Measuring { 52 | id: u16, 53 | send_time: Option