Add summary statistics.
Add new options to ptp4l and phc2sys to print summary statistics of the clock instead of the individual samples. [ RC - Fix () function prototype with (void). - Use unsigned for sample counter. - Fix over-zealous line breaks. ] Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com> Signed-off-by: Richard Cochran <richardcochran@gmail.com>master
parent
c7098364f2
commit
3136e3e46c
80
clock.c
80
clock.c
|
@ -31,6 +31,7 @@
|
||||||
#include "phc.h"
|
#include "phc.h"
|
||||||
#include "port.h"
|
#include "port.h"
|
||||||
#include "servo.h"
|
#include "servo.h"
|
||||||
|
#include "stats.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "tlv.h"
|
#include "tlv.h"
|
||||||
#include "uds.h"
|
#include "uds.h"
|
||||||
|
@ -50,6 +51,13 @@ struct freq_estimator {
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct clock_stats {
|
||||||
|
struct stats *offset;
|
||||||
|
struct stats *freq;
|
||||||
|
struct stats *delay;
|
||||||
|
int max_count;
|
||||||
|
};
|
||||||
|
|
||||||
struct clock {
|
struct clock {
|
||||||
clockid_t clkid;
|
clockid_t clkid;
|
||||||
struct servo *servo;
|
struct servo *servo;
|
||||||
|
@ -80,6 +88,8 @@ struct clock {
|
||||||
tmv_t t1;
|
tmv_t t1;
|
||||||
tmv_t t2;
|
tmv_t t2;
|
||||||
struct clock_description desc;
|
struct clock_description desc;
|
||||||
|
struct clock_stats stats;
|
||||||
|
int stats_interval;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct clock the_clock;
|
struct clock the_clock;
|
||||||
|
@ -104,6 +114,9 @@ void clock_destroy(struct clock *c)
|
||||||
}
|
}
|
||||||
servo_destroy(c->servo);
|
servo_destroy(c->servo);
|
||||||
mave_destroy(c->avg_delay);
|
mave_destroy(c->avg_delay);
|
||||||
|
stats_destroy(c->stats.offset);
|
||||||
|
stats_destroy(c->stats.freq);
|
||||||
|
stats_destroy(c->stats.delay);
|
||||||
memset(c, 0, sizeof(*c));
|
memset(c, 0, sizeof(*c));
|
||||||
msg_cleanup();
|
msg_cleanup();
|
||||||
}
|
}
|
||||||
|
@ -232,6 +245,40 @@ static int clock_master_lost(struct clock *c)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clock_stats_update(struct clock_stats *s,
|
||||||
|
int64_t offset, double freq)
|
||||||
|
{
|
||||||
|
struct stats_result offset_stats, freq_stats, delay_stats;
|
||||||
|
|
||||||
|
stats_add_value(s->offset, offset);
|
||||||
|
stats_add_value(s->freq, freq);
|
||||||
|
|
||||||
|
if (stats_get_num_values(s->offset) < s->max_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stats_get_result(s->offset, &offset_stats);
|
||||||
|
stats_get_result(s->freq, &freq_stats);
|
||||||
|
|
||||||
|
/* Path delay stats are updated separately, they may be empty. */
|
||||||
|
if (!stats_get_result(s->delay, &delay_stats)) {
|
||||||
|
pr_info("rms %4.0f max %4.0f "
|
||||||
|
"freq %+6.0f +/- %3.0f "
|
||||||
|
"delay %5.0f +/- %3.0f",
|
||||||
|
offset_stats.rms, offset_stats.max_abs,
|
||||||
|
freq_stats.mean, freq_stats.stddev,
|
||||||
|
delay_stats.mean, delay_stats.stddev);
|
||||||
|
} else {
|
||||||
|
pr_info("rms %4.0f max %4.0f "
|
||||||
|
"freq %+6.0f +/- %3.0f",
|
||||||
|
offset_stats.rms, offset_stats.max_abs,
|
||||||
|
freq_stats.mean, freq_stats.stddev);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats_reset(s->offset);
|
||||||
|
stats_reset(s->freq);
|
||||||
|
stats_reset(s->delay);
|
||||||
|
}
|
||||||
|
|
||||||
static enum servo_state clock_no_adjust(struct clock *c)
|
static enum servo_state clock_no_adjust(struct clock *c)
|
||||||
{
|
{
|
||||||
double fui;
|
double fui;
|
||||||
|
@ -278,9 +325,13 @@ static enum servo_state clock_no_adjust(struct clock *c)
|
||||||
tmv_dbl(tmv_sub(c->t2, f->ingress1));
|
tmv_dbl(tmv_sub(c->t2, f->ingress1));
|
||||||
freq = (1.0 - ratio) * 1e9;
|
freq = (1.0 - ratio) * 1e9;
|
||||||
|
|
||||||
|
if (c->stats.max_count > 1) {
|
||||||
|
clock_stats_update(&c->stats, c->master_offset, freq);
|
||||||
|
} else {
|
||||||
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
|
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
|
||||||
"path delay %9" PRId64,
|
"path delay %9" PRId64,
|
||||||
c->master_offset, state, freq, c->path_delay);
|
c->master_offset, state, freq, c->path_delay);
|
||||||
|
}
|
||||||
|
|
||||||
fui = 1.0 + (c->status.cumulativeScaledRateOffset + 0.0) / POW2_41;
|
fui = 1.0 + (c->status.cumulativeScaledRateOffset + 0.0) / POW2_41;
|
||||||
|
|
||||||
|
@ -433,7 +484,7 @@ UInteger8 clock_class(struct clock *c)
|
||||||
|
|
||||||
struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||||
enum timestamp_type timestamping, struct default_ds *dds,
|
enum timestamp_type timestamping, struct default_ds *dds,
|
||||||
enum servo_type servo)
|
enum servo_type servo, int stats_interval)
|
||||||
{
|
{
|
||||||
int i, fadj = 0, max_adj = 0.0, sw_ts = timestamping == TS_SOFTWARE ? 1 : 0;
|
int i, fadj = 0, max_adj = 0.0, sw_ts = timestamping == TS_SOFTWARE ? 1 : 0;
|
||||||
struct clock *c = &the_clock;
|
struct clock *c = &the_clock;
|
||||||
|
@ -486,6 +537,14 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||||
pr_err("Failed to create moving average");
|
pr_err("Failed to create moving average");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
c->stats_interval = stats_interval;
|
||||||
|
c->stats.offset = stats_create();
|
||||||
|
c->stats.freq = stats_create();
|
||||||
|
c->stats.delay = stats_create();
|
||||||
|
if (!c->stats.offset || !c->stats.freq || !c->stats.delay) {
|
||||||
|
pr_err("failed to create stats");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
c->dds = dds->dds;
|
c->dds = dds->dds;
|
||||||
|
|
||||||
|
@ -834,12 +893,18 @@ void clock_path_delay(struct clock *c, struct timespec req, struct timestamp rx,
|
||||||
c->cur.meanPathDelay = tmv_to_TimeInterval(c->path_delay);
|
c->cur.meanPathDelay = tmv_to_TimeInterval(c->path_delay);
|
||||||
|
|
||||||
pr_debug("path delay %10lld %10lld", c->path_delay, pd);
|
pr_debug("path delay %10lld %10lld", c->path_delay, pd);
|
||||||
|
|
||||||
|
if (c->stats.delay)
|
||||||
|
stats_add_value(c->stats.delay, pd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr)
|
void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr)
|
||||||
{
|
{
|
||||||
c->path_delay = ppd;
|
c->path_delay = ppd;
|
||||||
c->nrr = nrr;
|
c->nrr = nrr;
|
||||||
|
|
||||||
|
if (c->stats.delay)
|
||||||
|
stats_add_value(c->stats.delay, ppd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clock_remove_fda(struct clock *c, struct port *p, struct fdarray fda)
|
void clock_remove_fda(struct clock *c, struct port *p, struct fdarray fda)
|
||||||
|
@ -903,9 +968,13 @@ enum servo_state clock_synchronize(struct clock *c,
|
||||||
|
|
||||||
adj = servo_sample(c->servo, c->master_offset, ingress, &state);
|
adj = servo_sample(c->servo, c->master_offset, ingress, &state);
|
||||||
|
|
||||||
|
if (c->stats.max_count > 1) {
|
||||||
|
clock_stats_update(&c->stats, c->master_offset, adj);
|
||||||
|
} else {
|
||||||
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
|
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
|
||||||
"path delay %9" PRId64,
|
"path delay %9" PRId64,
|
||||||
c->master_offset, state, adj, c->path_delay);
|
c->master_offset, state, adj, c->path_delay);
|
||||||
|
}
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case SERVO_UNLOCKED:
|
case SERVO_UNLOCKED:
|
||||||
|
@ -925,12 +994,17 @@ enum servo_state clock_synchronize(struct clock *c,
|
||||||
|
|
||||||
void clock_sync_interval(struct clock *c, int n)
|
void clock_sync_interval(struct clock *c, int n)
|
||||||
{
|
{
|
||||||
int shift = c->freq_est_interval - n;
|
int shift;
|
||||||
|
|
||||||
|
shift = c->freq_est_interval - n;
|
||||||
if (shift < 0)
|
if (shift < 0)
|
||||||
shift = 0;
|
shift = 0;
|
||||||
|
|
||||||
c->fest.max_count = (1 << shift);
|
c->fest.max_count = (1 << shift);
|
||||||
|
|
||||||
|
shift = c->stats_interval - n;
|
||||||
|
if (shift < 0)
|
||||||
|
shift = 0;
|
||||||
|
c->stats.max_count = (1 << shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timePropertiesDS *clock_time_properties(struct clock *c)
|
struct timePropertiesDS *clock_time_properties(struct clock *c)
|
||||||
|
|
3
clock.h
3
clock.h
|
@ -67,11 +67,12 @@ UInteger8 clock_class(struct clock *c);
|
||||||
* @param timestamping The timestamping mode for this clock.
|
* @param timestamping The timestamping mode for this clock.
|
||||||
* @param dds A pointer to a default data set for the clock.
|
* @param dds A pointer to a default data set for the clock.
|
||||||
* @param servo The servo that this clock will use.
|
* @param servo The servo that this clock will use.
|
||||||
|
* @param stats_interval Interval in which are printed clock statistics.
|
||||||
* @return A pointer to the single global clock instance.
|
* @return A pointer to the single global clock instance.
|
||||||
*/
|
*/
|
||||||
struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||||
enum timestamp_type timestamping, struct default_ds *dds,
|
enum timestamp_type timestamping, struct default_ds *dds,
|
||||||
enum servo_type servo);
|
enum servo_type servo, int stats_interval);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains a clock's default data set.
|
* Obtains a clock's default data set.
|
||||||
|
|
5
config.c
5
config.c
|
@ -378,6 +378,11 @@ static enum parser_result parse_global_setting(const char *option,
|
||||||
for (i = 0; i < OUI_LEN; i++)
|
for (i = 0; i < OUI_LEN; i++)
|
||||||
cfg->dds.clock_desc.manufacturerIdentity[i] = oui[i];
|
cfg->dds.clock_desc.manufacturerIdentity[i] = oui[i];
|
||||||
|
|
||||||
|
} else if (!strcmp(option, "summary_interval")) {
|
||||||
|
if (1 != sscanf(value, "%d", &val))
|
||||||
|
return BAD_VALUE;
|
||||||
|
cfg->summary_interval = val;
|
||||||
|
|
||||||
} else
|
} else
|
||||||
return NOT_PARSED;
|
return NOT_PARSED;
|
||||||
|
|
||||||
|
|
1
config.h
1
config.h
|
@ -76,6 +76,7 @@ struct config {
|
||||||
int print_level;
|
int print_level;
|
||||||
int use_syslog;
|
int use_syslog;
|
||||||
int verbose;
|
int verbose;
|
||||||
|
int summary_interval;
|
||||||
};
|
};
|
||||||
|
|
||||||
int config_read(char *name, struct config *cfg);
|
int config_read(char *name, struct config *cfg);
|
||||||
|
|
|
@ -32,6 +32,7 @@ follow_up_info 0
|
||||||
tx_timestamp_retries 100
|
tx_timestamp_retries 100
|
||||||
use_syslog 1
|
use_syslog 1
|
||||||
verbose 0
|
verbose 0
|
||||||
|
summary_interval 0
|
||||||
#
|
#
|
||||||
# Servo Options
|
# Servo Options
|
||||||
#
|
#
|
||||||
|
|
1
gPTP.cfg
1
gPTP.cfg
|
@ -31,6 +31,7 @@ follow_up_info 1
|
||||||
tx_timestamp_retries 100
|
tx_timestamp_retries 100
|
||||||
use_syslog 1
|
use_syslog 1
|
||||||
verbose 0
|
verbose 0
|
||||||
|
summary_interval 0
|
||||||
#
|
#
|
||||||
# Servo options
|
# Servo options
|
||||||
#
|
#
|
||||||
|
|
8
makefile
8
makefile
|
@ -30,8 +30,8 @@ CFLAGS = -Wall $(VER) $(INC) $(DEBUG) $(FEAT_CFLAGS) $(EXTRA_CFLAGS)
|
||||||
LDLIBS = -lm -lrt $(EXTRA_LDFLAGS)
|
LDLIBS = -lm -lrt $(EXTRA_LDFLAGS)
|
||||||
PRG = ptp4l pmc phc2sys hwstamp_ctl
|
PRG = ptp4l pmc phc2sys hwstamp_ctl
|
||||||
OBJ = bmc.o clock.o config.o fsm.o ptp4l.o mave.o msg.o phc.o pi.o port.o \
|
OBJ = bmc.o clock.o config.o fsm.o ptp4l.o mave.o msg.o phc.o pi.o port.o \
|
||||||
print.o raw.o servo.o sk.o tlv.o tmtab.o transport.o udp.o udp6.o uds.o util.o \
|
print.o raw.o servo.o sk.o stats.o tlv.o tmtab.o transport.o udp.o udp6.o \
|
||||||
version.o
|
uds.o util.o version.o
|
||||||
|
|
||||||
OBJECTS = $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o
|
OBJECTS = $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o
|
||||||
SRC = $(OBJECTS:.o=.c)
|
SRC = $(OBJECTS:.o=.c)
|
||||||
|
@ -52,8 +52,8 @@ ptp4l: $(OBJ)
|
||||||
pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
|
pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
|
||||||
udp6.o uds.o util.o version.o
|
udp6.o uds.o util.o version.o
|
||||||
|
|
||||||
phc2sys: msg.o phc2sys.o pmc_common.o print.o pi.o servo.o raw.o sk.o sysoff.o \
|
phc2sys: msg.o phc2sys.o pmc_common.o print.o pi.o servo.o raw.o sk.o stats.o \
|
||||||
tlv.o transport.o udp.o udp6.o uds.o util.o version.o
|
sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.o version.o
|
||||||
|
|
||||||
hwstamp_ctl: hwstamp_ctl.o version.o
|
hwstamp_ctl: hwstamp_ctl.o version.o
|
||||||
|
|
||||||
|
|
11
phc2sys.8
11
phc2sys.8
|
@ -27,6 +27,8 @@ phc2sys \- synchronize two clocks
|
||||||
] [
|
] [
|
||||||
.BI \-O " offset"
|
.BI \-O " offset"
|
||||||
] [
|
] [
|
||||||
|
.BI \-u " summary-updates"
|
||||||
|
] [
|
||||||
.B \-w
|
.B \-w
|
||||||
] [
|
] [
|
||||||
.BI \-l " print-level"
|
.BI \-l " print-level"
|
||||||
|
@ -112,6 +114,15 @@ Without
|
||||||
.B \-w
|
.B \-w
|
||||||
the default is 0.
|
the default is 0.
|
||||||
.TP
|
.TP
|
||||||
|
.BI \-u " summary-updates"
|
||||||
|
Specify the number of clock updates included in summary statistics. The
|
||||||
|
statistics include offset root mean square (RMS), maximum absolute offset,
|
||||||
|
frequency offset mean and standard deviation, and mean of the delay in clock
|
||||||
|
readings and standard deviation. The units are nanoseconds and parts per
|
||||||
|
billion (ppb). If zero, the individual samples are printed instead of the
|
||||||
|
statistics. The messages are printed at the LOG_INFO level.
|
||||||
|
The default is 0 (disabled).
|
||||||
|
.TP
|
||||||
.B \-w
|
.B \-w
|
||||||
Wait until ptp4l is in a synchronized state.
|
Wait until ptp4l is in a synchronized state.
|
||||||
.TP
|
.TP
|
||||||
|
|
63
phc2sys.c
63
phc2sys.c
|
@ -42,6 +42,7 @@
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "servo.h"
|
#include "servo.h"
|
||||||
#include "sk.h"
|
#include "sk.h"
|
||||||
|
#include "stats.h"
|
||||||
#include "sysoff.h"
|
#include "sysoff.h"
|
||||||
#include "tlv.h"
|
#include "tlv.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
@ -155,8 +156,47 @@ struct clock {
|
||||||
clockid_t clkid;
|
clockid_t clkid;
|
||||||
struct servo *servo;
|
struct servo *servo;
|
||||||
const char *source_label;
|
const char *source_label;
|
||||||
|
struct stats *offset_stats;
|
||||||
|
struct stats *freq_stats;
|
||||||
|
struct stats *delay_stats;
|
||||||
|
int stats_max_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void update_clock_stats(struct clock *clock,
|
||||||
|
int64_t offset, double freq, int64_t delay)
|
||||||
|
{
|
||||||
|
struct stats_result offset_stats, freq_stats, delay_stats;
|
||||||
|
|
||||||
|
stats_add_value(clock->offset_stats, offset);
|
||||||
|
stats_add_value(clock->freq_stats, freq);
|
||||||
|
if (delay >= 0)
|
||||||
|
stats_add_value(clock->delay_stats, delay);
|
||||||
|
|
||||||
|
if (stats_get_num_values(clock->offset_stats) < clock->stats_max_count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stats_get_result(clock->offset_stats, &offset_stats);
|
||||||
|
stats_get_result(clock->freq_stats, &freq_stats);
|
||||||
|
|
||||||
|
if (!stats_get_result(clock->delay_stats, &delay_stats)) {
|
||||||
|
pr_info("rms %4.0f max %4.0f "
|
||||||
|
"freq %+6.0f +/- %3.0f "
|
||||||
|
"delay %5.0f +/- %3.0f",
|
||||||
|
offset_stats.rms, offset_stats.max_abs,
|
||||||
|
freq_stats.mean, freq_stats.stddev,
|
||||||
|
delay_stats.mean, delay_stats.stddev);
|
||||||
|
} else {
|
||||||
|
pr_info("rms %4.0f max %4.0f "
|
||||||
|
"freq %+6.0f +/- %3.0f",
|
||||||
|
offset_stats.rms, offset_stats.max_abs,
|
||||||
|
freq_stats.mean, freq_stats.stddev);
|
||||||
|
}
|
||||||
|
|
||||||
|
stats_reset(clock->offset_stats);
|
||||||
|
stats_reset(clock->freq_stats);
|
||||||
|
stats_reset(clock->delay_stats);
|
||||||
|
}
|
||||||
|
|
||||||
static void update_clock(struct clock *clock,
|
static void update_clock(struct clock *clock,
|
||||||
int64_t offset, uint64_t ts, int64_t delay)
|
int64_t offset, uint64_t ts, int64_t delay)
|
||||||
{
|
{
|
||||||
|
@ -176,6 +216,9 @@ static void update_clock(struct clock *clock,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clock->offset_stats) {
|
||||||
|
update_clock_stats(clock, offset, ppb, delay);
|
||||||
|
} else {
|
||||||
if (delay >= 0) {
|
if (delay >= 0) {
|
||||||
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f "
|
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f "
|
||||||
"delay %6" PRId64,
|
"delay %6" PRId64,
|
||||||
|
@ -184,6 +227,7 @@ static void update_clock(struct clock *clock,
|
||||||
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f",
|
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f",
|
||||||
clock->source_label, offset, state, ppb);
|
clock->source_label, offset, state, ppb);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_pps(int fd, int64_t *offset, uint64_t *ts)
|
static int read_pps(int fd, int64_t *offset, uint64_t *ts)
|
||||||
|
@ -423,6 +467,7 @@ static void usage(char *progname)
|
||||||
" -R [rate] slave clock update rate in HZ (1)\n"
|
" -R [rate] slave clock update rate in HZ (1)\n"
|
||||||
" -N [num] number of master clock readings per update (5)\n"
|
" -N [num] number of master clock readings per update (5)\n"
|
||||||
" -O [offset] slave-master time offset (0)\n"
|
" -O [offset] slave-master time offset (0)\n"
|
||||||
|
" -u [num] number of clock updates in summary stats (0)\n"
|
||||||
" -w wait for ptp4l\n"
|
" -w wait for ptp4l\n"
|
||||||
" -h prints this message and exits\n"
|
" -h prints this message and exits\n"
|
||||||
" -v prints the software version and exits\n"
|
" -v prints the software version and exits\n"
|
||||||
|
@ -446,7 +491,8 @@ int main(int argc, char *argv[])
|
||||||
/* Process the command line arguments. */
|
/* Process the command line arguments. */
|
||||||
progname = strrchr(argv[0], '/');
|
progname = strrchr(argv[0], '/');
|
||||||
progname = progname ? 1+progname : argv[0];
|
progname = progname ? 1+progname : argv[0];
|
||||||
while (EOF != (c = getopt(argc, argv, "c:d:hs:P:I:S:R:N:O:i:wl:mqv"))) {
|
while (EOF != (c = getopt(argc, argv,
|
||||||
|
"c:d:hs:P:I:S:R:N:O:i:u:wl:mqv"))) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'c':
|
case 'c':
|
||||||
dst_clock.clkid = clock_open(optarg);
|
dst_clock.clkid = clock_open(optarg);
|
||||||
|
@ -484,6 +530,9 @@ int main(int argc, char *argv[])
|
||||||
case 'i':
|
case 'i':
|
||||||
ethdev = optarg;
|
ethdev = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'u':
|
||||||
|
dst_clock.stats_max_count = atoi(optarg);
|
||||||
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
wait_sync = 1;
|
wait_sync = 1;
|
||||||
break;
|
break;
|
||||||
|
@ -529,6 +578,18 @@ int main(int argc, char *argv[])
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dst_clock.stats_max_count > 0) {
|
||||||
|
dst_clock.offset_stats = stats_create();
|
||||||
|
dst_clock.freq_stats = stats_create();
|
||||||
|
dst_clock.delay_stats = stats_create();
|
||||||
|
if (!dst_clock.offset_stats ||
|
||||||
|
!dst_clock.freq_stats ||
|
||||||
|
!dst_clock.delay_stats) {
|
||||||
|
fprintf(stderr, "failed to create stats");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
print_set_progname(progname);
|
print_set_progname(progname);
|
||||||
print_set_verbose(verbose);
|
print_set_verbose(verbose);
|
||||||
print_set_syslog(use_syslog);
|
print_set_syslog(use_syslog);
|
||||||
|
|
10
ptp4l.8
10
ptp4l.8
|
@ -307,6 +307,16 @@ The default is 0 (disabled).
|
||||||
Print messages to the system log if enabled.
|
Print messages to the system log if enabled.
|
||||||
The default is 1 (enabled).
|
The default is 1 (enabled).
|
||||||
.TP
|
.TP
|
||||||
|
.B summary_interval
|
||||||
|
The time interval in which are printed summary statistics of the clock. It is
|
||||||
|
specified as a power of two in seconds. The statistics include offset root mean
|
||||||
|
square (RMS), maximum absolute offset, frequency offset mean and standard
|
||||||
|
deviation, and path delay mean and standard deviation. The units are
|
||||||
|
nanoseconds and parts per billion (ppb). If there is only one clock update in
|
||||||
|
the interval, the sample will be printed instead of the statistics. The
|
||||||
|
messages are printed at the LOG_INFO level.
|
||||||
|
The default is 0 (1 second).
|
||||||
|
.TP
|
||||||
.B time_stamping
|
.B time_stamping
|
||||||
The time stamping method. The allowed values are hardware, software and legacy.
|
The time stamping method. The allowed values are hardware, software and legacy.
|
||||||
The default is hardware.
|
The default is hardware.
|
||||||
|
|
4
ptp4l.c
4
ptp4l.c
|
@ -99,6 +99,7 @@ static struct config cfg_settings = {
|
||||||
.print_level = LOG_INFO,
|
.print_level = LOG_INFO,
|
||||||
.use_syslog = 1,
|
.use_syslog = 1,
|
||||||
.verbose = 0,
|
.verbose = 0,
|
||||||
|
.summary_interval = 0,
|
||||||
|
|
||||||
.cfg_ignore = 0,
|
.cfg_ignore = 0,
|
||||||
};
|
};
|
||||||
|
@ -351,7 +352,8 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
clock = clock_create(phc_index, iface, cfg_settings.nports,
|
clock = clock_create(phc_index, iface, cfg_settings.nports,
|
||||||
*timestamping, &cfg_settings.dds,
|
*timestamping, &cfg_settings.dds,
|
||||||
cfg_settings.clock_servo);
|
cfg_settings.clock_servo,
|
||||||
|
cfg_settings.summary_interval);
|
||||||
if (!clock) {
|
if (!clock) {
|
||||||
fprintf(stderr, "failed to create a clock\n");
|
fprintf(stderr, "failed to create a clock\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* @file stats.c
|
||||||
|
* @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "stats.h"
|
||||||
|
|
||||||
|
struct stats {
|
||||||
|
unsigned int num;
|
||||||
|
double min;
|
||||||
|
double max;
|
||||||
|
double mean;
|
||||||
|
double sum_sqr;
|
||||||
|
double sum_diff_sqr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stats *stats_create(void)
|
||||||
|
{
|
||||||
|
struct stats *stats;
|
||||||
|
|
||||||
|
stats = calloc(1, sizeof *stats);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stats_destroy(struct stats *stats)
|
||||||
|
{
|
||||||
|
free(stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stats_add_value(struct stats *stats, double value)
|
||||||
|
{
|
||||||
|
double old_mean = stats->mean;
|
||||||
|
|
||||||
|
if (!stats->num || stats->max < value)
|
||||||
|
stats->max = value;
|
||||||
|
if (!stats->num || stats->min > value)
|
||||||
|
stats->min = value;
|
||||||
|
|
||||||
|
stats->num++;
|
||||||
|
stats->mean = old_mean + (value - old_mean) / stats->num;
|
||||||
|
stats->sum_sqr += value * value;
|
||||||
|
stats->sum_diff_sqr += (value - old_mean) * (value - stats->mean);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int stats_get_num_values(struct stats *stats)
|
||||||
|
{
|
||||||
|
return stats->num;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stats_get_result(struct stats *stats, struct stats_result *result)
|
||||||
|
{
|
||||||
|
if (!stats->num)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
result->min = stats->min;
|
||||||
|
result->max = stats->max;
|
||||||
|
result->max_abs = stats->max > -stats->min ? stats->max : -stats->min;
|
||||||
|
result->mean = stats->mean;
|
||||||
|
result->rms = sqrt(stats->sum_sqr / stats->num);
|
||||||
|
result->stddev = sqrt(stats->sum_diff_sqr / stats->num);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stats_reset(struct stats *stats)
|
||||||
|
{
|
||||||
|
memset(stats, 0, sizeof *stats);
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* @file stats.h
|
||||||
|
* @brief Implements various statistics.
|
||||||
|
* @note Copyright (C) 2013 Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#ifndef HAVE_STATS_H
|
||||||
|
#define HAVE_STATS_H
|
||||||
|
|
||||||
|
/** Opaque type */
|
||||||
|
struct stats;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of statistics.
|
||||||
|
* @return A pointer to a new stats on success, NULL otherwise.
|
||||||
|
*/
|
||||||
|
struct stats *stats_create(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy an instance of stats.
|
||||||
|
* @param servo Pointer to stats obtained via @ref stats_create().
|
||||||
|
*/
|
||||||
|
void stats_destroy(struct stats *stats);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new value to the stats.
|
||||||
|
* @param stats Pointer to stats obtained via @ref stats_create().
|
||||||
|
* @param value The measured value.
|
||||||
|
*/
|
||||||
|
void stats_add_value(struct stats *stats, double value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of values collected in the stats so far.
|
||||||
|
* @param stats Pointer to stats obtained via @ref stats_create().
|
||||||
|
* @return The number of values.
|
||||||
|
*/
|
||||||
|
unsigned int stats_get_num_values(struct stats *stats);
|
||||||
|
|
||||||
|
struct stats_result {
|
||||||
|
double min;
|
||||||
|
double max;
|
||||||
|
double max_abs;
|
||||||
|
double mean;
|
||||||
|
double rms;
|
||||||
|
double stddev;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the results of the calculated statistics.
|
||||||
|
* @param stats Pointer to stats obtained via @ref stats_create().
|
||||||
|
* @param stats_result Pointer to stats_result to store the results.
|
||||||
|
* @return Zero on success, non-zero if no values were added.
|
||||||
|
*/
|
||||||
|
int stats_get_result(struct stats *stats, struct stats_result *result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all statistics.
|
||||||
|
* @param stats Pointer to stats obtained via @ref stats_create().
|
||||||
|
*/
|
||||||
|
void stats_reset(struct stats *stats);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue