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 >= 5, SR68 rev
57+ * >= 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 ;
0 commit comments