352 lines
11 KiB
C++
352 lines
11 KiB
C++
#ifndef MockBackEnd
|
|
|
|
#include "BackEnd.h"
|
|
#include "./MainWidget.h"
|
|
#include "../component/FrequentShower.h"
|
|
#include "../component/ProgressBar.h"
|
|
#include "IntermediateFrequencyWidget.h"
|
|
#include "RadioWidget.h"
|
|
#include "SweepFrequencyWidget.h"
|
|
#include "am_fm_cw_modem.hpp"
|
|
#include "device_uhd.h"
|
|
|
|
#include <sndfile.h> // 用于音频文件写入
|
|
#include <portaudio.h> // 用于音频播放
|
|
#include <QAudioFormat>
|
|
#include <QAudioOutput>
|
|
#include <alsa/asoundlib.h>
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#define BUFFER_SIZE 512 // 缓冲区大小
|
|
Device_UHD* ys_radio;
|
|
|
|
AM_Demod am_demod;
|
|
snd_pcm_t *handle;
|
|
int err;
|
|
|
|
namespace {
|
|
double clamp_to_axis_range(double value, renderive::Range range) {
|
|
const double lower = std::min(range.origin, range.target);
|
|
const double upper = std::max(range.origin, range.target);
|
|
return std::clamp(value, lower, upper);
|
|
}
|
|
}
|
|
|
|
void Back_End::play(const QString& fileName){
|
|
// ys_radio->YsSetFreq(value);
|
|
}
|
|
|
|
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)
|
|
{
|
|
ys_radio->setDevParam("RX0",UHD_ParamType::CenterFreq,value);
|
|
ys_radio->setDevZDParam("RX0",0,value,150000,300000);
|
|
}
|
|
qint64 Back_End::YsGetFreq() {
|
|
return (qint64)ys_radio->getDevParam("RX0",UHD_ParamType::CenterFreq);
|
|
}
|
|
|
|
// 获取到频率
|
|
void Back_End::YsReceiveFreq(quint64 freq) {
|
|
qDebug() << "接收到频率:" << freq;
|
|
Main_Widget::instance()->radio_widget->select_value_widget->set_frequent(freq);
|
|
}
|
|
|
|
// 音量设置
|
|
void Back_End::YsSetAfGain(int value) {
|
|
std::string command = "amixer sset Master " + std::to_string(value) + "%";
|
|
int result = system(command.c_str());
|
|
if (result != 0) {
|
|
std::cerr << "Failed to set volume: " << result << std::endl;
|
|
}
|
|
}
|
|
|
|
int Back_End::YsGetAfGain() {
|
|
FILE *pipe = popen("amixer sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }'", "r");
|
|
if (!pipe) {
|
|
std::cerr << "Failed to run command" << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
char buffer[128];
|
|
std::string result = "";
|
|
while (!feof(pipe)) {
|
|
if (fgets(buffer, 128, pipe) != NULL)
|
|
result += buffer;
|
|
}
|
|
|
|
pclose(pipe);
|
|
|
|
// Remove newline character if present
|
|
result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
|
|
|
|
// Convert string to integer
|
|
int volume = std::stoi(result);
|
|
return volume;
|
|
}
|
|
|
|
int Back_End::YsGetRfGain() {
|
|
return 60;
|
|
}
|
|
|
|
// 增益设置
|
|
void Back_End::YsSetRfGain(int value)
|
|
{
|
|
ys_radio->setDevParam("RX0",UHD_ParamType::Gain,value);
|
|
qDebug()<<"增益设置"<<value;
|
|
}
|
|
|
|
void Back_End::call_back_data_func(int channel, void *pData, int i_length)
|
|
{
|
|
UHD_Struct_KFFT *pUHDData = (UHD_Struct_KFFT *)pData;
|
|
// qDebug()<<"收到数据包大小"<<pUHDData->DataLen;
|
|
//绘图用
|
|
if(pUHDData->DataType == UHD_Enum_DataType::kd_fft ) {
|
|
std::vector<double> data;
|
|
data.clear();
|
|
for(int i = 0; i < 1024; i++)
|
|
{
|
|
unsigned short ts = *((unsigned short *)pUHDData+32+i);
|
|
//qDebug()<<(double) (ts/256);
|
|
data.push_back((double) (ts/256));
|
|
// if(i==512)
|
|
// {qDebug()<<ts;}
|
|
}
|
|
|
|
// qDebug()<< "data.size ==" << data.size() << "data == "<< data;
|
|
auto mw = Main_Widget::instance();
|
|
auto rw = mw->radio_widget;
|
|
auto iw = mw->intermediate_frequency_widget;
|
|
rw->water_fall->give_data(QTime::currentTime(), data); // 中频瀑布图
|
|
rw->spectrogram_line->give_data(data); // 中频频谱图
|
|
|
|
iw->afterglow->give_data(data); // 余晖图
|
|
iw->spectrogram_line->give_data(data); // 瀑布图
|
|
|
|
|
|
|
|
// auto sl = Main_Widget::instance()->sweep_frequency_widget->sweep_frequency_line;
|
|
// int n = sl->block_frequency_point_size();
|
|
// sl->give_data(data);
|
|
|
|
|
|
}
|
|
|
|
if(pUHDData->DataType == UHD_Enum_DataType::zd_ddc)
|
|
{
|
|
// qDebug()<<"收到zd_ddc数据包大小"<<pUHDData->DataLen;
|
|
|
|
short rbuf[512];
|
|
std::complex<short>* cbuf_ptr = reinterpret_cast<std::complex<short>*>((char*)pData + 64);
|
|
am_demod.step(cbuf_ptr, rbuf, 512); // 解调数据
|
|
|
|
//Main_Widget::instance()->radio_widget->giv
|
|
|
|
auto main = Main_Widget::instance();
|
|
auto radio = main->radio_widget;
|
|
|
|
if (radio && radio->audio_spectrogram_plot) {
|
|
std::vector<double> data(512);
|
|
auto axis = radio->audio_spectrogram_plot->power_axis();
|
|
const auto range = axis ? axis->coord_range() : renderive::Range{-32768.0, 32767.0};
|
|
for(int i = 0; i < 512; ++i){
|
|
data[i] = clamp_to_axis_range(rbuf[i], range);
|
|
}
|
|
//qDebug() << data;
|
|
radio->audio_spectrogram_plot->give_data(data);
|
|
}
|
|
|
|
//std::cout << "audio == " << rbuf[256] << std::endl;
|
|
|
|
if (radio && radio->time_power) {
|
|
auto axis = radio->time_power->value_axis();
|
|
const auto range = axis ? axis->coord_range() : renderive::Range{-32768.0, 32767.0};
|
|
radio->time_power->give_data(QTime::currentTime(), clamp_to_axis_range(rbuf[256], range));
|
|
}
|
|
|
|
|
|
// 保存音频流
|
|
// ofstream fp_dst;
|
|
// fp_dst.open("FM_DeMod.dat", ios::out | ios::binary | ios::app);
|
|
// fp_dst.write((char *)rbuf, 512 * 2ll);
|
|
// fp_dst.close();
|
|
|
|
|
|
// snd_pcm_writei 是ALSA库提供的函数,用于将音频数据写入PCM设备。
|
|
// handle:指向已打开的PCM设备句柄。
|
|
// buffer:指向包含音频数据的缓冲区。
|
|
// BUFFER_SIZE:要写入的数据帧数。
|
|
// err:存储 snd_pcm_writei 函数的返回值。
|
|
// == -EPIPE:检查返回值是否为 -EPIPE,这表示发生了XRUN(欠载)错误。XRUN发生在音频缓冲区为空时,即音频设备没有足够的数据进行播放。
|
|
|
|
// 播放数据
|
|
if ((err = snd_pcm_writei(handle, rbuf, BUFFER_SIZE)) == -EPIPE) {
|
|
fprintf(stderr, "XRUN occurred\n");
|
|
// 重新准备PCM设备
|
|
if ((err = snd_pcm_prepare(handle)) < 0) {
|
|
fprintf(stderr, "无法重新准备PCM设备: %s\n", snd_strerror(err));
|
|
}
|
|
} else if (err < 0) {
|
|
fprintf(stderr, "无法写入音频数据: %s\n", snd_strerror(err));
|
|
}
|
|
|
|
// // 关闭音频设备
|
|
// snd_pcm_close(handle);
|
|
|
|
}
|
|
}
|
|
|
|
Back_End::Back_End() {
|
|
|
|
function_map["主菜单"] = Qt::Key_F9;
|
|
function_map["属性菜单"] = Qt::Key_F11;
|
|
function_map["顶部菜单"] = Qt::Key_F10;
|
|
function_map["退出"] = Qt::Key_Escape;
|
|
function_map["确定"] = Qt::Key_F2;
|
|
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_F1;
|
|
function_map["特别增加"] = Qt::Key_PageUp;
|
|
function_map["特别减小"] = Qt::Key_PageDown;
|
|
|
|
ys_radio = new Device_UHD();
|
|
uhd::device_addrs_t tmp_add = ys_radio->findDevices();
|
|
while(ys_radio->openDevice(tmp_add[0]) != true)
|
|
{
|
|
qDebug()<<"9002连接失败,重试中...";
|
|
sleep(1);
|
|
ys_radio->openDevice(tmp_add[0]);
|
|
}
|
|
qDebug()<<"9002设备连接成功";
|
|
|
|
quint64 value = 10125000;
|
|
ys_radio->setDevParam("RX0",UHD_ParamType::CenterFreq,value); // 宽带设置
|
|
ys_radio->setDevParam("RX0",UHD_ParamType::BandWidth,100000); // 改带宽,频谱带宽不变
|
|
ys_radio->setDevParam("RX0",UHD_ParamType::SampleFreq,480000); // 改采样率,频谱带宽变化
|
|
ys_radio->setDevParam("RX0",UHD_ParamType::Gain,33);
|
|
// ys_radio->setDevDataSwitch("RX0",KDDCSwitch,0,true);
|
|
|
|
ys_radio->setDevFFTParam(UHD_FFTWin::blkmc,_1024,10); // FFT设置
|
|
ys_radio->setDevDataSwitch("RX0",KFFTSwitch,0,true);
|
|
|
|
ys_radio->setDevZDParam("RX0",0,value,15000,30000); // 窄带设置
|
|
ys_radio->setDevDataSwitch("RX0",ZDDCSwitch,0, true);
|
|
ys_radio->startDevRecv(0);
|
|
|
|
|
|
//ys_radio->stopDevRecv(0);
|
|
|
|
ys_radio->callBackData = std::bind(&Back_End::call_back_data_func, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
|
|
|
// ys_radio->hide();
|
|
// connect(ys_radio, &YsRadio::YsGetFreq, this, &Back_End::YsReceiveFreq);
|
|
// connect(ys_radio,&YsRadio::YsReceiveSpectrumData,this, &Back_End::YsReceiveSpectrumData);
|
|
|
|
// 打开音频设备
|
|
if ((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
|
fprintf(stderr, "无法打开音频设备: %s\n", snd_strerror(err));
|
|
exit(1);
|
|
} else{
|
|
qDebug()<<"打开音频设备成功";
|
|
}
|
|
|
|
// 设置音频参数
|
|
snd_pcm_hw_params_t *params;
|
|
snd_pcm_hw_params_alloca(¶ms);
|
|
|
|
// 初始化硬件参数结构
|
|
if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
|
|
fprintf(stderr, "无法初始化硬件参数结构: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
}else{
|
|
qDebug()<<"初始化硬件参数结构成功";
|
|
}
|
|
|
|
// 设置访问类型
|
|
if ((err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
|
fprintf(stderr, "无法设置访问类型: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
}else{
|
|
qDebug()<<"设置访问类型";
|
|
}
|
|
|
|
// 设置样本格式
|
|
if ((err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)) < 0) {
|
|
fprintf(stderr, "无法设置样本格式: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
}else{
|
|
qDebug()<<"设置样本格式";
|
|
}
|
|
|
|
// 设置采样率
|
|
unsigned int rate = 30000;//44100
|
|
if ((err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0)) < 0) {
|
|
fprintf(stderr, "无法设置采样率: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
}else{
|
|
qDebug()<<"设置采样率";
|
|
}
|
|
|
|
// 设置声道数
|
|
if ((err = snd_pcm_hw_params_set_channels(handle, params, 1)) < 0) {
|
|
fprintf(stderr, "无法设置声道数: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
}else{
|
|
qDebug()<<"设置声道数";
|
|
}
|
|
|
|
// 应用硬件参数
|
|
if ((err = snd_pcm_hw_params(handle, params)) < 0) {
|
|
fprintf(stderr, "无法应用硬件参数: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
}else{
|
|
qDebug()<<"应用硬件参数";
|
|
}
|
|
// 准备播放
|
|
if ((err = snd_pcm_prepare(handle)) < 0) {
|
|
fprintf(stderr, "无法准备播放: %s\n", snd_strerror(err));
|
|
// goto close_device;
|
|
} else{
|
|
qDebug()<<"准备播放";
|
|
}
|
|
|
|
//close_device:
|
|
// // 关闭音频设备
|
|
// snd_pcm_close(handle);
|
|
}
|
|
|
|
|
|
|
|
QString Back_End::get_audio_file_location() {
|
|
return "./";
|
|
}
|
|
|
|
QString Back_End::get_screenshot_save_file_name() {
|
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
|
return "截屏_" +
|
|
currentDateTime.toString()
|
|
.replace(":", "_")
|
|
.replace("/", "_") + ".png";
|
|
}
|
|
|
|
QString Back_End::get_screenshot_save_dir_path() {
|
|
return "/media/sdcard1/";
|
|
}
|
|
|
|
|
|
#endif
|