Bike-X  0.8
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
OVR_SensorCalibration.cpp
Go to the documentation of this file.
1 /************************************************************************************
2 
3 Filename : OVR_SensorCalibration.cpp
4 Content : Calibration data implementation for the IMU messages
5 Created : January 28, 2014
6 Authors : Max Katsev
7 
8 Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved.
9 
10 Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License");
11 you may not use the Oculus VR Rift SDK except in compliance with the License,
12 which is provided at the time of installation or download, or which
13 otherwise accompanies this software in either electronic or hard copy form.
14 
15 You may obtain a copy of the License at
16 
17 http://www.oculusvr.com/licenses/LICENSE-3.1
18 
19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24 
25 *************************************************************************************/
26 
27 #include "OVR_SensorCalibration.h"
28 #include "Kernel/OVR_Log.h"
29 #include "Kernel/OVR_Threads.h"
30 #include <time.h>
31 
32 namespace OVR {
33 
34 using namespace Alg;
35 
36 const UByte VERSION = 2;
38 
40  : MagCalibrated(false), GyroFilter(6000), GyroAutoTemperature(0)
41 {
42  this->pSensor = pSensor;
43 };
44 
46 {
47  // read factory calibration
49 
50  // if the headset has an autocalibrated offset, prefer it over the factory defaults
51  GyroOffsetReport gyroReport;
52  bool result = pSensor->GetGyroOffsetReport(&gyroReport);
53  if (result && gyroReport.Version != GyroOffsetReport::Version_NoOffset)
54  {
55  GyroAutoOffset = (Vector3f) gyroReport.Offset;
56  GyroAutoTemperature = (float) gyroReport.Temperature;
57  }
58 
59  // read the temperature tables and prepare the interpolation structures
61  OVR_ASSERT(result);
62  for (int i = 0; i < 3; i++)
64 
65  // read the mag calibration
66  MagCalibrationReport report;
67  result = pSensor->GetMagCalibrationReport(&report);
68  MagCalibrated = result && report.Version > 0;
69  MagMatrix = report.Calibration;
70  if (!MagCalibrated)
71  {
72  // OVR_ASSERT(false);
73  LogError("Magnetometer calibration not found!\n");
74  }
75 }
76 
78 {
79  LogText("TemperatureReports:\n");
80  for (int i = 0; i < (int)TemperatureReports.GetSize(); i++)
81  {
82  for (int j = 0; j < (int)TemperatureReports[i].GetSize(); j++)
83  {
85 
86  LogText("[%d][%d]: Version=%3d, Bin=%d/%d, "
87  "Sample=%d/%d, TargetTemp=%3.1lf, "
88  "ActualTemp=%4.1lf, "
89  "Offset=(%7.2lf, %7.2lf, %7.2lf), "
90  "Time=%d\n", i, j, tr.Version,
91  tr.Bin, tr.NumBins,
92  tr.Sample, tr.NumSamples,
95  tr.Offset.x, tr.Offset.y, tr.Offset.z,
96  tr.Time);
97  }
98  }
99 }
100 
102 {
103  OVR_ASSERT(pSensor != NULL);
104 
105  bool result;
106 
107  Array<Array<TemperatureReport> > temperatureReports;
108  pSensor->GetAllTemperatureReports(&temperatureReports);
109 
110  OVR_ASSERT(temperatureReports.GetSize() > 0);
111  OVR_ASSERT(temperatureReports[0].GetSize() > 0);
112 
114 
115  tr.ActualTemperature = 0.0;
116  tr.Time = 0;
117  tr.Version = 0;
118  tr.Offset.x = tr.Offset.y = tr.Offset.z = 0.0;
119 
120  for (UByte i = 0; i < tr.NumBins; i++)
121  {
122  tr.Bin = i;
123 
124  for (UByte j = 0; j < tr.NumSamples; j++)
125  {
126  tr.Sample = j;
127 
128  result = pSensor->SetTemperatureReport(tr);
129  OVR_ASSERT(result);
130 
131  // Need to wait for the tracker board to finish writing to eeprom.
132  Thread::MSleep(50);
133  }
134  }
135 }
136 
138 {
139  AutocalibrateGyro(msg);
140 
141  // compute the interpolated offset
142  Vector3f gyroOffset;
143  for (int i = 0; i < 3; i++)
144  gyroOffset[i] = (float) Interpolators[i].GetOffset(msg.Temperature, GyroAutoTemperature, GyroAutoOffset[i]);
145 
146  // apply calibration
147  msg.RotationRate = GyroMatrix.Transform(msg.RotationRate - gyroOffset);
149  if (MagCalibrated)
151 }
152 
154 {
155  const float alpha = 0.4f;
156  // 1.25f is a scaling factor related to conversion from per-axis comparison to length comparison
157  const float absLimit = 1.25f * 0.349066f;
158  const float noiseLimit = 1.25f * 0.03f;
159 
160  Vector3f gyro = msg.RotationRate;
161  // do a moving average to reject short term noise
162  Vector3f avg = (GyroFilter.IsEmpty()) ? gyro : gyro * alpha + GyroFilter.PeekBack() * (1 - alpha);
163 
164  // Make sure the absolute value is below what is likely motion
165  // Make sure it is close enough to the current average that it is probably noise and not motion
166  if (avg.Length() >= absLimit || (avg - GyroFilter.Mean()).Length() >= noiseLimit)
167  GyroFilter.Clear();
168  GyroFilter.PushBack(avg);
169 
170  // if had a reasonable number of samples already use it for the current offset
172  {
175  // After ~6 seconds of no motion, use the average as the new zero rate offset
176  if (GyroFilter.IsFull())
177  StoreAutoOffset();
178  }
179 }
180 
182 {
183  const double maxDeltaT = 2.5;
184  const double minExtraDeltaT = 0.5;
185  const UInt32 minDelay = 24 * 3600; // 1 day in seconds
186 
187  // find the best bin
188  UPInt binIdx = 0;
189  for (UPInt i = 1; i < TemperatureReports.GetSize(); i++)
190  if (Abs(GyroAutoTemperature - TemperatureReports[i][0].TargetTemperature) <
191  Abs(GyroAutoTemperature - TemperatureReports[binIdx][0].TargetTemperature))
192  binIdx = i;
193 
194  // find the oldest and newest samples
195  // NB: uninitialized samples have Time == 0, so they will get picked as the oldest
196  UPInt newestIdx = 0, oldestIdx = 0;
197  for (UPInt i = 1; i < TemperatureReports[binIdx].GetSize(); i++)
198  {
199  // if the version is newer - do nothing
200  if (TemperatureReports[binIdx][i].Version > VERSION)
201  return;
202  if (TemperatureReports[binIdx][i].Time > TemperatureReports[binIdx][newestIdx].Time)
203  newestIdx = i;
204  if (TemperatureReports[binIdx][i].Time < TemperatureReports[binIdx][oldestIdx].Time)
205  oldestIdx = i;
206  }
207  TemperatureReport& oldestReport = TemperatureReports[binIdx][oldestIdx];
208  TemperatureReport& newestReport = TemperatureReports[binIdx][newestIdx];
209  OVR_ASSERT((oldestReport.Sample == 0 && newestReport.Sample == 0 && newestReport.Version == 0) ||
210  oldestReport.Sample == (newestReport.Sample + 1) % newestReport.NumSamples);
211 
212  bool writeSuccess = false;
213  UInt32 now = (UInt32) time(0);
214  if (now - newestReport.Time > minDelay)
215  {
216  // only write a new sample if the temperature is close enough
217  if (Abs(GyroAutoTemperature - oldestReport.TargetTemperature) < maxDeltaT)
218  {
219  oldestReport.Time = now;
220  oldestReport.ActualTemperature = GyroAutoTemperature;
221  oldestReport.Offset = (Vector3d) GyroAutoOffset;
222  oldestReport.Version = VERSION;
223  writeSuccess = pSensor->SetTemperatureReport(oldestReport);
224  OVR_ASSERT(writeSuccess);
225  }
226  }
227  else
228  {
229  // if the newest sample is too recent - _update_ it if significantly closer to the target temp
230  if (Abs(GyroAutoTemperature - newestReport.TargetTemperature) + minExtraDeltaT
231  < Abs(newestReport.ActualTemperature - newestReport.TargetTemperature))
232  {
233  // (do not update the time!)
234  newestReport.ActualTemperature = GyroAutoTemperature;
235  newestReport.Offset = (Vector3d) GyroAutoOffset;
236  newestReport.Version = VERSION;
237  writeSuccess = pSensor->SetTemperatureReport(newestReport);
238  OVR_ASSERT(writeSuccess);
239  }
240  }
241 
242  // update the interpolators with the new data
243  // this is not particularly expensive call and would only happen rarely
244  // but if performance is a problem, it's possible to only recompute the data that has changed
245  if (writeSuccess)
246  for (int i = 0; i < 3; i++)
248 }
249 
250 const TemperatureReport& median(const Array<TemperatureReport>& temperatureReportsBin, int coord)
251 {
252  Array<double> values;
253  values.Reserve(temperatureReportsBin.GetSize());
254  for (unsigned i = 0; i < temperatureReportsBin.GetSize(); i++)
255  if (temperatureReportsBin[i].ActualTemperature != 0)
256  values.PushBack(temperatureReportsBin[i].Offset[coord]);
257  if (values.GetSize() > 0)
258  {
259  double med = Median(values);
260  // this is kind of a hack
261  for (unsigned i = 0; i < temperatureReportsBin.GetSize(); i++)
262  if (temperatureReportsBin[i].Offset[coord] == med)
263  return temperatureReportsBin[i];
264  // if we haven't found the median in the original array, something is wrong
266  }
267  return temperatureReportsBin[0];
268 }
269 
270 void OffsetInterpolator::Initialize(Array<Array<TemperatureReport> > const& temperatureReports, int coord)
271 {
272  int bins = (int) temperatureReports.GetSize();
274  Temperatures.Reserve(bins);
275  Values.Clear();
276  Values.Reserve(bins);
277 
278  for (int bin = 0; bin < bins; bin++)
279  {
280  OVR_ASSERT(temperatureReports[bin].GetSize() == temperatureReports[0].GetSize());
281  const TemperatureReport& report = median(temperatureReports[bin], coord);
282  if (report.Version > 0 && report.Version <= MAX_COMPAT_VERSION)
283  {
285  Values.PushBack(report.Offset[coord]);
286  }
287  }
288 }
289 
290 double OffsetInterpolator::GetOffset(double targetTemperature, double autoTemperature, double autoValue)
291 {
292  const double autoRangeExtra = 1.0;
293  const double minInterpolationDist = 0.5;
294 
295  // difference between current and autocalibrated temperature adjusted for preference over historical data
296  const double adjustedDeltaT = Abs(autoTemperature - targetTemperature) - autoRangeExtra;
297 
298  int count = (int) Temperatures.GetSize();
299  // handle special cases when we don't have enough data for proper interpolation
300  if (count == 0)
301  return autoValue;
302  if (count == 1)
303  {
304  if (adjustedDeltaT < Abs(Temperatures[0] - targetTemperature))
305  return autoValue;
306  else
307  return Values[0];
308  }
309 
310  // first, find the interval that contains targetTemperature
311  // if all points are on the same side of targetTemperature, find the adjacent interval
312  int l;
313  if (targetTemperature < Temperatures[1])
314  l = 0;
315  else if (targetTemperature >= Temperatures[count - 2])
316  l = count - 2;
317  else
318  for (l = 1; l < count - 2; l++)
319  if (Temperatures[l] <= targetTemperature && targetTemperature < Temperatures[l+1])
320  break;
321  int u = l + 1;
322 
323  // extend the interval if it's too small and the interpolation is unreliable
324  if (Temperatures[u] - Temperatures[l] < minInterpolationDist)
325  {
326  if (l > 0
327  && (u == count - 1 || Temperatures[u] - Temperatures[l - 1] < Temperatures[u + 1] - Temperatures[l]))
328  l--;
329  else if (u < count - 1)
330  u++;
331  }
332 
333  // verify correctness
334  OVR_ASSERT(l >= 0 && u < count);
335  OVR_ASSERT(l == 0 || Temperatures[l] <= targetTemperature);
336  OVR_ASSERT(u == count - 1 || targetTemperature < Temperatures[u]);
337  OVR_ASSERT((l == 0 && u == count - 1) || Temperatures[u] - Temperatures[l] > minInterpolationDist);
339 
340  // perform the interpolation
341  double slope;
342  if (Temperatures[u] - Temperatures[l] >= minInterpolationDist)
343  slope = (Values[u] - Values[l]) / (Temperatures[u] - Temperatures[l]);
344  else
345  // avoid a badly conditioned problem
346  slope = 0;
347  if (adjustedDeltaT < Abs(Temperatures[u] - targetTemperature))
348  // use the autocalibrated value, if it's close
349  return autoValue + slope * (targetTemperature - autoTemperature);
350  else
351  return Values[u] + slope * (targetTemperature - Temperatures[u]);
352 }
353 
354 } // namespace OVR
void Clear()
Definition: OVR_Array.h:382
void Apply(MessageBodyFrame &msg)
void LogText(const char *fmt,...) OVR_LOG_VAARG_ATTRIBUTE(1
void PushBack(const ValueType &val)
Definition: OVR_Array.h:427
T Length() const
Definition: OVR_Math.h:509
OffsetInterpolator Interpolators[3]
virtual bool IsFull() const
Definition: OVR_Deque.h:271
virtual void GetFactoryCalibration(Vector3f *AccelOffset, Vector3f *GyroOffset, Matrix4f *AccelMatrix, Matrix4f *GyroMatrix, float *Temperature)=0
#define NULL
static bool MSleep(unsigned msecs)
const TemperatureReport & median(const Array< TemperatureReport > &temperatureReportsBin, int coord)
void Initialize(Array< Array< TemperatureReport > > const &temperatureReports, int coord)
uint32_t UInt32
Definition: OVR_Types.h:253
SensorCalibration(SensorDevice *pSensor)
void AutocalibrateGyro(MessageBodyFrame const &msg)
size_t UPInt
Definition: OVR_Types.h:218
double GetOffset(double targetTemperature, double autoTemperature, double autoValue)
uint8_t UByte
Definition: OVR_Types.h:249
virtual UPInt GetCapacity(void) const
Definition: OVR_Deque.h:253
virtual bool GetGyroOffsetReport(GyroOffsetReport *)
Definition: OVR_Device.h:1056
const UByte MAX_COMPAT_VERSION
void void LogError(const char *fmt,...) OVR_LOG_VAARG_ATTRIBUTE(1
Vector3< double > Vector3d
Definition: OVR_Math.h:555
#define OVR_ASSERT(p)
const UByte VERSION
virtual UPInt GetSize(void) const
Definition: OVR_Deque.h:259
virtual bool SetTemperatureReport(const TemperatureReport &)
Definition: OVR_Device.h:1053
Array::ValueType & Median(Array &arr)
Definition: OVR_Alg.h:443
Array< Array< TemperatureReport > > TemperatureReports
virtual Elem & PeekBack(int count=0)
Definition: OVR_Deque.h:241
virtual bool GetMagCalibrationReport(MagCalibrationReport *)
Definition: OVR_Device.h:1036
void PushBack(const T &e)
VersionEnum Version
Definition: OVR_Device.h:948
void Reserve(UPInt newCapacity)
Definition: OVR_Array.h:386
Vector3< float > Vector3f
Definition: OVR_Math.h:554
OVR_FORCE_INLINE const T Abs(const T v)
Definition: OVR_Alg.h:79
Vector3< T > Transform(const Vector3< T > &v) const
Definition: OVR_Math.h:1405
virtual bool GetAllTemperatureReports(Array< Array< TemperatureReport > > *)
Definition: OVR_Device.h:1054
#define OVR_DEBUG_BREAK