Compare commits

...

No commits in common. "c8" and "c9" have entirely different histories.
c8 ... c9

12 changed files with 1333 additions and 71 deletions

3
.linuxptp.metadata Normal file
View File

@ -0,0 +1,3 @@
3ae72eb0ddafd8d8aeea7eac382d6e4a958717ef SOURCES/clknetsim-5d1dc0.tar.gz
309e6ab1fa3f61b3deb1735c3082dc2070870be1 SOURCES/linuxptp-4.2.tgz
7594d0705a1a648d5f7380d476bb3afebff21f6c SOURCES/linuxptp-testsuite-bf8ead.tar.gz

View File

@ -0,0 +1,176 @@
commit 073faba77e8a82c54210941cee6023dc9e719329
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Apr 25 14:25:57 2024 +0200
udp+udp6: Make IP addresses configurable.
Allow configuration of the multicast IPv4/IPv6 addresses, which can be
useful for testing. This complements the L2-specific ptp_dst_mac and
p2p_dst_mac options.
[ RPC: removed unused #defines PTP_PRIMARY_MCAST_IPADDR and PTP_PDELAY_MCAST_IPADDR ]
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
diff --git a/config.c b/config.c
index d7775c0..c220a3e 100644
--- a/config.c
+++ b/config.c
@@ -299,6 +299,9 @@ struct config_item config_tab[] = {
GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX),
PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_MAX),
+ PORT_ITEM_STR("p2p_dst_ipv4", "224.0.0.107"),
+ PORT_ITEM_STR("p2p_dst_ipv6", "FF02:0:0:0:0:0:0:6B"),
+ PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
PORT_ITEM_INT("path_trace_enabled", 0, 0, 1),
PORT_ITEM_INT("phc_index", -1, -1, INT_MAX),
GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX),
@@ -317,8 +320,9 @@ struct config_item config_tab[] = {
GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX),
GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX),
GLOB_ITEM_STR("productDescription", ";;"),
+ PORT_ITEM_STR("ptp_dst_ipv4", "224.0.1.129"),
+ PORT_ITEM_STR("ptp_dst_ipv6", "FF0E:0:0:0:0:0:0:181"),
PORT_ITEM_STR("ptp_dst_mac", "01:1B:19:00:00:00"),
- PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
GLOB_ITEM_INT("ptp_minor_version", 0, 0, 1),
GLOB_ITEM_STR("refclock_sock_address", "/var/run/refclock.ptp.sock"),
GLOB_ITEM_STR("revisionData", ";;"),
diff --git a/configs/default.cfg b/configs/default.cfg
index 8f94c16..54ce62a 100644
--- a/configs/default.cfg
+++ b/configs/default.cfg
@@ -94,6 +94,10 @@ write_phase_mode 0
# Transport options
#
transportSpecific 0x0
+ptp_dst_ipv4 224.0.1.129
+p2p_dst_ipv4 224.0.0.107
+ptp_dst_ipv6 FF0E:0:0:0:0:0:0:181
+p2p_dst_ipv6 FF02:0:0:0:0:0:0:6B
ptp_dst_mac 01:1B:19:00:00:00
p2p_dst_mac 01:80:C2:00:00:0E
udp_ttl 1
diff --git a/ptp4l.8 b/ptp4l.8
index c59b0b4..a4eb603 100644
--- a/ptp4l.8
+++ b/ptp4l.8
@@ -397,6 +397,27 @@ This value may be changed dynamically using the
POWER_PROFILE_SETTINGS_NP management message.
The default is "none".
+.TP
+.B ptp_dst_ipv4
+The IPv4 address to which PTP messages should be sent.
+Relevant only with UDPv4 transport. The default is 224.0.1.129.
+
+.TP
+.B p2p_dst_ipv4
+The IPv4 address to which peer delay messages should be sent.
+Relevant only with UDPv4 transport. The default is 224.0.0.107.
+
+.TP
+.B ptp_dst_ipv6
+The IPv6 address to which PTP messages should be sent.
+The second byte of the address is substituted with udp6_scope.
+Relevant only with UDPv6 transport. The default is FF0E:0:0:0:0:0:0:181.
+
+.TP
+.B p2p_dst_ipv6
+The IPv6 address to which peer delay messages should be sent.
+Relevant only with UDPv6 transport. The default is FF02:0:0:0:0:0:0:6B.
+
.TP
.B ptp_dst_mac
The MAC address to which PTP messages should be sent.
diff --git a/udp.c b/udp.c
index 7c9402e..38d0ec4 100644
--- a/udp.c
+++ b/udp.c
@@ -39,8 +39,6 @@
#define EVENT_PORT 319
#define GENERAL_PORT 320
-#define PTP_PRIMARY_MCAST_IPADDR "224.0.1.129"
-#define PTP_PDELAY_MCAST_IPADDR "224.0.0.107"
struct udp {
struct transport t;
@@ -157,6 +155,7 @@ static int udp_open(struct transport *t, struct interface *iface,
const char *name = interface_name(iface);
uint8_t event_dscp, general_dscp;
int efd, gfd, ttl;
+ char *str;
ttl = config_get_int(t->cfg, name, "udp_ttl");
udp->mac.len = 0;
@@ -165,11 +164,17 @@ static int udp_open(struct transport *t, struct interface *iface,
udp->ip.len = 0;
sk_interface_addr(name, AF_INET, &udp->ip);
- if (!inet_aton(PTP_PRIMARY_MCAST_IPADDR, &mcast_addr[MC_PRIMARY]))
+ str = config_get_string(t->cfg, name, "ptp_dst_ipv4");
+ if (!inet_aton(str, &mcast_addr[MC_PRIMARY])) {
+ pr_err("invalid ptp_dst_ipv4 %s", str);
return -1;
+ }
- if (!inet_aton(PTP_PDELAY_MCAST_IPADDR, &mcast_addr[MC_PDELAY]))
+ str = config_get_string(t->cfg, name, "p2p_dst_ipv4");
+ if (!inet_aton(str, &mcast_addr[MC_PDELAY])) {
+ pr_err("invalid p2p_dst_ipv4 %s", str);
return -1;
+ }
efd = open_socket(name, mcast_addr, EVENT_PORT, ttl);
if (efd < 0)
diff --git a/udp6.c b/udp6.c
index bde1710..188d20e 100644
--- a/udp6.c
+++ b/udp6.c
@@ -40,10 +40,6 @@
#define EVENT_PORT 319
#define GENERAL_PORT 320
-/* The 0x0e in second byte is substituted with udp6_scope at runtime. */
-#define PTP_PRIMARY_MCAST_IP6ADDR "FF0E:0:0:0:0:0:0:181"
-#define PTP_PDELAY_MCAST_IP6ADDR "FF02:0:0:0:0:0:0:6B"
-
enum { MC_PRIMARY, MC_PDELAY };
struct udp6 {
@@ -167,6 +163,7 @@ static int udp6_open(struct transport *t, struct interface *iface,
const char *name = interface_name(iface);
uint8_t event_dscp, general_dscp;
int efd, gfd, hop_limit;
+ char *str;
hop_limit = config_get_int(t->cfg, name, "udp_ttl");
udp6->mac.len = 0;
@@ -175,16 +172,20 @@ static int udp6_open(struct transport *t, struct interface *iface,
udp6->ip.len = 0;
sk_interface_addr(name, AF_INET6, &udp6->ip);
- if (1 != inet_pton(AF_INET6, PTP_PRIMARY_MCAST_IP6ADDR,
- &udp6->mc6_addr[MC_PRIMARY]))
+ str = config_get_string(t->cfg, name, "ptp_dst_ipv6");
+ if (1 != inet_pton(AF_INET6, str, &udp6->mc6_addr[MC_PRIMARY])) {
+ pr_err("invalid ptp_dst_ipv6 %s", str);
return -1;
+ }
udp6->mc6_addr[MC_PRIMARY].s6_addr[1] = config_get_int(t->cfg, name,
"udp6_scope");
- if (1 != inet_pton(AF_INET6, PTP_PDELAY_MCAST_IP6ADDR,
- &udp6->mc6_addr[MC_PDELAY]))
+ str = config_get_string(t->cfg, name, "p2p_dst_ipv6");
+ if (1 != inet_pton(AF_INET6, str, &udp6->mc6_addr[MC_PDELAY])) {
+ pr_err("invalid p2p_dst_ipv6 %s", str);
return -1;
+ }
efd = open_socket_ipv6(name, udp6->mc6_addr, EVENT_PORT, &udp6->index,
hop_limit);

View File

@ -0,0 +1,360 @@
commit c7acd4b5bdb95e45d93833ffaac9cac51dfe934b
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu May 16 15:52:48 2024 +0200
ts2phc: Avoid unnecessary call of getppstime().
Don't get the ToD timestamp for the pulse polarity detection if it won't
be needed (i.e. extts_polarity is not "both"). This allows PPS
timestamps to be saved even when the ToD source fails.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index 0d399b8..76912a2 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -277,21 +277,22 @@ static enum extts_result ts2phc_pps_sink_event(struct ts2phc_private *priv,
goto out;
}
- err = ts2phc_pps_source_getppstime(priv->src, &source_ts);
- if (err < 0) {
- pr_debug("source ts not valid");
- return 0;
- }
-
- if (sink->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE) &&
- ts2phc_pps_sink_ignore(priv, sink, source_ts)) {
+ if (sink->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE)) {
+ err = ts2phc_pps_source_getppstime(priv->src, &source_ts);
+ if (err < 0) {
+ pr_debug("source ts not valid");
+ return 0;
+ }
- pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 ".%ld",
- sink->name, event.index, event.t.sec, event.t.nsec,
- (int64_t) source_ts.tv_sec, source_ts.tv_nsec);
+ if (ts2phc_pps_sink_ignore(priv, sink, source_ts)) {
+ pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 ".%ld",
+ sink->name, event.index, event.t.sec,
+ event.t.nsec, (int64_t)source_ts.tv_sec,
+ source_ts.tv_nsec);
- result = EXTTS_IGNORE;
- goto out;
+ result = EXTTS_IGNORE;
+ goto out;
+ }
}
out:
commit 9880cccc928351a42a3fa9e018949442aca7ddae
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu May 16 15:52:49 2024 +0200
ts2phc: Add holdover support.
If the external PPS signal is generated by a clock with better long-term
stability than the clock synchronizing to it, it can work as a good time
source for some period of time after losing its own time source (e.g.
GPS receiver losing its signal).
Add an option to specify a holdover interval where ts2phc can continue
synchronizing the clock without any ToD information. Allow that only in
the LOCKED_STABLE servo state, which needs to be enabled by the
servo_num_offset_values option.
This is supported only in the non-automatic mode and when the pulse
polarity detection is disabled.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/config.c b/config.c
index c220a3e..58481db 100644
--- a/config.c
+++ b/config.c
@@ -344,6 +344,7 @@ struct config_item config_tab[] = {
PORT_ITEM_INT("ts2phc.channel", 0, 0, INT_MAX),
PORT_ITEM_INT("ts2phc.extts_correction", 0, INT_MIN, INT_MAX),
PORT_ITEM_ENU("ts2phc.extts_polarity", PTP_RISING_EDGE, extts_polarity_enu),
+ PORT_ITEM_INT("ts2phc.holdover", 0, 0, INT_MAX),
PORT_ITEM_INT("ts2phc.master", 0, 0, 1),
PORT_ITEM_INT("ts2phc.nmea_baudrate", 9600, 300, INT_MAX),
GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
diff --git a/ts2phc.8 b/ts2phc.8
index 852a527..bcc6f61 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -168,6 +168,16 @@ by changing the clock frequency instead of stepping the clock. When
set to 0.0, the servo will never step the clock except on start.
The default is 0.0.
+.TP
+.B ts2phc.holdover
+The holdover interval, specified in seconds. When the ToD information stops
+working (e.g. GNSS receiver lost its fix), ts2phc is allowed for the specified
+interval to continue synchronizing the target clock as long as the servo is in
+the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the
+\fBservo_num_offset_values\fP option. The holdover is not supported with the
+\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP.
+The default is 0 (disabled).
+
.TP
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
Specifies the remote host providing ToD information when using the
diff --git a/ts2phc.c b/ts2phc.c
index 4817c85..d552e0f 100644
--- a/ts2phc.c
+++ b/ts2phc.c
@@ -440,9 +440,10 @@ static int ts2phc_pps_source_implicit_tstamp(struct ts2phc_private *priv,
static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
{
+ struct timespec source_ts, now;
tmv_t source_tmv;
struct ts2phc_clock *c;
- int valid, err;
+ int holdover, valid;
if (autocfg) {
if (!priv->ref_clock) {
@@ -456,9 +457,20 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
return;
}
} else {
- err = ts2phc_pps_source_implicit_tstamp(priv, &source_tmv);
- if (err < 0)
+ valid = !ts2phc_pps_source_implicit_tstamp(priv, &source_tmv);
+ }
+
+ if (valid) {
+ priv->holdover_start = 0;
+ holdover = 0;
+ } else {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+
+ if (!priv->holdover_start)
+ priv->holdover_start = now.tv_sec;
+ if (now.tv_sec >= priv->holdover_start + priv->holdover_length)
return;
+ holdover = 1;
}
LIST_FOREACH(c, &priv->clocks, list) {
@@ -475,6 +487,16 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
continue;
}
+ if (holdover) {
+ if (c->servo_state != SERVO_LOCKED_STABLE)
+ continue;
+ source_ts = tmv_to_timespec(ts);
+ if (source_ts.tv_nsec > NS_PER_SEC / 2)
+ source_ts.tv_sec++;
+ source_ts.tv_nsec = 0;
+ source_tmv = timespec_to_tmv(source_ts);
+ }
+
offset = tmv_to_nanoseconds(tmv_sub(ts, source_tmv));
if (c->no_adj) {
@@ -486,8 +508,15 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
adj = servo_sample(c->servo, offset, tmv_to_nanoseconds(ts),
SAMPLE_WEIGHT, &c->servo_state);
- pr_info("%s offset %10" PRId64 " s%d freq %+7.0f",
- c->name, offset, c->servo_state, adj);
+ if (holdover && c->servo_state != SERVO_LOCKED_STABLE) {
+ pr_info("%s lost holdover lock (offset %10" PRId64 ")",
+ c->name, offset);
+ continue;
+ }
+
+ pr_info("%s offset %10" PRId64 " s%d freq %+7.0f%s",
+ c->name, offset, c->servo_state, adj,
+ holdover ? " holdover" : "");
switch (c->servo_state) {
case SERVO_UNLOCKED:
@@ -751,6 +780,9 @@ int main(int argc, char *argv[])
return -1;
}
+ priv.holdover_length = config_get_int(cfg, NULL, "ts2phc.holdover");
+ priv.holdover_start = 0;
+
while (is_running()) {
struct ts2phc_clock *c;
diff --git a/ts2phc.h b/ts2phc.h
index 4833ded..5dbde9b 100644
--- a/ts2phc.h
+++ b/ts2phc.h
@@ -55,6 +55,8 @@ struct ts2phc_private {
bool state_changed;
LIST_HEAD(port_head, ts2phc_port) ports;
LIST_HEAD(clock_head, ts2phc_clock) clocks;
+ int holdover_length;
+ time_t holdover_start;
};
struct ts2phc_clock *ts2phc_clock_add(struct ts2phc_private *priv,
commit bf237fd55d4983a42d9344890dd861f18bea70ca
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Jul 25 12:43:59 2024 +0200
ts2phc: Describe servo options in man page.
Copy and adapt missing servo options used by ts2phc from the ptp4l man
page.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc.8 b/ts2phc.8
index bcc6f61..4c54576 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -117,6 +117,16 @@ command line option.
.SH GLOBAL OPTIONS
+.TP
+.B clock_servo
+The servo which is used to synchronize the local clock. Valid values
+are "pi" for a PI controller, "linreg" for an adaptive controller
+using linear regression, "ntpshm" and "refclock_sock" for the NTP SHM and
+chrony SOCK reference clocks respectively to allow another process to
+synchronize the local clock, and "nullf" for a servo that always dials
+frequency offset zero (for use in SyncE nodes).
+The default is "pi".
+
.TP
.B first_step_threshold
The maximum offset, specified in seconds, that the servo will correct by
@@ -162,12 +172,64 @@ with the log level of the message as a number. The default is an empty string
argument).
.TP
-.B step_threshold
-The maximum offset, specified in seconds, that the servo will correct
-by changing the clock frequency instead of stepping the clock. When
-set to 0.0, the servo will never step the clock except on start.
+.B ntpshm_segment
+The number of the SHM segment used by ntpshm servo.
+The default is 0.
+
+.TP
+.B pi_integral_const
+The integral constant of the PI controller. When set to 0.0, the
+integral constant will be set by the following formula from the current
+sync interval.
+The default is 0.0.
+
+ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync)
+
+.TP
+.B pi_integral_exponent
+The ki_exponent constant in the formula used to set the integral constant of
+the PI controller from the sync interval.
+The default is 0.4.
+
+.TP
+.B pi_integral_norm_max
+The ki_norm_max constant in the formula used to set the integral constant of
+the PI controller from the sync interval.
+The default is 0.3.
+
+.TP
+.B pi_integral_scale
+The ki_scale constant in the formula used to set the integral constant of
+the PI controller from the sync interval.
+The default is 0.3.
+
+.TP
+.B pi_proportional_const
+The proportional constant of the PI controller. When set to 0.0, the
+proportional constant will be set by the following formula from the current
+sync interval.
The default is 0.0.
+kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync)
+
+.TP
+.B pi_proportional_exponent
+The kp_exponent constant in the formula used to set the proportional constant of
+the PI controller from the sync interval.
+The default is \-0.3.
+
+.TP
+.B pi_proportional_norm_max
+The kp_norm_max constant in the formula used to set the proportional constant of
+the PI controller from the sync interval.
+The default is 0.7
+
+.TP
+.B pi_proportional_scale
+The kp_scale constant in the formula used to set the proportional constant of
+the PI controller from the sync interval.
+The default is 0.7.
+
.TP
.B ts2phc.holdover
The holdover interval, specified in seconds. When the ToD information stops
@@ -178,6 +240,28 @@ the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the
\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP.
The default is 0 (disabled).
+.TP
+.B servo_num_offset_values
+The number of offset values considered in order to transition from the
+SERVO_LOCKED to the SERVO_LOCKED_STABLE state.
+The transition occurs once the last 'servo_num_offset_values' offsets
+are all below the 'servo_offset_threshold' value.
+The default value is 10.
+
+.TP
+.B servo_offset_threshold
+The offset threshold used in order to transition from the SERVO_LOCKED
+to the SERVO_LOCKED_STABLE state. The transition occurs once the
+last 'servo_num_offset_values' offsets are all below the threshold value.
+The default value of offset_threshold is 0 (disabled).
+
+.TP
+.B step_threshold
+The maximum offset, specified in seconds, that the servo will correct
+by changing the clock frequency instead of stepping the clock. When
+set to 0.0, the servo will never step the clock except on start.
+The default is 0.0.
+
.TP
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
Specifies the remote host providing ToD information when using the
commit 7734d2fe8ac52afaa233262548615f79021ae6ee
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Jul 25 12:46:43 2024 +0200
ts2phc: Fix description of holdover option in man page.
Fix the man page to explain that the LOCKED_STABLE servo state is
enabled by setting the servo_offset_threshold, not
servo_num_offset_values, which is already enabled by default.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc.8 b/ts2phc.8
index 4c54576..c0b718b 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -236,7 +236,7 @@ The holdover interval, specified in seconds. When the ToD information stops
working (e.g. GNSS receiver lost its fix), ts2phc is allowed for the specified
interval to continue synchronizing the target clock as long as the servo is in
the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the
-\fBservo_num_offset_values\fP option. The holdover is not supported with the
+\fBservo_offset_threshold\fP option. The holdover is not supported with the
\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP.
The default is 0 (disabled).

View File

@ -1,29 +1,365 @@
commit 0404c1924254c9162222dd5000a140165d21250c
commit 0c406008b530140ed6d992915a6c8a1e5abf15d5
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Jan 25 15:23:44 2024 +0100
Date: Thu Jan 25 11:26:15 2024 +0100
ts2phc: Fix offset for NMEA delays over 0.5 seconds.
ts2phc: Don't switch system clock to nanosecond mode.
The current code of ts2phc assumes that the NMEA RMC message is received
within 0.5 seconds of the pulse the timestamp in the message is refering
to. However, with most receivers NMEA messages are referenced to the
previous pulse. This causes a 1-second error in the measured offset for
receivers with delays over 0.5 seconds.
ts2phc is not synchronizing the system clock and should not switch the
clock to the nanosecond mode with adjtimex(modes=ADJ_NANO) or make any
other modifications to it. The process that is controlling the clock
(e.g. an NTP client) might not be using the nanosecond mode.
Add a 0.5 second correction to map the whole interval between pulses to
the preceding pulse.
There are two instances of the adjtimex() call in the code. One is used
only to read the clock and can be replaced with faster clock_gettime().
The other instance is also reading the TAI offset. Instead of switching
to the nanosecond mode, change the timestamp conversion to handle both
microsecond and nanosecond modes according to the current clock status.
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_generic_pps_source.c b/ts2phc_generic_pps_source.c
index d503aac..e6b8145 100644
--- a/ts2phc_generic_pps_source.c
+++ b/ts2phc_generic_pps_source.c
@@ -38,7 +38,6 @@ static int get_ntx(struct timex *ntx)
return -1;
memset(ntx, 0, sizeof(*ntx));
- ntx->modes = ADJ_NANO;
code = adjtimex(ntx);
if (code == -1) {
pr_err("adjtimex failed: %m");
@@ -93,7 +92,10 @@ static int ts2phc_generic_pps_source_getppstime(struct ts2phc_pps_source *src,
}
ts->tv_sec = ntx.time.tv_sec + tai_offset;
- ts->tv_nsec = ntx.time.tv_usec;
+ if (ntx.status & STA_NANO)
+ ts->tv_nsec = ntx.time.tv_usec;
+ else
+ ts->tv_nsec = ntx.time.tv_usec * 1000;
return 0;
}
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
index 3a4267d..5b2e06b 100644
--- a/ts2phc_nmea_pps_source.c
+++ b/ts2phc_nmea_pps_source.c
@@ -62,14 +62,13 @@ static int open_nmea_connection(const char *host, const char *port,
static void *monitor_nmea_status(void *arg)
{
+ struct timespec rxtime, rxtime_rt, tmo = { 2, 0 };
struct nmea_parser *np = nmea_parser_create();
struct pollfd pfd = { -1, POLLIN | POLLPRI };
char *host, input[256], *port, *ptr, *uart;
struct ts2phc_nmea_pps_source *s = arg;
- struct timespec rxtime, tmo = { 2, 0 };
int cnt, num, parsed, baud;
struct nmea_rmc rmc;
- struct timex ntx;
if (!np) {
pr_err("failed to create NMEA parser");
@@ -79,8 +78,6 @@ static void *monitor_nmea_status(void *arg)
port = config_get_string(s->config, NULL, "ts2phc.nmea_remote_port");
uart = config_get_string(s->config, NULL, "ts2phc.nmea_serialport");
baud = config_get_int(s->config, NULL, "ts2phc.nmea_baudrate");
- memset(&ntx, 0, sizeof(ntx));
- ntx.modes = ADJ_NANO;
while (is_running()) {
if (pfd.fd == -1) {
@@ -92,7 +89,7 @@ static void *monitor_nmea_status(void *arg)
}
num = poll(&pfd, 1, NMEA_TMO);
clock_gettime(CLOCK_MONOTONIC, &rxtime);
- adjtimex(&ntx);
+ clock_gettime(CLOCK_REALTIME, &rxtime_rt);
if (num < 0) {
pr_err("poll failed");
break;
@@ -124,8 +121,7 @@ static void *monitor_nmea_status(void *arg)
if (!nmea_parse(np, ptr, cnt, &rmc, &parsed)) {
pthread_mutex_lock(&s->mutex);
s->local_monotime = rxtime;
- s->local_utctime.tv_sec = ntx.time.tv_sec;
- s->local_utctime.tv_nsec = ntx.time.tv_usec;
+ s->local_utctime = rxtime_rt;
s->rmc_utctime = rmc.ts;
s->rmc_fix_valid = rmc.fix_valid;
pthread_mutex_unlock(&s->mutex);
commit 72b44bc885e519667a12c89d5b640484807e4946
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Tue Jun 4 08:57:15 2024 +0200
ts2phc: Use CLOCK_MONOTONIC_RAW for NMEA PPS timestamp.
In the calculation of the NMEA PPS timestamp is used an interval
measured by the CLOCK_MONOTONIC system clock. This clock may have a
large frequency error when another process (e.g. phc2sys or an NTP
client) is correcting a large time error by slewing.
This frequency error may cause the timestamp to overflow into the next
second and cause a one-second error in the measured offset, or the wrong
edge of the pulse to be rejected.
Switch from CLOCK_MONOTONIC to CLOCK_MONOTONIC_RAW to avoid the
impact of the system clock adjustments.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
index 3a4267d..274b70a 100644
index 5b2e06b..7a28433 100644
--- a/ts2phc_nmea_pps_source.c
+++ b/ts2phc_nmea_pps_source.c
@@ -191,6 +191,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
return -1;
@@ -88,7 +88,7 @@ static void *monitor_nmea_status(void *arg)
}
}
num = poll(&pfd, 1, NMEA_TMO);
- clock_gettime(CLOCK_MONOTONIC, &rxtime);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &rxtime);
clock_gettime(CLOCK_REALTIME, &rxtime_rt);
if (num < 0) {
pr_err("poll failed");
@@ -160,7 +160,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
int64_t utc_time;
bool fix_valid;
- clock_gettime(CLOCK_MONOTONIC, &now);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
local_t2 = timespec_to_tmv(now);
pthread_mutex_lock(&m->mutex);
commit 30e6c4dba892236d8cfe08dc6c55238e11504c71
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Mon Jun 3 10:32:05 2024 +0200
ts2phc: Provide source type.
Save the PPS source type in the instance and add a function to retrieve
it. This will be needed for NMEA-specific rounding of timestamps.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_pps_source.c b/ts2phc_pps_source.c
index c333f65..ae2ad46 100644
--- a/ts2phc_pps_source.c
+++ b/ts2phc_pps_source.c
@@ -26,6 +26,8 @@ struct ts2phc_pps_source *ts2phc_pps_source_create(struct ts2phc_private *priv,
src = ts2phc_phc_pps_source_create(priv, dev);
break;
}
rmc = tmv_add(rmc, duration_since_rmc);
+ rmc = tmv_add(rmc, nanoseconds_to_tmv(500000000));
utc_time = tmv_to_nanoseconds(rmc);
utc_time /= (int64_t) 1000000000;
*ts = tmv_to_timespec(rmc);
+ if (src)
+ src->type = type;
return src;
}
@@ -46,3 +48,8 @@ struct ts2phc_clock *ts2phc_pps_source_get_clock(struct ts2phc_pps_source *src)
return NULL;
}
+
+enum ts2phc_pps_source_type ts2phc_pps_source_get_type(struct ts2phc_pps_source *src)
+{
+ return src->type;
+}
diff --git a/ts2phc_pps_source.h b/ts2phc_pps_source.h
index 293c693..c87e3af 100644
--- a/ts2phc_pps_source.h
+++ b/ts2phc_pps_source.h
@@ -53,4 +53,11 @@ int ts2phc_pps_source_getppstime(struct ts2phc_pps_source *src, struct timespec
struct ts2phc_clock *ts2phc_pps_source_get_clock(struct ts2phc_pps_source *src);
+/**
+ * Returns the type of the PPS source
+ * @param src Pointer to a source obtained via @ref ts2phc_pps_source_create().
+ * @return The type of the clock.
+ */
+enum ts2phc_pps_source_type ts2phc_pps_source_get_type(struct ts2phc_pps_source *src);
+
#endif
diff --git a/ts2phc_pps_source_private.h b/ts2phc_pps_source_private.h
index 99e6a78..ea6a8ad 100644
--- a/ts2phc_pps_source_private.h
+++ b/ts2phc_pps_source_private.h
@@ -13,6 +13,7 @@
#include "ts2phc_pps_source.h"
struct ts2phc_pps_source {
+ enum ts2phc_pps_source_type type;
void (*destroy)(struct ts2phc_pps_source *src);
int (*getppstime)(struct ts2phc_pps_source *src, struct timespec *ts);
struct ts2phc_clock *(*get_clock)(struct ts2phc_pps_source *src);
commit bebd15ae90dbfcb74a33e5b428f24c733abf1134
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Mon Jun 3 11:06:16 2024 +0200
ts2phc: Fix edge rejection for pulse widths over 0.5s.
If the configured pulse width is longer than 0.5 seconds, the calculated
range of ignored offsets is too small to cover the wrong edge. Fix the
calculation of the limits to use the minimum of pulsewidth and
(1.0s - pulsewidth). A pulsewidth of 0.5s should give the shortest interval.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index 05ac225..ca9f721 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -175,6 +175,8 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri
sink->correction = nanoseconds_to_tmv(correction);
pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth");
+ if (pulsewidth > 500000000)
+ pulsewidth = 1000000000 - pulsewidth;
pulsewidth /= 2;
sink->ignore_upper = 1000000000 - pulsewidth;
sink->ignore_lower = pulsewidth;
commit 435e9fc6ebec8daa8ab9f88c2d590e35ace9b2f6
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Mon Jun 3 13:57:23 2024 +0200
ts2phc: Move upper/lower rejection limit calculation.
The ignore_upper and ignore_lower fields of the ts2phc_pps_sink struct
are calculated when the PPS source is not known yet. Replace the fields
with the configured pulsewidth and calculate the limits locally later
when needed in ts2phc_pps_sink_ignore(). This will allow an
NMEA-specific calculation of the limits.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index ca9f721..8121afb 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -30,8 +30,7 @@ struct ts2phc_pps_sink {
struct ptp_pin_desc pin_desc;
unsigned int polarity;
tmv_t correction;
- uint32_t ignore_lower;
- uint32_t ignore_upper;
+ uint32_t pulsewidth;
struct ts2phc_clock *clock;
};
@@ -153,8 +152,8 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri
struct config *cfg = priv->cfg;
struct ptp_extts_request extts;
struct ts2phc_pps_sink *sink;
- int err, pulsewidth;
int32_t correction;
+ int err;
sink = calloc(1, sizeof(*sink));
if (!sink) {
@@ -174,12 +173,9 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri
correction = config_get_int(cfg, device, "ts2phc.extts_correction");
sink->correction = nanoseconds_to_tmv(correction);
- pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth");
- if (pulsewidth > 500000000)
- pulsewidth = 1000000000 - pulsewidth;
- pulsewidth /= 2;
- sink->ignore_upper = 1000000000 - pulsewidth;
- sink->ignore_lower = pulsewidth;
+ sink->pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth");
+ if (sink->pulsewidth > 500000000)
+ sink->pulsewidth = 1000000000 - sink->pulsewidth;
sink->clock = ts2phc_clock_add(priv, device);
if (!sink->clock) {
@@ -243,12 +239,16 @@ static bool ts2phc_pps_sink_ignore(struct ts2phc_private *priv,
struct timespec source_ts)
{
tmv_t source_tmv = timespec_to_tmv(source_ts);
+ uint32_t ignore_lower, ignore_upper;
source_tmv = tmv_sub(source_tmv, priv->perout_phase);
source_ts = tmv_to_timespec(source_tmv);
- return source_ts.tv_nsec > sink->ignore_lower &&
- source_ts.tv_nsec < sink->ignore_upper;
+ ignore_upper = 1000000000 - sink->pulsewidth / 2;
+ ignore_lower = sink->pulsewidth / 2;
+
+ return source_ts.tv_nsec > ignore_lower &&
+ source_ts.tv_nsec < ignore_upper;
}
static enum extts_result ts2phc_pps_sink_event(struct ts2phc_private *priv,
commit 0257b245df1a32869f356c0cfbeacfe5f0a522f5
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Mon Jun 3 11:31:45 2024 +0200
ts2phc: Allow longer NMEA delays.
Timestamps from PPS sources are rounded to the nearest pulse, i.e. for
correct synchronization the offset between the source timestamp and
external PPS event timestamp needs to be between -0.5 and +0.5 seconds.
For the pulse edge rejection to work correctly it needs to be even
smaller, between -pulsewidth/2 and +pulsewidth/2.
With the NMEA PPS source the offset is the delay in the reception of the
RMC message. This message is not expected to come exactly on time with
the corresponding pulse. A positive delay is expected, typically at
least few tens of milliseconds, but some receivers have delays longer
than 0.5 seconds.
Add NMEA-specific rounding of the offset to not expect negative delays
and allow positive delays of up to 1 second, or whole pulsewidth if the
edge rejection is enabled.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc.c b/ts2phc.c
index 03c88b1..4817c85 100644
--- a/ts2phc.c
+++ b/ts2phc.c
@@ -418,9 +418,16 @@ static int ts2phc_pps_source_implicit_tstamp(struct ts2phc_private *priv,
* deduce the timestamp (actually only seconds part, nanoseconds are by
* construction zero) of this edge at the emitter based on the
* emitter's current time.
+ *
+ * With an NMEA source assume its messages always follow the pulse, i.e.
+ * assign the timestamp to the previous pulse instead of nearest pulse.
*/
- if (source_ts.tv_nsec > NS_PER_SEC / 2)
+ if (ts2phc_pps_source_get_type(priv->src) == TS2PHC_PPS_SOURCE_NMEA) {
source_ts.tv_sec++;
+ } else {
+ if (source_ts.tv_nsec > NS_PER_SEC / 2)
+ source_ts.tv_sec++;
+ }
source_ts.tv_nsec = 0;
tmv = timespec_to_tmv(source_ts);
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
index 8121afb..0d399b8 100644
--- a/ts2phc_pps_sink.c
+++ b/ts2phc_pps_sink.c
@@ -244,8 +244,13 @@ static bool ts2phc_pps_sink_ignore(struct ts2phc_private *priv,
source_tmv = tmv_sub(source_tmv, priv->perout_phase);
source_ts = tmv_to_timespec(source_tmv);
- ignore_upper = 1000000000 - sink->pulsewidth / 2;
- ignore_lower = sink->pulsewidth / 2;
+ if (ts2phc_pps_source_get_type(priv->src) == TS2PHC_PPS_SOURCE_NMEA) {
+ ignore_upper = sink->pulsewidth;
+ ignore_lower = 0;
+ } else {
+ ignore_upper = 1000000000 - sink->pulsewidth / 2;
+ ignore_lower = sink->pulsewidth / 2;
+ }
return source_ts.tv_nsec > ignore_lower &&
source_ts.tv_nsec < ignore_upper;

View File

@ -0,0 +1,83 @@
commit 0d508d8cfb96cc32d3516f1d0d3e80db49f00468
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Mon Jun 3 16:43:54 2024 +0200
ts2phc: Add option to correct for NMEA delay.
Add an option to specify the minimum expected delay of NMEA RMC messages
to correct timestamps returned by the NMEA PPS time source.
This enables operation with receivers that have delays outside of the
expected 0-1.0s interval, or 0-pulsewidth if the PPS edge rejection is
enabled.
[ RPC: Preserve alphabetical ordering in the ts2phc man page. ]
(Rebased to 4.2)
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
diff --git a/config.c b/config.c
index 58481db..d441d1e 100644
--- a/config.c
+++ b/config.c
@@ -347,6 +347,7 @@ struct config_item config_tab[] = {
PORT_ITEM_INT("ts2phc.holdover", 0, 0, INT_MAX),
PORT_ITEM_INT("ts2phc.master", 0, 0, 1),
PORT_ITEM_INT("ts2phc.nmea_baudrate", 9600, 300, INT_MAX),
+ PORT_ITEM_INT("ts2phc.nmea_delay", 0, INT_MIN, INT_MAX),
GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
GLOB_ITEM_STR("ts2phc.nmea_remote_port", ""),
GLOB_ITEM_STR("ts2phc.nmea_serialport", "/dev/ttyS0"),
diff --git a/ts2phc.8 b/ts2phc.8
index c0b718b..b8383ff 100644
--- a/ts2phc.8
+++ b/ts2phc.8
@@ -262,6 +262,16 @@ by changing the clock frequency instead of stepping the clock. When
set to 0.0, the servo will never step the clock except on start.
The default is 0.0.
+.TP
+.B ts2phc.nmea_delay
+Specifies the minimum expected delay of NMEA RMC messages in nanoseconds.
+If the maximum delay is longer than 1 second, or 'ts2phc.pulsewidth'
+if 'ts2phc.extts_polarity' is set to "both", this option needs to be set
+accordingly to allow the timestamps from NMEA messages to be correctly
+assigned to pulses from the PPS signal and wrong PPS edges to be rejected if
+the edge rejection is enabled.
+The default is 0 nanoseconds.
+
.TP
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
Specifies the remote host providing ToD information when using the
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
index bdfaf19..e345969 100644
--- a/ts2phc_nmea_pps_source.c
+++ b/ts2phc_nmea_pps_source.c
@@ -33,6 +33,7 @@ struct ts2phc_nmea_pps_source {
pthread_t worker;
/* Protects anonymous struct fields, below, from concurrent access. */
pthread_mutex_t mutex;
+ tmv_t delay_correction;
struct {
struct timespec local_monotime;
struct timespec local_utctime;
@@ -207,6 +208,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
}
rmc = tmv_add(rmc, duration_since_rmc);
+ rmc = tmv_add(rmc, m->delay_correction);
*ts = tmv_to_timespec(rmc);
ts->tv_sec += tai_offset;
@@ -234,6 +236,8 @@ struct ts2phc_pps_source *ts2phc_nmea_pps_source_create(struct ts2phc_private *p
s->pps_source.destroy = ts2phc_nmea_pps_source_destroy;
s->pps_source.getppstime = ts2phc_nmea_pps_source_getppstime;
s->config = priv->cfg;
+ s->delay_correction = nanoseconds_to_tmv(
+ config_get_int(priv->cfg, NULL, "ts2phc.nmea_delay"));
pthread_mutex_init(&s->mutex, NULL);
err = pthread_create(&s->worker, NULL, monitor_nmea_status, s);
if (err) {

View File

@ -0,0 +1,76 @@
commit f3c742e24a40cf75272ec39789a2cba35389230d
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Tue Jun 11 15:25:54 2024 +0200
nmea: Fix conversion of leap second.
When a leap second is inserted, the RMC message reports time of
23:59:60, which overflows in mktime() to 0:00:00 after the leap second
with an incremented TAI-UTC offset. This causes a one-second error in
the offset meaured by ts2phc.
Check the seconds field of the RMC message and convert 60 to 59 to make
the timestamp ambiguous per is_utc_ambiguous() and ignored by ts2phc to
avoid updating the clock with the one-second error.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/nmea.c b/nmea.c
index 7f1d9a2..b84b42e 100644
--- a/nmea.c
+++ b/nmea.c
@@ -155,6 +155,9 @@ static int nmea_scan_rmc(struct nmea_parser *np, struct nmea_rmc *result)
if (cnt != 3) {
return -1;
}
+ /* Convert an inserted leap second to ambiguous 23:59:59 */
+ if (tm.tm_sec == 60)
+ tm.tm_sec = 59;
tm.tm_year += 100;
tm.tm_mon--;
tm.tm_isdst = 0;
commit b396d361b0d290ce83395851860c2dcd074e0f3b
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Tue Jun 11 15:32:55 2024 +0200
ts2phc: Fix timestamp conversion for leap seconds.
The UTC timestamp parsed from the RMC message needs to be converted to
TAI in order to calculate the PHC offset. This conversion was done after
adjusting the timestamp for the measured delay between the reception of
the message and the following pulse, which caused the offset measured by
ts2phc to have a one-second error if the message expected during or
after a leap second was missed.
Apply the TAI-UTC offset to the timestamp parsed from the RMC message
before any adjustments are made to avoid the error.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
index 7a28433..bdfaf19 100644
--- a/ts2phc_nmea_pps_source.c
+++ b/ts2phc_nmea_pps_source.c
@@ -186,10 +186,9 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
pr_err("nmea: rmc time stamp stale");
return -1;
}
- rmc = tmv_add(rmc, duration_since_rmc);
+
utc_time = tmv_to_nanoseconds(rmc);
utc_time /= (int64_t) 1000000000;
- *ts = tmv_to_timespec(rmc);
result = lstab_utc2tai(m->lstab, utc_time, &tai_offset);
switch (result) {
@@ -206,6 +205,9 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
pr_err("nmea: utc time stamp is ambiguous");
break;
}
+
+ rmc = tmv_add(rmc, duration_since_rmc);
+ *ts = tmv_to_timespec(rmc);
ts->tv_sec += tai_offset;
return lstab_error;

View File

@ -0,0 +1,25 @@
commit b4ad9dccf23cef2d79621cd36987428a65f2bcc9
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Tue Jun 11 12:35:10 2024 +0200
ts2phc: Reset parser after RMC message.
When the receiver was configured to generate only RMC messages, every
other message was skipped due to the parser not being in the right
state to process another message. Reset the parser to correctly parse
every valid message.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/nmea.c b/nmea.c
index 44c7c01..7f1d9a2 100644
--- a/nmea.c
+++ b/nmea.c
@@ -171,6 +171,7 @@ int nmea_parse(struct nmea_parser *np, const char *ptr, int buflen,
while (buflen) {
if (!nmea_parse_symbol(np, *ptr)) {
if (!nmea_scan_rmc(np, result)) {
+ nmea_reset(np);
*parsed = count + 1;
return 0;
}

View File

@ -0,0 +1,73 @@
commit e3867eb1ce2282f9ac4cc729a3b1beb74bb017d6
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Tue Nov 26 15:10:34 2024 +0100
phc2sys: Don't require -O option without -a and -w.
Allow phc2sys to be started in the static mode without the -O option,
meaning it should fetch the UTC offset from ptp4l, even if the -w option
is not included on the command line to wait for ptp4l to have a port in
the master or slave state.
This is useful when the PHC is synchronized by ts2phc or other
application and the system clock needs to be synchronized to the PHC
even when ptp4l has all ports in the faulty state (e.g. all network
links are down).
It's the responsibility of the user to start phc2sys when ptp4l is
already running. phc2sys will exit with an error message if it cannot
get a response with the 1-second timeout.
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
diff --git a/phc2sys.8 b/phc2sys.8
index df0608a..5ca467d 100644
--- a/phc2sys.8
+++ b/phc2sys.8
@@ -161,7 +161,10 @@ minimize the error caused by random delays in scheduling and bus utilization.
The default is 5.
.TP
.BI \-O " offset"
-Specify the offset between the sink and source times in seconds. Not
+Specify the offset between the sink and source times in seconds. If not
+specified, the offset will be set according to the currentUtcOffset value
+obtained from ptp4l and the direction of synchronization, assuming the system
+clock is keeping time in UTC and all used PHCs keep time in TAI. Not
compatible with the
.B \-a
option. See
diff --git a/phc2sys.c b/phc2sys.c
index 1745558..d8b0991 100644
--- a/phc2sys.c
+++ b/phc2sys.c
@@ -1406,12 +1406,6 @@ int main(int argc, char *argv[])
goto bad_usage;
}
- if (!autocfg && !wait_sync && !settings.forced_sync_offset) {
- fprintf(stderr,
- "time offset must be specified using -w or -O\n");
- goto bad_usage;
- }
-
if (hardpps_configured(pps_fd) && (dst_cnt != 1 ||
strcmp(dst_names[0], "CLOCK_REALTIME"))) {
fprintf(stderr,
@@ -1501,7 +1495,7 @@ int main(int argc, char *argv[])
r = -1;
- if (wait_sync) {
+ if (wait_sync || !domains[0].forced_sync_offset) {
snprintf(uds_local, sizeof(uds_local),
"/var/run/phc2sys.%d", getpid());
@@ -1516,7 +1510,7 @@ int main(int argc, char *argv[])
phc2sys_recv_subscribed, &domains[0]))
goto end;
- while (is_running()) {
+ while (wait_sync && is_running()) {
r = run_pmc_wait_sync(domains[0].agent, 1000);
if (r < 0)
goto end;

View File

@ -0,0 +1,57 @@
commit b2ceafdd8e4cde6c0cef67373006049b12f45c4a
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date: Thu Apr 28 14:23:57 2022 +0200
timemaster: Check for RH-specific kernel with vclock support.
diff --git a/timemaster.8 b/timemaster.8
index bc0b5b6..50699d6 100644
--- a/timemaster.8
+++ b/timemaster.8
@@ -104,7 +104,7 @@ Enable or disable synchronization with virtual clocks. If enabled,
needed by configured PTP domains. This enables hardware time stamping for
multiple \fBptp4l\fR instances using the same network interface. The default
value is -1, which enables the virtual clocks if running on Linux 5.18 or
-later.
+later, or the EL9-specific kernel-5.14.0-106 or later release.
.SS [ntp_server address]
diff --git a/timemaster.c b/timemaster.c
index fab71fc..05f0af4 100644
--- a/timemaster.c
+++ b/timemaster.c
@@ -546,6 +546,23 @@ static int check_kernel_version(int version, int patch)
return 0;
}
+static int check_rh_kernel_version(const char *el, int version, int patch,
+ int sub, int release)
+{
+ struct utsname uts;
+ int v, p, sp, r;
+
+ if (uname(&uts) < 0)
+ return 1;
+ if (!strstr(uts.release, el))
+ return 1;
+ if (sscanf(uts.release, "%d.%d.%d-%d", &v, &p, &sp, &r) < 4)
+ return 1;
+ if (version != v || patch != p || sub != sp || release > r)
+ return 1;
+ return 0;
+}
+
static struct timemaster_config *config_parse(char *path)
{
struct timemaster_config *config = xcalloc(1, sizeof(*config));
@@ -621,7 +638,8 @@ static struct timemaster_config *config_parse(char *path)
fclose(f);
if (config->use_vclocks < 0)
- config->use_vclocks = !check_kernel_version(5, 18);
+ config->use_vclocks = !check_kernel_version(5, 18) ||
+ !check_rh_kernel_version(".el9.", 5, 14, 0, 106);
if (section_name)
free(section_name);

14
SOURCES/ptp4l.conf Normal file
View File

@ -0,0 +1,14 @@
# For more information about this file, see the ptp4l(8) man page.
# Examples are available in /usr/share/doc/linuxptp/configs.
[global]
domainNumber 0
slaveOnly 1
time_stamping hardware
tx_timestamp_timeout 1
logging_level 6
summary_interval 0
[eth0]
network_transport UDPv4
hybrid_e2e 0

View File

@ -14,18 +14,11 @@ ntp_program chronyd
[chrony.conf]
include /etc/chrony.conf
[ntp.conf]
includefile /etc/ntp.conf
[ptp4l.conf]
[chronyd]
path /usr/sbin/chronyd
[ntpd]
path /usr/sbin/ntpd
options -u ntp:ntp -g
[phc2sys]
path /usr/sbin/phc2sys

View File

@ -4,10 +4,9 @@
Name: linuxptp
Version: 4.2
Release: 1%{?dist}
Release: 3%{?dist}.1
Summary: PTP implementation for Linux
Group: System Environment/Base
License: GPLv2+
URL: http://linuxptp.sourceforge.net/
@ -16,6 +15,7 @@ Source1: phc2sys.service
Source2: ptp4l.service
Source3: timemaster.service
Source4: timemaster.conf
Source5: ptp4l.conf
# external test suite
Source10: https://github.com/mlichvar/linuxptp-testsuite/archive/%{testsuite_ver}/linuxptp-testsuite-%{testsuite_ver}.tar.gz
# simulator for test suite
@ -33,9 +33,22 @@ Patch4: linuxptp-ucastrate.patch
Patch5: linuxptp-nmeadelay.patch
# fix loading and reloading of leapfile
Patch6: linuxptp-lstab.patch
# fix ts2phc to correctly handle leap seconds
Patch7: linuxptp-nmealeap.patch
# fix ts2phc to reset NMEA parser after RMC message
Patch8: linuxptp-nmeareset.patch
# add options to configure multicast IP addresses
Patch9: linuxptp-addropts.patch
# add holdover support to ts2phc
Patch10: linuxptp-holdover.patch
# add option to ts2phc to specify minimum expected NMEA delay
Patch11: linuxptp-nmeadelay2.patch
# check for EL-specific kernels with vclock support
Patch12: linuxptp-vclock.patch
# don't require -O option without -a and -w in phc2sys
Patch13: linuxptp-nowait.patch
BuildRequires: kernel-headers > 4.18.0-87
BuildRequires: systemd
BuildRequires: gcc gcc-c++ make systemd
%{?systemd_requires}
@ -53,7 +66,7 @@ mv linuxptp-testsuite-%{testsuite_ver}* testsuite
mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim
%build
make %{?_smp_mflags} \
%{make_build} \
EXTRA_CFLAGS="$RPM_OPT_FLAGS" \
EXTRA_LDFLAGS="$RPM_LD_FLAGS"
@ -61,11 +74,10 @@ make %{?_smp_mflags} \
%makeinstall
mkdir -p $RPM_BUILD_ROOT{%{_sysconfdir}/sysconfig,%{_unitdir},%{_mandir}/man5}
install -m 644 -p configs/default.cfg $RPM_BUILD_ROOT%{_sysconfdir}/ptp4l.conf
install -m 644 -p %{SOURCE1} %{SOURCE2} %{SOURCE3} $RPM_BUILD_ROOT%{_unitdir}
install -m 644 -p %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}
install -m 644 -p %{SOURCE4} %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir}
echo 'OPTIONS="-f /etc/ptp4l.conf -i eth0"' > \
echo 'OPTIONS="-f /etc/ptp4l.conf"' > \
$RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/ptp4l
echo 'OPTIONS="-a -r"' > $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/phc2sys
@ -79,7 +91,7 @@ find configs -type f ! -name '*.cfg' -delete
cd testsuite
# set random seed to get deterministic results
export CLKNETSIM_RANDOM_SEED=26743
make %{?_smp_mflags} -C clknetsim
%{make_build} -C clknetsim
PATH=..:$PATH ./run
%post
@ -113,58 +125,112 @@ PATH=..:$PATH ./run
%{_mandir}/man8/*.8*
%changelog
* Thu Feb 22 2024 Miroslav Lichvar <mlichvar@redhat.com> 4.2-1
- update to 4.2 (RHEL-21326 RHEL-21328 RHEL-21329)
- fix ts2phc to handle large NMEA delay (RHEL-23278)
* Thu Jan 02 2025 Miroslav Lichvar <mlichvar@redhat.com> 4.2-3.el9_5.1
- add option to ts2phc to specify minimum expected NMEA delay (RHEL-70168)
- don't require -O option without -a and -w in phc2sys (RHEL-70678)
* Thu Jul 25 2024 Miroslav Lichvar <mlichvar@redhat.com> 4.2-3
- rework NMEA delay patch to fix PPS edge rejection (RHEL-39387)
- fix ts2phc to correctly handle leap seconds (RHEL-40216)
- fix ts2phc to reset NMEA parser after RMC message (RHEL-40947)
- add options to configure multicast IP addresses (RHEL-31178)
- add holdover support to ts2phc (RHEL-50216)
* Thu Feb 22 2024 Miroslav Lichvar <mlichvar@redhat.com> 4.2-2
- fix loading and reloading of leapfile
* Wed May 03 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-6
- clear pending errors on sockets (#2192560)
* Tue Jan 30 2024 Miroslav Lichvar <mlichvar@redhat.com> 4.2-1
- update to 4.2 (RHEL-2026 RHEL-2342 RHEL-12182 RHEL-15929)
- fix ts2phc to handle large NMEA delay (RHEL-23208)
* Wed Apr 12 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-5
- handle EINTR when waiting for transmit timestamp (#2123224)
* Wed May 03 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-9
- clear pending errors on sockets (#2192559)
* Mon Mar 20 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-4
- don't re-arm fault clearing timer on unrelated netlink events (#2174900)
* Mon Mar 20 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-8
- don't switch from system clock to PHC with SW timestamping (#2179041)
* Wed Jun 29 2022 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-3
- handle PHC read failing with EBUSY in phc2sys (#2079129)
* Thu Mar 09 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-7
- don't re-arm fault clearing timer on unrelated netlink events (#2172650)
* Mon Nov 01 2021 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-2
- make sanity clock check more reliable (#2007281)
* Thu Jan 05 2023 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-6
- add support for VLAN over bond (#2120521)
- handle EINTR when waiting for transmit timestamp (#2128786)
- check for unexpected changes in frequency offset (#2150815)
* Mon Jul 26 2021 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-1
- update to 3.1.1 (#1895005 CVE-2021-3571)
- add read-only UDS port (#1929797)
- add option to set clockClass threshold (#1980386)
- don't repeat some log messages in multi-port configuration (#1980377)
- increase default TX timestamp timeout to 10 ms (#1977136)
* Thu Jul 28 2022 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-5
- disable PHC switch with vclocks (#2066452)
* Thu Jun 24 2021 Miroslav Lichvar <mlichvar@redhat.com> 2.0-5.el8_4.1
- validate length of forwarded messages (CVE-2021-3570)
* Thu Jun 30 2022 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-4
- handle PHC read failing with EBUSY in phc2sys (#2102568)
* Mon Apr 27 2020 Miroslav Lichvar <mlichvar@redhat.com> 2.0-5
- fix sample timestamps when synchronizing PHC to system clock (#1787376)
- fix handling of zero-length messages (#1827275)
* Thu Jun 09 2022 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-3
- add support for virtual clocks (#2067310)
- make sanity clock check more reliable (#2079893)
* Thu May 16 2019 Miroslav Lichvar <mlichvar@redhat.com> 2.0-4
- rebuild with enabled gating (#1680888)
* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 3.1.1-2
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags
Related: rhbz#1991688
* Wed May 15 2019 Miroslav Lichvar <mlichvar@redhat.com> 2.0-3
- add support for active-backup team interface (#1685467)
- add support for more accurate synchronization to phc2sys (#1677217)
- add hwts_filter option to ptp4l (#1708554)
- limit unicast message rate per address and grant duration (#1707395)
- fix comparing of unicast addresses (#1707395)
- fix building with new kernel headers (#1707395)
- update testsuite (#1707395)
- don't leak memory when allocation fails (#1707395)
* Mon Aug 02 2021 Miroslav Lichvar <mlichvar@redhat.com> 3.1.1-1
- update to 3.1.1 (#1979954 CVE-2021-3570 CVE-2021-3571)
- add read-only UDS port
- add option to set clockClass threshold
- don't repeat some log messages in multi-port configuration
- increase default TX timestamp timeout to 10 ms
- limit unicast message rate per address and grant duration
* Tue Jun 22 2021 Mohan Boddu <mboddu@redhat.com> - 3.1-5
- Rebuilt for RHEL 9 BETA for openssl 3.0
Related: rhbz#1971065
* Fri Apr 16 2021 Mohan Boddu <mboddu@redhat.com> - 3.1-4
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
* Thu Feb 25 2021 Miroslav Lichvar <mlichvar@redhat.com> 3.1-3
- fix handling of zero-length messages
- minimize default configuration
- remove obsolete build requirement
* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 3.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild
* Tue Sep 29 2020 Miroslav Lichvar <mlichvar@redhat.com> 3.1-1
- update to 3.1
* Mon Jul 27 2020 Miroslav Lichvar <mlichvar@redhat.com> 3.0-1
- update to 3.0
* Mon Feb 03 2020 Miroslav Lichvar <mlichvar@redhat.com> 2.0-7.20191225gite05809
- update to 20191225gite05809
- fix testing with new glibc
* Wed Jan 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 2.0-6.20190912git48e605
- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild
* Wed Sep 25 2019 Miroslav Lichvar <mlichvar@redhat.com> 2.0-5.20190912git48e605
- update to 20190912git48e605
* Thu Jul 25 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.0-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild
* Fri Feb 01 2019 Fedora Release Engineering <releng@fedoraproject.org> - 2.0-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
* Tue Nov 13 2018 Miroslav Lichvar <mlichvar@redhat.com> 2.0-2
- start ptp4l, timemaster and phc2sys after network-online target (#1632282)
- start ptp4l, timemaster and phc2sys after network-online target
- fix building with new kernel headers
* Mon Aug 13 2018 Miroslav Lichvar <mlichvar@redhat.com> 2.0-1
- update to 2.0 (#1614300)
- update to 2.0
* Thu Aug 09 2018 Miroslav Lichvar <mlichvar@redhat.com> 2.0-0.1.20180805gita27407
- update to 20180805gita27407
* Mon Jul 16 2018 Miroslav Lichvar <mlichvar@redhat.com> 1.9.2-3
- add gcc and gcc-c++ to build requirements
* Fri Jul 13 2018 Fedora Release Engineering <releng@fedoraproject.org> - 1.9.2-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
* Mon Apr 09 2018 Miroslav Lichvar <mlichvar@redhat.com> 1.9.2-1
- update to 1.9.2