diff --git a/ShimmerDriver/src/main/java/com/shimmerresearch/verisense/sensors/SensorLSM6DSV.java b/ShimmerDriver/src/main/java/com/shimmerresearch/verisense/sensors/SensorLSM6DSV.java index 190b079d..d9daa35c 100644 --- a/ShimmerDriver/src/main/java/com/shimmerresearch/verisense/sensors/SensorLSM6DSV.java +++ b/ShimmerDriver/src/main/java/com/shimmerresearch/verisense/sensors/SensorLSM6DSV.java @@ -41,11 +41,16 @@ * {@code VerisenseDevice.parseDataBlockDataLsm6dsv(...)}. This class provides the * channel definitions, configuration and calibration. *

- * Sensitivities: 32768/(FS*9.80665) LSB per m/s^2 for accel, the ST angular-rate - * spec of 4.375 mdps/LSB at +-125 dps for gyro, and 667 LSB/Gauss for the LIS2MDL - * mag - the last taken from {@link SensorLIS2MDL} rather than duplicated here, so - * calibrated magnetometer output is in GAUSS, consistent with every other Shimmer - * magnetometer and with the per-unit calibration the device stores. + * Sensitivities are the ST datasheet nominals throughout: + *

+ * The mag figure is taken from {@link SensorLIS2MDL} rather than duplicated here, + * so calibrated magnetometer output is in GAUSS, consistent with every other + * Shimmer magnetometer and with the per-unit calibration the device stores. *

* Default alignment is the real sensor->ASM frame map (accel/gyro share the chip * mounting; the LIS2MDL frame is left-handed), matching the web SDK's @@ -285,16 +290,42 @@ public static final class DatabaseConfigHandle { * det +1 and computes canonical 0.0 entries, so it stays derived.) */ public static final double[][] DEFAULT_ALIGNMENT_LIS2MDL_MAG = {{1,0,0},{0,0,1},{0,1,0}}; - // Accel sensitivity (LSB per m/s^2) = 32768/(FS_g*9.80665) - public static final double[][] SENS_ACCEL_2G = {{1670.703,0,0},{0,1670.703,0},{0,0,1670.703}}; - public static final double[][] SENS_ACCEL_4G = {{835.3517,0,0},{0,835.3517,0},{0,0,835.3517}}; - public static final double[][] SENS_ACCEL_8G = {{417.6759,0,0},{0,417.6759,0},{0,0,417.6759}}; - public static final double[][] SENS_ACCEL_16G = {{208.8379,0,0},{0,208.8379,0},{0,0,208.8379}}; + // Accel sensitivity (LSB per m/s^2) from the ST datasheet linear-acceleration + // sensitivity: 0.061 / 0.122 / 0.244 / 0.488 mg/LSB, i.e. 1/(mg_per_LSB/1000)/9.80665. + // + // These were previously derived as 32768/(FS_g*9.80665), on the assumption that + // the accel spans exactly the full 16-bit range at nominal full scale and that + // ST's printed figures were just that quantity rounded to three significant + // figures. They are not. The datasheet's own worked example (AN5922 Table 27, + // FS_XL = +-2 g) settles it: 1 g reads 0x4009 = 16393 LSB and 350 mg reads + // 0x1669 = 5737 LSB. 0.061 mg/LSB predicts 16393.4 and 5737.7; the 32768/FS + // derivation predicts 16384 and 5734.4. So the sensor really is 0.061 mg/LSB - + // full scale sits a little inside +-2 g - and the old derivation was wrong by + // 0.0576%, not merely a rounding apart from the datasheet. + // + // Same story as the gyro below, just with a much smaller margin: neither part + // maps its nominal full scale onto exactly 2^15 counts, so for both of them the + // ST mg/LSB and mdps/LSB figures are the authority and a 32768/FS derivation is + // simply incorrect. These values also match the firmware calibration seed + // (SC_ACCEL_SENS in asm_calibration.c), the web SDK catalog, the gen-1 + // SensorLSM6DS3/SensorLIS2DW12 classes and ST's own reference driver + // (lsm6dsv_from_fs2_to_mg multiplies by 0.061f). Every example in + // STMems_Standard_C_drivers/lsm6dsv_STdC/examples converts through those + // helpers and none derives 32768/FS - including lsm6dsv_self_test.c, whose + // pass/fail limits are specified in mg, so ST's own self-test only comes out + // right if 0.122 mg/LSB is the true sensitivity. + public static final double[][] SENS_ACCEL_2G = {{1671.665922915,0,0},{0,1671.665922915,0},{0,0,1671.665922915}}; + public static final double[][] SENS_ACCEL_4G = {{835.832961457,0,0},{0,835.832961457,0},{0,0,835.832961457}}; + public static final double[][] SENS_ACCEL_8G = {{417.916480729,0,0},{0,417.916480729,0},{0,0,417.916480729}}; + public static final double[][] SENS_ACCEL_16G = {{208.958240364,0,0},{0,208.958240364,0},{0,0,208.958240364}}; // Gyro sensitivity (LSB per dps) from the ST datasheet angular-rate sensitivity // (4.375 mdps/LSB at +-125 dps, doubling per range) - the same spec/values as the - // gen-1 LSM6DS3. NOTE: the gyro does NOT span the full 16-bit range at its nominal - // full scale (unlike the accel), so a 32768/FS derivation is ~12.8% off. + // gen-1 LSM6DS3. NOTE: unlike the accel above, the gyro genuinely does NOT span + // the full 16-bit range at its nominal full scale - 4.375 mdps/LSB implies a real + // full scale of 143.4 dps for the +-125 dps setting, which is exactly where the + // part is observed to saturate - so here a 32768/FS derivation is not a rounding + // difference but plainly wrong (~14.7%). public static final double[][] SENS_GYRO_125DPS = {{228.571428571,0,0},{0,228.571428571,0},{0,0,228.571428571}}; public static final double[][] SENS_GYRO_250DPS = {{114.285714286,0,0},{0,114.285714286,0},{0,0,114.285714286}}; public static final double[][] SENS_GYRO_500DPS = {{57.142857143,0,0},{0,57.142857143,0},{0,0,57.142857143}}; diff --git a/ShimmerDriver/src/test/java/com/shimmerresearch/verisense/API_00008_VerisenseLsm6dsvTaggedFifoParsing.java b/ShimmerDriver/src/test/java/com/shimmerresearch/verisense/API_00008_VerisenseLsm6dsvTaggedFifoParsing.java index 75fe05de..0a11c2dd 100644 --- a/ShimmerDriver/src/test/java/com/shimmerresearch/verisense/API_00008_VerisenseLsm6dsvTaggedFifoParsing.java +++ b/ShimmerDriver/src/test/java/com/shimmerresearch/verisense/API_00008_VerisenseLsm6dsvTaggedFifoParsing.java @@ -142,14 +142,16 @@ public void test001_accelGyroMagInterleaved() throws Exception { // CAL assertions - regression-locks the calibration constants against // LITERAL expected values (the gyro sensitivity was ~12.8% wrong before the // DEV-793 round-2 review fix; deriving the expectation from the class - // constants would defeat the lock). Defaults: accel +/-4 g = 835.3517 - // LSB/(m/s^2); gyro +/-500 dps = 57.142857 LSB/dps (ST 17.50 mdps/LSB); + // constants would defeat the lock). Defaults: accel +/-4 g = 835.832961457 + // LSB/(m/s^2) (ST 0.122 mg/LSB - see the sensitivity note in SensorLSM6DSV + // for why the datasheet figure is used and not the exact 32768/FS form); + // gyro +/-500 dps = 57.142857 LSB/dps (ST 17.50 mdps/LSB); // mag 667 LSB/Gauss (LIS2MDL 1.5 mGauss/LSB, legacy rounding of 666.67). // Since DEV-922 the defaults also apply the gen-2 sensor->ASM alignment: // accel/gyro calibrated X/Y/Z come from raw Y/Z/X, mag X/Y/Z from raw X/Z/Y. - assertEquals(-200 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_X), 0.0001); - assertEquals(300 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Y), 0.0001); - assertEquals(100 / 835.3517, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Z), 0.0001); + assertEquals(-200 / 835.832961457, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_X), 0.0001); + assertEquals(300 / 835.832961457, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Y), 0.0001); + assertEquals(100 / 835.832961457, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_ACC_Z), 0.0001); assertEquals(-20 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_X), 0.0001); assertEquals(30 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_Y), 0.0001); assertEquals(10 / 57.142857143, cal(aligned0, SensorLSM6DSV.ObjectClusterSensorName.LSM6DSV_GYRO_Z), 0.0001);