PTP status: 2-min average offset, details split; uptime as d/h/m/s
- Firmware: status.ptp.offset_avg_ns = mean |offset| over the last 2 min (one bucket per second, cleared on a clock step). - Main panel: Time Offset (2 min avg) shows only the Bolero bracket of the average. Details: the current offset (coloured by the same brackets), hops, time/frequency traceable, version and own clock class. - Uptime shown as "1d 2h 3m 4s". - Preset buttons renamed: "Riedel PTP defaults", "AES67 Media PTP defaults". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#define MAX_DRIFT_PPB 500000.0
|
||||
#define WINDOW 64 // samples for interval/delay statistics
|
||||
#define SUMMARY_US (60 * 1000000LL)
|
||||
#define AVG_S 120 // status.ptp.offset_avg_ns: mean |offset| over 2 min
|
||||
#define FLAG_PTP_TIMESCALE 0x0008 // flagField octet 1 bit 3
|
||||
#define FLAG_UNICAST 0x0400 // flagField octet 0 bit 2
|
||||
#define UTC_OFFSET 37 // TAI - UTC (s), announced as information only
|
||||
@@ -102,8 +103,48 @@ static struct {
|
||||
uint32_t delay_req, delay_resp;
|
||||
uint8_t own_class;
|
||||
int64_t sum_max_ns, sum_start_us;
|
||||
// Mean |offset| over the last AVG_S seconds (status.ptp.offset_avg_ns): one bucket per second.
|
||||
struct {
|
||||
int64_t sec, sum;
|
||||
uint32_t n;
|
||||
} avg[AVG_S];
|
||||
} s;
|
||||
|
||||
static void avg_add(int64_t offset)
|
||||
{
|
||||
int64_t sec = esp_timer_get_time() / 1000000;
|
||||
typeof(s.avg[0]) *b = &s.avg[sec % AVG_S];
|
||||
if (b->sec != sec) {
|
||||
b->sec = sec;
|
||||
b->sum = 0;
|
||||
b->n = 0;
|
||||
}
|
||||
b->sum += llabs(offset);
|
||||
b->n++;
|
||||
}
|
||||
|
||||
static void avg_clear(void)
|
||||
{
|
||||
memset(s.avg, 0, sizeof(s.avg));
|
||||
}
|
||||
|
||||
// False while no offset was measured in the window.
|
||||
static bool avg_get(double *mean)
|
||||
{
|
||||
int64_t now = esp_timer_get_time() / 1000000, sum = 0;
|
||||
uint32_t n = 0;
|
||||
for (int i = 0; i < AVG_S; i++) {
|
||||
if (s.avg[i].n && now - s.avg[i].sec < AVG_S) {
|
||||
sum += s.avg[i].sum;
|
||||
n += s.avg[i].n;
|
||||
}
|
||||
}
|
||||
if (n) {
|
||||
*mean = (double)sum / n;
|
||||
}
|
||||
return n > 0;
|
||||
}
|
||||
|
||||
static void win_add(window_t *w, int64_t v)
|
||||
{
|
||||
w->v[w->next] = v;
|
||||
@@ -309,6 +350,7 @@ static void servo(int64_t offset)
|
||||
s.stepped = true;
|
||||
s.offset_ns = 0; // the pre-step offset is history: not for status or the summary
|
||||
s.sum_max_ns = 0;
|
||||
avg_clear();
|
||||
s.sum_start_us = 0;
|
||||
s.prev_t2 = 0; // rate across the step is meaningless
|
||||
s.bad = 0;
|
||||
@@ -323,6 +365,7 @@ static void servo(int64_t offset)
|
||||
s.drift_ppb = fmax(-MAX_DRIFT_PPB, fmin(MAX_DRIFT_PPB, s.drift_ppb + ki * offset));
|
||||
s.freq_ppb = -ppb;
|
||||
ptp_hw_adj_freq(s.freq_ppb);
|
||||
avg_add(offset);
|
||||
|
||||
if (llabs(offset) < LOCK_NS) {
|
||||
s.bad = 0;
|
||||
@@ -838,6 +881,9 @@ void ptp_clock_status(cJSON *st)
|
||||
cJSON_AddBoolToObject(p, "gm_freq_traceable", s.gm.flags & 0x20);
|
||||
if (s.stepped) {
|
||||
cJSON_AddNumberToObject(p, "offset_ns", s.offset_ns);
|
||||
if (avg_get(&mean)) {
|
||||
cJSON_AddNumberToObject(p, "offset_avg_ns", round(mean));
|
||||
}
|
||||
cJSON_AddNumberToObject(p, "freq_ppb", round(s.freq_ppb));
|
||||
}
|
||||
if (s.delays.n) {
|
||||
|
||||
Reference in New Issue
Block a user