examples/a121/algo/surface_velocity/example_app.py

examples/a121/algo/surface_velocity/example_app.py#

  1# Copyright (c) Acconeer AB, 2023
  2# All rights reserved
  3
  4from __future__ import annotations
  5
  6import numpy as np
  7
  8# Added here to force pyqtgraph to choose PySide
  9import PySide6  # noqa: F401
 10
 11import pyqtgraph as pg
 12
 13import acconeer.exptool as et
 14from acconeer.exptool import a121
 15from acconeer.exptool.a121.algo.surface_velocity import ExampleApp, ExampleAppConfig
 16
 17
 18def main():
 19    args = a121.ExampleArgumentParser().parse_args()
 20    et.utils.config_logging(args)
 21
 22    client = a121.Client.open(**a121.get_client_args(args))
 23
 24    example_app_config = ExampleAppConfig(
 25        surface_distance=0.40,
 26        sensor_angle=35,
 27        num_points=4,
 28        step_length=6,
 29        hwaas=16,
 30        sweeps_per_frame=128,
 31        sweep_rate=2000,
 32        time_series_length=1024,
 33    )
 34
 35    example_app = ExampleApp(
 36        client=client,
 37        sensor_id=1,
 38        example_app_config=example_app_config,
 39    )
 40    example_app.start()
 41
 42    pg_updater = PGUpdater(
 43        example_app_config,
 44    )
 45
 46    pg_process = et.PGProcess(pg_updater)
 47    pg_process.start()
 48
 49    interrupt_handler = et.utils.ExampleInterruptHandler()
 50    print("Press Ctrl-C to end session")
 51
 52    print(f"Measured distances (m): {np.around(example_app.processor.distances, 2)}")
 53
 54    while not interrupt_handler.got_signal:
 55        example_app_result = example_app.get_next()
 56        print(
 57            f"Estimated velocity {np.around(example_app_result.velocity, 2)} m/s, "
 58            f"at distance {np.around(example_app_result.distance_m, 2)} m"
 59        )
 60
 61        try:
 62            pg_process.put_data(example_app_result)
 63        except et.PGProccessDiedException:
 64            break
 65
 66    print("Disconnecting...")
 67    pg_process.close()
 68    client.close()
 69
 70
 71class PGUpdater:
 72    _VELOCITY_Y_SCALE_MARGIN_M = 0.25
 73
 74    def __init__(
 75        self,
 76        example_app_config: ExampleAppConfig,
 77    ):
 78        self.slow_zone = example_app_config.slow_zone
 79        self.history_length_s = 10
 80        if example_app_config.frame_rate is None:
 81            estimated_frame_rate = (
 82                example_app_config.sweep_rate / example_app_config.sweeps_per_frame
 83            )
 84        else:
 85            estimated_frame_rate = example_app_config.frame_rate
 86
 87        self.history_length_n = int(np.around(self.history_length_s * estimated_frame_rate))
 88
 89        self.setup_is_done = False
 90
 91    def setup(self, win):
 92        c0_dashed_pen = et.utils.pg_pen_cycler(0, width=2.5, style="--")
 93
 94        # Velocity plot
 95
 96        self.velocity_history_plot = self._create_plot(win, row=0, col=0)
 97        self.velocity_history_plot.setTitle("Estimated velocity")
 98        self.velocity_history_plot.setLabel(axis="left", text="Velocity", units="m/s")
 99        self.velocity_history_plot.setLabel(axis="bottom", text="Time", units="s")
100        self.velocity_history_plot.addLegend(labelTextSize="10pt")
101        self.velocity_smooth_limits = et.utils.SmoothLimits()
102
103        self.velocity_curve = self.velocity_history_plot.plot(
104            pen=et.utils.pg_pen_cycler(0), name="Estimated velocity"
105        )
106
107        self.psd_html = (
108            '<div style="text-align: center">'
109            '<span style="color: #FFFFFF;font-size:13pt;">'
110            "{}</span></div>"
111        )
112
113        self.distance_text_item = pg.TextItem(
114            html=self.psd_html,
115            fill=pg.mkColor(0x1F, 0x77, 0xB4, 180),
116            anchor=(0.5, 0),
117        )
118
119        self.velocity_history_plot.addItem(self.distance_text_item)
120
121        self.velocity_history = np.zeros(self.history_length_n)
122
123        self.lower_std_history = np.zeros(self.history_length_n)
124        self.upper_std_history = np.zeros(self.history_length_n)
125
126        self.lower_std_curve = self.velocity_history_plot.plot()
127        self.upper_std_curve = self.velocity_history_plot.plot()
128
129        fbi = pg.FillBetweenItem(
130            self.lower_std_curve,
131            self.upper_std_curve,
132            brush=pg.mkBrush(f"{et.utils.color_cycler(0)}50"),
133        )
134
135        self.velocity_history_plot.addItem(fbi)
136
137        # PSD plot
138
139        self.psd_plot = self._create_plot(win, row=1, col=0)
140        self.psd_plot.setTitle("PSD<br>(colored area represents the slow zone)")
141        self.psd_plot.setLabel(axis="left", text="Power")
142        self.psd_plot.setLabel(axis="bottom", text="Velocity", units="m/s")
143        self.psd_plot.addLegend(labelTextSize="10pt")
144
145        self.psd_smooth_max = et.utils.SmoothMax(tau_grow=0.5, tau_decay=2.0)
146        self.psd_curve = self.psd_plot.plot(pen=et.utils.pg_pen_cycler(0), name="PSD")
147        self.psd_threshold = self.psd_plot.plot(pen=c0_dashed_pen, name="Threshold")
148
149        psd_slow_zone_color = et.utils.color_cycler(0)
150        psd_slow_zone_color = f"{psd_slow_zone_color}50"
151        psd_slow_zone_brush = pg.mkBrush(psd_slow_zone_color)
152
153        self.psd_slow_zone = pg.LinearRegionItem(brush=psd_slow_zone_brush, movable=False)
154        self.psd_plot.addItem(self.psd_slow_zone)
155
156        brush = et.utils.pg_brush_cycler(0)
157        self.psd_peak_plot_item = pg.PlotDataItem(
158            pen=None, symbol="o", symbolSize=8, symbolBrush=brush, symbolPen="k"
159        )
160        self.psd_plot.addItem(self.psd_peak_plot_item)
161
162        self.psd_plot.setLogMode(x=False, y=True)
163
164    def update(self, example_app_result) -> None:
165        processor_extra_result = example_app_result.processor_extra_result
166
167        lim = self.velocity_smooth_limits.update(example_app_result.velocity)
168
169        self.velocity_history_plot.setYRange(
170            lim[0] - self._VELOCITY_Y_SCALE_MARGIN_M, lim[1] + self._VELOCITY_Y_SCALE_MARGIN_M
171        )
172        self.velocity_history_plot.setXRange(-self.history_length_s, 0)
173
174        xs = np.linspace(-self.history_length_s, 0, self.history_length_n)
175
176        self.velocity_history = np.roll(self.velocity_history, -1)
177        self.velocity_history[-1] = example_app_result.velocity
178        self.velocity_curve.setData(xs, self.velocity_history)
179
180        velocity_html = self.psd_html.format(
181            f"Distance {np.around(example_app_result.distance_m, 2)} m"
182        )
183        self.distance_text_item.setHtml(velocity_html)
184        self.distance_text_item.setPos(
185            -self.history_length_s / 2, lim[1] + self._VELOCITY_Y_SCALE_MARGIN_M
186        )
187
188        self.lower_std_history = np.roll(self.lower_std_history, -1)
189        self.lower_std_history[-1] = (
190            example_app_result.velocity + 0.5 * processor_extra_result.peak_width
191        )
192        self.lower_std_curve.setData(xs, self.lower_std_history)
193
194        self.upper_std_history = np.roll(self.upper_std_history, -1)
195        self.upper_std_history[-1] = (
196            example_app_result.velocity - 0.5 * processor_extra_result.peak_width
197        )
198        self.upper_std_curve.setData(xs, self.upper_std_history)
199
200        lim = self.psd_smooth_max.update(processor_extra_result.psd)
201        self.psd_plot.setYRange(np.log(0.5), np.log(lim))
202        self.psd_plot.setXRange(
203            processor_extra_result.max_bin_vertical_vs[0],
204            processor_extra_result.max_bin_vertical_vs[-1],
205        )
206        self.psd_curve.setData(
207            processor_extra_result.vertical_velocities, processor_extra_result.psd
208        )
209        self.psd_threshold.setData(
210            processor_extra_result.vertical_velocities, processor_extra_result.psd_threshold
211        )
212        if processor_extra_result.peak_idx is not None:
213            self.psd_peak_plot_item.setData(
214                [processor_extra_result.vertical_velocities[processor_extra_result.peak_idx]],
215                [processor_extra_result.psd[processor_extra_result.peak_idx]],
216            )
217        else:
218            self.psd_peak_plot_item.clear()
219
220        middle_idx = int(np.around(processor_extra_result.vertical_velocities.shape[0] / 2))
221        self.psd_slow_zone.setRegion(
222            [
223                processor_extra_result.vertical_velocities[middle_idx - self.slow_zone],
224                processor_extra_result.vertical_velocities[middle_idx + self.slow_zone],
225            ]
226        )
227
228    @staticmethod
229    def _create_plot(parent: pg.GraphicsLayout, row: int, col: int) -> pg.PlotItem:
230        velocity_history_plot = parent.addPlot(row=row, col=col)
231        velocity_history_plot.setMenuEnabled(False)
232        velocity_history_plot.setMouseEnabled(x=False, y=False)
233        velocity_history_plot.hideButtons()
234        velocity_history_plot.showGrid(x=True, y=True, alpha=0.5)
235
236        return velocity_history_plot
237
238
239if __name__ == "__main__":
240    main()

View this example on GitHub: acconeer/acconeer-python-exploration