|
15 | 15 |
|
16 | 16 | from gnss_lib_py.parsers.navdata import NavData |
17 | 17 | from gnss_lib_py.utils.coordinates import geodetic_to_ecef |
| 18 | +from gnss_lib_py.utils.coordinates import ecef_to_geodetic |
18 | 19 | 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 |
19 | 21 |
|
20 | 22 | class AndroidDerived2021(NavData): |
21 | 23 | """Class handling derived measurements from Android dataset. |
@@ -82,7 +84,7 @@ def postprocess(self): |
82 | 84 | - self['intersignal_bias_m'] \ |
83 | 85 | - self['tropo_delay_m'] \ |
84 | 86 | - self['iono_delay_m'] |
85 | | - self['corr_pr_m'] = pr_corrected |
| 87 | + self['corr_pr_m'] = pr_corrected |
86 | 88 | # rename gnss_id column to constellation type |
87 | 89 | constellation_map = {0.:"unknown", |
88 | 90 | 1.:"gps", |
@@ -234,7 +236,10 @@ def _row_map(): |
234 | 236 | 'TroposphericDelayMeters' : 'tropo_delay_m', |
235 | 237 | 'Cn0DbHz': 'cn0_dbhz', |
236 | 238 | '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', |
238 | 243 | } |
239 | 244 | return row_map |
240 | 245 |
|
@@ -488,3 +493,137 @@ def make_csv(input_path, output_directory, field, show_path=False): |
488 | 493 | print(output_path) |
489 | 494 |
|
490 | 495 | 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