mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-25 02:18:30 +01:00
Finished soapy module + added file source + added RTL_TCP source (windows only rn)
This commit is contained in:
parent
47b04ffef4
commit
60342de9c0
@ -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")
|
||||
|
@ -186,6 +186,8 @@ int sdrpp_main() {
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
//ImGui::ShowDemoWindow();
|
||||
|
||||
if (_maximized != maximized) {
|
||||
_maximized = maximized;
|
||||
core::configManager.aquire();
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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>
|
||||
|
@ -27,6 +27,7 @@ void Menu::draw() {
|
||||
item = items[name];
|
||||
if (ImGui::CollapsingHeader(name.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
item.drawHandler(item.ctx);
|
||||
ImGui::Spacing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,5 @@ namespace audiomenu {
|
||||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
};
|
@ -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();
|
||||
}
|
||||
};
|
@ -18,6 +18,5 @@ namespace displaymenu {
|
||||
core::configManager.conf["showWaterfall"] = showWaterfall;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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})
|
||||
|
@ -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_() {
|
||||
|
62
file_source/src/wavreader.h
Normal file
62
file_source/src/wavreader.h
Normal 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;
|
||||
};
|
@ -29,5 +29,6 @@
|
||||
"windowSize": {
|
||||
"h": 720,
|
||||
"w": 1280
|
||||
}
|
||||
},
|
||||
"menuOrder": ["Source", "Radio", "Recorder", "Audio", "Band Plan", "Display"]
|
||||
}
|
@ -14,5 +14,6 @@
|
||||
"windowSize": {
|
||||
"h": 720,
|
||||
"w": 1280
|
||||
}
|
||||
},
|
||||
"menuOrder": ["Source", "Radio", "Recorder", "Audio", "Band Plan", "Display"]
|
||||
}
|
@ -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"
|
||||
}
|
19
root_dev/soapy_source_config.json
Normal file
19
root_dev/soapy_source_config.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
20
rtl_tcp_source/CMakeLists.txt
Normal file
20
rtl_tcp_source/CMakeLists.txt
Normal 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
188
rtl_tcp_source/src/main.cpp
Normal 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
|
||||
}
|
120
rtl_tcp_source/src/rtltcp_client.h
Normal file
120
rtl_tcp_source/src/rtltcp_client.h
Normal 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;
|
||||
|
||||
};
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user