From: Joseph Matan <joseph.matan...@gmail.com> The percentile filter can be very useful when running in a non-ptp-aware environment. For example, if we set a low percentile value (and the filter length is large enough), we can still get a good estimation of the real delay, even if our setup is running under heavy network traffic. (when setting the percentile value to 0.50, the median filter is a private use-case of the percentile filter)
Signed-off-by: Joseph Matan <joseph.matan...@gmail.com> --- clock.c | 3 ++- config.c | 6 ++++-- configs/default.cfg | 1 + filter.c | 8 +++++--- filter.h | 8 +++++--- makefile | 2 +- mmedian.c => mpercentile.c | 42 +++++++++++++++++++++----------------- mmedian.h => mpercentile.h | 10 ++++----- nsm.c | 2 +- port.c | 3 ++- ptp4l.8 | 6 +++++- tsproc.c | 4 ++-- tsproc.h | 3 ++- 13 files changed, 58 insertions(+), 40 deletions(-) rename mmedian.c => mpercentile.c (64%) rename mmedian.h => mpercentile.h (81%) diff --git a/clock.c b/clock.c index ec70f91..9c4b6cc 100644 --- a/clock.c +++ b/clock.c @@ -1171,7 +1171,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, } c->tsproc = tsproc_create(config_get_int(config, NULL, "tsproc_mode"), config_get_int(config, NULL, "delay_filter"), - config_get_int(config, NULL, "delay_filter_length")); + config_get_int(config, NULL, "delay_filter_length"), + config_get_double(config, NULL, "delay_filter_percentile")); if (!c->tsproc) { pr_err("Failed to create time stamp processor"); return NULL; diff --git a/config.c b/config.c index eb8b988..31351f4 100644 --- a/config.c +++ b/config.c @@ -158,8 +158,9 @@ static struct config_enum dataset_comp_enu[] = { }; static struct config_enum delay_filter_enu[] = { - { "moving_average", FILTER_MOVING_AVERAGE }, - { "moving_median", FILTER_MOVING_MEDIAN }, + { "moving_average", FILTER_MOVING_AVERAGE }, + { "moving_median", FILTER_MOVING_MEDIAN }, + { "moving_percentile", FILTER_MOVING_PERCENTILE }, { NULL, 0 }, }; @@ -238,6 +239,7 @@ struct config_item config_tab[] = { PORT_ITEM_INT("delayAsymmetry", 0, INT_MIN, INT_MAX), PORT_ITEM_ENU("delay_filter", FILTER_MOVING_MEDIAN, delay_filter_enu), PORT_ITEM_INT("delay_filter_length", 10, 1, INT_MAX), + PORT_ITEM_DBL("delay_filter_percentile", 0.5, 0.0, 1.0), PORT_ITEM_ENU("delay_mechanism", DM_E2E, delay_mech_enu), GLOB_ITEM_INT("dscp_event", 0, 0, 63), GLOB_ITEM_INT("dscp_general", 0, 0, 63), diff --git a/configs/default.cfg b/configs/default.cfg index d615610..29cba1a 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -102,6 +102,7 @@ time_stamping hardware tsproc_mode filter delay_filter moving_median delay_filter_length 10 +delay_filter_percentile 0.5 egressLatency 0 ingressLatency 0 boundary_clock_jbod 0 diff --git a/filter.c b/filter.c index fceb29a..f779280 100644 --- a/filter.c +++ b/filter.c @@ -19,15 +19,17 @@ #include "filter_private.h" #include "mave.h" -#include "mmedian.h" +#include "mpercentile.h" -struct filter *filter_create(enum filter_type type, int length) +struct filter *filter_create(enum filter_type type, int length, double percentile) { switch (type) { case FILTER_MOVING_AVERAGE: return mave_create(length); case FILTER_MOVING_MEDIAN: - return mmedian_create(length); + return mpercentile_create(length, 0.5); + case FILTER_MOVING_PERCENTILE: + return mpercentile_create(length, percentile); default: return NULL; } diff --git a/filter.h b/filter.h index 5a196bc..78be629 100644 --- a/filter.h +++ b/filter.h @@ -31,15 +31,17 @@ struct filter; enum filter_type { FILTER_MOVING_AVERAGE, FILTER_MOVING_MEDIAN, + FILTER_MOVING_PERCENTILE, }; /** * Create a new instance of a filter. - * @param type The type of the filter to create. - * @param length The filter's length. + * @param type The type of the filter to create. + * @param length The filter's length. + * @param percentile The percentile of the delay filter samples (valid only for FILTER_MOVING_PERCENTILE filter type). * @return A pointer to a new filter on success, NULL otherwise. */ -struct filter *filter_create(enum filter_type type, int length); +struct filter *filter_create(enum filter_type type, int length, double percentile); /** * Destroy an instance of a filter. diff --git a/makefile b/makefile index 33e7ca0..7de5eab 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ VER = -DVER=$(version) CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) LDLIBS = -lm -lrt -pthread $(EXTRA_LDFLAGS) PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc -FILTERS = filter.o mave.o mmedian.o +FILTERS = filter.o mave.o mpercentile.o SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o TRANSP = raw.o transport.o udp.o udp6.o uds.o TS2PHC = ts2phc.o lstab.o nmea.o serial.o sock.o ts2phc_generic_master.o \ diff --git a/mmedian.c b/mpercentile.c similarity index 64% rename from mmedian.c rename to mpercentile.c index 2383467..cb4010b 100644 --- a/mmedian.c +++ b/mpercentile.c @@ -1,5 +1,5 @@ /** - * @file mmedian.c + * @file mpercentile.c * @note Copyright (C) 2013 Miroslav Lichvar <mlich...@redhat.com> * * This program is free software; you can redistribute it and/or modify @@ -19,36 +19,41 @@ #include <stdlib.h> #include <string.h> -#include "mmedian.h" +#include "mpercentile.h" #include "filter_private.h" +#include "print.h" +#include "math.h" -struct mmedian { +struct mpercentile { struct filter filter; int cnt; int len; int index; + double percentile; + int percentile_index; /* Indices sorted by value. */ int *order; /* Values stored in circular buffer. */ tmv_t *samples; }; -static void mmedian_destroy(struct filter *filter) +static void mpercentile_destroy(struct filter *filter) { - struct mmedian *m = container_of(filter, struct mmedian, filter); + struct mpercentile *m = container_of(filter, struct mpercentile, filter); free(m->order); free(m->samples); free(m); } -static tmv_t mmedian_sample(struct filter *filter, tmv_t sample) +static tmv_t mpercentile_sample(struct filter *filter, tmv_t sample) { - struct mmedian *m = container_of(filter, struct mmedian, filter); + struct mpercentile *m = container_of(filter, struct mpercentile, filter); int i; m->samples[m->index] = sample; if (m->cnt < m->len) { m->cnt++; + m->percentile_index = (int)round(m->percentile * (m->cnt - 1)); } else { /* Remove index of the replaced value from order. */ for (i = 0; i < m->cnt; i++) @@ -69,32 +74,30 @@ static tmv_t mmedian_sample(struct filter *filter, tmv_t sample) m->index = (1 + m->index) % m->len; - if (m->cnt % 2) - return m->samples[m->order[m->cnt / 2]]; - else - return tmv_div(tmv_add(m->samples[m->order[m->cnt / 2 - 1]], - m->samples[m->order[m->cnt / 2]]), 2); + /*TODO: deleteMe*/ pr_info("lenght(%d) percentile(%0.2f) percentile_index(%d), filtered_delay(%lld)", + m->cnt, m->percentile, m->percentile_index, tmv_to_nanoseconds(m->samples[m->order[m->percentile_index]])); + return m->samples[m->order[m->percentile_index]]; } -static void mmedian_reset(struct filter *filter) +static void mpercentile_reset(struct filter *filter) { - struct mmedian *m = container_of(filter, struct mmedian, filter); + struct mpercentile *m = container_of(filter, struct mpercentile, filter); m->cnt = 0; m->index = 0; } -struct filter *mmedian_create(int length) +struct filter *mpercentile_create(int length, double percentile) { - struct mmedian *m; + struct mpercentile *m; if (length < 1) return NULL; m = calloc(1, sizeof(*m)); if (!m) return NULL; - m->filter.destroy = mmedian_destroy; - m->filter.sample = mmedian_sample; - m->filter.reset = mmedian_reset; + m->filter.destroy = mpercentile_destroy; + m->filter.sample = mpercentile_sample; + m->filter.reset = mpercentile_reset; m->order = calloc(1, length * sizeof(*m->order)); if (!m->order) { free(m); @@ -107,5 +110,6 @@ struct filter *mmedian_create(int length) return NULL; } m->len = length; + m->percentile = percentile; return &m->filter; } diff --git a/mmedian.h b/mpercentile.h similarity index 81% rename from mmedian.h rename to mpercentile.h index 8fee0dc..193a333 100644 --- a/mmedian.h +++ b/mpercentile.h @@ -1,6 +1,6 @@ /** - * @file mmedian.h - * @brief Implements a moving median. + * @file mpercentile.h + * @brief Implements a moving percentile. * @note Copyright (C) 2013 Miroslav Lichvar <mlich...@redhat.com> * * This program is free software; you can redistribute it and/or modify @@ -17,11 +17,11 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef HAVE_MMEDIAN_H -#define HAVE_MMEDIAN_H +#ifndef HAVE_MPERCENTILE_H +#define HAVE_MPERCENTILE_H #include "filter.h" -struct filter *mmedian_create(int length); +struct filter *mpercentile_create(int length, double percentile); #endif diff --git a/nsm.c b/nsm.c index 5aa925b..3ede6a0 100644 --- a/nsm.c +++ b/nsm.c @@ -295,7 +295,7 @@ static int nsm_open(struct nsm *nsm, struct config *cfg) } nsm->port_identity.portNumber = 1; - nsm->tsproc = tsproc_create(TSPROC_RAW, FILTER_MOVING_AVERAGE, 10); + nsm->tsproc = tsproc_create(TSPROC_RAW, FILTER_MOVING_AVERAGE, 10, 0); if (!nsm->tsproc) { pr_err("failed to create time stamp processor"); goto no_tsproc; diff --git a/port.c b/port.c index c82bdaf..6dae97b 100644 --- a/port.c +++ b/port.c @@ -3200,7 +3200,8 @@ struct port *port_open(const char *phc_device, p->tsproc = tsproc_create(config_get_int(cfg, p->name, "tsproc_mode"), config_get_int(cfg, p->name, "delay_filter"), - config_get_int(cfg, p->name, "delay_filter_length")); + config_get_int(cfg, p->name, "delay_filter_length"), + config_get_double(cfg, p->name, "delay_filter_percentile")); if (!p->tsproc) { pr_err("Failed to create time stamp processor"); goto err_uc_service; diff --git a/ptp4l.8 b/ptp4l.8 index a0779ef..3a3fc13 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -340,13 +340,17 @@ The default is filter. .TP .B delay_filter Select the algorithm used to filter the measured delay and peer delay. Possible -values are moving_average and moving_median. +values are moving_average, moving_median and moving_percentile. The default is moving_median. .TP .B delay_filter_length The length of the delay filter in samples. The default is 10. .TP +.B delay_filter_percentile +The percentile of the delay filter samples (relevant only when choosing the delay_filter as moving_percentile). +The default is 0.5 (50%). +.TP .B egressLatency Specifies the difference in nanoseconds between the actual transmission time at the reference plane and the reported transmit time stamp. This diff --git a/tsproc.c b/tsproc.c index a871049..8ace4a6 100644 --- a/tsproc.c +++ b/tsproc.c @@ -61,7 +61,7 @@ static int weighting(struct tsproc *tsp) } struct tsproc *tsproc_create(enum tsproc_mode mode, - enum filter_type delay_filter, int filter_length) + enum filter_type delay_filter, int filter_length, double percentile) { struct tsproc *tsp; @@ -81,7 +81,7 @@ struct tsproc *tsproc_create(enum tsproc_mode mode, return NULL; } - tsp->delay_filter = filter_create(delay_filter, filter_length); + tsp->delay_filter = filter_create(delay_filter, filter_length, percentile); if (!tsp->delay_filter) { free(tsp); return NULL; diff --git a/tsproc.h b/tsproc.h index fdb35a8..5970705 100644 --- a/tsproc.h +++ b/tsproc.h @@ -40,10 +40,11 @@ enum tsproc_mode { * @param mode Time stamp processing mode. * @param delay_filter Type of the filter that will be applied to delay. * @param filter_length Length of the filter. + * @param percentile The percentile of the delay filter samples (valid only for FILTER_MOVING_PERCENTILE filter type). * @return A pointer to a new tsproc on success, NULL otherwise. */ struct tsproc *tsproc_create(enum tsproc_mode mode, - enum filter_type delay_filter, int filter_length); + enum filter_type delay_filter, int filter_length, double percentile); /** * Destroy a time stamp processor. -- 2.17.1 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel