Skip to content

Commit cd65636

Browse files
committed
add fde tutorial
1 parent 17526d2 commit cd65636

3 files changed

Lines changed: 279 additions & 0 deletions

File tree

docs/source/tutorials/tutorials.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ for baseline position solutions.
4343
:maxdepth: 1
4444

4545
tutorials_algorithms_notebook
46+
tutorials_fde_notebook
4647

4748

4849
Utility Tutorials
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"path": "../../../notebooks/tutorials/fde.ipynb"
3+
}

notebooks/tutorials/fde.ipynb

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "2b7e92ce",
6+
"metadata": {},
7+
"source": [
8+
"This tutorial illustrates a few of the fault detection and exclusion capabilities from `algorithms/fde.py`."
9+
]
10+
},
11+
{
12+
"cell_type": "code",
13+
"execution_count": null,
14+
"id": "cce0e69e",
15+
"metadata": {},
16+
"outputs": [],
17+
"source": [
18+
"import numpy as np\n",
19+
"import gnss_lib_py as glp\n",
20+
"\n",
21+
"# load Android Google Challenge data\n",
22+
"!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/data/unit_test/android_2022/device_gnss.csv --quiet -O \"device_gnss.csv\"\n",
23+
"navdata = glp.AndroidDerived2022(\"device_gnss.csv\")"
24+
]
25+
},
26+
{
27+
"cell_type": "markdown",
28+
"id": "76ee7e5e",
29+
"metadata": {},
30+
"source": [
31+
"# FDE: Fault Detection and Exclusion"
32+
]
33+
},
34+
{
35+
"cell_type": "markdown",
36+
"id": "b9f9b91d-0a3f-49aa-b380-fb6d57639fe6",
37+
"metadata": {},
38+
"source": [
39+
"Several pre-built methods exist for performing fault detection and exclusion and can be accessed through the ``solve_fde()`` function."
40+
]
41+
},
42+
{
43+
"cell_type": "markdown",
44+
"id": "0194ecce",
45+
"metadata": {},
46+
"source": [
47+
"## Greedy Euclidean Distance Matrix FDE"
48+
]
49+
},
50+
{
51+
"cell_type": "markdown",
52+
"id": "067ad75b-d0ff-4a25-8d3b-85bf11634af6",
53+
"metadata": {},
54+
"source": [
55+
"The first method is based on \"Detection and Exclusion of Multiple Faults using Euclidean Distance Matrices\" by Derek Knowles and Grace Gao from the ION GNSS+ 2023 conference."
56+
]
57+
},
58+
{
59+
"cell_type": "code",
60+
"execution_count": null,
61+
"id": "535130e8-c01c-4840-8939-666677ba51cb",
62+
"metadata": {},
63+
"outputs": [],
64+
"source": [
65+
"result = glp.solve_fde(navdata, method=\"edm\")"
66+
]
67+
},
68+
{
69+
"cell_type": "markdown",
70+
"id": "f566b10b-fe33-4140-9e89-69b3c6eee64c",
71+
"metadata": {},
72+
"source": [
73+
"After this method runs, a new row is added called \"fault_edm\" which has a 0 if no fault is predicted, 1 if a fault is predicted, and 2 for an unknown fault status (usually due to lack of necessary columns or information)."
74+
]
75+
},
76+
{
77+
"cell_type": "code",
78+
"execution_count": null,
79+
"id": "b775909a-ac85-4ce8-9fea-6f71cec3f19f",
80+
"metadata": {},
81+
"outputs": [],
82+
"source": [
83+
"result[\"fault_edm\"]"
84+
]
85+
},
86+
{
87+
"cell_type": "markdown",
88+
"id": "9805d5a9-5138-4588-a0af-3e5649ecef36",
89+
"metadata": {},
90+
"source": [
91+
"You can channge the ``threshold`` variable to be more or less sensitive based on false alarm or missed detection system requirements. Greedy EDM FDE has a range for the threshold between 0 and 1 since the detection statistic is normalized to 1. Note that if the threshold is set to the lower limit of zero, faults are detected until only four measurements remain at each timestep."
92+
]
93+
},
94+
{
95+
"cell_type": "code",
96+
"execution_count": null,
97+
"id": "6a144355-2564-45ff-9155-a6a4da1d0755",
98+
"metadata": {},
99+
"outputs": [],
100+
"source": [
101+
"result = glp.solve_fde(navdata, method=\"edm\", \n",
102+
" threshold=0)\n",
103+
"result[\"fault_edm\"]"
104+
]
105+
},
106+
{
107+
"cell_type": "markdown",
108+
"id": "1ed8aa60-6ce4-4ab8-bb35-4084277ba4de",
109+
"metadata": {},
110+
"source": [
111+
"The ``max_faults`` variable can be changed to apply a maximum number of faults detected at each timestep. In this example, the threshold is still set to zero, but we limit the faults removed with the ``max_faults`` variable."
112+
]
113+
},
114+
{
115+
"cell_type": "code",
116+
"execution_count": null,
117+
"id": "c0f8efa0-8be1-49aa-b323-69aa16f5952b",
118+
"metadata": {},
119+
"outputs": [],
120+
"source": [
121+
"result = glp.solve_fde(navdata, method=\"edm\",\n",
122+
" threshold=0, max_faults=4)\n",
123+
"result[\"fault_edm\"]"
124+
]
125+
},
126+
{
127+
"cell_type": "markdown",
128+
"id": "775035ec-8d48-4780-a45f-80c5766b465d",
129+
"metadata": {},
130+
"source": [
131+
"If the ``remove_outliers`` variable is set, then the outliers and unknown statuses will automatically be removed from the returned ``NavData`` object."
132+
]
133+
},
134+
{
135+
"cell_type": "code",
136+
"execution_count": null,
137+
"id": "a09f05e2-9a13-4a7d-8ef2-935b66eb1f87",
138+
"metadata": {},
139+
"outputs": [],
140+
"source": [
141+
"result = glp.solve_fde(navdata, method=\"edm\",\n",
142+
" threshold=0, remove_outliers=True)\n",
143+
"result[\"fault_edm\"]"
144+
]
145+
},
146+
{
147+
"cell_type": "markdown",
148+
"id": "6a8540d7-0652-4a1b-bdf0-d819db379e6d",
149+
"metadata": {},
150+
"source": [
151+
"## Greedy Residual FDE"
152+
]
153+
},
154+
{
155+
"cell_type": "markdown",
156+
"id": "85df2e4f-0298-4cf6-8184-9cc13361bdfc",
157+
"metadata": {},
158+
"source": [
159+
"This FDE method is based on \"Fast multiple fault exclusion with a large number of measurements.\" by Juan, Blanch, Todd Walter, and Per Enge from the ION GNSS+ 2015 conference."
160+
]
161+
},
162+
{
163+
"cell_type": "code",
164+
"execution_count": null,
165+
"id": "9d03b637-9489-45a7-b908-334ad0012874",
166+
"metadata": {},
167+
"outputs": [],
168+
"source": [
169+
"result = glp.solve_fde(navdata, method=\"residual\")"
170+
]
171+
},
172+
{
173+
"cell_type": "markdown",
174+
"id": "5c247a63-db03-4f45-b52a-ad7ede25e41f",
175+
"metadata": {},
176+
"source": [
177+
"After this method runs, a new row is added called \"fault_residual\" which has a 0 if no fault is predicted, 1 if a fault is predicted, and 2 for an unknown fault status (usually due to lack of necessary columns or information)."
178+
]
179+
},
180+
{
181+
"cell_type": "code",
182+
"execution_count": null,
183+
"id": "ebc3467e-7594-405d-85d2-5742acaf82ca",
184+
"metadata": {},
185+
"outputs": [],
186+
"source": [
187+
"result[\"fault_residual\"]"
188+
]
189+
},
190+
{
191+
"cell_type": "markdown",
192+
"id": "2ec33c02-d460-45eb-bdad-9f613461b63a",
193+
"metadata": {},
194+
"source": [
195+
"## Evaluate FDE"
196+
]
197+
},
198+
{
199+
"cell_type": "markdown",
200+
"id": "0d62f70e-3081-459b-b681-63c65ed9ca03",
201+
"metadata": {},
202+
"source": [
203+
"The ``evaluate_fde()`` function can be used to create overall metrics on accuracy and timing based on a ground truth fault status row. The below example shows a comparison between the default parameters for greedy EDM and greedy residual FDE."
204+
]
205+
},
206+
{
207+
"cell_type": "code",
208+
"execution_count": null,
209+
"id": "02263541-fa69-41d0-8834-2c7b1caacff8",
210+
"metadata": {},
211+
"outputs": [],
212+
"source": [
213+
"edm_metrics, _ = glp.evaluate_fde(navdata, method=\"edm\",\n",
214+
" fault_truth_row=\"MultipathIndicator\",\n",
215+
" time_fde=True)\n",
216+
"for key, value in edm_metrics.items():\n",
217+
" print(key,\":\",value)"
218+
]
219+
},
220+
{
221+
"cell_type": "code",
222+
"execution_count": null,
223+
"id": "bfcf48a7-efd7-4324-8446-9a70bf4e1b1b",
224+
"metadata": {},
225+
"outputs": [],
226+
"source": [
227+
"residual_metrics, _ = glp.evaluate_fde(navdata, method=\"residual\",\n",
228+
" fault_truth_row=\"MultipathIndicator\",\n",
229+
" time_fde=True)\n",
230+
"for key, value in residual_metrics.items():\n",
231+
" print(key,\":\",value)"
232+
]
233+
},
234+
{
235+
"cell_type": "markdown",
236+
"id": "cb756cd0-9382-4991-9ed0-3b41b7482841",
237+
"metadata": {},
238+
"source": [
239+
"Checkout the timing difference between the too methods!"
240+
]
241+
},
242+
{
243+
"cell_type": "code",
244+
"execution_count": null,
245+
"id": "d59bd302-d610-4463-ad1f-ab16a8899f0b",
246+
"metadata": {},
247+
"outputs": [],
248+
"source": [
249+
"speed_increase = residual_metrics[\"timestep_mean_ms\"] - edm_metrics[\"timestep_mean_ms\"]\n",
250+
"print(f\"Greedy EDM is {np.round(speed_increase,1)} milliseconds faster than Greedy Residual on average!\")"
251+
]
252+
}
253+
],
254+
"metadata": {
255+
"kernelspec": {
256+
"display_name": "Python 3 (ipykernel)",
257+
"language": "python",
258+
"name": "python3"
259+
},
260+
"language_info": {
261+
"codemirror_mode": {
262+
"name": "ipython",
263+
"version": 3
264+
},
265+
"file_extension": ".py",
266+
"mimetype": "text/x-python",
267+
"name": "python",
268+
"nbconvert_exporter": "python",
269+
"pygments_lexer": "ipython3",
270+
"version": "3.8.9"
271+
}
272+
},
273+
"nbformat": 4,
274+
"nbformat_minor": 5
275+
}

0 commit comments

Comments
 (0)