SatDump 2.0.0-alpha-520736c72
Loading...
Searching...
No Matches
rec_frontend.h
1#pragma once
2
3#include "base/remote_handler.h"
4#include "base/remote_handler_backend.h"
5#include "common/widgets/fft_plot.h"
6#include "common/widgets/waterfall_plot.h"
7#include "core/resources.h"
8#include "core/style.h"
9#include "dsp/device/dev.h"
10#include "dsp/device/options_displayer.h"
11#include "imgui/imgui.h"
12#include "imgui/imgui_internal.h"
13#include "utils/task_queue.h"
14#include <cstddef>
15#include <mutex>
16#include <volk/volk_alloc.hh>
17
18namespace satdump
19{
20 namespace handlers
21 {
22 class RecFrontendHandler : public RemoteHandlerHandler
23 {
24 private:
25 std::vector<ndsp::DeviceInfo> available_devices;
26 ndsp::DeviceInfo current_device;
27
28 ndsp::OptionsDisplayer dev_opt_disp;
29
30 std::mutex fm;
31 TaskQueue tq;
32
33 volk::vector<float> fft_vec;
34 std::shared_ptr<widgets::FFTPlot> fft_plot;
35 std::shared_ptr<widgets::WaterfallPlot> waterfall_plot;
36
37 bool is_started = false;
38
39 size_t last_fft_size = 0;
40 int fft_size = 0;
41 int fft_rate = 0;
42 int fft_avg_num = 0;
43
44 float buffer_usage = 0;
45
46 bool recording = false;
47 size_t rec_size = 0;
48
49 protected:
50 bool mustUpdate = true;
51 void handle_stream_data(std::string id, uint8_t *data, size_t size)
52 {
53 if (id == "upd")
54 {
55 mustUpdate = true;
56 }
57 else if (id == "fft" && (size / sizeof(float)) >= 8)
58 {
59 if (size != last_fft_size)
60 {
61 fft_vec.resize(size / sizeof(float));
62 fft_plot->set_ptr(fft_vec.data());
63 fft_plot->set_size(size / sizeof(float));
64 waterfall_plot->set_size(size / sizeof(float));
65 last_fft_size = size;
66 }
67
68 memcpy(fft_vec.data(), data, size);
69 waterfall_plot->push_fft(fft_vec.data());
70 }
71 else if (id == "rec_size" && size == sizeof(size_t))
72 {
73 rec_size = *((size_t *)data);
74 }
75 else if (id == "buffer_usage" && size == sizeof(float))
76 {
77 buffer_usage = *((float *)data);
78 }
79 }
80
81 public:
82 RecFrontendHandler(std::shared_ptr<RemoteHandlerBackend> bkd) : RemoteHandlerHandler(bkd)
83 {
84 fft_vec.resize(8);
85
86 fft_plot = std::make_shared<widgets::FFTPlot>(fft_vec.data(), 8, -150, 150, 10);
87 fft_plot->frequency = 431.8e6;
88 fft_plot->enable_freq_scale = true;
89
90 waterfall_plot = std::make_shared<widgets::WaterfallPlot>(65536 * 4, 2000);
91 waterfall_plot->set_size(8);
92 waterfall_plot->set_rate(30, 20);
93 waterfall_plot->set_palette(colormaps::loadMap(resources::getResourcePath("waterfall/classic.json")));
94 }
95 ~RecFrontendHandler() {}
96
97 void resyncFFT()
98 {
99 if (fft_plot->scale_max < fft_plot->scale_min)
100 {
101 fft_plot->scale_min = waterfall_plot->scale_min;
102 fft_plot->scale_max = waterfall_plot->scale_max;
103 }
104 else if (fft_plot->scale_min > fft_plot->scale_max)
105 {
106 fft_plot->scale_min = waterfall_plot->scale_min;
107 fft_plot->scale_max = waterfall_plot->scale_max;
108 }
109 else
110 {
111 waterfall_plot->scale_min = fft_plot->scale_min;
112 waterfall_plot->scale_max = fft_plot->scale_max;
113 }
114 }
115
116 // The Rest
117 void drawMenu()
118 {
119 if (mustUpdate)
120 {
121 tq.push(
122 [this]()
123 {
124 std::scoped_lock l(fm);
125 available_devices = bkd->get_cfg("available_devices");
126 current_device = bkd->get_cfg("current_device");
127
128 dev_opt_disp.clear();
129 dev_opt_disp.add_options(bkd->get_cfg("dev/list"));
130 dev_opt_disp.set_values(bkd->get_cfg("dev/cfg"));
131
132 fft_size = bkd->get_cfg("fft/size");
133 fft_rate = bkd->get_cfg("fft/rate");
134 fft_avg_num = bkd->get_cfg("fft/avg_num");
135 resyncFFT();
136
137 is_started = bkd->get_cfg("started");
138
139 buffer_usage = bkd->get_cfg("buffer_usage");
140
141 recording = bkd->get_cfg("recording");
142 rec_size = bkd->get_cfg("rec_size");
143 });
144
145 mustUpdate = false;
146 }
147
148 // std::scoped_lock l(fm);
149
150 if (ImGui::CollapsingHeader("Source", ImGuiTreeNodeFlags_DefaultOpen))
151 {
152 if (is_started)
153 style::beginDisabled();
154 if (ImGui::BeginCombo("Device##devicebox", current_device.name.c_str()))
155 {
156 for (auto &d : available_devices)
157 {
158 if (ImGui::Selectable(d.name.c_str(), d == current_device))
159 {
160 tq.push(
161 [this, d]()
162 {
163 std::scoped_lock l(fm);
164 bkd->set_cfg("current_device", (nlohmann::json)d);
165
166 dev_opt_disp.clear();
167 dev_opt_disp.add_options(bkd->get_cfg("dev/list"));
168 dev_opt_disp.set_values(bkd->get_cfg("dev/cfg"));
169 });
170 }
171 }
172
173 ImGui::EndCombo();
174 }
175 if (is_started)
176 style::endDisabled();
177
178 nlohmann::json changed = dev_opt_disp.draw();
179
180 if (changed.size() > 0)
181 {
182 tq.push(
183 [this, changed]()
184 {
185 std::scoped_lock l(fm);
186 auto r = bkd->set_cfg("dev/cfg", changed);
187 if (r >= RemoteHandlerBackend::RES_LISTUPD)
188 {
189 dev_opt_disp.clear();
190 dev_opt_disp.add_options(bkd->get_cfg("dev/list"));
191 dev_opt_disp.set_values(bkd->get_cfg("dev/cfg"));
192 }
193 });
194 }
195
196 if (!is_started)
197 {
198 if (ImGui::Button("Start"))
199 {
200 tq.push(
201 [this]()
202 {
203 std::scoped_lock l(fm);
204 bkd->set_cfg("started", true);
205 });
206 }
207 }
208 else
209 {
210 if (ImGui::Button("Stop"))
211 {
212 tq.push(
213 [this]()
214 {
215 std::scoped_lock l(fm);
216 bkd->set_cfg("started", false);
217 });
218 }
219 }
220
221 ImGui::ProgressBar(buffer_usage);
222 }
223
224 if (ImGui::CollapsingHeader("FFT", ImGuiTreeNodeFlags_DefaultOpen))
225 {
226 widgets::SteppedSliderFloat("FFT Max", &fft_plot->scale_max, -160, 150);
227 widgets::SteppedSliderFloat("FFT Min", &fft_plot->scale_min, -160, 150);
228
229 resyncFFT();
230
231 if (ImGui::InputInt("Size##fftsize", &fft_size))
232 {
233 tq.push(
234 [this]()
235 {
236 std::scoped_lock l(fm);
237 bkd->set_cfg("fft/size", fft_size);
238 });
239 }
240
241 if (ImGui::InputInt("Rate##fftrate", &fft_rate))
242 tq.push(
243 [this]()
244 {
245 std::scoped_lock l(fm);
246 bkd->set_cfg("fft/rate", fft_rate);
247 });
248
249 if (ImGui::InputInt("Avg Num##fftavg", &fft_avg_num))
250 tq.push(
251 [this]()
252 {
253 std::scoped_lock l(fm);
254 bkd->set_cfg("fft/avg_num", fft_avg_num);
255 });
256 }
257
258 if (ImGui::CollapsingHeader("Recording", ImGuiTreeNodeFlags_DefaultOpen))
259 {
260 if (!is_started)
261 style::beginDisabled();
262
263 if (!recording)
264 {
265 if (ImGui::Button("Rec Start"))
266 {
267 tq.push(
268 [this]()
269 {
270 std::scoped_lock l(fm);
271 bkd->set_cfg("recording", true);
272 });
273 }
274 }
275 else
276 {
277 ImGui::Text("Size : %lu", rec_size);
278
279 if (ImGui::Button("Rec Stop"))
280 {
281 tq.push(
282 [this]()
283 {
284 std::scoped_lock l(fm);
285 bkd->set_cfg("recording", false);
286 });
287 }
288 }
289
290 if (!is_started)
291 style::endDisabled();
292 }
293 }
294
295 void drawContents(ImVec2 win_size)
296 {
297 float right_width = win_size.x;
298 float wf_size = win_size.y;
299 bool show_waterfall = true;
300 float waterfall_ratio = 0.3;
301 float left_width = ImGui::GetCursorPosX() - 9;
302
303 ImGui::BeginChild("RecorderFFT", {right_width, wf_size}, false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
304 {
305 float fft_height = wf_size * (show_waterfall ? waterfall_ratio : 1.0);
306 float wf_height = wf_size * (1 - waterfall_ratio) + 15 * ui_scale;
307 float wfft_widht = right_width - 9 * ui_scale;
308 bool t = true;
309#ifdef __ANDROID__
310 int offset = 8;
311#else
312 int offset = 30;
313#endif
314 ImGui::SetNextWindowSizeConstraints(ImVec2((right_width + offset * ui_scale), 50), ImVec2((right_width + offset * ui_scale), wf_size));
315 ImGui::SetNextWindowSize(ImVec2((right_width + offset * ui_scale), show_waterfall ? waterfall_ratio * wf_size : wf_size));
316 ImGui::SetNextWindowPos(ImVec2(left_width, 25 * ui_scale));
317 if (ImGui::Begin("#fft", &t,
318 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoScrollbar |
319 ImGuiWindowFlags_NoScrollWithMouse))
320 {
321 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 9 * ui_scale);
322 fft_plot->draw({float(wfft_widht), fft_height});
323 if (show_waterfall && ImGui::IsMouseDragging(ImGuiMouseButton_Left))
324 waterfall_ratio = ImGui::GetWindowHeight() / wf_size;
325 }
326 ImGui::EndChild();
327 if (show_waterfall)
328 {
329 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 15 * ui_scale);
330 ImGui::SetCursorPosX(9 * ui_scale);
331 waterfall_plot->draw({wfft_widht, wf_height}, true);
332 }
333 }
334 ImGui::EndChild();
335 }
336
337 std::string getName() { return "RemoteHandleTest"; }
338
339 std::string getID() { return "remote_test_handler"; }
340 };
341 } // namespace handlers
342} // namespace satdump
A simple thread pool running tasks sequentially in a single thread.
Definition task_queue.h:24
std::string getName()
Get this handler's readable name.
Definition rec_frontend.h:337
void drawContents(ImVec2 win_size)
Render explorer contents (center/left)
Definition rec_frontend.h:295
void drawMenu()
Render explorer menu left sidebar.
Definition rec_frontend.h:117
Definition options_displayer.h:25
Definition dev.h:12
A simple single-thread thread pool.