1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.navigation.inertial.calibration.intervals.thresholdfactor;
17
18 import com.irurueta.algebra.Matrix;
19 import com.irurueta.navigation.LockedException;
20 import com.irurueta.navigation.NotReadyException;
21 import com.irurueta.navigation.inertial.INSLooselyCoupledKalmanInitializerConfig;
22 import com.irurueta.navigation.inertial.INSTightlyCoupledKalmanInitializerConfig;
23 import com.irurueta.navigation.inertial.calibration.AccelerometerNoiseRootPsdSource;
24 import com.irurueta.navigation.inertial.calibration.BodyKinematicsSequence;
25 import com.irurueta.navigation.inertial.calibration.CalibrationException;
26 import com.irurueta.navigation.inertial.calibration.GyroscopeBiasUncertaintySource;
27 import com.irurueta.navigation.inertial.calibration.GyroscopeCalibrationSource;
28 import com.irurueta.navigation.inertial.calibration.GyroscopeNoiseRootPsdSource;
29 import com.irurueta.navigation.inertial.calibration.StandardDeviationTimedBodyKinematics;
30 import com.irurueta.navigation.inertial.calibration.TimedBodyKinematics;
31 import com.irurueta.navigation.inertial.calibration.accelerometer.KnownBiasAccelerometerCalibrator;
32 import com.irurueta.navigation.inertial.calibration.generators.GyroscopeMeasurementsGenerator;
33 import com.irurueta.navigation.inertial.calibration.generators.GyroscopeMeasurementsGeneratorListener;
34 import com.irurueta.navigation.inertial.calibration.gyroscope.GyroscopeCalibratorMeasurementOrSequenceType;
35 import com.irurueta.navigation.inertial.calibration.gyroscope.GyroscopeNonLinearCalibrator;
36 import com.irurueta.navigation.inertial.calibration.gyroscope.OrderedBodyKinematicsSequenceGyroscopeCalibrator;
37 import com.irurueta.navigation.inertial.calibration.gyroscope.QualityScoredGyroscopeCalibrator;
38 import com.irurueta.navigation.inertial.calibration.gyroscope.UnknownBiasGyroscopeCalibrator;
39 import com.irurueta.navigation.inertial.calibration.intervals.TriadStaticIntervalDetector;
40 import com.irurueta.units.Acceleration;
41 import com.irurueta.units.AccelerationUnit;
42 import com.irurueta.units.Time;
43
44 import java.util.ArrayList;
45 import java.util.List;
46
47
48
49
50
51
52
53
54
55
56 public abstract class GyroscopeIntervalDetectorThresholdFactorOptimizer extends
57 IntervalDetectorThresholdFactorOptimizer<TimedBodyKinematics,
58 GyroscopeIntervalDetectorThresholdFactorOptimizerDataSource>
59 implements GyroscopeNoiseRootPsdSource, GyroscopeBiasUncertaintySource,
60 GyroscopeCalibrationSource, AccelerometerNoiseRootPsdSource {
61
62
63
64
65 public static final double DEFAULT_MIN_THRESHOLD_FACTOR = 2.0;
66
67
68
69
70 public static final double DEFAULT_MAX_THRESHOLD_FACTOR = 10.0;
71
72
73
74
75 protected double minThresholdFactor = DEFAULT_MIN_THRESHOLD_FACTOR;
76
77
78
79
80 protected double maxThresholdFactor = DEFAULT_MAX_THRESHOLD_FACTOR;
81
82
83
84
85 private GyroscopeNonLinearCalibrator calibrator;
86
87
88
89
90 private GyroscopeMeasurementsGenerator generator;
91
92
93
94
95 private List<BodyKinematicsSequence<StandardDeviationTimedBodyKinematics>> sequences;
96
97
98
99
100
101 private QualityScoreMapper<BodyKinematicsSequence<StandardDeviationTimedBodyKinematics>> qualityScoreMapper =
102 new DefaultGyroscopeQualityScoreMapper();
103
104
105
106
107
108 private double angularSpeedNoiseRootPsd;
109
110
111
112
113
114
115
116
117 private double baseNoiseLevel;
118
119
120
121
122
123
124 private double threshold;
125
126
127
128
129 private Matrix estimatedCovariance;
130
131
132
133
134
135 private double[] estimatedBiases;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 private Matrix estimatedMg;
177
178
179
180
181
182
183 private Matrix estimatedGg;
184
185
186
187
188 protected GyroscopeIntervalDetectorThresholdFactorOptimizer() {
189 initialize();
190 }
191
192
193
194
195
196
197 protected GyroscopeIntervalDetectorThresholdFactorOptimizer(
198 final GyroscopeIntervalDetectorThresholdFactorOptimizerDataSource dataSource) {
199 super(dataSource);
200 initialize();
201 }
202
203
204
205
206
207
208
209
210
211
212 protected GyroscopeIntervalDetectorThresholdFactorOptimizer(final GyroscopeNonLinearCalibrator calibrator) {
213 try {
214 setCalibrator(calibrator);
215 } catch (final LockedException ignore) {
216
217 }
218 initialize();
219 }
220
221
222
223
224
225
226
227
228
229
230
231 protected GyroscopeIntervalDetectorThresholdFactorOptimizer(
232 final GyroscopeIntervalDetectorThresholdFactorOptimizerDataSource dataSource,
233 final GyroscopeNonLinearCalibrator calibrator) {
234 super(dataSource);
235 try {
236 setCalibrator(calibrator);
237 } catch (final LockedException ignore) {
238
239 }
240 initialize();
241 }
242
243
244
245
246
247
248 public GyroscopeNonLinearCalibrator getCalibrator() {
249 return calibrator;
250 }
251
252
253
254
255
256
257
258
259
260
261 public void setCalibrator(final GyroscopeNonLinearCalibrator calibrator) throws LockedException {
262 if (running) {
263 throw new LockedException();
264 }
265
266 if (calibrator != null && calibrator.getMeasurementOrSequenceType()
267 != GyroscopeCalibratorMeasurementOrSequenceType.BODY_KINEMATICS_SEQUENCE) {
268 throw new IllegalArgumentException();
269 }
270
271 this.calibrator = calibrator;
272 }
273
274
275
276
277
278
279
280 public QualityScoreMapper<BodyKinematicsSequence<StandardDeviationTimedBodyKinematics>> getQualityScoreMapper() {
281 return qualityScoreMapper;
282 }
283
284
285
286
287
288
289
290
291 public void setQualityScoreMapper(
292 final QualityScoreMapper<BodyKinematicsSequence<StandardDeviationTimedBodyKinematics>> qualityScoreMapper)
293 throws LockedException {
294 if (running) {
295 throw new LockedException();
296 }
297
298 this.qualityScoreMapper = qualityScoreMapper;
299 }
300
301
302
303
304
305
306 public double getMinThresholdFactor() {
307 return minThresholdFactor;
308 }
309
310
311
312
313
314
315 public double getMaxThresholdFactor() {
316 return maxThresholdFactor;
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330 public void setThresholdFactorRange(final double minThresholdFactor, final double maxThresholdFactor)
331 throws LockedException {
332 if (running) {
333 throw new LockedException();
334 }
335 if (minThresholdFactor < 0.0 || maxThresholdFactor < 0.0 || minThresholdFactor >= maxThresholdFactor) {
336 throw new IllegalArgumentException();
337 }
338
339 this.minThresholdFactor = minThresholdFactor;
340 this.maxThresholdFactor = maxThresholdFactor;
341 }
342
343
344
345
346
347
348 @Override
349 public boolean isReady() {
350 return super.isReady() && calibrator != null && qualityScoreMapper != null;
351 }
352
353
354
355
356
357
358
359 public double getTimeInterval() {
360 return generator.getTimeInterval();
361 }
362
363
364
365
366
367
368
369
370
371 public void setTimeInterval(final double timeInterval) throws LockedException {
372 if (running) {
373 throw new LockedException();
374 }
375
376 generator.setTimeInterval(timeInterval);
377 }
378
379
380
381
382
383
384
385 public Time getTimeIntervalAsTime() {
386 return generator.getTimeIntervalAsTime();
387 }
388
389
390
391
392
393
394
395 public void getTimeIntervalAsTime(final Time result) {
396 generator.getTimeIntervalAsTime(result);
397 }
398
399
400
401
402
403
404
405
406
407 public void setTimeInterval(final Time timeInterval) throws LockedException {
408 if (running) {
409 throw new LockedException();
410 }
411
412 generator.setTimeInterval(timeInterval);
413 }
414
415
416
417
418
419
420
421
422
423
424 public int getMinStaticSamples() {
425 return generator.getMinStaticSamples();
426 }
427
428
429
430
431
432
433
434
435
436
437
438
439 public void setMinStaticSamples(final int minStaticSamples) throws LockedException {
440 if (running) {
441 throw new LockedException();
442 }
443
444 generator.setMinStaticSamples(minStaticSamples);
445 }
446
447
448
449
450
451
452
453 public int getMaxDynamicSamples() {
454 return generator.getMaxDynamicSamples();
455 }
456
457
458
459
460
461
462
463
464
465
466 public void setMaxDynamicSamples(final int maxDynamicSamples) throws LockedException {
467 if (running) {
468 throw new LockedException();
469 }
470
471 generator.setMaxDynamicSamples(maxDynamicSamples);
472 }
473
474
475
476
477
478
479
480
481 public int getWindowSize() {
482 return generator.getWindowSize();
483 }
484
485
486
487
488
489
490
491
492
493
494
495
496 public void setWindowSize(final int windowSize) throws LockedException {
497 if (running) {
498 throw new LockedException();
499 }
500
501 generator.setWindowSize(windowSize);
502 }
503
504
505
506
507
508
509
510
511 public int getInitialStaticSamples() {
512 return generator.getInitialStaticSamples();
513 }
514
515
516
517
518
519
520
521
522
523
524
525 public void setInitialStaticSamples(final int initialStaticSamples) throws LockedException {
526 if (running) {
527 throw new LockedException();
528 }
529
530 generator.setInitialStaticSamples(initialStaticSamples);
531 }
532
533
534
535
536
537
538
539
540
541 public double getInstantaneousNoiseLevelFactor() {
542 return generator.getInstantaneousNoiseLevelFactor();
543 }
544
545
546
547
548
549
550
551
552
553
554
555
556
557 public void setInstantaneousNoiseLevelFactor(final double instantaneousNoiseLevelFactor) throws LockedException {
558 if (running) {
559 throw new LockedException();
560 }
561
562 generator.setInstantaneousNoiseLevelFactor(instantaneousNoiseLevelFactor);
563 }
564
565
566
567
568
569
570
571
572
573 public double getBaseNoiseLevelAbsoluteThreshold() {
574 return generator.getBaseNoiseLevelAbsoluteThreshold();
575 }
576
577
578
579
580
581
582
583
584
585
586
587
588 public void setBaseNoiseLevelAbsoluteThreshold(final double baseNoiseLevelAbsoluteThreshold)
589 throws LockedException {
590 if (running) {
591 throw new LockedException();
592 }
593
594 generator.setBaseNoiseLevelAbsoluteThreshold(baseNoiseLevelAbsoluteThreshold);
595 }
596
597
598
599
600
601
602
603
604 public Acceleration getBaseNoiseLevelAbsoluteThresholdAsMeasurement() {
605 return generator.getBaseNoiseLevelAbsoluteThresholdAsMeasurement();
606 }
607
608
609
610
611
612
613
614 public void getBaseNoiseLevelAbsoluteThresholdAsMeasurement(final Acceleration result) {
615 generator.getBaseNoiseLevelAbsoluteThresholdAsMeasurement(result);
616 }
617
618
619
620
621
622
623
624
625
626
627
628 public void setBaseNoiseLevelAbsoluteThreshold(final Acceleration baseNoiseLevelAbsoluteThreshold)
629 throws LockedException {
630 if (running) {
631 throw new LockedException();
632 }
633
634 generator.setBaseNoiseLevelAbsoluteThreshold(baseNoiseLevelAbsoluteThreshold);
635 }
636
637
638
639
640
641
642
643
644
645
646
647 public double getAccelerometerBaseNoiseLevel() {
648 return baseNoiseLevel;
649 }
650
651
652
653
654
655
656
657
658
659
660 public Acceleration getAccelerometerBaseNoiseLevelAsMeasurement() {
661 return createMeasurement(baseNoiseLevel, getDefaultUnit());
662 }
663
664
665
666
667
668
669
670
671
672 public void getAccelerometerBaseNoiseLevelAsMeasurement(final Acceleration result) {
673 result.setValue(baseNoiseLevel);
674 result.setUnit(getDefaultUnit());
675 }
676
677
678
679
680
681
682
683 public double getGyroscopeBaseNoiseLevelPsd() {
684 return angularSpeedNoiseRootPsd * angularSpeedNoiseRootPsd;
685 }
686
687
688
689
690
691
692
693 @Override
694 public double getGyroscopeBaseNoiseLevelRootPsd() {
695 return angularSpeedNoiseRootPsd;
696 }
697
698
699
700
701
702
703
704 public double getAccelerometerBaseNoiseLevelPsd() {
705 return baseNoiseLevel * baseNoiseLevel * getTimeInterval();
706 }
707
708
709
710
711
712
713
714 @Override
715 public double getAccelerometerBaseNoiseLevelRootPsd() {
716 return baseNoiseLevel * Math.sqrt(getTimeInterval());
717 }
718
719
720
721
722
723
724
725
726
727 public double getThreshold() {
728 return threshold;
729 }
730
731
732
733
734
735
736
737
738 public Acceleration getThresholdAsMeasurement() {
739 return createMeasurement(threshold, getDefaultUnit());
740 }
741
742
743
744
745
746
747
748 public void getThresholdAsMeasurement(final Acceleration result) {
749 result.setValue(threshold);
750 result.setUnit(getDefaultUnit());
751 }
752
753
754
755
756
757
758
759
760
761
762 @Override
763 public Double getEstimatedBiasStandardDeviationNorm() {
764 return estimatedCovariance != null
765 ? Math.sqrt(getEstimatedBiasXVariance() + getEstimatedBiasYVariance() + getEstimatedBiasZVariance())
766 : null;
767 }
768
769
770
771
772
773
774 public Double getEstimatedBiasXVariance() {
775 return estimatedCovariance != null ? estimatedCovariance.getElementAt(0, 0) : null;
776 }
777
778
779
780
781
782
783 public Double getEstimatedBiasYVariance() {
784 return estimatedCovariance != null ? estimatedCovariance.getElementAt(1, 1) : null;
785 }
786
787
788
789
790
791
792 public Double getEstimatedBiasZVariance() {
793 return estimatedCovariance != null ? estimatedCovariance.getElementAt(2, 2) : null;
794 }
795
796
797
798
799
800
801
802 @Override
803 public double[] getEstimatedBiases() {
804 return estimatedBiases;
805 }
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848 @Override
849 public Matrix getEstimatedMg() {
850 return estimatedMg;
851 }
852
853
854
855
856
857
858
859 @Override
860 public Matrix getEstimatedGg() {
861 return estimatedGg;
862 }
863
864
865
866
867
868
869
870
871
872
873
874
875
876 protected double evaluateForThresholdFactor(final double thresholdFactor) throws LockedException,
877 CalibrationException, NotReadyException, IntervalDetectorThresholdFactorOptimizerException {
878 if (sequences == null) {
879 sequences = new ArrayList<>();
880 } else {
881 sequences.clear();
882 }
883
884 generator.reset();
885 generator.setThresholdFactor(thresholdFactor);
886
887 var count = dataSource.count();
888 var failed = false;
889 for (var i = 0; i < count; i++) {
890 final var bodyKinematics = dataSource.getAt(i);
891 if (!generator.process(bodyKinematics)) {
892 failed = true;
893 break;
894 }
895 }
896
897 if (failed || generator.getStatus() == TriadStaticIntervalDetector.Status.FAILED) {
898
899 return Double.MAX_VALUE;
900 }
901
902
903 if (sequences.size() < calibrator.getMinimumRequiredMeasurementsOrSequences()) {
904 return Double.MAX_VALUE;
905 }
906
907
908 if (calibrator.getMeasurementOrSequenceType()
909 == GyroscopeCalibratorMeasurementOrSequenceType.BODY_KINEMATICS_SEQUENCE) {
910 final var cal = (OrderedBodyKinematicsSequenceGyroscopeCalibrator) this.calibrator;
911
912 cal.setSequences(sequences);
913 } else {
914 throw new IntervalDetectorThresholdFactorOptimizerException();
915 }
916
917 if (calibrator.isQualityScoresRequired()) {
918 final var cal = (QualityScoredGyroscopeCalibrator) this.calibrator;
919
920 final var size = sequences.size();
921 final var qualityScores = new double[size];
922 for (var i = 0; i < size; i++) {
923 qualityScores[i] = qualityScoreMapper.map(sequences.get(i));
924 }
925 cal.setQualityScores(qualityScores);
926 }
927
928 calibrator.calibrate();
929
930 final var mse = calibrator.getEstimatedMse();
931 if (mse < minMse) {
932 keepBestResult(mse, thresholdFactor);
933 }
934 return mse;
935 }
936
937
938
939
940
941
942 private void initialize() {
943 final var generatorListener = new GyroscopeMeasurementsGeneratorListener() {
944 @Override
945 public void onInitializationStarted(final GyroscopeMeasurementsGenerator generator) {
946
947 }
948
949 @Override
950 public void onInitializationCompleted(
951 final GyroscopeMeasurementsGenerator generator,
952 final double baseNoiseLevel) {
953
954 }
955
956 @Override
957 public void onError(
958 final GyroscopeMeasurementsGenerator generator,
959 final TriadStaticIntervalDetector.ErrorReason reason) {
960
961 }
962
963 @Override
964 public void onStaticIntervalDetected(final GyroscopeMeasurementsGenerator generator) {
965
966 }
967
968 @Override
969 public void onDynamicIntervalDetected(final GyroscopeMeasurementsGenerator generator) {
970
971 }
972
973 @Override
974 public void onStaticIntervalSkipped(final GyroscopeMeasurementsGenerator generator) {
975
976 }
977
978 @Override
979 public void onDynamicIntervalSkipped(final GyroscopeMeasurementsGenerator generator) {
980
981 }
982
983 @Override
984 public void onGeneratedMeasurement(
985 final GyroscopeMeasurementsGenerator generator,
986 final BodyKinematicsSequence<StandardDeviationTimedBodyKinematics> sequence) {
987 sequences.add(sequence);
988 }
989
990 @Override
991 public void onReset(final GyroscopeMeasurementsGenerator generator) {
992
993 }
994 };
995
996 generator = new GyroscopeMeasurementsGenerator(generatorListener);
997 }
998
999
1000
1001
1002
1003
1004
1005 private void keepBestResult(final double mse, final double thresholdFactor) {
1006 minMse = mse;
1007 optimalThresholdFactor = thresholdFactor;
1008
1009 angularSpeedNoiseRootPsd = generator.getGyroscopeBaseNoiseLevelRootPsd();
1010 baseNoiseLevel = generator.getAccelerometerBaseNoiseLevel();
1011 threshold = generator.getThreshold();
1012
1013 if (estimatedCovariance == null) {
1014 estimatedCovariance = new Matrix(calibrator.getEstimatedCovariance());
1015 } else {
1016 estimatedCovariance.copyFrom(calibrator.getEstimatedCovariance());
1017 }
1018 if (estimatedMg == null) {
1019 estimatedMg = new Matrix(calibrator.getEstimatedMg());
1020 } else {
1021 estimatedMg.copyFrom(calibrator.getEstimatedMg());
1022 }
1023 if (estimatedGg == null) {
1024 estimatedGg = new Matrix(calibrator.getEstimatedGg());
1025 } else {
1026 estimatedGg.copyFrom(calibrator.getEstimatedGg());
1027 }
1028 if (calibrator instanceof UnknownBiasGyroscopeCalibrator unknownBiasGyroscopeCalibrator) {
1029 estimatedBiases = unknownBiasGyroscopeCalibrator.getEstimatedBiases();
1030 } else if (calibrator instanceof KnownBiasAccelerometerCalibrator knownBiasAccelerometerCalibrator) {
1031 estimatedBiases = knownBiasAccelerometerCalibrator.getBias();
1032 }
1033 }
1034
1035
1036
1037
1038
1039
1040
1041
1042 private Acceleration createMeasurement(final double value, final AccelerationUnit unit) {
1043 return new Acceleration(value, unit);
1044 }
1045
1046
1047
1048
1049
1050
1051
1052 private AccelerationUnit getDefaultUnit() {
1053 return AccelerationUnit.METERS_PER_SQUARED_SECOND;
1054 }
1055 }