Skip to content

Commit e30fab3

Browse files
Merge pull request #85 from Stanford-NavLab/derek/kaggle
Derek/kaggle
2 parents b8abfca + ba3399c commit e30fab3

18 files changed

Lines changed: 1050 additions & 507 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
runs-on: ${{ matrix.os }}
1717
strategy:
1818
matrix:
19-
python-version: ["3.7.1", "3.8", "3.10"]
19+
python-version: ["3.8", "3.10"]
2020
os: [ubuntu-latest, macos-latest, windows-latest]
2121
fail-fast : false
2222
defaults:

data/unit_test/android_2022/device_gnss.csv

Lines changed: 235 additions & 235 deletions
Large diffs are not rendered by default.

data/unit_test/android_2022/ground_truth.csv

Lines changed: 201 additions & 201 deletions
Large diffs are not rendered by default.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
int,float,datetime,string
2+
4,4.321,2022-11-14,testing

gnss_lib_py/algorithms/snapshot.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,14 @@ def solve_wls(measurements, weight_type = None,
8282
try:
8383
position = wls(position, pos_sv_m, corr_pr_m, weights,
8484
only_bias, tol, max_count)
85-
8685
states.append([timestamp] + np.squeeze(position).tolist())
8786
except RuntimeError as error:
8887
warnings.warn("RuntimeError encountered at gps_millis: " \
8988
+ str(int(timestamp)) + " RuntimeError: " \
9089
+ str(error), RuntimeWarning)
91-
states = np.array(states)
90+
states.append([timestamp, np.nan, np.nan, np.nan, np.nan])
9291

93-
if states.size == 0:
94-
warnings.warn("No valid state estimate computed in WLS, "\
95-
+ "returning None.", RuntimeWarning)
96-
return None
92+
states = np.array(states)
9793

9894
state_estimate = NavData()
9995
state_estimate["gps_millis"] = states[:,0]
@@ -102,6 +98,11 @@ def solve_wls(measurements, weight_type = None,
10298
state_estimate["z_rx_m"] = states[:,3]
10399
state_estimate["b_rx_m"] = states[:,4]
104100

101+
if np.isnan(states[:,1:]).all():
102+
warnings.warn("No valid state estimate computed in WLS, "\
103+
+ "returning NaNs.", RuntimeWarning)
104+
return state_estimate
105+
105106
lat,lon,alt = ecef_to_geodetic(state_estimate[["x_rx_m","y_rx_m",
106107
"z_rx_m"]].reshape(3,-1))
107108
state_estimate["lat_rx_deg"] = lat

gnss_lib_py/parsers/android.py

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
from gnss_lib_py.parsers.navdata import NavData
1717
from gnss_lib_py.utils.coordinates import geodetic_to_ecef
18+
from gnss_lib_py.utils.coordinates import ecef_to_geodetic
1819
from gnss_lib_py.utils.time_conversions import unix_to_gps_millis
20+
from gnss_lib_py.utils.time_conversions import gps_to_unix_millis
1921

2022
class AndroidDerived2021(NavData):
2123
"""Class handling derived measurements from Android dataset.
@@ -82,7 +84,7 @@ def postprocess(self):
8284
- self['intersignal_bias_m'] \
8385
- self['tropo_delay_m'] \
8486
- self['iono_delay_m']
85-
self['corr_pr_m'] = pr_corrected
87+
self['corr_pr_m'] = pr_corrected
8688
# rename gnss_id column to constellation type
8789
constellation_map = {0.:"unknown",
8890
1.:"gps",
@@ -234,7 +236,10 @@ def _row_map():
234236
'TroposphericDelayMeters' : 'tropo_delay_m',
235237
'Cn0DbHz': 'cn0_dbhz',
236238
'AccumulatedDeltaRangeMeters' : 'accumulated_delta_range_m',
237-
'AccumulatedDeltaRangeUncertaintyMeters': 'accumulated_delta_range_sigma_m'
239+
'AccumulatedDeltaRangeUncertaintyMeters': 'accumulated_delta_range_sigma_m',
240+
'WlsPositionXEcefMeters' : 'x_rx_m',
241+
'WlsPositionYEcefMeters' : 'y_rx_m',
242+
'WlsPositionZEcefMeters' : 'z_rx_m',
238243
}
239244
return row_map
240245

@@ -488,3 +493,137 @@ def make_csv(input_path, output_directory, field, show_path=False):
488493
print(output_path)
489494

490495
return output_path
496+
497+
def solve_kaggle_baseline(navdata):
498+
"""Convert Decimeter challenge baseline into state_estimate.
499+
500+
The baseline solution was provided in 2022, but not in 2021.
501+
502+
Parameters
503+
----------
504+
navdata : gnss_lib_py.parsers.android.AndroidDerived2022
505+
Instance of the AndroidDerived2022 class.
506+
507+
Returns
508+
-------
509+
state_estimate : gnss_lib_py.parsers.navdata.NavData
510+
Baseline state estimate.
511+
512+
"""
513+
514+
columns = ["unix_millis",
515+
"x_rx_m",
516+
"y_rx_m",
517+
"z_rx_m",
518+
]
519+
navdata.in_rows(columns)
520+
data_df = (navdata.pandas_df().drop_duplicates(subset='unix_millis')[columns]
521+
.reset_index(drop=True))
522+
lat,lon,alt = np.transpose(ecef_to_geodetic(data_df[["x_rx_m",
523+
"y_rx_m",
524+
"z_rx_m",
525+
]].to_numpy()))
526+
527+
state_estimate = NavData()
528+
state_estimate["gps_millis"] = unix_to_gps_millis(
529+
data_df["unix_millis"].to_numpy())
530+
state_estimate["lat_rx_deg"] = lat
531+
state_estimate["lon_rx_deg"] = lon
532+
state_estimate["alt_rx_deg"] = alt
533+
534+
return state_estimate
535+
536+
def prepare_kaggle_submission(state_estimate, trip_id="trace/phone"):
537+
"""Converts from gnss_lib_py receiver state to Kaggle submission.
538+
539+
Parameters
540+
----------
541+
state_estimate : gnss_lib_py.parsers.navdata.NavData
542+
Estimated receiver position in latitude and longitude as an
543+
instance of the NavData class with the following
544+
rows: ``gps_millis``, ``lat_*_deg``, ``lon_*_deg``.
545+
trip_id : string
546+
Value for the tripId column in kaggle submission which is a
547+
fusion of the data and phone type.
548+
549+
Returns
550+
-------
551+
output : gnss_lib_py.parsers.navdata.NavData
552+
NavData structure ready for Kaggle submission.
553+
554+
"""
555+
556+
state_estimate.in_rows("gps_millis")
557+
wildcards = state_estimate.find_wildcard_indexes(["lat_*_deg",
558+
"lon_*_deg"],max_allow = 1)
559+
560+
output = NavData()
561+
output["tripId"] = np.array([trip_id] * state_estimate.shape[1])
562+
output["UnixTimeMillis"] = gps_to_unix_millis(state_estimate["gps_millis"])
563+
output.orig_dtypes["UnixTimeMillis"] = np.int64
564+
output["LatitudeDegrees"] = state_estimate[wildcards["lat_*_deg"]]
565+
output["LongitudeDegrees"] = state_estimate[wildcards["lon_*_deg"]]
566+
567+
output.interpolate("UnixTimeMillis",["LatitudeDegrees",
568+
"LongitudeDegrees"],inplace=True)
569+
return output
570+
571+
def solve_kaggle_dataset(folder_path, solver, verbose=False, *args):
572+
"""Run solver on all kaggle traces.
573+
574+
Additional ``*args`` arguments are passed into the ``solver``
575+
function.
576+
577+
Parameters
578+
----------
579+
folder_path: string or path-like
580+
Path to folder containing all traces (e.g. full path to "train"
581+
or "test" directories.
582+
solver : function
583+
State estimate solver that takes an instance of
584+
AndroidDerived2022 and outputs a state_estimate NavData object.
585+
Additional ``*args`` arguments are passed into this ``solver``
586+
function.
587+
verbose : bool
588+
If verbose, will print each trace trajectory name and phone name
589+
pair when it is solving the state estimate for that pair.
590+
591+
Returns
592+
-------
593+
submission : gnss_lib_py.parsers.navdata.NavData
594+
Full solution submission across all traces. Can then be saved
595+
using submission.to_csv().
596+
597+
"""
598+
599+
# create solution NavData object
600+
solution = NavData()
601+
602+
# iterate through all trace options
603+
for trace_name in sorted(os.listdir(folder_path)):
604+
trace_path = os.path.join(folder_path, trace_name)
605+
# iterate through all phone types
606+
for phone_type in sorted(os.listdir(trace_path)):
607+
data_path = os.path.join(folder_path,trace_name,
608+
phone_type,"device_gnss.csv")
609+
try:
610+
# convert data to Measurement class
611+
derived_data = AndroidDerived2022(data_path)
612+
613+
if verbose:
614+
print("solving:",trace_name,phone_type)
615+
616+
# compute state estimate using provided solver function
617+
state_estimate = solver(derived_data, *args)
618+
619+
trip_id = "/".join([trace_name,phone_type])
620+
output = prepare_kaggle_submission(state_estimate,
621+
trip_id)
622+
623+
# concatenate solution to previous solutions
624+
solution.concat(navdata=output, inplace=True)
625+
626+
except FileNotFoundError:
627+
continue
628+
629+
return solution

0 commit comments

Comments
 (0)