From 14161c0c5ed31c981a519131b39b4c8682925d4b Mon Sep 17 00:00:00 2001 From: Jonathan Diamond Date: Tue, 9 Apr 2024 21:46:56 -0700 Subject: [PATCH] Annotate logs with device names. --- .../analysis/pose_compare.py | 44 ++++++++++++------- .../applications/p1_drive_analysis.py | 26 ++++++++--- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/python/fusion_engine_client/analysis/pose_compare.py b/python/fusion_engine_client/analysis/pose_compare.py index d8af45c6..a84f2011 100755 --- a/python/fusion_engine_client/analysis/pose_compare.py +++ b/python/fusion_engine_client/analysis/pose_compare.py @@ -38,11 +38,6 @@ SolutionTypeInfo = namedtuple('SolutionTypeInfo', ['name', 'style']) -_LOG_NAMES = [ - 'Test', - 'Reference', -] - _SOLUTION_TYPE_MAP = [{ SolutionType.Invalid: SolutionTypeInfo(name='Invalid', style={'color': 'black'}), SolutionType.Integrate: SolutionTypeInfo(name='Integrated', style={'color': 'cyan'}), @@ -122,7 +117,7 @@ def __init__(self, file_test: Union[DataLoader, str], file_reference: Union[DataLoader, str], ignore_index: bool = False, output_dir: str = None, prefix: str = '', time_range: TimeRange = None, max_messages: int = None, - time_axis: str = 'relative'): + time_axis: str = 'relative', test_device_name=None, reference_device_name=None): """! @brief Create an analyzer for the comparing the pose from two p1log files. @@ -139,6 +134,8 @@ def __init__(self, @param time_axis Specify the way in which time will be plotted: - `absolute`, `abs` - Absolute P1 or system timestamps - `relative`, `rel` - Elapsed time since the start of the log + @param test_device_name If set, label test data with this device name. + @param reference_device_name If set, label reference data with this device name. """ self.params = { 'time_range': time_range, @@ -166,10 +163,22 @@ def __init__(self, if len(self.reference_pose.p1_time) == 0: raise ValueError('Reference log did not contain pose data.') + self.test_data_name = 'Test' + if test_device_name: + self.test_data_name += ' ' + test_device_name + + self.reference_data_name = 'Reference' + if reference_device_name: + self.reference_data_name += ' ' + reference_device_name + + self.data_names = [ + self.test_data_name, + self.reference_data_name, + ] + self.output_dir = output_dir self.prefix = prefix - gps_time_test = self.test_pose.gps_time valid_gps_time = gps_time_test[np.isfinite(gps_time_test)] if len(valid_gps_time) == 0: @@ -226,7 +235,7 @@ def plot_solution_type(self): ticktext=['%s (%d)' % (e.name, e.value) for e in SolutionType], tickvals=[e.value for e in SolutionType]) - for name, pose_data in zip(_LOG_NAMES, (self.test_pose, self.reference_pose)): + for name, pose_data in zip(self.data_names, (self.test_pose, self.reference_pose)): time = pose_data.gps_time - float(self.t0) text = ["Time: %.3f sec (%.3f sec)" % (t, t + float(self.t0)) for t in time] @@ -256,7 +265,7 @@ def plot_map(self, mapbox_token): map_data = [] for i, pose_data in enumerate((self.test_pose, self.reference_pose)): - log_name = _LOG_NAMES[i] + log_name = self.data_names[i] # Remove invalid solutions. valid_idx = np.logical_and(~np.isnan(pose_data.p1_time), pose_data.solution_type != SolutionType.Invalid) @@ -326,7 +335,8 @@ def _plot_pose_error(self, time, solution_type, error_enu_m, std_enu_m): time_figure = make_subplots(rows=4, cols=1, print_grid=False, shared_xaxes=True, subplot_titles=['3D', 'East', 'North', 'Up']) - time_figure['layout'].update(showlegend=True, modebar_add=['v1hovermode']) + time_figure['layout'].update(showlegend=True, modebar_add=['v1hovermode'], + title=f'{self.test_data_name} Vs. {self.reference_data_name}') for i in range(4): time_figure['layout']['xaxis%d' % (i + 1)].update(title=self.gps_time_label, showticklabels=True) time_figure['layout']['yaxis1'].update(title="Error (m)") @@ -515,13 +525,13 @@ def _set_data_summary(self): log_b_end = log_b_p1_time[-1] data = [[], []] - data[0].append('Test Log Start') + data[0].append(self.test_data_name + ' Log Start') data[1].append(log_a_t0) - data[0].append('Reference Log Start') + data[0].append(self.reference_data_name + ' Log Start') data[1].append(log_b_t0) - data[0].append('Test Log Duration') + data[0].append(self.test_data_name + ' Log Duration') data[1].append(log_a_end - log_a_t0) - data[0].append('Reference Log Duration') + data[0].append(self.reference_data_name + ' Log Duration') data[1].append(log_b_end - log_b_t0) data[0].append('Matched GPS Epochs') data[1].append(len(matched_p1_time_a)) @@ -550,6 +560,8 @@ def get_stats(label, values): error_table = _data_to_table(columns, data, row_major=True, round_decimal_places=2) self.summary += f""" +

Comparison of {self.test_data_name} to {self.reference_data_name}

+ {time_table}

Solution Type CDF

@@ -736,6 +748,8 @@ def main(): "- The path to a FusionEngine log directory\n" "- A pattern matching a FusionEngine log directory under the specified base directory " "(see find_fusion_engine_log() and --log-base-dir)") + log_group.add_argument('--reference-device-name', default=None, help='Label to apply to reference data.') + log_group.add_argument('--test-device-name', default=None, help='Label to apply to test data.') output_group = parser.add_argument_group('Output Control') output_group.add_argument( @@ -800,7 +814,7 @@ def main(): # Read pose data from the file. analyzer = PoseCompare(file_test=input_path_test, file_reference=input_path_reference, output_dir=output_dir, ignore_index=options.ignore_index, prefix=options.prefix + '.' if options.prefix is not None else '', - time_range=time_range, time_axis=options.time_axis) + time_range=time_range, time_axis=options.time_axis, test_device_name=options.test_device_name, reference_device_name=options.reference_device_name) if not options.skip_plots: analyzer.plot_solution_type() diff --git a/python/fusion_engine_client/applications/p1_drive_analysis.py b/python/fusion_engine_client/applications/p1_drive_analysis.py index 21445b1b..772797b0 100755 --- a/python/fusion_engine_client/applications/p1_drive_analysis.py +++ b/python/fusion_engine_client/applications/p1_drive_analysis.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import glob import io import sys import os @@ -52,6 +53,10 @@ def find_logs(prefix, log_guids) -> Dict[str, str]: return resp +def get_device_name_from_path(log_path: Path) -> str: + return '_'.join(log_path.name.split('_')[1:-1]) + + def main(): parser = ArgumentParser(description="""\ Run p1_pose_compare for each device included in a drive test. @@ -98,7 +103,8 @@ def main(): meta_key = options.key_for_log_in_drive + '/' + META_FILE drive_meta_data = download_to_memory(options.key_for_log_in_drive + '/' + META_FILE) except: - _logger.error(f'Could not find "S3://{S3_DEFAULT_INGEST_BUCKET}/{meta_key}". Make sure this log was taken as part of a drive test collection.') + _logger.error( + f'Could not find "S3://{S3_DEFAULT_INGEST_BUCKET}/{meta_key}". Make sure this log was taken as part of a drive test collection.') exit(1) drive_meta = json.loads(drive_meta_data.decode('utf-8')) @@ -112,11 +118,12 @@ def main(): logs_to_download = [] log_paths = {} for guid in [reference_guid] + test_guids: - log_paths[guid] = LOG_DIR / f'{guid}.p1log' - if not log_paths[guid].exists(): - logs_to_download.append(guid) - else: + matches = glob.glob(str(LOG_DIR / f'*{guid}.p1log')) + if len(matches) > 0: + log_paths[guid] = Path(matches[0]) _logger.info(f'Using cached: {log_paths[guid]}') + else: + logs_to_download.append(guid) if len(logs_to_download) > 0: log_prefixes = find_logs(prefix, logs_to_download) @@ -131,6 +138,8 @@ def main(): exit(1) for guid, s3_prefix in log_prefixes.items(): + file_name = s3_prefix.replace('/', '_') + '.p1log' + log_paths[guid] = LOG_DIR / file_name _logger.info(f'Downloading: {log_paths[guid]}') if reference_guid == guid: reference_p1log_key = s3_prefix + '/' + REFERENCE_LOG_FILE @@ -139,9 +148,12 @@ def main(): s3_client.download_file(S3_DEFAULT_INGEST_BUCKET, reference_p1log_key, log_paths[guid]) + reference_device = get_device_name_from_path(log_paths[reference_guid]) for guid in test_guids: - _logger.info(f'Comparing log: {guid}') - sys.argv = ['pose_compare_main', str(log_paths[guid]), str(log_paths[reference_guid]), '--time-axis=rel'] + _logger.info(f'Comparing log: {log_paths[guid]}') + test_device = get_device_name_from_path(log_paths[guid]) + sys.argv = ['pose_compare_main', str(log_paths[guid]), str( + log_paths[reference_guid]), '--time-axis=rel', f'--test-device-name={test_device}', f'--reference-device-name={reference_device}'] try: pose_compare_main() except Exception as e: