blob: 4ae8952ddbe462c58b21e7ae2bdae66d561b22aa [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This module provides an online algorithm for compensating a 3-axis sensor's
* offset over its operating temperature:
*
* 1) Estimates of sensor offset with associated temperature are consumed,
* {offset, offset_temperature}.
* 2) A temperature dependence model is extracted from the collected set of
* data pairs.
* 3) Until a "complete" model has been built and a model equation has been
* computed, the compensation will use the collected offset nearest in
* temperature. If a model is available, then the compensation will take
* the form of:
*
* Linear Compensation Model Equation:
* sensor_out = sensor_in - compensated_offset
* Where,
* compensated_offset = (temp_sensitivity * current_temp + sensor_intercept)
*
* NOTE - 'current_temp' is the current measured temperature. 'temp_sensitivity'
* is the modeled temperature sensitivity (i.e., linear slope).
* 'sensor_intercept' is linear model intercept.
*
* Assumptions:
*
* 1) Sensor hysteresis is negligible.
* 2) Sensor offset temperature dependence is sufficiently "linear".
* 3) The impact of long-term offset drift/aging compared to the magnitude of
* deviation resulting from the thermal sensitivity of the offset is
* relatively small.
*
* Sensor Input and Units:
* - General 3-axis sensor data.
* - Temperature measurements [Celsius].
*
* NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
*
* #define OVERTEMPCAL_DBG_ENABLED to enable debug printout statements.
* #define OVERTEMPCAL_DBG_LOG_TEMP to periodically printout sensor temperature.
*/
#ifndef LOCATION_LBS_CONTEXTHUB_NANOAPPS_CALIBRATION_OVER_TEMP_OVER_TEMP_CAL_H_
#define LOCATION_LBS_CONTEXTHUB_NANOAPPS_CALIBRATION_OVER_TEMP_OVER_TEMP_CAL_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Defines the maximum size of the 'model_data' array.
#define OVERTEMPCAL_MODEL_SIZE (40)
// The maximum number of successive outliers that may be rejected.
#define OVERTEMPCAL_MAX_OUTLIER_COUNT (3)
// Over-temperature sensor offset estimate structure.
struct OverTempCalDataPt {
// Sensor offset estimate, temperature, and timestamp.
float offset[3];
float offset_temp_celsius; // [Celsius]
uint64_t timestamp_nanos; // [nanoseconds]
};
#ifdef OVERTEMPCAL_DBG_ENABLED
// Debug printout state enumeration.
enum OverTempCalDebugState {
OTC_IDLE = 0,
OTC_WAIT_STATE,
OTC_PRINT_OFFSET,
OTC_PRINT_MODEL_PARAMETERS,
OTC_PRINT_MODEL_ERROR,
OTC_PRINT_MODEL_DATA
};
// OverTempCal debug information/data tracking structure.
struct DebugOverTempCal {
uint64_t modelupdate_timestamp_nanos;
// The most recent offset estimate received.
struct OverTempCalDataPt latest_offset;
// The offset estimate nearest the current sensor temperature.
struct OverTempCalDataPt nearest_offset;
// The maximum model error over all model_data points.
float max_error[3];
float temp_sensitivity[3];
float sensor_intercept[3];
float temperature_celsius;
size_t num_model_pts;
};
#endif // OVERTEMPCAL_DBG_ENABLED
// The following data structure contains all of the necessary components for
// modeling a sensor's temperature dependency and providing over-temperature
// offset corrections.
struct OverTempCal {
// Storage for over-temperature model data.
struct OverTempCalDataPt model_data[OVERTEMPCAL_MODEL_SIZE];
// Total number of model data points collected.
size_t num_model_pts;
// Modeled temperature sensitivity, dOffset/dTemp [sensor_units/Celsius].
float temp_sensitivity[3];
// Sensor model equation intercept [sensor_units].
float sensor_intercept[3];
// Timestamp of the last model update.
uint64_t modelupdate_timestamp_nanos; // [nanoseconds]
// The temperature at which the offset compensation is performed.
float temperature_celsius;
// Pointer to the offset estimate closest to the current sensor temperature.
struct OverTempCalDataPt *nearest_offset;
///// Online Model Identification Parameters ////////////////////////////////
//
// The rules for determining whether a new model fit is computed and the
// resulting fit parameters are accepted are:
// 1) A minimum number of data points must have been collected:
// num_model_pts >= min_num_model_pts
// NOTE: Collecting 'num_model_pts' and given that only one point is
// kept per temperature bin (spanning a thermal range specified by
// 'delta_temp_per_bin'), implies that model data covers at least,
// model_temp_span >= 'num_model_pts' * delta_temp_per_bin
// 2) New model updates will not occur for intervals less than:
// (current_timestamp_nanos - modelupdate_timestamp_nanos) <
// min_update_interval_nanos
// 3) A new set of model parameters are accepted if:
// i. The model fit error is less than, 'max_error_limit'. See
// getModelError() for error metric description.
// ii. The model fit parameters must be within certain absolute
// bounds:
// a. ABS(temp_sensitivity) < temp_sensitivity_limit
// b. ABS(sensor_intercept) < sensor_intercept_limit
size_t min_num_model_pts;
uint64_t min_update_interval_nanos; // [nanoseconds]
float max_error_limit; // [sensor units]
float temp_sensitivity_limit; // [sensor units/Celsius]
float sensor_intercept_limit; // [sensor units]
// The number of successive outliers rejected in a row. This is used to
// prevent the possibility of a bad state where an initial bad fit causes
// good data to be continually rejected.
size_t num_outliers;
// The rules for accepting new offset estimates into the 'model_data'
// collection:
// 1) The temperature domain is divided into bins each spanning
// 'delta_temp_per_bin'.
// 2) Find and replace the i'th 'model_data' estimate data if:
// Let, bin_num = floor(current_temp / delta_temp_per_bin)
// temp_lo_check = bin_num * delta_temp_per_bin
// temp_hi_check = (bin_num + 1) * delta_temp_per_bin
// Check condition:
// temp_lo_check <= model_data[i].offset_temp_celsius < temp_hi_check
// 3) If nothing was replaced, and the 'model_data' buffer is not full then
// add the sensor offset estimate to the array.
// 4) Otherwise (nothing was replaced and buffer is full), replace the
// oldest data with the incoming one.
// This approach ensures a uniform spread of collected data, keeps the most
// recent estimates in cases where they arrive frequently near a given
// temperature, and prevents model oversampling (i.e., dominance of estimates
// concentrated at a given set of temperatures).
float delta_temp_per_bin; // [Celsius/bin]
// Timer used to limit the rate at which a search for the nearest offset
// estimate is performed.
uint64_t nearest_search_timer; // [nanoseconds]
// Timer used to limit the rate at which old estimates are removed from
// the 'model_data' collection.
uint64_t stale_data_timer; // [nanoseconds]
// Duration beyond which data will be removed to avoid corrupting the model
// with drift-compromised data.
uint64_t age_limit_nanos; // [nanoseconds]
// Flag set by user to control whether over-temp compensation is used.
bool over_temp_enable;
// True when new compensation model values have been computed; and reset when
// overTempCalNewModelUpdateAvailable() is called. This variable indicates
// that the following should be stored/updated in persistent system memory:
// 1) 'temp_sensitivity' and 'sensor_intercept'.
// 2) The sensor offset data pointed to by 'nearest_offset'
// (saving timestamp information is not required).
bool new_overtemp_model_available;
#ifdef OVERTEMPCAL_DBG_ENABLED
struct DebugOverTempCal debug_overtempcal; // Debug data structure.
enum OverTempCalDebugState debug_state; // Debug printout state machine.
size_t debug_num_model_updates; // Total number of model updates.
size_t debug_num_estimates; // Total number of offset estimates.
bool debug_print_trigger; // Flag used to trigger data printout.
#endif // OVERTEMPCAL_DBG_ENABLED
};
/////// FUNCTION PROTOTYPES ///////////////////////////////////////////////////
/*
* Initializes the over-temp calibration model identification parameters.
*
* INPUTS:
* over_temp_cal: Over-temp main data structure.
* min_num_model_pts: Minimum number of model points per model
* calculation update.
* min_update_interval_nanos: Minimum model update interval.
* delta_temp_per_bin: Temperature span that defines the spacing of
* collected model estimates.
* max_error_limit: Model acceptance fit error tolerance.
* age_limit_nanos: Sets the age limit beyond which a offset
* estimate is removed from 'model_data'.
* temp_sensitivity_limit: Values that define the upper limits for the
* sensor_intercept_limit: model parameters. The acceptance of new model
* parameters must satisfy:
* i. ABS(temp_sensitivity) < temp_sensitivity_limit
* ii. ABS(sensor_intercept) < sensor_intercept_limit
* over_temp_enable: Flag that determines whether over-temp sensor
* offset compensation is applied.
*/
void overTempCalInit(struct OverTempCal *over_temp_cal,
size_t min_num_model_pts,
uint64_t min_update_interval_nanos,
float delta_temp_per_bin, float max_error_limit,
uint64_t age_limit_nanos, float temp_sensitivity_limit,
float sensor_intercept_limit, bool over_temp_enable);
/*
* Sets the over-temp calibration model parameters.
*
* INPUTS:
* over_temp_cal: Over-temp main data structure.
* offset: Update values for the latest offset estimate (array).
* offset_temp_celsius: Measured temperature for the offset estimate.
* timestamp_nanos: Timestamp for the offset estimate [nanoseconds].
* temp_sensitivity: Modeled temperature sensitivity (array).
* sensor_intercept: Linear model intercept for the over-temp model (array).
* jump_start_model: When 'true' populates an empty 'model_data' array using
* valid input model parameters.
*
* NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
*/
void overTempCalSetModel(struct OverTempCal *over_temp_cal, const float *offset,
float offset_temp_celsius, uint64_t timestamp_nanos,
const float *temp_sensitivity,
const float *sensor_intercept, bool jump_start_model);
/*
* Gets the over-temp calibration model parameters.
*
* INPUTS:
* over_temp_cal: Over-temp data structure.
* OUTPUTS:
* offset: Offset values for the latest offset estimate (array).
* offset_temp_celsius: Measured temperature for the offset estimate.
* timestamp_nanos: Timestamp for the offset estimate [nanoseconds].
* temp_sensitivity: Modeled temperature sensitivity (array).
* sensor_intercept: Linear model intercept for the over-temp model (array).
*
* NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
*/
void overTempCalGetModel(struct OverTempCal *over_temp_cal, float *offset,
float *offset_temp_celsius, uint64_t *timestamp_nanos,
float *temp_sensitivity, float *sensor_intercept);
/*
* Removes the over-temp compensated offset from the input sensor data.
*
* INPUTS:
* over_temp_cal: Over-temp data structure.
* timestamp_nanos: Timestamp of the sensor estimate update.
* xi, yi, zi: 3-axis sensor data to be compensated.
* OUTPUTS:
* xo, yo, zo: 3-axis sensor data that has been compensated.
*/
void overTempCalRemoveOffset(struct OverTempCal *over_temp_cal,
uint64_t timestamp_nanos, float xi, float yi,
float zi, float *xo, float *yo, float *zo);
// Returns true when a new over-temp model update is available; and the
// 'new_overtemp_model_available' flag is reset.
bool overTempCalNewModelUpdateAvailable(struct OverTempCal *over_temp_cal);
/*
* Updates the sensor's offset estimate and conditionally assimilates it into
* the over-temp model data set, 'model_data'.
*
* INPUTS:
* over_temp_cal: Over-temp data structure.
* timestamp_nanos: Timestamp of the sensor estimate update.
* offset: 3-axis sensor data to be compensated (array).
* temperature_celsius: Measured temperature for the new sensor estimate.
*
* NOTE: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
*/
void overTempCalUpdateSensorEstimate(struct OverTempCal *over_temp_cal,
uint64_t timestamp_nanos,
const float *offset,
float temperature_celsius);
// Updates the temperature at which the offset compensation is performed (i.e.,
// the current measured temperature value). This function is provided mainly for
// flexibility since temperature updates may come in from a source other than
// the sensor itself, and at a different rate.
void overTempCalSetTemperature(struct OverTempCal *over_temp_cal,
uint64_t timestamp_nanos,
float temperature_celsius);
/*
* Computes the maximum absolute error between the 'model_data' estimates and
* the estimate determined by the input model parameters.
* max_error (over all i)
* |model_data[i]->offset_xyz -
* getCompensatedOffset(model_data[i]->offset_temp_celsius,
* temp_sensitivity, sensor_intercept)|
*
* INPUTS:
* over_temp_cal: Over-temp data structure.
* temp_sensitivity: Model temperature sensitivity to test (array).
* sensor_intercept: Model intercept to test (array).
* OUTPUTS:
* max_error: Maximum absolute error for the candidate model (array).
*
* NOTE 1: Arrays are all 3-dimensional with indices: 0=x, 1=y, 2=z.
* NOTE 2: This function is provided for testing purposes.
*/
void getModelError(const struct OverTempCal *over_temp_cal,
const float *temp_sensitivity, const float *sensor_intercept,
float *max_error);
#ifdef OVERTEMPCAL_DBG_ENABLED
// This debug printout function assumes the input sensor data is a gyroscope
// [rad/sec].
void overTempCalDebugPrint(struct OverTempCal *over_temp_cal,
uint64_t timestamp_nanos);
#endif // OVERTEMPCAL_DBG_ENABLED
#ifdef __cplusplus
}
#endif
#endif // LOCATION_LBS_CONTEXTHUB_NANOAPPS_CALIBRATION_OVER_TEMP_OVER_TEMP_CAL_H_