#include "util.h"
#include "sources.h"
#include "logging.h"
+#include "regress.h"
#include "sched.h"
#include "mkdirpp.h"
int used;
int last;
struct FilterSample *samples;
- int *sort_array;
+ int *selected;
+ double *x_data;
+ double *y_data;
+ double *w_data;
};
struct RCL_Instance_Record {
static void filter_reset(struct MedianFilter *filter);
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_select_samples(struct MedianFilter *filter);
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_add_dispersion(struct MedianFilter *filter, double dispersion);
filter->used = 0;
filter->last = -1;
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
filter_fini(struct MedianFilter *filter)
{
Free(filter->samples);
- Free(filter->sort_array);
+ Free(filter->selected);
+ Free(filter->x_data);
+ Free(filter->y_data);
+ Free(filter->w_data);
}
static void
return 0;
}
-static int
-filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
+int
+filter_select_samples(struct MedianFilter *filter)
{
- if (filter->used == 0)
+ int i, j, k, o, from, to, *selected;
+ double min_dispersion;
+
+ if (filter->used < 1)
return 0;
- if (filter->used == 1) {
- *sample_time = filter->samples[filter->index].sample_time;
- *offset = filter->samples[filter->index].offset;
- *dispersion = filter->samples[filter->index].dispersion;
+ 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 {
- struct FilterSample *s;
- int i, j, from, to;
- double x, x1, y, d, e, min_dispersion;
-
- /* find minimum dispersion */
- 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;
- }
+ from = 0;
+ to = j;
+ }
- /* 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)
- filter->sort_array[j++] = i;
- }
+ /* mark unused samples and sort the rest from oldest to newest */
- assert(j > 0);
-
- /* and sort their indexes by offset */
- tmp_sorted_array = filter->samples;
- qsort(filter->sort_array, j, sizeof (int), sample_compare);
+ o = filter->used - filter->index - 1;
- /* select half of the samples closest to the median */
- if (j > 2) {
- from = (j + 2) / 4;
- to = j - from;
- } else {
- from = 0;
- to = j;
- }
+ 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;
- /* 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;
+ 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;
}
+ }
- x /= to - from;
- y /= to - from;
+ for (i = j = 0, k = -1; i < filter->used; i++) {
+ if (selected[i] != -1)
+ selected[j++] = (selected[i] + filter->used - o) % filter->used;
+ }
- 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;
- }
+ return j;
+}
- d = sqrt(d / (to - from));
- e /= to - from;
+static int
+filter_get_sample(struct MedianFilter *filter, struct timeval *sample_time, double *offset, double *dispersion)
+{
+ struct FilterSample *s, *ls;
+ int i, n;
+ double x, y, d, e;
- UTI_AddDoubleToTimeval(&filter->samples[0].sample_time, x, sample_time);
- *offset = y;
- *dispersion = d + e;
+ n = filter_select_samples(filter);
+
+ if (n < 1)
+ return 0;
+
+ ls = &filter->samples[filter->selected[n - 1]];
+
+ /* prepare data */
+ for (i = 0; i < n; i++) {
+ s = &filter->samples[filter->selected[i]];
+
+ UTI_DiffTimevalsToDouble(&filter->x_data[i], &s->sample_time, &ls->sample_time);
+ filter->y_data[i] = s->offset;
+ filter->w_data[i] = s->dispersion;
+ }
+
+ /* 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;
}