1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.irurueta.navigation.gnss;
17
18 import com.irurueta.algebra.AlgebraException;
19 import com.irurueta.algebra.Matrix;
20 import com.irurueta.algebra.Utils;
21 import com.irurueta.algebra.WrongSizeException;
22 import com.irurueta.navigation.LockedException;
23 import com.irurueta.navigation.NotReadyException;
24 import com.irurueta.navigation.frames.ECEFPosition;
25 import com.irurueta.navigation.frames.ECEFVelocity;
26 import com.irurueta.navigation.frames.NEDPosition;
27 import com.irurueta.navigation.frames.NEDVelocity;
28 import com.irurueta.navigation.frames.converters.ECEFtoNEDPositionVelocityConverter;
29 import com.irurueta.navigation.frames.converters.NEDtoECEFPositionVelocityConverter;
30 import com.irurueta.navigation.geodesic.Constants;
31
32 import java.util.Collection;
33
34
35
36
37
38
39
40
41
42
43
44
45 public class GNSSLeastSquaresPositionAndVelocityEstimator {
46
47
48
49
50 public static final int MIN_MEASUREMENTS = 4;
51
52
53
54
55 public static final double CONVERGENCE_THRESHOLD = 1e-4;
56
57
58
59
60 public static final double SPEED_OF_LIGHT = Constants.SPEED_OF_LIGHT;
61
62
63
64
65 public static final double EARTH_ROTATION_RATE = Constants.EARTH_ROTATION_RATE;
66
67
68
69
70 private static final int STATE_COMPONENTS = ECEFPosition.COMPONENTS + 1;
71
72
73
74
75 private static final int ELEMS = ECEFPosition.COMPONENTS;
76
77
78
79
80 private static final int ELEMS_MINUS_ONE = ELEMS - 1;
81
82
83
84
85 private Collection<GNSSMeasurement> measurements;
86
87
88
89
90 private ECEFPositionAndVelocity priorPositionAndVelocity;
91
92
93
94
95 private GNSSLeastSquaresPositionAndVelocityEstimatorListener listener;
96
97
98
99
100 private double convergenceThreshold = CONVERGENCE_THRESHOLD;
101
102
103
104
105 private boolean running;
106
107
108
109
110
111 private final Matrix cei;
112
113
114
115
116 private final Matrix xPred;
117
118
119
120
121 private final Matrix tmp1;
122
123
124
125
126 private final Matrix tmp2;
127
128
129
130
131 private final Matrix xEst;
132
133
134
135
136 private final Matrix hSqr;
137
138
139
140
141 private final Matrix invHSqr;
142
143
144
145
146 private final Matrix tmp3;
147
148
149
150
151 private final Matrix omegaIe;
152
153
154
155
156 private final Matrix tmp4;
157
158
159
160
161 private final Matrix tmp5;
162
163
164
165
166 private final Matrix tmp6;
167
168
169
170
171 private final Matrix tmp7;
172
173
174
175
176 private final Matrix tmp8;
177
178
179
180
181 private final Matrix tmp9;
182
183
184
185
186 private final Matrix tmp10;
187
188
189
190
191 private final Matrix measurementPosition;
192
193
194
195
196 private final Matrix measurementVelocity;
197
198
199
200
201 private final Matrix predVelocity;
202
203
204
205
206 private final Matrix resultPosition;
207
208
209
210
211 public GNSSLeastSquaresPositionAndVelocityEstimator() {
212 Matrix cxPred = null;
213 Matrix ctmp1 = null;
214 Matrix ctmp2 = null;
215 Matrix cxEst = null;
216 Matrix chSqr = null;
217 Matrix cinvHSqr = null;
218 Matrix ctmp3 = null;
219 Matrix comegaIe = null;
220 Matrix ccei = null;
221 Matrix ctmp4 = null;
222 Matrix ctmp5 = null;
223 Matrix ctmp6 = null;
224 Matrix ctmp7 = null;
225 Matrix ctmp8 = null;
226 Matrix ctmp9 = null;
227 Matrix ctmp10 = null;
228 Matrix cmeasurementPosition = null;
229 Matrix cmeasurementVelocity = null;
230 Matrix cpredVelocity = null;
231 Matrix cresultPosition = null;
232 try {
233 ccei = Matrix.identity(ELEMS, ELEMS);
234 cxPred = new Matrix(STATE_COMPONENTS, 1);
235 ctmp1 = new Matrix(ELEMS, 1);
236 ctmp2 = new Matrix(ELEMS, 1);
237 cxEst = new Matrix(STATE_COMPONENTS, 1);
238 chSqr = new Matrix(STATE_COMPONENTS, STATE_COMPONENTS);
239 cinvHSqr = new Matrix(STATE_COMPONENTS, STATE_COMPONENTS);
240 ctmp3 = new Matrix(STATE_COMPONENTS, 1);
241 comegaIe = Utils.skewMatrix(new double[]{0.0, 0.0, EARTH_ROTATION_RATE});
242 ctmp4 = new Matrix(STATE_COMPONENTS, 1);
243 ctmp5 = new Matrix(STATE_COMPONENTS, 1);
244 ctmp6 = new Matrix(STATE_COMPONENTS, 1);
245 ctmp7 = new Matrix(STATE_COMPONENTS, 1);
246 ctmp8 = new Matrix(STATE_COMPONENTS, 1);
247 ctmp9 = new Matrix(STATE_COMPONENTS, 1);
248 ctmp10 = new Matrix(STATE_COMPONENTS, 1);
249 cmeasurementPosition = new Matrix(ELEMS, 1);
250 cmeasurementVelocity = new Matrix(ELEMS, 1);
251 cpredVelocity = new Matrix(ELEMS, 1);
252 cresultPosition = new Matrix(ELEMS, 1);
253 } catch (WrongSizeException ignore) {
254
255 }
256
257 this.cei = ccei;
258 this.xPred = cxPred;
259 this.tmp1 = ctmp1;
260 this.tmp2 = ctmp2;
261 this.xEst = cxEst;
262 this.hSqr = chSqr;
263 this.invHSqr = cinvHSqr;
264 this.tmp3 = ctmp3;
265 this.omegaIe = comegaIe;
266 this.tmp4 = ctmp4;
267 this.tmp5 = ctmp5;
268 this.tmp6 = ctmp6;
269 this.tmp7 = ctmp7;
270 this.tmp8 = ctmp8;
271 this.tmp9 = ctmp9;
272 this.tmp10 = ctmp10;
273 this.measurementPosition = cmeasurementPosition;
274 this.measurementVelocity = cmeasurementVelocity;
275 this.predVelocity = cpredVelocity;
276 this.resultPosition = cresultPosition;
277 }
278
279
280
281
282
283
284
285 public GNSSLeastSquaresPositionAndVelocityEstimator(final Collection<GNSSMeasurement> measurements) {
286 this();
287 try {
288 setMeasurements(measurements);
289 } catch (final LockedException ignore) {
290
291 }
292 }
293
294
295
296
297
298
299
300
301
302 public GNSSLeastSquaresPositionAndVelocityEstimator(
303 final Collection<GNSSMeasurement> measurements,
304 final ECEFPositionAndVelocity priorPositionAndVelocity) {
305 this();
306 try {
307 setMeasurements(measurements);
308 setPriorPositionAndVelocity(priorPositionAndVelocity);
309 } catch (final LockedException ignore) {
310
311 }
312 }
313
314
315
316
317
318
319
320
321 public GNSSLeastSquaresPositionAndVelocityEstimator(
322 final Collection<GNSSMeasurement> measurements,
323 final GNSSEstimation priorEstimation) {
324 this();
325 try {
326 setMeasurements(measurements);
327 setPriorPositionAndVelocityFromEstimation(priorEstimation);
328 } catch (final LockedException ignore) {
329
330 }
331 }
332
333
334
335
336
337
338 public GNSSLeastSquaresPositionAndVelocityEstimator(
339 final GNSSLeastSquaresPositionAndVelocityEstimatorListener listener) {
340 this();
341 try {
342 setListener(listener);
343 } catch (final LockedException ignore) {
344
345 }
346 }
347
348
349
350
351
352
353
354
355 public GNSSLeastSquaresPositionAndVelocityEstimator(
356 final Collection<GNSSMeasurement> measurements,
357 final GNSSLeastSquaresPositionAndVelocityEstimatorListener listener) {
358 this(measurements);
359 try {
360 setListener(listener);
361 } catch (final LockedException ignore) {
362
363 }
364 }
365
366
367
368
369
370
371
372
373
374
375
376 public GNSSLeastSquaresPositionAndVelocityEstimator(
377 final Collection<GNSSMeasurement> measurements,
378 final ECEFPositionAndVelocity priorPositionAndVelocity,
379 final GNSSLeastSquaresPositionAndVelocityEstimatorListener listener) {
380 this(measurements, priorPositionAndVelocity);
381 try {
382 setListener(listener);
383 } catch (final LockedException ignore) {
384
385 }
386 }
387
388
389
390
391
392
393
394
395
396 public GNSSLeastSquaresPositionAndVelocityEstimator(
397 final Collection<GNSSMeasurement> measurements,
398 final GNSSEstimation priorEstimation,
399 final GNSSLeastSquaresPositionAndVelocityEstimatorListener listener) {
400 this(measurements, priorEstimation);
401 try {
402 setListener(listener);
403 } catch (final LockedException ignore) {
404
405 }
406 }
407
408
409
410
411
412
413 public Collection<GNSSMeasurement> getMeasurements() {
414 return measurements;
415 }
416
417
418
419
420
421
422
423
424 public void setMeasurements(final Collection<GNSSMeasurement> measurements) throws LockedException {
425 if (running) {
426 throw new LockedException();
427 }
428 if (!isValidMeasurements(measurements)) {
429 throw new IllegalArgumentException();
430 }
431
432 this.measurements = measurements;
433 }
434
435
436
437
438
439
440 public ECEFPositionAndVelocity getPriorPositionAndVelocity() {
441 return priorPositionAndVelocity;
442 }
443
444
445
446
447
448
449
450
451 public void setPriorPositionAndVelocity(
452 final ECEFPositionAndVelocity priorPositionAndVelocity) throws LockedException {
453 if (running) {
454 throw new LockedException();
455 }
456
457 this.priorPositionAndVelocity = priorPositionAndVelocity;
458 }
459
460
461
462
463
464
465
466
467 public void setPriorPositionAndVelocityFromEstimation(
468 final GNSSEstimation priorEstimation) throws LockedException {
469 if (running) {
470 throw new LockedException();
471 }
472
473 priorPositionAndVelocity = priorEstimation != null ? priorEstimation.getPositionAndVelocity() : null;
474 }
475
476
477
478
479
480
481 public GNSSLeastSquaresPositionAndVelocityEstimatorListener getListener() {
482 return listener;
483 }
484
485
486
487
488
489
490
491 public void setListener(
492 final GNSSLeastSquaresPositionAndVelocityEstimatorListener listener) throws LockedException {
493 if (running) {
494 throw new LockedException();
495 }
496 this.listener = listener;
497 }
498
499
500
501
502
503
504 public double getConvergenceThreshold() {
505 return convergenceThreshold;
506 }
507
508
509
510
511
512
513
514
515
516 public void setConvergenceThreshold(final double convergenceThreshold) throws LockedException,
517 IllegalArgumentException {
518 if (running) {
519 throw new LockedException();
520 }
521 if (convergenceThreshold <= 0.0) {
522 throw new IllegalArgumentException();
523 }
524
525 this.convergenceThreshold = convergenceThreshold;
526 }
527
528
529
530
531
532
533 public boolean isReady() {
534 return isValidMeasurements(measurements);
535 }
536
537
538
539
540
541
542 public boolean isRunning() {
543 return running;
544 }
545
546
547
548
549
550
551
552 public static boolean isValidMeasurements(final Collection<GNSSMeasurement> gnssMeasurements) {
553 return gnssMeasurements != null && gnssMeasurements.size() >= MIN_MEASUREMENTS;
554 }
555
556
557
558
559
560
561
562
563
564
565 @SuppressWarnings("DuplicatedCode")
566 public void estimate(final GNSSEstimation result) throws NotReadyException, LockedException, GNSSException {
567 if (!isReady()) {
568 throw new NotReadyException();
569 }
570 if (running) {
571 throw new LockedException();
572 }
573
574 try {
575 running = true;
576
577 if (listener != null) {
578 listener.onEstimateStart(this);
579 }
580
581
582
583
584 initializePriorPositionAndVelocityIfNeeded();
585
586
587
588
589 final var priorX = priorPositionAndVelocity.getX();
590 final var priorY = priorPositionAndVelocity.getY();
591 final var priorZ = priorPositionAndVelocity.getZ();
592
593 xPred.setElementAtIndex(0, priorX);
594 xPred.setElementAtIndex(1, priorY);
595 xPred.setElementAtIndex(2, priorZ);
596 xPred.setElementAtIndex(3, 0.0);
597
598 final var numMeasurements = measurements.size();
599 final var predMeas = new Matrix(numMeasurements, 1);
600 final var h = new Matrix(numMeasurements, STATE_COMPONENTS);
601 for (var i = 0; i < numMeasurements; i++) {
602 h.setElementAt(i, 3, 1.0);
603 }
604
605 final var hTrans = new Matrix(STATE_COMPONENTS, numMeasurements);
606 final var hTmp1 = new Matrix(STATE_COMPONENTS, numMeasurements);
607 final var deltaPseudoRange = new Matrix(numMeasurements, 1);
608
609
610 var testConvergence = 1.0;
611 while (testConvergence > convergenceThreshold) {
612
613
614 var j = 0;
615 for (final var measurement : measurements) {
616
617
618 final var measX = measurement.getX();
619 final var measY = measurement.getY();
620 final var measZ = measurement.getZ();
621
622 var deltaRx = measX - priorX;
623 var deltaRy = measY - priorY;
624 var deltaRz = measZ - priorZ;
625 final var approxRange = norm(deltaRx, deltaRy, deltaRz);
626
627
628 final var ceiValue = EARTH_ROTATION_RATE * approxRange / SPEED_OF_LIGHT;
629 cei.setElementAt(0, 1, ceiValue);
630 cei.setElementAt(1, 0, -ceiValue);
631
632
633 tmp1.setElementAtIndex(0, measX);
634 tmp1.setElementAtIndex(1, measY);
635 tmp1.setElementAtIndex(2, measZ);
636
637 cei.multiply(tmp1, tmp2);
638
639 deltaRx = tmp2.getElementAtIndex(0) - xPred.getElementAtIndex(0);
640 deltaRy = tmp2.getElementAtIndex(1) - xPred.getElementAtIndex(1);
641 deltaRz = tmp2.getElementAtIndex(2) - xPred.getElementAtIndex(2);
642 final var range = norm(deltaRx, deltaRy, deltaRz);
643
644 final var predictedPseudoRange = range + xPred.getElementAtIndex(3);
645 predMeas.setElementAtIndex(j, predictedPseudoRange);
646
647 deltaPseudoRange.setElementAtIndex(j, measurement.getPseudoRange() - predictedPseudoRange);
648
649
650 h.setElementAt(j, 0, -deltaRx / range);
651 h.setElementAt(j, 1, -deltaRy / range);
652 h.setElementAt(j, 2, -deltaRz / range);
653
654 j++;
655 }
656
657
658 h.transpose(hTrans);
659 hTrans.multiply(h, hSqr);
660 Utils.inverse(hSqr, invHSqr);
661 invHSqr.multiply(hTrans, hTmp1);
662 hTmp1.multiply(deltaPseudoRange, tmp3);
663
664 xPred.add(tmp3, xEst);
665
666
667 testConvergence = predictionError();
668
669
670 xPred.copyFrom(xEst);
671 }
672
673
674 final var resultX = xEst.getElementAtIndex(0);
675 final var resultY = xEst.getElementAtIndex(1);
676 final var resultZ = xEst.getElementAtIndex(2);
677 result.setPositionCoordinates(resultX, resultY, resultZ);
678
679 final var resultClockOffset = xEst.getElementAtIndex(3);
680 result.setClockOffset(resultClockOffset);
681
682
683
684
685
686 final var priorVx = priorPositionAndVelocity.getVx();
687 final var priorVy = priorPositionAndVelocity.getVy();
688 final var priorVz = priorPositionAndVelocity.getVz();
689
690 xPred.setElementAtIndex(0, priorVx);
691 xPred.setElementAtIndex(1, priorVy);
692 xPred.setElementAtIndex(2, priorVz);
693 xPred.setElementAtIndex(3, 0.0);
694
695 resultPosition.setElementAtIndex(0, resultX);
696 resultPosition.setElementAtIndex(1, resultY);
697 resultPosition.setElementAtIndex(2, resultZ);
698
699 final var deltaPseudoRangeRate = new Matrix(numMeasurements, 1);
700
701
702 testConvergence = 1.0;
703 while (testConvergence > convergenceThreshold) {
704
705
706 var j = 0;
707 for (final var measurement : measurements) {
708
709 final var measX = measurement.getX();
710 final var measY = measurement.getY();
711 final var measZ = measurement.getZ();
712
713 var deltaRx = measX - resultX;
714 var deltaRy = measY - resultY;
715 var deltaRz = measZ - resultZ;
716 final var approxRange = norm(deltaRx, deltaRy, deltaRz);
717
718
719 final var ceiValue = EARTH_ROTATION_RATE * approxRange / SPEED_OF_LIGHT;
720 cei.setElementAt(0, 1, ceiValue);
721 cei.setElementAt(1, 0, -ceiValue);
722
723
724 tmp1.setElementAtIndex(0, measX);
725 tmp1.setElementAtIndex(1, measY);
726 tmp1.setElementAtIndex(2, measZ);
727
728 cei.multiply(tmp1, tmp2);
729
730 deltaRx = tmp2.getElementAtIndex(0) - resultX;
731 deltaRy = tmp2.getElementAtIndex(1) - resultY;
732 deltaRz = tmp2.getElementAtIndex(2) - resultZ;
733 final var range = norm(deltaRx, deltaRy, deltaRz);
734
735
736 final var uaseX = deltaRx / range;
737 final var uaseY = deltaRy / range;
738 final var uaseZ = deltaRz / range;
739
740
741 measurementPosition.setElementAtIndex(0, measX);
742 measurementPosition.setElementAtIndex(1, measY);
743 measurementPosition.setElementAtIndex(2, measZ);
744
745 final var measVx = measurement.getVx();
746 final var measVy = measurement.getVy();
747 final var measVz = measurement.getVz();
748
749 measurementVelocity.setElementAtIndex(0, measVx);
750 measurementVelocity.setElementAtIndex(1, measVy);
751 measurementVelocity.setElementAtIndex(2, measVz);
752
753 omegaIe.multiply(measurementPosition, tmp4);
754
755 measurementVelocity.add(tmp4, tmp5);
756
757 cei.multiply(tmp5, tmp6);
758
759 omegaIe.multiply(resultPosition, tmp7);
760
761 xPred.getSubmatrix(0, 0, ELEMS_MINUS_ONE, 0, predVelocity);
762
763 predVelocity.add(tmp7, tmp8);
764
765 tmp6.subtract(tmp8, tmp9);
766
767 final var rangeRate = uaseX * tmp9.getElementAtIndex(0) + uaseY * tmp9.getElementAtIndex(1)
768 + uaseZ * tmp9.getElementAtIndex(2);
769
770 final var predictedPseudoRangeRate = rangeRate + xPred.getElementAtIndex(3);
771 predMeas.setElementAtIndex(j, predictedPseudoRangeRate);
772
773 deltaPseudoRangeRate.setElementAtIndex(j,
774 measurement.getPseudoRate() - predictedPseudoRangeRate);
775
776
777 h.setElementAt(j, 0, -uaseX);
778 h.setElementAt(j, 1, -uaseY);
779 h.setElementAt(j, 2, -uaseZ);
780
781 j++;
782 }
783
784
785 h.transpose(hTrans);
786 hTrans.multiply(h, hSqr);
787 Utils.inverse(hSqr, invHSqr);
788 invHSqr.multiply(hTrans, hTmp1);
789 hTmp1.multiply(deltaPseudoRangeRate, tmp10);
790
791 xPred.add(tmp10, xEst);
792
793
794 testConvergence = predictionError();
795
796
797 xPred.copyFrom(xEst);
798 }
799
800
801 final var resultVx = xEst.getElementAtIndex(0);
802 final var resultVy = xEst.getElementAtIndex(1);
803 final var resultVz = xEst.getElementAtIndex(2);
804 result.setVelocityCoordinates(resultVx, resultVy, resultVz);
805
806 final var resultClockDrift = xEst.getElementAtIndex(3);
807 result.setClockDrift(resultClockDrift);
808
809 } catch (final AlgebraException e) {
810 throw new GNSSException(e);
811 } finally {
812 if (listener != null) {
813 listener.onEstimateEnd(this);
814 }
815
816 running = false;
817 }
818 }
819
820
821
822
823
824
825
826
827
828
829 public GNSSEstimation estimate() throws NotReadyException, LockedException, GNSSException {
830 final var result = new GNSSEstimation();
831 estimate(result);
832 return result;
833 }
834
835
836
837
838
839
840 private void initializePriorPositionAndVelocityIfNeeded() {
841 if (priorPositionAndVelocity != null) {
842 return;
843 }
844
845 var numMeasurements = measurements.size();
846 final var nedPosition = new NEDPosition();
847 final var nedVelocity = new NEDVelocity();
848
849 final var ecefPosition = new ECEFPosition();
850 final var ecefVelocity = new ECEFVelocity();
851
852 var userLatitude = 0.0;
853 var userLongitude = 0.0;
854 for (final var measurement : measurements) {
855 measurement.getEcefPosition(ecefPosition);
856 measurement.getEcefVelocity(ecefVelocity);
857 ECEFtoNEDPositionVelocityConverter.convertECEFtoNED(ecefPosition, ecefVelocity, nedPosition, nedVelocity);
858
859 final var satLatitude = nedPosition.getLatitude();
860 final var satLongitude = nedPosition.getLongitude();
861
862 userLatitude += satLatitude / numMeasurements;
863 userLongitude += satLongitude / numMeasurements;
864 }
865
866 nedPosition.setCoordinates(userLatitude, userLongitude, 0.0);
867 nedVelocity.setCoordinates(0.0, 0.0, 0.0);
868
869 NEDtoECEFPositionVelocityConverter.convertNEDtoECEF(nedPosition, nedVelocity, ecefPosition, ecefVelocity);
870
871 priorPositionAndVelocity = new ECEFPositionAndVelocity(ecefPosition, ecefVelocity);
872 }
873
874
875
876
877
878
879
880
881
882 private static double norm(final double x, final double y, final double z) {
883 return Math.sqrt(x * x + y * y + z * z);
884 }
885
886
887
888
889
890
891
892 private double predictionError() {
893 var sqrPredictionError = 0.0;
894 for (var i = 0; i < STATE_COMPONENTS; i++) {
895 final var diff = xEst.getElementAtIndex(i) - xPred.getElementAtIndex(i);
896 sqrPredictionError += diff * diff;
897 }
898 return Math.sqrt(sqrPredictionError);
899 }
900 }