1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module my.signal_theory.simulate;
7
8 import core.time : Duration, dur;
9
10 @safe:
11
12 struct Simulator {
13 import std.random;
14
15 // how much we have drifted from the desired period.
16 Duration pv;
17
18 // The current time.
19 Duration currTime = 16666667.dur!"nsecs";
20 // Simulated length of a tick
21 Duration simTick = 100.dur!"nsecs";
22
23 // Last time the PV where updated.
24 Duration lastUpdate;
25
26 // Next time the PID should be updated
27 Duration wakeupTime = 100.dur!"nsecs";
28
29 // True if the inputFn+outputFn where called.
30 bool updated;
31
32 // The desired period.
33 Duration period = 16666667.dur!"nsecs";
34 // The target time that should be as close as possible to currTime.
35 // starting at -2ms simulate a static offset
36 Duration targetTime = 14666667.dur!"nsecs";
37
38 double gain0 = 0;
39
40 MinstdRand0 g = MinstdRand0(42);
41 double posRn() {
42 return uniform01(g);
43 }
44
45 double spreadRn() {
46 return uniform!"[]"(-1.0, 1.0, g);
47 }
48
49 void tick(string TsUnit)(void delegate(Duration) @safe inputFn, double delegate() @safe outputFn) {
50 currTime += simTick;
51 updated = false;
52
53 if (currTime < wakeupTime)
54 return;
55 pv = currTime - targetTime;
56
57 inputFn(pv);
58
59 double gain = spreadRn() * period.total!TsUnit / 10000;
60
61 if (posRn > 0.8) {
62 gain0 = spreadRn * period.total!TsUnit / 1000;
63 }
64 gain += gain0;
65
66 // simulate that high frequency jitter only sometimes occur.
67 if (posRn > 0.99) {
68 gain += posRn * period.total!TsUnit * 0.5;
69 }
70
71 double output = outputFn();
72
73 // the output is a time delay and thus can never be negative.
74 wakeupTime = currTime + period + (cast(long)(output + gain)).dur!TsUnit;
75
76 lastUpdate = targetTime;
77 targetTime += period;
78 updated = true;
79 }
80 };