91 lines
2.9 KiB
C
91 lines
2.9 KiB
C
|
#include "clock.h"
|
||
|
|
||
|
void
|
||
|
sc_clock_init(struct sc_clock *clock) {
|
||
|
clock->count = 0;
|
||
|
clock->head = 0;
|
||
|
clock->left_sum.system = 0;
|
||
|
clock->left_sum.stream = 0;
|
||
|
clock->right_sum.system = 0;
|
||
|
clock->right_sum.stream = 0;
|
||
|
}
|
||
|
|
||
|
// Estimate the affine function f(stream) = slope * stream + offset
|
||
|
static void
|
||
|
sc_clock_estimate(struct sc_clock *clock,
|
||
|
double *out_slope, sc_tick *out_offset) {
|
||
|
assert(clock->count > 1); // two points are necessary
|
||
|
struct sc_clock_point left_avg = {
|
||
|
.system = clock->left_sum.system / (clock->count / 2),
|
||
|
.stream = clock->left_sum.stream / (clock->count / 2),
|
||
|
};
|
||
|
struct sc_clock_point right_avg = {
|
||
|
.system = clock->right_sum.system / ((clock->count + 1) / 2),
|
||
|
.stream = clock->right_sum.stream / ((clock->count + 1) / 2),
|
||
|
};
|
||
|
struct sc_clock_point global_avg = {
|
||
|
.system = (clock->left_sum.system + clock->right_sum.system)
|
||
|
/ clock->count,
|
||
|
.stream = (clock->left_sum.stream + clock->right_sum.stream)
|
||
|
/ clock->count,
|
||
|
};
|
||
|
|
||
|
double slope = (double) (right_avg.system - left_avg.system)
|
||
|
/ (right_avg.stream - left_avg.stream);
|
||
|
sc_tick offset = global_avg.system - (sc_tick) (global_avg.stream * slope);
|
||
|
|
||
|
*out_slope = slope;
|
||
|
*out_offset = offset;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
sc_clock_update(struct sc_clock *clock, sc_tick system, sc_tick stream) {
|
||
|
struct sc_clock_point *point = &clock->points[clock->head];
|
||
|
|
||
|
if (clock->count == SC_CLOCK_RANGE || clock->count & 1) {
|
||
|
// One point passes from the right sum to the left sum
|
||
|
|
||
|
unsigned mid;
|
||
|
if (clock->count == SC_CLOCK_RANGE) {
|
||
|
mid = (clock->head + SC_CLOCK_RANGE / 2) % SC_CLOCK_RANGE;
|
||
|
} else {
|
||
|
// Only for the first frames
|
||
|
mid = clock->count / 2;
|
||
|
}
|
||
|
|
||
|
struct sc_clock_point *mid_point = &clock->points[mid];
|
||
|
clock->left_sum.system += mid_point->system;
|
||
|
clock->left_sum.stream += mid_point->stream;
|
||
|
clock->right_sum.system -= mid_point->system;
|
||
|
clock->right_sum.stream -= mid_point->stream;
|
||
|
}
|
||
|
|
||
|
if (clock->count == SC_CLOCK_RANGE) {
|
||
|
// The current point overwrites the previous value in the circular
|
||
|
// array, update the left sum accordingly
|
||
|
clock->left_sum.system -= point->system;
|
||
|
clock->left_sum.stream -= point->stream;
|
||
|
} else {
|
||
|
++clock->count;
|
||
|
}
|
||
|
|
||
|
point->system = system;
|
||
|
point->stream = stream;
|
||
|
|
||
|
clock->right_sum.system += system;
|
||
|
clock->right_sum.stream += stream;
|
||
|
|
||
|
clock->head = (clock->head + 1) % SC_CLOCK_RANGE;
|
||
|
|
||
|
if (clock->count > 1) {
|
||
|
// Update estimation
|
||
|
sc_clock_estimate(clock, &clock->slope, &clock->offset);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sc_tick
|
||
|
sc_clock_to_system_time(struct sc_clock *clock, sc_tick stream) {
|
||
|
assert(clock->count > 1); // sc_clock_update() must have been called
|
||
|
return (sc_tick) (stream * clock->slope) + clock->offset;
|
||
|
}
|