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
92
clock.c
92
clock.c
|
@ -31,6 +31,7 @@
|
|||
#include "phc.h"
|
||||
#include "port.h"
|
||||
#include "servo.h"
|
||||
#include "stats.h"
|
||||
#include "print.h"
|
||||
#include "tlv.h"
|
||||
#include "uds.h"
|
||||
|
@ -50,6 +51,13 @@ struct freq_estimator {
|
|||
unsigned int count;
|
||||
};
|
||||
|
||||
struct clock_stats {
|
||||
struct stats *offset;
|
||||
struct stats *freq;
|
||||
struct stats *delay;
|
||||
int max_count;
|
||||
};
|
||||
|
||||
struct clock {
|
||||
clockid_t clkid;
|
||||
struct servo *servo;
|
||||
|
@ -80,6 +88,8 @@ struct clock {
|
|||
tmv_t t1;
|
||||
tmv_t t2;
|
||||
struct clock_description desc;
|
||||
struct clock_stats stats;
|
||||
int stats_interval;
|
||||
};
|
||||
|
||||
struct clock the_clock;
|
||||
|
@ -104,6 +114,9 @@ void clock_destroy(struct clock *c)
|
|||
}
|
||||
servo_destroy(c->servo);
|
||||
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));
|
||||
msg_cleanup();
|
||||
}
|
||||
|
@ -232,6 +245,40 @@ static int clock_master_lost(struct clock *c)
|
|||
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)
|
||||
{
|
||||
double fui;
|
||||
|
@ -278,9 +325,13 @@ static enum servo_state clock_no_adjust(struct clock *c)
|
|||
tmv_dbl(tmv_sub(c->t2, f->ingress1));
|
||||
freq = (1.0 - ratio) * 1e9;
|
||||
|
||||
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
|
||||
"path delay %9" PRId64,
|
||||
c->master_offset, state, freq, c->path_delay);
|
||||
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 "
|
||||
"path delay %9" PRId64,
|
||||
c->master_offset, state, freq, c->path_delay);
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
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");
|
||||
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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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)
|
||||
{
|
||||
c->path_delay = ppd;
|
||||
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)
|
||||
|
@ -903,9 +968,13 @@ enum servo_state clock_synchronize(struct clock *c,
|
|||
|
||||
adj = servo_sample(c->servo, c->master_offset, ingress, &state);
|
||||
|
||||
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
|
||||
"path delay %9" PRId64,
|
||||
c->master_offset, state, adj, c->path_delay);
|
||||
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 "
|
||||
"path delay %9" PRId64,
|
||||
c->master_offset, state, adj, c->path_delay);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case SERVO_UNLOCKED:
|
||||
|
@ -925,12 +994,17 @@ enum servo_state clock_synchronize(struct clock *c,
|
|||
|
||||
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)
|
||||
shift = 0;
|
||||
|
||||
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)
|
||||
|
|
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 dds A pointer to a default data set for the clock.
|
||||
* @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.
|
||||
*/
|
||||
struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||
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.
|
||||
|
|
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++)
|
||||
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
|
||||
return NOT_PARSED;
|
||||
|
||||
|
|
1
config.h
1
config.h
|
@ -76,6 +76,7 @@ struct config {
|
|||
int print_level;
|
||||
int use_syslog;
|
||||
int verbose;
|
||||
int summary_interval;
|
||||
};
|
||||
|
||||
int config_read(char *name, struct config *cfg);
|
||||
|
|
|
@ -32,6 +32,7 @@ follow_up_info 0
|
|||
tx_timestamp_retries 100
|
||||
use_syslog 1
|
||||
verbose 0
|
||||
summary_interval 0
|
||||
#
|
||||
# Servo Options
|
||||
#
|
||||
|
|
1
gPTP.cfg
1
gPTP.cfg
|
@ -31,6 +31,7 @@ follow_up_info 1
|
|||
tx_timestamp_retries 100
|
||||
use_syslog 1
|
||||
verbose 0
|
||||
summary_interval 0
|
||||
#
|
||||
# Servo options
|
||||
#
|
||||
|
|
8
makefile
8
makefile
|
@ -30,8 +30,8 @@ CFLAGS = -Wall $(VER) $(INC) $(DEBUG) $(FEAT_CFLAGS) $(EXTRA_CFLAGS)
|
|||
LDLIBS = -lm -lrt $(EXTRA_LDFLAGS)
|
||||
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 \
|
||||
print.o raw.o servo.o sk.o tlv.o tmtab.o transport.o udp.o udp6.o uds.o util.o \
|
||||
version.o
|
||||
print.o raw.o servo.o sk.o stats.o tlv.o tmtab.o transport.o udp.o udp6.o \
|
||||
uds.o util.o version.o
|
||||
|
||||
OBJECTS = $(OBJ) hwstamp_ctl.o phc2sys.o pmc.o pmc_common.o sysoff.o
|
||||
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 \
|
||||
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 \
|
||||
tlv.o transport.o udp.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 stats.o \
|
||||
sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.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 \-u " summary-updates"
|
||||
] [
|
||||
.B \-w
|
||||
] [
|
||||
.BI \-l " print-level"
|
||||
|
@ -112,6 +114,15 @@ Without
|
|||
.B \-w
|
||||
the default is 0.
|
||||
.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
|
||||
Wait until ptp4l is in a synchronized state.
|
||||
.TP
|
||||
|
|
75
phc2sys.c
75
phc2sys.c
|
@ -42,6 +42,7 @@
|
|||
#include "print.h"
|
||||
#include "servo.h"
|
||||
#include "sk.h"
|
||||
#include "stats.h"
|
||||
#include "sysoff.h"
|
||||
#include "tlv.h"
|
||||
#include "version.h"
|
||||
|
@ -155,8 +156,47 @@ struct clock {
|
|||
clockid_t clkid;
|
||||
struct servo *servo;
|
||||
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,
|
||||
int64_t offset, uint64_t ts, int64_t delay)
|
||||
{
|
||||
|
@ -176,13 +216,17 @@ static void update_clock(struct clock *clock,
|
|||
break;
|
||||
}
|
||||
|
||||
if (delay >= 0) {
|
||||
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f "
|
||||
"delay %6" PRId64,
|
||||
clock->source_label, offset, state, ppb, delay);
|
||||
if (clock->offset_stats) {
|
||||
update_clock_stats(clock, offset, ppb, delay);
|
||||
} else {
|
||||
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f",
|
||||
clock->source_label, offset, state, ppb);
|
||||
if (delay >= 0) {
|
||||
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f "
|
||||
"delay %6" PRId64,
|
||||
clock->source_label, offset, state, ppb, delay);
|
||||
} else {
|
||||
pr_info("%s offset %9" PRId64 " s%d freq %+7.0f",
|
||||
clock->source_label, offset, state, ppb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -423,6 +467,7 @@ static void usage(char *progname)
|
|||
" -R [rate] slave clock update rate in HZ (1)\n"
|
||||
" -N [num] number of master clock readings per update (5)\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"
|
||||
" -h prints this message 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. */
|
||||
progname = strrchr(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) {
|
||||
case 'c':
|
||||
dst_clock.clkid = clock_open(optarg);
|
||||
|
@ -484,6 +530,9 @@ int main(int argc, char *argv[])
|
|||
case 'i':
|
||||
ethdev = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
dst_clock.stats_max_count = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
wait_sync = 1;
|
||||
break;
|
||||
|
@ -529,6 +578,18 @@ int main(int argc, char *argv[])
|
|||
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_verbose(verbose);
|
||||
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.
|
||||
The default is 1 (enabled).
|
||||
.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
|
||||
The time stamping method. The allowed values are hardware, software and legacy.
|
||||
The default is hardware.
|
||||
|
|
4
ptp4l.c
4
ptp4l.c
|
@ -99,6 +99,7 @@ static struct config cfg_settings = {
|
|||
.print_level = LOG_INFO,
|
||||
.use_syslog = 1,
|
||||
.verbose = 0,
|
||||
.summary_interval = 0,
|
||||
|
||||
.cfg_ignore = 0,
|
||||
};
|
||||
|
@ -351,7 +352,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
clock = clock_create(phc_index, iface, cfg_settings.nports,
|
||||
*timestamping, &cfg_settings.dds,
|
||||
cfg_settings.clock_servo);
|
||||
cfg_settings.clock_servo,
|
||||
cfg_settings.summary_interval);
|
||||
if (!clock) {
|
||||
fprintf(stderr, "failed to create a clock\n");
|
||||
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