File: postproc.cpp

package info (click to toggle)
liblsl 1.16.2b1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,724 kB
  • sloc: cpp: 12,515; ansic: 666; python: 28; sh: 25; makefile: 18
file content (80 lines) | stat: -rw-r--r-- 2,371 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "time_postprocessor.h"
#include <loguru.hpp>
#include <random>
#include <thread>
// include loguru before catch
#include <catch2/catch.hpp>

// clazy:excludeall=non-pod-global-static

template <std::size_t N>
inline void test_postproc_array(
	lsl::time_postprocessor &pp, const double (&in)[N], const double (&expected)[N]) {
	for (std::size_t i = 0; i < N; ++i) CHECK(pp.process_timestamp(in[i]) == Approx(expected[i]));
}

TEST_CASE("postprocessing", "[basic]") {
	double time_offset = -50.0, srate = 1.;
	bool was_reset = false;
	lsl::time_postprocessor pp(
		[&]() {
			UNSCOPED_INFO("time_offset " << time_offset);
			return time_offset;
		},
		[&]() { return srate; }, [&]() { return was_reset; });

	double nopostproc[] = {2, 3.1, 3, 5, 5.9, 7.1};

	{
		INFO("proc_clocksync")
		pp.set_options(proc_clocksync);
		for (double t : nopostproc) CHECK(pp.process_timestamp(t) == Approx(t + time_offset));
	}

	{
		INFO("proc_clocksync + clock_reset")
		pp.set_options(proc_clocksync);
		// std::this_thread::sleep_for(std::chrono::milliseconds(600));
		was_reset = true;
		time_offset = -200;
		// for (double t : nopostproc) CHECK(pp.process_timestamp(t) == Approx(t + time_offset));
	}

	{
		INFO("proc_monotonize")
		double monotonized[] = {2, 3.1, 3.1, 5, 5.9, 7.1};
		pp.set_options(proc_monotonize);
		test_postproc_array(pp, nopostproc, monotonized);
	}

	{
		INFO("reset with proc_none")
		pp.set_options(proc_none);
		test_postproc_array(pp, nopostproc, nopostproc);
	}
}

TEST_CASE("rls_smoothing", "[basic]") {
	const int n = 100000, warmup_samples = 1000;
	const double t0 = 5000, latency = 0.05, srate = 100., halftime = 90;
	lsl::postproc_dejitterer pp(t0, srate, halftime);

	std::default_random_engine rng;
	std::normal_distribution<double> jitter(latency, .005);

	pp.dejitter(t0);
	int n_outlier = 0;

	for (int i = 0; i < n; ++i) {
		const double t = t0 + i / srate, e = jitter(rng), dejittered = pp.dejitter(t + e),
					 est_error = dejittered - t - latency;
		if (i > warmup_samples && fabs(est_error) > std::max(e, .001)) n_outlier++;
	}
	LOG_F(INFO, "\nP:\t%f\t%f\n\t%f\t%f\nw:\t%f\t%f", pp.P00_, pp.P01_, pp.P01_, pp.P11_, pp.w0_,
		pp.w1_);
	CHECK(n_outlier < n / 100);

	CHECK(pp.dejitter(t0 + latency + n / srate) == Approx(t0 + latency + n / srate));
	CHECK(fabs(pp.w0_ - latency) < .1);
	CHECK(fabs(pp.w1_ - 1 / srate) < 1e-6);
}