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