packetstamper.cpp
Go to the documentation of this file.
1 
2 // Copyright (c) 2003-2021 Xsens Technologies B.V. or subsidiaries worldwide.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions, and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice,
12 // this list of conditions, and the following disclaimer in the documentation
13 // and/or other materials provided with the distribution.
14 //
15 // 3. Neither the names of the copyright holders nor the names of their contributors
16 // may be used to endorse or promote products derived from this software without
17 // specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
24 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
26 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.THE LAWS OF THE NETHERLANDS
28 // SHALL BE EXCLUSIVELY APPLICABLE AND ANY DISPUTES SHALL BE FINALLY SETTLED UNDER THE RULES
29 // OF ARBITRATION OF THE INTERNATIONAL CHAMBER OF COMMERCE IN THE HAGUE BY ONE OR MORE
30 // ARBITRATORS APPOINTED IN ACCORDANCE WITH SAID RULES.
31 //
32 
33 
34 // Copyright (c) 2003-2021 Xsens Technologies B.V. or subsidiaries worldwide.
35 // All rights reserved.
36 //
37 // Redistribution and use in source and binary forms, with or without modification,
38 // are permitted provided that the following conditions are met:
39 //
40 // 1. Redistributions of source code must retain the above copyright notice,
41 // this list of conditions, and the following disclaimer.
42 //
43 // 2. Redistributions in binary form must reproduce the above copyright notice,
44 // this list of conditions, and the following disclaimer in the documentation
45 // and/or other materials provided with the distribution.
46 //
47 // 3. Neither the names of the copyright holders nor the names of their contributors
48 // may be used to endorse or promote products derived from this software without
49 // specific prior written permission.
50 //
51 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
52 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
54 // THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 // SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
56 // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
58 // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
59 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.THE LAWS OF THE NETHERLANDS
60 // SHALL BE EXCLUSIVELY APPLICABLE AND ANY DISPUTES SHALL BE FINALLY SETTLED UNDER THE RULES
61 // OF ARBITRATION OF THE INTERNATIONAL CHAMBER OF COMMERCE IN THE HAGUE BY ONE OR MORE
62 // ARBITRATORS APPOINTED IN ACCORDANCE WITH SAID RULES.
63 //
64 
65 #include <xstypes/xsdatapacket.h>
66 #include "packetstamper.h"
67 
73 const int64_t PacketStamper::AWINDABOUNDARY = 0x100000000LL;
75 
77 const int64_t PacketStamper::MTSCBOUNDARY = 0x00010000LL;
78 
80 const int64_t PacketStamper::SC8BOUNDARY = 0x00000100LL;
81 
84 {
86 }
87 
90 {
91  m_latest = DataPair{-1, 0};
93  m_dataPoints.clear();
94  m_rate = 0;
95  m_toa0 = 0.0;
96 }
97 
106 int64_t PacketStamper::calculateLargePacketCounter(int64_t frameCounter, int64_t lastCounter, int64_t boundary)
107 {
108  if (lastCounter < 0)
109  return frameCounter;
110 
111  const int64_t lowMask = boundary - 1;
112  const int64_t boundaryHalf = boundary / 2;
113 
114  int64_t low = lastCounter & lowMask;
115  int64_t dt = frameCounter - low;
116  if (dt < -boundaryHalf)
117  return lastCounter + dt + boundary; // positive wraparound
118  if (dt < boundaryHalf)
119  return lastCounter + dt; // normal increment
120 
121  return lastCounter + dt - boundary; // negative wraparound
122 }
123 
133 int64_t PacketStamper::stampPacket(XsDataPacket& pack, XsDataPacket const& highestPacket)
134 {
135  pack.setTimeOfArrival(XsTimeStamp::now());
136  int64_t newCounter, lastCounter = -1;
137 
138  if (!highestPacket.empty())
139  lastCounter = highestPacket.packetId();
140 
141  if (pack.packetId() > 0)
142  newCounter = pack.packetId();
143  else if (pack.containsPacketCounter())
144  newCounter = calculateLargePacketCounter(pack.packetCounter(), lastCounter, MTSCBOUNDARY);
145  else if (pack.containsSampleTimeFine())
146  {
147  newCounter = lastCounter + 1;
148  //if (pack.containsSampleTimeCoarse())
149  // newCounter = (int64_t) pack.sampleTime64();
150  //else
151  // newCounter = calculateLargeSampleTime((int32_t) pack.sampleTimeFine(), lastCounter);
152  }
153  else if (pack.containsPacketCounter8())
154  newCounter = calculateLargePacketCounter(pack.packetCounter8(), lastCounter, SC8BOUNDARY);
155  else if (pack.containsAwindaSnapshot())
156  newCounter = calculateLargePacketCounter(pack.awindaSnapshot().m_frameNumber, lastCounter, AWINDABOUNDARY);
157  else
158  newCounter = lastCounter + 1;
159 
160  // JLDEBUGG("XsensDeviceAPI", "%s [%08x] old = %I64d new = %I64d diff = %I64d", __FUNCTION__, did, lastCounter, newCounter, (newCounter-lastCounter));
161 
162  pack.setPacketId(newCounter);
163  estimateTos(pack);
164 
165  return newCounter;
166 }
167 
175 int64_t PacketStamper::calculateLargeSampleTime(int64_t frameTime, int64_t lastTime)
176 {
177  if (lastTime < 0)
178  return frameTime;
179 
180  int64_t low = lastTime % 864000000;
181  int64_t dt = frameTime - low;
182  if (dt < (-864000000 / 2))
183  return lastTime + dt + 864000000; // positive wraparound
184  if (dt < (864000000 / 2))
185  return lastTime + dt; // normal increment
186 
187  return lastTime + dt - 864000000; // negative wraparound
188 }
189 
192 {
193  if (pack.containsSampleTime64())
194  pack.setEstimatedTimeOfSampling(XsTimeStamp((int64_t) pack.sampleTime64()));
195  else
196  pack.setEstimatedTimeOfSampling(estimateTosInternal(pack.packetId(), pack.timeOfArrival().msTime()));
197 }
198 
204 {
205  // now we need to find the most consistent rate by doing a least square best fit
206  // which we then shift down to match the fastest toa
207  double avgPid = 0.0;
208  double avgToa = 0.0;
209 
210  // if we have enough data we exclude the last item from the averages since it is volatile
211  auto last = *m_dataPoints.rbegin();
212  bool popit = (m_dataPoints.size() >= 5);
213  if (popit)
214  m_dataPoints.pop_back();
215  for (auto const& d : m_dataPoints)
216  {
217  avgPid += d.m_pid;
218  avgToa += d.m_toa;
219  }
220  avgPid /= m_dataPoints.size();
221  avgToa /= m_dataPoints.size();
222 
223  double fracTop = 0.0, fracBot = 0.0;
224  for (auto const& d : m_dataPoints)
225  {
226  double dpid = d.m_pid - avgPid;
227  double dtoa = d.m_toa - avgToa;
228  fracTop += dpid * dtoa;
229  fracBot += dpid * dpid;
230  }
231  m_rate = fracTop / fracBot;
232  m_toa0 = avgToa - m_rate * avgPid;
233 
234  // put last item back
235  if (popit)
236  m_dataPoints.push_back(last);
237 
238  // shift down
239  for (auto const& d : m_dataPoints)
240  {
241  double diff = d.m_pid * m_rate + m_toa0 - d.m_toa;
242  if (diff > 0.0)
243  m_toa0 -= diff;
244  }
245 }
246 
253 {
254  auto reject = m_dataPoints.end();
255  double diffMin = 0.0;
256  for (auto d = m_dataPoints.begin(); d != m_dataPoints.end(); ++d)
257  {
258  double diff = d->m_pid * m_rate + m_toa0 - d->m_toa;
259  if (diff < -m_rate && diff < diffMin)
260  {
261  diffMin = diff;
262  reject = d;
263  }
264  }
265  if (reject != m_dataPoints.end())
266  {
267  m_dataPoints.erase(reject);
268  return true;
269  }
270  return false;
271 }
272 
284 int64_t PacketStamper::estimateTosInternal(int64_t pid, int64_t toa)
285 {
286  if (m_dataPoints.size() < 2)
287  {
288  if (m_dataPoints.empty())
289  {
290  m_linearize = DataPair{pid, toa};
291  m_dataPoints.push_back(DataPair{0, 0});
292  m_toa0 = 0;
293  m_rate = 0;
295  }
296  else if (pid > m_latest.m_pid && toa > m_latest.m_toa)
297  {
298  DataPair last = {pid - m_linearize.m_pid, toa - m_linearize.m_toa};
299  auto first = *m_dataPoints.begin();
300  m_toa0 = 0;
301  m_rate = (double)(last.m_toa - first.m_toa) / (double)(last.m_pid - first.m_pid);
302  m_dataPoints.push_back(last);
303  }
304  m_latest = DataPair{pid, toa}; // non-linearized values!
305  return toa;
306  }
307  if (pid > m_latest.m_pid && toa > m_latest.m_toa)
308  {
309  // we might do an update
310  while (pid - m_latest.m_pid == 1) // 'while' so we can break out of this scope if necessary
311  {
312  // do sanity check on the data point before adding it
313  bool enough = (m_dataPoints.size() >= 5 && toa - m_linearize.m_toa >= 1000);
314  if (enough)
315  {
316  double toaPred = (pid - m_linearize.m_pid) * m_rate + m_toa0;
317  if ((double)(toa - m_linearize.m_toa) - toaPred >= 2.0 * m_rate)
318  {
319  // ignore this point, it doesn't match known information well enough
320  // also ignore the next few points as they're likely also not very reliable
322  break;
323  }
324  }
325  if (m_rejectionCountdown > 0)
326  {
328  break;
329  }
330 
331  // add data point to list
332  m_dataPoints.push_back(DataPair {pid - m_linearize.m_pid, toa - m_linearize.m_toa});
333 
334  /* filter list, we remove any points that can't define the rate because they're above the
335  toa line spanned by the neighbouring points
336  */
337  if (enough)
338  {
339  auto next = m_dataPoints.begin();
340  auto prev = next++;
341  auto it = next++;
342  while (next != m_dataPoints.end())
343  {
344  double rate = (double)(next->m_toa - prev->m_toa) / (double)(next->m_pid - prev->m_pid);
345  double itoa = (it->m_pid - prev->m_pid) * rate + prev->m_toa;
346  if ((double) it->m_toa >= itoa)
347  {
348  // useless data point, discard
349  it = m_dataPoints.erase(it);
350  if (it == m_dataPoints.end())
351  break;
352  prev = it;
353  --prev;
354  }
355  else
356  {
357  // useful data point, keep it
358  prev = it++;
359  if (it == m_dataPoints.end())
360  break;
361  }
362  next = it;
363  ++next;
364  }
365  }
366 
367  // forget too old data if we have enough data left afterwards
369  if (enough && m_dataPoints.size() >= 16)
370  {
371  bool reestimate = rejectOutlier();
372  if (m_dataPoints.size() >= 16)
373  {
374  auto it = m_dataPoints.begin();
375  ++it;
376  if ((toa - m_linearize.m_toa) - it->m_toa >= 30000)
377  {
378  m_dataPoints.pop_front();
379  reestimate = true;
380  }
381  }
382  if (reestimate)
384  }
385  break;
386  }
387  m_latest = DataPair{pid, toa};
388  }
389 
390  // estimate tos from last known values
391  return std::min(toa, XsMath_doubleToInt64((pid - m_linearize.m_pid) * m_rate + m_toa0) + m_linearize.m_toa);
392 }
PacketStamper::m_toa0
double m_toa0
The recomputed Time Of Arrival of PID 0.
Definition: packetstamper.h:104
PacketStamper::m_rate
double m_rate
The estimated clock rate per pid.
Definition: packetstamper.h:105
XsMath_doubleToInt64
XSMATHINLINE2 int64_t XsMath_doubleToInt64(double d)
Returns d integer converted from a double precision floating point value.
Definition: xsmath.h:293
packetstamper.h
XsDataPacket
Contains an interpreted data message. The class provides easy access to the contained data through it...
Definition: xsdatapacket.h:301
PacketStamper::DataPair
Definition: packetstamper.h:89
PacketStamper::m_latest
DataPair m_latest
Latest known data (later data may arrive with a lower pid, which will not be put in this item)
Definition: packetstamper.h:100
PacketStamper::SC8BOUNDARY
static const int64_t SC8BOUNDARY
8 bit Sample Counter boundary
Definition: packetstamper.h:81
PacketStamper::m_dataPoints
std::list< DataPair > m_dataPoints
The filtered history of interesting data items.
Definition: packetstamper.h:102
PacketStamper::AWINDABOUNDARY
static const int64_t AWINDABOUNDARY
32 bit MT Sample Counter boundary
Definition: packetstamper.h:79
PacketStamper::stampPacket
int64_t stampPacket(XsDataPacket &pack, XsDataPacket const &highest)
Create 64 bit counter for a packet.
Definition: packetstamper.cpp:133
PacketStamper::calculateLargePacketCounter
static int64_t calculateLargePacketCounter(int64_t frameCounter, int64_t lastCounter, int64_t boundary)
Calculate the new large packet counter value based on frameCounter and the lastCounter.
Definition: packetstamper.cpp:106
PacketStamper::DataPair::m_pid
int64_t m_pid
Packet ID of data item.
Definition: packetstamper.h:91
d
d
PacketStamper::MTSCBOUNDARY
static const int64_t MTSCBOUNDARY
16 bit MT Sample Counter boundary
Definition: packetstamper.h:80
xsdatapacket.h
PacketStamper::estimateClockParameters
void estimateClockParameters()
Estimate the clock parameters based on the available data points.
Definition: packetstamper.cpp:203
PacketStamper::m_linearize
DataPair m_linearize
The very first item received, used to normalize to 0,0 so we have less computational issues with larg...
Definition: packetstamper.h:101
PacketStamper::estimateTos
void estimateTos(XsDataPacket &pack)
Estimate the time of sampling for the supplied packet pack and update it.
Definition: packetstamper.cpp:191
PacketStamper::rejectOutlier
bool rejectOutlier()
Remove the worst outlier from the known data points.
Definition: packetstamper.cpp:252
PacketStamper::PacketStamper
PacketStamper()
Default constructor.
Definition: packetstamper.cpp:83
PacketStamper::resetTosEstimation
void resetTosEstimation()
Reset the Time Of Sampling estimation parameters.
Definition: packetstamper.cpp:89
PacketStamper::m_rejectionCountdown
int m_rejectionCountdown
A countdown value that is used after the input sanity check rejects input to reject subsequent sample...
Definition: packetstamper.h:106
PacketStamper::estimateTosInternal
int64_t estimateTosInternal(int64_t pid, int64_t toa)
Estimate the time of sampling for the supplied pid.
Definition: packetstamper.cpp:284
PacketStamper::calculateLargeSampleTime
static int64_t calculateLargeSampleTime(int64_t frameTime, int64_t lastTime)
Calculate the new large sample time value based on frameTime and the lastTime.
Definition: packetstamper.cpp:175
XsTimeStamp
struct XsTimeStamp XsTimeStamp
Definition: xstimestamp.h:458
PacketStamper::DataPair::m_toa
int64_t m_toa
Time Of Arrival of data item.
Definition: packetstamper.h:92


xsens_mti_driver
Author(s):
autogenerated on Sun Sep 3 2023 02:43:20