5.8 delay.h

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