nano_calibration.cc revision 3e72c615f0f33f3c7d90de16275c0cae24f23ba3
1/* 2 * Copyright (C) 2017 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#include "nano_calibration.h" 17 18#include <cmath> 19#include <cstring> 20 21#ifdef DIVERSITY_CHECK_ENABLED 22#include "calibration/common/diversity_checker.h" 23#endif // DIVERSITY_CHECK_ENABLED 24 25#include "calibration/util/cal_log.h" 26#include "chre/util/nanoapp/log.h" 27#include "chre/util/time.h" 28 29namespace nano_calibration { 30 31namespace { 32#ifdef NANO_SENSOR_CAL_DBG_ENABLED 33#define NANO_CAL_LOG(tag, format, ...) CAL_DEBUG_LOG(tag, format, ##__VA_ARGS__) 34#else 35#define NANO_CAL_LOG(tag, format, ...) chreLogNull(format, ##__VA_ARGS__) 36#endif 37 38#ifdef SPHERE_FIT_ENABLED 39constexpr size_t kSamplesToAverageForOdrEstimateMag = 10; 40 41// Unit conversion: nanoseconds to seconds. 42constexpr float kNanosToSec = 1.0e-9f; 43 44 45// Helper function that estimates the ODR based on the incoming data timestamp. 46void SamplingRateEstimate(struct SampleRateData *sample_rate_data, 47 float *mean_sampling_rate_hz, 48 uint64_t timestamp_nanos, 49 bool reset_stats) { 50 // If 'mean_sampling_rate_hz' is not nullptr then this function just reads 51 // out the estimate of the sampling rate. 52 if (mean_sampling_rate_hz != nullptr) { 53 if (sample_rate_data->num_samples > 1 && 54 sample_rate_data->time_delta_accumulator > 0) { 55 // Computes the final mean calculation. 56 *mean_sampling_rate_hz = 57 sample_rate_data->num_samples / 58 (static_cast<float>(sample_rate_data->time_delta_accumulator) * 59 kNanosToSec); 60 } else { 61 // Not enough samples to compute a valid sample rate estimate. Indicate 62 // this with a -1 value. 63 *mean_sampling_rate_hz = -1.0f; 64 } 65 reset_stats = true; 66 } 67 68 // Resets the sampling rate mean estimator data. 69 if (reset_stats) { 70 sample_rate_data->last_timestamp_nanos = 0; 71 sample_rate_data->time_delta_accumulator = 0; 72 sample_rate_data->num_samples = 0; 73 return; 74 } 75 76 // Skip adding this data to the accumulator if: 77 // 1. A bad timestamp was received (i.e., time not monotonic). 78 // 2. 'last_timestamp_nanos' is zero. 79 if (timestamp_nanos <= sample_rate_data->last_timestamp_nanos || 80 sample_rate_data->last_timestamp_nanos == 0) { 81 sample_rate_data->last_timestamp_nanos = timestamp_nanos; 82 return; 83 } 84 85 // Increments the number of samples. 86 sample_rate_data->num_samples++; 87 88 // Accumulate the time steps. 89 sample_rate_data->time_delta_accumulator += timestamp_nanos - 90 sample_rate_data->last_timestamp_nanos; 91 sample_rate_data->last_timestamp_nanos = timestamp_nanos; 92} 93#endif // SPHERE_FIT_ENABLED 94 95// Helper function that resets calibration data to a known initial state. 96void ResetCalParams(struct ashCalParams *cal_params) { 97 // Puts 'cal_params' into a known "default" pass-through state (i.e., 98 // calibration data will not influence sensor streams). 99 memset(cal_params, 0, sizeof(struct ashCalParams)); 100 101 // Sets 'scaleFactor' to unity. 102 cal_params->scaleFactor[0] = 1.0f; 103 cal_params->scaleFactor[1] = 1.0f; 104 cal_params->scaleFactor[2] = 1.0f; 105} 106 107// Helper function that resets calibration info to a known initial state. 108void ResetCalInfo(struct ashCalInfo *cal_info) { 109 // Puts 'cal_info' into a known "default" pass-through state (i.e., 110 // calibration info will not influence sensor streams). 111 memset(cal_info, 0, sizeof(struct ashCalInfo)); 112 113 // Sets 'compMatrix' to the Identity matrix. 114 cal_info->compMatrix[0] = 1.0f; 115 cal_info->compMatrix[4] = 1.0f; 116 cal_info->compMatrix[8] = 1.0f; 117 118 cal_info->accuracy = ASH_CAL_ACCURACY_MEDIUM; 119} 120 121// Helper function to print out calibration data. 122void PrintAshCalParams(const struct ashCalParams &cal_params, const char *tag) { 123 NANO_CAL_LOG(tag, "Offset | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 124 cal_params.offset[0], cal_params.offset[1], cal_params.offset[2], 125 cal_params.offsetTempCelsius); 126 127 NANO_CAL_LOG( 128 tag, "Temp Sensitivity | Intercept: %.6f, %.6f, %.6f | %.6f, %.6f, %.6f", 129 cal_params.tempSensitivity[0], cal_params.tempSensitivity[1], 130 cal_params.tempSensitivity[2], cal_params.tempIntercept[0], 131 cal_params.tempIntercept[1], cal_params.tempIntercept[2]); 132 133 NANO_CAL_LOG(tag, "Scale Factor: %.6f, %.6f, %.6f", cal_params.scaleFactor[0], 134 cal_params.scaleFactor[1], cal_params.scaleFactor[2]); 135 136 NANO_CAL_LOG(tag, "Cross-Axis in [yx, zx, zy] order: %.6f, %.6f, %.6f", 137 cal_params.crossAxis[0], cal_params.crossAxis[1], 138 cal_params.crossAxis[2]); 139} 140 141// Detects and converts Factory Calibration data into a format consumable by the 142// runtime accelerometer calibration algorithm. 143#ifdef ACCEL_CAL_ENABLED 144void HandleAccelFactoryCalibration(struct ashCalParams *cal_params) { 145 // Checks for factory calibration data and performs any processing on the 146 // input to make it compatible with this runtime algorithm. NOTE: Factory 147 // calibrations are distinguished by 'offsetSource'=ASH_CAL_PARAMS_SOURCE_NONE 148 // and 'offsetTempCelsiusSource'=ASH_CAL_PARAMS_SOURCE_FACTORY. 149 bool factory_cal_detected = 150 cal_params->offsetSource == ASH_CAL_PARAMS_SOURCE_NONE && 151 cal_params->offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_FACTORY; 152 153 if (factory_cal_detected) { 154 // Prints the received factory data. 155 PrintAshCalParams(*cal_params,"[NanoSensorCal:ACCEL_FACTORY_CAL]"); 156 157 // Sets the parameter source to runtime calibration. 158 cal_params->offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 159 cal_params->offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 160 161 // Ensures that the offset vector is zero in case it has been overwritten by 162 // mistake. 163 memset(cal_params->offset, 0, sizeof(cal_params->offset)); 164 165 //TODO: Incorporate over-temperature offset compensation. 166 } 167} 168#endif // ACCEL_CAL_ENABLED 169 170// Detects and converts Factory Calibration data into a format consumable by the 171// runtime gyroscope calibration algorithm. 172#ifdef GYRO_CAL_ENABLED 173void HandleGyroFactoryCalibration(struct ashCalParams *cal_params) { 174#ifdef OVERTEMPCAL_GYRO_ENABLED 175 // Checks for factory calibration data and performs any processing on the 176 // input to make it compatible with this runtime algorithm. NOTE: Factory 177 // calibrations are distinguished by 'offsetSource'=ASH_CAL_PARAMS_SOURCE_NONE 178 // and 'offsetTempCelsiusSource'=ASH_CAL_PARAMS_SOURCE_FACTORY 179 bool factory_cal_detected = 180 cal_params->offsetSource == ASH_CAL_PARAMS_SOURCE_NONE && 181 cal_params->offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_FACTORY && 182 cal_params->tempSensitivitySource == ASH_CAL_PARAMS_SOURCE_FACTORY && 183 cal_params->tempInterceptSource == ASH_CAL_PARAMS_SOURCE_FACTORY; 184 185 if (factory_cal_detected) { 186 // Prints the received factory data. 187 PrintAshCalParams(*cal_params,"[NanoSensorCal:GYRO_FACTORY_CAL]"); 188 189 // Sets the parameter source to runtime calibration. 190 cal_params->offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 191 cal_params->offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 192 cal_params->tempSensitivitySource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 193 cal_params->tempInterceptSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 194 195 // Since the Factory-Cal OTC model is computed from raw measured data and 196 // the 'offset' at 'offsetTempCelsius' is removed from the input sensor 197 // stream, the intercept must be adjusted so that the runtime OTC produces a 198 // zero offset vector at 'offsetTempCelsius'. 199 for (size_t i = 0; i < 3; i++) { 200 // Shifts the OTC linear model intercept by 'offset_at_offsetTempCelsius'. 201 float offset_at_offsetTempCelsius = 202 cal_params->tempSensitivity[i] * cal_params->offsetTempCelsius + 203 cal_params->tempIntercept[i]; 204 cal_params->tempIntercept[i] -= offset_at_offsetTempCelsius; 205 } 206 207 // Ensures that the offset vector is zero in case it has been overwritten by 208 // mistake. 209 memset(cal_params->offset, 0, sizeof(cal_params->offset)); 210 } 211#else 212 // Checks for factory calibration data and performs any processing on the 213 // input to make it compatible with this runtime algorithm. 214 bool factory_cal_detected = 215 cal_params->offsetSource == ASH_CAL_PARAMS_SOURCE_NONE && 216 cal_params->offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_FACTORY; 217 218 if (factory_cal_detected) { 219 // Prints the received factory data. 220 PrintAshCalParams(*cal_params,"[NanoSensorCal:GYRO_FACTORY_CAL]"); 221 222 // Sets the parameter source to runtime calibration. 223 cal_params->offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 224 cal_params->offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 225 226 // Ensures that the offset vector is zero in case it has been overwritten by 227 // mistake. 228 memset(cal_params->offset, 0, sizeof(cal_params->offset)); 229 } 230#endif // OVERTEMPCAL_GYRO_ENABLED 231} 232#endif // GYRO_CAL_ENABLED 233 234// Detects and converts Factory Calibration data into a format consumable by the 235// runtime magnetometer calibration algorithm. 236#ifdef MAG_CAL_ENABLED 237void HandleMagFactoryCalibration(struct ashCalParams *cal_params) { 238 // Checks for factory calibration data and performs any processing on the 239 // input to make it compatible with this runtime algorithm. 240 bool factory_cal_detected = 241 cal_params->offsetSource == ASH_CAL_PARAMS_SOURCE_NONE && 242 cal_params->offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_FACTORY; 243 244 if (factory_cal_detected) { 245 // Print the received factory data. 246 PrintAshCalParams(*cal_params,"[NanoSensorCal:MAG_FACTORY_CAL]"); 247 248 // Sets the parameter source to runtime calibration. 249 cal_params->offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 250 cal_params->offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 251 252 // Ensures that the offset vector is zero in case it has been overwritten by 253 // mistake. 254 memset(cal_params->offset, 0, sizeof(cal_params->offset)); 255 } 256} 257#endif // MAG_CAL_ENABLED 258 259} // anonymous namespace 260 261NanoSensorCal::NanoSensorCal() { 262 // Initializes the calibration data to a known default state. 263 ResetCalParams(&accel_cal_params_); 264 ResetCalParams(&gyro_cal_params_); 265 ResetCalParams(&mag_cal_params_); 266} 267 268void NanoSensorCal::Initialize() { 269 NANO_CAL_LOG("[NanoSensorCal]", "Initialized."); 270 271#ifdef ACCEL_CAL_ENABLED 272 // Initializes the accelerometer offset calibration algorithm. 273 accelCalInit(&accel_cal_, 274 800000000, // Stillness Time in ns (0.8s) 275 5, // Minimum Sample Number 276 0.00025f, // Threshold 277 15, // nx bucket count 278 15, // nxb bucket count 279 15, // ny bucket count 280 15, // nyb bucket count 281 15, // nz bucket count 282 15, // nzb bucket count 283 15); // nle bucket count 284 285 // Retrieves stored calibration data using the ASH API. 286 LoadAshAccelCal(); 287#endif // ACCEL_CAL_ENABLED 288 289#ifdef GYRO_CAL_ENABLED 290 // Initializes the gyroscope offset calibration algorithm. 291 gyroCalInit( 292 &gyro_cal_, 293 5e9, // min stillness period = 5 seconds 294 6e9, // max stillness period = 6 seconds 295 0, 0, 0, // initial bias offset calibration 296 0, // time stamp of initial bias calibration 297 1.5e9, // analysis window length = 1.5 seconds 298 6.3e-6f, // gyroscope variance threshold [rad/sec]^2 299 1.3e-6f, // gyroscope confidence delta [rad/sec]^2 300 3.1e-4f, // accelerometer variance threshold [m/sec^2]^2 301 6.2e-5f, // accelerometer confidence delta [m/sec^2]^2 302 3.0f, // magnetometer variance threshold [uT]^2 303 0.6f, // magnetometer confidence delta [uT]^2 304 0.95f, // stillness threshold [0,1] 305 60.0e-3f * kPi / 180.0f, // stillness mean variation limit [rad/sec] 306 1.5f, // maximum temperature deviation during stillness [C] 307 true); // gyro calibration enable 308 309#ifdef OVERTEMPCAL_GYRO_ENABLED 310 // Initializes the over-temperature compensated gyroscope (OTC-Gyro) offset 311 // calibration algorithm. 312 overTempCalInit( 313 &over_temp_gyro_cal_, 314 5, // Min num of points to enable model update 315 5000000000, // Min model update interval [nsec] 316 0.75f, // Temperature span of bin method [C] 317 80.0e-3f * kPi / 180.0f, // Model fit tolerance [rad/sec] 318 250.0e-3f * kPi / 180.0f, // Outlier rejection tolerance [rad/sec] 319 172800000000000, // Model data point age limit [nsec] 320 100.0e-3f * kPi / 180.0f, // Limit for temp. sensitivity [rad/sec/C] 321 3.0f * kPi / 180.0f, // Limit for model intercept parameter [rad/sec] 322 3.0e-3f * kPi / 180.0f, // Significant offset change [rad/sec] 323 true); // Over-temp compensation enable 324#endif // OVERTEMPCAL_GYRO_ENABLED 325 326 // Retrieves stored calibration data using the ASH API. 327 LoadAshGyroCal(); 328#endif // GYRO_CAL_ENABLED 329 330#ifdef MAG_CAL_ENABLED 331#ifdef DIVERSITY_CHECK_ENABLED 332#ifdef SPHERE_FIT_ENABLED 333 // Full Sphere Fit. 334 // TODO: Replace function parameters with a struct, to avoid swapping them per 335 // accident. 336 initMagCalSphere(&mag_cal_sphere_, 337 0.0f, 0.0f, 0.0f, // bias x, y, z. 338 1.0f, 0.0f, 0.0f, // c00, c01, c02. 339 0.0f, 1.0f, 0.0f, // c10, c11, c12. 340 0.0f, 0.0f, 1.0f, // c20, c21, c22. 341 7357000, // min_batch_window_in_micros 342 15, // min_num_diverse_vectors. 343 1, // max_num_max_distance. 344 5.0f, // var_threshold. 345 8.0f, // max_min_threshold. 346 48.f, // local_field. 347 0.49f, // threshold_tuning_param. 348 2.5f); // max_distance_tuning_param. 349 magCalSphereOdrUpdate(&mag_cal_sphere_, 50 /* Default sample rate Hz */); 350 351 // ODR init. 352 memset(&mag_sample_rate_data_, 0, sizeof(SampleRateData)); 353#endif // SPHERE_FIT_ENABLED 354 355 // Initializes the magnetometer offset calibration algorithm (with diversity 356 // checker). 357 initMagCal(&mag_cal_, 358 0.0f, 0.0f, 0.0f, // bias x, y, z 359 1.0f, 0.0f, 0.0f, // c00, c01, c02 360 0.0f, 1.0f, 0.0f, // c10, c11, c12 361 0.0f, 0.0f, 1.0f, // c20, c21, c22 362 3000000, // min_batch_window_in_micros 363 8, // min_num_diverse_vectors 364 1, // max_num_max_distance 365 6.0f, // var_threshold 366 10.0f, // max_min_threshold 367 48.f, // local_field 368 0.49f, // threshold_tuning_param 369 2.5f); // max_distance_tuning_param 370#else 371 // Initializes the magnetometer offset calibration algorithm. 372 initMagCal(&mag_cal_, 373 0.0f, 0.0f, 0.0f, // bias x, y, z 374 1.0f, 0.0f, 0.0f, // c00, c01, c02 375 0.0f, 1.0f, 0.0f, // c10, c11, c12 376 0.0f, 0.0f, 1.0f, // c20, c21, c22 377 3000000); // min_batch_window_in_micros 378#endif // DIVERSITY_CHECK_ENABLED 379 380 // Retrieves stored calibration data using the ASH API. 381 LoadAshMagCal(); 382#endif // MAG_CAL_ENABLED 383 384 // Resets the calibration ready flags. 385 accel_calibration_ready_ = false; 386 gyro_calibration_ready_ = false; 387 mag_calibration_ready_ = false; 388 389 // NanoSensorCal algorithms have been initialized. 390 nanosensorcal_initialized_ = true; 391} 392 393// TODO: Evaluate the impact of sensor batching on the performance of the 394// calibration algorithms (versus processing on a per-sample basis). For 395// example, some of the internal algorithms rely on the temperature signal to 396// determine when temperature variation is too high to perform calibrations. 397void NanoSensorCal::HandleSensorSamples( 398 uint16_t event_type, const chreSensorThreeAxisData *event_data) { 399 if (nanosensorcal_initialized_) { 400 HandleSensorSamplesAccelCal(event_type, event_data); 401 HandleSensorSamplesGyroCal(event_type, event_data); 402 HandleSensorSamplesMagCal(event_type, event_data); 403 } 404} 405 406void NanoSensorCal::HandleTemperatureSamples( 407 uint16_t event_type, const chreSensorFloatData *event_data) { 408 if (!nanosensorcal_initialized_) 409 return; 410 411 // Takes the mean of the batched temperature samples and delivers it to the 412 // calibration algorithms. The latency setting determines the minimum update 413 // interval. 414 if (event_type == CHRE_EVENT_SENSOR_ACCELEROMETER_TEMPERATURE_DATA) { 415 const auto header = event_data->header; 416 const auto *data = event_data->readings; 417 uint64_t timestamp_nanos = header.baseTimestamp; 418 float mean_temperature_celsius = 0.0f; 419 for (size_t i = 0; i < header.readingCount; i++) { 420 timestamp_nanos += data[i].timestampDelta; 421 mean_temperature_celsius += data[i].value; 422 } 423 mean_temperature_celsius /= header.readingCount; 424 temperature_celsius_ = mean_temperature_celsius; 425 426#ifdef GYRO_CAL_ENABLED 427#ifdef OVERTEMPCAL_GYRO_ENABLED 428 // Updates the OTC gyro temperature. 429 overTempCalSetTemperature(&over_temp_gyro_cal_, timestamp_nanos, 430 temperature_celsius_); 431#endif // OVERTEMPCAL_GYRO_ENABLED 432#endif // GYRO_CAL_ENABLED 433 } 434} 435 436void NanoSensorCal::HandleSensorSamplesAccelCal( 437 uint16_t event_type, const chreSensorThreeAxisData *event_data) { 438#ifdef ACCEL_CAL_ENABLED 439 if (event_type == CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA) { 440 const auto header = event_data->header; 441 const auto *data = event_data->readings; 442 uint64_t timestamp_nanos = header.baseTimestamp; 443 for (size_t i = 0; i < header.readingCount; i++) { 444 timestamp_nanos += data[i].timestampDelta; 445 accelCalRun(&accel_cal_, timestamp_nanos, 446 data[i].v[0], // x-axis data [m/sec^2] 447 data[i].v[1], // y-axis data [m/sec^2] 448 data[i].v[2], // z-axis data [m/sec^2] 449 temperature_celsius_); 450 } 451 // Checks for an accelerometer bias calibration change. 452 float offset[3] = {0.0f, 0.0f, 0.0f}; 453 if (accelCalUpdateBias(&accel_cal_, &offset[0], &offset[1], &offset[2])) { 454 // Provides a new accelerometer calibration update. 455 accel_calibration_ready_ = true; 456 NotifyAshAccelCal(); 457 } 458 459#ifdef ACCEL_CAL_DBG_ENABLED 460 // Print debug data report. 461 accelCalDebPrint(&accel_cal_, temperature_celsius_); 462#endif 463 } 464#endif // ACCEL_CAL_ENABLED 465} 466 467// TODO: Factor common code to shorten function and improve readability. 468void NanoSensorCal::HandleSensorSamplesGyroCal( 469 uint16_t event_type, const chreSensorThreeAxisData *event_data) { 470#ifdef GYRO_CAL_ENABLED 471 switch (event_type) { 472 case CHRE_EVENT_SENSOR_UNCALIBRATED_ACCELEROMETER_DATA: { 473 const auto header = event_data->header; 474 const auto *data = event_data->readings; 475 uint64_t timestamp_nanos = header.baseTimestamp; 476 for (size_t i = 0; i < header.readingCount; i++) { 477 timestamp_nanos += data[i].timestampDelta; 478 gyroCalUpdateAccel(&gyro_cal_, timestamp_nanos, 479 data[i].v[0], // x-axis data [m/sec^2] 480 data[i].v[1], // y-axis data [m/sec^2] 481 data[i].v[2]); // z-axis data [m/sec^2] 482 } 483 break; 484 } 485 486 case CHRE_EVENT_SENSOR_UNCALIBRATED_GYROSCOPE_DATA: { 487 const auto header = event_data->header; 488 const auto *data = event_data->readings; 489 uint64_t timestamp_nanos = header.baseTimestamp; 490 for (size_t i = 0; i < header.readingCount; i++) { 491 timestamp_nanos += data[i].timestampDelta; 492 gyroCalUpdateGyro(&gyro_cal_, timestamp_nanos, 493 data[i].v[0], // x-axis data [rad/sec] 494 data[i].v[1], // y-axis data [rad/sec] 495 data[i].v[2], // z-axis data [rad/sec] 496 temperature_celsius_); 497 } 498 499 if (gyroCalNewBiasAvailable(&gyro_cal_)) { 500#ifdef OVERTEMPCAL_GYRO_ENABLED 501 // Sends new GyroCal offset estimate to the OTC-Gyro. 502 float offset[3] = {0.0f, 0.0f, 0.0f}; 503 float offset_temperature_celsius = 0.0f; 504 gyroCalGetBias(&gyro_cal_, &offset[0], &offset[1], &offset[2], 505 &offset_temperature_celsius); 506 overTempCalUpdateSensorEstimate(&over_temp_gyro_cal_, timestamp_nanos, 507 offset, offset_temperature_celsius); 508#else 509 // Provides a new gyroscope calibration update. 510 gyro_calibration_ready_ = true; 511 NotifyAshGyroCal(); 512#endif // OVERTEMPCAL_GYRO_ENABLED 513 } 514 515#ifdef OVERTEMPCAL_GYRO_ENABLED 516 // Limits the frequency of the offset update checks. 517 if ((timestamp_nanos - otc_offset_timer_nanos_) >= 518 kOtcGyroOffsetMaxUpdateIntervalNanos) { 519 otc_offset_timer_nanos_ = timestamp_nanos; 520 521 // Checks OTC for new calibration model update. 522 bool new_otc_model_update = 523 overTempCalNewModelUpdateAvailable(&over_temp_gyro_cal_); 524 525 // Checks for a change in the OTC-Gyro temperature compensated offset 526 // estimate. 527 bool new_otc_offset = 528 overTempCalNewOffsetAvailable(&over_temp_gyro_cal_); 529 530 if (new_otc_model_update || new_otc_offset) { 531 // Provides a temperature compensated gyroscope calibration update. 532 gyro_calibration_ready_ = true; 533 NotifyAshGyroCal(); 534 } 535 } 536#endif // OVERTEMPCAL_GYRO_ENABLED 537 538#ifdef GYRO_CAL_DBG_ENABLED 539 // Print debug data report. 540 gyroCalDebugPrint(&gyro_cal_, timestamp_nanos); 541#endif // GYRO_CAL_DBG_ENABLED 542 543#if defined(OVERTEMPCAL_GYRO_ENABLED) && defined(OVERTEMPCAL_DBG_ENABLED) 544 // Print debug data report. 545 overTempCalDebugPrint(&over_temp_gyro_cal_, timestamp_nanos); 546#endif // OVERTEMPCAL_GYRO_ENABLED && OVERTEMPCAL_DBG_ENABLED 547 break; 548 } 549 550 case CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA: { 551 const auto header = event_data->header; 552 const auto *data = event_data->readings; 553 uint64_t timestamp_nanos = header.baseTimestamp; 554 for (size_t i = 0; i < header.readingCount; i++) { 555 timestamp_nanos += data[i].timestampDelta; 556 gyroCalUpdateMag(&gyro_cal_, timestamp_nanos, 557 data[i].v[0], // x-axis data [uT] 558 data[i].v[1], // y-axis data [uT] 559 data[i].v[2]); // z-axis data [uT] 560 } 561 break; 562 } 563 564 default: 565 break; 566 } 567#endif // GYRO_CAL_ENABLED 568} 569 570void NanoSensorCal::HandleSensorSamplesMagCal( 571 uint16_t event_type, const chreSensorThreeAxisData *event_data) { 572#ifdef MAG_CAL_ENABLED 573 if (event_type == CHRE_EVENT_SENSOR_UNCALIBRATED_GEOMAGNETIC_FIELD_DATA) { 574 const auto header = event_data->header; 575 const auto *data = event_data->readings; 576 uint64_t timestamp_nanos = header.baseTimestamp; 577 MagUpdateFlags new_calibration_update_mag_cal = MagUpdate::NO_UPDATE; 578 579 for (size_t i = 0; i < header.readingCount; i++) { 580 timestamp_nanos += data[i].timestampDelta; 581 582 // Sets the flag to indicate a new calibration update. 583 new_calibration_update_mag_cal |= magCalUpdate( 584 &mag_cal_, 585 static_cast<uint64_t>(timestamp_nanos * kNanoToMicroseconds), 586 data[i].v[0], // x-axis data [uT] 587 data[i].v[1], // y-axis data [uT] 588 data[i].v[2]); // z-axis data [uT] 589 590#ifdef SPHERE_FIT_ENABLED 591 // Sphere Fit Algo Part. 592 593 // getting ODR. 594 if (mag_sample_rate_data_.num_samples < 595 kSamplesToAverageForOdrEstimateMag) { 596 SamplingRateEstimate(&mag_sample_rate_data_, nullptr, timestamp_nanos, 597 false); 598 } else { 599 SamplingRateEstimate(&mag_sample_rate_data_, &mag_odr_estimate_hz_, 600 0, true); 601 602 // Sphere fit ODR update. 603 magCalSphereOdrUpdate(&mag_cal_sphere_, mag_odr_estimate_hz_); 604 } 605 606 // Running Sphere fit, and getting trigger. 607 new_calibration_update_mag_cal |= magCalSphereUpdate( 608 &mag_cal_sphere_, 609 static_cast<uint64_t>(timestamp_nanos * kNanoToMicroseconds), 610 data[i].v[0], // x-axis data [uT] 611 data[i].v[1], // y-axis data [uT] 612 data[i].v[2]); // z-axis data [uT] 613#endif // SPHERE_FIT_ENABLED 614 } 615 616 if ((MagUpdate::UPDATE_BIAS & new_calibration_update_mag_cal) || 617 (MagUpdate::UPDATE_SPHERE_FIT & new_calibration_update_mag_cal)) { 618 // Sets the flag to indicate a new calibration update is pending. 619 mag_calibration_ready_ = true; 620 NotifyAshMagCal(new_calibration_update_mag_cal); 621 } 622 } 623#endif // MAG_CAL_ENABLED 624} 625 626void NanoSensorCal::GetAccelerometerCalibration( 627 struct ashCalParams *accel_cal_params) const { 628 // Resets the calibration ready flag; and returns the calibration data. 629 accel_calibration_ready_ = false; 630 memcpy(accel_cal_params, &accel_cal_params_, sizeof(struct ashCalParams)); 631} 632 633void NanoSensorCal::GetGyroscopeCalibration( 634 struct ashCalParams *gyro_cal_params) const { 635 // Resets the calibration ready flag; and returns the calibration data. 636 gyro_calibration_ready_ = false; 637 memcpy(gyro_cal_params, &gyro_cal_params_, sizeof(struct ashCalParams)); 638} 639 640void NanoSensorCal::GetMagnetometerCalibration( 641 struct ashCalParams *mag_cal_params) const { 642 // Resets the calibration ready flag; and returns the calibration data. 643 mag_calibration_ready_ = false; 644 memcpy(mag_cal_params, &mag_cal_params_, sizeof(struct ashCalParams)); 645} 646 647void NanoSensorCal::UpdateAccelCalParams() { 648#ifdef ACCEL_CAL_ENABLED 649 // Gets the accelerometer's offset vector and temperature. 650 accelCalUpdateBias(&accel_cal_, &accel_cal_params_.offset[0], 651 &accel_cal_params_.offset[1], &accel_cal_params_.offset[2]); 652 accel_cal_params_.offsetTempCelsius = temperature_celsius_; 653 654 // Sets the parameter source to runtime calibration. 655 accel_cal_params_.offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 656 accel_cal_params_.offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 657#endif // ACCEL_CAL_ENABLED 658} 659 660void NanoSensorCal::UpdateGyroCalParams() { 661#ifdef GYRO_CAL_ENABLED 662#ifdef OVERTEMPCAL_GYRO_ENABLED 663 // Gets the gyroscope's offset vector and temperature; and OTC linear model 664 // parameters. 665 uint64_t timestamp_nanos = 0; 666 overTempCalGetModel(&over_temp_gyro_cal_, gyro_cal_params_.offset, 667 &gyro_cal_params_.offsetTempCelsius, ×tamp_nanos, 668 gyro_cal_params_.tempSensitivity, 669 gyro_cal_params_.tempIntercept); 670 671 // Sets the parameter source to runtime calibration. 672 gyro_cal_params_.offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 673 gyro_cal_params_.offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 674 gyro_cal_params_.tempSensitivitySource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 675 gyro_cal_params_.tempInterceptSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 676#else 677 // Gets the gyroscope's offset vector and temperature. 678 gyroCalGetBias(&gyro_cal_, &gyro_cal_params_.offset[0], 679 &gyro_cal_params_.offset[1], &gyro_cal_params_.offset[2], 680 &gyro_cal_params_.offsetTempCelsius); 681 682 // Sets the parameter source to runtime calibration. 683 gyro_cal_params_.offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 684 gyro_cal_params_.offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 685#endif // OVERTEMPCAL_GYRO_ENABLED 686#endif // GYRO_CAL_ENABLED 687} 688 689void NanoSensorCal::UpdateMagCalParams(MagUpdateFlags new_update) { 690#ifdef MAG_CAL_ENABLED 691 if (MagUpdate::UPDATE_SPHERE_FIT & new_update) { 692#ifdef SPHERE_FIT_ENABLED 693 // Updating the mag offset from sphere fit. 694 mag_cal_params_.offset[0] = mag_cal_sphere_.sphere_fit.sphere_param.bias[0]; 695 mag_cal_params_.offset[1] = mag_cal_sphere_.sphere_fit.sphere_param.bias[1]; 696 mag_cal_params_.offset[2] = mag_cal_sphere_.sphere_fit.sphere_param.bias[2]; 697 698 // Updating the Sphere Param. 699 mag_cal_params_.scaleFactor[0] = 700 mag_cal_sphere_.sphere_fit.sphere_param.scale_factor_x; 701 mag_cal_params_.scaleFactor[1] = 702 mag_cal_sphere_.sphere_fit.sphere_param.scale_factor_y; 703 mag_cal_params_.scaleFactor[2] = 704 mag_cal_sphere_.sphere_fit.sphere_param.scale_factor_z; 705 mag_cal_params_.crossAxis[0] = 706 mag_cal_sphere_.sphere_fit.sphere_param.skew_yx; 707 mag_cal_params_.crossAxis[1] = 708 mag_cal_sphere_.sphere_fit.sphere_param.skew_zx; 709 mag_cal_params_.crossAxis[2] = 710 mag_cal_sphere_.sphere_fit.sphere_param.skew_zy; 711 712 // Updating the temperature. 713 mag_cal_params_.offsetTempCelsius = temperature_celsius_; 714 715 // Sets the parameter source to runtime calibration. 716 mag_cal_params_.offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 717 mag_cal_params_.scaleFactorSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 718 mag_cal_params_.crossAxisSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 719 mag_cal_params_.offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 720#endif // SPHERE_FIT_ENABLED 721 } else if (MagUpdate::UPDATE_BIAS & new_update) { 722 // Gets the magnetometer's offset vector and temperature. 723 magCalGetBias(&mag_cal_, &mag_cal_params_.offset[0], 724 &mag_cal_params_.offset[1], &mag_cal_params_.offset[2]); 725 mag_cal_params_.offsetTempCelsius = temperature_celsius_; 726 727 // Sets the parameter source to runtime calibration. 728 mag_cal_params_.offsetSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 729 mag_cal_params_.offsetTempCelsiusSource = ASH_CAL_PARAMS_SOURCE_RUNTIME; 730 } 731#endif // MAG_CAL_ENABLED 732} 733 734void NanoSensorCal::LoadAshAccelCal() { 735#ifdef ACCEL_CAL_ENABLED 736 bool load_successful = false; 737 struct ashCalParams cal_params; 738 if (ashLoadCalibrationParams(CHRE_SENSOR_TYPE_ACCELEROMETER, &cal_params)) { 739 // Checks for and performs required processing on input factory cal data. 740 HandleAccelFactoryCalibration(&cal_params); 741 742 // Checks for valid calibration data. 743 if (cal_params.offsetSource == ASH_CAL_PARAMS_SOURCE_RUNTIME && 744 cal_params.offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_RUNTIME) { 745 // On a successful load, copies the new set of calibration parameters. 746 memcpy(&accel_cal_params_, &cal_params, sizeof(struct ashCalParams)); 747 748 // Sets the accelerometer algorithm's calibration data. 749 accelCalBiasSet(&accel_cal_, accel_cal_params_.offset[0], 750 accel_cal_params_.offset[1], accel_cal_params_.offset[2]); 751 load_successful = true; 752 } 753 } 754 755 if (load_successful) { 756 // Updates the calibration data with ASH. 757 NotifyAshAccelCal(); 758 759 // Prints recalled calibration data. 760 NANO_CAL_LOG("[NanoSensorCal:RECALL ACCEL]", 761 "Offset [m/sec^2] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 762 accel_cal_params_.offset[0], accel_cal_params_.offset[1], 763 accel_cal_params_.offset[2], 764 accel_cal_params_.offsetTempCelsius); 765 } else { 766 NANO_CAL_LOG("[NanoSensorCal:RECALL ACCEL]", 767 "Failed to recall accelerometer calibration data from " 768 "persistent memory."); 769 } 770#endif // ACCEL_CAL_ENABLED 771} 772 773void NanoSensorCal::LoadAshGyroCal() { 774#ifdef GYRO_CAL_ENABLED 775 bool load_successful = false; 776 struct ashCalParams cal_params; 777 if (ashLoadCalibrationParams(CHRE_SENSOR_TYPE_GYROSCOPE, &cal_params)) { 778 // Checks for and performs required processing on input factory cal data. 779 HandleGyroFactoryCalibration(&cal_params); 780 781#ifdef OVERTEMPCAL_GYRO_ENABLED 782 // Gyroscope offset calibration with over-temperature compensation (OTC) 783 // parameters were recalled. 784 if (cal_params.offsetSource == ASH_CAL_PARAMS_SOURCE_RUNTIME && 785 cal_params.offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_RUNTIME && 786 cal_params.tempSensitivitySource == ASH_CAL_PARAMS_SOURCE_RUNTIME && 787 cal_params.tempInterceptSource == ASH_CAL_PARAMS_SOURCE_RUNTIME) { 788 // On a successful load, copies the new set of calibration parameters. 789 memcpy(&gyro_cal_params_, &cal_params, sizeof(struct ashCalParams)); 790 791 // Sets the gyroscope algorithm's calibration data. 792 gyroCalSetBias(&gyro_cal_, gyro_cal_params_.offset[0], 793 gyro_cal_params_.offset[1], gyro_cal_params_.offset[2], 794 /*calibration_time_nanos=*/0); //TODO -- get accurate time. 795 overTempCalSetModel(&over_temp_gyro_cal_, 796 gyro_cal_params_.offset, 797 gyro_cal_params_.offsetTempCelsius, 798 /*timestamp_nanos=*/0, //TODO -- get accurate time. 799 gyro_cal_params_.tempSensitivity, 800 gyro_cal_params_.tempIntercept, 801 /*jump_start_model=*/true); 802 load_successful = true; 803 804 // Prints recalled calibration data. 805 NANO_CAL_LOG("[NanoSensorCal:RECALL OTC-GYRO]", 806 "Offset [rad/sec] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 807 gyro_cal_params_.offset[0], gyro_cal_params_.offset[1], 808 gyro_cal_params_.offset[2], 809 gyro_cal_params_.offsetTempCelsius); 810 NANO_CAL_LOG("[NanoSensorCal:RECALL OTC-GYRO]", 811 "Sensitivity [rad/sec/C] | Intercept [rad/sec]: %.6f, %.6f, " 812 "%.6f | %.6f, %.6f, %.6f", 813 gyro_cal_params_.tempSensitivity[0], 814 gyro_cal_params_.tempSensitivity[1], 815 gyro_cal_params_.tempSensitivity[2], 816 gyro_cal_params_.tempIntercept[0], 817 gyro_cal_params_.tempIntercept[1], 818 gyro_cal_params_.tempIntercept[2]); 819 } 820#else 821 // Gyroscope offset calibration parameters were recalled. 822 if (cal_params.offsetSource == ASH_CAL_PARAMS_SOURCE_RUNTIME && 823 cal_params.offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_RUNTIME) { 824 // On a successful load, copies the new set of calibration parameters. 825 memcpy(&gyro_cal_params_, &cal_params, sizeof(struct ashCalParams)); 826 827 // Sets the gyroscope algorithm's calibration data. 828 gyroCalSetBias(&gyro_cal_, gyro_cal_params_.offset[0], 829 gyro_cal_params_.offset[1], gyro_cal_params_.offset[2], 830 /*calibration_time_nanos=*/0); 831 load_successful = true; 832 833 // Prints recalled calibration data. 834 NANO_CAL_LOG("[NanoSensorCal:RECALL GYRO]", 835 "Offset [rad/sec] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 836 gyro_cal_params_.offset[0], gyro_cal_params_.offset[1], 837 gyro_cal_params_.offset[2], 838 gyro_cal_params_.offsetTempCelsius); 839 } 840#endif // OVERTEMPCAL_GYRO_ENABLED 841 } 842 843 if (load_successful) { 844 // Updates the calibration data with ASH. 845 NotifyAshGyroCal(); 846 } else { 847 NANO_CAL_LOG( 848 "[NanoSensorCal:RECALL GYRO]", 849 "Failed to recall gyroscope calibration data from persistent memory."); 850 } 851#endif // GYRO_CAL_ENABLED 852} 853 854void NanoSensorCal::LoadAshMagCal() { 855#ifdef MAG_CAL_ENABLED 856 bool load_successful = false; 857 struct ashCalParams cal_params; 858 if (ashLoadCalibrationParams(CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD, 859 &cal_params)) { 860 // Checks for and performs required processing on input factory cal data. 861 HandleMagFactoryCalibration(&cal_params); 862 863 // Checks for valid calibration data. 864 if (cal_params.offsetSource == ASH_CAL_PARAMS_SOURCE_RUNTIME && 865 cal_params.offsetTempCelsiusSource == ASH_CAL_PARAMS_SOURCE_RUNTIME) { 866 // On a successful load, copies the new set of calibration parameters. 867 memcpy(&mag_cal_params_, &cal_params, sizeof(struct ashCalParams)); 868 869 // Sets the magnetometer algorithm's calibration data. 870 magCalReset(&mag_cal_); // Resets the magnetometer's offset vector. 871 magCalAddBias(&mag_cal_, mag_cal_params_.offset[0], 872 mag_cal_params_.offset[1], mag_cal_params_.offset[2]); 873 874#ifdef SPHERE_FIT_ENABLED 875 // Sets Sphere Fit calibration data. 876 mag_cal_sphere_.sphere_fit.sphere_param.scale_factor_x = 877 mag_cal_params_.scaleFactor[0]; 878 mag_cal_sphere_.sphere_fit.sphere_param.scale_factor_y = 879 mag_cal_params_.scaleFactor[1]; 880 mag_cal_sphere_.sphere_fit.sphere_param.scale_factor_z = 881 mag_cal_params_.scaleFactor[2]; 882 mag_cal_sphere_.sphere_fit.sphere_param.skew_yx = 883 mag_cal_params_.crossAxis[0]; 884 mag_cal_sphere_.sphere_fit.sphere_param.skew_zx = 885 mag_cal_params_.crossAxis[1]; 886 mag_cal_sphere_.sphere_fit.sphere_param.skew_zy = 887 mag_cal_params_.crossAxis[2]; 888 mag_cal_sphere_.sphere_fit.sphere_param.bias[0] = 889 mag_cal_params_.offset[0]; 890 mag_cal_sphere_.sphere_fit.sphere_param.bias[1] = 891 mag_cal_params_.offset[1]; 892 mag_cal_sphere_.sphere_fit.sphere_param.bias[2] = 893 mag_cal_params_.offset[2]; 894#endif // SPHERE_FIT_ENABLED 895 load_successful = true; 896 } 897 } 898 899 if (load_successful) { 900 // Updates the calibration data with ASH. 901#ifdef SPHERE_FIT_ENABLED 902 NotifyAshMagCal(MagUpdate::UPDATE_SPHERE_FIT); 903#else 904 NotifyAshMagCal(MagUpdate::UPDATE_BIAS); 905#endif // SPHERE_FIT_ENABLED 906 907 // Prints recalled calibration data. 908 NANO_CAL_LOG("[NanoSensorCal:RECALL MAG]", 909 "Offset [uT] | Temp [Celsius] : %.3f, %.3f, %.3f | %.3f", 910 mag_cal_params_.offset[0], mag_cal_params_.offset[1], 911 mag_cal_params_.offset[2], mag_cal_params_.offsetTempCelsius); 912#ifdef SPHERE_FIT_ENABLED 913 NANO_CAL_LOG("[NanoSensorCal:RECALL MAG]", 914 "Scale Factor [%] | Cross Axis [%]: %.3f, %.3f, %.3f |" 915 " %.3f, %.3f, %.3f", 916 mag_cal_params_.scaleFactor[0], mag_cal_params_.scaleFactor[1], 917 mag_cal_params_.scaleFactor[2], mag_cal_params_.crossAxis[0], 918 mag_cal_params_.crossAxis[1], mag_cal_params_.crossAxis[2]); 919#endif // SPHERE_FIT_ENABLED 920 } else { 921 NANO_CAL_LOG("[NanoSensorCal:RECALL MAG]", 922 "Failed to recall Magnetometer calibration data from " 923 "persistent memory."); 924 } 925#endif // MAG_CAL_ENABLED 926} 927 928void NanoSensorCal::NotifyAshAccelCal() { 929#ifdef ACCEL_CAL_ENABLED 930 // Update ASH with the latest calibration data. 931 UpdateAccelCalParams(); 932 struct ashCalInfo cal_info; 933 ResetCalInfo(&cal_info); 934 memcpy(cal_info.bias, accel_cal_params_.offset, sizeof(cal_info.bias)); 935 cal_info.accuracy = ASH_CAL_ACCURACY_HIGH; 936 ashSetCalibration(CHRE_SENSOR_TYPE_ACCELEROMETER, &cal_info); 937 938 // TODO: Remove #if FACTORYCAL_IS_VERIFIED directives, which currently prevent 939 // saving calibration parameter updates to the sensor registry, after Factory 940 // Calibration testing has been fully qualified on hardware. 941#define FACTORYCAL_IS_VERIFIED 0 // Set to 0, blocks overwriting factory data. 942#if FACTORYCAL_IS_VERIFIED 943 // Store the calibration parameters using the ASH API. 944 if (ashSaveCalibrationParams(CHRE_SENSOR_TYPE_ACCELEROMETER, 945 &accel_cal_params_)) { 946 NANO_CAL_LOG("[NanoSensorCal:STORED ACCEL]", 947 "Offset [m/sec^2] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 948 accel_cal_params_.offset[0], accel_cal_params_.offset[1], 949 accel_cal_params_.offset[2], 950 accel_cal_params_.offsetTempCelsius); 951 } 952#endif // FACTORYCAL_IS_VERIFIED 953#endif // ACCEL_CAL_ENABLED 954} 955 956void NanoSensorCal::NotifyAshGyroCal() { 957#ifdef GYRO_CAL_ENABLED 958 // Update ASH with the latest calibration data. 959 UpdateGyroCalParams(); 960 struct ashCalInfo cal_info; 961 ResetCalInfo(&cal_info); 962 memcpy(cal_info.bias, gyro_cal_params_.offset, sizeof(cal_info.bias)); 963 cal_info.accuracy = ASH_CAL_ACCURACY_HIGH; 964 ashSetCalibration(CHRE_SENSOR_TYPE_GYROSCOPE, &cal_info); 965 966 // TODO: Remove #if FACTORYCAL_IS_VERIFIED directives, which currently prevent 967 // saving calibration parameter updates to the sensor registry, after Factory 968 // Calibration testing has been fully qualified on hardware. 969#if FACTORYCAL_IS_VERIFIED 970 // Store the calibration parameters using the ASH API. 971 if (ashSaveCalibrationParams(CHRE_SENSOR_TYPE_GYROSCOPE, &gyro_cal_params_)) { 972#ifdef OVERTEMPCAL_GYRO_ENABLED 973 NANO_CAL_LOG("[NanoSensorCal:STORED OTC-GYRO]", 974 "Offset [rad/sec] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 975 gyro_cal_params_.offset[0], gyro_cal_params_.offset[1], 976 gyro_cal_params_.offset[2], 977 gyro_cal_params_.offsetTempCelsius); 978 NANO_CAL_LOG( 979 "[NanoSensorCal:STORED OTC-GYRO]", 980 "Sensitivity [rad/sec/C] | Intercept [rad/sec]: %.6f, %.6f, " 981 "%.6f | %.6f, %.6f, %.6f", 982 gyro_cal_params_.tempSensitivity[0], 983 gyro_cal_params_.tempSensitivity[1], 984 gyro_cal_params_.tempSensitivity[2], gyro_cal_params_.tempIntercept[0], 985 gyro_cal_params_.tempIntercept[1], gyro_cal_params_.tempIntercept[2]); 986#else 987 NANO_CAL_LOG("[NanoSensorCal:STORED GYRO]", 988 "Offset [rad/sec] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 989 gyro_cal_params_.offset[0], gyro_cal_params_.offset[1], 990 gyro_cal_params_.offset[2], 991 gyro_cal_params_.offsetTempCelsius); 992#endif // OVERTEMPCAL_GYRO_ENABLED 993 } 994#endif // FACTORYCAL_IS_VERIFIED 995#endif // GYRO_CAL_ENABLED 996} 997 998void NanoSensorCal::NotifyAshMagCal(MagUpdateFlags new_update) { 999#ifdef MAG_CAL_ENABLED 1000 // Update ASH with the latest calibration data. 1001 UpdateMagCalParams(new_update); 1002 struct ashCalInfo cal_info; 1003 ResetCalInfo(&cal_info); 1004 memcpy(cal_info.bias, mag_cal_params_.offset, sizeof(cal_info.bias)); 1005 1006 // TODO: Adding Sphere Parameters to compensation matrix. 1007 cal_info.accuracy = ASH_CAL_ACCURACY_HIGH; 1008 ashSetCalibration(CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD, &cal_info); 1009 1010 // TODO: Remove #if FACTORYCAL_IS_VERIFIED directives, which currently prevent 1011 // saving calibration parameter updates to the sensor registry, after Factory 1012 // Calibration testing has been fully qualified on hardware. 1013#if FACTORYCAL_IS_VERIFIED 1014 // Store the calibration parameters using the ASH API. 1015 if (ashSaveCalibrationParams(CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD, 1016 &mag_cal_params_)) { 1017 NANO_CAL_LOG("[NanoSensorCal:STORED MAG]", 1018 "Offset [uT] | Temp [Celsius]: %.6f, %.6f, %.6f | %.6f", 1019 mag_cal_params_.offset[0], mag_cal_params_.offset[1], 1020 mag_cal_params_.offset[2], mag_cal_params_.offsetTempCelsius); 1021#ifdef SPHERE_FIT_ENABLED 1022 NANO_CAL_LOG("[NanoSensorCal:STORED MAG]", 1023 "Scale Factor [%] | Cross Axis [%]: %.3f, %.3f, %.3f | " 1024 " %.3f, %.3f, %.3f", 1025 mag_cal_params_.scaleFactor[0], mag_cal_params_.scaleFactor[1], 1026 mag_cal_params_.scaleFactor[2], mag_cal_params_.crossAxis[0], 1027 mag_cal_params_.crossAxis[1], mag_cal_params_.crossAxis[2]); 1028#endif // SPHERE_FIT_ENABLED 1029 } 1030#endif // FACTORYCAL_IS_VERIFIED 1031#endif // MAG_CAL_ENABLED 1032} 1033 1034} // namespace nano_calibration 1035