diff --git a/.gitignore b/.gitignore index c6b15170..d28d7cf8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,8 @@ poggers_decoder m17_decoder/libcorrect SDR++.app android/deps -android/app/assets \ No newline at end of file +android/app/assets +source_modules/dragonlabs_source +source_modules/badgesdr_source +decoder_modules/mystery_decoder +decoder_modules/tetra_decoder \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c62b84f2..ec919012 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_HYDRASDR_SOURCE "Build HydraSDR Source Module (Dependencies: libhydrasdr)" OFF) 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) @@ -162,6 +163,10 @@ if (OPT_BUILD_HERMES_SOURCE) add_subdirectory("source_modules/hermes_source") endif (OPT_BUILD_HERMES_SOURCE) +if (OPT_BUILD_HYDRASDR_SOURCE) +add_subdirectory("source_modules/hydrasdr_source") +endif (OPT_BUILD_HYDRASDR_SOURCE) + if (OPT_BUILD_KCSDR_SOURCE) add_subdirectory("source_modules/kcsdr_source") endif (OPT_BUILD_KCSDR_SOURCE) diff --git a/core/src/core.cpp b/core/src/core.cpp index cc74364f..e234d6d8 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -181,6 +181,8 @@ int sdrpp_main(int argc, char* argv[]) { defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true; defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source"; defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true; + defConfig["moduleInstances"]["HydraSDR Source"]["module"] = "hydrasdr_source"; + defConfig["moduleInstances"]["HydraSDR Source"]["enabled"] = true; defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source"; defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true; defConfig["moduleInstances"]["Network Source"]["module"] = "network_source"; diff --git a/source_modules/badgesdr_source/CMakeLists.txt b/source_modules/badgesdr_source/CMakeLists.txt deleted file mode 100644 index cf92410d..00000000 --- a/source_modules/badgesdr_source/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -cmake_minimum_required(VERSION 3.13) -project(badgesdr_source) - -file(GLOB SRC "src/*.cpp") - -include(${SDRPP_MODULE_CMAKE}) - -if (MSVC) - find_package(libusb CONFIG REQUIRED) - target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS}) - target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES}) -elseif (ANDROID) - target_link_libraries(badgesdr_source PUBLIC - /sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so - /sdr-kit/${ANDROID_ABI}/lib/librtlsdr.so - ) -else (MSVC) - find_package(PkgConfig) - - pkg_check_modules(LIBUSB REQUIRED libusb-1.0) - - target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS}) - target_link_directories(badgesdr_source PRIVATE ${LIBUSB_LIBRARY_DIRS}) - target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES}) -endif () \ No newline at end of file diff --git a/source_modules/badgesdr_source/src/badgesdr.cpp b/source_modules/badgesdr_source/src/badgesdr.cpp deleted file mode 100644 index cd42bfae..00000000 --- a/source_modules/badgesdr_source/src/badgesdr.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include "badgesdr.h" -#include -#include - -#define R820T_I2C_ADDR 0x1A - -namespace BadgeSDR { - enum Commands { - CMD_I2C_RW, - CMD_I2C_STATUS, - CMD_ADC_START, - CMD_ADC_STOP, - CMD_ADC_GET_SAMP_COUNT - }; - - libusb_context* ctx = NULL; - - bool DeviceInfo::operator==(const DeviceInfo& b) const { - return serialNumber == b.serialNumber; - } - - void Device::write_reg(uint8_t reg, uint8_t value, void* ctx) { - Device* dev = (Device*)ctx; - dev->writeR820TReg(reg, value); - dev->writeR820TReg(0x1F, 0); // TODO: Figure out why this is needed - } - - void Device::read_reg(uint8_t* data, int len, void* ctx) { - Device* dev = (Device*)ctx; - dev->readI2C(R820T_I2C_ADDR, data, len); - } - - Device::Device(libusb_device_handle* dev) { - // Save device handle - this->dev = dev; - - // Init tuner - r820t = { - 16000000, // xtal_freq => 16MHz - 3000000, // Set at boot to airspy_m0_m4_conf_t conf0 -> r820t_if_freq - 100000000, /* Default Freq 100Mhz */ - { - /* 05 */ 0x9F, // LNA manual gain mode, init to 0 - /* 06 */ 0x80, - /* 07 */ 0x60, - /* 08 */ 0x80, // Image Gain Adjustment - /* 09 */ 0x40, // Image Phase Adjustment - /* 0A */ 0xA8, // Channel filter [0..3]: 0 = widest, f = narrowest - Optimal. Don't touch! - /* 0B */ 0x0F, // High pass filter - Optimal. Don't touch! - /* 0C */ 0x4F, // VGA control by code, init at 0 - /* 0D */ 0x63, // LNA AGC settings: [0..3]: Lower threshold; [4..7]: High threshold - /* 0E */ 0x75, - /* 0F */ 0xE8, // Filter Widest, LDO_5V OFF, clk out ON, - /* 10 */ 0x7C, - /* 11 */ 0x42, - /* 12 */ 0x06, - /* 13 */ 0x00, - /* 14 */ 0x0F, - /* 15 */ 0x00, - /* 16 */ 0xC0, - /* 17 */ 0xA0, - /* 18 */ 0x48, - /* 19 */ 0xCC, - /* 1A */ 0x60, - /* 1B */ 0x00, - /* 1C */ 0x54, - /* 1D */ 0xAE, - /* 1E */ 0x0A, - /* 1F */ 0xC0 - }, - 0 /* uint16_t padding */ - }; - r820t_init(&r820t, 3000000, write_reg, read_reg, this); - r820t_set_mixer_gain(&r820t, 15); - r820t_set_vga_gain(&r820t, 15); - } - - Device::~Device() { - // Release the bulk interface - libusb_release_interface(dev, 0); - - // Close device - libusb_close(dev); - } - - void Device::setFrequency(double freq) { - r820t_set_freq(&r820t, freq - 2125000); - } - - void Device::setLNAGain(int gain) { - r820t_set_lna_gain(&r820t, gain); - } - - void Device::setMixerGain(int gain) { - r820t_set_mixer_gain(&r820t, gain); - } - - void Device::setVGAGain(int gain) { - r820t_set_vga_gain(&r820t, gain); - } - - - void Device::start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx, int minBufferSize) { - // Do nothing if already running - if (run) { return; } - - // Save handler - this->callback = callback; - this->ctx = ctx; - - // Compute buffer size - int bufCount = minBufferSize / 64; - if (minBufferSize % 64) { bufCount++; } - bufferSize = bufCount * 64; - - // Mark as running - run = true; - - // Start the ADC - startADC(); - - // Start worker thread - workerThread = std::thread(&Device::worker, this); - } - - void Device::stop() { - // Do nothing if already stopped - if (!run) { return; } - - // Mark as stopped - run = false; - - // Wait for the worker to exit - if (workerThread.joinable()) { workerThread.join(); } - - // Stop the ADC - stopADC(); - } - - int Device::getI2CStatus() { - int status; - int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000); - if (ret <= 0 || status < 0) { - return -1; - } - return status; - } - - int Device::readI2C(uint8_t addr, uint8_t* data, int len) { - // Do read - int bytes = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_RW, 0, addr, data, len, 1000); - if (bytes < 0) { - return -1; - } - - // Get status (TODO: Use function) - int status; - int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000); - if (ret <= 0 || status < 0) { - return -1; - } - - return bytes; - } - - int Device::writeI2C(uint8_t addr, const uint8_t* data, int len) { - // Do write - int bytes = libusb_control_transfer(dev, (2 << 5), CMD_I2C_RW, 0, addr, (uint8_t*)data, len, 1000); - if (bytes < 0) { - return -1; - } - - // Get status (TODO: Use function) - int status; - int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000); - if (ret <= 0 || status < 0) { - return -1; - } - - return bytes; - } - - uint8_t bitrev(uint8_t val) { - return ((val & 0x01) << 7) | ((val & 0x02) << 5) | ((val & 0x04) << 3) | ((val & 0x08) << 1) | ((val & 0x10) >> 1) | ((val & 0x20) >> 3) | ((val & 0x40) >> 5) | ((val & 0x80) >> 7); - } - - uint8_t Device::readR820TReg(uint8_t reg) { - // Read registers up to it (can't read single) - uint8_t regs[0x20]; - readI2C(R820T_I2C_ADDR, regs, reg+1); - - // Invert bit order - return bitrev(regs[reg]); - } - - void Device::writeR820TReg(uint8_t reg, uint8_t val) { - // Write register id then value - uint8_t cmd[2] = { reg, val }; - writeI2C(R820T_I2C_ADDR, cmd, 2); - } - - int Device::startADC() { - return libusb_control_transfer(dev, (2 << 5), CMD_ADC_START, 0, 0, NULL, 0, 1000); - } - - int Device::stopADC() { - return libusb_control_transfer(dev, (2 << 5), CMD_ADC_STOP, 0, 0, NULL, 0, 1000); - } - - void Device::worker() { - // Allocate sample buffer - uint8_t* buffer = new uint8_t[bufferSize]; - int sampleCount = 0; - - while (run) { - // Receive data with bulk transfer - int recvLen = 0; - int val = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_IN | 1, &buffer[sampleCount], bufferSize - sampleCount, &recvLen, 1000); - - // If timed out, try again. Otherwise, if an error occur, stop the thread - if (val == LIBUSB_ERROR_TIMEOUT) { - continue; - } - else if (val) { - flog::error("USB Error: {}", val); - break; - } - - // Increment sample count - if (recvLen) { sampleCount += recvLen; } - - // If the buffer is full, call handler and reset sample count - if (sampleCount >= bufferSize) { - callback(buffer, sampleCount, ctx); - sampleCount = 0; - } - } - - // Free buffer - delete[] buffer; - } - - std::vector list() { - // Init libusb if done yet - if (!ctx) { - libusb_init(&ctx); - libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_WARNING); - } - - // List devices - std::vector devList; - libusb_device** devices; - int devCount = libusb_get_device_list(ctx, &devices); - for (int i = 0; i < devCount; i++) { - // Get device info - DeviceInfo devInfo; - devInfo.dev = devices[i]; - libusb_device_descriptor desc; - libusb_get_device_descriptor(devInfo.dev, &desc); - - // Check the VID/PID and give up if not the right ones - if (desc.idVendor != 0xCAFE || desc.idProduct != 0x4010) { - libusb_unref_device(devInfo.dev); - continue; - } - - // Open devices - libusb_device_handle* openDev; - int err = libusb_open(devInfo.dev, &openDev); - if (err) { - libusb_unref_device(devInfo.dev); - continue; - } - - // Get serial number - char serial[128]; - libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, (uint8_t*)serial, sizeof(serial)); - devInfo.serialNumber = serial; - - // TODO: The libusb device should be unreffed but would need to know when the list disappears - - // Close device - libusb_close(openDev); - - // Add to list - devList.push_back(devInfo); - } - - // Return devices - return devList; - } - - std::shared_ptr open(const DeviceInfo& dev) { - // Open device - libusb_device_handle* openDev; - int err = libusb_open(dev.dev, &openDev); - if (err) { - throw std::runtime_error("Failed to open device"); - } - - // Claim the bulk transfer interface - if (libusb_claim_interface(openDev, 0)) { - throw std::runtime_error("Failed to claim bulk interface"); - } - - // Create device - return std::make_shared(openDev); - } -} \ No newline at end of file diff --git a/source_modules/badgesdr_source/src/badgesdr.h b/source_modules/badgesdr_source/src/badgesdr.h deleted file mode 100644 index 68d4184e..00000000 --- a/source_modules/badgesdr_source/src/badgesdr.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include "r820t.h" - -namespace BadgeSDR { - struct DeviceInfo { - std::string serialNumber; - libusb_device* dev; - bool operator==(const DeviceInfo& b) const; - }; - - class Device { - public: - Device(libusb_device_handle* dev); - ~Device(); - - void setFrequency(double freq); - void setLNAGain(int gain); - void setMixerGain(int gain); - void setVGAGain(int gain); - - void start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx = NULL, int minBufferSize = 2500); - void stop(); - - private: - int getI2CStatus(); - int readI2C(uint8_t addr, uint8_t* data, int len); - int writeI2C(uint8_t addr, const uint8_t* data, int len); - uint8_t readR820TReg(uint8_t reg); - void writeR820TReg(uint8_t reg, uint8_t val); - int startADC(); - int stopADC(); - void worker(); - - libusb_device_handle* dev; - std::thread workerThread; - bool run = false; - int bufferSize = 0; // Must be multiple of 64 for best performance - void* ctx = NULL; - void (*callback)(const uint8_t* samples, int count, void* ctx); - - static void write_reg(uint8_t reg, uint8_t value, void* ctx); - static void read_reg(uint8_t* data, int len, void* ctx); - r820t_priv_t r820t; - }; - - std::vector list(); - std::shared_ptr open(const DeviceInfo& dev); -} \ No newline at end of file diff --git a/source_modules/badgesdr_source/src/main.cpp b/source_modules/badgesdr_source/src/main.cpp deleted file mode 100644 index 9831bfb0..00000000 --- a/source_modules/badgesdr_source/src/main.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "badgesdr.h" - -SDRPP_MOD_INFO{ - /* Name: */ "badgesdr_source", - /* Description: */ "BadgeSDR Source Module", - /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 0, - /* Max instances */ -1 -}; - -#define CONCAT(a, b) ((std::string(a) + b).c_str()) - -class BadgeSDRSourceModule : public ModuleManager::Instance { -public: - BadgeSDRSourceModule(std::string name) { - this->name = name; - - sampleRate = 250000.0; - - // Initialize DSP - dcBlock.init(&input, 0.001); - ddc.init(&dcBlock.out, 500000, 250000, 250000, 125000); - - handler.ctx = this; - handler.selectHandler = menuSelected; - handler.deselectHandler = menuDeselected; - handler.menuHandler = menuHandler; - handler.startHandler = start; - handler.stopHandler = stop; - handler.tuneHandler = tune; - handler.stream = &ddc.out; - - // Refresh devices - refresh(); - - // Select first (TODO: Select from config) - select(""); - - sigpath::sourceManager.registerSource("BadgeSDR", &handler); - } - - ~BadgeSDRSourceModule() { - - } - - void postInit() {} - - void enable() { - enabled = true; - } - - void disable() { - enabled = false; - } - - bool isEnabled() { - return enabled; - } - -private: - void refresh() { - devices.clear(); - auto list = BadgeSDR::list(); - for (const auto& info : list) { - // Format device name - std::string devName = "BadgeSDR "; - devName += " ["; - devName += info.serialNumber; - devName += ']'; - - // Save device - devices.define(info.serialNumber, devName, info); - } - } - - 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; - } - - // Save serial number - selectedSerial = serial; - selectedDev = devices.value(devices.keyId(serial)); - } - - static void menuSelected(void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - core::setInputSampleRate(_this->sampleRate); - flog::info("BadgeSDRSourceModule '{0}': Menu Select!", _this->name); - } - - static void menuDeselected(void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - flog::info("BadgeSDRSourceModule '{0}': Menu Deselect!", _this->name); - } - - static void start(void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - if (_this->running) { return; } - - // Open the device - _this->openDev = BadgeSDR::open(_this->selectedDev); - - // Configure the device - _this->openDev->setFrequency(_this->freq); - _this->openDev->setLNAGain(_this->lnaGain); - _this->openDev->setMixerGain(_this->mixerGain); - _this->openDev->setVGAGain(_this->vgaGain); - - // Start DSP - _this->dcBlock.start(); - _this->ddc.start(); - - // Start device - _this->openDev->start(callback, _this, 500000/200); - - _this->running = true; - flog::info("BadgeSDRSourceModule '{0}': Start!", _this->name); - } - - static void stop(void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - if (!_this->running) { return; } - _this->running = false; - - // Stop worker - _this->openDev->stop(); - - // Stop DSP - _this->dcBlock.stop(); - _this->ddc.stop(); - - // Close device - _this->openDev.reset(); - - flog::info("BadgeSDRSourceModule '{0}': Stop!", _this->name); - } - - static void tune(double freq, void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - if (_this->running) { - _this->openDev->setFrequency(freq); - } - _this->freq = freq; - flog::info("BadgeSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); - } - - static void menuHandler(void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - - if (_this->running) { SmGui::BeginDisabled(); } - - SmGui::FillWidth(); - SmGui::ForceSync(); - if (SmGui::Combo(CONCAT("##_badgesdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { - _this->select(_this->devices.key(_this->devId)); - core::setInputSampleRate(_this->sampleRate); - // TODO: Save - } - - SmGui::FillWidth(); - SmGui::ForceSync(); - if (SmGui::Button(CONCAT("Refresh##_badgesdr_refr_", _this->name))) { - _this->refresh(); - _this->select(_this->selectedSerial); - core::setInputSampleRate(_this->sampleRate); - } - - if (_this->running) { SmGui::EndDisabled(); } - - SmGui::LeftLabel("LNA Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_badgesdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { - if (_this->running) { - _this->openDev->setLNAGain(_this->lnaGain); - } - // TODO: Save - } - - SmGui::LeftLabel("Mixer Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_badgesdr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) { - if (_this->running) { - _this->openDev->setMixerGain(_this->mixerGain); - } - // TODO: Save - } - - SmGui::LeftLabel("VGA Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_badgesdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { - if (_this->running) { - _this->openDev->setVGAGain(_this->vgaGain); - } - // TODO: Save - } - } - - static void callback(const uint8_t* samples, int count, void* ctx) { - BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; - - // Convert samples to float - dsp::complex_t* out = _this->input.writeBuf; - int min = 255, max = 0; - for (int i = 0; i < count; i++) { - if (samples[i] < min) { min = samples[i]; } - if (samples[i] > max) { max = samples[i]; } - - out[i].re = ((float)samples[i] - 127.5f) * (1.0f/127.0f); - out[i].im = 1.0f; - } - - // Send out samples - _this->input.swap(count); - - flog::debug("Amplitudes: {} -> {}", min, max); - } - - std::string name; - bool enabled = true; - double sampleRate; - SourceManager::SourceHandler handler; - bool running = false; - double freq; - - OptionList devices; - - int devId = 0; - int lnaGain = 0; - int mixerGain = 0; - int vgaGain = 0; - std::string selectedSerial; - BadgeSDR::DeviceInfo selectedDev; - std::shared_ptr openDev; - - dsp::stream input; - dsp::correction::DCBlocker dcBlock; - dsp::channel::RxVFO ddc; -}; - -MOD_EXPORT void _INIT_() { - // Nothing here -} - -MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { - return new BadgeSDRSourceModule(name); -} - -MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { - delete (BadgeSDRSourceModule*)instance; -} - -MOD_EXPORT void _END_() { - // Nothing here -} \ No newline at end of file diff --git a/source_modules/badgesdr_source/src/r820t.cpp b/source_modules/badgesdr_source/src/r820t.cpp deleted file mode 100644 index ae22071b..00000000 --- a/source_modules/badgesdr_source/src/r820t.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Rafael Micro R820T driver for AIRSPY - * - * Copyright 2013 Youssef Touil - * Copyright 2014-2016 Benjamin Vernoux - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include "r820t.h" -#include - -static int r820t_read_cache_reg(r820t_priv_t *priv, int reg); - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -/* Tuner frequency ranges */ -struct r820t_freq_range -{ - uint8_t open_d; - uint8_t rf_mux_ploy; - uint8_t tf_c; -}; - -#define R820T_READ_MAX_DATA 32 -#define R820T_INIT_NB_REGS (32-5) -uint8_t r820t_read_data[R820T_READ_MAX_DATA]; /* Buffer for data read from I2C */ -uint8_t r820t_state_standby = 1; /* 1=standby/power off 0=r820t initialized/power on */ - -/* Tuner frequency ranges -"Copyright (C) 2013 Mauro Carvalho Chehab" -https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c -part of freq_ranges() -*/ -const struct r820t_freq_range freq_ranges[] = -{ - { - /* 0 MHz */ - /* .open_d = */ 0x08, /* low */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0xdf, /* R27[7:0] band2,band0 */ - }, { - /* 50 MHz */ - /* .open_d = */ 0x08, /* low */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0xbe, /* R27[7:0] band4,band1 */ - }, { - /* 55 MHz */ - /* .open_d = */ 0x08, /* low */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x8b, /* R27[7:0] band7,band4 */ - }, { - /* 60 MHz */ - /* .open_d = */ 0x08, /* low */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x7b, /* R27[7:0] band8,band4 */ - }, { - /* 65 MHz */ - /* .open_d = */ 0x08, /* low */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x69, /* R27[7:0] band9,band6 */ - }, { - /* 70 MHz */ - /* .open_d = */ 0x08, /* low */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x58, /* R27[7:0] band10,band7 */ - }, { - /* 75 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ - }, { - /* 80 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ - }, { - /* 90 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ - }, { - /* 100 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ - }, { - /* 110 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ - }, { - /* 120 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ - }, { - /* 140 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x14, /* R27[7:0] band14,band11 */ - }, { - /* 180 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ - }, { - /* 220 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ - }, { - /* 250 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x11, /* R27[7:0] highest,highest */ - }, { - /* 280 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ - }, { - /* 310 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ - }, { - /* 450 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ - }, { - /* 588 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ - }, { - /* 650 MHz */ - /* .open_d = */ 0x00, /* high */ - /* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ - /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ - } -}; - -#define FREQ_TO_IDX_SIZE (600) -const uint8_t freq_to_idx[FREQ_TO_IDX_SIZE]= -{ - /* 50 */ 1,/* 51 */ 1,/* 52 */ 1,/* 53 */ 1,/* 54 */ 1, - /* 55 */ 2,/* 56 */ 2,/* 57 */ 2,/* 58 */ 2,/* 59 */ 2, - /* 60 */ 3,/* 61 */ 3,/* 62 */ 3,/* 63 */ 3,/* 64 */ 3, - /* 65 */ 4,/* 66 */ 4,/* 67 */ 4,/* 68 */ 4,/* 69 */ 4, - /* 70 */ 5,/* 71 */ 5,/* 72 */ 5,/* 73 */ 5,/* 74 */ 5, - /* 75 */ 6,/* 76 */ 6,/* 77 */ 6,/* 78 */ 6,/* 79 */ 6, - /* 80 */ 7,/* 81 */ 7,/* 82 */ 7,/* 83 */ 7,/* 84 */ 7,/* 85 */ 7,/* 86 */ 7,/* 87 */ 7,/* 88 */ 7,/* 89 */ 7, - /* 90 */ 8,/* 91 */ 8,/* 92 */ 8,/* 93 */ 8,/* 94 */ 8,/* 95 */ 8,/* 96 */ 8,/* 97 */ 8,/* 98 */ 8,/* 99 */ 8, - /* 100 */ 9,/* 101 */ 9,/* 102 */ 9,/* 103 */ 9,/* 104 */ 9,/* 105 */ 9,/* 106 */ 9,/* 107 */ 9,/* 108 */ 9,/* 109 */ 9, - /* 110 */ 10,/* 111 */ 10,/* 112 */ 10,/* 113 */ 10,/* 114 */ 10,/* 115 */ 10,/* 116 */ 10,/* 117 */ 10,/* 118 */ 10,/* 119 */ 10, - /* 120 */ 11,/* 121 */ 11,/* 122 */ 11,/* 123 */ 11,/* 124 */ 11,/* 125 */ 11,/* 126 */ 11,/* 127 */ 11,/* 128 */ 11,/* 129 */ 11, - /* 130 */ 11,/* 131 */ 11,/* 132 */ 11,/* 133 */ 11,/* 134 */ 11,/* 135 */ 11,/* 136 */ 11,/* 137 */ 11,/* 138 */ 11,/* 139 */ 11, - /* 140 */ 12,/* 141 */ 12,/* 142 */ 12,/* 143 */ 12,/* 144 */ 12,/* 145 */ 12,/* 146 */ 12,/* 147 */ 12,/* 148 */ 12,/* 149 */ 12, - /* 150 */ 12,/* 151 */ 12,/* 152 */ 12,/* 153 */ 12,/* 154 */ 12,/* 155 */ 12,/* 156 */ 12,/* 157 */ 12,/* 158 */ 12,/* 159 */ 12, - /* 160 */ 12,/* 161 */ 12,/* 162 */ 12,/* 163 */ 12,/* 164 */ 12,/* 165 */ 12,/* 166 */ 12,/* 167 */ 12,/* 168 */ 12,/* 169 */ 12, - /* 170 */ 12,/* 171 */ 12,/* 172 */ 12,/* 173 */ 12,/* 174 */ 12,/* 175 */ 12,/* 176 */ 12,/* 177 */ 12,/* 178 */ 12,/* 179 */ 12, - /* 180 */ 13,/* 181 */ 13,/* 182 */ 13,/* 183 */ 13,/* 184 */ 13,/* 185 */ 13,/* 186 */ 13,/* 187 */ 13,/* 188 */ 13,/* 189 */ 13, - /* 190 */ 13,/* 191 */ 13,/* 192 */ 13,/* 193 */ 13,/* 194 */ 13,/* 195 */ 13,/* 196 */ 13,/* 197 */ 13,/* 198 */ 13,/* 199 */ 13, - /* 200 */ 13,/* 201 */ 13,/* 202 */ 13,/* 203 */ 13,/* 204 */ 13,/* 205 */ 13,/* 206 */ 13,/* 207 */ 13,/* 208 */ 13,/* 209 */ 13, - /* 210 */ 13,/* 211 */ 13,/* 212 */ 13,/* 213 */ 13,/* 214 */ 13,/* 215 */ 13,/* 216 */ 13,/* 217 */ 13,/* 218 */ 13,/* 219 */ 13, - /* 220 */ 14,/* 221 */ 14,/* 222 */ 14,/* 223 */ 14,/* 224 */ 14,/* 225 */ 14,/* 226 */ 14,/* 227 */ 14,/* 228 */ 14,/* 229 */ 14, - /* 230 */ 14,/* 231 */ 14,/* 232 */ 14,/* 233 */ 14,/* 234 */ 14,/* 235 */ 14,/* 236 */ 14,/* 237 */ 14,/* 238 */ 14,/* 239 */ 14, - /* 240 */ 14,/* 241 */ 14,/* 242 */ 14,/* 243 */ 14,/* 244 */ 14,/* 245 */ 14,/* 246 */ 14,/* 247 */ 14,/* 248 */ 14,/* 249 */ 14, - /* 250 */ 15,/* 251 */ 15,/* 252 */ 15,/* 253 */ 15,/* 254 */ 15,/* 255 */ 15,/* 256 */ 15,/* 257 */ 15,/* 258 */ 15,/* 259 */ 15, - /* 260 */ 15,/* 261 */ 15,/* 262 */ 15,/* 263 */ 15,/* 264 */ 15,/* 265 */ 15,/* 266 */ 15,/* 267 */ 15,/* 268 */ 15,/* 269 */ 15, - /* 270 */ 15,/* 271 */ 15,/* 272 */ 15,/* 273 */ 15,/* 274 */ 15,/* 275 */ 15,/* 276 */ 15,/* 277 */ 15,/* 278 */ 15,/* 279 */ 15, - /* 280 */ 16,/* 281 */ 16,/* 282 */ 16,/* 283 */ 16,/* 284 */ 16,/* 285 */ 16,/* 286 */ 16,/* 287 */ 16,/* 288 */ 16,/* 289 */ 16, - /* 290 */ 16,/* 291 */ 16,/* 292 */ 16,/* 293 */ 16,/* 294 */ 16,/* 295 */ 16,/* 296 */ 16,/* 297 */ 16,/* 298 */ 16,/* 299 */ 16, - /* 300 */ 16,/* 301 */ 16,/* 302 */ 16,/* 303 */ 16,/* 304 */ 16,/* 305 */ 16,/* 306 */ 16,/* 307 */ 16,/* 308 */ 16,/* 309 */ 16, - /* 310 */ 17,/* 311 */ 17,/* 312 */ 17,/* 313 */ 17,/* 314 */ 17,/* 315 */ 17,/* 316 */ 17,/* 317 */ 17,/* 318 */ 17,/* 319 */ 17, - /* 320 */ 17,/* 321 */ 17,/* 322 */ 17,/* 323 */ 17,/* 324 */ 17,/* 325 */ 17,/* 326 */ 17,/* 327 */ 17,/* 328 */ 17,/* 329 */ 17, - /* 330 */ 17,/* 331 */ 17,/* 332 */ 17,/* 333 */ 17,/* 334 */ 17,/* 335 */ 17,/* 336 */ 17,/* 337 */ 17,/* 338 */ 17,/* 339 */ 17, - /* 340 */ 17,/* 341 */ 17,/* 342 */ 17,/* 343 */ 17,/* 344 */ 17,/* 345 */ 17,/* 346 */ 17,/* 347 */ 17,/* 348 */ 17,/* 349 */ 17, - /* 350 */ 17,/* 351 */ 17,/* 352 */ 17,/* 353 */ 17,/* 354 */ 17,/* 355 */ 17,/* 356 */ 17,/* 357 */ 17,/* 358 */ 17,/* 359 */ 17, - /* 360 */ 17,/* 361 */ 17,/* 362 */ 17,/* 363 */ 17,/* 364 */ 17,/* 365 */ 17,/* 366 */ 17,/* 367 */ 17,/* 368 */ 17,/* 369 */ 17, - /* 370 */ 17,/* 371 */ 17,/* 372 */ 17,/* 373 */ 17,/* 374 */ 17,/* 375 */ 17,/* 376 */ 17,/* 377 */ 17,/* 378 */ 17,/* 379 */ 17, - /* 380 */ 17,/* 381 */ 17,/* 382 */ 17,/* 383 */ 17,/* 384 */ 17,/* 385 */ 17,/* 386 */ 17,/* 387 */ 17,/* 388 */ 17,/* 389 */ 17, - /* 390 */ 17,/* 391 */ 17,/* 392 */ 17,/* 393 */ 17,/* 394 */ 17,/* 395 */ 17,/* 396 */ 17,/* 397 */ 17,/* 398 */ 17,/* 399 */ 17, - /* 400 */ 17,/* 401 */ 17,/* 402 */ 17,/* 403 */ 17,/* 404 */ 17,/* 405 */ 17,/* 406 */ 17,/* 407 */ 17,/* 408 */ 17,/* 409 */ 17, - /* 410 */ 17,/* 411 */ 17,/* 412 */ 17,/* 413 */ 17,/* 414 */ 17,/* 415 */ 17,/* 416 */ 17,/* 417 */ 17,/* 418 */ 17,/* 419 */ 17, - /* 420 */ 17,/* 421 */ 17,/* 422 */ 17,/* 423 */ 17,/* 424 */ 17,/* 425 */ 17,/* 426 */ 17,/* 427 */ 17,/* 428 */ 17,/* 429 */ 17, - /* 430 */ 17,/* 431 */ 17,/* 432 */ 17,/* 433 */ 17,/* 434 */ 17,/* 435 */ 17,/* 436 */ 17,/* 437 */ 17,/* 438 */ 17,/* 439 */ 17, - /* 440 */ 17,/* 441 */ 17,/* 442 */ 17,/* 443 */ 17,/* 444 */ 17,/* 445 */ 17,/* 446 */ 17,/* 447 */ 17,/* 448 */ 17,/* 449 */ 17, - /* 450 */ 18,/* 451 */ 18,/* 452 */ 18,/* 453 */ 18,/* 454 */ 18,/* 455 */ 18,/* 456 */ 18,/* 457 */ 18,/* 458 */ 18,/* 459 */ 18, - /* 460 */ 18,/* 461 */ 18,/* 462 */ 18,/* 463 */ 18,/* 464 */ 18,/* 465 */ 18,/* 466 */ 18,/* 467 */ 18,/* 468 */ 18,/* 469 */ 18, - /* 470 */ 18,/* 471 */ 18,/* 472 */ 18,/* 473 */ 18,/* 474 */ 18,/* 475 */ 18,/* 476 */ 18,/* 477 */ 18,/* 478 */ 18,/* 479 */ 18, - /* 480 */ 18,/* 481 */ 18,/* 482 */ 18,/* 483 */ 18,/* 484 */ 18,/* 485 */ 18,/* 486 */ 18,/* 487 */ 18,/* 488 */ 18,/* 489 */ 18, - /* 490 */ 18,/* 491 */ 18,/* 492 */ 18,/* 493 */ 18,/* 494 */ 18,/* 495 */ 18,/* 496 */ 18,/* 497 */ 18,/* 498 */ 18,/* 499 */ 18, - /* 500 */ 18,/* 501 */ 18,/* 502 */ 18,/* 503 */ 18,/* 504 */ 18,/* 505 */ 18,/* 506 */ 18,/* 507 */ 18,/* 508 */ 18,/* 509 */ 18, - /* 510 */ 18,/* 511 */ 18,/* 512 */ 18,/* 513 */ 18,/* 514 */ 18,/* 515 */ 18,/* 516 */ 18,/* 517 */ 18,/* 518 */ 18,/* 519 */ 18, - /* 520 */ 18,/* 521 */ 18,/* 522 */ 18,/* 523 */ 18,/* 524 */ 18,/* 525 */ 18,/* 526 */ 18,/* 527 */ 18,/* 528 */ 18,/* 529 */ 18, - /* 530 */ 18,/* 531 */ 18,/* 532 */ 18,/* 533 */ 18,/* 534 */ 18,/* 535 */ 18,/* 536 */ 18,/* 537 */ 18,/* 538 */ 18,/* 539 */ 18, - /* 540 */ 18,/* 541 */ 18,/* 542 */ 18,/* 543 */ 18,/* 544 */ 18,/* 545 */ 18,/* 546 */ 18,/* 547 */ 18,/* 548 */ 18,/* 549 */ 18, - /* 550 */ 18,/* 551 */ 18,/* 552 */ 18,/* 553 */ 18,/* 554 */ 18,/* 555 */ 18,/* 556 */ 18,/* 557 */ 18,/* 558 */ 18,/* 559 */ 18, - /* 560 */ 18,/* 561 */ 18,/* 562 */ 18,/* 563 */ 18,/* 564 */ 18,/* 565 */ 18,/* 566 */ 18,/* 567 */ 18,/* 568 */ 18,/* 569 */ 18, - /* 570 */ 18,/* 571 */ 18,/* 572 */ 18,/* 573 */ 18,/* 574 */ 18,/* 575 */ 18,/* 576 */ 18,/* 577 */ 18,/* 578 */ 18,/* 579 */ 18, - /* 580 */ 18,/* 581 */ 18,/* 582 */ 18,/* 583 */ 18,/* 584 */ 18,/* 585 */ 18,/* 586 */ 18,/* 587 */ 18, - /* 588 */ 19,/* 589 */ 19,/* 590 */ 19,/* 591 */ 19,/* 592 */ 19,/* 593 */ 19,/* 594 */ 19,/* 595 */ 19,/* 596 */ 19,/* 597 */ 19, - /* 598 */ 19,/* 599 */ 19,/* 600 */ 19,/* 601 */ 19,/* 602 */ 19,/* 603 */ 19,/* 604 */ 19,/* 605 */ 19,/* 606 */ 19,/* 607 */ 19, - /* 608 */ 19,/* 609 */ 19,/* 610 */ 19,/* 611 */ 19,/* 612 */ 19,/* 613 */ 19,/* 614 */ 19,/* 615 */ 19,/* 616 */ 19,/* 617 */ 19, - /* 618 */ 19,/* 619 */ 19,/* 620 */ 19,/* 621 */ 19,/* 622 */ 19,/* 623 */ 19,/* 624 */ 19,/* 625 */ 19,/* 626 */ 19,/* 627 */ 19, - /* 628 */ 19,/* 629 */ 19,/* 630 */ 19,/* 631 */ 19,/* 632 */ 19,/* 633 */ 19,/* 634 */ 19,/* 635 */ 19,/* 636 */ 19,/* 637 */ 19, - /* 638 */ 19,/* 639 */ 19,/* 640 */ 19,/* 641 */ 19,/* 642 */ 19,/* 643 */ 19,/* 644 */ 19,/* 645 */ 19,/* 646 */ 19,/* 647 */ 19, - /* 648 */ 19,/* 649 */ 19 -}; - -#define FREQ_50MHZ (50) -#define FREQ_TO_IDX_0_TO_49MHZ (0) -#define FREQ_TO_IDX_650_TO_1800MHZ (20) - -int r820t_freq_get_idx(uint32_t freq_mhz) -{ - uint32_t freq_mhz_fix; - - if(freq_mhz < FREQ_50MHZ) - { - /* Frequency Less than 50MHz */ - return FREQ_TO_IDX_0_TO_49MHZ; - }else - { - /* Frequency Between 50 to 649MHz use table */ - /* Fix the frequency for the table */ - freq_mhz_fix = freq_mhz - FREQ_50MHZ; - if(freq_mhz_fix < FREQ_TO_IDX_SIZE) - { - - return freq_to_idx[freq_mhz_fix]; - }else - { - /* Frequency Between 650 to 1800MHz */ - return FREQ_TO_IDX_650_TO_1800MHZ; - } - } -} - -static inline bool r820t_is_power_enabled(void) -{ - return true; -} - -/* - * Write regs 5 to 32 (R820T_INIT_NB_REGS values) using data parameter and write last reg to 0 - */ -void airspy_r820t_write_init(r820t_priv_t *priv, const uint8_t* data) -{ - for (int i = 0; i < R820T_INIT_NB_REGS; i++) { - priv->write_reg(i+REG_SHADOW_START, data[i], priv->ctx); - } - priv->write_reg(0x1F, 0, priv->ctx); -} - -/* - * Read from one or more contiguous registers. data[0] should be the first - * register number, one or more values follow. - */ - const uint8_t lut[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, - 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; - -static uint8_t r82xx_bitrev(uint8_t byte) -{ - return (lut[byte & 0xf] << 4) | lut[byte >> 4]; -} - -static int r820t_write_reg(r820t_priv_t *priv, uint8_t reg, uint8_t val) -{ - if (r820t_read_cache_reg(priv, reg) == val) - return 0; - priv->write_reg(reg, val, priv->ctx); - priv->regs[reg - REG_SHADOW_START] = val; - return 0; -} - -static int r820t_read_cache_reg(r820t_priv_t *priv, int reg) -{ - reg -= REG_SHADOW_START; - - if (reg >= 0 && reg < NUM_REGS) - return priv->regs[reg]; - else - return -1; -} - -static int r820t_write_reg_mask(r820t_priv_t *priv, uint8_t reg, uint8_t val, uint8_t bit_mask) -{ - int rc = r820t_read_cache_reg(priv, reg); - - if (rc < 0) - return rc; - - val = (rc & ~bit_mask) | (val & bit_mask); - - return r820t_write_reg(priv, reg, val); -} - -static int r820t_read(r820t_priv_t *priv, uint8_t *val, int len) -{ - /* reg not used and assumed to be always 0 because start from reg0 to reg0+len */ - priv->read_reg(val, len, priv->ctx); - - return 0; -} - -/* - * r820t tuning logic - */ -#ifdef OPTIM_SET_MUX -int r820t_set_mux_freq_idx = -1; /* Default set to invalid value in order to force set_mux */ -#endif - -/* -"inspired by Mauro Carvalho Chehab set_mux technique" -https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c -part of r820t_set_mux() (set tracking filter) -*/ -static int r820t_set_tf(r820t_priv_t *priv, uint32_t freq) -{ - const struct r820t_freq_range *range; - int freq_idx; - int rc = 0; - - /* Get the proper frequency range in MHz instead of Hz */ - /* Fast divide freq by 1000000 */ - freq = (uint32_t)((uint64_t)freq * 4295 >> 32); - - freq_idx = r820t_freq_get_idx(freq); - range = &freq_ranges[freq_idx]; - - /* Only reconfigure mux freq if modified vs previous range */ -#ifdef OPTIM_SET_MUX - if(freq_idx != r820t_set_mux_freq_idx) - { -#endif - /* Open Drain */ - rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08); - if (rc < 0) - return rc; - - /* RF_MUX,Polymux */ - rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); - if (rc < 0) - return rc; - - /* TF BAND */ - rc = r820t_write_reg(priv, 0x1b, range->tf_c); - if (rc < 0) - return rc; - - /* XTAL CAP & Drive */ - rc = r820t_write_reg_mask(priv, 0x10, 0x08, 0x0b); - if (rc < 0) - return rc; - - rc = r820t_write_reg_mask(priv, 0x08, 0x00, 0x3f); - if (rc < 0) - return rc; - - rc = r820t_write_reg_mask(priv, 0x09, 0x00, 0x3f); -#ifdef OPTIM_SET_MUX - } - r820t_set_mux_freq_idx = freq_idx; -#endif - - return rc; -} - -int r820t_set_pll(r820t_priv_t *priv, uint32_t freq) -{ - const uint32_t vco_min = 1770000000; - const uint32_t vco_max = 3900000000; - uint32_t ref = priv->xtal_freq >> 1; - - int rc; - uint32_t div_num; - uint32_t vco; - uint32_t rem; - uint32_t mask; - uint16_t sdm; - uint8_t nint; - uint8_t ni; - uint8_t si; - uint8_t div_found; - - /* Find a suitable divider */ - div_found = 0; - for (div_num = 0; div_num <= 5; div_num++) - { - vco = freq << (div_num + 1); - if (vco >= vco_min && vco <= vco_max) - { - div_found = 1; - break; - } - } - - if (!div_found) - return -1; - - vco += ref >> 16; - ref <<= 8; - mask = 1 << 23; - rem = 0; - while (mask > 0 && vco > 0) - { - if (vco >= ref) - { - rem |= mask; - vco -= ref; - } - ref >>= 1; - mask >>= 1; - } - - nint = rem >> 16; - sdm = rem & 0xffff; - - nint -= 13; - ni = nint >> 2; - si = nint & 3; - - /* Set the phase splitter */ - rc = r820t_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0); - if(rc < 0) - return rc; - - /* Set the integer part of the PLL */ - rc = r820t_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6))); - if(rc < 0) - return rc; - - if (sdm == 0) - { - /* Disable SDM */ - rc = r820t_write_reg_mask(priv, 0x12, 0x08, 0x08); - if(rc < 0) - return rc; - } - else - { - /* Write SDM */ - rc = r820t_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff)); - if (rc < 0) - return rc; - - rc = r820t_write_reg(priv, 0x16, (uint8_t)(sdm >> 8)); - if (rc < 0) - return rc; - - /* Enable SDM */ - rc = r820t_write_reg_mask(priv, 0x12, 0x00, 0x08); - if (rc < 0) - return rc; - } - return rc; -} - -int r820t_set_freq(r820t_priv_t *priv, uint32_t freq) -{ - int rc; - uint32_t lo_freq = freq + priv->if_freq; - - rc = r820t_set_tf(priv, freq); - if (rc < 0) - return rc; - - rc = r820t_set_pll(priv, lo_freq); - if (rc < 0) - return rc; - - priv->freq = freq; - - return 0; -} - -int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index) -{ - return r820t_write_reg_mask(priv, 0x05, gain_index, 0x0f); -} - -int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index) -{ - return r820t_write_reg_mask(priv, 0x07, gain_index, 0x0f); -} - -int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index) -{ - return r820t_write_reg_mask(priv, 0x0c, gain_index, 0x0f); -} - -int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value) -{ - value = value != 0 ? 0x00 : 0x10; - return r820t_write_reg_mask(priv, 0x05, value, 0x10); -} - -int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value) -{ - value = value != 0 ? 0x10 : 0x00; - return r820t_write_reg_mask(priv, 0x07, value, 0x10); -} - -/* -"inspired by Mauro Carvalho Chehab calibration technique" -https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c -part of r820t_set_tv_standard() -*/ -int r820t_calibrate(r820t_priv_t *priv) -{ - int i, rc, cal_code; - uint8_t data[5]; - - for (i = 0; i < 5; i++) - { - /* Set filt_cap */ - rc = r820t_write_reg_mask(priv, 0x0b, 0x08, 0x60); - if (rc < 0) - return rc; - - /* set cali clk =on */ - rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04); - if (rc < 0) - return rc; - - /* X'tal cap 0pF for PLL */ - rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03); - if (rc < 0) - return rc; - - rc = r820t_set_pll(priv, CALIBRATION_LO * 1000); - if (rc < 0) - return rc; - - /* Start Trigger */ - rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10); - if (rc < 0) - return rc; - - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - /* Stop Trigger */ - rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); - if (rc < 0) - return rc; - - /* set cali clk =off */ - rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04); - if (rc < 0) - return rc; - - /* Check if calibration worked */ - rc = r820t_read(priv, data, sizeof(data)); - if (rc < 0) - return rc; - - cal_code = data[4] & 0x0f; - if (cal_code && cal_code != 0x0f) - return 0; - } - - return -1; -} - -int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx) -{ - int rc; - uint32_t saved_freq; - - r820t_state_standby = 0; - priv->if_freq = if_freq; - priv->write_reg = write_reg; - priv->read_reg = read_reg; - priv->ctx = ctx; - /* Initialize registers */ - airspy_r820t_write_init(priv, priv->regs); - - r820t_set_freq(priv, priv->freq); - - /* Calibrate the IF filter */ - saved_freq = priv->freq; - rc = r820t_calibrate(priv); - priv->freq = saved_freq; - if (rc < 0) - { - saved_freq = priv->freq; - r820t_calibrate(priv); - priv->freq = saved_freq; - } - - /* Restore freq as it has been modified by r820t_calibrate() */ - rc = r820t_set_freq(priv, priv->freq); - return rc; -} - -void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw) -{ - const uint8_t modes[] = { 0xE0, 0x80, 0x60, 0x00 }; - const uint8_t opt[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; - uint8_t a = 0xB0 | opt[bw & 0x0F]; - uint8_t b = 0x0F | modes[bw >> 4]; - r820t_write_reg(priv, 0x0A, a); - r820t_write_reg(priv, 0x0B, b); -} \ No newline at end of file diff --git a/source_modules/badgesdr_source/src/r820t.h b/source_modules/badgesdr_source/src/r820t.h deleted file mode 100644 index d643ebc7..00000000 --- a/source_modules/badgesdr_source/src/r820t.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2013-2016 Benjamin Vernoux - * - * This file is part of AirSpy. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#pragma once -#include - -#define REG_SHADOW_START 5 -#define NUM_REGS 30 - -/* R820T Clock */ -#define CALIBRATION_LO 88000 - -typedef void (*r820t_write_reg_f)(uint8_t reg, uint8_t value, void* ctx); -typedef void (*r820t_read_f)(uint8_t* data, int len, void* ctx); - -typedef struct -{ - uint32_t xtal_freq; /* XTAL_FREQ_HZ */ - uint32_t freq; - uint32_t if_freq; - uint8_t regs[NUM_REGS]; - uint16_t padding; - - r820t_write_reg_f write_reg; - r820t_read_f read_reg; - void* ctx; - -} r820t_priv_t; - -void airspy_r820t_write_single(r820t_priv_t *priv, uint8_t reg, uint8_t val); -uint8_t airspy_r820t_read_single(r820t_priv_t *priv, uint8_t reg); - -int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx); -int r820t_set_freq(r820t_priv_t *priv, uint32_t freq); -int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index); -int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index); -int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index); -int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value); -int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value); -void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw); \ No newline at end of file diff --git a/source_modules/hydrasdr_source/CMakeLists.txt b/source_modules/hydrasdr_source/CMakeLists.txt new file mode 100644 index 00000000..eb86e936 --- /dev/null +++ b/source_modules/hydrasdr_source/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.13) +project(hydrasdr_source) + +file(GLOB SRC "src/*.cpp") + +include(${SDRPP_MODULE_CMAKE}) + +if (MSVC) + target_link_directories(hydrasdr_source PRIVATE "C:/Program Files/HydraSDR/") + + target_include_directories(hydrasdr_source PUBLIC "C:/Program Files/HydraSDR/include/") + + target_link_libraries(hydrasdr_source PRIVATE hydrasdr) +elseif (ANDROID) + # TODO +else (MSVC) + # TODO +endif () \ No newline at end of file diff --git a/source_modules/hydrasdr_source/src/main.cpp b/source_modules/hydrasdr_source/src/main.cpp new file mode 100644 index 00000000..0b034390 --- /dev/null +++ b/source_modules/hydrasdr_source/src/main.cpp @@ -0,0 +1,635 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + +SDRPP_MOD_INFO{ + /* Name: */ "hydrasdr_source", + /* Description: */ "HydraSDR source module for SDR++", + /* Author: */ "Ryzerth", + /* Version: */ 0, 1, 0, + /* Max instances */ 1 +}; + +ConfigManager config; + +class HydraSDRSourceModule : public ModuleManager::Instance { +public: + HydraSDRSourceModule(std::string name) { + this->name = name; + + // Define the ports + ports.define("rx0", "RX0", RF_PORT_RX0); + ports.define("rx1", "RX1", RF_PORT_RX1); + ports.define("rx2", "RX2", RF_PORT_RX2); + + sampleRate = 10000000.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(); + + // Select device from config + config.acquire(); + std::string devSerial = config.conf["device"]; + config.release(); + selectByString(devSerial); + + sigpath::sourceManager.registerSource("HydraSDR", &handler); + } + + ~HydraSDRSourceModule() { + stop(this); + sigpath::sourceManager.unregisterSource("HydraSDR");; + } + + void postInit() {} + + void enable() { + enabled = true; + } + + void disable() { + enabled = false; + } + + bool isEnabled() { + return enabled; + } + + void refresh() { +#ifndef __ANDROID__ + devices.clear(); + + uint64_t serials[256]; + int n = hydrasdr_list_devices(serials, 256); + + char buf[1024]; + for (int i = 0; i < n; i++) { + sprintf(buf, "%016" PRIX64, serials[i]); + devices.define(buf, buf, serials[i]); + } +#else + // Check for device presence + int vid, pid; + devFd = backend::getDeviceFD(vid, pid, backend::AIRSPY_VIDPIDS); + if (devFd < 0) { return; } + + // Get device info + std::string fakeName = "HydraSDR USB"; + devList.push_back(0xDEADBEEF); + devListTxt += fakeName; + devListTxt += '\0'; +#endif + } + + void selectFirst() { + if (!devices.empty()) { + selectBySerial(devices.value(0)); + } + } + + void selectByString(std::string serial) { + if (devices.keyExists(serial)) { + selectBySerial(devices.value(devices.keyId(serial))); + return; + } + selectFirst(); + } + + void selectBySerial(uint64_t serial) { + hydrasdr_device* dev; + try { +#ifndef __ANDROID__ + int err = hydrasdr_open_sn(&dev, serial); +#else + int err = hydrasdr_open_fd(&dev, devFd); +#endif + if (err != 0) { + char buf[1024]; + sprintf(buf, "%016" PRIX64, serial); + flog::error("Could not open HydraSDR {0}", buf); + selectedSerial = 0; + return; + } + } + catch (const std::exception& e) { + char buf[1024]; + sprintf(buf, "%016" PRIX64, serial); + flog::error("Could not open HydraSDR {}", buf); + } + devId = devices.valueId(serial); + selectedSerial = serial; + selectedSerStr = devices.key(devId); + + uint32_t sampleRates[256]; + hydrasdr_get_samplerates(dev, sampleRates, 0); + int n = sampleRates[0]; + hydrasdr_get_samplerates(dev, sampleRates, n); + samplerates.clear(); + for (int i = 0; i < n; i++) { + samplerates.define(sampleRates[i], getBandwdithScaled(sampleRates[i]), sampleRates[i]); + } + + // Load config here + config.acquire(); + bool created = false; + if (!config.conf["devices"].contains(selectedSerStr)) { + created = true; + config.conf["devices"][selectedSerStr]["sampleRate"] = 10000000; + config.conf["devices"][selectedSerStr]["gainMode"] = 0; + config.conf["devices"][selectedSerStr]["sensitiveGain"] = 0; + config.conf["devices"][selectedSerStr]["linearGain"] = 0; + config.conf["devices"][selectedSerStr]["lnaGain"] = 0; + config.conf["devices"][selectedSerStr]["mixerGain"] = 0; + config.conf["devices"][selectedSerStr]["vgaGain"] = 0; + config.conf["devices"][selectedSerStr]["lnaAgc"] = false; + config.conf["devices"][selectedSerStr]["mixerAgc"] = false; + config.conf["devices"][selectedSerStr]["biasT"] = false; + } + + // Load sample rate + srId = 0; + sampleRate = samplerates.value(0); + if (config.conf["devices"][selectedSerStr].contains("sampleRate")) { + int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"]; + if (samplerates.keyExists(selectedSr)) { + srId = samplerates.keyId(selectedSr); + sampleRate = samplerates[srId]; + } + } + + // Load port + if (config.conf["devices"][selectedSerStr].contains("port")) { + std::string portStr = config.conf["devices"][selectedSerStr]["port"]; + if (ports.keyExists(portStr)) { + portId = ports.keyId(portStr); + } + } + + // Load gains + if (config.conf["devices"][selectedSerStr].contains("gainMode")) { + gainMode = config.conf["devices"][selectedSerStr]["gainMode"]; + } + if (config.conf["devices"][selectedSerStr].contains("sensitiveGain")) { + sensitiveGain = config.conf["devices"][selectedSerStr]["sensitiveGain"]; + } + if (config.conf["devices"][selectedSerStr].contains("linearGain")) { + linearGain = config.conf["devices"][selectedSerStr]["linearGain"]; + } + if (config.conf["devices"][selectedSerStr].contains("lnaGain")) { + lnaGain = config.conf["devices"][selectedSerStr]["lnaGain"]; + } + if (config.conf["devices"][selectedSerStr].contains("mixerGain")) { + mixerGain = config.conf["devices"][selectedSerStr]["mixerGain"]; + } + if (config.conf["devices"][selectedSerStr].contains("vgaGain")) { + vgaGain = config.conf["devices"][selectedSerStr]["vgaGain"]; + } + if (config.conf["devices"][selectedSerStr].contains("lnaAgc")) { + lnaAgc = config.conf["devices"][selectedSerStr]["lnaAgc"]; + } + if (config.conf["devices"][selectedSerStr].contains("mixerAgc")) { + mixerAgc = config.conf["devices"][selectedSerStr]["mixerAgc"]; + } + + // Load Bias-T + if (config.conf["devices"][selectedSerStr].contains("biasT")) { + biasT = config.conf["devices"][selectedSerStr]["biasT"]; + } + + config.release(created); + + hydrasdr_close(dev); + } + +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); + } + + static void menuSelected(void* ctx) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx; + core::setInputSampleRate(_this->sampleRate); + flog::info("HydraSDRSourceModule '{0}': Menu Select!", _this->name); + } + + static void menuDeselected(void* ctx) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx; + flog::info("HydraSDRSourceModule '{0}': Menu Deselect!", _this->name); + } + + static void start(void* ctx) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx; + if (_this->running) { return; } + if (_this->selectedSerial == 0) { + flog::error("Tried to start HydraSDR source with null serial"); + return; + } + +#ifndef __ANDROID__ + int err = hydrasdr_open_sn(&_this->openDev, _this->selectedSerial); +#else + int err = hydrasdr_open_fd(&_this->openDev, _this->devFd); +#endif + if (err != 0) { + char buf[1024]; + sprintf(buf, "%016" PRIX64, _this->selectedSerial); + flog::error("Could not open HydraSDR {0}", buf); + return; + } + + hydrasdr_set_samplerate(_this->openDev, _this->samplerates[_this->srId]); + hydrasdr_set_freq(_this->openDev, _this->freq); + + hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]); + + if (_this->gainMode == 0) { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); + } + else if (_this->gainMode == 1) { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain); + } + else if (_this->gainMode == 2) { + if (_this->lnaAgc) { + hydrasdr_set_lna_agc(_this->openDev, 1); + } + else { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain); + } + if (_this->mixerAgc) { + hydrasdr_set_mixer_agc(_this->openDev, 1); + } + else { + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain); + } + hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain); + } + + hydrasdr_set_rf_bias(_this->openDev, _this->biasT); + + hydrasdr_start_rx(_this->openDev, callback, _this); + + _this->running = true; + flog::info("HydraSDRSourceModule '{0}': Start!", _this->name); + } + + static void stop(void* ctx) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx; + if (!_this->running) { return; } + _this->running = false; + _this->stream.stopWriter(); + hydrasdr_close(_this->openDev); + _this->stream.clearWriteStop(); + flog::info("HydraSDRSourceModule '{0}': Stop!", _this->name); + } + + static void tune(double freq, void* ctx) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx; + if (_this->running) { + hydrasdr_set_freq(_this->openDev, freq); + } + _this->freq = freq; + flog::info("HydraSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); + } + + static void menuHandler(void* ctx) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx; + + if (_this->running) { SmGui::BeginDisabled(); } + + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_hydrasdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { + _this->selectBySerial(_this->devices[_this->devId]); + core::setInputSampleRate(_this->sampleRate); + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["device"] = _this->selectedSerStr; + config.release(true); + } + } + + if (SmGui::Combo(CONCAT("##_hydrasdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) { + _this->sampleRate = _this->samplerates[_this->srId]; + core::setInputSampleRate(_this->sampleRate); + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->samplerates.key(_this->srId); + config.release(true); + } + } + + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_hydrasdr_refr_", _this->name))) { + _this->refresh(); + config.acquire(); + std::string devSerial = config.conf["device"]; + config.release(); + _this->selectByString(devSerial); + core::setInputSampleRate(_this->sampleRate); + } + + if (_this->running) { SmGui::EndDisabled(); } + + SmGui::LeftLabel("Antenna Port"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_hydrasdr_port_", _this->name), &_this->portId, _this->ports.txt)) { + if (_this->running) { + hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["port"] = _this->ports.key(_this->portId); + config.release(true); + } + } + + SmGui::BeginGroup(); + SmGui::Columns(3, CONCAT("HydraSDRGainModeColumns##_", _this->name), false); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Sensitive##_hydrasdr_gm_", _this->name), _this->gainMode == 0)) { + _this->gainMode = 0; + if (_this->running) { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0; + config.release(true); + } + } + SmGui::NextColumn(); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Linear##_hydrasdr_gm_", _this->name), _this->gainMode == 1)) { + _this->gainMode = 1; + if (_this->running) { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1; + config.release(true); + } + } + SmGui::NextColumn(); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Free##_hydrasdr_gm_", _this->name), _this->gainMode == 2)) { + _this->gainMode = 2; + if (_this->running) { + if (_this->lnaAgc) { + hydrasdr_set_lna_agc(_this->openDev, 1); + } + else { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain); + } + if (_this->mixerAgc) { + hydrasdr_set_mixer_agc(_this->openDev, 1); + } + else { + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain); + } + hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2; + config.release(true); + } + } + SmGui::Columns(1, CONCAT("EndHydraSDRGainModeColumns##_", _this->name), false); + SmGui::EndGroup(); + + // Gain menus + + if (_this->gainMode == 0) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_hydrasdr_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) { + if (_this->running) { + hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain; + config.release(true); + } + } + } + else if (_this->gainMode == 1) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_hydrasdr_lin_gain_", _this->name), &_this->linearGain, 0, 21)) { + if (_this->running) { + hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain; + config.release(true); + } + } + } + else if (_this->gainMode == 2) { + // TODO: Switch to a table for alignment + if (_this->lnaAgc) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("LNA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_hydrasdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { + if (_this->running) { + hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain; + config.release(true); + } + } + if (_this->lnaAgc) { SmGui::EndDisabled(); } + + if (_this->mixerAgc) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("Mixer Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_hydrasdr_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) { + if (_this->running) { + hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain; + config.release(true); + } + } + if (_this->mixerAgc) { SmGui::EndDisabled(); } + + SmGui::LeftLabel("VGA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_hydrasdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { + if (_this->running) { + hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain; + config.release(true); + } + } + + // AGC Control + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("LNA AGC##_hydrasdr_", _this->name), &_this->lnaAgc)) { + if (_this->running) { + if (_this->lnaAgc) { + hydrasdr_set_lna_agc(_this->openDev, 1); + } + else { + hydrasdr_set_lna_agc(_this->openDev, 0); + hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain); + } + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc; + config.release(true); + } + } + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("Mixer AGC##_hydrasdr_", _this->name), &_this->mixerAgc)) { + if (_this->running) { + if (_this->mixerAgc) { + hydrasdr_set_mixer_agc(_this->openDev, 1); + } + else { + hydrasdr_set_mixer_agc(_this->openDev, 0); + hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain); + } + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc; + config.release(true); + } + } + } + + // Bias T + if (SmGui::Checkbox(CONCAT("Bias T##_hydrasdr_", _this->name), &_this->biasT)) { + if (_this->running) { + hydrasdr_set_rf_bias(_this->openDev, _this->biasT); + } + if (_this->selectedSerStr != "") { + config.acquire(); + config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT; + config.release(true); + } + } + } + + static int callback(hydrasdr_transfer_t* transfer) { + HydraSDRSourceModule* _this = (HydraSDRSourceModule*)transfer->ctx; + memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t)); + if (!_this->stream.swap(transfer->sample_count)) { return -1; } + return 0; + } + + std::string name; + hydrasdr_device* openDev; + bool enabled = true; + dsp::stream stream; + double sampleRate; + SourceManager::SourceHandler handler; + bool running = false; + double freq; + uint64_t selectedSerial = 0; + std::string selectedSerStr = ""; + int devId = 0; + int srId = 0; + int portId = 0; + + bool biasT = false; + + int lnaGain = 0; + int vgaGain = 0; + int mixerGain = 0; + int linearGain = 0; + int sensitiveGain = 0; + + int gainMode = 0; + + bool lnaAgc = false; + bool mixerAgc = false; + +#ifdef __ANDROID__ + int devFd = 0; +#endif + + OptionList devices; + OptionList samplerates; + OptionList ports; +}; + +MOD_EXPORT void _INIT_() { + json def = json({}); + def["devices"] = json({}); + def["device"] = ""; + config.setPath(core::args["root"].s() + "/hydrasdr_config.json"); + config.load(def); + config.enableAutoSave(); +} + +MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { + return new HydraSDRSourceModule(name); +} + +MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { + delete (HydraSDRSourceModule*)instance; +} + +MOD_EXPORT void _END_() { + config.disableAutoSave(); + config.save(); +} \ No newline at end of file