diff --git a/decoder_modules/ryfi_decoder/src/main.cpp b/decoder_modules/ryfi_decoder/src/main.cpp index 950b47e5..788148c5 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 800000 -#define INPUT_SAMPLE_RATE 1500000 -#define INPUT_BAUDRATE 720000 +#define INPUT_BANDWIDTH 138e3 +#define INPUT_SAMPLE_RATE 250e3 +#define INPUT_BAUDRATE 125e3 #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 79572d08..6e1c6a5d 100644 --- a/decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp +++ b/decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp @@ -93,7 +93,7 @@ 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", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000); // Cancel the partial packet if there was one pktExpected = 0; diff --git a/source_modules/badgesdr_source/CMakeLists.txt b/source_modules/badgesdr_source/CMakeLists.txt new file mode 100644 index 00000000..cf92410d --- /dev/null +++ b/source_modules/badgesdr_source/CMakeLists.txt @@ -0,0 +1,25 @@ +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 new file mode 100644 index 00000000..cd42bfae --- /dev/null +++ b/source_modules/badgesdr_source/src/badgesdr.cpp @@ -0,0 +1,309 @@ +#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 new file mode 100644 index 00000000..68d4184e --- /dev/null +++ b/source_modules/badgesdr_source/src/badgesdr.h @@ -0,0 +1,53 @@ +#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 new file mode 100644 index 00000000..9831bfb0 --- /dev/null +++ b/source_modules/badgesdr_source/src/main.cpp @@ -0,0 +1,272 @@ +#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 new file mode 100644 index 00000000..ae22071b --- /dev/null +++ b/source_modules/badgesdr_source/src/r820t.cpp @@ -0,0 +1,622 @@ +/* + * 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 new file mode 100644 index 00000000..d643ebc7 --- /dev/null +++ b/source_modules/badgesdr_source/src/r820t.h @@ -0,0 +1,58 @@ +/* + * 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/spectran_source/src/main.cpp b/source_modules/spectran_source/src/main.cpp index 96d75c3d..29363a90 100644 --- a/source_modules/spectran_source/src/main.cpp +++ b/source_modules/spectran_source/src/main.cpp @@ -33,10 +33,13 @@ public: SpectranSourceModule(std::string name) { this->name = name; - if (AARTSAAPI_Init(AARTSAAPI_MEMORY_MEDIUM) != AARTSAAPI_OK) { + AARTSAAPI_Result res; + if ((res = AARTSAAPI_Init(AARTSAAPI_MEMORY_MEDIUM)) != AARTSAAPI_OK) { + flog::error("Failed to initialize the RTSAAPI: {}", (uint32_t)res); return; } - if (AARTSAAPI_Open(&api) != AARTSAAPI_OK) { + if ((res = AARTSAAPI_Open(&api)) != AARTSAAPI_OK) { + flog::error("Failed to open the RTSAAPI: {}", (uint32_t)res); return; } @@ -452,13 +455,16 @@ private: void updateRef() { // Get and update bounds - AARTSAAPI_Config config; - AARTSAAPI_ConfigInfo refInfo; - AARTSAAPI_ConfigFind(&dev, &croot, &config, L"main/reflevel"); - AARTSAAPI_ConfigGetInfo(&dev, &config, &refInfo); + AARTSAAPI_Config config = {}; + AARTSAAPI_ConfigInfo refInfo = {}; + auto res = AARTSAAPI_ConfigFind(&dev, &croot, &config, L"main/reflevel"); + flog::debug("Res A: {}", res); + res = AARTSAAPI_ConfigGetInfo(&dev, &config, &refInfo); + flog::debug("Res B: {}", res); minRef = refInfo.minValue; maxRef = refInfo.maxValue; refStep = refInfo.stepValue; + flog::debug("Gain: {} -> {}", refInfo.minValue, refInfo.maxValue); refLevel = std::clamp(refLevel, minRef, maxRef); // Apply new ref level