1#include <deque>
2#include <map>
3#include <string>
4#include <vector>
5#include <float.h>
6
7// this utility class manages the arrival of timestamps for
8// a number of channels and tries to group them to a start
9// signal on one channel and stop signals on the other channels
10
11// delay status
12enum DelayStatus {
13 NotEnoughData, // we do not know, if the following signal has already arrived
14 StopsMissing, // some of the stops have arrived after a maximum wait time
15 Complete, // start and all expected stops were processed correctly
16 StartMissing
17};
18
19class ChannelInfo {
20 public:
21 size_t index;
22 int channel;
23 std::string name;
24 // contains the timestamps of pulses
25 std::deque<double> timestamps;
26 bool early;
27 bool ok;
28 bool HasData() const { return timestamps.size() > 0; }
29};
30
31// per channel output of delay measurement
32class ChannelDelay {
33 public:
34 int channel;
35 bool missing;
36 bool isStart;
37 std::string name;
38 double delayPs;
39 // number of events that were ignored because of missing start
40 int ignoredCount;
41};
42
43// output of delay measurement
44class Delays {
45 public:
46 DelayStatus status;
47 std::vector<ChannelDelay> channelDelays;
48 double startTimestamp;
49};
50
51// class for measurement of delays between given number of channels
52class DelayMeasurement {
53 std::vector<ChannelInfo> channels;
54 // map from channel to the index in channels
55 std::map<int, size_t> channelIndexes;
56 //
57 Delays delays;
58 size_t startIndex;
59 // maxDelay is the time that two timestamps are considered to be in the
60 // same group, e.g., the maximum delay for a simple cable delay time
61 double maxDelay;
62 // maxWaitTime is the time to wait after a signal, to know that a following
63 // signal has been received; this allows deciding if a group is complete
64 double maxWaitTime;
65
66 public:
67 void Init(int startChannel, double maxDelay,
68 std::map<int, std::string> channelMap) {
69 channels.resize(channelMap.size());
70 this->maxDelay = maxDelay;
71 maxWaitTime = 10 * maxDelay;
72
73 delays.channelDelays.resize(channelMap.size());
74 size_t i = 0;
75 for (auto const &e : channelMap) {
76 int channel = e.first;
77 std::string name = e.second;
78 channels[i].index = i;
79 channels[i].channel = channel;
80 channels[i].name = name;
81 delays.channelDelays[i].channel = channel;
82 delays.channelDelays[i].name = name;
83 delays.channelDelays[i].isStart = startChannel == channel;
84 channelIndexes[channel] = i;
85 i++;
86 }
87 startIndex = channelIndexes[startChannel];
88 }
89
90 void SetMaxWaitTime(double maxWaitTime) {
91 this->maxWaitTime = maxWaitTime;
92 }
93 // write the current timestamp in ps to the structure
94 void InsertTimestamp(int channel, double timestamp) {
95 size_t index = channelIndexes[channel];
96 channels[index].timestamps.push_back(timestamp);
97 }
98
99 // the parameter is returned by pointer to avoid memory allocations
100 Delays *MeasureDelays() {
101 size_t maxSize = channels[0].timestamps.size();
102 size_t earliestIndex = 0;
103 double earliestTimestamp = DBL_MAX;
104 double latestTimestamp = 0;
105 delays.channelDelays.resize(channels.size());
106
107 for (const ChannelInfo &ci : channels) {
108 maxSize = std::max(maxSize, ci.timestamps.size());
109 if (ci.timestamps.size() > 0 &&
110 ci.timestamps.front() < earliestTimestamp) {
111 earliestTimestamp = ci.timestamps.front();
112 earliestIndex = ci.index;
113 }
114 if (ci.timestamps.size() > 0 &&
115 ci.timestamps.back() > latestTimestamp) {
116 latestTimestamp = ci.timestamps.back();
117 }
118 delays.channelDelays[ci.index].missing = false;
119 delays.channelDelays[ci.index].ignoredCount = 0;
120 }
121
122 // process the queues if enough data is expected
123 if (maxSize > 0 && latestTimestamp - earliestTimestamp > maxWaitTime) {
124 int channelsTooEarly = 0;
125 int channelsTooLate = 0;
126 int channelsOk = 0;
127 int channelsMissing = 0;
128 bool startPresent = channels[startIndex].HasData();
129 double startTimestamp = startPresent
130 ? channels[startIndex].timestamps[0]
131 : latestTimestamp - 2 * maxDelay;
132
133 for (ChannelInfo &ci : channels) {
134 ci.early = false;
135 ci.ok = false;
136 if (ci.HasData()) {
137 double diffToStart = ci.timestamps[0] - startTimestamp;
138 delays.channelDelays[ci.index].delayPs = diffToStart;
139 if (diffToStart < -maxDelay) {
140 ci.early = true;
141 channelsTooEarly++;
142 } else if (diffToStart > maxDelay) {
143 channelsTooLate++;
144 delays.channelDelays[ci.index].missing = true;
145 } else {
146 ci.ok = true;
147 channelsOk++;
148 }
149 } else {
150 if (latestTimestamp > startTimestamp + maxWaitTime) {
151 // if there was data it should have arrived by now
152 delays.channelDelays[ci.index].missing = true;
153 channelsMissing++;
154 }
155 }
156 }
157
158 if (channelsOk + channelsTooLate + channelsMissing ==
159 channels.size()) {
160 // best case, every stop and start is included;
161 // otherwise some channels are missing/too late
162 for (ChannelInfo &ci : channels) {
163 if (ci.ok) {
164 ci.timestamps.pop_front();
165 }
166 }
167 delays.startTimestamp = startTimestamp;
168 delays.status = Complete;
169 } else if (channelsTooEarly > 0 || !startPresent) {
170 // cut away
171 double cutOffTimestamp = startPresent
172 ? startTimestamp - maxDelay
173 : latestTimestamp - maxWaitTime;
174
175 bool removed = false;
176 for (ChannelInfo &ci : channels) {
177 while (ci.timestamps.size() > 0 &&
178 ci.timestamps[0] < cutOffTimestamp) {
179 ci.timestamps.pop_front();
180 delays.channelDelays[ci.index].ignoredCount++;
181 removed = true;
182 }
183 }
184 delays.startTimestamp = earliestTimestamp;
185
186 delays.status = removed ? StartMissing : NotEnoughData;
187 } else {
188 delays.status = NotEnoughData;
189 }
190 } else {
191 // else not enough data, process during next process packets
192 delays.status = NotEnoughData;
193 }
194 return &delays;
195 }
196
197 // the parameter is passed by reference to avoid memory allocations
198 void PrintDelays(Delays *delays) {
199 if (delays->status == NotEnoughData) {
200 return;
201 }
202 if (delays->status == Complete || delays->status == StopsMissing) {
203 for (const ChannelDelay &cd : delays->channelDelays) {
204 if (cd.isStart) {
205 printf("---\n%s: Start %.3lf ns\n", cd.name.c_str(),
206 delays->startTimestamp / 1000.);
207 } else if (!cd.missing) {
208 printf("%s: Delay %.3lf ns\n", cd.name.c_str(),
209 cd.delayPs / 1000.);
210 } else {
211 printf("%s: Missing\n", cd.name.c_str());
212 }
213 }
214 }
215 if (delays->status == StartMissing) {
216
217 printf("---\n Start missing at %.3lf ns\n",
218 delays->startTimestamp / 1000.);
219 for (const ChannelDelay &cd : delays->channelDelays) {
220 if (cd.isStart) {
221 // ignore
222 } else if (cd.ignoredCount > 0) {
223 printf("%s: Ignored %d\n", cd.name.c_str(),
224 cd.ignoredCount);
225 }
226 }
227 }
228 }
229};