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, &timestamp_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