Skip to content

Commit b3bdf15

Browse files
authored
Merge pull request #282 from ShimmerResearch/DEV-922_gen2_alignment_upstream
DEV-922 Upstream gen-2 default alignment into SensorLSM6DSV
2 parents 0c0a202 + 316df2d commit b3bdf15

2 files changed

Lines changed: 73 additions & 23 deletions

File tree

ShimmerDriver/src/main/java/com/shimmerresearch/verisense/sensors/SensorLSM6DSV.java

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,16 @@
4747
* calibrated magnetometer output is in GAUSS, consistent with every other Shimmer
4848
* magnetometer and with the per-unit calibration the device stores.
4949
* <p>
50-
* Alignment is left as identity here and corrected by the file parser per hardware
51-
* revision; offsets are zero.
50+
* Default alignment is the real sensor->ASM frame map (accel/gyro share the chip
51+
* mounting; the LIS2MDL frame is left-handed), matching the web SDK's
52+
* CALIBRATION_SENSORS_GEN2 and what verisense-device-console writes to the device.
53+
* No hardware-revision gate is needed here: this sensor class is only instantiated
54+
* for firmware payload design v13 and above (see
55+
* {@code VerisenseDevice.sensorAndConfigMapsCreate()}), i.e. second-generation
56+
* firmware, and every second-generation mounting (SR61 rev &gt;= 5, SR68 rev
57+
* &gt;= 9) shares this frame. Note the FW/HW pairing itself is not enforced
58+
* anywhere: a first-generation board flashed with gen-2 firmware would get this
59+
* frame too. Offsets are zero.
5260
*
5361
* @author Mark Nolan
5462
*/
@@ -237,12 +245,45 @@ public static final class DatabaseConfigHandle {
237245
CompatibilityInfoForMaps.listOfCompatibleVersionInfoLSM6DSV);
238246

239247
// ----------------- Calibration Start -----------------------
240-
// Identity alignment is a placeholder, NOT the real sensor->ASM map: the file
241-
// parser overrides it per hardware revision at parse time (see
242-
// CalibrationFileManager.applyGen2DefaultAlignment in the VerisenseDriver repo).
243-
// Correcting it here as well would give two sources of truth for the same values.
244248
public static final double[][] DEFAULT_OFFSET_VECTOR_LSM6DSV = {{0},{0},{0}};
245-
public static final double[][] DEFAULT_ALIGNMENT_MATRIX_LSM6DSV = {{1,0,0},{0,1,0},{0,0,1}};
249+
250+
// Default alignment, stored first in APPLIED form - the sensor->ASM map,
251+
// physical = applied . (raw - bias) / sens - so the literals below read exactly
252+
// as verisense-device-console displays them and as the web SDK declares them
253+
// (calibrationDefaults.ts, CALIBRATION_SENSORS_GEN2). Those two and this class
254+
// must stay in agreement; they are the same physical mounting.
255+
//
256+
// WARNING for future per-unit calibration work: the device stores alignment in
257+
// this same APPLIED form (the firmware seeds it in asm_calibration.c and the
258+
// console writes it back), but the existing load paths
259+
// (CalibDetailsKinematic.parseCalParamByteArray and the file parser's
260+
// CalibrationFileManager) copy bytes into the AM slot WITHOUT inverting, and
261+
// UtilCalibration applies AM^-1. Neither path is used for this sensor today;
262+
// if gen-2 per-unit calibration is ever enabled, the loaded alignment must be
263+
// inverted to driver form at load or calibration will be applied backwards.
264+
// The trap is bidirectional: generateCalParamByteArray() likewise writes the
265+
// driver-form AM out uninverted, so writing calibration TO a gen-2 device
266+
// would send the inverse of the applied form the firmware and console expect.
267+
/** Applied sensor->ASM alignment for the LSM6DSV accel + gyro; det +1 (a proper rotation). */
268+
public static final double[][] APPLIED_ALIGNMENT_LSM6DSV_ACCEL_GYRO = {{0,1,0},{0,0,1},{1,0,0}};
269+
/** Applied sensor->ASM alignment for the LIS2MDL mag; its frame is left-handed, so det -1 (a reflection). */
270+
public static final double[][] APPLIED_ALIGNMENT_LIS2MDL_MAG = {{1,0,0},{0,0,1},{0,1,0}};
271+
272+
// The driver stores the opposite convention: CalibDetailsKinematic holds AM and
273+
// UtilCalibration computes AM^-1 . SM^-1 . (data - OV), so the matrices handed to
274+
// the calibration blocks below are the INVERSES of the applied form. A true
275+
// inverse, not a transpose: the two coincide only for orthogonal
276+
// signed-permutation defaults like these, and would differ for a rig-measured
277+
// matrix with cross-axis terms.
278+
public static final double[][] DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO =
279+
UtilCalibration.matrixInverse3x3(APPLIED_ALIGNMENT_LSM6DSV_ACCEL_GYRO);
280+
/* The mag applied matrix is its own inverse (an involution), but deriving it via
281+
* matrixInverse3x3 would stamp -0.0 into the zero entries (its determinant is -1),
282+
* which survives serialization and fails Arrays.deepEquals against 0.0 - so the
283+
* driver-form matrix is written out as a literal. ASM_PC_00032 guards that it
284+
* really is the inverse of the applied form. (The accel/gyro inverse above is
285+
* det +1 and computes canonical 0.0 entries, so it stays derived.) */
286+
public static final double[][] DEFAULT_ALIGNMENT_LIS2MDL_MAG = {{1,0,0},{0,0,1},{0,1,0}};
246287

247288
// Accel sensitivity (LSB per m/s^2) = 32768/(FS_g*9.80665)
248289
public static final double[][] SENS_ACCEL_2G = {{1670.703,0,0},{0,1670.703,0},{0,0,1670.703}};
@@ -261,7 +302,8 @@ public static final class DatabaseConfigHandle {
261302
public static final double[][] SENS_GYRO_2000DPS = {{14.285714286,0,0},{0,14.285714286,0},{0,0,14.285714286}};
262303

263304
// Mag sensitivity: taken from the LIS2MDL's own sensor class rather than
264-
// re-declared here, because it is a property of the chip (1.5 mGauss/LSB) and not
305+
// re-declared here, because it is a property of the chip (1.5 mGauss/LSB =
306+
// 2000/3 = 666.67 LSB/Gauss; 667 is the established Shimmer rounding) and not
265307
// of the LSM6DSV sensor hub the samples arrive through. 667 LSB/Gauss, matching
266308
// the firmware calibration seed (SC_LIS2MDL_MAG_SENS), the web SDK catalog, and
267309
// every other Shimmer magnetometer (LSM303DLHC etc. are all LSB/Gauss).
@@ -274,35 +316,35 @@ public static final class DatabaseConfigHandle {
274316

275317
public CalibDetailsKinematic calibDetailsAccel2g = new CalibDetailsKinematic(
276318
LSM6DSV_ACCEL_RANGE.RANGE_2G.configValue, LSM6DSV_ACCEL_RANGE.RANGE_2G.label,
277-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_ACCEL_2G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
319+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_ACCEL_2G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
278320
public CalibDetailsKinematic calibDetailsAccel4g = new CalibDetailsKinematic(
279321
LSM6DSV_ACCEL_RANGE.RANGE_4G.configValue, LSM6DSV_ACCEL_RANGE.RANGE_4G.label,
280-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_ACCEL_4G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
322+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_ACCEL_4G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
281323
public CalibDetailsKinematic calibDetailsAccel8g = new CalibDetailsKinematic(
282324
LSM6DSV_ACCEL_RANGE.RANGE_8G.configValue, LSM6DSV_ACCEL_RANGE.RANGE_8G.label,
283-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_ACCEL_8G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
325+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_ACCEL_8G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
284326
public CalibDetailsKinematic calibDetailsAccel16g = new CalibDetailsKinematic(
285327
LSM6DSV_ACCEL_RANGE.RANGE_16G.configValue, LSM6DSV_ACCEL_RANGE.RANGE_16G.label,
286-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_ACCEL_16G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
328+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_ACCEL_16G, DEFAULT_OFFSET_VECTOR_LSM6DSV);
287329

288330
public CalibDetailsKinematic calibDetailsGyro125dps = new CalibDetailsKinematic(
289331
LSM6DSV_GYRO_RANGE.RANGE_125DPS.configValue, LSM6DSV_GYRO_RANGE.RANGE_125DPS.label,
290-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_GYRO_125DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
332+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_GYRO_125DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
291333
public CalibDetailsKinematic calibDetailsGyro250dps = new CalibDetailsKinematic(
292334
LSM6DSV_GYRO_RANGE.RANGE_250DPS.configValue, LSM6DSV_GYRO_RANGE.RANGE_250DPS.label,
293-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_GYRO_250DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
335+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_GYRO_250DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
294336
public CalibDetailsKinematic calibDetailsGyro500dps = new CalibDetailsKinematic(
295337
LSM6DSV_GYRO_RANGE.RANGE_500DPS.configValue, LSM6DSV_GYRO_RANGE.RANGE_500DPS.label,
296-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_GYRO_500DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
338+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_GYRO_500DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
297339
public CalibDetailsKinematic calibDetailsGyro1000dps = new CalibDetailsKinematic(
298340
LSM6DSV_GYRO_RANGE.RANGE_1000DPS.configValue, LSM6DSV_GYRO_RANGE.RANGE_1000DPS.label,
299-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_GYRO_1000DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
341+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_GYRO_1000DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
300342
public CalibDetailsKinematic calibDetailsGyro2000dps = new CalibDetailsKinematic(
301343
LSM6DSV_GYRO_RANGE.RANGE_2000DPS.configValue, LSM6DSV_GYRO_RANGE.RANGE_2000DPS.label,
302-
DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_GYRO_2000DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
344+
DEFAULT_ALIGNMENT_LSM6DSV_ACCEL_GYRO, SENS_GYRO_2000DPS, DEFAULT_OFFSET_VECTOR_LSM6DSV);
303345

304346
public CalibDetailsKinematic calibDetailsMag = new CalibDetailsKinematic(
305-
0, "Default", DEFAULT_ALIGNMENT_MATRIX_LSM6DSV, SENS_MAG, DEFAULT_OFFSET_VECTOR_LSM6DSV);
347+
0, "Default", DEFAULT_ALIGNMENT_LIS2MDL_MAG, SENS_MAG, DEFAULT_OFFSET_VECTOR_LSM6DSV);
306348

307349
public CalibDetailsKinematic mCurrentCalibDetailsAccel = calibDetailsAccel4g;
308350
public CalibDetailsKinematic mCurrentCalibDetailsGyro = calibDetailsGyro500dps;

ShimmerDriver/src/test/java/com/shimmerresearch/verisense/API_00008_VerisenseLsm6dsvTaggedFifoParsing.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,19 @@ public void test001_accelGyroMagInterleaved() throws Exception {
143143
// LITERAL expected values (the gyro sensitivity was ~12.8% wrong before the
144144
// DEV-793 round-2 review fix; deriving the expectation from the class
145145
// constants would defeat the lock). Defaults: accel +/-4 g = 835.3517
146-
// LSB/(m/s^2); gyro +/-500 dps = 57.142857 LSB/dps (ST 17.50 mdps/LSB).
147-
assertEquals(100 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_X), 0.0001);
148-
assertEquals(-200 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Y), 0.0001);
149-
assertEquals(10 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_X), 0.0001);
150-
assertEquals(-20 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_Y), 0.0001);
146+
// LSB/(m/s^2); gyro +/-500 dps = 57.142857 LSB/dps (ST 17.50 mdps/LSB);
147+
// mag 667 LSB/Gauss (LIS2MDL 1.5 mGauss/LSB, legacy rounding of 666.67).
148+
// Since DEV-922 the defaults also apply the gen-2 sensor->ASM alignment:
149+
// accel/gyro calibrated X/Y/Z come from raw Y/Z/X, mag X/Y/Z from raw X/Z/Y.
150+
assertEquals(-200 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_X), 0.0001);
151+
assertEquals(300 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Y), 0.0001);
152+
assertEquals(100 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Z), 0.0001);
153+
assertEquals(-20 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_X), 0.0001);
154+
assertEquals(30 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_Y), 0.0001);
155+
assertEquals(10 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_Z), 0.0001);
156+
assertEquals(-150 / 667.0, cal(magOjc, SensorLSM6DSV.ObjectClusterSensorName.LIS2MDL_MAG_X), 0.0001);
157+
assertEquals(150 / 667.0, cal(magOjc, SensorLSM6DSV.ObjectClusterSensorName.LIS2MDL_MAG_Y), 0.0001);
158+
assertEquals(5 / 667.0, cal(magOjc, SensorLSM6DSV.ObjectClusterSensorName.LIS2MDL_MAG_Z), 0.0001);
151159
}
152160

153161
/** Gyro-only: gyro acts as the aligned reference stream. */

0 commit comments

Comments
 (0)