Exemple #1
0
def gnuplot_aspect(stream: typing.TextIO = sys.stdout) -> typing.List[str]:
    arrays_from_transits = {
        min_max: aspect.aspects(min_max)
        for min_max in list(video_data.ErrorDirection)
    }
    timebase = arrays_from_transits[video_data.ErrorDirection.MID][:, 0]
    aspect_from_transits_with_errors = np.column_stack((
        timebase,
        arrays_from_transits[video_data.ErrorDirection.MID][:, 1],
        arrays_from_transits[video_data.ErrorDirection.MIN][:, 0],
        arrays_from_transits[video_data.ErrorDirection.MAX][:, 0],
        arrays_from_transits[video_data.ErrorDirection.MIN][:, 1],
        arrays_from_transits[video_data.ErrorDirection.MAX][:, 1],
    ))
    aspect_from_transits_fitted = aspect.aspect_fitted_line()
    arrays_from_wing_tips = {
        min_max: aspect.aspects_from_wing_tips(min_max)
        for min_max in list(video_data.ErrorDirection)
    }
    timebase_from_wing_tips = arrays_from_wing_tips[
        video_data.ErrorDirection.MID][:, 0]
    aspect_from_wing_tips_with_errors = np.column_stack((
        timebase_from_wing_tips,
        arrays_from_wing_tips[video_data.ErrorDirection.MID][:, 1],
        arrays_from_wing_tips[video_data.ErrorDirection.MIN][:, 0],
        arrays_from_wing_tips[video_data.ErrorDirection.MAX][:, 0],
        arrays_from_wing_tips[video_data.ErrorDirection.MIN][:, 1],
        arrays_from_wing_tips[video_data.ErrorDirection.MAX][:, 1],
    ))
    aspect_fitted_from_wing_tips = aspect.aspect_from_wing_tips_fitted_line()
    # TODO: Make use of notes the same in all functions
    notes = [
        '"{}"'.format('Aspects, errors, and polynomial fit.'),
        'Columns:',
        '1: time (s)',
        '2: aspect_from_transits (deg)',
        '3: -dt',
        '4: +dt',
        '5: -dy (deg)',
        '6: +dy (deg)',
        '7: aspect_from_transit_fit (deg)',
        '8: aspect_from_wingtips (deg)',
        '9: -dt',
        '10: +dt',
        '11: -dy (deg)',
        '12: +dy (deg)',
        '13: aspect_from_wingtips_fit (deg)',
    ]
    if len(notes):
        stream.write('# Notes:\n')
        for note in notes:
            stream.write('# {}\n'.format(note))
    plot_common.gnuplot_write_arrays(
        stream,
        aspect_from_transits_with_errors,
        aspect_from_transits_fitted,
        aspect_from_wing_tips_with_errors,
        aspect_fitted_from_wing_tips,
    )
    return []
Exemple #2
0
def gnuplot_aircraft_yaw(
        stream: typing.TextIO = sys.stdout) -> typing.List[str]:
    result = ['# "{}"'.format('Estimated yaw of aircraft.')]
    notes = ['time yaw(degrees)']
    if len(notes):
        stream.write('#\n')
        stream.write('# Notes:\n')
        for note in notes:
            stream.write('# {}\n'.format(note))
    gs_fit = video_analysis.ground_speed_curve_fit(
        video_data.ErrorDirection.MID)
    # time_dist_brng = video_analysis.observer_time_distance_bearing(gs_fit, video_data.ErrorDirection.MID)
    time_dist_brng = video_analysis.observer_time_distance_bearing_from_wing_tips(
        gs_fit, video_data.ErrorDirection.MID)
    # time_dist_brng = video_analysis.time_distance_bearing_from_fits(time_interval=1.0)

    # time_dist_brng is a four column array of (time, x_distance, aspect, aspect_error)
    # from the observed aspect data.
    # Units are (seconds, metres, degrees).

    time_yaw = []
    # ((x_mean, x_std), (y_mean, y_std)) = video_analysis.observer_position_mean_std_from_aspects(
    #     baseline=plot_constants.OBSERVER_XY_MINIMUM_BASELINE,
    #     ignore_first_n=plot_constants.OBSERVER_XY_IGNORE_N_FIRST_BEARINGS,
    #     t_range=plot_constants.OBSERVER_XY_TIME_RANGE,
    # )
    ((x_mean, x_std),
     (y_mean,
      y_std)) = video_analysis.observer_position_mean_std_from_full_transits()
    observer_error = math.sqrt(x_std**2 + y_std**2)
    # Observer from bearings    : x=3480.0 y=-763.0
    # Observer from google earth: x=3457.9 y=-655.5
    #       Diff (bearings - ge): x=  22.1 y=-107.5
    # x_mean = 3457.9 - 1214
    # y_mean = -655.5
    # y_mean = -744.88
    # y_mean = -754.88
    # y_mean = -764.88
    for t, x_distance, aspect, aspect_error in time_dist_brng:
        # Calculate the bearing from the observers assumed position to the aircraft position
        x_obs_aircraft = x_mean - x_distance - plot_common.x_offset()
        obs_brng = math.degrees(math.atan2(y_mean, x_obs_aircraft))
        obs_brng %= 360
        yaw = (obs_brng - aspect) % 360
        if yaw > 180.0:
            yaw -= 360
        # Compute error as the angle:
        # 2.0 * atan(OBSERVER_ASSUMED_POSITION_ERROR / observer-to_aircraft_distance)
        obs_aircraft_distance = math.sqrt(y_mean**2 + x_obs_aircraft**2)
        error = 2.0 * math.degrees(
            math.atan(observer_error / obs_aircraft_distance))
        error += aspect_error
        time_yaw.append((t, yaw, yaw - error, yaw + error))
        # print('TRACE: t={:8.1f} yaw={:6.3f}),'.format(t, yaw))
    # print('TRACE: time_yaw:')
    # pprint.pprint(time_yaw)
    plot_common.gnuplot_write_arrays(stream, np.array(time_yaw))
    return ['']
Exemple #3
0
def gnuplot_pitch(stream: typing.TextIO=sys.stdout) -> typing.List[str]:
    arrays = {
        min_max: pitch.pitches(min_max) for min_max in list(video_data.ErrorDirection)
    }
    timebase = arrays[video_data.ErrorDirection.MID][:, 0]
    aspect_with_errors = np.column_stack(
        (
            timebase,
            arrays[video_data.ErrorDirection.MID][:, 1],
            arrays[video_data.ErrorDirection.MIN][:, 0],
            arrays[video_data.ErrorDirection.MAX][:, 0],
            arrays[video_data.ErrorDirection.MIN][:, 1],
            arrays[video_data.ErrorDirection.MAX][:, 1],
        )
    )
    aspect_fitted = pitch.pitch_fitted_line()
    result = [
        '# "{}"'.format('Pitch (degrees), errors, and polynomial fit.'),
        '# "{}"'.format('t, -dt, +dt, -dy, +dy, fit.'),
    ]
    notes = [
    ]
    if len(notes):
        result.append('#')
        result.append('# Notes:')
    for note in notes:
        result.append('# {}'.format(note))
        result.append('# {:>2} {:>8} {:>8}'.format(
            't', 'speed', 'smoothed'
        )
        )

    plot_common.gnuplot_write_arrays(
        stream,
        aspect_with_errors,
        aspect_fitted,
    )
    return []
def gnuplot_observer_xy(stream: typing.TextIO=sys.stdout) -> typing.List[str]:
    notes = [
        '"{}"'.format('Estimated x/y of observer.'),
        'Columns:',
        '1: x (m)',
        '2: y minumum (m)',
        '3: y mid (m)',
        '4: y maximum (m)',
    ]
    if len(notes):
        stream.write('# Notes:\n')
        for note in notes:
            stream.write('# {}\n'.format(note))
    # observer_xy array
    observations = [
        video_analysis.observer_position_combinations_from_aspects(
            min_mid_max=error_direction,
            baseline=plot_constants.OBSERVER_XY_MINIMUM_BASELINE,
            ignore_first_n=plot_constants.OBSERVER_XY_IGNORE_N_FIRST_BEARINGS,
            t_range=plot_constants.OBSERVER_XY_TIME_RANGE,
        )[0] for error_direction in list(video_data.ErrorDirection)
    ]
    for obs in observations:
        obs[:,0] += plot_common.x_offset()
    plot_common.gnuplot_write_arrays(stream, *observations)
    # Print header twice, once for x, once for y.
    print('TRACE: gnuplot_observer_xy()')
    for i in range(2):
        axis_name = 'X' if i == 0 else 'Y'
        print('{}: {:>8s} {:>8s} {:>8s} {:>8s} {:>8s} {:>8s} {:>8s}  '.format(
            axis_name, 'Count', 'Min', 'Median', 'Mean', 'Max', 'Std', 'Range'), end=''
        )
    print()
    for obs in observations:
        for i in range(2):
            axis_name = 'X' if i == 0 else 'Y'
            print('{}: {:8d} {:8.0f} {:8.0f} {:8.0f} {:8.0f} {:8.0f} {:8.0f}  '.format(
                axis_name, len(obs),
                np.min(obs, axis=0)[i], np.median(obs, axis=0)[i],
                np.mean(obs, axis=0)[i], np.max(obs, axis=0)[i],
                np.std(obs, axis=0)[i], np.max(obs, axis=0)[i] - np.min(obs, axis=0)[i],
            ), end='')
        print()
    # x_mean = np.mean(observations[1], axis=0)[0]
    # x_min = np.min(observations[1], axis=0)[0]
    # x_max = np.max(observations[1], axis=0)[0]
    # x_std = np.std(observations[1], axis=0)[0]
    # y_mean = np.mean(observations[1], axis=0)[1]
    # y_min = np.min(observations[1], axis=0)[1]
    # y_max = np.max(observations[1], axis=0)[1]
    # y_std = np.std(observations[1], axis=0)[1]
    offset_label_x = -35
    ((x_mean, x_std), (y_mean, y_std)) = video_analysis.observer_position_mean_std_from_aspects(
        baseline=plot_constants.OBSERVER_XY_MINIMUM_BASELINE,
        ignore_first_n=plot_constants.OBSERVER_XY_IGNORE_N_FIRST_BEARINGS,
        t_range=plot_constants.OBSERVER_XY_TIME_RANGE,
    )
    x_mean += plot_common.x_offset()

    ret = [
        'set title "Observers Position [{:d} observations]"'.format(len(observations[1]))
        ]
    # Label and arrow for observer position from bearings
    ret.extend(
        [
            'set label "X={x_mean:.0f} ±{x_err:.0f}m Y={y_mean:.0f} ±{y_err:.0f} m" at {x:.0f},{y:.0f} right font ",12" textcolor rgb "#007F00"'.format(
                x_mean=x_mean,
                x_err=x_std,
                y_mean=y_mean,
                y_err=y_std,
                x=x_mean+offset_label_x,
                y=y_mean,
            ),
            'set arrow from {:.0f},{:.0f} to {:.0f},{:.0f} lt -1 lw 2 empty'.format(
                x_mean+offset_label_x+1, y_mean, x_mean, y_mean,
            ),
        ]
    )
    # Add transit lines
    # ret.extend(plot_common.full_transit_labels_and_arrows('#FF00FF'))
    ret.extend(plot_common.full_transit_labels_and_arrows('#FF00FF', 1.5))
    # Apply positional error and plot
    # 808080
    error = video_data.GOOGLE_EARTH_ERROR
    ret.extend(
        plot_common.full_transit_arrows_with_position_error('#00D0D0', error, 1.5)
    )
    ret.extend(
        plot_common.full_transit_arrows_with_position_error('#D0D000', -error, 1.5)
    )
    # Add transit label
    x, dx, y, dy = plot_common.observer_position_from_full_transits()
    # Add label and arrow for observer position from full transits
    ret.extend(
        [
            'set label "X={x_mean:.0f} ±{x_err:.0f}m Y={y_mean:.0f} ±{y_err:.0f} m" at {x:.0f},{y:.0f} right font ",12" textcolor rgb "#FF00FF"'.format(
                x_mean=x,
                x_err=dx,
                y_mean=y,
                y_err=dy,
                x=x+offset_label_x,
                y=y,
            ),
            'set arrow from {:.0f},{:.0f} to {:.0f},{:.0f} lt -1 lw 2 empty'.format(
                x+offset_label_x+1, y, x, y,
            ),
        ]
    )
    return ret
def _gnuplot_distance(
    offset_distance_at_t: float,
    include_labels_t_0: bool,
    stream: typing.TextIO = sys.stdout,
) -> typing.List[str]:
    """
    If offset_distance_at_t is non-zero an offset will be applied that is the
    runway length - the distance at that offset time.
    """
    three_dist_arrays = get_distances_min_mid_max(offset_distance_at_t)
    plot_common.gnuplot_write_arrays(stream, *three_dist_arrays)
    plot_data = ['# Start distance arrows and labels']
    if offset_distance_at_t == 0.0:
        text_offset = 0
    else:
        text_offset = 1000
    plot_data.extend([
        # set arrow from -30.137,0 to -30.137,-791.8 lt 1
        'set arrow from {t:.3f},{offset:d} to {t:.3f},{d:.3f} lt {lt:d}'.
        format(
            t=three_dist_arrays[i][0, 0],
            offset=text_offset + i * 200,
            d=three_dist_arrays[i][0, 1],
            lt=i + 1,
        ) for i in range(len(three_dist_arrays))
    ])
    plot_data.extend([
        # set label 1 "Calculated start at -30s, -790m" at -30.137,100 left font ",10" # rotate by 45
        'set label "Calculated start at {t:.1f}s, {d:.0f}m" at {t:.1f},{offset:d} left font ",9"'
        .format(
            t=three_dist_arrays[i][0, 0],
            offset=text_offset + 100 + i * 200,
            d=three_dist_arrays[i][0, 1],
        ) for i in range(len(three_dist_arrays))
    ])
    plot_data.append('# End labels at start of take off')
    if include_labels_t_0:
        plot_data.append('# Labels at t=0')
        for i in range(len(three_dist_arrays)):
            d = video_utils.interpolate(three_dist_arrays[i][:, 0],
                                        three_dist_arrays[i][:, 1], 0.0)
            plot_data.append(
                # set arrow from -30.137,0 to -30.137,-791.8 lt 1
                'set arrow from {t:.3f},{d:.1f} to 0.0,{d:.3f} lt {lt:d}'.
                format(
                    t=6.0,
                    d=d,
                    lt=i + 1,
                ))
            plot_data.append(
                # set label 1 "Calculated start at -30s, -790m" at -30.137,100 left font ",10" # rotate by 45
                'set label "t=0.0s, d={d:.0f}m" at {t:.1f},{d:.1f} left font ",9"'
                .format(
                    t=6.0,
                    d=d,
                ))
        plot_data.append('# End labels at t=0')
    if offset_distance_at_t == 0.0:
        d_to = 1850
    else:
        d_to = video_data.RUNWAY_LEN_M - 100
    d_from = d_to - 850
    plot_data.append(
        'set arrow from {t:.3f},{d_from:d} to {t:.3f},{d_to:d} lt -1'.format(
            d_from=d_from,
            d_to=d_to,
            t=video_data.TIME_VIDEO_END_ASPHALT.time,
        ))
    plot_data.append(
        # set label 7 "End asphalt 27.8s" at 27.8,900 center font ",10"
        'set label "End asphalt {t:.1f}s" at {t:.1f},{d:d} center font ",10"'.
        format(
            d=d_from - 100,
            t=video_data.TIME_VIDEO_END_ASPHALT.time,
        ))

    if offset_distance_at_t == 0.0:
        plot_data.extend([
            # set arrow from 17.8,1919 to 27.8,1919 as 4 lt 1
            'set arrow from {t0:.3f},{distance:.3f} to {t1:.3f},{distance:.3f} lt {lt:d}'
            .format(
                t0=video_data.TIME_VIDEO_END_ASPHALT.time - 10,
                t1=video_data.TIME_VIDEO_END_ASPHALT.time,
                distance=np.interp(
                    video_data.TIME_VIDEO_END_ASPHALT.time,
                    three_dist_arrays[i][:, 0],
                    three_dist_arrays[i][:, 1],
                ),
                lt=i + 1,
            ) for i in range(len(three_dist_arrays))
        ])
        plot_data.extend([
            # set label 4 "1920m" at 17,1920 right font ",10" # rotate by 45
            'set label "{distance:.0f}m" at {t:.1f},{distance:.1f} right font ",10"'
            .format(
                t=video_data.TIME_VIDEO_END_ASPHALT.time - 10 - 1,
                distance=np.interp(
                    video_data.TIME_VIDEO_END_ASPHALT.time,
                    three_dist_arrays[i][:, 0],
                    three_dist_arrays[i][:, 1],
                ),
            ) for i in range(len(three_dist_arrays))
        ])
    else:
        plot_data.append(
            'set arrow from {t0:.3f},{distance:.3f} to {t1:.3f},{distance:.3f} lt {lt:d}'
            .format(
                t0=video_data.TIME_VIDEO_END_ASPHALT.time - 10,
                t1=video_data.TIME_VIDEO_END_ASPHALT.time,
                distance=video_data.RUNWAY_LEN_M,
                lt=2,
            ))
        plot_data.append(
            'set label "{distance:.0f}m" at {t:.1f},{distance:.1f} right font ",10"'
            .format(
                t=video_data.TIME_VIDEO_END_ASPHALT.time - 10 - 1,
                distance=video_data.RUNWAY_LEN_M,
            ))
    plot_data.append('# End video')
    # TODO: Constants here, they should be computed.
    if offset_distance_at_t == 0.0:
        d_to = 2350
    else:
        d_to = 3700
    d_from = d_to - 850
    plot_data.append(
        # set arrow from 33.7,1500 to 33.7,2350 as 11 lt -1
        'set arrow from {t:.3f},{d_from:d} to {t:.3f},{d_to:d} lt -1'.format(
            t=video_data.TIME_VIDEO_END.time,
            d_from=d_from,
            d_to=d_to,
        ))
    plot_data.append(
        # set label 11 "End video 33.7s" at 33.7,1400 center font ",10" # rotate by 45
        'set label "End video {t:.1f}s" at {t:.1f},{d:d} center font ",10"'.
        format(t=video_data.TIME_VIDEO_END.time, d=d_from - 100))
    if offset_distance_at_t == 0.0:
        # Plot individual arrows and labels of end of video
        plot_data.extend([
            # set arrow from 23.7,2432.8 to 33.7,2432.8 as 8 lt 1
            'set arrow from {t0:.3f},{distance:.3f} to {t1:.3f},{distance:.3f} lt {lt:d}'
            .format(
                t0=video_data.TIME_VIDEO_END.time - 10,
                t1=video_data.TIME_VIDEO_END.time,
                distance=np.interp(
                    video_data.TIME_VIDEO_END.time,
                    three_dist_arrays[i][:, 0],
                    three_dist_arrays[i][:, 1],
                ),
                lt=i + 1,
            ) for i in range(len(three_dist_arrays))
        ])
        plot_data.extend([
            # label 10 "2770m" at 23,2770 right font ",10" # rotate by 45
            'set label "{distance:.0f}m" at {t:.1f},{distance:.1f} right font ",10"'
            .format(
                t=video_data.TIME_VIDEO_END.time - 10 - 1,
                distance=np.interp(
                    video_data.TIME_VIDEO_END.time,
                    three_dist_arrays[i][:, 0],
                    three_dist_arrays[i][:, 1],
                ),
            ) for i in range(len(three_dist_arrays))
        ])
    return plot_data
Exemple #6
0
def gnuplot_angle_of_view(
        stream: typing.TextIO = sys.stdout) -> typing.List[str]:

    for raw_aspect in video_data.AIRCRAFT_ASPECTS_FROM_WING_TIPS:
        t = raw_aspect.video_time
        px_len = raw_aspect.length
        px_span = raw_aspect.span

    arrays_from_wing_tips = {
        min_max: aspect.aspects_from_wing_tips(min_max)
        for min_max in list(video_data.ErrorDirection)
    }
    # raw_data = video_data.AIRCRAFT_ASPECTS_FROM_WING_TIPS
    timebase_from_wing_tips = arrays_from_wing_tips[
        video_data.ErrorDirection.MID][:, 0]
    aspect_from_wing_tips_with_errors = np.column_stack((
        timebase_from_wing_tips,
        arrays_from_wing_tips[video_data.ErrorDirection.MID][:, 1],
        arrays_from_wing_tips[video_data.ErrorDirection.MIN][:, 0],
        arrays_from_wing_tips[video_data.ErrorDirection.MAX][:, 0],
        arrays_from_wing_tips[video_data.ErrorDirection.MIN][:, 1],
        arrays_from_wing_tips[video_data.ErrorDirection.MAX][:, 1],
    ))
    aspect_fitted_from_wing_tips = aspect.aspect_from_wing_tips_fitted_line()

    # Get the min/mid/max ground speed fits
    offset_distance_at_t = video_data.TIME_VIDEO_END_ASPHALT.time
    gs_fits = [
        plot_common.get_gs_fit(err) for err in video_data.ErrorDirection
    ]
    offsets = [
        video_data.RUNWAY_LEN_M -
        video_analysis.ground_speed_integral(0, offset_distance_at_t, gs_fit)
        for gs_fit in gs_fits
    ]
    # Establish observer
    observer_xy_start_runway = plot_common.observer_xy()

    notes = [
        '"{}"'.format('Aspects, errors, and polynomial fit.'),
        'Columns:',
        '1: time (s)',
        '2: horizontal_angle (deg)',
        '3: -dt',
        '4: +dt',
        '5: -dy (deg)',
        '6: +dy (deg)',
        '7: horizontal_angle_fit (deg)',
        '8: vertical_angle (deg)',
        '9: -dt',
        '10: +dt',
        '11: -dy (deg)',
        '12: +dy (deg)',
        '13: vertical_angle_fit (deg)',
    ]
    if len(notes):
        stream.write('# Notes:\n')
        for note in notes:
            stream.write('# {}\n'.format(note))
    plot_common.gnuplot_write_arrays(
        stream,
        aspect_from_wing_tips_with_errors,
        aspect_fitted_from_wing_tips,
    )
    return []
def gnuplot_ground_speed_extrapolated(
        stream: typing.TextIO = sys.stdout) -> typing.List[str]:
    gs_arrays = [
        video_analysis.ground_speeds(min_max)
        for min_max in list(video_data.ErrorDirection)
    ]
    gs_fits = [
        plot_common.get_gs_fit(err) for err in video_data.ErrorDirection
    ]
    result = [
        '# "{}"'.format(
            'Grounds speed, mid data and smoothed data, extrapolated.')
    ]
    notes = [
        'Columns',
        '1: Time (s)',
        '2: Ground speed, mid values (knots).',
        '3: Time, minimum (s).',
        '4: Time, maximum (s).',
        '5: Ground speed, min values (knots).',
        '6: Ground speed, max values (knots).',
        '7: Ground speed extrapolated, mid values (knots).',
        '8: Ground speed extrapolated, min values (knots).',
        '9: Ground speed extrapolated, max values (knots).',
    ]
    if len(notes):
        result.append('#')
        result.append('# Notes:')
        for note in notes:
            result.append('# {}'.format(note))
    gs_arrays_extrapolated = []
    for fit in gs_fits:
        gs_arrays_extrapolated.append(
            np.array([(t, video_analysis.ground_speed_from_fit(t, fit))
                      for t in plot_constants.EXTRAPOLATED_RANGE]))
    # Convert selected columns to knots
    k = video_utils.m_p_s_to_knots(1.0)
    for i in range(3):
        gs_arrays[i][:, 1] *= k
        gs_arrays_extrapolated[i][:, 1] *= k

    stream.write('\n'.join(result))
    stream.write('\n')
    plot_common.gnuplot_write_arrays(
        stream,
        gs_arrays[1],
        np.column_stack((gs_arrays[1][:, 0],
                         gs_arrays[1][:, 0] - video_data.ERROR_TIMESTAMP)),
        np.column_stack((gs_arrays[1][:, 0],
                         gs_arrays[1][:, 0] + video_data.ERROR_TIMESTAMP)),
        gs_arrays[0],
        gs_arrays[2],
        gs_arrays_extrapolated[1],
        gs_arrays_extrapolated[0],
        gs_arrays_extrapolated[2],
    )
    # Print time for v=0
    plot_data = []
    for i in range(len(gs_arrays_extrapolated)):
        t = video_utils.interpolate(gs_arrays_extrapolated[i][:, 1],
                                    gs_arrays_extrapolated[i][:, 0], 0.0)
        print('Time start[{:d}] t, v=0: {:.3f} Polynomial roots: {}'.format(
            i, t, np.roots(list(reversed(gs_fits[i])))))
        plot_data.append(
            'set arrow from {t:.3f},{offset:d} to {t:.3f},{zero:.3f} lt {lt:d}'
            .format(
                t=t,
                offset=50 + i * 25,
                zero=0.0,
                lt=i + 1,
            ))
        plot_data.append(
            'set label "t={t0:.1f}s" at {t1:.1f},{offset:d} left font ",12"'.
            format(
                t0=t,
                t1=t - 2,
                offset=54 + i * 25,
            ))
    return plot_data