Files
2026-08-10 19:55:29 +08:00

285 lines
11 KiB
C++

#include "BackEnd.h"
#ifdef MockBackEnd
#include "./MainWidget.h"
#include "../component/FrequentShower.h"
#include "../component/ProgressBar.h"
#include "IntermediateFrequencyWidget.h"
#include "RadioWidget.h"
#include "SweepFrequencyWidget.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <memory_resource>
#include <mutex>
#include <QByteArray>
#include <QDebug>
#include <QMetaObject>
#include <QPointer>
#include <utility>
#include <vector>
#include "export.h"
namespace {
enum class Trace_Mode {
Random,
Sine
};
struct Mock_Config {
bool performance{};
int interval_ms = 16;
int batch_count = 1;
Trace_Mode trace_mode = Trace_Mode::Random;
};
int read_env_int(const char* name, int fallback) {
bool ok = false;
int value = qEnvironmentVariableIntValue(name, &ok);
return ok ? value : fallback;
}
Trace_Mode read_trace_mode() {
QByteArray value = qgetenv("RADIO_MOCK_TRACE_MODE").trimmed().toLower();
return value == "sine" ? Trace_Mode::Sine : Trace_Mode::Random;
}
const char* trace_mode_text(Trace_Mode mode) {
return mode == Trace_Mode::Sine ? "sine" : "random";
}
Mock_Config read_mock_config() {
Mock_Config config;
config.performance = read_env_int("RADIO_MOCK_PERF_MODE", 0) != 0;
config.interval_ms = std::max(1, read_env_int("RADIO_MOCK_INTERVAL_MS", config.performance ? 1 : 16));
config.batch_count = std::max(1, read_env_int("RADIO_MOCK_BATCH_COUNT", config.performance ? 4 : 1));
config.trace_mode = read_trace_mode();
return config;
}
std::pmr::vector<double> make_random_data(renderive::Range range, int size, renderive::Memory_Domain domain) {
if (range.origin > range.target)
std::swap(range.origin, range.target);
std::pmr::vector<double> data(renderive::memory_resource(domain));
data.resize(static_cast<std::size_t>(std::max(0, size)));
renderive::fill_data(range, data);
return data;
}
double make_random_value(renderive::Range range) {
if (range.origin > range.target)
std::swap(range.origin, range.target);
return renderive::get_data(range);
}
double make_trace_value(renderive::Range range, Trace_Mode mode) {
if (mode == Trace_Mode::Random)
return make_random_value(range);
if (range.origin > range.target)
std::swap(range.origin, range.target);
static std::uint64_t sine_index = 0;
const double phase = static_cast<double>(sine_index++) * 0.08;
const double amplitude = range.length() * 0.40;
return range.center() + std::sin(phase) * amplitude;
}
renderive::Time_Of_Day current_render_time() {
const QTime now = QTime::currentTime();
return {now.isValid() ? now.msecsSinceStartOfDay() : -1};
}
void push_radio_data(Radio_Widget* rw, Trace_Mode trace_mode) {
if (!rw || !rw->spectrogram_line || !rw->water_fall || !rw->time_power || !rw->audio_spectrogram_plot || !rw->progress_bar1 || !rw->progress_bar2)
return;
{
auto r = rw->spectrogram_line->power_axis()->coord_range();
renderive::Range nr = {r.center() - r.length() / 4, r.center() + r.length() / 4};
auto data = make_random_data(nr, rw->spectrogram_line->frequency_point_size(), renderive::Memory_Domain::Spectrum);
double middle_power = data.empty() ? nr.center() : data[data.size() / 2];
rw->spectrogram_line->update_samples(std::move(data));
rw->progress_bar1->setValue(middle_power);
}
{
rw->water_fall->append_row(current_render_time(), make_random_data(rw->water_fall->power_range(), rw->water_fall->frequency_bin_count(), renderive::Memory_Domain::Waterfall));
}
{
renderive::Range r = rw->time_power->value_axis()->coord_range();
renderive::Range nr = {r.center() - r.length() / 4, r.center() + r.length() / 4};
rw->time_power->append_sample(current_render_time(), make_trace_value(nr, trace_mode));
}
{
auto r = rw->audio_spectrogram_plot->power_axis()->coord_range();
renderive::Range nr = {r.center() - r.length() / 4, r.center() + r.length() / 4};
rw->audio_spectrogram_plot->update_samples(make_random_data(nr, rw->audio_spectrogram_plot->frequency_point_size(), renderive::Memory_Domain::Spectrum));
}
rw->progress_bar2->setValue(make_random_value({1, 5}));
}
struct Mock_Performance_Frame {
std::pmr::vector<double> spectrum;
double spectrum_middle_power{};
renderive::Time_Of_Day waterfall_time;
std::pmr::vector<double> waterfall;
renderive::Time_Of_Day trace_time;
double trace_value{};
std::pmr::vector<double> audio;
double progress_value{};
};
struct Mock_Performance_Batch {
std::vector<Mock_Performance_Frame> frames;
};
struct Mock_Delivery_State {
std::mutex mutex;
std::shared_ptr<Mock_Performance_Batch> latest;
bool delivery_posted{};
};
std::shared_ptr<Mock_Performance_Batch> make_performance_batch(const Mock_Config& config) {
auto batch = std::make_shared<Mock_Performance_Batch>();
batch->frames.reserve(static_cast<std::size_t>(config.batch_count));
for (int i = 0; i < config.batch_count; ++i) {
auto spectrum = make_random_data({40, 120}, 1024, renderive::Memory_Domain::Spectrum);
double middle_power = spectrum.empty() ? 80.0 : spectrum[spectrum.size() / 2];
auto waterfall_time = current_render_time();
auto waterfall = make_random_data({0, 160}, 1024, renderive::Memory_Domain::Waterfall);
auto trace_time = current_render_time();
double trace_value = make_trace_value({-6000, 6000}, config.trace_mode);
auto audio = make_random_data({-6000, 6000}, 512, renderive::Memory_Domain::Spectrum);
batch->frames.push_back(Mock_Performance_Frame{
std::move(spectrum),
middle_power,
waterfall_time,
std::move(waterfall),
trace_time,
trace_value,
std::move(audio),
make_random_value({1, 5})
});
}
return batch;
}
void apply_performance_batch(Radio_Widget* rw, Mock_Performance_Batch& batch) {
if (!rw || !rw->spectrogram_line || !rw->water_fall || !rw->time_power || !rw->audio_spectrogram_plot || !rw->progress_bar1 || !rw->progress_bar2)
return;
for (auto& frame : batch.frames) {
rw->spectrogram_line->update_samples(std::move(frame.spectrum));
rw->progress_bar1->setValue(frame.spectrum_middle_power);
rw->water_fall->append_row(frame.waterfall_time, std::move(frame.waterfall));
rw->time_power->append_sample(frame.trace_time, frame.trace_value);
rw->audio_spectrogram_plot->update_samples(std::move(frame.audio));
rw->progress_bar2->setValue(frame.progress_value);
}
}
}
void Back_End::play(const QString& fileName) {
qDebug() << "播放音频文件:" << fileName;
}
QString Back_End::get_screenshot_save_dir_path() {
return "D:/work/电台保存文件";
}
QString Back_End::get_screenshot_save_file_name() {
QDateTime currentDateTime = QDateTime::currentDateTime();
return "截屏_" +
currentDateTime.toString()
.replace(":", "_")
.replace("/", "_") + ".png";
}
QString Back_End::get_audio_file_location() {
return "D:/work/save/";
}
Back_End* Back_End::instance() {
static Back_End temp;
return &temp;
}
bool Back_End::match(QKeyEvent* e, const QString& function) {
bool ok = function_map.contains(function);
if (!ok) {
qDebug() << "error Global::match 失败! 功能" << function << "不存在!" << e;
}
return e->key() == function_map[function];
}
void Back_End::YsSetFreq(quint64 value) {
qDebug() << "后端设置频率为: " << value;
}
// 获取到频率
void Back_End::YsReceiveFreq(quint64 freq) {
qDebug() << "接收到频率:" << freq;
}
// 音量设置
void Back_End::YsSetAfGain(int value) {}
int Back_End::YsGetAfGain() {
return 60;
}
int Back_End::YsGetRfGain() {
return 60;
}
qint64 Back_End::YsGetFreq() {
return 70;
}
// 增益设置
void Back_End::YsSetRfGain(int value) {}
Back_End::Back_End() {
//qDebug() << "Back_End::Back_End() " << QThread::current_thread_id();
function_map["主菜单"] = Qt::Key_F1;
function_map["属性菜单"] = Qt::Key_F2;
function_map["顶部菜单"] = Qt::Key_F3;
function_map["退出"] = Qt::Key_Escape;
function_map["确定"] = Qt::Key_Return;
function_map["左移"] = Qt::Key_Left;
function_map["右移"] = Qt::Key_Right;
function_map["增加"] = Qt::Key_Up;
function_map["减小"] = Qt::Key_Down;
function_map["切换音量/RF Gain"] = Qt::Key_Return;
function_map["特别增加"] = Qt::Key_PageUp;
function_map["特别减小"] = Qt::Key_PageDown;
Mock_Config config = read_mock_config();
qInfo() << "Radio mock perf:" << config.performance
<< "interval_ms:" << config.interval_ms
<< "batch_count:" << config.batch_count
<< "trace_mode:" << trace_mode_text(config.trace_mode);
std::uint64_t start_generation = mock_generation;
QTimer::singleShot(0, this, [this, config, start_generation]() {
if (start_generation != mock_generation)
return;
QPointer<Radio_Widget> rw = Main_Widget::instance()->radio_widget;
if (!rw)
return;
if (!config.performance) {
mock_timer = new QTimer(this);
mock_timer->setTimerType(Qt::PreciseTimer);
QObject::connect(mock_timer, &QTimer::timeout, this, [config, rw]() {
for (int i = 0; i < config.batch_count; ++i) {
if (!rw)
return;
push_radio_data(rw.data(), config.trace_mode);
}
});
mock_timer->start(config.interval_ms);
return;
}
auto delivery_state = std::make_shared<Mock_Delivery_State>();
mock_thread = new QThread(this);
mock_timer = new QTimer;
mock_timer->setTimerType(Qt::PreciseTimer);
mock_timer->setInterval(config.interval_ms);
mock_timer->moveToThread(mock_thread);
QTimer* timer = mock_timer;
QObject::connect(mock_thread, &QThread::started, timer, [timer]() {
timer->start();
});
QObject::connect(timer, &QTimer::timeout, timer, [this, config, rw, delivery_state, start_generation]() {
auto batch = make_performance_batch(config);
bool post_delivery = false;
{
std::lock_guard<std::mutex> lock(delivery_state->mutex);
delivery_state->latest = std::move(batch);
if (!delivery_state->delivery_posted) {
delivery_state->delivery_posted = true;
post_delivery = true;
}
}
if (!post_delivery)
return;
QMetaObject::invokeMethod(this, [this, rw, delivery_state, start_generation]() {
std::shared_ptr<Mock_Performance_Batch> latest;
{
std::lock_guard<std::mutex> lock(delivery_state->mutex);
latest = std::move(delivery_state->latest);
delivery_state->delivery_posted = false;
}
if (start_generation != mock_generation || !rw || !latest)
return;
apply_performance_batch(rw.data(), *latest);
}, Qt::QueuedConnection);
});
mock_thread->start();
});
}
#endif