4141
4242import java .nio .ByteBuffer ;
4343import java .nio .ByteOrder ;
44+ import java .util .Arrays ;
4445import java .util .Calendar ;
4546
47+ import bolts .CancellationToken ;
48+ import bolts .Capture ;
4649import bolts .Task ;
4750
4851import static com .mbientlab .metawear .impl .Constant .Module .SENSOR_FUSION ;
@@ -74,10 +77,11 @@ static String createUri(DataTypeBase dataType) {
7477
7578 private static final long serialVersionUID = -7041546136871081754L ;
7679
80+ private static final byte CALIBRATION_STATE_REV = 1 , CALIBRATION_DATA_REV = 2 ;
7781 private static final byte ENABLE = 1 , MODE = 2 , OUTPUT_ENABLE = 3 ,
7882 CORRECTED_ACC = 4 , CORRECTED_ROT = 5 , CORRECTED_MAG = 6 ,
7983 QUATERNION = 7 , EULER_ANGLES = 8 , GRAVITY_VECTOR = 9 , LINEAR_ACC = 0xa ,
80- CALIB_STATUS = 0xb ;
84+ CALIB_STATUS = 0xb , ACC_CALIB_DATA = 0xc , GYRO_CALIB_DATA = 0xd , MAG_CALIB_DATA = 0xe ;
8185 private final static String QUATERNION_PRODUCER = "com.mbientlab.metawear.impl.SensorFusionBoschImpl.QUATERNION_PRODUCER" ,
8286 EULER_ANGLES_PRODUCER = "com.mbientlab.metawear.impl.SensorFusionBoschImpl.EULER_ANGLES_PRODUCER" ,
8387 GRAVITY_PRODUCER = "com.mbientlab.metawear.impl.SensorFusionBoschImpl.GRAVITY_PRODUCER" ,
@@ -381,6 +385,7 @@ public void stop() {
381385 private byte dataEnableMask ;
382386
383387 private transient TimedTask <byte []> readRegisterTask ;
388+ private transient AsyncDataProducer correctedAccProducer , correctedAngVelProducer , correctedMagProducer , quaterionProducer , eulerAnglesProducer , gravityProducer , linearAccProducer ;
384389
385390 SensorFusionBoschImpl (MetaWearBoardPrivate mwPrivate ) {
386391 super (mwPrivate );
@@ -399,7 +404,14 @@ protected void init() {
399404 readRegisterTask = new TimedTask <>();
400405
401406 mwPrivate .addResponseHandler (new Pair <>(SENSOR_FUSION .id , Util .setRead (MODE )), response -> readRegisterTask .setResult (response ));
402- mwPrivate .addResponseHandler (new Pair <>(SENSOR_FUSION .id , Util .setRead (CALIB_STATUS )), response -> readRegisterTask .setResult (response ));
407+ if (mwPrivate .lookupModuleInfo (SENSOR_FUSION ).revision >= CALIBRATION_STATE_REV ) {
408+ mwPrivate .addResponseHandler (new Pair <>(SENSOR_FUSION .id , Util .setRead (CALIB_STATUS )), response -> readRegisterTask .setResult (response ));
409+ }
410+ if (mwPrivate .lookupModuleInfo (SENSOR_FUSION ).revision >= CALIBRATION_DATA_REV ) {
411+ mwPrivate .addResponseHandler (new Pair <>(SENSOR_FUSION .id , Util .setRead (ACC_CALIB_DATA )), response -> readRegisterTask .setResult (response ));
412+ mwPrivate .addResponseHandler (new Pair <>(SENSOR_FUSION .id , Util .setRead (GYRO_CALIB_DATA )), response -> readRegisterTask .setResult (response ));
413+ mwPrivate .addResponseHandler (new Pair <>(SENSOR_FUSION .id , Util .setRead (MAG_CALIB_DATA )), response -> readRegisterTask .setResult (response ));
414+ }
403415 }
404416
405417 @ Override
@@ -542,37 +554,58 @@ public void commit() {
542554
543555 @ Override
544556 public AsyncDataProducer correctedAcceleration () {
545- return new SensorFusionAsyncDataProducer (CORRECTED_ACC_PRODUCER , (byte ) 0x01 );
557+ if (correctedAccProducer == null ) {
558+ correctedAccProducer = new SensorFusionAsyncDataProducer (CORRECTED_ACC_PRODUCER , (byte ) 0x01 );
559+ }
560+ return correctedAccProducer ;
546561 }
547562
548563 @ Override
549564 public AsyncDataProducer correctedAngularVelocity () {
550- return new SensorFusionAsyncDataProducer (CORRECTED_ROT_PRODUCER , (byte ) 0x02 );
565+ if (correctedAngVelProducer == null ) {
566+ correctedAngVelProducer = new SensorFusionAsyncDataProducer (CORRECTED_ROT_PRODUCER , (byte ) 0x02 );
567+ }
568+ return correctedAngVelProducer ;
551569 }
552570
553571 @ Override
554572 public AsyncDataProducer correctedMagneticField () {
555- return new SensorFusionAsyncDataProducer (CORRECTED_MAG_PRODUCER , (byte ) 0x04 );
573+ if (correctedMagProducer == null ) {
574+ correctedMagProducer = new SensorFusionAsyncDataProducer (CORRECTED_MAG_PRODUCER , (byte ) 0x04 );
575+ }
576+ return correctedMagProducer ;
556577 }
557578
558579 @ Override
559580 public AsyncDataProducer quaternion () {
560- return new SensorFusionAsyncDataProducer (QUATERNION_PRODUCER , (byte ) 0x08 );
581+ if (quaterionProducer == null ) {
582+ quaterionProducer = new SensorFusionAsyncDataProducer (QUATERNION_PRODUCER , (byte ) 0x08 );
583+ }
584+ return quaterionProducer ;
561585 }
562586
563587 @ Override
564588 public AsyncDataProducer eulerAngles () {
565- return new SensorFusionAsyncDataProducer (EULER_ANGLES_PRODUCER , (byte ) 0x10 );
589+ if (eulerAnglesProducer == null ) {
590+ eulerAnglesProducer = new SensorFusionAsyncDataProducer (EULER_ANGLES_PRODUCER , (byte ) 0x10 );
591+ }
592+ return eulerAnglesProducer ;
566593 }
567594
568595 @ Override
569596 public AsyncDataProducer gravity () {
570- return new SensorFusionAsyncDataProducer (GRAVITY_PRODUCER , (byte ) 0x20 );
597+ if (gravityProducer == null ) {
598+ gravityProducer = new SensorFusionAsyncDataProducer (GRAVITY_PRODUCER , (byte ) 0x20 );
599+ }
600+ return gravityProducer ;
571601 }
572602
573603 @ Override
574604 public AsyncDataProducer linearAcceleration () {
575- return new SensorFusionAsyncDataProducer (LINEAR_ACC_PRODUCER , (byte ) 0x40 );
605+ if (linearAccProducer == null ) {
606+ linearAccProducer = new SensorFusionAsyncDataProducer (LINEAR_ACC_PRODUCER , (byte ) 0x40 );
607+ }
608+ return linearAccProducer ;
576609 }
577610
578611 @ Override
@@ -659,15 +692,113 @@ public Task<Void> pullConfigAsync() {
659692
660693 @ Override
661694 public Task <CalibrationState > readCalibrationStateAsync () {
662- return readRegisterTask .execute ("Did not receive sensor fusion calibration status within %dms" , Constant .RESPONSE_TIMEOUT ,
663- () -> mwPrivate .sendCommand (new byte [] {SENSOR_FUSION .id , Util .setRead (CALIB_STATUS )})
664- ).onSuccessTask (task -> {
665- CalibrationAccuracy values [] = CalibrationAccuracy .values ();
666- return Task .forResult (new CalibrationState (
667- values [task .getResult ()[2 ]],
668- values [task .getResult ()[3 ]],
669- values [task .getResult ()[4 ]]
670- ));
671- });
695+ if (mwPrivate .lookupModuleInfo (SENSOR_FUSION ).revision >= CALIBRATION_STATE_REV ) {
696+ return readRegisterTask .execute ("Did not receive sensor fusion calibration status within %dms" , Constant .RESPONSE_TIMEOUT ,
697+ () -> mwPrivate .sendCommand (new byte [] {SENSOR_FUSION .id , Util .setRead (CALIB_STATUS )})
698+ ).onSuccessTask (task -> {
699+ CalibrationAccuracy values [] = CalibrationAccuracy .values ();
700+ return Task .forResult (new CalibrationState (
701+ values [task .getResult ()[2 ]],
702+ values [task .getResult ()[3 ]],
703+ values [task .getResult ()[4 ]]
704+ ));
705+ });
706+ }
707+ return Task .forError (new UnsupportedOperationException ("Minimum firmware v1.4.2 required to use this function" ));
708+ }
709+
710+ @ Override
711+ public Task <CalibrationData > calibrate (CancellationToken ct , long pollingPeriod , CalibrationStateUpdateHandler updateHandler ) {
712+ if (mwPrivate .lookupModuleInfo (SENSOR_FUSION ).revision >= CALIBRATION_DATA_REV ) {
713+ final Capture <Boolean > terminate = new Capture <>(false );
714+ final Capture <byte []> acc = new Capture <>(null ),
715+ gyro = new Capture <>(null ),
716+ mag = new Capture <>(null );
717+
718+ return Task .forResult (null ).continueWhile (() -> !terminate .get (), ignored -> ct .isCancellationRequested () ? readCalibrationStateAsync ().onSuccessTask (task -> {
719+ if (updateHandler != null ) {
720+ updateHandler .receivedUpdate (task .getResult ());
721+ }
722+
723+ switch (mode ) {
724+ case NDOF :
725+ terminate .set (task .getResult ().accelerometer == CalibrationAccuracy .HIGH_ACCURACY &&
726+ task .getResult ().gyroscope == CalibrationAccuracy .HIGH_ACCURACY &&
727+ task .getResult ().magnetometer == CalibrationAccuracy .HIGH_ACCURACY );
728+ break ;
729+ case IMU_PLUS :
730+ terminate .set (task .getResult ().accelerometer == CalibrationAccuracy .HIGH_ACCURACY &&
731+ task .getResult ().gyroscope == CalibrationAccuracy .HIGH_ACCURACY );
732+ break ;
733+ case COMPASS :
734+ terminate .set (task .getResult ().accelerometer == CalibrationAccuracy .HIGH_ACCURACY &&
735+ task .getResult ().magnetometer == CalibrationAccuracy .HIGH_ACCURACY );
736+ break ;
737+ case M4G :
738+ terminate .set (task .getResult ().accelerometer == CalibrationAccuracy .HIGH_ACCURACY &&
739+ task .getResult ().magnetometer == CalibrationAccuracy .HIGH_ACCURACY );
740+ break ;
741+ }
742+
743+ return !terminate .get () ? Task .delay (pollingPeriod ) : Task .<Void >forResult (null );
744+ }) : Task .cancelled ()
745+ ).onSuccessTask (ignored -> readRegisterTask .execute ("Did not receive accelerometer calibration data within %dms" , Constant .RESPONSE_TIMEOUT ,
746+ () -> mwPrivate .sendCommand (new byte [] {SENSOR_FUSION .id , Util .setRead (ACC_CALIB_DATA )}))
747+ ).onSuccessTask (task -> {
748+ byte [] result = task .getResult ();
749+ acc .set (Arrays .copyOfRange (result , 2 , result .length ));
750+
751+ return mode == Mode .IMU_PLUS || mode == Mode .NDOF ? readRegisterTask .execute ("Did not receive gyroscope calibration data within %dms" , Constant .RESPONSE_TIMEOUT ,
752+ () -> mwPrivate .sendCommand (new byte [] {SENSOR_FUSION .id , Util .setRead (GYRO_CALIB_DATA )})
753+ ) : Task .forResult (null );
754+ }).onSuccessTask (task -> {
755+ if (task .getResult () != null ) {
756+ byte [] result = task .getResult ();
757+ gyro .set (Arrays .copyOfRange (result , 2 , result .length ));
758+ }
759+
760+ return mode != Mode .IMU_PLUS ? readRegisterTask .execute ("Did not receive magnetometer calibration data within %dms" , Constant .RESPONSE_TIMEOUT ,
761+ () -> mwPrivate .sendCommand (new byte [] {SENSOR_FUSION .id , Util .setRead (MAG_CALIB_DATA )})
762+ ) : Task .forResult (null );
763+ }).onSuccessTask (task -> {
764+ if (task .getResult () != null ) {
765+ byte [] result = task .getResult ();
766+ mag .set (Arrays .copyOfRange (result , 2 , result .length ));
767+ }
768+
769+ return Task .forResult (new CalibrationData (acc .get (), gyro .get (), mag .get ()));
770+ });
771+ }
772+ return Task .forError (new UnsupportedOperationException ("Minimum firmware v1.4.4 required to use this function" ));
773+ }
774+
775+ @ Override
776+ public Task <CalibrationData > calibrate (CancellationToken ct , CalibrationStateUpdateHandler updateHandler ) {
777+ return calibrate (ct , 1000 , updateHandler );
778+ }
779+
780+ @ Override
781+ public Task <CalibrationData > calibrate (CancellationToken ct , long pollingPeriod ) {
782+ return calibrate (ct , pollingPeriod , null );
783+ }
784+
785+ @ Override
786+ public Task <CalibrationData > calibrate (CancellationToken ct ) {
787+ return calibrate (ct , 1000 , null );
788+ }
789+
790+ @ Override
791+ public void writeCalibrationData (CalibrationData data ) {
792+ if (mwPrivate .lookupModuleInfo (SENSOR_FUSION ).revision >= CALIBRATION_STATE_REV ) {
793+ mwPrivate .sendCommand (SENSOR_FUSION , ACC_CALIB_DATA , data .accelerometer );
794+
795+ if (mode == Mode .IMU_PLUS || mode == Mode .NDOF ) {
796+ mwPrivate .sendCommand (SENSOR_FUSION , GYRO_CALIB_DATA , data .gyroscope );
797+ }
798+
799+ if (mode != Mode .IMU_PLUS ) {
800+ mwPrivate .sendCommand (SENSOR_FUSION , MAG_CALIB_DATA , data .magnetometer );
801+ }
802+ }
672803 }
673804}
0 commit comments