• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * This module provides an online algorithm for compensating a 3-axis sensor's
19  * offset over its operating temperature:
20  *
21  *   1) Estimates of sensor offset with associated temperature are consumed,
22  *      {offset, offset_temperature}.
23  *   2) A temperature dependence model is extracted from the collected set of
24  *      data pairs.
25  *   3) Until a "complete" model has been built and a model equation has been
26  *      computed, the compensation will use the collected offset nearest in
27  *      temperature. If a model is available, then the compensation will take
28  *      the form of:
29  *
30  * Linear Compensation Model Equation:
31  *   sensor_out = sensor_in - compensated_offset
32  *   Where,
33  *     compensated_offset = (temp_sensitivity * current_temp + sensor_intercept)
34  *
35  * NOTE - 'current_temp' is the current measured temperature. 'temp_sensitivity'
36  *        is the modeled temperature sensitivity (i.e., linear slope).
37  *        'sensor_intercept' is linear model intercept.
38  *
39  * Assumptions:
40  *
41  *   1) Sensor hysteresis is negligible.
42  *   2) Sensor offset temperature dependence is sufficiently "linear".
43  *   3) The impact of long-term offset drift/aging compared to the magnitude of
44  *      deviation resulting from the thermal sensitivity of the offset is
45  *      relatively small.
46  *
47  * Sensor Input and Units:
48  *       - General 3-axis sensor data.
49  *       - Temperature measurements [Celsius].
50  *
51  * NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
52  *
53  * #define OVERTEMPCAL_DBG_ENABLED to enable debug printout statements.
54  * #define OVERTEMPCAL_DBG_LOG_TEMP to periodically printout sensor temperature.
55  */
56 
57 #ifndef LOCATION_LBS_CONTEXTHUB_NANOAPPS_CALIBRATION_OVER_TEMP_OVER_TEMP_CAL_H_
58 #define LOCATION_LBS_CONTEXTHUB_NANOAPPS_CALIBRATION_OVER_TEMP_OVER_TEMP_CAL_H_
59 
60 #include <stdbool.h>
61 #include <stddef.h>
62 #include <stdint.h>
63 
64 #ifdef __cplusplus
65 extern "C" {
66 #endif
67 
68 // Defines the maximum size of the 'model_data' array.
69 #define OVERTEMPCAL_MODEL_SIZE (40)
70 
71 // A common sensor operating temperature at which to start producing the model
72 // jump-start data.
73 #define JUMPSTART_START_TEMP_CELSIUS (30.0f)
74 
75 // The maximum number of successive outliers that may be rejected.
76 #define OVERTEMPCAL_MAX_OUTLIER_COUNT (3)
77 
78 // The 'temp_sensitivity' parameters are set to this value to indicate that the
79 // model is in its initial state.
80 #define OTC_INITIAL_SENSITIVITY (1e6f)
81 
82 // Minimum "significant" change of offset value.
83 #define SIGNIFICANT_OFFSET_CHANGE_RPS (5.23e-5f)  // 3mDPS
84 
85 // Valid sensor temperature operating range.
86 #define OVERTEMPCAL_TEMP_MIN_CELSIUS (-40.0f)
87 #define OVERTEMPCAL_TEMP_MAX_CELSIUS (85.0f)
88 
89 // Over-temperature sensor offset estimate structure.
90 struct OverTempCalDataPt {
91   // Sensor offset estimate, temperature, and timestamp.
92   float offset[3];
93   float offset_temp_celsius;  // [Celsius]
94   uint64_t timestamp_nanos;   // [nanoseconds]
95 };
96 
97 #ifdef OVERTEMPCAL_DBG_ENABLED
98 // Debug printout state enumeration.
99 enum OverTempCalDebugState {
100   OTC_IDLE = 0,
101   OTC_WAIT_STATE,
102   OTC_PRINT_OFFSET,
103   OTC_PRINT_MODEL_PARAMETERS,
104   OTC_PRINT_MODEL_ERROR,
105   OTC_PRINT_MODEL_DATA
106 };
107 
108 // OverTempCal debug information/data tracking structure.
109 struct DebugOverTempCal {
110   uint64_t modelupdate_timestamp_nanos;
111 
112   // The offset estimate nearest the current sensor temperature.
113   struct OverTempCalDataPt nearest_offset;
114 
115   // The maximum model error over all model_data points.
116   float max_error[3];
117 
118   float temp_sensitivity[3];
119   float sensor_intercept[3];
120   float temperature_celsius;
121   size_t num_model_pts;
122 };
123 #endif  // OVERTEMPCAL_DBG_ENABLED
124 
125 // The following data structure contains all of the necessary components for
126 // modeling a sensor's temperature dependency and providing over-temperature
127 // offset corrections.
128 struct OverTempCal {
129   // Storage for over-temperature model data.
130   struct OverTempCalDataPt model_data[OVERTEMPCAL_MODEL_SIZE];
131 
132   // Total number of model data points collected.
133   size_t num_model_pts;
134 
135   // Modeled temperature sensitivity, dOffset/dTemp [sensor_units/Celsius].
136   float temp_sensitivity[3];
137 
138   // Sensor model equation intercept [sensor_units].
139   float sensor_intercept[3];
140 
141   // Timestamp of the last model update.
142   uint64_t modelupdate_timestamp_nanos;  // [nanoseconds]
143 
144   // The temperature at which the offset compensation is performed.
145   float temperature_celsius;
146 
147   // The stored value of the temperature compensated sensor offset.
148   float compensated_offset_previous[3];
149 
150   // Pointer to the offset estimate closest to the current sensor temperature.
151   struct OverTempCalDataPt *nearest_offset;
152 
153   ///// Online Model Identification Parameters ////////////////////////////////
154   //
155   // The rules for determining whether a new model fit is computed and the
156   // resulting fit parameters are accepted are:
157   //    1) A minimum number of data points must have been collected:
158   //          num_model_pts >= min_num_model_pts
159   //       NOTE: Collecting 'num_model_pts' and given that only one point is
160   //       kept per temperature bin (spanning a thermal range specified by
161   //       'delta_temp_per_bin'), implies that model data covers at least,
162   //          model_temp_span >= 'num_model_pts' * delta_temp_per_bin
163   //    2) New model updates will not occur for intervals less than:
164   //          (current_timestamp_nanos - modelupdate_timestamp_nanos) <
165   //            min_update_interval_nanos
166   //    3) A new set of model parameters are accepted if:
167   //         i.  The model fit error is less than, 'max_error_limit'. See
168   //             overTempGetModelError() for error metric description.
169   //         ii. The model fit parameters must be within certain absolute
170   //             bounds:
171   //               a. ABS(temp_sensitivity) < temp_sensitivity_limit
172   //               b. ABS(sensor_intercept) < sensor_intercept_limit
173   size_t min_num_model_pts;
174   uint64_t min_update_interval_nanos;  // [nanoseconds]
175   float max_error_limit;               // [sensor units]
176   float temp_sensitivity_limit;        // [sensor units/Celsius]
177   float sensor_intercept_limit;        // [sensor units]
178 
179   // The number of successive outliers rejected in a row. This is used to
180   // prevent the possibility of a bad state where an initial bad fit causes
181   // good data to be continually rejected.
182   size_t num_outliers;
183 
184   // The rules for accepting new offset estimates into the 'model_data'
185   // collection:
186   //    1) The temperature domain is divided into bins each spanning
187   //       'delta_temp_per_bin'.
188   //    2) Find and replace the i'th 'model_data' estimate data if:
189   //          Let, bin_num = floor(current_temp / delta_temp_per_bin)
190   //          temp_lo_check = bin_num * delta_temp_per_bin
191   //          temp_hi_check = (bin_num + 1) * delta_temp_per_bin
192   //          Check condition:
193   //          temp_lo_check <= model_data[i].offset_temp_celsius < temp_hi_check
194   //    3) If nothing was replaced, and the 'model_data' buffer is not full then
195   //       add the sensor offset estimate to the array.
196   //    4) Otherwise (nothing was replaced and buffer is full), replace the
197   //       oldest data with the incoming one.
198   // This approach ensures a uniform spread of collected data, keeps the most
199   // recent estimates in cases where they arrive frequently near a given
200   // temperature, and prevents model oversampling (i.e., dominance of estimates
201   // concentrated at a given set of temperatures).
202   float delta_temp_per_bin;        // [Celsius/bin]
203 
204   // Timer used to limit the rate at which a search for the nearest offset
205   // estimate is performed.
206   uint64_t nearest_search_timer;   // [nanoseconds]
207 
208   // Timer used to limit the rate at which old estimates are removed from
209   // the 'model_data' collection.
210   uint64_t stale_data_timer;       // [nanoseconds]
211 
212   // Duration beyond which data will be removed to avoid corrupting the model
213   // with drift-compromised data.
214   uint64_t age_limit_nanos;        // [nanoseconds]
215 
216   // Flag set by user to control whether over-temp compensation is used.
217   bool over_temp_enable;
218 
219   // True when new compensation model values have been computed; and reset when
220   // overTempCalNewModelUpdateAvailable() is called. This variable indicates
221   // that the following should be stored/updated in persistent system memory:
222   //    1) 'temp_sensitivity' and 'sensor_intercept'.
223   //    2) The sensor offset data pointed to by 'nearest_offset'
224   //       (saving timestamp information is not required).
225   bool new_overtemp_model_available;
226 
227 #ifdef OVERTEMPCAL_DBG_ENABLED
228   struct DebugOverTempCal debug_overtempcal;  // Debug data structure.
229   enum OverTempCalDebugState debug_state;     // Debug printout state machine.
230   size_t debug_num_model_updates;  // Total number of model updates.
231   size_t debug_num_estimates;      // Total number of offset estimates.
232   bool debug_print_trigger;        // Flag used to trigger data printout.
233 #endif  // OVERTEMPCAL_DBG_ENABLED
234 };
235 
236 /////// FUNCTION PROTOTYPES ///////////////////////////////////////////////////
237 
238 /*
239  * Initializes the over-temp calibration model identification parameters.
240  *
241  * INPUTS:
242  *   over_temp_cal:             Over-temp main data structure.
243  *   min_num_model_pts:         Minimum number of model points per model
244  *                              calculation update.
245  *   min_update_interval_nanos: Minimum model update interval.
246  *   delta_temp_per_bin:        Temperature span that defines the spacing of
247  *                              collected model estimates.
248  *   max_error_limit:           Model acceptance fit error tolerance.
249  *   age_limit_nanos:           Sets the age limit beyond which a offset
250  *                              estimate is removed from 'model_data'.
251  *   temp_sensitivity_limit:    Values that define the upper limits for the
252  *   sensor_intercept_limit:    model parameters. The acceptance of new model
253  *                              parameters must satisfy:
254  *                          i.  ABS(temp_sensitivity) < temp_sensitivity_limit
255  *                          ii. ABS(sensor_intercept) < sensor_intercept_limit
256  *   over_temp_enable:          Flag that determines whether over-temp sensor
257  *                              offset compensation is applied.
258  */
259 void overTempCalInit(struct OverTempCal *over_temp_cal,
260                      size_t min_num_model_pts,
261                      uint64_t min_update_interval_nanos,
262                      float delta_temp_per_bin, float max_error_limit,
263                      uint64_t age_limit_nanos, float temp_sensitivity_limit,
264                      float sensor_intercept_limit, bool over_temp_enable);
265 
266 /*
267  * Sets the over-temp calibration model parameters.
268  *
269  * INPUTS:
270  *   over_temp_cal:    Over-temp main data structure.
271  *   offset:           Update values for the latest offset estimate (array).
272  *   offset_temp_celsius: Measured temperature for the offset estimate.
273  *   timestamp_nanos:  Timestamp for the offset estimate [nanoseconds].
274  *   temp_sensitivity: Modeled temperature sensitivity (array).
275  *   sensor_intercept: Linear model intercept for the over-temp model (array).
276  *   jump_start_model: When 'true' populates an empty 'model_data' array using
277  *                     valid input model parameters.
278  *
279  * NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
280  */
281 void overTempCalSetModel(struct OverTempCal *over_temp_cal, const float *offset,
282                          float offset_temp_celsius, uint64_t timestamp_nanos,
283                          const float *temp_sensitivity,
284                          const float *sensor_intercept, bool jump_start_model);
285 
286 /*
287  * Gets the over-temp calibration model parameters.
288  *
289  * INPUTS:
290  *   over_temp_cal:    Over-temp data structure.
291  * OUTPUTS:
292  *   offset:           Offset values for the latest offset estimate (array).
293  *   offset_temp_celsius: Measured temperature for the offset estimate.
294  *   timestamp_nanos:  Timestamp for the offset estimate [nanoseconds].
295  *   temp_sensitivity: Modeled temperature sensitivity (array).
296  *   sensor_intercept: Linear model intercept for the over-temp model (array).
297  *
298  * NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
299  */
300 void overTempCalGetModel(struct OverTempCal *over_temp_cal, float *offset,
301                          float *offset_temp_celsius, uint64_t *timestamp_nanos,
302                          float *temp_sensitivity, float *sensor_intercept);
303 
304 /*
305  * Sets the over-temp compensation model data set, and computes new model
306  * parameters provided that 'min_num_model_pts' is satisfied.
307  *
308  * INPUTS:
309  *   over_temp_cal:    Over-temp main data structure.
310  *   model_data:       Array of the new model data set.
311  *   data_length:      Number of model data entries in 'model_data'.
312  *
313  * NOTE: Max array length for 'model_data' is OVERTEMPCAL_MODEL_SIZE.
314  */
315 void overTempCalSetModelData(struct OverTempCal *over_temp_cal,
316                              size_t data_length,
317                              const struct OverTempCalDataPt *model_data);
318 
319 /*
320  * Gets the over-temp compensation model data set.
321  *
322  * INPUTS:
323  *   over_temp_cal:    Over-temp main data structure.
324  * OUTPUTS:
325  *   model_data:       Array containing the model data set.
326  *   data_length:      Number of model data entries in 'model_data'.
327  *
328  * NOTE: Max array length for 'model_data' is OVERTEMPCAL_MODEL_SIZE.
329  */
330 void overTempCalGetModelData(struct OverTempCal *over_temp_cal,
331                              size_t *data_length,
332                              struct OverTempCalDataPt *model_data);
333 
334 /*
335  * Returns 'true' if the estimated offset has changed by
336  * 'SIGNIFICANT_OFFSET_CHANGE_RPS' and provides the current over-temperature
337  * compensated offset vector. This function is useful for detecting changes in
338  * the offset vector.
339  *
340  * INPUTS:
341  *   over_temp_cal:    Over-temp data structure.
342  *   timestamp_nanos:  The current system timestamp.
343  * OUTPUTS:
344  *   compensated_offset: Temperature compensated offset estimate array.
345  *   compensated_offset_temperature_celsius: Compensated offset temperature.
346  *
347  * NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
348  */
349 bool overTempCalGetOffset(struct OverTempCal *over_temp_cal,
350                           uint64_t timestamp_nanos,
351                           float *compensated_offset_temperature_celsius,
352                           float *compensated_offset);
353 
354 /*
355  * Removes the over-temp compensated offset from the input sensor data.
356  *
357  * INPUTS:
358  *   over_temp_cal:    Over-temp data structure.
359  *   timestamp_nanos:  Timestamp of the sensor estimate update.
360  *   xi, yi, zi:       3-axis sensor data to be compensated.
361  * OUTPUTS:
362  *   xo, yo, zo:       3-axis sensor data that has been compensated.
363  */
364 void overTempCalRemoveOffset(struct OverTempCal *over_temp_cal,
365                              uint64_t timestamp_nanos, float xi, float yi,
366                              float zi, float *xo, float *yo, float *zo);
367 
368 // Returns true when a new over-temp model update is available; and the
369 // 'new_overtemp_model_available' flag is reset.
370 bool overTempCalNewModelUpdateAvailable(struct OverTempCal *over_temp_cal);
371 
372 /*
373  * Updates the sensor's offset estimate and conditionally assimilates it into
374  * the over-temp model data set, 'model_data'.
375  *
376  * INPUTS:
377  *   over_temp_cal:       Over-temp data structure.
378  *   timestamp_nanos:     Timestamp of the sensor estimate update.
379  *   offset:              3-axis sensor data to be compensated (array).
380  *   temperature_celsius: Measured temperature for the new sensor estimate.
381  *
382  * NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
383  */
384 void overTempCalUpdateSensorEstimate(struct OverTempCal *over_temp_cal,
385                                      uint64_t timestamp_nanos,
386                                      const float *offset,
387                                      float temperature_celsius);
388 
389 // Updates the temperature at which the offset compensation is performed (i.e.,
390 // the current measured temperature value). This function is provided mainly for
391 // flexibility since temperature updates may come in from a source other than
392 // the sensor itself, and at a different rate.
393 void overTempCalSetTemperature(struct OverTempCal *over_temp_cal,
394                                uint64_t timestamp_nanos,
395                                float temperature_celsius);
396 
397 /*
398  * Computes the maximum absolute error between the 'model_data' estimates and
399  * the estimate determined by the input model parameters.
400  *   max_error (over all i)
401  *     |model_data[i]->offset_xyz -
402  *       getCompensatedOffset(model_data[i]->offset_temp_celsius,
403  *         temp_sensitivity, sensor_intercept)|
404  *
405  * INPUTS:
406  *   over_temp_cal:    Over-temp data structure.
407  *   temp_sensitivity: Model temperature sensitivity to test (array).
408  *   sensor_intercept: Model intercept to test (array).
409  * OUTPUTS:
410  *   max_error:        Maximum absolute error for the candidate model (array).
411  *
412  * NOTE 1: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
413  * NOTE 2: This function is provided for testing purposes.
414  */
415 void overTempGetModelError(const struct OverTempCal *over_temp_cal,
416                            const float *temp_sensitivity,
417                            const float *sensor_intercept, float *max_error);
418 
419 #ifdef OVERTEMPCAL_DBG_ENABLED
420 // This debug printout function assumes the input sensor data is a gyroscope
421 // [rad/sec].
422 void overTempCalDebugPrint(struct OverTempCal *over_temp_cal,
423                            uint64_t timestamp_nanos);
424 #endif  // OVERTEMPCAL_DBG_ENABLED
425 
426 #ifdef __cplusplus
427 }
428 #endif
429 
430 #endif  // LOCATION_LBS_CONTEXTHUB_NANOAPPS_CALIBRATION_OVER_TEMP_OVER_TEMP_CAL_H_
431