From 46e9266752bde0330f9b0842aeb9e9b0a89d9e25 Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Sun, 13 Dec 2020 14:52:54 +0100 Subject: [PATCH] fixed soapy bug --- airspy_hf_source/CMakeLists.txt | 20 ++++ airspy_hf_source/src/main.cpp | 192 ++++++++++++++++++++++++++++++ core/src/gui/main_window.cpp | 35 ++++-- core/src/signal_path/dsp.cpp | 16 +++ core/src/signal_path/dsp.h | 4 +- root_dev/config.json | 22 ++-- root_dev/radio_config.json | 72 +++++++++++ root_dev/soapy_source_config.json | 2 +- soapy_source/src/main.cpp | 24 ---- 9 files changed, 342 insertions(+), 45 deletions(-) create mode 100644 airspy_hf_source/CMakeLists.txt create mode 100644 airspy_hf_source/src/main.cpp diff --git a/airspy_hf_source/CMakeLists.txt b/airspy_hf_source/CMakeLists.txt new file mode 100644 index 00000000..adf4c72e --- /dev/null +++ b/airspy_hf_source/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.13) +project(airspy_hf_source) + +if (MSVC) + set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc") +else() + set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive") +endif (MSVC) + +include_directories("src/") + +file(GLOB SRC "src/*.cpp") + +add_library(airspy_hf_source SHARED ${SRC}) +target_link_libraries(airspy_hf_source PRIVATE sdrpp_core) +set_target_properties(airspy_hf_source PROPERTIES PREFIX "") + +if(WIN32) + target_link_libraries(airspy_hf_source PRIVATE wsock32 ws2_32) +endif() \ No newline at end of file diff --git a/airspy_hf_source/src/main.cpp b/airspy_hf_source/src/main.cpp new file mode 100644 index 00000000..98c5bfbe --- /dev/null +++ b/airspy_hf_source/src/main.cpp @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +SDRPP_MOD_INFO { + /* Name: */ "airspy_hf_source", + /* Description: */ "Airspy HF source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 +}; + +class AirspyHFSourceModule : public ModuleManager::Instance { +public: + AirspyHFSourceModule(std::string name) { + this->name = name; + + sampleRate = 2560000.0; + + handler.ctx = this; + handler.selectHandler = menuSelected; + handler.deselectHandler = menuDeselected; + handler.menuHandler = menuHandler; + handler.startHandler = start; + handler.stopHandler = stop; + handler.tuneHandler = tune; + handler.stream = &stream; + sigpath::sourceManager.registerSource("Airspy HF+", &handler); + } + + ~AirspyHFSourceModule() { + + } + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + +private: + static void menuSelected(void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + core::setInputSampleRate(_this->sampleRate); + spdlog::info("AirspyHFSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + spdlog::info("AirspyHFSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + if (_this->running) { + return; + } + // DO START HERE + _this->running = true; + _this->workerThread = std::thread(worker, _this); + spdlog::info("AirspyHFSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + if (!_this->running) { + return; + } + // DO STOP HERE + _this->running = false; + _this->stream.stopWriter(); + _this->workerThread.join(); + _this->stream.clearWriteStop(); + spdlog::info("AirspyHFSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + if (_this->running) { + _this->client.setFrequency(freq); + } + _this->freq = freq; + spdlog::info("AirspyHFSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + static void menuHandler(void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + float menuWidth = ImGui::GetContentRegionAvailWidth(); + float portWidth = ImGui::CalcTextSize("00000").x + 20; + + ImGui::SetNextItemWidth(menuWidth - portWidth); + ImGui::InputText(CONCAT("##_ip_select_", _this->name), _this->ip, 1024); + ImGui::SameLine(); + ImGui::SetNextItemWidth(portWidth); + ImGui::InputInt(CONCAT("##_port_select_", _this->name), &_this->port, 0); + + ImGui::SetNextItemWidth(ImGui::CalcTextSize("OOOOOOOOOO").x); + if (ImGui::Combo("Direct sampling", &_this->directSamplingMode, "Disabled\0I branch\0Q branch\0")) { + if (_this->running) { + _this->client.setDirectSampling(_this->directSamplingMode); + } + } + + if (ImGui::Checkbox("RTL AGC", &_this->rtlAGC)) { + if (_this->running) { + _this->client.setAGCMode(_this->rtlAGC); + } + } + + if (ImGui::Checkbox("Tuner AGC", &_this->tunerAGC)) { + if (_this->running) { + _this->client.setGainMode(!_this->tunerAGC); + if (!_this->tunerAGC) { + _this->client.setGainIndex(_this->gain); + } + } + } + + if (_this->tunerAGC) { style::beginDisabled(); } + ImGui::SetNextItemWidth(menuWidth); + if (ImGui::SliderInt(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 29, "")) { + if (_this->running) { + _this->client.setGainIndex(_this->gain); + } + } + if (_this->tunerAGC) { style::endDisabled(); } + } + + static void worker(void* ctx) { + AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx; + int blockSize = _this->sampleRate / 200.0; + uint8_t* inBuf = new uint8_t[blockSize * 2]; + + while (true) { + // Read samples here + _this->client.receiveData(inBuf, blockSize * 2); + if (_this->stream.aquire() < 0) { break; } + for (int i = 0; i < blockSize; i++) { + _this->stream.data[i].q = ((double)inBuf[i * 2] - 128.0) / 128.0; + _this->stream.data[i].i = ((double)inBuf[(i * 2) + 1] - 128.0) / 128.0; + } + _this->stream.write(blockSize); + } + + delete[] inBuf; + } + + std::string name; + bool enabled = true; + dsp::stream stream; + double sampleRate; + SourceManager::SourceHandler handler; + std::thread workerThread; + RTLTCPClient client; + bool running = false; + double freq; + char ip[1024] = "localhost"; + int port = 1234; + int gain = 0; + bool rtlAGC = false; + bool tunerAGC = false; + int directSamplingMode = 0; +}; + +MOD_EXPORT void _INIT_() { + // Do your one time init here +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new AirspyHFSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { + delete (AirspyHFSourceModule*)instance; +} + +MOD_EXPORT void _END_() { + // Do your one shutdown here +} \ No newline at end of file diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 1b4d66a8..e0e95de4 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -31,6 +31,27 @@ #include #include +// const int FFTSizes[] = { +// 65536, +// 32768, +// 16384, +// 8192, +// 4096, +// 2048 +// }; + +// const char* FFTSizesStr[] = { +// "65536", +// "32768", +// "16384", +// "8192", +// "4096", +// "2048" +// }; + +// int fftSizeId = 0; +int fftSize = 8192 * 8; + std::thread worker; std::mutex fft_mtx; fftwf_complex *fft_in, *fft_out; @@ -39,18 +60,15 @@ float* tempFFT; float* FFTdata; char buf[1024]; -int fftSize = 8192 * 8; + void fftHandler(dsp::complex_t* samples, int count, void* ctx) { - if (count < fftSize) { - return; - } memcpy(fft_in, samples, count * sizeof(dsp::complex_t)); fftwf_execute(p); - int half = fftSize / 2; + int half = count / 2; - volk_32fc_s32f_power_spectrum_32f(tempFFT, (lv_32fc_t*)fft_out, fftSize, fftSize); - volk_32f_s32f_multiply_32f(FFTdata, tempFFT, 0.5f, fftSize); + volk_32fc_s32f_power_spectrum_32f(tempFFT, (lv_32fc_t*)fft_out, count, count); + volk_32f_s32f_multiply_32f(FFTdata, tempFFT, 0.5f, count); memcpy(tempFFT, &FFTdata[half], half * sizeof(float)); memmove(&FFTdata[half], FFTdata, half * sizeof(float)); @@ -62,7 +80,7 @@ void fftHandler(dsp::complex_t* samples, int count, void* ctx) { return; } float last = FFTdata[0]; - for (int i = 0; i < fftSize; i++) { + for (int i = 0; i < count; i++) { last = (FFTdata[i] * 0.1f) + (last * 0.9f); fftBuf[i] = last; } @@ -169,7 +187,6 @@ void windowInit() { // TODO for 0.2.5 // Add "select file" option for the file source - // Add default main config to avoid having to ship one // Have a good directory system on both linux and windows // Switch to double buffering (should fix occassional underruns) // Fix gain not updated on startup, soapysdr diff --git a/core/src/signal_path/dsp.cpp b/core/src/signal_path/dsp.cpp index aab8e25d..6a19515f 100644 --- a/core/src/signal_path/dsp.cpp +++ b/core/src/signal_path/dsp.cpp @@ -92,4 +92,20 @@ void SignalPath::bindIQStream(dsp::stream* stream) { void SignalPath::unbindIQStream(dsp::stream* stream) { split.unbindStream(stream); +} + +void SignalPath::setFFTSize(int size) { + fftSize = size; + int skip = (sampleRate / fftRate) - fftSize; + reshape.setSkip(skip); +} + +void SignalPath::startFFT() { + reshape.start(); + fftHandlerSink.start(); +} + +void SignalPath::stopFFT() { + reshape.stop(); + fftHandlerSink.stop(); } \ No newline at end of file diff --git a/core/src/signal_path/dsp.h b/core/src/signal_path/dsp.h index 128a7ea2..f9380132 100644 --- a/core/src/signal_path/dsp.h +++ b/core/src/signal_path/dsp.h @@ -11,13 +11,15 @@ public: void start(); void stop(); void setSampleRate(double sampleRate); - void setFFTRate(double rate); double getSampleRate(); dsp::VFO* addVFO(std::string name, double outSampleRate, double bandwidth, double offset); void removeVFO(std::string name); void setInput(dsp::stream* input); void bindIQStream(dsp::stream* stream); void unbindIQStream(dsp::stream* stream); + void setFFTSize(int size); + void startFFT(); + void stopFFT(); private: struct VFO_t { diff --git a/root_dev/config.json b/root_dev/config.json index d8b11f03..76cb30a1 100644 --- a/root_dev/config.json +++ b/root_dev/config.json @@ -5,10 +5,11 @@ "fftHeight": 300, "frequency": 99000000, "max": 0.0, - "maximized": false, + "maximized": true, "menuOrder": [ "Source", - "Radio", + "Radio 1", + "Radio 2", "Recorder", "Sinks", "Audio", @@ -22,17 +23,18 @@ "Audio Sink": "audio_sink", "PlutoSDR Source": "plutosdr_source", "RTL-TCP Source": "rtl_tcp_source", - "Radio": "radio", + "Radio 1": "radio", + "Radio 2": "radio", "Recorder": "recorder", "SoapySDR Source": "soapy_source" }, "modules": [ - "./radio/RelWithDebInfo/radio.dll", - "./recorder/RelWithDebInfo/recorder.dll", - "./soapy_source/RelWithDebInfo/soapy_source.dll", - "./rtl_tcp_source/RelWithDebInfo/rtl_tcp_source.dll", - "./audio_sink/RelWithDebInfo/audio_sink.dll", - "./plutosdr_source/RelWithDebInfo/plutosdr_source.dll" + "./radio/Release/radio.dll", + "./recorder/Release/recorder.dll", + "./soapy_source/Release/soapy_source.dll", + "./rtl_tcp_source/Release/rtl_tcp_source.dll", + "./audio_sink/Release/audio_sink.dll", + "./plutosdr_source/Release/plutosdr_source.dll" ], "offset": 0.0, "showWaterfall": true, @@ -46,7 +48,7 @@ "Radio 1": { "muted": false, "sink": "Audio", - "volume": 0.4292035400867462 + "volume": 0.6377550959587097 }, "Radio 2": { "muted": false, diff --git a/root_dev/radio_config.json b/root_dev/radio_config.json index d065ee78..1a979577 100644 --- a/root_dev/radio_config.json +++ b/root_dev/radio_config.json @@ -36,5 +36,77 @@ "squelchLevel": -100.0 }, "selectedDemodId": 1 + }, + "Radio 1": { + "AM": { + "bandwidth": 12500.0, + "snapInterval": 1000.0 + }, + "CW": { + "bandwidth": 200.0, + "snapInterval": 10.0 + }, + "DSB": { + "bandwidth": 6000.0, + "snapInterval": 100.0 + }, + "FM": { + "bandwidth": 12500.0, + "snapInterval": 10000.0 + }, + "LSB": { + "bandwidth": 3000.0, + "snapInterval": 100.0 + }, + "RAW": { + "snapInterval": 10000.0 + }, + "USB": { + "bandwidth": 3000.0, + "snapInterval": 100.0 + }, + "WFM": { + "bandwidth": 200000.0, + "deempMode": 0, + "snapInterval": 100000.0, + "squelchLevel": -100.0 + }, + "selectedDemodId": 1 + }, + "Radio 2": { + "AM": { + "bandwidth": 12500.0, + "snapInterval": 1000.0 + }, + "CW": { + "bandwidth": 200.0, + "snapInterval": 10.0 + }, + "DSB": { + "bandwidth": 6000.0, + "snapInterval": 100.0 + }, + "FM": { + "bandwidth": 12500.0, + "snapInterval": 10000.0 + }, + "LSB": { + "bandwidth": 3000.0, + "snapInterval": 100.0 + }, + "RAW": { + "snapInterval": 10000.0 + }, + "USB": { + "bandwidth": 3000.0, + "snapInterval": 100.0 + }, + "WFM": { + "bandwidth": 200000.0, + "deempMode": 0, + "snapInterval": 100000.0, + "squelchLevel": -100.0 + }, + "selectedDemodId": 1 } } \ No newline at end of file diff --git a/root_dev/soapy_source_config.json b/root_dev/soapy_source_config.json index 6c9fbb52..6fd6e953 100644 --- a/root_dev/soapy_source_config.json +++ b/root_dev/soapy_source_config.json @@ -35,7 +35,7 @@ "LNA": 24.503000259399414, "VGA": 16.332000732421875 }, - "sampleRate": 2000000.0 + "sampleRate": 8000000.0 }, "Microphone (Realtek High Definition Audio)": { "sampleRate": 96000.0 diff --git a/soapy_source/src/main.cpp b/soapy_source/src/main.cpp index 171a0bdc..4ab5d84a 100644 --- a/soapy_source/src/main.cpp +++ b/soapy_source/src/main.cpp @@ -143,8 +143,6 @@ private: hasAgc = dev->hasGainMode(SOAPY_SDR_RX, channelId); - hasIQBalance = dev->hasIQBalanceMode(SOAPY_SDR_RX, channelId); - SoapySDR::Device::unmake(dev); config.aquire(); @@ -165,12 +163,6 @@ private: else { agc = false; } - if (hasIQBalance && config.conf["devices"][name].contains("iqBalance")) { - iqBalance = config.conf["devices"][name]["iqBalance"]; - } - else { - iqBalance = false; - } if (config.conf["devices"][name].contains("sampleRate")) { selectSampleRate(config.conf["devices"][name]["sampleRate"]); } @@ -204,9 +196,6 @@ private: if (hasAgc) { conf["agc"] = agc; } - if (hasIQBalance) { - conf["iqBalance"] = iqBalance; - } config.aquire(); config.conf["devices"][devArgs["label"]] = conf; config.release(true); @@ -242,10 +231,6 @@ private: _this->dev->setGainMode(SOAPY_SDR_RX, _this->channelId, _this->agc); } - if (_this->hasIQBalance) { - _this->dev->setIQBalanceMode(SOAPY_SDR_RX, _this->channelId, _this->iqBalance); - } - _this->dev->setFrequency(SOAPY_SDR_RX, _this->channelId, _this->freq); _this->devStream = _this->dev->setupStream(SOAPY_SDR_RX, "CF32"); @@ -337,13 +322,6 @@ private: } } - if (_this->hasIQBalance) { - if (ImGui::Checkbox((std::string("AGC##_iq_bal_sel_") + _this->name).c_str(), &_this->iqBalance)) { - if (_this->running) { _this->dev->setIQBalanceMode(SOAPY_SDR_RX, _this->channelId, _this->iqBalance); } - _this->saveCurrent(); - } - } - int i = 0; for (auto gain : _this->gainList) { ImGui::Text("%s gain", gain.c_str()); @@ -393,8 +371,6 @@ private: bool running = false; bool hasAgc = false; bool agc = false; - bool hasIQBalance = false; - bool iqBalance = false; std::vector sampleRates; int srId = -1; float* uiGains;