Finished soapy module + added file source + added RTL_TCP source (windows only rn)

This commit is contained in:
Ryzerth 2020-10-04 02:56:02 +02:00
parent 47b04ffef4
commit 60342de9c0
21 changed files with 602 additions and 27 deletions

View File

@ -1,11 +1,13 @@
cmake_minimum_required(VERSION 3.13)
project(sdrpp_core)
# Cross platform modules
add_subdirectory("core")
add_subdirectory("radio")
add_subdirectory("recorder")
add_subdirectory("soapy")
add_subdirectory("file_source")
add_subdirectory("rtl_tcp_source")
add_subdirectory("demo")
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")

View File

@ -186,6 +186,8 @@ int sdrpp_main() {
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
//ImGui::ShowDemoWindow();
if (_maximized != maximized) {
_maximized = maximized;
core::configManager.aquire();

View File

@ -48,7 +48,8 @@ namespace dsp {
}
void setFrequency(float frequency) {
_phasorSpeed = (2 * 3.1415926535) / (_sampleRate / frequency);
_frequency = frequency;
_phasorSpeed = (2 * 3.1415926535 * frequency) / _sampleRate;
}
void setBlockSize(int blockSize) {

View File

@ -123,6 +123,8 @@ void windowInit() {
fftHeight = core::configManager.conf["fftHeight"];
gui::waterfall.setFFTHeight(fftHeight);
gui::menu.order = core::configManager.conf["menuOrder"].get<std::vector<std::string>>();
core::configManager.release();
}
@ -411,8 +413,7 @@ void drawWindow() {
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - (ImGui::CalcTextSize("Zoom").x / 2.0f));
ImGui::Text("Zoom");
ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0f) - 10);
// TODO: use global sample rate value from DSP instead of 8000000
ImGui::VSliderFloat("##_7_", ImVec2(20.0f, 150.0f), &bw.val, 8000000, 1000.0f, "");
ImGui::VSliderFloat("##_7_", ImVec2(20.0f, 150.0f), &bw.val, gui::waterfall.getBandwidth(), 1000.0f, "");
ImGui::NewLine();

View File

@ -17,7 +17,6 @@
#include <gui/frequency_select.h>
#include <fftw3.h>
#include <signal_path/dsp.h>
#include <io/soapy.h>
#include <gui/icons.h>
#include <gui/bandplan.h>
#include <watcher.h>

View File

@ -27,6 +27,7 @@ void Menu::draw() {
item = items[name];
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
item.drawHandler(item.ctx);
ImGui::Spacing();
}
}
}

View File

@ -134,6 +134,5 @@ namespace audiomenu {
}
ImGui::Spacing();
}
ImGui::Spacing();
}
};

View File

@ -47,6 +47,5 @@ namespace bandplanmenu {
bandplan::BandPlan_t plan = bandplan::bandplans[bandplan::bandplanNames[bandplanId]];
ImGui::Text("Country: %s (%s)", plan.countryName.c_str(), plan.countryCode.c_str());
ImGui::Text("Author: %s", plan.authorName.c_str());
ImGui::Spacing();
}
};

View File

@ -18,6 +18,5 @@ namespace displaymenu {
core::configManager.conf["showWaterfall"] = showWaterfall;
core::configManager.release(true);
}
ImGui::Spacing();
}
}

View File

@ -2,7 +2,6 @@
#include <signal_path/dsp.h>
#include <signal_path/vfo_manager.h>
#include <signal_path/source.h>
#include <io/soapy.h>
#include <module.h>
namespace sigpath {

View File

@ -7,6 +7,8 @@ else()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
endif (MSVC)
include_directories("src/")
file(GLOB SRC "src/*.cpp")
add_library(file_source SHARED ${SRC})

View File

@ -3,6 +3,8 @@
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <wavreader.h>
#include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -29,6 +31,11 @@ public:
handler.tuneHandler = tune;
handler.stream = &stream;
sigpath::sourceManager.registerSource("File", &handler);
reader = new WavReader("D:/satpic/raw_recordings/NOAA-18_09-08-2018_21-39-00_baseband_NR.wav");
spdlog::info("Samplerate: {0}, Bit depth: {1}, Channel count: {2}", reader->getSampleRate(), reader->getBitDepth(), reader->getChannelCount());
spdlog::info("FileSourceModule '{0}': Instance created!", name);
}
@ -39,6 +46,7 @@ public:
private:
static void menuSelected(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx;
core::setInputSampleRate(_this->reader->getSampleRate());
spdlog::info("FileSourceModule '{0}': Menu Select!", _this->name);
}
@ -49,11 +57,15 @@ private:
static void start(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx;
_this->workerThread = std::thread(worker, _this);
spdlog::info("FileSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx;
_this->stream.stopWriter();
_this->workerThread.join();
_this->stream.clearWriteStop();
spdlog::info("FileSourceModule '{0}': Stop!", _this->name);
}
@ -62,15 +74,39 @@ private:
spdlog::info("FileSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx;
ImGui::Text("Hi from %s!", _this->name.c_str());
}
static void worker(void* ctx) {
FileSourceModule* _this = (FileSourceModule*)ctx;
double sampleRate = _this->reader->getSampleRate();
int blockSize = sampleRate / 200.0;
int16_t* inBuf = new int16_t[blockSize * 2];
dsp::complex_t* outBuf = new dsp::complex_t[blockSize];
_this->stream.setMaxLatency(blockSize * 2);
while (true) {
_this->reader->readSamples(inBuf, blockSize * 2 * sizeof(int16_t));
for (int i = 0; i < blockSize; i++) {
outBuf[i].q = (float)inBuf[i * 2] / (float)0x7FFF;
outBuf[i].i = (float)inBuf[(i * 2) + 1] / (float)0x7FFF;
}
if (_this->stream.write(outBuf, blockSize) < 0) { break; };
//std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
delete[] inBuf;
delete[] outBuf;
}
std::string name;
dsp::stream<dsp::complex_t> stream;
SourceManager::SourceHandler handler;
WavReader* reader;
std::thread workerThread;
};
MOD_EXPORT void _INIT_() {

View File

@ -0,0 +1,62 @@
#pragma once
#pragma once
#include <stdint.h>
#include <fstream>
#define WAV_SIGNATURE "RIFF"
#define WAV_TYPE "WAVE"
#define WAV_FORMAT_MARK "fmt "
#define WAV_DATA_MARK "data"
#define WAV_SAMPLE_TYPE_PCM 1
class WavReader {
public:
WavReader(std::string path) {
file = std::ifstream(path.c_str(), std::ios::binary);
file.read((char*)&hdr, sizeof(WavHeader_t));
}
uint16_t getBitDepth() {
return hdr.bitDepth;
}
uint16_t getChannelCount() {
return hdr.channelCount;
}
uint32_t getSampleRate() {
return hdr.sampleRate;
}
void readSamples(void* data, size_t size) {
file.read((char*)data, size);
bytesRead += size;
}
void close() {
file.close();
}
private:
struct WavHeader_t {
char signature[4]; // "RIFF"
uint32_t fileSize; // data bytes + sizeof(WavHeader_t) - 8
char fileType[4]; // "WAVE"
char formatMarker[4]; // "fmt "
uint32_t formatHeaderLength; // Always 16
uint16_t sampleType; // PCM (1)
uint16_t channelCount;
uint32_t sampleRate;
uint32_t bytesPerSecond;
uint16_t bytesPerSample;
uint16_t bitDepth;
char dataMarker[4]; // "data"
uint32_t dataSize;
};
std::ifstream file;
size_t bytesRead = 0;
WavHeader_t hdr;
};

View File

@ -29,5 +29,6 @@
"windowSize": {
"h": 720,
"w": 1280
}
},
"menuOrder": ["Source", "Radio", "Recorder", "Audio", "Band Plan", "Display"]
}

View File

@ -14,5 +14,6 @@
"windowSize": {
"h": 720,
"w": 1280
}
},
"menuOrder": ["Source", "Radio", "Recorder", "Audio", "Band Plan", "Display"]
}

View File

@ -2,5 +2,6 @@
"Radio": "./radio/Release/radio.dll",
"Recorder": "./recorder/Release/recorder.dll",
"Soapy": "./soapy/Release/soapy.dll",
"FileSource": "./file_source/Release/file_source.dll"
"FileSource": "./file_source/Release/file_source.dll",
"RTLTCPSource": "./rtl_tcp_source/Release/rtl_tcp_source.dll"
}

View File

@ -0,0 +1,19 @@
{
"device": "Generic RTL2832U OEM :: 00000001",
"devices": {
"Generic RTL2832U OEM :: 00000001": {
"gains": {
"TUNER": 12.817999839782715
},
"sampleRate": 2560000.0
},
"HackRF One #0 901868dc282c8f8b": {
"gains": {
"AMP": 0.0,
"LNA": 24.711999893188477,
"VGA": 15.906000137329102
},
"sampleRate": 8000000.0
}
}
}

View File

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.13)
project(rtl_tcp_source)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17")
else()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
endif (MSVC)
include_directories("src/")
file(GLOB SRC "src/*.cpp")
add_library(rtl_tcp_source SHARED ${SRC})
target_link_libraries(rtl_tcp_source PRIVATE sdrpp_core)
set_target_properties(rtl_tcp_source PROPERTIES PREFIX "")
if(WIN32)
target_link_libraries(rtl_tcp_source PRIVATE wsock32 ws2_32)
endif()

188
rtl_tcp_source/src/main.cpp Normal file
View File

@ -0,0 +1,188 @@
#include <rtltcp_client.h>
#include <imgui.h>
#include <spdlog/spdlog.h>
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <gui/style.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
MOD_INFO {
/* Name: */ "fike_source",
/* Description: */ "File input module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ "0.1.0"
};
class RTLTCPSourceModule {
public:
RTLTCPSourceModule(std::string name) {
this->name = name;
stream.init(100);
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("RTL-TCP", &handler);
spdlog::info("RTLTCPSourceModule '{0}': Instance created!", name);
}
~RTLTCPSourceModule() {
spdlog::info("RTLTCPSourceModule '{0}': Instance deleted!", name);
}
private:
static void menuSelected(void* ctx) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
spdlog::info("RTLTCPSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
spdlog::info("RTLTCPSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
if (!_this->client.connectToRTL(_this->ip, _this->port)) {
spdlog::error("Could not connect to {0}:{1}", _this->ip, _this->port);
return;
}
_this->client.setFrequency(_this->freq);
_this->client.setSampleRate(_this->sampleRate);
_this->client.setGainIndex(_this->gain);
_this->client.setGainMode(!_this->tunerAGC);
_this->client.setDirectSampling(_this->directSamplingMode);
_this->client.setAGCMode(_this->rtlAGC);
_this->running = true;
_this->workerThread = std::thread(worker, _this);
spdlog::info("RTLTCPSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
_this->running = false;
_this->stream.stopWriter();
_this->workerThread.join();
_this->stream.clearWriteStop();
_this->client.disconnect();
spdlog::info("RTLTCPSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
if (_this->running) {
_this->client.setFrequency(freq);
}
_this->freq = freq;
spdlog::info("RTLTCPSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)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) {
RTLTCPSourceModule* _this = (RTLTCPSourceModule*)ctx;
int blockSize = _this->sampleRate / 200.0;
uint8_t* inBuf = new uint8_t[blockSize * 2];
dsp::complex_t* outBuf = new dsp::complex_t[blockSize];
_this->stream.setMaxLatency(blockSize * 2);
while (true) {
// Read samples here
_this->client.receiveData(inBuf, blockSize * 2);
for (int i = 0; i < blockSize; i++) {
outBuf[i].q = ((double)inBuf[i * 2] - 128.0) / 128.0;
outBuf[i].i = ((double)inBuf[(i * 2) + 1] - 128.0) / 128.0;
}
if (_this->stream.write(outBuf, blockSize) < 0) { break; };
}
delete[] inBuf;
delete[] outBuf;
}
std::string name;
dsp::stream<dsp::complex_t> 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 void* _CREATE_INSTANCE_(std::string name) {
return new RTLTCPSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (RTLTCPSourceModule*)instance;
}
MOD_EXPORT void _STOP_() {
// Do your one shutdown here
}

View File

@ -0,0 +1,120 @@
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#ifdef _WIN32
#define __attribute__(x)
#pragma pack(push, 1)
#endif
struct command_t{
unsigned char cmd;
unsigned int param;
}__attribute__((packed));
#ifdef _WIN32
#pragma pack(pop)
#endif
class RTLTCPClient {
public:
RTLTCPClient() {
}
bool connectToRTL(char* host, uint16_t port) {
struct addrinfo *result = NULL;
struct addrinfo *ptr = NULL;
struct addrinfo hints;
ZeroMemory( &hints, sizeof(hints) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
char buf[128];
sprintf(buf, "%hu", port);
int iResult = getaddrinfo(host, buf, &hints, &result);
if (iResult != 0) {
// TODO: log error
WSACleanup();
return false;
}
ptr = result;
sock = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (sock == INVALID_SOCKET) {
// TODO: log error
freeaddrinfo(result);
WSACleanup();
return false;
}
iResult = connect(sock, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(sock);
freeaddrinfo(result);
WSACleanup();
return false;
}
freeaddrinfo(result);
return true;
}
void disconnect() {
closesocket(sock);
WSACleanup();
}
// struct command_t {
// uint8_t cmd;
// uint32_t arg;
// };
void sendCommand(uint8_t command, uint32_t param) {
command_t cmd;
cmd.cmd = command;
cmd.param = htonl(param);
send(sock, (char*)&cmd, sizeof(command_t), 0);
}
void receiveData(uint8_t* buf, size_t count) {
recv(sock, (char*)buf, count, 0);
}
void setFrequency(double freq) {
sendCommand(1, freq);
}
void setSampleRate(double sr) {
sendCommand(2, sr);
}
void setGainMode(int mode) {
sendCommand(3, mode);
}
void setGain(double gain) {
sendCommand(4, gain);
}
void setAGCMode(int mode) {
sendCommand(8, mode);
}
void setDirectSampling(int mode) {
sendCommand(9, mode);
}
void setGainIndex(int index) {
sendCommand(13, index);
}
private:
SOCKET sock;
};

View File

@ -7,6 +7,7 @@
#include <SoapySDR/Modules.hpp>
#include <SoapySDR/Logger.hpp>
#include <core.h>
#include <gui/style.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -17,6 +18,8 @@ MOD_INFO {
/* Version: */ "0.1.0"
};
ConfigManager config;
class SoapyModule {
public:
SoapyModule(std::string name) {
@ -24,15 +27,18 @@ public:
//TODO: Make module tune on source select change (in sdrpp_core)
devList = SoapySDR::Device::enumerate();
txtDevList = "";
for (auto& dev : devList) {
txtDevList += dev["label"];
txtDevList += '\0';
}
uiGains = new float[1];
refresh();
stream.init(100);
// Select default device
config.aquire();
std::string devName = config.conf["device"];
config.release();
selectDevice(devName);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
@ -42,10 +48,17 @@ public:
handler.tuneHandler = tune;
handler.stream = &stream;
sigpath::sourceManager.registerSource("SoapySDR", &handler);
spdlog::info("SoapyModule '{0}': Instance created!", name);
// Select default device
selectDevice(devList[0]["label"]);
spdlog::info("SoapyModule '{0}': Instance created!", name);
}
void refresh() {
devList = SoapySDR::Device::enumerate();
txtDevList = "";
for (auto& dev : devList) {
txtDevList += dev["label"];
txtDevList += '\0';
}
}
~SoapyModule() {
@ -54,6 +67,7 @@ public:
private:
void selectSampleRate(double samplerate) {
spdlog::info("Setting sample rate to {0}", samplerate);
if (sampleRates.size() == 0) {
devId = -1;
return;
@ -65,6 +79,7 @@ private:
srId = i;
sampleRate = sr;
found = true;
core::setInputSampleRate(sampleRate);
break;
}
i++;
@ -98,15 +113,57 @@ private:
}
SoapySDR::Device* dev = SoapySDR::Device::make(devArgs);
gainList = dev->listGains(SOAPY_SDR_RX, 0);
delete[] uiGains;
uiGains = new float[gainList.size()];
for (auto gain : gainList) {
gainRanges.push_back(dev->getGainRange(SOAPY_SDR_RX, 0, gain));
}
sampleRates = dev->listSampleRates(SOAPY_SDR_RX, 0);
txtSrList = "";
for (double sr : sampleRates) {
txtSrList += std::to_string((int)sr);
txtSrList += '\0';
}
SoapySDR::Device::unmake(dev);
selectSampleRate(sampleRates[0]);
SoapySDR::Device::unmake(dev);
config.aquire();
if (config.conf["devices"].contains(name)) {
int i = 0;
for (auto gain : gainList) {
if (config.conf["devices"][name]["gains"].contains(gain)) {
uiGains[i] = config.conf["devices"][name]["gains"][gain];
}
i++;
}
selectSampleRate(config.conf["devices"][name]["sampleRate"]);
}
else {
int i = 0;
for (auto gain : gainList) {
uiGains[i] = gainRanges[i].minimum();
i++;
}
selectSampleRate(sampleRates[0]); // Select default
}
config.release();
}
void saveCurrent() {
json conf;
conf["sampleRate"] = sampleRate;
int i = 0;
for (auto gain : gainList) {
conf["gains"][gain] = uiGains[i];
i++;
}
config.aquire();
config.conf["devices"][devArgs["label"]] = conf;
config.release(true);
}
static void menuSelected(void* ctx) {
@ -126,7 +183,17 @@ private:
static void start(void* ctx) {
SoapyModule* _this = (SoapyModule*)ctx;
_this->dev = SoapySDR::Device::make(_this->devArgs);
_this->dev->setSampleRate(SOAPY_SDR_RX, 0, _this->sampleRate);
int i = 0;
for (auto gain : _this->gainList) {
_this->dev->setGain(SOAPY_SDR_RX, 0, gain, _this->uiGains[i]);
i++;
}
_this->dev->setFrequency(SOAPY_SDR_RX, 0, _this->freq);
_this->devStream = _this->dev->setupStream(SOAPY_SDR_RX, "CF32");
_this->dev->activateStream(_this->devStream);
_this->running = true;
@ -137,6 +204,11 @@ private:
static void stop(void* ctx) {
SoapyModule* _this = (SoapyModule*)ctx;
_this->running = false;
_this->dev->deactivateStream(_this->devStream);
_this->dev->closeStream(_this->devStream);
_this->workerThread.join();
SoapySDR::Device::unmake(_this->dev);
spdlog::info("SoapyModule '{0}': Stop!", _this->name);
}
@ -159,14 +231,56 @@ private:
}
float menuWidth = ImGui::GetContentRegionAvailWidth();
if (_this->running) { style::beginDisabled(); }
ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(CONCAT("##_dev_select_", _this->name), &_this->devId, _this->txtDevList.c_str())) {
_this->selectDevice(_this->devList[_this->devId]["label"]);
config.aquire();
config.conf["device"] = _this->devList[_this->devId]["label"];
config.release(true);
}
ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(CONCAT("##_sr_select_", _this->name), &_this->srId, _this->txtSrList.c_str())) {
_this->selectSampleRate(_this->sampleRates[_this->srId]);
core::setInputSampleRate(_this->sampleRate);
_this->saveCurrent();
}
ImGui::SameLine();
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
if (ImGui::Button(CONCAT("Refresh##_dev_select_", _this->name), ImVec2(refreshBtnWdith, 0))) {
_this->refresh();
_this->selectDevice(config.conf["device"]);
}
if (_this->running) { style::endDisabled(); }
float gainNameLen = 0;
float len;
for (auto gain : _this->gainList) {
len = ImGui::CalcTextSize((gain + " gain").c_str()).x;
if (len > gainNameLen) {
gainNameLen = len;
}
}
gainNameLen += 5.0f;
int i = 0;
for (auto gain : _this->gainList) {
ImGui::Text((gain + " gain").c_str());
ImGui::SameLine();
ImGui::SetCursorPosX(gainNameLen);
ImGui::SetNextItemWidth(menuWidth - gainNameLen);
if (ImGui::SliderFloat((gain + std::string("##_gain_sel_") + _this->name).c_str(), &_this->uiGains[i],
_this->gainRanges[i].minimum(), _this->gainRanges[i].maximum())) {
if (_this->running) {
_this->dev->setGain(SOAPY_SDR_RX, 0, gain, _this->uiGains[i]);
}
_this->saveCurrent();
}
i++;
}
}
@ -203,10 +317,18 @@ private:
bool running = false;
std::vector<double> sampleRates;
int srId = -1;
float* uiGains;
std::vector<std::string> gainList;
std::vector<SoapySDR::Range> gainRanges;
};
MOD_EXPORT void _INIT_() {
// Do your one time init here
config.setPath(ROOT_DIR "/soapy_source_config.json");
json defConf;
defConf["device"] = "";
defConf["devices"] = json({});
config.load(defConf);
config.enableAutoSave();
}
MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) {
@ -218,5 +340,6 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
}
MOD_EXPORT void _STOP_() {
// Do your one shutdown here
config.disableAutoSave();
config.save();
}