Skip to content

Commit 171f8db

Browse files
committed
add navdata.find_wildcard_indexes
1 parent 247c5f3 commit 171f8db

4 files changed

Lines changed: 160 additions & 42 deletions

File tree

gnss_lib_py/parsers/navdata.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,63 @@ def in_rows(self, rows):
575575
raise KeyError(", ".join(missing_rows) + " row(s) are" \
576576
+ " missing from NavData object.")
577577

578+
def find_wildcard_indexes(self, wildcards, max_allow = None):
579+
"""Searches for indexes matching wildcard search input.
580+
581+
For example, a search for ``x_*_m`` would find ``x_rx_m`` or
582+
``x_sv_m`` or ``x_alpha_beta_gamma_m`` depending on the rows
583+
existing in the NavData instance.
584+
585+
Will return an error no index is found matching the wildcard or
586+
if more than ``max_allow`` indexes are found.
587+
588+
Currently only allows for a single wildcard per index.
589+
590+
Parameters
591+
----------
592+
wildcards : array-like or str
593+
List/tuple/np.ndarray/set of indexes for which to search.
594+
max_allow : int or None
595+
Maximum number of valid indexes to allow before throwing an
596+
error. If None, then no limit is placed.
597+
598+
Returns
599+
-------
600+
wildcard_indexes : dict
601+
Dictionary of the form {"search_term", [indexes,...]},
602+
603+
"""
604+
605+
if isinstance(wildcards,str):
606+
wildcards = [wildcards]
607+
if not isinstance(wildcards, (list,tuple,np.ndarray,set)):
608+
raise TypeError("wildcards input in find_wildcard_indexes" \
609+
+ " must be array-like or single string")
610+
if not (isinstance(max_allow,int) or max_allow is None):
611+
raise TypeError("max_allow input in find_wildcard_indexes" \
612+
+ " must be an integer or None.")
613+
614+
wildcard_indexes = {}
615+
616+
for wildcard in wildcards:
617+
if not isinstance(wildcard,str):
618+
raise TypeError("wildcards must be strings")
619+
if wildcard.count("*") != 1:
620+
raise RuntimeError("One wildcard '*' and only one "\
621+
+ "wildcard must be present in search string")
622+
indexes = [row for row in self.rows
623+
if row.startswith(wildcard.split("*",maxsplit=1)[0])
624+
and row.endswith(wildcard.split("*",maxsplit=1)[1])]
625+
if max_allow is not None and len(indexes) > max_allow:
626+
raise KeyError("More than " + str(max_allow) \
627+
+ " possible row indexes for " + wildcard)
628+
if len(indexes) == 0:
629+
raise KeyError("Missing " + wildcard + " row.")
630+
631+
wildcard_indexes[wildcard] = indexes
632+
633+
return wildcard_indexes
634+
578635
def pandas_df(self):
579636
"""Return pandas DataFrame equivalent to class
580637

gnss_lib_py/utils/visualizations.py

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -284,23 +284,8 @@ def plot_skyplot(navdata, receiver_state, save=False, prefix="",
284284
receiver_state.in_rows(["gps_millis"])
285285

286286
# check for receiver_state indexes
287-
rx_idxs = {"x_*_m" : [],
288-
"y_*_m" : [],
289-
"z_*_m" : [],
290-
}
291-
for name, indexes in rx_idxs.items():
292-
indexes = [row for row in receiver_state.rows
293-
if row.startswith(name.split("*",maxsplit=1)[0])
294-
and row.endswith(name.split("*",maxsplit=1)[1])]
295-
if len(indexes) > 1:
296-
raise KeyError("Multiple possible row indexes for " \
297-
+ name \
298-
+ ". Unable to resolve for plot_skyplot().")
299-
if len(indexes) == 0:
300-
raise KeyError("Missing required " + name + " row for " \
301-
+ "plot_skyplot().")
302-
# must call dictionary to avoid pass by value
303-
rx_idxs[name] = indexes[0]
287+
rx_idxs = receiver_state.find_wildcard_indexes(["x_*_m","y_*_m",
288+
"z_*_m"],max_allow=1)
304289

305290
if "el_sv_deg" not in navdata.rows or "az_sv_deg" not in navdata.rows:
306291
sv_el_az = None
@@ -312,9 +297,9 @@ def plot_skyplot(navdata, receiver_state, save=False, prefix="",
312297
# find time index for receiver_state NavData instance
313298
rx_t_idx = np.argmin(np.abs(receiver_state["gps_millis"] - timestamp))
314299

315-
pos_rx_m = receiver_state[[rx_idxs["x_*_m"],
316-
rx_idxs["y_*_m"],
317-
rx_idxs["z_*_m"]],
300+
pos_rx_m = receiver_state[[rx_idxs["x_*_m"][0],
301+
rx_idxs["y_*_m"][0],
302+
rx_idxs["z_*_m"][0]],
318303
rx_t_idx].reshape(1,-1)
319304

320305
timestep_el_az = ecef_to_el_az(pos_rx_m, pos_sv_m)
@@ -457,26 +442,14 @@ def plot_map(*args, sections=0, save=False, prefix="",
457442
+ "NavData.")
458443

459444
# check for lat/lon indexes
460-
traj_idxs = {}
461-
traj_idxs_possibilities = ["lat_*_deg", "lon_*_deg"]
462-
for name in traj_idxs_possibilities:
463-
indexes = [row for row in traj_data.rows
464-
if row.startswith(name.split("*",maxsplit=1)[0])
465-
and row.endswith(name.split("*",maxsplit=1)[1])]
466-
if len(indexes) > 1:
467-
raise KeyError("Multiple possible row indexes for " \
468-
+ name \
469-
+ ". Unable to resolve for plot_map().")
470-
if len(indexes) == 0:
471-
raise KeyError("Missing required " + name + " row for " \
472-
+ "plot_map().")
473-
# must call dictionary to avoid pass by value
474-
traj_idxs[name] = indexes[0]
475-
476-
label_name = _get_label({"":"_".join((traj_idxs["lat_*_deg"].split("_"))[1:-1])})
477-
478-
data = {"latitude" : traj_data[traj_idxs["lat_*_deg"]],
479-
"longitude" : traj_data[traj_idxs["lon_*_deg"]],
445+
traj_idxs = traj_data.find_wildcard_indexes(["lat_*_deg",
446+
"lon_*_deg"],
447+
max_allow=1)
448+
449+
label_name = _get_label({"":"_".join((traj_idxs["lat_*_deg"][0].split("_"))[1:-1])})
450+
451+
data = {"latitude" : traj_data[traj_idxs["lat_*_deg"][0]],
452+
"longitude" : traj_data[traj_idxs["lon_*_deg"][0]],
480453
"Trajectory" : [label_name] * len(traj_data),
481454
}
482455
if sections >= 2:

tests/parsers/test_navdata.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,3 +1877,91 @@ def test_large_int():
18771877
navdata["numbers"] = test_list
18781878

18791879
np.testing.assert_array_equal(navdata["numbers"], test_list)
1880+
1881+
def test_find_wildcard_indexes(data):
1882+
"""Tests find_wildcard_indexes
1883+
1884+
"""
1885+
1886+
all_matching = data.rename({"names" : "x_alpha_m",
1887+
"integers" : "x_beta_m",
1888+
"floats" : "x_gamma_m",
1889+
"strings" : "x_zeta_m"})
1890+
expected = ["x_alpha_m","x_beta_m","x_gamma_m","x_zeta_m"]
1891+
1892+
indexes = all_matching.find_wildcard_indexes("x_*_m")
1893+
assert indexes["x_*_m"] == expected
1894+
expect_pass_allows = [None,12,4]
1895+
for max_allow in expect_pass_allows:
1896+
indexes = all_matching.find_wildcard_indexes("x_*_m",max_allow)
1897+
assert indexes["x_*_m"] == expected
1898+
1899+
expect_fail_allows = [0,-1,3,2,1]
1900+
for max_allow in expect_fail_allows:
1901+
with pytest.raises(KeyError) as excinfo:
1902+
all_matching.find_wildcard_indexes("x_*_m",max_allow)
1903+
assert "More than " + str(max_allow) in str(excinfo.value)
1904+
assert "x_*_m" in str(excinfo.value)
1905+
1906+
multi = data.rename({"names" : "x_alpha_m",
1907+
"integers" : "x_beta_m",
1908+
"floats" : "y_alpha_deg",
1909+
"strings" : "x_zeta_deg"})
1910+
expected = {"x_*_m" : ["x_alpha_m","x_beta_m"],
1911+
"y_*_deg" : ["y_alpha_deg"]}
1912+
1913+
expect_pass_allows = [None,2,4]
1914+
for max_allow in expect_pass_allows:
1915+
indexes = multi.find_wildcard_indexes(["x_*_m","y_*_deg"],
1916+
max_allow)
1917+
assert indexes == expected
1918+
1919+
expect_pass_allows = [None,2,4]
1920+
for max_allow in expect_pass_allows:
1921+
indexes = multi.find_wildcard_indexes(tuple(["x_*_m","y_*_deg"]),
1922+
max_allow)
1923+
assert indexes == expected
1924+
1925+
expect_pass_allows = [None,2,4]
1926+
for max_allow in expect_pass_allows:
1927+
indexes = multi.find_wildcard_indexes(set(["x_*_m","y_*_deg"]),
1928+
max_allow)
1929+
assert indexes == expected
1930+
1931+
expect_pass_allows = [None,2,4]
1932+
for max_allow in expect_pass_allows:
1933+
indexes = multi.find_wildcard_indexes(np.array(["x_*_m",
1934+
"y_*_deg"]),
1935+
max_allow)
1936+
assert indexes == expected
1937+
1938+
expect_fail_allows = [0,-1,1]
1939+
for max_allow in expect_fail_allows:
1940+
with pytest.raises(KeyError) as excinfo:
1941+
multi.find_wildcard_indexes(["x_*_m","y_*_deg"],max_allow)
1942+
assert "More than " + str(max_allow) in str(excinfo.value)
1943+
assert "x_*_m" in str(excinfo.value)
1944+
1945+
with pytest.raises(KeyError) as excinfo:
1946+
multi.find_wildcard_indexes(["z_*_m"])
1947+
assert "Missing " in str(excinfo.value)
1948+
assert "z_*_m" in str(excinfo.value)
1949+
1950+
with pytest.raises(TypeError) as excinfo:
1951+
multi.find_wildcard_indexes(1.0)
1952+
assert "find_wildcard_indexes " in str(excinfo.value)
1953+
assert "array-like" in str(excinfo.value)
1954+
1955+
with pytest.raises(TypeError) as excinfo:
1956+
multi.find_wildcard_indexes([1.0])
1957+
assert "wildcards must be strings" in str(excinfo.value)
1958+
1959+
with pytest.raises(RuntimeError) as excinfo:
1960+
multi.find_wildcard_indexes("x_*_*")
1961+
assert "One wildcard" in str(excinfo.value)
1962+
1963+
incorrect_max_allow = [3.,"hi",[]]
1964+
for max_allow in incorrect_max_allow:
1965+
with pytest.raises(TypeError) as excinfo:
1966+
multi.find_wildcard_indexes("x_*_m",max_allow)
1967+
assert "max_allow" in str(excinfo.value)

tests/utils/test_visualizations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ def test_plot_skyplot(navdata, state_estimate):
395395
with pytest.raises(KeyError) as excinfo:
396396
viz.plot_skyplot(navdata, state_double, save=False)
397397
assert row.replace("rx","*") in str(excinfo.value)
398-
assert "Multiple" in str(excinfo.value)
398+
assert "More than 1" in str(excinfo.value)
399399

400400
def test_get_label():
401401
"""Test for getting nice labels.
@@ -493,4 +493,4 @@ def test_plot_map(gtruth, state_estimate):
493493
with pytest.raises(KeyError) as excinfo:
494494
viz.plot_map(gtruth, state_double, save=False)
495495
assert row.replace("rx","*") in str(excinfo.value)
496-
assert "Multiple" in str(excinfo.value)
496+
assert "More than 1" in str(excinfo.value)

0 commit comments

Comments
 (0)