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