Make linear fit in refclock dispersion calculation
This should improve reaction to sudden temperature changes with very precise refclocks and/or long polling intervals.
This commit is contained in:
parent
fd375ca55b
commit
7fb0598b50
1 changed files with 128 additions and 65 deletions
193
refclock.c
193
refclock.c
|
@ -33,6 +33,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "sources.h"
|
#include "sources.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "regress.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
#include "mkdirpp.h"
|
#include "mkdirpp.h"
|
||||||
|
|
||||||
|
@ -53,7 +54,10 @@ struct MedianFilter {
|
||||||
int used;
|
int used;
|
||||||
int last;
|
int last;
|
||||||
struct FilterSample *samples;
|
struct FilterSample *samples;
|
||||||
int *sort_array;
|
int *selected;
|
||||||
|
double *x_data;
|
||||||
|
double *y_data;
|
||||||
|
double *w_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RCL_Instance_Record {
|
struct RCL_Instance_Record {
|
||||||
|
@ -99,6 +103,7 @@ static void filter_fini(struct MedianFilter *filter);
|
||||||
static void filter_reset(struct MedianFilter *filter);
|
static void filter_reset(struct MedianFilter *filter);
|
||||||
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion);
|
static void filter_add_sample(struct MedianFilter *filter, struct timeval *sample_time, double offset, double dispersion);
|
||||||
static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
|
static int filter_get_last_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
|
||||||
|
static int filter_select_samples(struct MedianFilter *filter);
|
||||||
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
|
static int filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion);
|
||||||
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
|
static void filter_slew_samples(struct MedianFilter *filter, struct timeval *when, double dfreq, double doffset);
|
||||||
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
|
static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
|
||||||
|
@ -611,14 +616,20 @@ filter_init(struct MedianFilter *filter, int length)
|
||||||
filter->used = 0;
|
filter->used = 0;
|
||||||
filter->last = -1;
|
filter->last = -1;
|
||||||
filter->samples = MallocArray(struct FilterSample, filter->length);
|
filter->samples = MallocArray(struct FilterSample, filter->length);
|
||||||
filter->sort_array = MallocArray(int, filter->length);
|
filter->selected = MallocArray(int, filter->length);
|
||||||
|
filter->x_data = MallocArray(double, filter->length);
|
||||||
|
filter->y_data = MallocArray(double, filter->length);
|
||||||
|
filter->w_data = MallocArray(double, filter->length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
filter_fini(struct MedianFilter *filter)
|
filter_fini(struct MedianFilter *filter)
|
||||||
{
|
{
|
||||||
Free(filter->samples);
|
Free(filter->samples);
|
||||||
Free(filter->sort_array);
|
Free(filter->selected);
|
||||||
|
Free(filter->x_data);
|
||||||
|
Free(filter->y_data);
|
||||||
|
Free(filter->w_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -671,77 +682,129 @@ sample_compare(const void *a, const void *b)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
filter_select_samples(struct MedianFilter *filter)
|
||||||
|
{
|
||||||
|
int i, j, k, o, from, to, *selected;
|
||||||
|
double min_dispersion;
|
||||||
|
|
||||||
|
if (filter->used < 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
selected = filter->selected;
|
||||||
|
|
||||||
|
for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
|
||||||
|
if (min_dispersion > filter->samples[i].dispersion)
|
||||||
|
min_dispersion = filter->samples[i].dispersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* select samples with dispersion better than 1.5 * minimum */
|
||||||
|
for (i = j = 0; i < filter->used; i++) {
|
||||||
|
if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
|
||||||
|
selected[j++] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(j > 0);
|
||||||
|
|
||||||
|
/* and sort their indices by offset */
|
||||||
|
tmp_sorted_array = filter->samples;
|
||||||
|
qsort(selected, j, sizeof (int), sample_compare);
|
||||||
|
|
||||||
|
/* select half of the samples closest to the median */
|
||||||
|
if (j > 2) {
|
||||||
|
from = (j + 2) / 4;
|
||||||
|
to = j - from;
|
||||||
|
} else {
|
||||||
|
from = 0;
|
||||||
|
to = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mark unused samples and sort the rest from oldest to newest */
|
||||||
|
|
||||||
|
o = filter->used - filter->index - 1;
|
||||||
|
|
||||||
|
for (i = 0; i < from; i++)
|
||||||
|
selected[i] = -1;
|
||||||
|
for (; i < to; i++)
|
||||||
|
selected[i] = (selected[i] + o) % filter->used;
|
||||||
|
for (; i < filter->used; i++)
|
||||||
|
selected[i] = -1;
|
||||||
|
|
||||||
|
for (i = from; i < to; i++) {
|
||||||
|
j = selected[i];
|
||||||
|
selected[i] = -1;
|
||||||
|
while (j != -1 && selected[j] != j) {
|
||||||
|
k = selected[j];
|
||||||
|
selected[j] = j;
|
||||||
|
j = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = j = 0, k = -1; i < filter->used; i++) {
|
||||||
|
if (selected[i] != -1)
|
||||||
|
selected[j++] = (selected[i] + filter->used - o) % filter->used;
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
|
filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
|
||||||
{
|
{
|
||||||
if (filter->used == 0)
|
struct FilterSample *s, *ls;
|
||||||
|
int i, n;
|
||||||
|
double x, y, d, e;
|
||||||
|
|
||||||
|
n = filter_select_samples(filter);
|
||||||
|
|
||||||
|
if (n < 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (filter->used == 1) {
|
ls = &filter->samples[filter->selected[n - 1]];
|
||||||
*sample_time = filter->samples[filter->index].sample_time;
|
|
||||||
*offset = filter->samples[filter->index].offset;
|
|
||||||
*dispersion = filter->samples[filter->index].dispersion;
|
|
||||||
} else {
|
|
||||||
struct FilterSample *s;
|
|
||||||
int i, j, from, to;
|
|
||||||
double x, x1, y, d, e, min_dispersion;
|
|
||||||
|
|
||||||
/* find minimum dispersion */
|
/* prepare data */
|
||||||
for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
if (min_dispersion > filter->samples[i].dispersion)
|
s = &filter->samples[filter->selected[i]];
|
||||||
min_dispersion = filter->samples[i].dispersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* select samples with dispersion better than 1.5 * minimum */
|
UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time);
|
||||||
for (i = j = 0; i < filter->used; i++) {
|
filter->y_data[i] = s->offset;
|
||||||
if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
|
filter->w_data[i] = s->dispersion;
|
||||||
filter->sort_array[j++] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(j > 0);
|
|
||||||
|
|
||||||
/* and sort their indexes by offset */
|
|
||||||
tmp_sorted_array = filter->samples;
|
|
||||||
qsort(filter->sort_array, j, sizeof (int), sample_compare);
|
|
||||||
|
|
||||||
/* select half of the samples closest to the median */
|
|
||||||
if (j > 2) {
|
|
||||||
from = (j + 2) / 4;
|
|
||||||
to = j - from;
|
|
||||||
} else {
|
|
||||||
from = 0;
|
|
||||||
to = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* average offset and sample time */
|
|
||||||
for (i = from, x = y = 0.0; i < to; i++) {
|
|
||||||
s = &filter->samples[filter->sort_array[i]];
|
|
||||||
#if 0
|
|
||||||
LOG(LOGS_INFO, LOGF_Refclock, "refclock averaging sample: offset %.9f dispersion %.9f [%s]",
|
|
||||||
s->offset, s->dispersion, UTI_TimevalToString(&filter->samples[i].sample_time));
|
|
||||||
#endif
|
|
||||||
UTI_DiffTimevalsToDouble(&x1, &s->sample_time, &filter->samples[0].sample_time);
|
|
||||||
x += x1;
|
|
||||||
y += s->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
x /= to - from;
|
|
||||||
y /= to - from;
|
|
||||||
|
|
||||||
for (i = from, d = e = 0.0; i < to; i++) {
|
|
||||||
s = &filter->samples[filter->sort_array[i]];
|
|
||||||
d += (s->offset - y) * (s->offset - y);
|
|
||||||
e += s->dispersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
d = sqrt(d / (to - from));
|
|
||||||
e /= to - from;
|
|
||||||
|
|
||||||
UTI_AddDoubleToTimeval(&filter->samples[0].sample_time, x, sample_time);
|
|
||||||
*offset = y;
|
|
||||||
*dispersion = d + e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* mean offset, sample time and sample dispersion */
|
||||||
|
for (i = 0, x = y = e = 0.0; i < n; i++) {
|
||||||
|
x += filter->x_data[i];
|
||||||
|
y += filter->y_data[i];
|
||||||
|
e += filter->w_data[i];
|
||||||
|
}
|
||||||
|
x /= n;
|
||||||
|
y /= n;
|
||||||
|
e /= n;
|
||||||
|
|
||||||
|
if (n >= 4) {
|
||||||
|
double b0, b1, s2, sb0, sb1;
|
||||||
|
|
||||||
|
/* set y axis to the mean sample time */
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
filter->x_data[i] -= x;
|
||||||
|
|
||||||
|
/* make a linear fit and use the estimated standard deviation of intercept
|
||||||
|
as dispersion */
|
||||||
|
RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
|
||||||
|
&b0, &b1, &s2, &sb0, &sb1);
|
||||||
|
d = sb0;
|
||||||
|
} else if (n >= 2) {
|
||||||
|
for (i = 0, d = 0.0; i < n; i++)
|
||||||
|
d += (filter->y_data[i] - y) * (filter->y_data[i] - y);
|
||||||
|
d = sqrt(d / (n - 1));
|
||||||
|
} else {
|
||||||
|
d = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTI_AddDoubleToTimeval(&ls->sample_time, x, sample_time);
|
||||||
|
*offset = y;
|
||||||
|
*dispersion = d + e;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue