diff --git a/clock.c b/clock.c index beca9d6..642f688 100644 --- a/clock.c +++ b/clock.c @@ -1690,6 +1690,7 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin) tsproc_reset(c->tsproc, 0); break; case SERVO_LOCKED: + case SERVO_LOCKED_STABLE: clockadj_set_freq(c->clkid, -adj); if (c->clkid == CLOCK_REALTIME) { sysclk_set_sync(); diff --git a/config.c b/config.c index 2321310..0da7ce4 100644 --- a/config.c +++ b/config.c @@ -263,6 +263,8 @@ struct config_item config_tab[] = { PORT_ITEM_ENU("network_transport", TRANS_UDP_IPV4, nw_trans_enu), GLOB_ITEM_INT("ntpshm_segment", 0, INT_MIN, INT_MAX), 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_INT("path_trace_enabled", 0, 0, 1), GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX), GLOB_ITEM_DBL("pi_integral_exponent", 0.4, -DBL_MAX, DBL_MAX), @@ -279,6 +281,8 @@ struct config_item config_tab[] = { PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"), GLOB_ITEM_STR("revisionData", ";;"), GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX), + GLOB_ITEM_INT("servo_num_offset_values", 10, 0, INT_MAX), + GLOB_ITEM_INT("servo_offset_threshold", 0, 0, INT_MAX), GLOB_ITEM_INT("slaveOnly", 0, 0, 1), GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX), GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX), diff --git a/configs/automotive-slave.cfg b/configs/automotive-slave.cfg index 94d45b6..8cc7221 100644 --- a/configs/automotive-slave.cfg +++ b/configs/automotive-slave.cfg @@ -31,3 +31,7 @@ asCapable true ignore_source_id 1 # Required to quickly correct Time Jumps in master step_threshold 1 +operLogSyncInterval 0 +operLogPdelayReqInterval 2 +servo_offset_threshold 30 +servo_num_offset_values 10 diff --git a/configs/default.cfg b/configs/default.cfg index 3ee3a9b..2433553 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -23,8 +23,10 @@ maxStepsRemoved 255 # logAnnounceInterval 1 logSyncInterval 0 +operLogSyncInterval 0 logMinDelayReqInterval 0 logMinPdelayReqInterval 0 +operLogPdelayReqInterval 0 announceReceiptTimeout 3 syncReceiptTimeout 0 delayAsymmetry 0 @@ -73,6 +75,8 @@ max_frequency 900000000 clock_servo pi sanity_freq_limit 200000000 ntpshm_segment 0 +servo_num_offset_values 10 +servo_offset_threshold 0 # # Transport options # diff --git a/phc2sys.c b/phc2sys.c index a476e63..cc48f18 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -619,6 +619,7 @@ static void update_clock(struct node *node, struct clock *clock, clockcheck_step(clock->sanity_check, -offset); /* Fall through. */ case SERVO_LOCKED: + case SERVO_LOCKED_STABLE: clockadj_set_freq(clock->clkid, -ppb); if (clock->clkid == CLOCK_REALTIME) sysclk_set_sync(); diff --git a/port.c b/port.c index 982daf2..0aaf846 100644 --- a/port.c +++ b/port.c @@ -1110,10 +1110,12 @@ static void port_slave_priority_warning(struct port *p) static void port_synchronize(struct port *p, tmv_t ingress_ts, struct timestamp origin_ts, - Integer64 correction1, Integer64 correction2) + Integer64 correction1, Integer64 correction2, + Integer8 sync_interval) { - enum servo_state state; + enum servo_state state, last_state; tmv_t t1, t1c, t2, c1, c2; + struct servo *s; port_set_sync_rx_tmo(p); @@ -1123,10 +1125,20 @@ static void port_synchronize(struct port *p, c2 = correction_to_tmv(correction2); t1c = tmv_add(t1, tmv_add(c1, c2)); + s = clock_servo(p->clock); + last_state = clock_servo_state(p->clock); state = clock_synchronize(p->clock, t2, t1c); switch (state) { case SERVO_UNLOCKED: port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); + if (servo_offset_threshold(s) != 0 && + sync_interval != p->initialLogSyncInterval) { + p->logPdelayReqInterval = p->logMinPdelayReqInterval; + p->logSyncInterval = p->initialLogSyncInterval; + port_tx_interval_request(p, SIGNAL_NO_CHANGE, + SIGNAL_SET_INITIAL, + SIGNAL_NO_CHANGE); + } break; case SERVO_JUMP: port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); @@ -1139,6 +1151,23 @@ static void port_synchronize(struct port *p, case SERVO_LOCKED: port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0); break; + case SERVO_LOCKED_STABLE: + if (last_state == SERVO_LOCKED) { + p->logPdelayReqInterval = p->operLogPdelayReqInterval; + p->logSyncInterval = p->operLogSyncInterval; + port_tx_interval_request(p, SIGNAL_NO_CHANGE, + p->logSyncInterval, + SIGNAL_NO_CHANGE); + port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0); + } else if (sync_interval != p->operLogSyncInterval) { + /* + * The most likely reason for this to happen is the + * master daemon re-initialized due to some fault. + */ + servo_reset(s); + port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); + } + break; } } @@ -1192,7 +1221,8 @@ static void port_syfufsm(struct port *p, enum syfu_event event, syn = p->last_syncfup; port_synchronize(p, syn->hwts.ts, m->ts.pdu, syn->header.correction, - m->header.correction); + m->header.correction, + m->header.logMessageInterval); msg_put(p->last_syncfup); p->syfu = SF_EMPTY; break; @@ -1211,7 +1241,8 @@ static void port_syfufsm(struct port *p, enum syfu_event event, fup = p->last_syncfup; port_synchronize(p, m->hwts.ts, fup->ts.pdu, m->header.correction, - fup->header.correction); + fup->header.correction, + m->header.logMessageInterval); msg_put(p->last_syncfup); p->syfu = SF_EMPTY; break; @@ -1613,8 +1644,10 @@ int port_initialize(struct port *p) p->localPriority = config_get_int(cfg, p->name, "G.8275.portDS.localPriority"); p->initialLogSyncInterval = config_get_int(cfg, p->name, "logSyncInterval"); p->logSyncInterval = p->initialLogSyncInterval; + p->operLogSyncInterval = config_get_int(cfg, p->name, "operLogSyncInterval"); p->logMinPdelayReqInterval = config_get_int(cfg, p->name, "logMinPdelayReqInterval"); p->logPdelayReqInterval = p->logMinPdelayReqInterval; + p->operLogPdelayReqInterval = config_get_int(cfg, p->name, "operLogPdelayReqInterval"); p->neighborPropDelayThresh = config_get_int(cfg, p->name, "neighborPropDelayThresh"); p->min_neighbor_prop_delay = config_get_int(cfg, p->name, "min_neighbor_prop_delay"); @@ -2224,7 +2257,8 @@ void process_sync(struct port *p, struct ptp_message *m) if (one_step(m)) { port_synchronize(p, m->hwts.ts, m->ts.pdu, - m->header.correction, 0); + m->header.correction, 0, + m->header.logMessageInterval); flush_last_sync(p); return; } diff --git a/port_private.h b/port_private.h index d28ab07..bb1d86e 100644 --- a/port_private.h +++ b/port_private.h @@ -115,9 +115,11 @@ struct port { UInteger8 transportSpecific; UInteger8 localPriority; Integer8 initialLogSyncInterval; + Integer8 operLogSyncInterval; Integer8 logSyncInterval; Enumeration8 delayMechanism; Integer8 logMinPdelayReqInterval; + Integer8 operLogPdelayReqInterval; Integer8 logPdelayReqInterval; UInteger32 neighborPropDelayThresh; int follow_up_info; diff --git a/ptp4l.8 b/ptp4l.8 index 46e6715..df7da82 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -161,6 +161,13 @@ The mean time interval between Sync messages. A shorter interval may improve accuracy of the local clock. It's specified as a power of two in seconds. The default is 0 (1 second). .TP +.B operLogSyncInterval +The mean time interval between Sync messages. This value is only used by the +slave device when interval_update_timer is enabled. Slave will send this +interval to the master to switch to. This is done via a signaling message after +interval_update_timer expires. It's specified as a power of two in seconds. The +default value is 0 (1 second). +.TP .B logMinDelayReqInterval The minimum permitted mean time interval between Delay_Req messages. A shorter interval makes ptp4l react faster to the changes in the path delay. It's @@ -172,6 +179,13 @@ The minimum permitted mean time interval between Pdelay_Req messages. It's specified as a power of two in seconds. The default is 0 (1 second). .TP +.B operLogPdelayReqInterval +The mean time interval between Pdelay Request messages. This value is only used +by the slave device when interval_update_timer is enabled. Slave will switch to +the interval specified by this config option after the interval_update_timer +expires. It's specified as a power of two in seconds. The default value is 0 (1 +second). +.TP .B announceReceiptTimeout The number of missed Announce messages before the last Announce messages expires. @@ -715,6 +729,22 @@ This will disable source port identity checking for Sync and Follow_Up messages. This is useful when the announce messages are disabled in the master and the slave does not have any way to know it's identity. The default is 0 (disabled). +.TP +.B servo_num_offset_values +The number of offset values calculated in previously received Sync messages to +consider when adjusting the Sync and Pdelay request intervals. More information +provided in the description of 'offset_threshold'. The default value is 10. +.TP +.B servo_offset_threshold +This value is used by the slave for adjusting the intervals for Sync and Pdelay +request messages. The slave will check the last 'num_offset_values' offsets and +if all those offsets are less than the offset_threshold, it will adjust both +the intervals. The Sync interval is adjusted via the signaling mechanism and +the pdelay request interval is just adjusted locally. The new values to use for +sync message intervals and pdelay request intervals can be indicated by +operLogSyncInterval and operLogPdelayReqInterval respectively. This mechanism +is currently only supported when BMCA == 'noop'. The default +value of offset_threshold is 0 (disabled). .SH UNICAST DISCOVERY OPTIONS diff --git a/servo.c b/servo.c index 8be4b92..46042aa 100644 --- a/servo.c +++ b/servo.c @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include +#include #include "config.h" #include "linreg.h" @@ -25,6 +26,8 @@ #include "pi.h" #include "servo_private.h" +#include "print.h" + #define NSEC_PER_SEC 1000000000 struct servo *servo_create(struct config *cfg, enum servo_type type, @@ -79,6 +82,9 @@ struct servo *servo_create(struct config *cfg, enum servo_type type, } servo->first_update = 1; + servo->offset_threshold = config_get_int(cfg, NULL, "servo_offset_threshold"); + servo->num_offset_values = config_get_int(cfg, NULL, "servo_num_offset_values"); + servo->curr_offset_values = servo->num_offset_values; return servo; } @@ -88,6 +94,18 @@ void servo_destroy(struct servo *servo) servo->destroy(servo); } +static int check_offset_threshold(struct servo *s, int64_t offset) +{ + long long int abs_offset = llabs(offset); + + if (s->offset_threshold) { + if (abs_offset < s->offset_threshold && s->curr_offset_values) + s->curr_offset_values--; + return s->curr_offset_values ? 0 : 1; + } + return 0; +} + double servo_sample(struct servo *servo, int64_t offset, uint64_t local_ts, @@ -98,8 +116,28 @@ double servo_sample(struct servo *servo, r = servo->sample(servo, offset, local_ts, weight, state); - if (*state != SERVO_UNLOCKED) + switch (*state) { + case SERVO_UNLOCKED: + servo->curr_offset_values = servo->num_offset_values; + break; + case SERVO_JUMP: + servo->curr_offset_values = servo->num_offset_values; servo->first_update = 0; + break; + case SERVO_LOCKED: + if (check_offset_threshold(servo, offset)) { + *state = SERVO_LOCKED_STABLE; + } + servo->first_update = 0; + break; + case SERVO_LOCKED_STABLE: + /* + * This case will never occur since the only place + * SERVO_LOCKED_STABLE is set is in this switch/case block + * (case SERVO_LOCKED). + */ + break; + } return r; } @@ -127,3 +165,8 @@ void servo_leap(struct servo *servo, int leap) if (servo->leap) servo->leap(servo, leap); } + +int servo_offset_threshold(struct servo *servo) +{ + return servo->offset_threshold; +} diff --git a/servo.h b/servo.h index f90befd..6c30d33 100644 --- a/servo.h +++ b/servo.h @@ -57,6 +57,12 @@ enum servo_state { * The servo is tracking the master clock. */ SERVO_LOCKED, + + /** + * The Servo has stabilized. The last 'servo_num_offset_values' values + * of the estimated threshold are less than servo_offset_threshold. + */ + SERVO_LOCKED_STABLE, }; /** @@ -123,4 +129,11 @@ double servo_rate_ratio(struct servo *servo); */ void servo_leap(struct servo *servo, int leap); +/** + * Get the offset threshold for triggering the interval change request. + * @param servo Pointer to a servo obtained via @ref servo_create(). + * @return The offset threshold set by the user. + */ +int servo_offset_threshold(struct servo *servo); + #endif diff --git a/servo_private.h b/servo_private.h index 1c5cfdf..4d74ca2 100644 --- a/servo_private.h +++ b/servo_private.h @@ -29,6 +29,9 @@ struct servo { double step_threshold; double first_step_threshold; int first_update; + int64_t offset_threshold; + int num_offset_values; + int curr_offset_values; void (*destroy)(struct servo *servo);