diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ef2f80b..bf6c668f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio) option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF) option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF) option(OPT_BUILD_FILE_SOURCE "Wav file source" ON) +option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF) option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF) option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON) @@ -143,6 +144,10 @@ if (OPT_BUILD_FILE_SOURCE) add_subdirectory("source_modules/file_source") endif (OPT_BUILD_FILE_SOURCE) +if (OPT_BUILD_FOBOSSDR_SOURCE) +add_subdirectory("source_modules/fobossdr_source") +endif (OPT_BUILD_FOBOSSDR_SOURCE) + if (OPT_BUILD_HACKRF_SOURCE) add_subdirectory("source_modules/hackrf_source") endif (OPT_BUILD_HACKRF_SOURCE) diff --git a/decoder_modules/ryfi_decoder/src/main.cpp b/decoder_modules/ryfi_decoder/src/main.cpp index 788148c5..56cee5b8 100644 --- a/decoder_modules/ryfi_decoder/src/main.cpp +++ b/decoder_modules/ryfi_decoder/src/main.cpp @@ -23,9 +23,9 @@ SDRPP_MOD_INFO{ /* Max instances */ -1 }; -#define INPUT_BANDWIDTH 138e3 -#define INPUT_SAMPLE_RATE 250e3 -#define INPUT_BAUDRATE 125e3 +#define INPUT_BANDWIDTH 600e3 +#define INPUT_SAMPLE_RATE 1000e3 +#define INPUT_BAUDRATE 500e3 #define SYMBOL_DIAG_RATE 30 #define SYMBOL_DIAG_COUNT 1024 diff --git a/decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp b/decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp index 6e1c6a5d..2e924235 100644 --- a/decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp +++ b/decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp @@ -72,6 +72,7 @@ namespace ryfi { uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE]; int pktExpected = 0; int pktRead = 0; + int valid = 0; while (true) { // Read a frame @@ -80,6 +81,7 @@ namespace ryfi { // Deserialize the frame Frame::deserialize(rs.out.readBuf, frame); + valid++; // Flush the stream rs.out.flush(); @@ -93,11 +95,12 @@ namespace ryfi { // If the frames aren't consecutive int frameRead = 0; if (frame.counter != expectedCounter) { - flog::warn("Lost at least {} frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000); + flog::warn("Lost at least {} frames after {} valid frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000, valid); // Cancel the partial packet if there was one pktExpected = 0; pktRead = 0; + valid = 1; // If this frame is not an idle frame or continuation frame if (frame.firstPacket != PKT_OFFS_NONE) { diff --git a/sink_modules/network_sink/src/main.cpp b/sink_modules/network_sink/src/main.cpp index 3dd49d8f..231292cb 100644 --- a/sink_modules/network_sink/src/main.cpp +++ b/sink_modules/network_sink/src/main.cpp @@ -217,14 +217,19 @@ private: } void startServer() { - if (modeId == SINK_MODE_TCP) { - listener = net::listen(hostname, port); - if (listener) { - listener->acceptAsync(clientHandler, this); + try { + if (modeId == SINK_MODE_TCP) { + listener = net::listen(hostname, port); + if (listener) { + listener->acceptAsync(clientHandler, this); + } + } + else { + conn = net::openUDP("0.0.0.0", port, hostname, port, false); } } - else { - conn = net::openUDP("0.0.0.0", port, hostname, port, false); + catch (const std::exception& e) { + flog::error("Failed to open socket: {}", e.what()); } } diff --git a/source_modules/fobossdr_source/CMakeLists.txt b/source_modules/fobossdr_source/CMakeLists.txt new file mode 100644 index 00000000..e4053220 --- /dev/null +++ b/source_modules/fobossdr_source/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.13) +project(fobossdr_source) + +file(GLOB SRC "src/*.cpp") + +include(${SDRPP_MODULE_CMAKE}) + +if (MSVC) + # Lib path + target_link_directories(fobossdr_source PRIVATE "C:/Program Files/libfobos/lib/") + target_include_directories(fobossdr_source PRIVATE "C:/Program Files/libfobos/include/") + target_link_libraries(fobossdr_source PRIVATE fobos) +else (MSVC) + # TODO +endif () \ No newline at end of file diff --git a/source_modules/fobossdr_source/src/main.cpp b/source_modules/fobossdr_source/src/main.cpp new file mode 100644 index 00000000..b41b2a6e --- /dev/null +++ b/source_modules/fobossdr_source/src/main.cpp @@ -0,0 +1,381 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SDRPP_MOD_INFO{ + /* Name: */ "fobossdr_source", + /* Description: */ "FobosSDR Source Module", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ -1 +}; + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +class FobosSDRSourceModule : public ModuleManager::Instance { +public: + FobosSDRSourceModule(std::string name) { + this->name = name; + + sampleRate = 50000000.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; + + // Refresh devices + refresh(); + + // Select first (TODO: Select from config) + select(""); + + sigpath::sourceManager.registerSource("FobosSDR", &handler); + } + + ~FobosSDRSourceModule() { + + } + + void postInit() {} + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + + enum Port { + PORT_RF, + PORT_HF1, + PORT_HF2 + }; + +private: + std::string getBandwdithScaled(double bw) { + char buf[1024]; + if (bw >= 1000000.0) { + sprintf(buf, "%.1lfMHz", bw / 1000000.0); + } + else if (bw >= 1000.0) { + sprintf(buf, "%.1lfKHz", bw / 1000.0); + } + else { + sprintf(buf, "%.1lfHz", bw); + } + return std::string(buf); + } + + void refresh() { + devices.clear(); + + // Get device list + char serials[1024]; + memset(serials, 0, sizeof(serials)); + int devCount = fobos_rx_list_devices(serials); + if (devCount < 0) { + flog::error("Failed to get device list: {}", devCount); + return; + } + + // If no device, give up + if (!devCount) { return; } + + // Generate device entries + const char* _serials = serials; + int index = 0; + while (*_serials) { + // Read serial until space + std::string serial = ""; + while (*_serials) { + // Get a character + char c = *(_serials++); + + // If it's a space, we're done + if (c == ' ') { break; } + + // Otherwise, add it to the string + serial += c; + } + + // Create entry + devices.define(serial, serial, index++); + } + } + + void select(const std::string& serial) { + // If there are no devices, give up + if (devices.empty()) { + selectedSerial.clear(); + return; + } + + // If the serial was not found, select the first available serial + if (!devices.keyExists(serial)) { + select(devices.key(0)); + return; + } + + // Get the ID in the list + int id = devices.keyId(serial); + selectedDevId = devices[id]; + + // Open the device + fobos_dev_t* dev; + int err = fobos_rx_open(&dev, selectedDevId); + if (err) { + flog::error("Failed to open device: {}", err); + return; + } + + // Get a list of supported samplerates + double srList[128]; + unsigned int srCount; + err = fobos_rx_get_samplerates(dev, srList, &srCount); + if (err) { + flog::error("Failed to get samplerate list: {}", err); + return; + } + + // Generate samplerate list + samplerates.clear(); + for (int i = 0; i < srCount; i++) { + std::string str = getBandwdithScaled(srList[i]); + samplerates.define(srList[i], str, srList[i]); + } + + // Define the ports + ports.clear(); + ports.define("rf", "RF", PORT_RF); + ports.define("hf1", "HF1", PORT_HF1); + ports.define("hf2", "HF2", PORT_HF2); + + // Define clock sources + clockSources.clear(); + clockSources.define("internal", "Internal", 0); + clockSources.define("external", "External", 1); + + // Close the device + fobos_rx_close(dev); + + // Update the samplerate + core::setInputSampleRate(sampleRate); + + // Save serial number + selectedSerial = serial; + devId = id; + } + + static void menuSelected(void* ctx) { + FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; + core::setInputSampleRate(_this->sampleRate); + flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; + flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; + if (_this->running) { return; } + + // Open the device + int err = fobos_rx_open(&_this->openDev, _this->selectedDevId); + if (err) { + flog::error("Failed to open device: {}", err); + return; + } + + // Get the selected port + _this->port = _this->ports[_this->portId]; + + // Configure the device + double actualSr, actualFreq; + fobos_rx_set_samplerate(_this->openDev, _this->sampleRate, &actualSr); + fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq); + fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF); + fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]); + fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain); + fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain); + + // Compute buffer size + _this->bufferSize = _this->sampleRate / 200.0; + + // Start streaming + err = fobos_rx_start_sync(_this->openDev, _this->bufferSize); + if (err) { + flog::error("Failed to start stream: {}", err); + return; + } + + // Start worker + _this->run = true; + _this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this); + + _this->running = true; + flog::info("FobosSDRSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; + if (!_this->running) { return; } + _this->running = false; + + // Stop worker + _this->run = false; + _this->stream.stopWriter(); + if (_this->workerThread.joinable()) { _this->workerThread.join(); } + _this->stream.clearWriteStop(); + + // Stop streaming + fobos_rx_stop_sync(_this->openDev); + + // Close the device + fobos_rx_close(_this->openDev); + + flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; + if (_this->running) { + double actual; // Dummy, don't care + fobos_rx_set_frequency(_this->openDev, freq, &actual); + } + _this->freq = freq; + flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + static void menuHandler(void* ctx) { + FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; + + if (_this->running) { SmGui::BeginDisabled(); } + + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { + _this->select(_this->devices.key(_this->devId)); + core::setInputSampleRate(_this->sampleRate); + // TODO: Save + } + + if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) { + _this->sampleRate = _this->samplerates.value(_this->srId); + core::setInputSampleRate(_this->sampleRate); + // TODO: Save + } + + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) { + _this->refresh(); + _this->select(_this->selectedSerial); + core::setInputSampleRate(_this->sampleRate); + } + + SmGui::LeftLabel("Antenna Port"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)); + + if (_this->running) { SmGui::EndDisabled(); } + + SmGui::LeftLabel("Clock Source"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)); + + if (_this->port == PORT_RF) { + SmGui::LeftLabel("LNA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, 0, 2)) { + if (_this->running) { + fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain); + } + // TODO: Save + } + + SmGui::LeftLabel("VGA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { + if (_this->running) { + fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain); + } + // TODO: Save + } + } + } + + void worker() { + // Worker loop + while (run) { + // Read samples + unsigned int sampCount = 0; + int err = fobos_rx_read_sync(openDev, (float*)stream.writeBuf, &sampCount); + + // TODO: Send to DSP + if (!stream.swap(sampCount)) { break; } + } + } + + std::string name; + bool enabled = true; + dsp::stream stream; + double sampleRate; + SourceManager::SourceHandler handler; + bool running = false; + double freq; + + OptionList devices; + OptionList samplerates; + OptionList ports; + OptionList clockSources; + int devId = 0; + int srId = 0; + int portId = 0; + int clkSrcId = 0; + Port port; + int lnaGain = 0; + int vgaGain = 0; + std::string selectedSerial; + int selectedDevId; + + fobos_dev_t* openDev; + + int bufferSize; + std::thread workerThread; + std::atomic run = false; +}; + +MOD_EXPORT void _INIT_() { + // Nothing here +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new FobosSDRSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (FobosSDRSourceModule*)instance; +} + +MOD_EXPORT void _END_() { + // Nothing here +} \ No newline at end of file