11#define D2R 0.017453292519943295f
12#define R2D 57.29577951308232f
13#define STILL_GYRO (3.0f * D2R)
14#define ACCEL_STILL_MS 2000u
17#define GYRO_MAX_RESTARTS 3u
18#define COMPASS_MIN_SAMPLES 150u
19#define COMPASS_KEEP 256u
53 double N[6][6],
b[6]; uint32_t
n;
62 memset(
s, 0,
sizeof *
s);
63 for (
int i = 0; i < 3; ++i) {
64 s->accel.scale[i] = 1.0f;
65 s->compass.scale[i] = 1.0f;
66 s->compass.hi[i] = hi ? hi[i] : 0.0f;
68 s->battery.scale_v = 110.0f;
69 s->battery.scale_i = 1.0f;
83 const uint32_t
bit = 1u << (type - 1u);
114static void fail(uint8_t reason)
150uint8_t
cal_start(uint8_t type, uint8_t option, uint8_t armed, uint32_t now, uint8_t *reason)
190 memset(&
A, 0,
sizeof A);
194 memset(&
L, 0,
sizeof L);
201 memset(&
G, 0,
sizeof G);
206 memset(&
C, 0,
sizeof C);
210 memset(&
B, 0,
sizeof B);
255 for (
int i = 1; i < 3; ++i)
if (fabsf(a[i]) > fabsf(a[ax])) ax = i;
256 if (fabsf(a[ax]) < 0.8f)
return -1;
257 const int pos = (a[ax] > 0.0f);
259 case 2:
return pos ? 0 : 1;
260 case 0:
return pos ? 3 : 2;
261 default:
return pos ? 5 : 4;
267 for (
int i = 0; i < 6; ++i)
if (!(mask & (1u << i)))
return i;
274 static const int pplus[3] = { 3, 5, 0 }, pminus[3] = { 2, 4, 1 };
276 for (
int k = 0; k < 3; ++k) {
277 const float p =
A.cap[pplus[k]][k], m =
A.cap[pminus[k]][k];
278 off[k] = 0.5f * (p + m);
279 sc[k] = (p - m) > 1e-3f ? 2.0f / (p - m) : 0.0f;
282 for (
int i = 0; i < 6; ++i) {
284 for (
int k = 0; k < 3; ++k) {
const float c = sc[k] * (
A.cap[i][k] -
off[k]); n2 += c * c; }
285 const double e = sqrt((
double)n2) - 1.0;
288 A.fit_err_pct = (float)(100.0 * sqrt(e2 / 6.0));
289 for (
int k = 0; k < 3; ++k) {
294 const float q = 100.0f - 20.0f *
A.fit_err_pct;
295 s_quality = (uint8_t)(q < 0.0f ? 0.0f : (q > 100.0f ? 100.0f : q));
301 if (
A.n)
for (
int k = 0; k < 3; ++k)
A.cap[
pos][k] =
A.sum[k] / (
float)
A.n;
302 else for (
int k = 0; k < 3; ++k)
A.cap[
pos][k] =
A.last_a[k];
303 A.done_mask |= (uint8_t)(1u <<
pos);
304 A.sum[0] =
A.sum[1] =
A.sum[2] = 0.0f;
A.n = 0;
A.still_pct = 0;
308 for (
int i = 0; i < 6; ++i) cnt += (
A.done_mask >> i) & 1u;
310 if (
A.done_mask == 0x3Fu) {
accel_fit();
return; }
316 const float g = sqrtf(
s->gyro[0]*
s->gyro[0] +
s->gyro[1]*
s->gyro[1] +
s->gyro[2]*
s->gyro[2]);
318 memcpy(
A.last_a,
s->acc,
sizeof A.last_a);
321 A.sum[0] =
A.sum[1] =
A.sum[2] = 0.0f;
A.n = 0;
A.still_pct = 0;
A.pos = 0xFF;
325 A.pos = (uint8_t)
pos;
A.current = (uint8_t)
pos;
326 A.sum[0] =
A.sum[1] =
A.sum[2] = 0.0f;
A.n = 0;
A.t_still = now;
329 for (
int k = 0; k < 3; ++k)
A.sum[k] +=
s->acc[k];
331 const uint32_t el = now -
A.t_still;
341 if (!
s->running)
return;
342 const float g = sqrtf(
s->gyro[0]*
s->gyro[0] +
s->gyro[1]*
s->gyro[1] +
s->gyro[2]*
s->gyro[2]);
344 const float w =
s->q[0], x =
s->q[1], y =
s->q[2], z =
s->q[3];
345 const float roll = atan2f(2.0f*(w*x + y*z), 1.0f - 2.0f*(x*x + y*y)) *
R2D;
346 float sp = 2.0f*(w*y - z*x);
347 sp =
sp > 1.0f ? 1.0f : (
sp < -1.0f ? -1.0f :
sp);
350 L.roll = (float)(
L.sr /
L.n);
L.pitch = (float)(
L.sp /
L.n);
351 const uint32_t el = now -
s_t0;
355 const double vr =
L.sr2 /
L.n - (double)
L.roll *
L.roll, vp =
L.sp2 /
L.n - (
double)
L.pitch *
L.pitch;
356 const float sd = (float)sqrt((vr > 0 ? vr : 0) + (vp > 0 ? vp : 0));
357 const float q = 100.0f - 100.0f * sd;
358 s_quality = (uint8_t)(q < 0.0f ? 0.0f : q);
367 const float g = sqrtf(
s->gyro[0]*
s->gyro[0] +
s->gyro[1]*
s->gyro[1] +
s->gyro[2]*
s->gyro[2]);
371 memset(
G.s, 0,
sizeof G.s); memset(
G.s2, 0,
sizeof G.s2);
G.n = 0;
376 for (
int k = 0; k < 3; ++k) {
G.s[k] +=
s->gyro[k];
G.s2[k] += (double)
s->gyro[k]*
s->gyro[k]; }
378 const uint32_t el = now -
s_t0;
383 for (
int k = 0; k < 3; ++k) {
384 const double m =
G.s[k] /
G.n;
385 G.bias[k] = (float)m;
386 const double v =
G.s2[k] /
G.n - m*m;
387 var += v > 0 ? v : 0;
390 G.noise = (float)sqrt(var / 3.0);
391 const float q = 100.0f - 100.0f * (
G.noise *
R2D);
392 s_quality = (uint8_t)(q < 0.0f ? 0.0f : q);
400static void bin(
int ring,
float a,
float b)
402 if (sqrtf(a*a +
b*
b) < 0.3f)
return;
403 int k = (int)((atan2f(
b, a) + 3.14159265f) * (12.0f / 6.2831853f));
406 const int i = ring * 12 + k;
407 C.cov[i >> 3] |= (uint8_t)(1u << (i & 7));
413 for (
int i = 0; i < 36; ++i)
n += (
C.cov[i >> 3] >> (i & 7)) & 1u;
418static int solve6(
double A6[6][6],
double b6[6],
double x[6])
421 for (
int i = 0; i < 6; ++i) {
for (
int j = 0; j < 6; ++j) M[i][j] = A6[i][j]; M[i][6] = b6[i]; }
422 for (
int c = 0; c < 6; ++c) {
424 for (
int r = c + 1; r < 6; ++r)
if (fabs(M[r][c]) > fabs(M[p][c])) p = r;
425 if (fabs(M[p][c]) < 1e-12)
return -1;
426 if (p != c)
for (
int j = 0; j < 7; ++j) {
const double t = M[c][j]; M[c][j] = M[p][j]; M[p][j] = t; }
427 for (
int r = c + 1; r < 6; ++r) {
428 const double f = M[r][c] / M[c][c];
429 for (
int j = c; j < 7; ++j) M[r][j] -= f * M[c][j];
432 for (
int i = 5; i >= 0; --i) {
434 for (
int j = i + 1; j < 6; ++j)
s -= M[i][j] * x[j];
446 double c[3], r[3], Gs = 1.0;
447 for (
int i = 0; i < 3; ++i) { c[i] = -p[3 + i] / (2.0 * p[i]); Gs += p[i] * c[i] * c[i]; }
450 for (
int i = 0; i < 3; ++i) { r[i] = sqrt(Gs / p[i]); rm += r[i] / 3.0; }
451 for (
int i = 0; i < 3; ++i) {
452 const double sc = rm / r[i];
458 double s1 = 0.0,
s2 = 0.0;
459 for (uint16_t k = 0; k <
C.nkeep; ++k) {
461 for (
int i = 0; i < 3; ++i) {
465 const double n = sqrt(n2);
466 s1 +=
n;
s2 +=
n *
n;
468 const double mean = s1 /
C.nkeep, var =
s2 /
C.nkeep - mean * mean;
469 const double rel = (var > 0 ? sqrt(var) : 0.0) / (mean > 1e-6 ? mean : 1.0);
470 const double q = 100.0 - 1000.0 * rel;
471 C.qual = (uint8_t)(q < 0.0 ? 0.0 : (q > 100.0 ? 100.0 : q));
472 C.interf = (uint8_t)(rel * 100.0 > 100.0 ? 100.0 : rel * 100.0 + 0.5);
479 if (!
s->mag_new)
return;
480 const float *m =
s->mag;
482 float u[3],
n = 0.0f;
485 if (
n < 1e-3f)
return;
486 for (
int i = 0; i < 3; ++i) u[i] /=
n;
491 const double phi[6] = { (double)m[0]*m[0], (
double)m[1]*m[1], (double)m[2]*m[2], m[0], m[1], m[2] };
492 for (
int i = 0; i < 6; ++i) {
494 for (
int j = 0; j < 6; ++j)
C.N[i][j] += phi[i] * phi[j];
497 memcpy(
C.keep[
C.kidx], m,
sizeof C.keep[0]);
540 if (
A.pos != (uint8_t)
pos) {
A.sum[0] =
A.sum[1] =
A.sum[2] = 0.0f;
A.n = 0; }
547uint8_t
cal_set_ref(uint8_t type, uint8_t index,
float v, uint8_t *reason)
563 const float k = v /
B.vbat;
577 const float si =
B.scale_i_used * v /
B.used;
589static int16_t
cl16(
float v) {
return (int16_t)(v > 32767.f ? 32767 : (v < -32768.f ? -32768 : lrintf(v))); }
590static uint16_t
clu16(
float v) {
return (uint16_t)(v > 65535.f ? 65535 : (v < 0.f ? 0 : lrintf(v))); }
601 memset(&d, 0,
sizeof d);
605 d.a.done_mask =
A.done_mask; d.a.current =
A.current; d.a.still_pct =
A.still_pct;
606 int16_t o[3]; uint16_t sc[3];
607 for (
int k = 0; k < 3; ++k) { o[k] =
cl16(r->
off[k] * 1000.0f); sc[k] =
clu16(r->
scale[k] * 1e4f); }
608 memcpy(d.a.offset_mg, o,
sizeof o);
609 memcpy(d.a.scale_e4, sc,
sizeof sc);
610 const uint16_t fe =
clu16(
A.fit_err_pct * 100.0f);
611 memcpy(&d.a.fit_err_x100, &fe, 2);
616 const int16_t v[2] = {
cl16(
L.roll * 100.0f),
cl16(
L.pitch * 100.0f) };
618 memcpy(&d.l.roll_cdeg, &v[0], 2); memcpy(&d.l.pitch_cdeg, &v[1], 2);
619 memcpy(&d.l.remaining_ms, &
L.remaining, 2);
620 memcpy(&d.l.trim_roll_cdeg, &t[0], 2); memcpy(&d.l.trim_pitch_cdeg, &t[1], 2);
626 for (
int k = 0; k < 3; ++k)
b[k] =
cl16(
G.bias[k] *
R2D * 1000.0f);
627 const uint16_t nz =
clu16(
G.noise *
R2D * 1000.0f);
628 const int16_t tc = 0;
629 memcpy(&d.g.remaining_ms, &
G.remaining, 2);
630 memcpy(d.g.bias_mdps,
b,
sizeof b);
631 memcpy(&d.g.noise_mdps, &nz, 2);
632 memcpy(&d.g.temp_cc, &tc, 2);
637 memcpy(d.c.coverage,
C.cov, 5);
639 d.c.interf_fc = fin ?
C.interf : 0u;
640 d.c.interf_gps = 0xFFu;
641 d.c.quality_fc = fin ?
C.qual : 0xFFu;
642 d.c.quality_gps = 0xFFu;
649 memcpy(&d.b,
b,
sizeof b);
static uint32_t bit(uint8_t type)
static void apply(const cal_set_t *set)
static int accel_position(const float a[3])
uint8_t cal_take_changed(void)
1 if the state/step changed since the last call (send a CALIB frame now)
static int solve6(double A6[6][6], double b6[6], double x[6])
float keep[COMPASS_KEEP][3]
static void compass_fit(void)
static void gyro_feed(const cal_sample_t *s, uint32_t now)
#define COMPASS_MIN_SAMPLES
uint8_t cal_abort(uint8_t *reason)
static void done(uint8_t step)
static int16_t cl16(float v)
static void compass_feed(const cal_sample_t *s)
static uint8_t s_progress
uint8_t cal_capture(uint32_t now, uint8_t *reason)
void cal_init(const cal_config_t *cfg, cal_set_t *active)
active: the set in effect (RAM), owned by the caller.
static cal_set_t * s_active
static void accel_feed(const cal_sample_t *s, uint32_t now)
static uint16_t clu16(float v)
uint8_t cal_running(void)
active and not DONE: blocks arming / other starts
uint8_t cal_save_check(uint8_t *reason)
CAL_SAVE validation: returns the ACK; on OK the caller writes the result (cal_result()) to flash and ...
static uint8_t coverage_count(void)
void cal_saved(void)
state SAVED (one frame), then IDLE
static void level_feed(const cal_sample_t *s, uint32_t now)
size_t cal_frame(uint8_t *out, size_t cap)
CALIB frame for the current calibration; 0 if none.
uint8_t cal_start(uint8_t type, uint8_t option, uint8_t armed, uint32_t now, uint8_t *reason)
static int first_missing(uint8_t mask)
static void bin(int ring, float a, float b)
uint8_t cal_set_ref(uint8_t type, uint8_t index, float v, uint8_t *reason)
uint8_t cal_take_failed(void)
1 + type once when a calibration failed (EVENT 51), 0 otherwise
uint8_t cal_active(void)
a calibration exists (any state but IDLE)
#define GYRO_MAX_RESTARTS
const cal_set_t * cal_result(void)
static cal_config_t s_cfg
static uint8_t s_failed_type
uint8_t cal_wants_samples(void)
void cal_copy_type(cal_set_t *d, const cal_set_t *s, uint8_t type)
copies the part of 'src' that belongs to 'type' into 'dst'
void cal_set_defaults(cal_set_t *s, const float hi[3])
static void set_state(uint8_t st, uint8_t step)
void cal_set_sensors(uint16_t sensors)
expansion board detected later
void cal_tick(uint32_t now)
void cal_feed(const cal_sample_t *s, uint32_t now)
static void accel_fit(void)
static void accel_capture_pos(int pos)
static void fail(uint8_t reason)
Sensor calibrations of protocol v2 (HANDOVER_FW.md ยง10).
size_t pv2_calib(const pv2_calib_hdr_t *h, const void *data, uint8_t data_len, uint8_t *out, size_t cap)
ACwing Pilot app protocol v2: identifiers and frame layouts.
uint16_t sensors
CAPS bits.
cal_batt_fn batt
NULL: BATTERY -> SENSOR_MISSING.
uint32_t done_mask
bit (type-1): this calibration was done and saved
static USBD_StatusTypeDef st(HAL_StatusTypeDef h)