Skip to content

Commit 17526d2

Browse files
committed
udpate docstrings
1 parent bd99581 commit 17526d2

1 file changed

Lines changed: 110 additions & 43 deletions

File tree

gnss_lib_py/algorithms/fde.py

Lines changed: 110 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,23 @@ def solve_fde(navdata, method="residual", remove_outliers=False,
2222
Parameters
2323
----------
2424
navdata : gnss_lib_py.parsers.navdata.NavData
25-
NavData of GNSS measurements which must include the receiver's
26-
estimated state: x_rx*_m, y_rx*_m, z_rx*_m, b_rx*_m as well as
27-
the satellite states: x_sv_m, y_sv_m, z_sv_m, b_sv_m as well
28-
as timing "gps_millis" and the corrected pseudorange corr_pr_m.
25+
NavData of GNSS measurements which must include the satellite
26+
positions: ``x_sv_m``, ``y_sv_m``, ``z_sv_m``, ``b_sv_m`` as
27+
well as the time row ``gps_millis`` and the corrected
28+
pseudorange ``corr_pr_m``.
2929
method : string
3030
Method for fault detection and exclusion either "residual" for
3131
residual-based or "edm" for Euclidean Distance Matrix-based.
3232
remove_outliers : bool
33-
If true, will remove detected faults from NavData instance and
34-
unknown timesteps.
33+
If true, will remove detected faults from the returned NavData
34+
instance and unknown timesteps (both 1 and 2 fault status).
3535
If false, will detect but not exclude faults or unknowns.
3636
max_faults : int
3737
Maximum number of faults to detect and/or exclude.
3838
threshold : float
3939
Detection threshold.
40+
verbose : bool
41+
If true, prints extra debugging statements.
4042
4143
Returns
4244
-------
@@ -64,22 +66,26 @@ def solve_fde(navdata, method="residual", remove_outliers=False,
6466

6567
return navdata
6668

67-
def fde_edm(navdata, max_faults=None, threshold=1.0, verbose=False,
68-
time_fde=False):
69+
def fde_edm(navdata, max_faults=None, threshold=1.0, time_fde=False,
70+
verbose=False):
6971
"""Euclidean distance matrix-based fault detection and exclusion.
7072
7173
See [1]_ for more detailed explanation of algorithm.
7274
7375
Parameters
7476
----------
7577
navdata : gnss_lib_py.parsers.navdata.NavData
76-
NavData of GNSS measurements which must include the
77-
the satellite states: x_sv_m, y_sv_m, z_sv_m, b_sv_m as well
78-
as gps_millis and the corrected pseudorange corr_pr_m.
78+
NavData of GNSS measurements which must include the satellite
79+
positions: ``x_sv_m``, ``y_sv_m``, ``z_sv_m``, ``b_sv_m`` as
80+
well as the time row ``gps_millis`` and the corrected
81+
pseudorange ``corr_pr_m``.
7982
max_faults : int
8083
Maximum number of faults to detect and/or exclude.
8184
threshold : float
8285
Detection threshold.
86+
time_fde : bool
87+
If true, will time the fault detection and exclusion steps and
88+
return that info.
8389
verbose : bool
8490
Prints extra debugging print statements if true.
8591
@@ -90,6 +96,9 @@ def fde_edm(navdata, max_faults=None, threshold=1.0, verbose=False,
9096
value of 1 indicates a detected fault and 0 indicates that
9197
no fault was detected, and 2 indicates an unknown fault status
9298
usually due to lack of necessary columns or information.
99+
timing_info : dict
100+
If time_fde is true, also returns a dictionary of the compute
101+
times in seconds for each iteration.
93102
94103
References
95104
----------
@@ -113,7 +122,7 @@ def fde_edm(navdata, max_faults=None, threshold=1.0, verbose=False,
113122
compute_times = []
114123
navdata_timing = []
115124

116-
for ts, _, navdata_subset in navdata.loop_time('gps_millis'):
125+
for _, _, navdata_subset in navdata.loop_time('gps_millis'):
117126
if time_fde:
118127
time_start = time.time()
119128

@@ -149,7 +158,7 @@ def fde_edm(navdata, max_faults=None, threshold=1.0, verbose=False,
149158
break
150159

151160
edm_detect = np.delete(np.delete(edm,fault_idxs,0),fault_idxs,1)
152-
detection_statistic_detect, svd_u, svd_s, _ = _edm_detection_statistic(edm_detect)
161+
detection_statistic_detect, svd_u, _, _ = _edm_detection_statistic(edm_detect)
153162
detection_statistic = detection_statistic_detect[0]
154163

155164
if verbose:
@@ -240,23 +249,26 @@ def fde_edm(navdata, max_faults=None, threshold=1.0, verbose=False,
240249
return navdata, timing_info
241250
return navdata
242251

243-
def fde_greedy_residual(navdata, max_faults, threshold,
244-
verbose=False, time_fde=False):
252+
def fde_greedy_residual(navdata, max_faults, threshold, time_fde=False,
253+
verbose=False):
245254
"""Residual-based fault detection and exclusion.
246255
247-
Implemented based on paper from Blanch et al [1]_.
256+
Implemented based on paper from Blanch et al [2]_.
248257
249258
Parameters
250259
----------
251260
navdata : gnss_lib_py.parsers.navdata.NavData
252-
NavData of GNSS measurements which must include the receiver's
253-
estimated state: x_rx*_m, y_rx*_m, z_rx*_m, b_rx*_m as well as
254-
the satellite states: x_sv_m, y_sv_m, z_sv_m, b_sv_m as well
255-
as timing "gps_millis" and the corrected pseudorange corr_pr_m.
261+
NavData of GNSS measurements which must include the satellite
262+
positions: ``x_sv_m``, ``y_sv_m``, ``z_sv_m``, ``b_sv_m`` as
263+
well as the time row ``gps_millis`` and the corrected
264+
pseudorange ``corr_pr_m``.
256265
max_faults : int
257266
Maximum number of faults to detect and/or exclude.
258267
threshold : float
259268
Detection threshold.
269+
time_fde : bool
270+
If true, will time the fault detection and exclusion steps and
271+
return that info.
260272
verbose : bool
261273
Prints extra debugging print statements if true.
262274
@@ -267,10 +279,13 @@ def fde_greedy_residual(navdata, max_faults, threshold,
267279
value of 1 indicates a detected fault and 0 indicates that
268280
no fault was detected, and 2 indicates an unknown fault status
269281
usually due to lack of necessary columns or information.
282+
timing_info : dict
283+
If time_fde is true, also returns a dictionary of the compute
284+
times in seconds for each iteration.
270285
271286
References
272287
----------
273-
.. [1] Blanch, Juan, Todd Walter, and Per Enge. "Fast multiple fault
288+
.. [2] Blanch, Juan, Todd Walter, and Per Enge. "Fast multiple fault
274289
exclusion with a large number of measurements." Proceedings
275290
of the 2015 International Technical Meeting of the Institute
276291
of Navigation. 2015.
@@ -321,7 +336,8 @@ def fde_greedy_residual(navdata, max_faults, threshold,
321336
# greedy removal if chi_square above detection threshold
322337
while chi_square > threshold:
323338

324-
if len(navdata_subset) < 5 or (max_faults is not None and len(fault_idxs) >= max_faults):
339+
if len(navdata_subset) < 5 or (max_faults is not None \
340+
and len(fault_idxs) >= max_faults):
325341
break
326342

327343
normalized_residual = _residual_exclude(navdata_subset,receiver_state)
@@ -365,10 +381,16 @@ def evaluate_fde(navdata, method, fault_truth_row="fault_gt",
365381
**kwargs):
366382
"""Evaluate FDE methods and compute accuracy scores
367383
384+
The row designated in the ``fault_truth_row`` variable must indicate
385+
ground truth faults according to the following convention.
386+
A value of 1 indicates a fault and 0 indicates no fault. 2 indicates
387+
an unknown fault status usually due to lack of necessary columns or
388+
information.
389+
368390
Measurements that are returned as "unknown" (fault=2) by the fault
369391
detection method are excluded from all accuracy scores.
370392
371-
Accuracy metrics are defined accordingly.
393+
Accuracy metrics are defined accordingly:
372394
373395
True Positive (TP) : estimated = 1, truth = 1
374396
True Negative (TN) : estimated = 0, truth = 0
@@ -389,8 +411,9 @@ def evaluate_fde(navdata, method, fault_truth_row="fault_gt",
389411
----------
390412
navdata : gnss_lib_py.parsers.navdata.NavData
391413
NavData of GNSS measurements which must include the satellite
392-
positions: x_sv_m, y_sv_m, z_sv_m, b_sv_m as well
393-
as timing "gps_millis" and the corrected pseudorange corr_pr_m.
414+
positions: ``x_sv_m``, ``y_sv_m``, ``z_sv_m``, ``b_sv_m`` as
415+
well as the time row ``gps_millis`` and the corrected
416+
pseudorange ``corr_pr_m``.
394417
Additionally, the ground truth fault row must exist as indicated
395418
by the fault the ``fault_truth_row`` variable.
396419
method : string
@@ -402,10 +425,17 @@ def evaluate_fde(navdata, method, fault_truth_row="fault_gt",
402425
row is used to provide results on how well each method performs
403426
at fault detection and exclusion.
404427
time_fde : bool
405-
Additional debugging info added like timing
428+
Additional debugging info added like timing.
406429
verbose : bool
407430
Prints extra debugging print statements if true.
408431
432+
Returns
433+
-------
434+
metrics : dict
435+
Combined metrics that were computed.
436+
navdata : gnss_lib_py.parsers.navdata.NavData
437+
Resulting NavData from ``solve_fde()``.
438+
409439
"""
410440
truth_fault_counts = []
411441
measurement_counts = []
@@ -480,8 +510,6 @@ def evaluate_fde(navdata, method, fault_truth_row="fault_gt",
480510
if (true_positive + false_alarm) else 0.
481511
recall = tpr
482512

483-
484-
485513
if verbose:
486514
print("\n")
487515
print(method.upper() + " FDE METRICS")
@@ -545,7 +573,7 @@ def evaluate_fde(navdata, method, fault_truth_row="fault_gt",
545573
def _edm(points):
546574
"""Creates a Euclidean distance matrix (EDM) from point locations.
547575
548-
See [1]_ for more explanation.
576+
See [3]_ for more explanation.
549577
550578
Parameters
551579
----------
@@ -562,7 +590,7 @@ def _edm(points):
562590
563591
References
564592
----------
565-
.. [1] I. Dokmanic, R. Parhizkar, J. Ranieri, and M. Vetterli.
593+
.. [3] I. Dokmanic, R. Parhizkar, J. Ranieri, and M. Vetterli.
566594
“Euclidean Distance Matrices: Essential Theory, Algorithms,
567595
and Applications.” 2015. arxiv.org/abs/1502.07541.
568596
@@ -579,6 +607,8 @@ def _edm_from_satellites_ranges(sv_pos, ranges):
579607
Creates an EDM from a combination of known satellite positions as
580608
well as ranges from between the receiver and satellites.
581609
610+
Technique introduced in detail in [4]_.
611+
582612
Parameters
583613
----------
584614
sv_pos : np.array
@@ -590,10 +620,17 @@ def _edm_from_satellites_ranges(sv_pos, ranges):
590620
591621
Returns
592622
-------
593-
D : np.array
623+
edm : np.array
594624
Euclidean distance matrix in the shape (1 + s) x (1 + s) where
595625
s is the number of satellites
596626
627+
References
628+
----------
629+
.. [4] Knowles, Derek, and Grace Gao. "Euclidean distance
630+
matrix-based rapid fault detection and exclusion."
631+
NAVIGATION: Journal of the Institute of Navigation 70.1
632+
(2023).
633+
597634
"""
598635
num_s = sv_pos.shape[1]
599636
edm = np.zeros((num_s+1,num_s+1))
@@ -604,7 +641,7 @@ def _edm_from_satellites_ranges(sv_pos, ranges):
604641
return edm
605642

606643
def _edm_detection_statistic(edm):
607-
"""Calculate the EDM FDE detection statistic [4]_.
644+
"""Calculate the EDM FDE detection statistic [5]_.
608645
609646
Parameters
610647
----------
@@ -624,7 +661,7 @@ def _edm_detection_statistic(edm):
624661
625662
References
626663
----------
627-
.. [4] D. Knowles and G. Gao. "Detection and Exclusion of Multiple
664+
.. [5] D. Knowles and G. Gao. "Detection and Exclusion of Multiple
628665
Faults using Euclidean Distance Matrices." ION GNSS+ 2023.
629666
630667
"""
@@ -651,20 +688,33 @@ def _edm_detection_statistic(edm):
651688
def _residual_chi_square(navdata, receiver_state):
652689
"""Chi square test for residuals.
653690
654-
Implemented from Blanch et al [2]_.
691+
Implemented from Blanch et al [6]_.
692+
693+
Parameters
694+
----------
695+
navdata : gnss_lib_py.parsers.navdata.NavData
696+
NavData of GNSS measurements which must include the satellite
697+
positions: ``x_sv_m``, ``y_sv_m``, ``z_sv_m`` and residuals
698+
``residuals_m``.
699+
receiver_state : gnss_lib_py.parsers.navdata.NavData
700+
Reciever state that must include the receiver's state of:
701+
``x_rx_wls_m``, ``y_rx_wls_m``, and ``z_rx_wls_m``.
702+
703+
Returns
704+
-------
705+
chi_square : float
706+
Chi square test statistic.
655707
656708
References
657709
----------
658-
.. [2] Blanch, Juan, Todd Walter, and Per Enge. "Fast multiple fault
710+
.. [6] Blanch, Juan, Todd Walter, and Per Enge. "Fast multiple fault
659711
exclusion with a large number of measurements." Proceedings
660712
of the 2015 International Technical Meeting of the Institute
661713
of Navigation. 2015.
662714
663715
"""
664716

665717
# solve for residuals
666-
receiver_state = solve_wls(navdata)
667-
solve_residuals(navdata, receiver_state, inplace=True)
668718
residuals = navdata["residuals_m"].reshape(-1,1)
669719

670720
# weights
@@ -676,26 +726,41 @@ def _residual_chi_square(navdata, receiver_state):
676726
geo_matrix /= np.linalg.norm(geo_matrix,axis=0)
677727

678728
chi_square = residuals.T @ (weights - weights @ geo_matrix \
679-
@ np.linalg.pinv(geo_matrix.T @ weights @ geo_matrix) @ geo_matrix.T @ weights ) @ residuals
729+
@ np.linalg.pinv(geo_matrix.T @ weights @ geo_matrix) \
730+
@ geo_matrix.T @ weights ) @ residuals
680731
chi_square = chi_square.item()
681732

682733
return chi_square
683734

684735
def _residual_exclude(navdata, receiver_state):
685736
"""Detection statistic for Residual-based fault detection.
686737
687-
Implemented from Blanch et al [3]_.
738+
Implemented from Blanch et al [7]_.
739+
740+
Parameters
741+
----------
742+
navdata : gnss_lib_py.parsers.navdata.NavData
743+
NavData of GNSS measurements which must include the satellite
744+
positions: ``x_sv_m``, ``y_sv_m``, ``z_sv_m`` and residuals
745+
``residuals_m``.
746+
receiver_state : gnss_lib_py.parsers.navdata.NavData
747+
Reciever state that must include the receiver's state of:
748+
``x_rx_wls_m``, ``y_rx_wls_m``, and ``z_rx_wls_m``.
749+
750+
Returns
751+
-------
752+
normalized_residual : np.ndarray
753+
Array of the normalized residual for each satellite.
688754
689755
References
690756
----------
691-
.. [3] Blanch, Juan, Todd Walter, and Per Enge. "Fast multiple fault
757+
.. [7] Blanch, Juan, Todd Walter, and Per Enge. "Fast multiple fault
692758
exclusion with a large number of measurements." Proceedings
693759
of the 2015 International Technical Meeting of the Institute
694760
of Navigation. 2015.
695761
696762
"""
697763
# solve for residuals
698-
699764
residuals = navdata["residuals_m"].reshape(-1,1)
700765

701766
# weights
@@ -707,12 +772,14 @@ def _residual_exclude(navdata, receiver_state):
707772
geo_matrix /= np.linalg.norm(geo_matrix,axis=0)
708773

709774
# calculate normalized residual
710-
x_tilde = np.linalg.pinv(geo_matrix.T @ weights @ geo_matrix) @ geo_matrix.T @ weights @ residuals
775+
x_tilde = np.linalg.pinv(geo_matrix.T @ weights @ geo_matrix) \
776+
@ geo_matrix.T @ weights @ residuals
711777

712778
normalized_residual = np.divide(np.multiply(np.diag(weights).reshape(-1,1),
713779
(residuals - geo_matrix @ x_tilde)**2),
714780
np.diag(1 - weights @ geo_matrix @ \
715-
np.linalg.pinv(geo_matrix.T @ weights @ geo_matrix) @ geo_matrix.T).reshape(-1,1))
781+
np.linalg.pinv(geo_matrix.T @ weights @ geo_matrix) \
782+
@ geo_matrix.T).reshape(-1,1))
716783

717784
normalized_residual = normalized_residual[:,0]
718785

0 commit comments

Comments
 (0)