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 };