440 lines
19 KiB
C++
440 lines
19 KiB
C++
#include "RadioWidget.h"
|
||
#include <QDebug>
|
||
#include <QHBoxLayout>
|
||
#include <QVBoxLayout>
|
||
#include <vector>
|
||
#include "../component/FrequentShower.h"
|
||
#include "../component/ProgressBar.h"
|
||
#include "../Global.h"
|
||
#include "../menu/BaseWidgetMenu.h"
|
||
#include "BackEnd.h"
|
||
#include "../BackEnd/MainWidget.h"
|
||
#include "Demo_Gallery/base/export.h"
|
||
#include "Core/plottable/export.h"
|
||
static double max = 160;
|
||
static renderive::Range frequent_range = {-12000, 12000};
|
||
static bool radio_mock_perf_mode() {
|
||
bool ok = false;
|
||
int value = qEnvironmentVariableIntValue("RADIO_MOCK_PERF_MODE", &ok);
|
||
return ok ? value != 0 : true;
|
||
}
|
||
static bool radio_show_performance_overlay() {
|
||
bool ok = false;
|
||
int value = qEnvironmentVariableIntValue("RADIO_SHOW_PERFORMANCE_OVERLAY", &ok);
|
||
return ok ? value != 0 : false;
|
||
}
|
||
static void configure_radio_plot_perf(renderive::Latency_Eager_Plot* plot) {
|
||
if (!radio_mock_perf_mode())
|
||
return;
|
||
plot->set_max_render_fps(1000);
|
||
}
|
||
static void apply_radio_performance_overlay(renderive::Latency_Eager_Plot* plot) {
|
||
static bool logged = false;
|
||
const bool enabled = radio_show_performance_overlay();
|
||
if (!logged) {
|
||
logged = true;
|
||
qInfo() << "Performance overlay enabled:" << enabled;
|
||
}
|
||
if (!plot || !plot->core())
|
||
return;
|
||
renderive::set_performance_shower_enabled(*plot->core(), enabled);
|
||
}
|
||
QWidget* Radio_Widget::create_spectrogram_power_plot() {
|
||
class Spectrum_Plot : public renderive::Latency_Eager_Plot {
|
||
public:
|
||
std::shared_ptr<renderive::Frequency_Axis> x_axis{};
|
||
std::shared_ptr<renderive::Axis> y_axis{};
|
||
std::shared_ptr<renderive::Multi_Select_Rect> msr{};
|
||
std::shared_ptr<renderive::Spectrum> sp{};
|
||
QMenu* menu{};
|
||
void init() override {
|
||
Latency_Eager_Plot::init();
|
||
configure_radio_plot_perf(this);
|
||
object_name = "Spectrum_Plot";
|
||
auto data = create_renderable_node(get_root_renderable(), "Data_Renderable");
|
||
// auto frequency_axis_cache = create_renderable_node(get_root_renderable(), "Frequency_Axis_Cache");
|
||
// auto power_axis_cache = create_renderable_node(get_root_renderable(), "Power_Axis_Cache");
|
||
// frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
// power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
auto axis_cache = create_renderable_node(get_root_renderable(), "Power_Axis_Cache");
|
||
axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
|
||
x_axis = renderive::Frequency_Axis::Builder(axis_cache, renderive::Orientation::Horizontal)
|
||
.set_coord_range(frequent_range)
|
||
.set_tick_length(-10)
|
||
.build();
|
||
y_axis = renderive::Axis::Builder(axis_cache, renderive::Orientation::Vertical)
|
||
.set_coord_range({max, 0})
|
||
.set_unit_text("功率")
|
||
.build();
|
||
sp = renderive::Spectrum::Builder(data, x_axis, y_axis)
|
||
.set_frequency_range(frequent_range)
|
||
.set_frequency_point_size(1024)
|
||
.build();
|
||
msr = renderive::Multi_Select_Rect::Builder(overlay, x_axis, y_axis).build();
|
||
}
|
||
protected:
|
||
void resizeEvent(QResizeEvent* event) override {
|
||
x_axis->set_x(0);
|
||
x_axis->set_y(height() - 1);
|
||
x_axis->set_pixel_size(width());
|
||
y_axis->set_x(0);
|
||
y_axis->set_y(0);
|
||
y_axis->set_pixel_size(height());
|
||
}
|
||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||
if (!menu) {
|
||
auto ws = concat_widgets(
|
||
bw(cw(sp.get(), -1), "频谱图"),
|
||
bw(cw(x_axis.get()), "频率x轴"),
|
||
bw(cw(y_axis.get()), "功率y轴"),
|
||
bw(cw(msr.get()), "多选框"));
|
||
menu = create_menu(this, ws);
|
||
}
|
||
menu->exec(event->globalPos());
|
||
}
|
||
};
|
||
auto w = new Spectrum_Plot;
|
||
w->init();
|
||
apply_radio_performance_overlay(w);
|
||
spectrogram_line = w->sp;
|
||
if (!radio_mock_perf_mode()) {
|
||
progress_bar1->set_value = [this]() {
|
||
double middle_frequent = this->spectrogram_line->frequency_range().middle();
|
||
bool ok = false;
|
||
double power = spectrogram_line->get_power(middle_frequent, ok);
|
||
if (ok)
|
||
this->progress_bar1->setValue(power);
|
||
};
|
||
}
|
||
return w;
|
||
}
|
||
QWidget* Radio_Widget::create_water_fall_plot() {
|
||
class Water_Fall_Plot : public renderive::Latency_Eager_Plot {
|
||
public:
|
||
std::shared_ptr<renderive::Frequency_Axis> x_axis{};
|
||
std::shared_ptr<renderive::Time_Axis> y_axis{};
|
||
std::shared_ptr<renderive::Multi_Select_Rect> msr{};
|
||
std::shared_ptr<renderive::Waterfall> wf{};
|
||
QMenu* menu{};
|
||
void init() override {
|
||
Latency_Eager_Plot::init();
|
||
configure_radio_plot_perf(this);
|
||
object_name = "Water_Fall_Plot";
|
||
auto stream_cache = create_renderable_node(get_root_renderable(), "Waterfall_Stream_Cache");
|
||
auto data = create_renderable_node(stream_cache, "Data_Renderable");
|
||
auto time_axis = create_renderable_node(stream_cache, "Time_Axis_Renderable");
|
||
auto frequency_axis_cache = create_renderable_node(get_root_renderable(), "Frequency_Axis_Cache");
|
||
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
|
||
stream_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, renderive::Orientation::Horizontal)
|
||
.set_tick_length(-10)
|
||
.set_sub_tick_length(-5)
|
||
.set_coord_range(frequent_range)
|
||
.build();
|
||
y_axis = renderive::Time_Axis::Builder(time_axis, renderive::Orientation::Vertical)
|
||
.set_time_point_size(100)
|
||
.build();
|
||
wf = renderive::Waterfall::Builder(data, x_axis, y_axis)
|
||
.set_frequency_range(frequent_range)
|
||
.set_frequency_point_size(1024)
|
||
.set_power_range({0, max})
|
||
.build();
|
||
msr = renderive::Multi_Select_Rect::Builder(overlay, x_axis, y_axis).build();
|
||
}
|
||
protected:
|
||
void resizeEvent(QResizeEvent* event) override {
|
||
x_axis->set_x(0);
|
||
x_axis->set_y(height());
|
||
x_axis->set_pixel_size(width());
|
||
y_axis->set_x(0);
|
||
y_axis->set_y(0);
|
||
y_axis->set_pixel_size(height());
|
||
}
|
||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||
if (!menu) {
|
||
auto ws = concat_widgets(
|
||
bw(cw(wf.get(), -1), "瀑布图"),
|
||
bw(cw(x_axis.get()), "频率轴"),
|
||
bw(cw(y_axis.get()), "时间轴"),
|
||
bw(cw(msr.get()), "多选框"));
|
||
menu = create_menu(this, ws);
|
||
}
|
||
menu->exec(event->globalPos());
|
||
}
|
||
};
|
||
auto w = new Water_Fall_Plot;
|
||
w->init();
|
||
apply_radio_performance_overlay(w);
|
||
water_fall = w->wf;
|
||
return w;
|
||
}
|
||
QWidget* Radio_Widget::create_audio_spectrogram_plot() {
|
||
class Spectrum_Plot : public renderive::Latency_Eager_Plot {
|
||
public:
|
||
std::shared_ptr<renderive::Frequency_Axis> x_axis{};
|
||
std::shared_ptr<renderive::Axis> y_axis{};
|
||
std::shared_ptr<renderive::Multi_Select_Rect> msr{};
|
||
std::shared_ptr<renderive::Spectrum> sp{};
|
||
QMenu* menu{};
|
||
void init() override {
|
||
Latency_Eager_Plot::init();
|
||
configure_radio_plot_perf(this);
|
||
object_name = "Spectrum_Plot";
|
||
auto data = create_renderable_node(get_root_renderable(), "Data_Renderable");
|
||
auto frequency_axis_cache = create_renderable_node(get_root_renderable(), "Frequency_Axis_Cache");
|
||
auto power_axis_cache = create_renderable_node(get_root_renderable(), "Power_Axis_Cache");
|
||
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
|
||
frequency_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
x_axis = renderive::Frequency_Axis::Builder(frequency_axis_cache, renderive::Orientation::Horizontal)
|
||
.set_coord_range({-12000, 12000})
|
||
.set_tick_length(-10)
|
||
.set_sub_tick_length(-5)
|
||
.build();
|
||
y_axis = renderive::Axis::Builder(power_axis_cache, renderive::Orientation::Vertical)
|
||
.set_coord_range({12000, -12000})
|
||
.set_unit_text("功率").build();
|
||
sp = renderive::Spectrum::Builder(data, x_axis, y_axis)
|
||
.set_frequency_range({-12000, 12000})
|
||
.set_frequency_point_size(512)
|
||
.build();
|
||
msr = renderive::Multi_Select_Rect::Builder(overlay, x_axis, y_axis).build();
|
||
}
|
||
protected:
|
||
void resizeEvent(QResizeEvent* event) override {
|
||
x_axis->set_x(0);
|
||
x_axis->set_y(height() - 1);
|
||
x_axis->set_pixel_size(width());
|
||
y_axis->set_x(0);
|
||
y_axis->set_y(0);
|
||
y_axis->set_pixel_size(height());
|
||
}
|
||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||
if (!menu) {
|
||
auto ws = concat_widgets(
|
||
bw(cw(sp.get(), -1), "频谱图"),
|
||
bw(cw(x_axis.get()), "频率x轴"),
|
||
bw(cw(y_axis.get()), "功率y轴"),
|
||
bw(cw(msr.get()), "多选框"));
|
||
menu = create_menu(this, ws);
|
||
}
|
||
menu->exec(event->globalPos());
|
||
}
|
||
};
|
||
auto w = new Spectrum_Plot;
|
||
w->init();
|
||
apply_radio_performance_overlay(w);
|
||
audio_spectrogram_plot = w->sp;
|
||
return w;
|
||
}
|
||
QWidget* Radio_Widget::create_time_power_plot() {
|
||
class Audio_Plot : public renderive::Latency_Eager_Plot {
|
||
public:
|
||
std::shared_ptr<renderive::Time_Axis> x_axis{};
|
||
std::shared_ptr<renderive::Axis> y_axis{};
|
||
std::shared_ptr<renderive::Audio_Frequency> audio{};
|
||
std::shared_ptr<renderive::Multi_Select_Rect> msr{};
|
||
QMenu* menu{};
|
||
void init() override {
|
||
Latency_Eager_Plot::init();
|
||
configure_radio_plot_perf(this);
|
||
object_name = "Audio_Plot";
|
||
auto stream_cache = create_renderable_node(get_root_renderable(), "Audio_Stream_Cache");
|
||
auto data = create_renderable_node(stream_cache, "Data_Renderable");
|
||
auto time_axis = create_renderable_node(stream_cache, "Time_Axis_Renderable");
|
||
auto power_axis_cache = create_renderable_node(get_root_renderable(), "Power_Axis_Cache");
|
||
auto overlay = create_renderable_node(get_root_renderable(), "Overlay_Renderable");
|
||
stream_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
power_axis_cache->set_cache_mode(renderive::Renderable_Cache_Mode::Local_Pixel);
|
||
x_axis = renderive::Time_Axis::Builder(time_axis, renderive::Orientation::Horizontal)
|
||
.set_tick_length(-10)
|
||
.set_sub_tick_length(-5)
|
||
.build();
|
||
y_axis = renderive::Axis::Builder(power_axis_cache, renderive::Orientation::Vertical)
|
||
.set_coord_range({12000, -12000})
|
||
.build();
|
||
audio = renderive::Audio_Frequency::Builder(data, x_axis, y_axis)
|
||
.build();
|
||
msr = renderive::Multi_Select_Rect::Builder(overlay, x_axis, y_axis).build();
|
||
}
|
||
protected:
|
||
void resizeEvent(QResizeEvent* event) override {
|
||
x_axis->set_x(0);
|
||
x_axis->set_y(height() - 1);
|
||
x_axis->set_pixel_size(width());
|
||
y_axis->set_x(0);
|
||
y_axis->set_y(0);
|
||
y_axis->set_pixel_size(height());
|
||
x_axis->set_time_point_size(x_axis->get_pixel_point_size());
|
||
}
|
||
void contextMenuEvent(QContextMenuEvent* event) override {
|
||
if (!menu) {
|
||
auto ws = concat_widgets(
|
||
bw(cw(audio.get(), -1), "音频图"),
|
||
bw(cw(y_axis.get()), "功率y轴"),
|
||
bw(cw(x_axis.get()), "时间轴"),
|
||
bw(cw(msr.get()), "多选框"));
|
||
menu = create_menu(this, ws);
|
||
}
|
||
menu->exec(event->globalPos());
|
||
}
|
||
};
|
||
auto w = new Audio_Plot;
|
||
w->init();
|
||
apply_radio_performance_overlay(w);
|
||
time_power = w->audio;
|
||
return w;
|
||
}
|
||
//am, atu, vsql, nb, nr, dnf, pre, att, spl, fil1, agc_a
|
||
Radio_Widget::Radio_Widget(QWidget* parent) : Base_Widget(parent) {
|
||
setObjectName("电台界面");
|
||
voice_value = Back_End::instance()->YsGetAfGain();
|
||
rf_gain_value = Back_End::instance()->YsGetRfGain();
|
||
int font_size = 20;
|
||
m1 = create_normal("RF Gain\n" + QString::number(rf_gain_value), font_size);
|
||
m2 = create_normal("FFT SPAN\n100K", font_size);
|
||
m3 = create_normal("频率表\n(12)89.6MHZ", font_size);
|
||
m4 = create_normal("MODEM\nRTTY", font_size);
|
||
m5 = create_normal("FIL1\n20K", font_size);
|
||
m6 = create_normal("MSG1\n", font_size);
|
||
select_value_widget = new Frequent_Shower(this);
|
||
int n = 5;
|
||
std::vector<double> tick_list(n);
|
||
double space = 100.0 / n;
|
||
for (int i = 0; i < n; ++i) {
|
||
tick_list[i] = i * space;
|
||
}
|
||
QColor orange(255, 165, 0);
|
||
progress_bar1 = new Progress_Bar({
|
||
Tick("S", Qt::black),
|
||
{"3", Qt::black},
|
||
{"6", Qt::black},
|
||
{"9", Qt::black},
|
||
{"20", Qt::black},
|
||
{"40", orange},
|
||
{"60", orange},
|
||
{"100", orange},
|
||
{"+160", Qt::red},
|
||
}, this);
|
||
progress_bar2 = new Progress_Bar({
|
||
{"1", Qt::black},
|
||
{"2", Qt::yellow},
|
||
{"3", Qt::red},
|
||
{">5", Qt::red},
|
||
}, this);
|
||
progress_bar1->tick_space_w = 2.0f;
|
||
auto main = create_vbox_layout({}, this);
|
||
main->addStretch(1);
|
||
{
|
||
// 中心Layout
|
||
auto center = create_hbox_layout();
|
||
main->addLayout(center, 7);
|
||
auto front = create_vbox_layout();
|
||
center->addLayout(front, 1);
|
||
front->addWidget(m1, 3);
|
||
front->addStretch(1);
|
||
front->addWidget(m2, 3);
|
||
center->addSpacing(4);
|
||
auto middle = create_vbox_layout();
|
||
center->addLayout(middle, 9);
|
||
middle->addStretch(1);
|
||
middle->addWidget(select_value_widget, 4);
|
||
auto middle_h = create_hbox_layout();
|
||
middle->addLayout(middle_h, 2);
|
||
middle_h->addWidget(progress_bar1, 5);
|
||
middle_h->addStretch(1);
|
||
middle_h->addWidget(progress_bar2, 3);
|
||
center->addStretch(4);
|
||
auto back1 = create_vbox_layout();
|
||
center->addLayout(back1, 1);
|
||
back1->addWidget(m3, 3);
|
||
back1->addStretch(1);
|
||
back1->addWidget(m4, 3);
|
||
center->addSpacing(4);
|
||
auto back2 = create_vbox_layout();
|
||
center->addLayout(back2, 1);
|
||
back2->addWidget(m5, 3);
|
||
back2->addStretch(1);
|
||
back2->addWidget(m6, 3);
|
||
}
|
||
main->addStretch(1);
|
||
auto a_h1 = create_hbox_layout();
|
||
a_h1->setSpacing(8);
|
||
main->addLayout(a_h1, 12);
|
||
a_h1->addWidget(create_spectrogram_power_plot(), 12);
|
||
a_h1->addWidget(create_time_power_plot(), 4);
|
||
main->addSpacing(8);
|
||
auto a_h2 = create_hbox_layout();
|
||
a_h2->setSpacing(8);
|
||
main->addLayout(a_h2, 12);
|
||
a_h2->addWidget(create_water_fall_plot(), 12);
|
||
a_h2->addWidget(create_audio_spectrogram_plot(), 4);
|
||
init_menu();
|
||
}
|
||
Radio_Widget::~Radio_Widget() {
|
||
Back_End::instance()->stop_mock_thread();
|
||
}
|
||
void Radio_Widget::init_menu() {
|
||
int font_size = 30;
|
||
auto btn1_1 = new Menu_Button("模式设置", font_size, []() {});
|
||
auto btn1_2 = new Value_Menu_Button("发射功率:%1 W", font_size, [&](Value_Menu_Button* that, bool add) {
|
||
that->setValue(that->value + (double)(add ? 1 : -1));
|
||
}, -1);
|
||
auto btn2_1 = new Value_Menu_Button("参考增益:%1", font_size, [&](Value_Menu_Button* that, bool add) {
|
||
that->setValue(that->value + (double)(add ? 1 : -1));
|
||
}, -1);
|
||
base_widget_menu->add_menu_button_on_layout1({btn1_1, btn1_2, {btn2_1},});
|
||
//base_widget_menu->add_menu_button_on_layout2( );
|
||
Base_Widget_Menu* sub1 = base_widget_menu->create_sub_widget_menu(btn1_1);
|
||
auto s1_1 = new Menu_Button("AM", font_size, []() {});
|
||
auto s1_2 = new Menu_Button("FM", font_size, []() {});
|
||
auto s1_3 = new Menu_Button("SSB", font_size, []() {});
|
||
auto s1_4 = new Menu_Button("CW", font_size, []() {});
|
||
sub1->add_menu_button_on_layout1({s1_1, s1_2, s1_3, s1_4}, 1);
|
||
}
|
||
void Radio_Widget::focusInEvent(QFocusEvent* event) {
|
||
QWidget::focusInEvent(event);
|
||
select_value_widget->setFocus();
|
||
}
|
||
void Radio_Widget::keyPressEvent(QKeyEvent* event) {
|
||
if (event->type() == QEvent::KeyPress) {
|
||
Back_End* g = Back_End::instance();
|
||
auto* key_event = dynamic_cast<QKeyEvent*>(event);
|
||
if (g->match(key_event, "切换音量/RF Gain")) {
|
||
if (m1->text().startsWith("RF Gain")) {
|
||
m1->setText("Voice\n" + QString::number(voice_value));
|
||
}
|
||
else {
|
||
m1->setText("RF Gain\n" + QString::number(rf_gain_value));
|
||
}
|
||
}
|
||
if (g->match(key_event, "特别增加")) {
|
||
if (m1->text().startsWith("RF Gain")) {
|
||
rf_gain_value++;
|
||
Back_End::instance()->YsSetRfGain(rf_gain_value);
|
||
m1->setText("RF Gain\n" + QString::number(rf_gain_value));
|
||
}
|
||
else {
|
||
voice_value++;
|
||
Back_End::instance()->YsSetAfGain(voice_value);
|
||
m1->setText("Voice\n" + QString::number(voice_value));
|
||
}
|
||
}
|
||
if (g->match(key_event, "特别减小")) {
|
||
if (m1->text().startsWith("RF Gain")) {
|
||
rf_gain_value--;
|
||
Back_End::instance()->YsSetRfGain(rf_gain_value);
|
||
m1->setText("RF Gain\n" + QString::number(rf_gain_value));
|
||
}
|
||
else {
|
||
voice_value--;
|
||
Back_End::instance()->YsSetAfGain(voice_value);
|
||
m1->setText("Voice\n" + QString::number(voice_value));
|
||
}
|
||
}
|
||
}
|
||
Base_Widget::keyPressEvent(event);
|
||
}
|