From 1b27379a3d5680f47cc270cac0d07bbdaefb4d23 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 2 Oct 2024 18:57:05 +0200 Subject: [PATCH] add beginning of kcsdr source --- CMakeLists.txt | 5 + source_modules/kcsdr_source/.gitignore | 1 + source_modules/kcsdr_source/CMakeLists.txt | 10 + source_modules/kcsdr_source/src/kcsdr.c | 209 +++++++++++++ source_modules/kcsdr_source/src/kcsdr.h | 150 ++++++++++ source_modules/kcsdr_source/src/main.cpp | 324 +++++++++++++++++++++ 6 files changed, 699 insertions(+) create mode 100644 source_modules/kcsdr_source/.gitignore create mode 100644 source_modules/kcsdr_source/CMakeLists.txt create mode 100644 source_modules/kcsdr_source/src/kcsdr.c create mode 100644 source_modules/kcsdr_source/src/kcsdr.h create mode 100644 source_modules/kcsdr_source/src/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bf6c668f..13d19460 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: li 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) +option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF) option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF) option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON) option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF) @@ -160,6 +161,10 @@ if (OPT_BUILD_HERMES_SOURCE) add_subdirectory("source_modules/hermes_source") endif (OPT_BUILD_HERMES_SOURCE) +if (OPT_BUILD_KCSDR_SOURCE) +add_subdirectory("source_modules/kcsdr_source") +endif (OPT_BUILD_KCSDR_SOURCE) + if (OPT_BUILD_LIMESDR_SOURCE) add_subdirectory("source_modules/limesdr_source") endif (OPT_BUILD_LIMESDR_SOURCE) diff --git a/source_modules/kcsdr_source/.gitignore b/source_modules/kcsdr_source/.gitignore new file mode 100644 index 00000000..208a5991 --- /dev/null +++ b/source_modules/kcsdr_source/.gitignore @@ -0,0 +1 @@ +vendor/* \ No newline at end of file diff --git a/source_modules/kcsdr_source/CMakeLists.txt b/source_modules/kcsdr_source/CMakeLists.txt new file mode 100644 index 00000000..02ba3126 --- /dev/null +++ b/source_modules/kcsdr_source/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.13) +project(kcsdr_source) + +file(GLOB SRC "src/*.cpp" "src/*.c") + +include(${SDRPP_MODULE_CMAKE}) + +target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL") +target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10") +target_link_libraries(kcsdr_source PRIVATE FTD3XX) \ No newline at end of file diff --git a/source_modules/kcsdr_source/src/kcsdr.c b/source_modules/kcsdr_source/src/kcsdr.c new file mode 100644 index 00000000..622b39f7 --- /dev/null +++ b/source_modules/kcsdr_source/src/kcsdr.c @@ -0,0 +1,209 @@ +#include "kcsdr.h" +#include +#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h" +#include +#include + +#define KCSDR_PKT_EMPTY_LEN 0x0C + +#define KCSDR_COMMAND_PIPE 0x02 +#define KCSDR_RX_DATA_PIPE 0x83 +#define KCSDR_TX_DATA_PIPE 0x03 + +struct kcsdr { + FT_HANDLE ft; +}; + +#pragma pack(push, 1) +struct kcsdr_packet { + uint8_t zeros0[4]; + uint8_t length; + uint8_t zeros1[2]; + uint8_t hex_eighty; + uint32_t command; + uint8_t data[188]; +}; +typedef struct kcsdr_packet kcsdr_packet_t; +#pragma pack(pop) + +enum kcsdr_command { + CMD_NOT_USED_0x00 = 0x00, + CMD_SET_PORT = 0x01, + CMD_SET_FREQUENCY = 0x02, + CMD_SET_ATTENUATION = 0x03, + CMD_SET_AMPLIFIER = 0x04, + CMD_SET_BANDWIDTH = 0x05, + CMD_START = 0x06, + CMD_STOP = 0x07, + CMD_SET_EXT_AMP = 0x08, + CMD_START_REMOTE = 0x09, + CMD_STOP_REMOTE = 0x0A +}; +typedef enum kcsdr_command kcsdr_command_t; + +int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) { + Sleep(50); + + // Create an empty packet + kcsdr_packet_t pkt; + memset(&pkt, 0, sizeof(kcsdr_packet_t)); + + // Fill out the packet info + pkt.length = len + KCSDR_PKT_EMPTY_LEN; + pkt.hex_eighty = 0x80; // Whatever the fuck that is + pkt.command = (uint32_t)cmd | (uint32_t)dir; + + // Copy the data if there is some + if (len) { memcpy(pkt.data, data, len); } + + // Dump the bytes + uint8_t* dump = (uint8_t*)&pkt; + printf("Sending:"); + for (int i = 0; i < pkt.length; i++) { + printf(" %02X", dump[i]); + } + printf("\n"); + + // Send the command to endpoint 0 + int sent; + FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL); + if (err != FT_OK) { + return -err; + } + printf("Sent %d bytes (%d)\n", sent, err); + + Sleep(50); + + // Flush existing commands + FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE); + + return -(int)err; +} + +int kcsdr_list_devices(kcsdr_info_t** devices) { + // Generate a list of FTDI devices + int ftdiDevCount = 0; + FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount); + if (err != FT_OK) { + return -1; + } + + // If no device was found, return nothing + if (!ftdiDevCount) { + *devices = NULL; + return 0; + } + + // Get said device list + FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE)); + err = FT_GetDeviceInfoList(list, &ftdiDevCount); + if (err != FT_OK) { + return -1; + } + + // Allocate the device info list + *devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t)); + + // Find all KC908s + int kcCount = 0; + for (int i = 0; i < ftdiDevCount; i++) { + strcpy((*devices)[kcCount++].serial, list[i].SerialNumber); + } + + // Free the FTDI list + free(list); + + return kcCount; +} + +void kcsdr_free_device_list(kcsdr_info_t* devices) { + // Free the list + if (devices) { free(devices); } +} + +int kcsdr_open(kcsdr_t** dev, const char* serial) { + // Attempt to open the device using the serial number + FT_HANDLE ft; + FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft); + if (err != FT_OK) { + return -1; + } + + // Set the timeouts for the data pipes + FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000); + FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000); + + // Allocate the device object + *dev = malloc(sizeof(kcsdr_t)); + + // Fill out the device object + (*dev)->ft = ft; + + // Put device into remote control mode + return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0); +} + +void kcsdr_close(kcsdr_t* dev) { + // Put device back in normal mode + kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0); + + // Close the device + FT_Close(dev->ft); + + // Free the device object + free(dev); +} + +int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) { + // Send SET_PORT command + return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1); +} + +int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) { + // Send SET_FREQUENCY command + return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8); +} + +int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) { + // Send SET_ATTENUATION command + return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1); +} + +int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) { + // Send SET_AMPLIFIER command + return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1); +} + +int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) { + // Send CMD_SET_EXT_AMP command + return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1); +} + +int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) { + // Set SET_BANDWIDTH command + return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4); +} + +int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) { + // Send START command + return kcsdr_send_command(dev, dir, CMD_START, NULL, 0); +} + +int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) { + // Send STOP command + return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0); +} + +int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) { + // Receive samples (TODO: Endpoint might be 0x81) + int received; + FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL); + return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err; +} + +int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) { + // Transmit samples + int sent; + FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL); + return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err; +} \ No newline at end of file diff --git a/source_modules/kcsdr_source/src/kcsdr.h b/source_modules/kcsdr_source/src/kcsdr.h new file mode 100644 index 00000000..b06e577c --- /dev/null +++ b/source_modules/kcsdr_source/src/kcsdr.h @@ -0,0 +1,150 @@ +#pragma once +#include + +#define KCSDR_SERIAL_LEN 16 +#define KCSDR_MAX_PORTS 6 + +// Detect C++ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * KCSDR Device. +*/ +struct kcsdr; +typedef struct kcsdr kcsdr_t; + +/** + * Device Information +*/ +struct kcsdr_info { + char serial[KCSDR_SERIAL_LEN+1]; +}; +typedef struct kcsdr_info kcsdr_info_t; + +/** + * RF Direction. +*/ +enum kcsdr_direction { + KCSDR_DIR_RX = 0x00, + KCSDR_DIR_TX = 0x80 +}; +typedef enum kcsdr_direction kcsdr_direction_t; + +/** + * Get a list of KCSDR devices on the system. + * @param devices Pointer to an array of device info. + * @return Number of devices found or error code. +*/ +int kcsdr_list_devices(kcsdr_info_t** devices); + +/** + * Free a device list returned by `kcsdr_list_devices()`. + * @param devices Device list to free. +*/ +void kcsdr_free_device_list(kcsdr_info_t* devices); + +/** + * Open a KCSDR device. + * @param dev Newly open device. + * @param serial Serial number of the device to open as returned in the device list. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_open(kcsdr_t** dev, const char* serial); + +/** + * Close a KCSDR device. + * @param dev Device to be closed. +*/ +void kcsdr_close(kcsdr_t* dev); + +/** + * Select the RF port. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX. + * @param port RF port number to select. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port); + +/** + * Set the center frequency. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX + * @param freq Frequency in Hz. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq); + +/** + * Set the attenuation. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX + * @param samplerate Attenuation in dB. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att); + +/** + * Set the internal amplifier gain. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX + * @param gain Gain in dB. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain); + +/** + * Set the external amplifier gain. + * @param dev Device to control. + * @param gain Gain in dB. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain); + +/** + * Set the samplerate. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX + * @param samplerate Samplerate in Hz. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate); + +/** + * Start streaming samples. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir); + +/** + * Stop streaming samples. + * @param dev Device to control. + * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX. + * @return 0 on success, error code otherwise. +*/ +int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir); + +/** + * Receive a buffer of samples. + * @param samples Sample buffer. + * @param count Number of complex samples. + * @return Number of samples received. +*/ +int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count); + +/** + * Transmit a buffer of samples. + * @param samples Sample buffer. + * @param count Number of complex samples. + * @return Number of samples transmitted. +*/ +int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count); + +// Detect C++ +#ifdef __cplusplus +} +#endif diff --git a/source_modules/kcsdr_source/src/main.cpp b/source_modules/kcsdr_source/src/main.cpp new file mode 100644 index 00000000..924cc1cf --- /dev/null +++ b/source_modules/kcsdr_source/src/main.cpp @@ -0,0 +1,324 @@ +#include +#include +#include +#include +#include +#include +#include +#include "kcsdr.h" +#include + +SDRPP_MOD_INFO{ + /* Name: */ "kcsdr_source", + /* Description: */ "KCSDR Source Module", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ -1 +}; + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +class KCSDRSourceModule : public ModuleManager::Instance { +public: + KCSDRSourceModule(std::string name) { + this->name = name; + + sampleRate = 2000000.0; + samplerates.define(40e6, "40MHz", 40e6); + samplerates.define(35e6, "35MHz", 35e6); + samplerates.define(30e6, "30MHz", 30e6); + samplerates.define(25e6, "25MHz", 25e6); + samplerates.define(20e6, "20MHz", 20e6); + samplerates.define(15e6, "15MHz", 15e6); + samplerates.define(10e6, "10MHz", 10e6); + samplerates.define(5e6, "5MHz", 5e6); + + 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("KCSDR", &handler); + } + + ~KCSDRSourceModule() { + + } + + void postInit() {} + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + +private: + void refresh() { + devices.clear(); + + // Get device list + kcsdr_info_t* list; + int count = kcsdr_list_devices(&list); + if (count < 0) { + flog::error("Failed to list devices: {}", count); + return; + } + + // Create list + for (int i = 0; i < count; i++) { + devices.define(list[i].serial, list[i].serial, list[i].serial); + } + + // Free the device list + kcsdr_free_device_list(list); + } + + 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 menu ID + devId = devices.keyId(serial); + + // TODO + + // Update the samplerate + core::setInputSampleRate(sampleRate); + + // Save serial number + selectedSerial = serial; + } + + static void menuSelected(void* ctx) { + KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx; + core::setInputSampleRate(_this->sampleRate); + flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx; + flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx; + if (_this->running) { return; } + + // If no serial is given, do nothing + if (_this->selectedSerial.empty()) { return; } + + // Open the device + int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str()); + if (err) { + flog::error("Failed to open device: {}", err); + return; + } + + // Configure the device + kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0); + kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq); + kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att); + kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain); + kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain); + kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate); + + // Start the stream + kcsdr_start(_this->openDev, KCSDR_DIR_RX); + + // Start worker + _this->run = true; + _this->workerThread = std::thread(&KCSDRSourceModule::worker, _this); + + _this->running = true; + flog::info("KCSDRSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + KCSDRSourceModule* _this = (KCSDRSourceModule*)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 + kcsdr_stop(_this->openDev, KCSDR_DIR_RX); + + // Close the device + kcsdr_close(_this->openDev); + + flog::info("KCSDRSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx; + if (_this->running) { + kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq); + } + _this->freq = freq; + flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + static void menuHandler(void* ctx) { + KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx; + + if (_this->running) { SmGui::BeginDisabled(); } + + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_kcsdr_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("##_kcsdr_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##_kcsdr_refr_", _this->name))) { + _this->refresh(); + _this->select(_this->selectedSerial); + core::setInputSampleRate(_this->sampleRate); + } + + if (_this->running) { SmGui::EndDisabled(); } + + // SmGui::LeftLabel("RX Port"); + // SmGui::FillWidth(); + // if (SmGui::Combo(CONCAT("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) { + // if (_this->running) { + // // TODO + // } + // // TODO: Save + // } + + SmGui::LeftLabel("Attenuation"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) { + if (_this->running) { + kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att); + } + // TODO: Save + } + + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) { + if (_this->running) { + kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain); + } + // TODO: Save + } + + SmGui::LeftLabel("External Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) { + if (_this->running) { + kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain); + } + // TODO: Save + } + } + + void worker() { + // Compute the buffer size + int bufferSize = 0x4000/4;//sampleRate / 200; + + // Allocate the sample buffer + int16_t* samps = dsp::buffer::alloc(bufferSize*2); + + // Loop + while (run) { + // Read samples + int count = kcsdr_rx(openDev, samps, bufferSize); + if (!count) { continue; } + if (count < 0) { + flog::debug("Failed to read samples: {}", count); + break; + } + + // Convert the samples to float + volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2); + + // Send out the samples + if (!stream.swap(count)) { break; } + } + + // Free the sample buffer + dsp::buffer::free(samps); + } + + std::string name; + bool enabled = true; + dsp::stream stream; + double sampleRate; + SourceManager::SourceHandler handler; + bool running = false; + double freq; + + OptionList devices; + OptionList samplerates; + int devId = 0; + int srId = 0; + int att = 0; + int gain = 30; + int extGain = 1; + int portId = 0; + std::string selectedSerial; + + kcsdr_t* openDev; + + std::thread workerThread; + std::atomic run = false; +}; + +MOD_EXPORT void _INIT_() { + // Nothing here +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new KCSDRSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { + delete (KCSDRSourceModule*)instance; +} + +MOD_EXPORT void _END_() { + // Nothing here +} \ No newline at end of file