Push before merge

This commit is contained in:
Ryzerth 2021-05-13 17:31:40 +02:00
parent 0b276bed1d
commit c0244e819e
75 changed files with 9479 additions and 834 deletions

View File

@ -18,6 +18,7 @@ option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Depedencies: libsd
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON)
option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Depedencies: librtlsdr)" ON)
option(OPT_BUILD_SDDC_SOURCE "Build SDDC Source Module (Depedencies: libusb-1.0)" OFF)
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: rtaudio)" ON)
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
@ -72,6 +73,10 @@ if (OPT_BUILD_RTL_SDR_SOURCE)
add_subdirectory("rtl_sdr_source")
endif (OPT_BUILD_RTL_SDR_SOURCE)
if (OPT_BUILD_SDDC_SOURCE)
add_subdirectory("sddc_source")
endif (OPT_BUILD_SDDC_SOURCE)
if (OPT_BUILD_AUDIO_SINK)
add_subdirectory("audio_sink")
endif (OPT_BUILD_AUDIO_SINK)

View File

@ -1,22 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(rx888_source)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
include_directories("src/CyAPI")
file(GLOB SRC "src/*.cpp")
file(GLOB CYAPI "src/CyAPI/*.cpp")
add_library(rx888_source SHARED ${SRC} ${CYAPI})
target_link_libraries(rx888_source PRIVATE sdrpp_core)
set_target_properties(rx888_source PROPERTIES PREFIX "")
# Install directives
install(TARGETS rx888_source DESTINATION lib/sdrpp/plugins)

View File

@ -1,256 +0,0 @@
#include "Si5351.h"
#include "openFX3.h"
#include <spdlog/spdlog.h>
#define DbgPrintf spdlog::info
#define SI_CLK0_CONTROL 16 // Registers
#define SI_CLK1_CONTROL 17
#define SI_CLK2_CONTROL 18
#define SI_SYNTH_PLL_A 26
#define SI_SYNTH_PLL_B 34
#define SI_SYNTH_MS_0 42
#define SI_SYNTH_MS_1 50
#define SI_SYNTH_MS_2 58
#define SI_PLL_RESET (0x177)
#define SI_R_DIV_1 (0) // 0b00000000 /
#define SI_R_DIV_2 (0x10) // 0b00010000
#define SI_R_DIV_4 (0x20) // 0b00100000
#define SI_R_DIV_8 (0x30) // 0b00110000
#define SI_R_DIV_16 (0x40) // 0b01000000
#define SI_R_DIV_32 (0x50) // 0b01010000
#define SI_R_DIV_64 (0x60) // 0b01100000
#define SI_R_DIV_128 (0x70) // 0b01110000
#define SI_CLK_SRC_PLL_A 0b00000000
#define SI_CLK_SRC_PLL_B 0b00100000
#define SI5351_FREQ 27000000 // Crystal frequency
#define SI5351_PLL_FIXED 80000000000ULL
#define SI5351_ADDR (0x60 << 1 )
#define SI5351_CRYSTAL_LOAD 183
#define SI5351_CRYSTAL_LOAD_MASK (3<<6)
#define SI5351_CRYSTAL_LOAD_0PF (0<<6)
#define SI5351_CRYSTAL_LOAD_6PF (1<<6)
#define SI5351_CRYSTAL_LOAD_8PF (2<<6)
#define SI5351_CRYSTAL_LOAD_10PF (3<<6)
#define SI5351_PLL_INPUT_SOURCE 15
#define SI5351_CLKIN_DIV_MASK (3<<6)
#define SI5351_CLKIN_DIV_1 (0<<6)
#define SI5351_CLKIN_DIV_2 (1<<6)
#define SI5351_CLKIN_DIV_4 (2<<6)
#define SI5351_CLKIN_DIV_8 (3<<6)
#define SI5351_PLLB_SOURCE (1<<3)
#define SI5351_PLLA_SOURCE (1<<2)
void Si5351init()
{
// Set crystal load capacitance
fx3SendI2cbyte(SI5351_ADDR, SI5351_CRYSTAL_LOAD, SI5351_CRYSTAL_LOAD_6PF | 0b00010010);
fx3SendI2cbyte(SI5351_ADDR, SI_CLK0_CONTROL, 0x80); // clocks off
fx3SendI2cbyte(SI5351_ADDR, SI_CLK1_CONTROL, 0x80);
fx3SendI2cbyte(SI5351_ADDR, SI_CLK2_CONTROL, 0x80);
// si5351aSetFrequency(ADC_FREQ, R820T_ZERO);
DbgPrintf("Si5351_init()\n");
}
#define _PLLDEBUG_
//
// Set up specified PLL with mult, num and denom
// mult is 15..90
// num is 0..1,048,575 (0xFFFFF)
// denom is 0..1,048,575 (0xFFFFF)
//
static void setupPLL(UINT8 pll, UINT8 mult, UINT32 num, UINT32 denom)
{
UINT32 P1; // PLL config register P1
UINT32 P2; // PLL config register P2
UINT32 P3; // PLL config register P3
UINT8 data[8];
// the actual multiplier is mult + num / denom
#ifdef _PLLDEBUG_
double fa;
double x = 27000000.0;
fa = x * ((double)mult + (double)num / (double)denom);
DbgPrintf((char *) "pll = %d , mult = %d , num = %d , denom = %d\n", pll, mult, num, denom);
DbgPrintf((char *) "pll target %e \n", fa);
#endif
P1 = (UINT32)(128 * ((float)num / (float)denom));
P1 = (UINT32)(128 * (UINT32)(mult)+P1 - 512);
P2 = (UINT32)(128 * ((float)num / (float)denom));
P2 = (UINT32)(128 * num - denom * P2);
P3 = denom;
data[0] = (P3 & 0x0000FF00) >> 8;
data[1] = (P3 & 0x000000FF);
data[2] = (P1 & 0x00030000) >> 16;
data[3] = (P1 & 0x0000FF00) >> 8;
data[4] = (P1 & 0x000000FF);
data[5] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[6] = (P2 & 0x0000FF00) >> 8;
data[7] = (P2 & 0x000000FF);
fx3SendI2cbytes(SI5351_ADDR, pll, data, sizeof(data));
}
//
// Set up MultiSynth with integer divider and R divider
// R divider is the bit value which is OR'ed onto the appropriate register, it is a #define in si5351a.h
//
static void setupMultisynth(UINT8 synth, UINT32 divider, UINT8 rDiv)
{
UINT32 P1; // Synth config register P1
UINT32 P2; // Synth config register P2
UINT32 P3; // Synth config register P3
UINT8 data[8];
#ifdef _PLLDEBUG_
DbgPrintf("setupMultisynth synth = %d divider = %d rDiv= %d \n", synth, divider, rDiv);
//DbgPrintf("output expected f = %e\n", fa / divider);
#endif
P1 = 128 * divider - 512;
P2 = 0; // P2 = 0, P3 = 1 forces an integer value for the divider
P3 = 1;
data[0] = (P3 & 0x0000FF00) >> 8;
data[1] = (P3 & 0x000000FF);
data[2] = ((P1 & 0x00030000) >> 16) | rDiv;
data[3] = (P1 & 0x0000FF00) >> 8;
data[4] = (P1 & 0x000000FF);
data[5] = ((P3 & 0x000F0000) >> 12) | ((P2 & 0x000F0000) >> 16);
data[6] = (P2 & 0x0000FF00) >> 8;
data[7] = (P2 & 0x000000FF);
fx3SendI2cbytes(SI5351_ADDR, synth, data, sizeof(data));
}
//
// Switches off Si5351a output
// Example: si5351aOutputOff(SI_CLK0_CONTROL);
// will switch off output CLK0
//
void si5351aOutputOff(UINT8 clk)
{
fx3SendI2cbyte(SI5351_ADDR, clk, 0x80); // Refer to SiLabs AN619 to see bit values - 0x80 turns off the output stage
}
void si5351aSetFrequency(UINT32 freq, UINT32 freq2)
{
UINT32 frequency;
UINT32 pllFreq;
UINT32 xtalFreq = SI5351_FREQ;
UINT32 l;
double f;
UINT8 mult;
UINT32 num;
UINT32 denom;
UINT32 divider;
UINT32 rdiv;
double corr = 0.9999314;
//double corr = 1.0;
DbgPrintf("BBRF103 si5351 SetFreq ADC sampling:%d R820T reference:%d\n", freq, freq2);
rdiv = (UINT32)SI_R_DIV_1;
frequency = (UINT32)((double)freq * corr);
while (frequency < 1000000)
{
frequency = frequency * 2;
rdiv += SI_R_DIV_2;
}
#ifdef _PLLDEBUG_
DbgPrintf((char *) "\nCLK0 frequency %d \n", frequency);
#endif
divider = 900000000UL / frequency;// Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency: the divider * desired output frequency
#ifdef _PLLDEBUG_
DbgPrintf((char *) "pllA Freq %d \n", pllFreq);
#endif
mult = pllFreq / xtalFreq; // Determine the multiplier to get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = (double)l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = (UINT32)f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL A with the calculated multiplication ratio
setupPLL(SI_SYNTH_PLL_A, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// represented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(SI_SYNTH_MS_0, divider, rdiv);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
fx3SendI2cbyte((UINT8)SI5351_ADDR, (UINT8)SI_PLL_RESET, (UINT8)0x20); //pllA
// Finally switch on the CLK0 output (0x4F)
// and set the MultiSynth0 input to be PLL A
fx3SendI2cbyte(SI5351_ADDR, SI_CLK0_CONTROL, 0x4F | SI_CLK_SRC_PLL_A);
if (freq2 > 0)
{
// calculate clk2
frequency = (UINT32)((double)freq2 * corr);
rdiv = SI_R_DIV_1;
xtalFreq = SI5351_FREQ;
while (frequency <= 1000000)
{
frequency = frequency * 2;
rdiv += SI_R_DIV_2;
}
#ifdef _PLLDEBUG_
DbgPrintf((char *) "\nCLK2 frequency %d \n", frequency);
#endif
divider = 900000000UL / frequency;// Calculate the division ratio. 900,000,000 is the maximum internal
// PLL frequency: 900MHz
if (divider % 2) divider--; // Ensure an even integer division ratio
pllFreq = divider * frequency; // Calculate the pllFrequency: the divider * desired output frequency
#ifdef _PLLDEBUG_
DbgPrintf((char *) "pllB Freq %d \n", pllFreq);
#endif
mult = pllFreq / xtalFreq; // Determine the multiplier to get to the required pllFrequency
l = pllFreq % xtalFreq; // It has three parts:
f = (double)l; // mult is an integer that must be in the range 15..90
f *= 1048575; // num and denom are the fractional parts, the numerator and denominator
f /= xtalFreq; // each is 20 bits (range 0..1048575)
num = (UINT32)f; // the actual multiplier is mult + num / denom
denom = 1048575; // For simplicity we set the denominator to the maximum 1048575
// Set up PLL B with the calculated multiplication ratio
setupPLL(SI_SYNTH_PLL_B, mult, num, denom);
// Set up MultiSynth divider 0, with the calculated divider.
// The final R division stage can divide by a power of two, from 1..128.
// represented by constants SI_R_DIV1 to SI_R_DIV128 (see si5351a.h header file)
// If you want to output frequencies below 1MHz, you have to use the
// final R division stage
setupMultisynth(SI_SYNTH_MS_2, divider, rdiv);
// Reset the PLL. This causes a glitch in the output. For small changes to
// the parameters, you don't need to reset the PLL, and there is no glitch
fx3SendI2cbyte((UINT8)SI5351_ADDR, (UINT8)SI_PLL_RESET, (UINT8)0x80); //pllB
// Finally switch on the CLK2 output (0x4C)
// and set the MultiSynth0 input to be PLL A
fx3SendI2cbyte(SI5351_ADDR, SI_CLK2_CONTROL, 0x4C | SI_CLK_SRC_PLL_B); // select PLLB
// calculate clk2
}
else
{
fx3SendI2cbyte(SI5351_ADDR, SI_CLK2_CONTROL, 0x80);
}
}

View File

@ -1,13 +0,0 @@
// MIT License Copyright (c) 2017 ik1xpv@gmail.com, http://www.steila.com/blog
#ifndef SI5351_H
#define SI5351_H
#include <basetsd.h>
void Si5351init();
void si5351aSetFrequency(UINT32 freq, UINT32 freq2);
void si5351aOutputOff(UINT8 clk);
#endif

View File

@ -1,208 +0,0 @@
// MIT License Copyright (c) 2016 Booya Corp.
// booyasdr@gmail.com, http://booyasdr.sf.net
// modified 2017 11 30 ik1xpv@gmail.com, http://www.steila.com/blog
#include <windows.h>
#include <sys/stat.h>
#include <iostream>
#include <stdio.h>
#include "openfx3.h"
#include <spdlog/spdlog.h>
#define DbgPrintf spdlog::info
using namespace std;
void fx3Control(FX3Command command) { // firmware control
long lgt = 1;
UINT8 z = 0; // dummy data = 0
fx3dev->ControlEndPt->ReqCode = command;
fx3dev->ControlEndPt->Write(&z, lgt);
}
bool fx3Control(FX3Command command, PUINT8 data) { // firmware control BBRF
long lgt = 1; // default
bool r = false;
switch (command)
{
case GPIOFX3:
fx3dev->ControlEndPt->ReqCode = command;
fx3dev->ControlEndPt->Value = (USHORT)0;
fx3dev->ControlEndPt->Index = (USHORT)0;
lgt = 2;
DbgPrintf("GPIO %x %x\n", data[0], data[1]);
r = fx3dev->ControlEndPt->Write(data, lgt);
break;
case TESTFX3:
fx3dev->ControlEndPt->ReqCode = command;
fx3dev->ControlEndPt->Value = (USHORT)0;
fx3dev->ControlEndPt->Index = (USHORT)0;
lgt = 4; // TESTFX3 len
r = fx3dev->ControlEndPt->Read(data, lgt);
break;
default:
break;
}
if (r == false)
{
DbgPrintf("WARNING FX3FWControl failed %x .%x %x\n", r, command, *data);
closeFX3();
}
return r;
}
bool fx3SendI2cbytes(UINT8 i2caddr, UINT8 regaddr, PUINT8 pdata, UINT8 len)
{
bool r = false;
LONG lgt = len;
fx3dev->ControlEndPt->ReqCode = I2CWFX3;
fx3dev->ControlEndPt->Value = (USHORT)i2caddr;
fx3dev->ControlEndPt->Index = (USHORT)regaddr;
Sleep(10);
r = fx3dev->ControlEndPt->Write(pdata, lgt);
if (r == false)
DbgPrintf("\nWARNING fx3FWSendI2cbytes i2caddr 0x%02x regaddr 0x%02x 1data 0x%02x len 0x%02x \n",
i2caddr, regaddr, *pdata, len);
return r;
}
bool fx3SendI2cbyte(UINT8 i2caddr, UINT8 regaddr, UINT8 data)
{
return fx3SendI2cbytes(i2caddr, regaddr, &data, 1);
}
bool fx3ReadI2cbytes(UINT8 i2caddr, UINT8 regaddr, PUINT8 pdata, UINT8 len)
{
bool r = false;
LONG lgt = len;
WORD saveValue, saveIndex;
saveValue = fx3dev->ControlEndPt->Value;
saveIndex = fx3dev->ControlEndPt->Index;
fx3dev->ControlEndPt->ReqCode = I2CRFX3;
fx3dev->ControlEndPt->Value = (USHORT)i2caddr;
fx3dev->ControlEndPt->Index = (USHORT)regaddr;
r = fx3dev->ControlEndPt->Read(pdata, lgt);
if (r == false)
printf("WARNING fx3FWReadI2cbytes failed %x : %02x %02x %02x %02x : %02x\n", r, I2CRFX3, i2caddr, regaddr, len, *pdata);
fx3dev->ControlEndPt->Value = saveValue;
fx3dev->ControlEndPt->Index = saveIndex;
return r;
}
bool fx3ReadI2cbyte(UINT8 i2caddr, UINT8 regaddr, UINT8 data)
{
return fx3ReadI2cbytes(i2caddr, regaddr, &data, 1);
}
bool GetFx3Device(void); // open the device, called in initFX3()
CCyFX3Device *fx3dev = NULL;
const int VENDOR_ID = 0x04B4;
const int STREAMER_ID = 0x00F1;
const int BOOTLOADER_ID = 0x00F3;
CCyUSBEndPoint *EndPt;
bool bHighSpeedDevice;
bool bSuperSpeedDevice;
bool openFX3() {
bool r = false;
fx3dev = new CCyFX3Device; // instantiate the device
if (fx3dev == NULL) return 0; // return if failed
int n = fx3dev->DeviceCount(); // return if no devices connected
if (n == 0) {
DbgPrintf("Device Count = 0, Exit\n");
return r;
}
if (!GetFx3Device()) { DbgPrintf("No streamer or boot device found, Exit\n"); return r; }
char fwname[] = "rx888.img"; // firmware file
if (!fx3dev->IsBootLoaderRunning()) { // if not bootloader device
fx3Control(RESETFX3); // reset the fx3 firmware via CyU3PDeviceReset(false)
DbgPrintf("Reset Device\n");
Sleep(300);
fx3dev->Close(); // close class
delete fx3dev; // destroy class
Sleep(300);
fx3dev = new CCyFX3Device; // create class
GetFx3Device(); // open class
}
FX3_FWDWNLOAD_ERROR_CODE dlf = FAILED;
if (fx3dev->IsBootLoaderRunning()) {
dlf = fx3dev->DownloadFw(fwname, RAM);
Sleep(500); // wait for download to finish
}
if (dlf == 0) {
struct stat stbuf;
stat(fwname, &stbuf);
char *timestr;
timestr = ctime(&stbuf.st_mtime);
DbgPrintf("Loaded NEW FIRMWARE {0} {1}", fwname, timestr);
}
else if (dlf != 0)
{
DbgPrintf("OLD FIRMWARE\n");
}
GetFx3Device(); // open class with new firmware
if (!fx3dev->IsOpen()) {
DbgPrintf("Failed to open device\n");
return r;
}
EndPt = fx3dev->BulkInEndPt;
if (!EndPt) {
DbgPrintf("No Bulk In end point\n");
return r; // init failed
}
r = true;
return r; // init success
}
bool closeFX3() {
bool r = false;
fx3dev->Close(); // close class
delete fx3dev; // destroy class
Sleep(300);
return r;
}
bool GetFx3Device() { // open class
bool r = false;
if (fx3dev == NULL) return r;
int n = fx3dev->DeviceCount();
// Walk through all devices looking for VENDOR_ID/STREAMER_ID
if (n == 0) { DbgPrintf("Device Count = 0, Exit\n"); return r; }
fx3dev->Open(0);
// go down the list of devices to find our device
for (int i = 1; i <= n; i++) {
cout << hex << fx3dev->VendorID << " "
<< hex << fx3dev->ProductID << " " << fx3dev->FriendlyName << '\n';
if ((fx3dev->VendorID == VENDOR_ID) && (fx3dev->ProductID == STREAMER_ID))
{
r = true;
break;
}
if ((fx3dev->VendorID == VENDOR_ID) && (fx3dev->ProductID == BOOTLOADER_ID))
{
r = true;
break;
}
fx3dev->Open(i);
}
if (r == false)
fx3dev->Close();
return r;
}
bool fx3Check()
{
return (fx3dev != NULL);
}

View File

@ -1,36 +0,0 @@
// MIT License Copyright (c) 2016 Booya Corp.
// booyasdr@gmail.com, http://booyasdr.sf.net
// modified 2017 11 30 ik1xpv@gmail.com, http://www.steila.com/blog
#ifndef FX3DEV_H
#define FX3DEV_H
#include <windows.h>
#include "CyAPI.h"
#define PUINT8 UINT8*
bool openFX3(void);
extern CCyFX3Device *fx3dev;
extern CCyUSBEndPoint *EndPt;
bool closeFX3(void);
enum FX3Command {
STARTFX3 = 0xaa,
STOPFX3 = 0xab,
TESTFX3 = 0xac,
RESETFX3 = 0xcc,
PAUSEFX3 = 0xdd,
GPIOFX3 = 0xbc,
I2CWFX3 = 0xba,
I2CRFX3 = 0xbe
};
void fx3Control(FX3Command command);
bool fx3Control(FX3Command command, PUINT8 data);
bool fx3SendI2cbytes(UINT8 i2caddr, UINT8 regaddr, PUINT8 pdata, UINT8 len);
bool fx3SendI2cbyte(UINT8 i2caddr, UINT8 regaddr, UINT8 pdata);
bool fx3ReadI2cbytes(UINT8 i2caddr, UINT8 regaddr, PUINT8 pdata, UINT8 len);
bool fx3ReadI2cbyte(UINT8 i2caddr, UINT8 regaddr, UINT8 pdata);
bool fx3Check();
#endif

View File

@ -1,272 +0,0 @@
#include <imgui.h>
#include <spdlog/spdlog.h>
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <gui/style.h>
#include <dsp/math.h>
#include <openFX3.h>
#include <Si5351.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
#define ADC_RATE 64000000
#define XFER_TIMEOUT 5000
#define SEL0 (8) // SEL0 GPIO26
#define SEL1 (16) // SEL1 GPIO27
SDRPP_MOD_INFO {
/* Name: */ "rx888_source",
/* Description: */ "RX888 source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ 1
};
class RX888SourceModule : public ModuleManager::Instance {
public:
RX888SourceModule(std::string name) {
this->name = name;
if (!openFX3()) {
spdlog::error("No RX888 found!");
return;
}
Si5351init();
decimation = 5;
sampleRate = (ADC_RATE >> (decimation - 1));
freq = sampleRate / 2;
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
sigpath::sourceManager.registerSource("RX888", &handler);
spdlog::info("RX888SourceModule '{0}': Instance created!", name);
}
~RX888SourceModule() {
spdlog::info("RX888SourceModule '{0}': Instance deleted!", name);
}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
static void menuSelected(void* ctx) {
RX888SourceModule* _this = (RX888SourceModule*)ctx;
spdlog::info("RX888SourceModule '{0}': Menu Select!", _this->name);
core::setInputSampleRate(_this->sampleRate);
}
static void menuDeselected(void* ctx) {
RX888SourceModule* _this = (RX888SourceModule*)ctx;
spdlog::info("RX888SourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
RX888SourceModule* _this = (RX888SourceModule*)ctx;
_this->doStart();
spdlog::info("RX888SourceModule '{0}': Start!", _this->name);
}
void doStart() {
uint8_t Bgpio[2];
Bgpio[0] = 0x17 | SEL0 & (~SEL1);
Bgpio[1] = 0x00;
si5351aSetFrequency(ADC_RATE, 0);
fx3Control(GPIOFX3, Bgpio);
running = true;
usbThread = std::thread(_usbWorker, this);
workerThread = std::thread(_worker, this);
}
static void stop(void* ctx) {
RX888SourceModule* _this = (RX888SourceModule*)ctx;
_this->running = false;
_this->realStream.stopWriter();
_this->realStream.stopReader();
_this->stream.stopWriter();
_this->usbThread.join();
_this->workerThread.join();
_this->realStream.clearWriteStop();
_this->realStream.clearReadStop();
_this->stream.clearWriteStop();
spdlog::info("RX888SourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
RX888SourceModule* _this = (RX888SourceModule*)ctx;
_this->freq = freq;
spdlog::info("RX888SourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
RX888SourceModule* _this = (RX888SourceModule*)ctx;
if (ImGui::InputInt("Decimation", &_this->decimation)) {
_this->sampleRate = (ADC_RATE >> (_this->decimation - 1));
core::setInputSampleRate(_this->sampleRate);
}
}
static void _usbWorker(RX888SourceModule* _this) {
// Calculate hardware block siz
int realBlockSize = ADC_RATE / 100;
int i;
for (i = 1; i < realBlockSize; i = (i << 1));
realBlockSize = (i >> 1);
int realDataSize = realBlockSize * sizeof(int16_t);
int16_t* buffer = new int16_t[realBlockSize];
spdlog::info("Real block size: {0}", realBlockSize);
// Initialize transfer
long pktSize = EndPt->MaxPktSize;
EndPt->SetXferSize(realDataSize);
long ppx = realDataSize / pktSize;
OVERLAPPED inOvLap;
inOvLap.hEvent = CreateEvent(NULL, false, false, NULL);
auto context = EndPt->BeginDataXfer((PUCHAR)buffer, realDataSize, &inOvLap);
// Check weather the transfer begin was successful
if (EndPt->NtStatus || EndPt->UsbdStatus) {
spdlog::error("Xfer request rejected. 1 STATUS = {0} {1}", EndPt->NtStatus, EndPt->UsbdStatus);
delete[] buffer;
return;
}
// Start the USB chip
fx3Control(STARTFX3);
// Data loop
while (_this->running) {
// Wait for the transfer to begin and about if timeout
LONG rLen = realDataSize;
if (!EndPt->WaitForXfer(&inOvLap, XFER_TIMEOUT)) {
spdlog::error("Transfer aborted");
EndPt->Abort();
if (EndPt->LastError == ERROR_IO_PENDING) {
WaitForSingleObject(inOvLap.hEvent, XFER_TIMEOUT);
}
break;
}
// Check if the incomming data is bulk I/Q and end transfer
if (EndPt->Attributes == 2) {
if (EndPt->FinishDataXfer((PUCHAR)buffer, rLen, &inOvLap, context)) {
memcpy(_this->realStream.writeBuf, buffer, rLen);
_this->realStream.swap(rLen / 2);
}
}
// Start next transfer
context = EndPt->BeginDataXfer((PUCHAR)buffer, realBlockSize * 2, &inOvLap);
}
delete[] buffer;
// Stop FX3 chip
fx3Control(STOPFX3);
}
static void _worker(RX888SourceModule* _this) {
dsp::complex_t* iqbuffer = new dsp::complex_t[STREAM_BUFFER_SIZE];
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
// Read from real stream
int count = _this->realStream.read();
int blockSize = count;
while (count >= 0) {
for (int i = 0; i < count; i++) {
iqbuffer[i].q = (float)_this->realStream.readBuf[i] / 32768.0f;
}
_this->realStream.flush();
// Calculate the traslation frequency based on the tuning
float delta = -(_this->freq / (float)ADC_RATE) * 2.0f * FL_M_PI;
lv_32fc_t phaseDelta = lv_cmake(std::cos(delta), std::sin(delta));
// Apply translation
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_this->stream.writeBuf, (lv_32fc_t*)iqbuffer, phaseDelta, &phase, count);
// Decimate
blockSize = count;
for (int d = 0; d < (_this->decimation - 1); d++) {
blockSize = (blockSize >> 1);
for (int i = 0; i < blockSize; i++) {
_this->stream.writeBuf[i].i = (_this->stream.writeBuf[i*2].i + _this->stream.writeBuf[(i*2)+1].i) * 0.5f;
_this->stream.writeBuf[i].q = (_this->stream.writeBuf[i*2].q + _this->stream.writeBuf[(i*2)+1].q) * 0.5f;
}
}
// Write to output stream
_this->stream.swap(blockSize);
// Read from real stream
count = _this->realStream.read();
}
// Stop FX3 chip
fx3Control(STOPFX3);
// Deallocate buffers
delete[] iqbuffer;
}
std::string name;
dsp::stream<dsp::complex_t> stream;
dsp::stream<int16_t> realStream;
SourceManager::SourceHandler handler;
std::thread usbThread;
std::thread workerThread;
double freq;
double sampleRate;
int decimation;
bool running = false;
bool enabled = true;
};
MOD_EXPORT void _INIT_() {
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new RX888SourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
delete (RX888SourceModule*)instance;
}
MOD_EXPORT void _END_() {
}

View File

@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.13)
project(sddc_source)
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
else ()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
endif ()
include_directories("src/" "src/libsddc/" "src/libsddc/Core/" "src/libsddc/Core/pffft/" "src/libsddc/libsddc/")
if (MSVC)
# What the fuck?
file(GLOB SRC "src/*.cpp" "src/*.c" "src/libsddc/libsddc/*.c" "src/libsddc/libsddc/*.cpp" "src/libsddc/Core/*.c" "src/libsddc/Core/*.cpp" "src/libsddc/Core/radio/*.cpp" "src/libsddc/Core/pffft/*.c" "src/libsddc/Core/pffft/*.cpp" "src/libsddc/Core/arch/win32/*.c" "src/libsddc/Core/arch/win32/*.cpp" "src/libsddc/Core/arch/win32/CyAPI/*.cpp")
else (MSVC)
file(GLOB SRC "src/*.cpp" "src/*.c" "src/libsddc/libsddc/*.c" "src/libsddc/libsddc/*.cpp" "src/libsddc/Core/*.c" "src/libsddc/Core/*.cpp" "src/libsddc/Core/radio/*.cpp" "src/libsddc/Core/pffft/*.c" "src/libsddc/Core/pffft/*.cpp" "src/libsddc/Core/arch/linux/*.c" "src/libsddc/Core/arch/linux/*.cpp")
endif ()
add_library(sddc_source SHARED ${SRC})
target_link_libraries(sddc_source PRIVATE sdrpp_core)
set_target_properties(sddc_source PROPERTIES PREFIX "")
if (MSVC)
# Lib path
target_link_directories(sddc_source PUBLIC "C:/Program Files/PothosSDR/lib/")
# Misc headers
target_include_directories(sddc_source PUBLIC "C:/Program Files/PothosSDR/include/libusb-1.0/")
target_link_libraries(sddc_source PUBLIC libusb-1.0)
target_link_libraries(sddc_source PUBLIC Setupapi.lib)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
target_include_directories(sddc_source PUBLIC ${LIBUSB_INCLUDE_DIRS})
target_link_directories(sddc_source PUBLIC ${LIBUSB_LIBRARY_DIRS})
target_link_libraries(sddc_source PUBLIC ${LIBUSB_LIBRARIES})
endif ()
# Install directives
install(TARGETS sddc_source DESTINATION lib/sdrpp/plugins)

Binary file not shown.

View File

@ -0,0 +1,78 @@
cmake_minimum_required(VERSION 3.13)
project(SDDC VERSION 1.0.1)
include(CTest)
### build options
# default build type: Release
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif(NOT CMAKE_BUILD_TYPE)
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE} " - Version: " ${VERSION} " / " ${LIBVER})
# allow disabling optimizations - for debug reasons
option(USE_SIMD_OPTIMIZATIONS "enable SIMD optimizations" ON)
# allow enabling address sanitizer - for debug reasons
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
option(USE_DEBUG_ASAN "use GCC's address sanitizer?" OFF)
endif()
if (USE_DEBUG_ASAN)
set(ASANLIB "asan")
else()
set(ASANLIB "")
endif()
if (MSVC)
set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc /W3")
get_filename_component(SDKPATH ${CMAKE_LINKER} DIRECTORY)
find_program(LIBEXE lib HINTS ${SDKPATH} REQUIRED)
# External Project FFTW on Windows
if(${CMAKE_EXE_LINKER_FLAGS} MATCHES "X86")
SET(FFTW_URL "ftp://ftp.fftw.org/pub/fftw/fftw-3.3.5-dll32.zip")
SET(ARCH x86)
SET(HASH 29882a43033c9393479a4df52a2e9120589c06a2b724155b1a682747fa3e57d4)
else()
SET(FFTW_URL "ftp://ftp.fftw.org/pub/fftw/fftw-3.3.5-dll64.zip")
SET(ARCH x64)
SET(HASH cfd88dc0e8d7001115ea79e069a2c695d52c8947f5b4f3b7ac54a192756f439f)
endif()
include(ExternalProject)
ExternalProject_Add(
LIBFFTW
URL ${FFTW_URL}
URL_HASH SHA256=${HASH}
BUILD_IN_SOURCE TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ${LIBEXE} /def:./libfftw3f-3.def /MACHINE:${ARCH} /OUT:./fftw3f-3.lib
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(LIBFFTW SOURCE_DIR)
SET(LIBFFTW_INCLUDE_DIRS ${SOURCE_DIR})
SET(LIBFFTW_LIBRARY_DIRS ${SOURCE_DIR})
SET(LIBFFTW_LIBRARIES fftw3f-3)
add_subdirectory(ExtIO_sddc)
else()
if (USE_DEBUG_ASAN)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
set(CMAKE_CXX_FLAGS "-O0 -std=c++17 -Wall -Werror")
else()
set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wall -Werror")
endif(USE_DEBUG_ASAN)
#add_compile_options(-Wall -Wextra -pedantic)
include(FindPkgConfig)
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
pkg_check_modules(LIBFFTW REQUIRED fftw3f)
endif (MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
add_subdirectory(Core)
add_subdirectory(libsddc)
add_subdirectory(unittest)

View File

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.13)
if (MSVC)
file(GLOB ARCH_SRC "arch/win32/*.cpp" "arch/win32/CyAPI/*.cpp")
else()
file(GLOB ARCH_SRC "arch/linux/*.c" "arch/linux/*.cpp")
endif (MSVC)
file(GLOB SRC "*.cpp" "radio/*.cpp" "pffft/*.cpp" ${ARCH_SRC})
include_directories("." "CyAPI/" "pffft/")
if (MSVC)
set_source_files_properties(fft_mt_r2iq_avx.cpp PROPERTIES COMPILE_FLAGS /arch:AVX)
set_source_files_properties(fft_mt_r2iq_avx2.cpp PROPERTIES COMPILE_FLAGS /arch:AVX2)
set_source_files_properties(fft_mt_r2iq_avx512.cpp PROPERTIES COMPILE_FLAGS /arch:AVX512)
else()
set_source_files_properties(fft_mt_r2iq_avx.cpp PROPERTIES COMPILE_FLAGS -mavx)
set_source_files_properties(fft_mt_r2iq_avx2.cpp PROPERTIES COMPILE_FLAGS -mavx2)
set_source_files_properties(fft_mt_r2iq_avx512.cpp PROPERTIES COMPILE_FLAGS -mavx512f)
include_directories(${LIBUSB_INCLUDE_DIRS})
endif (MSVC)
include_directories(${LIBFFTW_INCLUDE_DIRS})
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
add_library(SDDC_CORE STATIC ${SRC})
set_target_properties(SDDC_CORE PROPERTIES POSITION_INDEPENDENT_CODE True)
target_compile_definitions(SDDC_CORE PUBLIC _CRT_SECURE_NO_WARNINGS)
if (NOT USE_SIMD_OPTIMIZATIONS)
target_compile_definitions(SDDC_CORE PRIVATE NO_SIMD_OPTIM)
endif()

View File

@ -0,0 +1,37 @@
#ifndef FX3CLASS_H
#define FX3CLASS_H
//
// FX3handler.cpp
// 2020 10 12 Oscar Steila ik1xpv
// loading arm code.img from resource by Howard Su and Hayati Ayguen
// This module was previous named:openFX3.cpp
// MIT License Copyright (c) 2016 Booya Corp.
// booyasdr@gmail.com, http://booyasdr.sf.net
// modified 2017 11 30 ik1xpv@gmail.com, http://www.steila.com/blog
//
#include <stdint.h>
#include <functional>
#include "../Interface.h"
#include "dsp/ringbuffer.h"
class fx3class
{
public:
virtual ~fx3class(void) {}
virtual bool Open(uint8_t* fw_data, uint32_t fw_size) = 0;
virtual bool Control(FX3Command command, uint8_t data = 0) = 0;
virtual bool Control(FX3Command command, uint32_t data) = 0;
virtual bool Control(FX3Command command, uint64_t data) = 0;
virtual bool SetArgument(uint16_t index, uint16_t value) = 0;
virtual bool GetHardwareInfo(uint32_t* data) = 0;
virtual bool ReadDebugTrace(uint8_t* pdata, uint8_t len) = 0;
virtual void StartStream(ringbuffer<int16_t>& input, int numofblock) = 0;
virtual void StopStream() = 0;
};
extern "C" fx3class* CreateUsbHandler();
#endif // FX3CLASS_H

View File

@ -0,0 +1,35 @@
#include "PScope_uti.h"
#include "license.txt"
int PScopeShot(const char * filename, const char * title2, const char * title1, short * data, float samplerate, unsigned int numsamples )
{
FILE *fp;
fp = fopen(filename, "w+");
fputs("Version,115\n", fp);
fprintf(fp, "Retainers,0,1,%d,1024,0,%f,1,1\n",numsamples,samplerate );
fputs("Placement,44,0,1,-1,-1,-1,-1,88,40,1116,879", fp);
fputs("WindMgr,7,2,0\n", fp);
fputs("Page,0,2\n", fp);
fputs("Col,3,1\n", fp);
fputs("Row,2,1\n", fp);
fputs("Row,3,146\n", fp);
fputs("Row,1,319\n", fp);
fputs("Col,2,1063\n", fp);
fputs("Row,4,1\n", fp);
fputs("Row,0,319\n", fp);
fputs("Page,1,2\n", fp);
fputs("Col,1,1\n", fp);
fputs("Row,1,1\n", fp);
fputs("Col,2,425\n", fp);
fputs("Row,4,1\n", fp);
fputs("Row,0,319\n", fp);
fprintf(fp,"DemoID,%s,%s,0\n", title1, title2 );
fprintf(fp,"RawData,1,%d,16,-32768,32767,%f,-3.276800e+04,3.276800e+04\n", numsamples,samplerate);
for (unsigned int n = 0; n < numsamples; n++ )
{
fprintf(fp, "%d\n", data[n]);
}
fputs("end\n", fp);
return fclose(fp);
}

View File

@ -0,0 +1,9 @@
#ifndef _PSCOPE_
#define _PSCOPE_
#include "license.txt"
#include <stdio.h>
#include <stdlib.h>
int PScopeShot(const char * filename, const char * title1, const char * title2, short * data, float samplerate, unsigned int numsamples );
#endif // _PSCOPE_

View File

@ -0,0 +1,440 @@
#include "license.txt"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "pf_mixer.h"
#include "RadioHandler.h"
#include "sddc_config.h"
#include "fft_mt_r2iq.h"
#include "sddc_config.h"
#include "PScope_uti.h"
#include "../Interface.h"
#include <chrono>
using namespace std::chrono;
// transfer variables
unsigned long Failures = 0;
void RadioHandlerClass::OnDataPacket()
{
auto len = outputbuffer.getBlockSize() / 2 / sizeof(float);
while(run)
{
auto buf = outputbuffer.getReadPtr();
if (!run)
break;
if (fc != 0.0f)
{
std::unique_lock<std::mutex> lk(fc_mutex);
shift_limited_unroll_C_sse_inp_c((complexf*)buf, len, stateFineTune);
}
#ifdef _DEBUG //PScope buffer screenshot
if (saveADCsamplesflag == true)
{
saveADCsamplesflag = false; // do it once
unsigned int numsamples = transferSize / sizeof(int16_t);
float samplerate = (float) getSampleRate();
PScopeShot("ADCrealsamples.adc", "ExtIO_sddc.dll",
"ADCrealsamples.adc input real ADC 16 bit samples",
(short*)buf, samplerate, numsamples);
}
#endif
Callback(buf, len);
outputbuffer.ReadDone();
SamplesXIF += len;
}
}
RadioHandlerClass::RadioHandlerClass() :
DbgPrintFX3(nullptr),
GetConsoleIn(nullptr),
run(false),
pga(false),
dither(false),
randout(false),
biasT_HF(false),
biasT_VHF(false),
firmware(0),
modeRF(NOMODE),
adcrate(DEFAULT_ADC_FREQ),
fc(0.0f),
hardware(new DummyRadio(nullptr))
{
inputbuffer.setBlockSize(transferSamples);
stateFineTune = new shift_limited_unroll_C_sse_data_t();
}
RadioHandlerClass::~RadioHandlerClass()
{
delete stateFineTune;
}
const char *RadioHandlerClass::getName()
{
return hardware->getName();
}
bool RadioHandlerClass::Init(fx3class* Fx3, void (*callback)(const float*, uint32_t), r2iqControlClass *r2iqCntrl)
{
uint8_t rdata[4];
this->fx3 = Fx3;
this->Callback = callback;
if (r2iqCntrl == nullptr)
r2iqCntrl = new fft_mt_r2iq();
Fx3->GetHardwareInfo((uint32_t*)rdata);
radio = (RadioModel)rdata[0];
firmware = (rdata[1] << 8) + rdata[2];
delete hardware; // delete dummy instance
switch (radio)
{
case HF103:
hardware = new HF103Radio(Fx3);
break;
case BBRF103:
hardware = new BBRF103Radio(Fx3);
break;
case RX888:
hardware = new RX888Radio(Fx3);
break;
case RX888r2:
hardware = new RX888R2Radio(Fx3);
break;
case RX888r3:
hardware = new RX888R3Radio(Fx3);
break;
case RX999:
hardware = new RX999Radio(Fx3);
break;
case RXLUCY:
hardware = new RXLucyRadio(Fx3);
break;
default:
hardware = new DummyRadio(Fx3);
DbgPrintf("WARNING no SDR connected\n");
break;
}
adcrate = adcnominalfreq;
hardware->Initialize(adcnominalfreq);
DbgPrintf("%s | firmware %x\n", hardware->getName(), firmware);
this->r2iqCntrl = r2iqCntrl;
r2iqCntrl->Init(hardware->getGain(), &inputbuffer, &outputbuffer);
return true;
}
bool RadioHandlerClass::Start(int srate_idx)
{
Stop();
DbgPrintf("RadioHandlerClass::Start\n");
int decimate = 4 - srate_idx; // 5 IF bands
if (adcnominalfreq > N2_BANDSWITCH)
decimate = 5 - srate_idx; // 6 IF bands
if (decimate < 0)
{
decimate = 0;
DbgPrintf("WARNING decimate mismatch at srate_idx = %d\n", srate_idx);
}
run = true;
count = 0;
hardware->FX3producerOn(); // FX3 start the producer
outputbuffer.setBlockSize(EXT_BLOCKLEN * 2 * sizeof(float));
// 0,1,2,3,4 => 32,16,8,4,2 MHz
r2iqCntrl->setDecimate(decimate);
r2iqCntrl->TurnOn();
fx3->StartStream(inputbuffer, QUEUE_SIZE);
submit_thread = std::thread(
[this]() {
this->OnDataPacket();
});
show_stats_thread = std::thread([this](void*) {
this->CaculateStats();
}, nullptr);
return true;
}
bool RadioHandlerClass::Stop()
{
std::unique_lock<std::mutex> lk(stop_mutex);
DbgPrintf("RadioHandlerClass::Stop %d\n", run);
if (run)
{
run = false; // now waits for threads
r2iqCntrl->TurnOff();
fx3->StopStream();
run = false; // now waits for threads
show_stats_thread.join(); //first to be joined
DbgPrintf("show_stats_thread join2\n");
submit_thread.join();
DbgPrintf("submit_thread join1\n");
hardware->FX3producerOff(); //FX3 stop the producer
}
return true;
}
bool RadioHandlerClass::Close()
{
delete hardware;
hardware = nullptr;
return true;
}
bool RadioHandlerClass::UpdateSampleRate(uint32_t samplefreq)
{
hardware->Initialize(samplefreq);
this->adcrate = samplefreq;
return 0;
}
// attenuator RF used in HF
int RadioHandlerClass::UpdateattRF(int att)
{
if (hardware->UpdateattRF(att))
{
return att;
}
return 0;
}
// attenuator RF used in HF
int RadioHandlerClass::UpdateIFGain(int idx)
{
if (hardware->UpdateGainIF(idx))
{
return idx;
}
return 0;
}
int RadioHandlerClass::GetRFAttSteps(const float **steps)
{
return hardware->getRFSteps(steps);
}
int RadioHandlerClass::GetIFGainSteps(const float **steps)
{
return hardware->getIFSteps(steps);
}
bool RadioHandlerClass::UpdatemodeRF(rf_mode mode)
{
if (modeRF != mode){
modeRF = mode;
DbgPrintf("Switch to mode: %d\n", modeRF);
hardware->UpdatemodeRF(mode);
if (mode == VHFMODE)
r2iqCntrl->setSideband(true);
else
r2iqCntrl->setSideband(false);
}
return true;
}
rf_mode RadioHandlerClass::PrepareLo(uint64_t lo)
{
return hardware->PrepareLo(lo);
}
uint64_t RadioHandlerClass::TuneLO(uint64_t wishedFreq)
{
uint64_t actLo;
actLo = hardware->TuneLo(wishedFreq);
// we need shift the samples
int64_t offset = wishedFreq - actLo;
DbgPrintf("Offset freq %" PRIi64 "\n", offset);
float fc = r2iqCntrl->setFreqOffset(offset / (getSampleRate() / 2.0f));
if (GetmodeRF() == VHFMODE)
fc = -fc; // sign change with sideband used
if (this->fc != fc)
{
std::unique_lock<std::mutex> lk(fc_mutex);
*stateFineTune = shift_limited_unroll_C_sse_init(fc, 0.0F);
this->fc = fc;
}
return wishedFreq;
}
bool RadioHandlerClass::UptDither(bool b)
{
dither = b;
if (dither)
hardware->FX3SetGPIO(DITH);
else
hardware->FX3UnsetGPIO(DITH);
return dither;
}
bool RadioHandlerClass::UptPga(bool b)
{
pga = b;
if (pga)
hardware->FX3SetGPIO(PGA_EN);
else
hardware->FX3UnsetGPIO(PGA_EN);
return pga;
}
bool RadioHandlerClass::UptRand(bool b)
{
randout = b;
if (randout)
hardware->FX3SetGPIO(RANDO);
else
hardware->FX3UnsetGPIO(RANDO);
r2iqCntrl->updateRand(randout);
return randout;
}
void RadioHandlerClass::CaculateStats()
{
high_resolution_clock::time_point EndingTime;
float kbRead = 0;
float kSReadIF = 0;
kbRead = 0; // zeros the kilobytes counter
kSReadIF = 0;
BytesXferred = 0;
SamplesXIF = 0;
uint8_t debdata[MAXLEN_D_USB];
memset(debdata, 0, MAXLEN_D_USB);
auto StartingTime = high_resolution_clock::now();
while (run) {
kbRead = float(BytesXferred) / 1000.0f;
kSReadIF = float(SamplesXIF) / 1000.0f;
EndingTime = high_resolution_clock::now();
duration<float,std::ratio<1,1>> timeElapsed(EndingTime-StartingTime);
mBps = (float)kbRead / timeElapsed.count() / 1000 / sizeof(int16_t);
mSpsIF = (float)kSReadIF / timeElapsed.count() / 1000;
BytesXferred = 0;
SamplesXIF = 0;
StartingTime = high_resolution_clock::now();
#ifdef _DEBUG
int nt = 10;
while (nt-- > 0)
{
std::this_thread::sleep_for(0.05s);
debdata[0] = 0; //clean buffer
if (GetConsoleIn != nullptr)
{
GetConsoleIn((char *)debdata, MAXLEN_D_USB);
if (debdata[0] !=0)
DbgPrintf("%s", (char*)debdata);
}
if (hardware->ReadDebugTrace(debdata, MAXLEN_D_USB) == true) // there are message from FX3 ?
{
int len = strlen((char*)debdata);
if (len > MAXLEN_D_USB - 1) len = MAXLEN_D_USB - 1;
debdata[len] = 0;
if ((len > 0)&&(DbgPrintFX3 != nullptr))
{
DbgPrintFX3("%s", (char*)debdata);
memset(debdata, 0, sizeof(debdata));
}
}
}
#else
std::this_thread::sleep_for(0.5s);
#endif
}
return;
}
void RadioHandlerClass::UpdBiasT_HF(bool flag)
{
biasT_HF = flag;
if (biasT_HF)
hardware->FX3SetGPIO(BIAS_HF);
else
hardware->FX3UnsetGPIO(BIAS_HF);
}
void RadioHandlerClass::UpdBiasT_VHF(bool flag)
{
biasT_VHF = flag;
if (biasT_VHF)
hardware->FX3SetGPIO(BIAS_VHF);
else
hardware->FX3UnsetGPIO(BIAS_VHF);
}
void RadioHandlerClass::uptLed(int led, bool on)
{
int pin;
switch(led)
{
case 0:
pin = LED_YELLOW;
break;
case 1:
pin = LED_RED;
break;
case 2:
pin = LED_BLUE;
break;
default:
return;
}
if (on)
hardware->FX3SetGPIO(pin);
else
hardware->FX3UnsetGPIO(pin);
}

View File

@ -0,0 +1,341 @@
#ifndef RADIOHANDLER_H
#define RADIOHANDLER_H
#include "license.txt"
#include "sddc_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include "FX3Class.h"
#include "dsp/ringbuffer.h"
class RadioHardware;
class r2iqControlClass;
enum {
RESULT_OK,
RESULT_BIG_STEP,
RESULT_TOO_HIGH,
RESULT_TOO_LOW,
RESULT_NOT_POSSIBLE
};
struct shift_limited_unroll_C_sse_data_s;
typedef struct shift_limited_unroll_C_sse_data_s shift_limited_unroll_C_sse_data_t;
class RadioHandlerClass {
public:
RadioHandlerClass();
virtual ~RadioHandlerClass();
bool Init(fx3class* Fx3, void (*callback)(const float*, uint32_t), r2iqControlClass *r2iqCntrl = nullptr);
bool Start(int srate_idx);
bool Stop();
bool Close();
bool IsReady(){return true;}
int GetRFAttSteps(const float **steps);
int UpdateattRF(int attIdx);
int GetIFGainSteps(const float **steps);
int UpdateIFGain(int attIdx);
bool UpdatemodeRF(rf_mode mode);
rf_mode GetmodeRF(){return (rf_mode)modeRF;}
bool UptDither (bool b);
bool GetDither () {return dither;}
bool UptPga(bool b);
bool GetPga() { return pga;}
bool UptRand (bool b);
bool GetRand () {return randout;}
uint16_t GetFirmware() { return firmware; }
uint32_t getSampleRate() { return adcrate; }
bool UpdateSampleRate(uint32_t samplerate);
float getBps() const { return mBps; }
float getSpsIF() const {return mSpsIF; }
const char* getName();
RadioModel getModel() { return radio; }
bool GetBiasT_HF() { return biasT_HF; }
void UpdBiasT_HF(bool flag);
bool GetBiasT_VHF() { return biasT_VHF; }
void UpdBiasT_VHF(bool flag);
uint64_t TuneLO(uint64_t lo);
rf_mode PrepareLo(uint64_t lo);
void uptLed(int led, bool on);
void EnableDebug(void (*dbgprintFX3)(const char* fmt, ...), bool (*getconsolein)(char* buf, int maxlen))
{
this->DbgPrintFX3 = dbgprintFX3;
this->GetConsoleIn = getconsolein;
};
bool ReadDebugTrace(uint8_t* pdata, uint8_t len) { return fx3->ReadDebugTrace(pdata, len); }
private:
void AdcSamplesProcess();
void AbortXferLoop(int qidx);
void CaculateStats();
void OnDataPacket();
r2iqControlClass* r2iqCntrl;
void (*Callback)(const float *data, uint32_t length);
void (*DbgPrintFX3)(const char* fmt, ...);
bool (*GetConsoleIn)(char* buf, int maxlen);
bool run;
unsigned long count; // absolute index
bool pga;
bool dither;
bool randout;
bool biasT_HF;
bool biasT_VHF;
uint16_t firmware;
rf_mode modeRF;
RadioModel radio;
// transfer variables
ringbuffer<int16_t> inputbuffer;
ringbuffer<float> outputbuffer;
// threads
std::thread show_stats_thread;
std::thread submit_thread;
// stats
unsigned long BytesXferred;
unsigned long SamplesXIF;
float mBps;
float mSpsIF;
fx3class *fx3;
uint32_t adcrate;
std::mutex fc_mutex;
std::mutex stop_mutex;
float fc;
RadioHardware* hardware;
shift_limited_unroll_C_sse_data_t* stateFineTune;
};
extern unsigned long Failures;
class RadioHardware {
public:
RadioHardware(fx3class* fx3) : Fx3(fx3), gpios(0) {}
virtual ~RadioHardware();
virtual const char* getName() = 0;
virtual rf_mode PrepareLo(uint64_t freq) = 0;
virtual float getGain() { return BBRF103_GAINFACTOR; }
virtual void Initialize(uint32_t samplefreq) = 0;
virtual bool UpdatemodeRF(rf_mode mode) = 0;
virtual bool UpdateattRF(int attIndex) = 0;
virtual uint64_t TuneLo(uint64_t freq) = 0;
virtual int getRFSteps(const float** steps ) { return 0; }
virtual int getIFSteps(const float** steps ) { return 0; }
virtual bool UpdateGainIF(int attIndex) { return false; }
bool FX3producerOn() { return Fx3->Control(STARTFX3); }
bool FX3producerOff() { return Fx3->Control(STOPFX3); }
bool ReadDebugTrace(uint8_t* pdata, uint8_t len) { return Fx3->ReadDebugTrace(pdata, len); }
bool FX3SetGPIO(uint32_t mask);
bool FX3UnsetGPIO(uint32_t mask);
protected:
fx3class* Fx3;
uint32_t gpios;
};
class BBRF103Radio : public RadioHardware {
public:
BBRF103Radio(fx3class* fx3);
const char* getName() override { return "BBRF103"; }
float getGain() override { return BBRF103_GAINFACTOR; }
rf_mode PrepareLo(uint64_t freq) override;
void Initialize(uint32_t samplefreq) override;
bool UpdatemodeRF(rf_mode mode) override;
uint64_t TuneLo(uint64_t freq) override;
bool UpdateattRF(int attIndex) override;
bool UpdateGainIF(int attIndex) override;
int getRFSteps(const float** steps ) override;
int getIFSteps(const float** steps ) override;
private:
static const int step_size = 29;
static const float steps[step_size];
static const float hfsteps[3];
static const int if_step_size = 16;
static const float if_steps[if_step_size];
uint32_t SampleRate;
};
class RX888Radio : public BBRF103Radio {
public:
RX888Radio(fx3class* fx3) : BBRF103Radio(fx3) {}
const char* getName() override { return "RX888"; }
float getGain() override { return RX888_GAINFACTOR; }
};
class RX888R2Radio : public RadioHardware {
public:
RX888R2Radio(fx3class* fx3);
const char* getName() override { return "RX888 mkII"; }
float getGain() override { return RX888mk2_GAINFACTOR; }
rf_mode PrepareLo(uint64_t freq) override;
void Initialize(uint32_t samplefreq) override;
bool UpdatemodeRF(rf_mode mode) override;
uint64_t TuneLo(uint64_t freq) override;
bool UpdateattRF(int attIndex) override;
bool UpdateGainIF(int attIndex) override;
int getRFSteps(const float** steps ) override;
int getIFSteps(const float** steps ) override;
private:
static const int hf_rf_step_size = 64;
static const int hf_if_step_size = 127;
static const int vhf_if_step_size = 16;
static const int vhf_rf_step_size = 29;
float hf_rf_steps[hf_rf_step_size];
float hf_if_steps[hf_if_step_size];
static const float vhf_rf_steps[vhf_rf_step_size];
static const float vhf_if_steps[vhf_if_step_size];
uint32_t SampleRate;
};
class RX888R3Radio : public RadioHardware {
public:
RX888R3Radio(fx3class* fx3);
const char* getName() override { return "RX888 mkIII"; }
float getGain() override { return RX888mk2_GAINFACTOR; }
rf_mode PrepareLo(uint64_t freq) override;
void Initialize(uint32_t samplefreq) override;
bool UpdatemodeRF(rf_mode mode) override;
uint64_t TuneLo(uint64_t freq) override;
bool UpdateattRF(int attIndex) override;
bool UpdateGainIF(int attIndex) override;
int getRFSteps(const float** steps ) override;
int getIFSteps(const float** steps ) override;
private:
static const int hf_rf_step_size = 64;
static const int hf_if_step_size = 127;
static const int vhf_if_step_size = 16;
static const int vhf_rf_step_size = 29;
float hf_rf_steps[hf_rf_step_size];
float hf_if_steps[hf_if_step_size];
static const float vhf_rf_steps[vhf_rf_step_size];
static const float vhf_if_steps[vhf_if_step_size];
uint32_t SampleRate;
};
class RX999Radio : public RadioHardware {
public:
RX999Radio(fx3class* fx3);
const char* getName() override { return "RX999"; }
float getGain() override { return RX888_GAINFACTOR; }
rf_mode PrepareLo(uint64_t freq) override;
void Initialize(uint32_t samplefreq) override;
bool UpdatemodeRF(rf_mode mode) override;
uint64_t TuneLo(uint64_t freq) override;
bool UpdateattRF(int attIndex) override;
bool UpdateGainIF(int attIndex) override;
int getRFSteps(const float** steps ) override;
int getIFSteps(const float** steps ) override;
private:
static const int if_step_size = 127;
float if_steps[if_step_size];
uint32_t SampleRate;
};
class HF103Radio : public RadioHardware {
public:
HF103Radio(fx3class* fx3);
const char* getName() override { return "HF103"; }
float getGain() override { return HF103_GAINFACTOR; }
rf_mode PrepareLo(uint64_t freq) override;
void Initialize(uint32_t samplefreq) override {};
bool UpdatemodeRF(rf_mode mode) override;
uint64_t TuneLo(uint64_t freq) override { return 0; }
bool UpdateattRF(int attIndex) override;
int getRFSteps(const float** steps ) override;
private:
static const int step_size = 64;
float steps[step_size];
};
class RXLucyRadio : public RadioHardware {
public:
RXLucyRadio(fx3class* fx3);
const char* getName() override { return "Lucy"; }
float getGain() override { return HF103_GAINFACTOR; }
rf_mode PrepareLo(uint64_t freq) override;
void Initialize(uint32_t samplefreq) override;
bool UpdatemodeRF(rf_mode mode) override;
uint64_t TuneLo(uint64_t freq) override ;
bool UpdateattRF(int attIndex) override;
bool UpdateGainIF(int attIndex) override;
int getRFSteps(const float** steps) override;
int getIFSteps(const float** steps) override;
private:
static const int step_size = 16;
float steps[step_size];
static const int if_step_size = 64;
float if_steps[if_step_size];
uint32_t SampleRate;
};
class DummyRadio : public RadioHardware {
public:
DummyRadio(fx3class* fx3) : RadioHardware(fx3) {}
const char* getName() override { return "Dummy"; }
rf_mode PrepareLo(uint64_t freq) override
{ return HFMODE;}
void Initialize(uint32_t samplefreq) override {}
bool UpdatemodeRF(rf_mode mode) override { return true; }
bool UpdateattRF(int attIndex) override { return true; }
uint64_t TuneLo(uint64_t freq) override {
if (freq < 64000000 /2)
return 0;
else
return freq;
}
};
#endif

View File

@ -0,0 +1,95 @@
#include <string.h>
#include "FX3handler.h"
#include "usb_device.h"
fx3class* CreateUsbHandler()
{
return new fx3handler();
}
fx3handler::fx3handler()
{
}
fx3handler::~fx3handler()
{
}
bool fx3handler::Open(uint8_t* fw_data, uint32_t fw_size)
{
dev = usb_device_open(0, (const char*)fw_data, fw_size);
return dev != nullptr;
}
bool fx3handler::Control(FX3Command command, uint8_t data)
{
return usb_device_control(this->dev, command, 0, 0, (uint8_t *) &data, sizeof(data), 0) == 0;
}
bool fx3handler::Control(FX3Command command, uint32_t data)
{
return usb_device_control(this->dev, command, 0, 0, (uint8_t *) &data, sizeof(data), 0) == 0;
}
bool fx3handler::Control(FX3Command command, uint64_t data)
{
return usb_device_control(this->dev, command, 0, 0, (uint8_t *) &data, sizeof(data), 0) == 0;
}
bool fx3handler::SetArgument(uint16_t index, uint16_t value)
{
uint8_t data = 0;
return usb_device_control(this->dev, SETARGFX3, value, index, (uint8_t *) &data, sizeof(data), 0) == 0;
}
bool fx3handler::GetHardwareInfo(uint32_t* data)
{
return usb_device_control(this->dev, TESTFX3, 0, 0, (uint8_t *) data, sizeof(*data), 1) == 0;
}
void fx3handler::StartStream(ringbuffer<int16_t>& input, int numofblock)
{
inputbuffer = &input;
auto readsize = input.getWriteCount() * sizeof(uint16_t);
stream = streaming_open_async(this->dev, readsize, numofblock, PacketRead, this);
// Start background thread to poll the events
run = true;
poll_thread = std::thread(
[this]() {
while(run)
{
usb_device_handle_events(this->dev);
}
});
if (stream)
{
streaming_start(stream);
}
}
void fx3handler::StopStream()
{
run = false;
poll_thread.join();
streaming_stop(stream);
streaming_close(stream);
}
void fx3handler::PacketRead(uint32_t data_size, uint8_t *data, void *context)
{
fx3handler *handler = (fx3handler*)context;
auto *ptr = handler->inputbuffer->getWritePtr();
memcpy(ptr, data, data_size);
handler->inputbuffer->WriteDone();
}
bool fx3handler::ReadDebugTrace(uint8_t* pdata, uint8_t len)
{
return true;
}

View File

@ -0,0 +1,44 @@
#ifndef FX3HANDLER_H
#define FX3HANDLER_H
#include "sddc_config.h"
#define VENDOR_ID (0x04B4)
#define STREAMER_ID (0x00F1)
#define BOOTLOADER_ID (0x00F3)
#include "FX3Class.h"
#include "usb_device.h"
#include "streaming.h"
#include "dsp/ringbuffer.h"
class fx3handler : public fx3class
{
public:
fx3handler();
virtual ~fx3handler(void);
bool Open(uint8_t* fw_data, uint32_t fw_size) override;
bool Control(FX3Command command, uint8_t data) override;
bool Control(FX3Command command, uint32_t data) override;
bool Control(FX3Command command, uint64_t data) override;
bool SetArgument(uint16_t index, uint16_t value) override;
bool GetHardwareInfo(uint32_t* data) override;
bool ReadDebugTrace(uint8_t* pdata, uint8_t len);
void StartStream(ringbuffer<int16_t>& input, int numofblock);
void StopStream();
private:
bool ReadUsb(uint8_t command, uint16_t value, uint16_t index, uint8_t *data, size_t size);
bool WriteUsb(uint8_t command, uint16_t value, uint16_t index, uint8_t *data, size_t size);
static void PacketRead(uint32_t data_size, uint8_t *data, void *context);
usb_device_t *dev;
streaming_t *stream;
ringbuffer<int16_t> *inputbuffer;
bool run;
std::thread poll_thread;
};
#endif // FX3HANDLER_H

View File

@ -0,0 +1,815 @@
/*
* Copyright © 2001 Stephen Williams (steve@icarus.com)
* Copyright © 2001-2002 David Brownell (dbrownell@users.sourceforge.net)
* Copyright © 2008 Roger Williams (rawqux@users.sourceforge.net)
* Copyright © 2012 Pete Batard (pete@akeo.ie)
* Copyright © 2013 Federico Manzan (f.manzan@gmail.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include "libusb.h"
#include "ezusb.h"
//extern void logerror(const char *format, ...)
// __attribute__ ((format(printf, 1, 2)));
//
void logerror(const char *format, ...)
__attribute__ ((format (__printf__, 1, 2)));
void logerror(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
/*
* This file contains functions for uploading firmware into Cypress
* EZ-USB microcontrollers. These chips use control endpoint 0 and vendor
* specific commands to support writing into the on-chip SRAM. They also
* support writing into the CPUCS register, which is how we reset the
* processor after loading firmware (including the reset vector).
*
* These Cypress devices are 8-bit 8051 based microcontrollers with
* special support for USB I/O. They come in several packages, and
* some can be set up with external memory when device costs allow.
* Note that the design was originally by AnchorChips, so you may find
* references to that vendor (which was later merged into Cypress).
* The Cypress FX parts are largely compatible with the Anchorhip ones.
*/
int verbose = 1;
/*
* return true if [addr,addr+len] includes external RAM
* for Anchorchips EZ-USB or Cypress EZ-USB FX
*/
static bool fx_is_external(uint32_t addr, size_t len)
{
/* with 8KB RAM, 0x0000-0x1b3f can be written
* we can't tell if it's a 4KB device here
*/
if (addr <= 0x1b3f)
return ((addr + len) > 0x1b40);
/* there may be more RAM; unclear if we can write it.
* some bulk buffers may be unused, 0x1b3f-0x1f3f
* firmware can set ISODISAB for 2KB at 0x2000-0x27ff
*/
return true;
}
/*
* return true if [addr,addr+len] includes external RAM
* for Cypress EZ-USB FX2
*/
static bool fx2_is_external(uint32_t addr, size_t len)
{
/* 1st 8KB for data/code, 0x0000-0x1fff */
if (addr <= 0x1fff)
return ((addr + len) > 0x2000);
/* and 512 for data, 0xe000-0xe1ff */
else if (addr >= 0xe000 && addr <= 0xe1ff)
return ((addr + len) > 0xe200);
/* otherwise, it's certainly external */
else
return true;
}
/*
* return true if [addr,addr+len] includes external RAM
* for Cypress EZ-USB FX2LP
*/
static bool fx2lp_is_external(uint32_t addr, size_t len)
{
/* 1st 16KB for data/code, 0x0000-0x3fff */
if (addr <= 0x3fff)
return ((addr + len) > 0x4000);
/* and 512 for data, 0xe000-0xe1ff */
else if (addr >= 0xe000 && addr <= 0xe1ff)
return ((addr + len) > 0xe200);
/* otherwise, it's certainly external */
else
return true;
}
/*****************************************************************************/
/*
* These are the requests (bRequest) that the bootstrap loader is expected
* to recognize. The codes are reserved by Cypress, and these values match
* what EZ-USB hardware, or "Vend_Ax" firmware (2nd stage loader) uses.
* Cypress' "a3load" is nice because it supports both FX and FX2, although
* it doesn't have the EEPROM support (subset of "Vend_Ax").
*/
#define RW_INTERNAL 0xA0 /* hardware implements this one */
#define RW_MEMORY 0xA3
/*
* Issues the specified vendor-specific write request.
*/
static int ezusb_write(libusb_device_handle *device, const char *label,
uint8_t opcode, uint32_t addr, const unsigned char *data, size_t len)
{
int status;
if (verbose > 1)
logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len);
status = libusb_control_transfer(device,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
opcode, addr & 0xFFFF, addr >> 16,
(unsigned char*)data, (uint16_t)len, 1000);
if (status != (signed)len) {
if (status < 0)
logerror("%s: %s\n", label, libusb_error_name(status));
else
logerror("%s ==> %d\n", label, status);
}
return (status < 0) ? -EIO : 0;
}
/*
* Issues the specified vendor-specific read request.
*/
static int ezusb_read(libusb_device_handle *device, const char *label,
uint8_t opcode, uint32_t addr, const unsigned char *data, size_t len)
{
int status;
if (verbose > 1)
logerror("%s, addr 0x%08x len %4u (0x%04x)\n", label, addr, (unsigned)len, (unsigned)len);
status = libusb_control_transfer(device,
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
opcode, addr & 0xFFFF, addr >> 16,
(unsigned char*)data, (uint16_t)len, 1000);
if (status != (signed)len) {
if (status < 0)
logerror("%s: %s\n", label, libusb_error_name(status));
else
logerror("%s ==> %d\n", label, status);
}
return (status < 0) ? -EIO : 0;
}
/*
* Modifies the CPUCS register to stop or reset the CPU.
* Returns false on error.
*/
static bool ezusb_cpucs(libusb_device_handle *device, uint32_t addr, bool doRun)
{
int status;
uint8_t data = doRun ? 0x00 : 0x01;
if (verbose)
logerror("%s\n", data ? "stop CPU" : "reset CPU");
status = libusb_control_transfer(device,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
RW_INTERNAL, addr & 0xFFFF, addr >> 16,
&data, 1, 1000);
if ((status != 1) &&
/* We may get an I/O error from libusb as the device disappears */
((!doRun) || (status != LIBUSB_ERROR_IO)))
{
const char *mesg = "can't modify CPUCS";
if (status < 0)
logerror("%s: %s\n", mesg, libusb_error_name(status));
else
logerror("%s\n", mesg);
return false;
} else
return true;
}
/*
* Send an FX3 jumpt to address command
* Returns false on error.
*/
static bool ezusb_fx3_jump(libusb_device_handle *device, uint32_t addr)
{
int status;
if (verbose)
logerror("transfer execution to Program Entry at 0x%08x\n", addr);
status = libusb_control_transfer(device,
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
RW_INTERNAL, addr & 0xFFFF, addr >> 16,
NULL, 0, 1000);
/* We may get an I/O error from libusb as the device disappears */
if ((status != 0) && (status != LIBUSB_ERROR_IO))
{
const char *mesg = "failed to send jump command";
if (status < 0)
logerror("%s: %s\n", mesg, libusb_error_name(status));
else
logerror("%s\n", mesg);
return false;
} else
return true;
}
/*****************************************************************************/
/*
* Parse an Intel HEX image file and invoke the poke() function on the
* various segments to implement policies such as writing to RAM (with
* a one or two stage loader setup, depending on the firmware) or to
* EEPROM (two stages required).
*
* image - the hex image file
* context - for use by poke()
* is_external - if non-null, used to check which segments go into
* external memory (writable only by software loader)
* poke - called with each memory segment; errors indicated
* by returning negative values.
*
* Caller is responsible for halting CPU as needed, such as when
* overwriting a second stage loader.
*/
static int parse_ihex(FILE *image, void *context,
bool (*is_external)(uint32_t addr, size_t len),
int (*poke) (void *context, uint32_t addr, bool external,
const unsigned char *data, size_t len))
{
unsigned char data[1023];
uint32_t data_addr = 0;
size_t data_len = 0;
int rc;
int first_line = 1;
bool external = false;
/* Read the input file as an IHEX file, and report the memory segments
* as we go. Each line holds a max of 16 bytes, but uploading is
* faster (and EEPROM space smaller) if we merge those lines into larger
* chunks. Most hex files keep memory segments together, which makes
* such merging all but free. (But it may still be worth sorting the
* hex files to make up for undesirable behavior from tools.)
*
* Note that EEPROM segments max out at 1023 bytes; the upload protocol
* allows segments of up to 64 KBytes (more than a loader could handle).
*/
for (;;) {
char buf[512], *cp;
char tmp, type;
size_t len;
unsigned idx, off;
cp = fgets(buf, sizeof(buf), image);
if (cp == NULL) {
logerror("EOF without EOF record!\n");
break;
}
/* EXTENSION: "# comment-till-end-of-line", for copyrights etc */
if (buf[0] == '#')
continue;
if (buf[0] != ':') {
logerror("not an ihex record: %s", buf);
return -2;
}
/* ignore any newline */
cp = strchr(buf, '\n');
if (cp)
*cp = 0;
if (verbose >= 3)
logerror("** LINE: %s\n", buf);
/* Read the length field (up to 16 bytes) */
tmp = buf[3];
buf[3] = 0;
len = strtoul(buf+1, NULL, 16);
buf[3] = tmp;
/* Read the target offset (address up to 64KB) */
tmp = buf[7];
buf[7] = 0;
off = (unsigned int)strtoul(buf+3, NULL, 16);
buf[7] = tmp;
/* Initialize data_addr */
if (first_line) {
data_addr = off;
first_line = 0;
}
/* Read the record type */
tmp = buf[9];
buf[9] = 0;
type = (char)strtoul(buf+7, NULL, 16);
buf[9] = tmp;
/* If this is an EOF record, then make it so. */
if (type == 1) {
if (verbose >= 2)
logerror("EOF on hexfile\n");
break;
}
if (type != 0) {
logerror("unsupported record type: %u\n", type);
return -3;
}
if ((len * 2) + 11 > strlen(buf)) {
logerror("record too short?\n");
return -4;
}
/* FIXME check for _physically_ contiguous not just virtually
* e.g. on FX2 0x1f00-0x2100 includes both on-chip and external
* memory so it's not really contiguous */
/* flush the saved data if it's not contiguous,
* or when we've buffered as much as we can.
*/
if (data_len != 0
&& (off != (data_addr + data_len)
/* || !merge */
|| (data_len + len) > sizeof(data))) {
if (is_external)
external = is_external(data_addr, data_len);
rc = poke(context, data_addr, external, data, data_len);
if (rc < 0)
return -1;
data_addr = off;
data_len = 0;
}
/* append to saved data, flush later */
for (idx = 0, cp = buf+9 ; idx < len ; idx += 1, cp += 2) {
tmp = cp[2];
cp[2] = 0;
data[data_len + idx] = (uint8_t)strtoul(cp, NULL, 16);
cp[2] = tmp;
}
data_len += len;
}
/* flush any data remaining */
if (data_len != 0) {
if (is_external)
external = is_external(data_addr, data_len);
rc = poke(context, data_addr, external, data, data_len);
if (rc < 0)
return -1;
}
return 0;
}
/*
* Parse a binary image file and write it as is to the target.
* Applies to Cypress BIX images for RAM or Cypress IIC images
* for EEPROM.
*
* image - the BIX image file
* context - for use by poke()
* is_external - if non-null, used to check which segments go into
* external memory (writable only by software loader)
* poke - called with each memory segment; errors indicated
* by returning negative values.
*
* Caller is responsible for halting CPU as needed, such as when
* overwriting a second stage loader.
*/
static int parse_bin(FILE *image, void *context,
bool (*is_external)(uint32_t addr, size_t len), int (*poke)(void *context,
uint32_t addr, bool external, const unsigned char *data, size_t len))
{
unsigned char data[4096];
uint32_t data_addr = 0;
size_t data_len = 0;
int rc;
bool external = false;
for (;;) {
data_len = fread(data, 1, 4096, image);
if (data_len == 0)
break;
if (is_external)
external = is_external(data_addr, data_len);
rc = poke(context, data_addr, external, data, data_len);
if (rc < 0)
return -1;
data_addr += (uint32_t)data_len;
}
return feof(image)?0:-1;
}
/*
* Parse a Cypress IIC image file and invoke the poke() function on the
* various segments for writing to RAM
*
* image - the IIC image file
* context - for use by poke()
* is_external - if non-null, used to check which segments go into
* external memory (writable only by software loader)
* poke - called with each memory segment; errors indicated
* by returning negative values.
*
* Caller is responsible for halting CPU as needed, such as when
* overwriting a second stage loader.
*/
static int parse_iic(FILE *image, void *context,
bool (*is_external)(uint32_t addr, size_t len),
int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len))
{
unsigned char data[4096];
uint32_t data_addr = 0;
size_t data_len = 0, read_len;
uint8_t block_header[4];
int rc;
bool external = false;
long file_size, initial_pos;
initial_pos = ftell(image);
if (initial_pos < 0)
return -1;
if (fseek(image, 0L, SEEK_END) != 0)
return -1;
file_size = ftell(image);
if (fseek(image, initial_pos, SEEK_SET) != 0)
return -1;
for (;;) {
/* Ignore the trailing reset IIC data (5 bytes) */
if (ftell(image) >= (file_size - 5))
break;
if (fread(&block_header, 1, sizeof(block_header), image) != 4) {
logerror("unable to read IIC block header\n");
return -1;
}
data_len = (block_header[0] << 8) + block_header[1];
data_addr = (block_header[2] << 8) + block_header[3];
if (data_len > sizeof(data)) {
/* If this is ever reported as an error, switch to using malloc/realloc */
logerror("IIC data block too small - please report this error to libusb.info\n");
return -1;
}
read_len = fread(data, 1, data_len, image);
if (read_len != data_len) {
logerror("read error\n");
return -1;
}
if (is_external)
external = is_external(data_addr, data_len);
rc = poke(context, data_addr, external, data, data_len);
if (rc < 0)
return -1;
}
return 0;
}
/* the parse call will be selected according to the image type */
static int (*parse[IMG_TYPE_MAX])(FILE *image, void *context, bool (*is_external)(uint32_t addr, size_t len),
int (*poke)(void *context, uint32_t addr, bool external, const unsigned char *data, size_t len))
= { parse_ihex, parse_iic, parse_bin };
/*****************************************************************************/
/*
* For writing to RAM using a first (hardware) or second (software)
* stage loader and 0xA0 or 0xA3 vendor requests
*/
typedef enum {
_undef = 0,
internal_only, /* hardware first-stage loader */
skip_internal, /* first phase, second-stage loader */
skip_external /* second phase, second-stage loader */
} ram_mode;
struct ram_poke_context {
libusb_device_handle *device;
ram_mode mode;
size_t total, count;
};
#define RETRY_LIMIT 5
static int ram_poke(void *context, uint32_t addr, bool external,
const unsigned char *data, size_t len)
{
struct ram_poke_context *ctx = (struct ram_poke_context*)context;
int rc;
unsigned retry = 0;
switch (ctx->mode) {
case internal_only: /* CPU should be stopped */
if (external) {
logerror("can't write %u bytes external memory at 0x%08x\n",
(unsigned)len, addr);
return -EINVAL;
}
break;
case skip_internal: /* CPU must be running */
if (!external) {
if (verbose >= 2) {
logerror("SKIP on-chip RAM, %u bytes at 0x%08x\n",
(unsigned)len, addr);
}
return 0;
}
break;
case skip_external: /* CPU should be stopped */
if (external) {
if (verbose >= 2) {
logerror("SKIP external RAM, %u bytes at 0x%08x\n",
(unsigned)len, addr);
}
return 0;
}
break;
case _undef:
default:
logerror("bug\n");
return -EDOM;
}
ctx->total += len;
ctx->count++;
/* Retry this till we get a real error. Control messages are not
* NAKed (just dropped) so time out means is a real problem.
*/
while ((rc = ezusb_write(ctx->device,
external ? "write external" : "write on-chip",
external ? RW_MEMORY : RW_INTERNAL,
addr, data, len)) < 0
&& retry < RETRY_LIMIT) {
if (rc != LIBUSB_ERROR_TIMEOUT)
break;
retry += 1;
}
return rc;
}
/*
* Load a Cypress Image file into target RAM.
* See http://www.cypress.com/?docID=41351 (AN76405 PDF) for more info.
*/
int fx3_load_ram(libusb_device_handle *device, const char *image)
{
uint32_t dCheckSum, dExpectedCheckSum, dAddress, i, dLen, dLength;
uint32_t* dImageBuf;
const unsigned char *bBuf, *hBuf;
unsigned char blBuf[4], rBuf[4096];
int ret = 0;
int offset = 0;
hBuf = image;
offset += 4;
// check "CY" signature byte and format
if ((hBuf[0] != 'C') || (hBuf[1] != 'Y')) {
logerror("image doesn't have a CYpress signature\n");
ret = -3;
goto exit;
}
// Check bImageType
switch(hBuf[3]) {
case 0xB0:
if (verbose)
logerror("normal FW binary %s image with checksum\n", (hBuf[2]&0x01)?"data":"executable");
break;
case 0xB1:
logerror("security binary image is not currently supported\n");
ret = -3;
goto exit;
case 0xB2:
logerror("VID:PID image is not currently supported\n");
ret = -3;
goto exit;
default:
logerror("invalid image type 0x%02X\n", hBuf[3]);
ret = -3;
goto exit;
}
// Read the bootloader version
if (verbose) {
if ((ezusb_read(device, "read bootloader version", RW_INTERNAL, 0xFFFF0020, blBuf, 4) < 0)) {
logerror("Could not read bootloader version\n");
ret = -8;
goto exit;
}
logerror("FX3 bootloader version: 0x%02X%02X%02X%02X\n", blBuf[3], blBuf[2], blBuf[1], blBuf[0]);
}
dCheckSum = 0;
if (verbose)
logerror("writing image...\n");
while (1) {
dLength = *(uint32_t*)(image+offset); offset += sizeof(uint32_t);
dAddress = *(uint32_t*)(image+offset); offset += sizeof(uint32_t);
if (dLength == 0)
break; // done
// read sections
dImageBuf = (uint32_t*)(image+offset); offset += sizeof(uint32_t) * dLength;
for (i = 0; i < dLength; i++)
dCheckSum += dImageBuf[i];
dLength <<= 2; // convert to Byte length
bBuf = (unsigned char*) dImageBuf;
while (dLength > 0) {
dLen = 4096; // 4K max
if (dLen > dLength)
dLen = dLength;
if ((ezusb_write(device, "write firmware", RW_INTERNAL, dAddress, bBuf, dLen) < 0) ||
(ezusb_read(device, "read firmware", RW_INTERNAL, dAddress, rBuf, dLen) < 0)) {
logerror("R/W error\n");
ret = -5;
goto exit;
}
// Verify data: rBuf with bBuf
for (i = 0; i < dLen; i++) {
if (rBuf[i] != bBuf[i]) {
logerror("verify error");
ret = -6;
goto exit;
}
}
dLength -= dLen;
bBuf += dLen;
dAddress += dLen;
}
}
// read pre-computed checksum data
dExpectedCheckSum = *(uint32_t*)(image + offset); offset += sizeof(uint32_t);
if (dCheckSum != dExpectedCheckSum) {
logerror("checksum error\n");
ret = -7;
goto exit;
}
// transfer execution to Program Entry
if (!ezusb_fx3_jump(device, dAddress)) {
ret = -6;
}
exit:
return ret;
}
/*
* Load a firmware file into target RAM. device is the open libusb
* device, and the path is the name of the source file. Open the file,
* parse the bytes, and write them in one or two phases.
*
* If stage == 0, this uses the first stage loader, built into EZ-USB
* hardware but limited to writing on-chip memory or CPUCS. Everything
* is written during one stage, unless there's an error such as the image
* holding data that needs to be written to external memory.
*
* Otherwise, things are written in two stages. First the external
* memory is written, expecting a second stage loader to have already
* been loaded. Then file is re-parsed and on-chip memory is written.
*/
int ezusb_load_ram(libusb_device_handle *device, const char *path, int fx_type, int img_type, int stage)
{
FILE *image;
uint32_t cpucs_addr;
bool (*is_external)(uint32_t off, size_t len);
struct ram_poke_context ctx;
int status;
uint8_t iic_header[8] = { 0 };
int ret = 0;
if (fx_type == FX_TYPE_FX3)
return fx3_load_ram(device, path);
image = fopen(path, "rb");
if (image == NULL) {
logerror("%s: unable to open for input.\n", path);
return -2;
} else if (verbose > 1)
logerror("open firmware image %s for RAM upload\n", path);
if (img_type == IMG_TYPE_IIC) {
if ( (fread(iic_header, 1, sizeof(iic_header), image) != sizeof(iic_header))
|| (((fx_type == FX_TYPE_FX2LP) || (fx_type == FX_TYPE_FX2)) && (iic_header[0] != 0xC2))
|| ((fx_type == FX_TYPE_AN21) && (iic_header[0] != 0xB2))
|| ((fx_type == FX_TYPE_FX1) && (iic_header[0] != 0xB6)) ) {
logerror("IIC image does not contain executable code - cannot load to RAM.\n");
ret = -1;
goto exit;
}
}
/* EZ-USB original/FX and FX2 devices differ, apart from the 8051 core */
switch(fx_type) {
case FX_TYPE_FX2LP:
cpucs_addr = 0xe600;
is_external = fx2lp_is_external;
break;
case FX_TYPE_FX2:
cpucs_addr = 0xe600;
is_external = fx2_is_external;
break;
default:
cpucs_addr = 0x7f92;
is_external = fx_is_external;
break;
}
/* use only first stage loader? */
if (stage == 0) {
ctx.mode = internal_only;
/* if required, halt the CPU while we overwrite its code/data */
if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false))
{
ret = -1;
goto exit;
}
/* 2nd stage, first part? loader was already uploaded */
} else {
ctx.mode = skip_internal;
/* let CPU run; overwrite the 2nd stage loader later */
if (verbose)
logerror("2nd stage: write external memory\n");
}
/* scan the image, first (maybe only) time */
ctx.device = device;
ctx.total = ctx.count = 0;
status = parse[img_type](image, &ctx, is_external, ram_poke);
if (status < 0) {
logerror("unable to upload %s\n", path);
ret = status;
goto exit;
}
/* second part of 2nd stage: rescan */
// TODO: what should we do for non HEX images there?
if (stage) {
ctx.mode = skip_external;
/* if needed, halt the CPU while we overwrite the 1st stage loader */
if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, false))
{
ret = -1;
goto exit;
}
/* at least write the interrupt vectors (at 0x0000) for reset! */
rewind(image);
if (verbose)
logerror("2nd stage: write on-chip memory\n");
status = parse_ihex(image, &ctx, is_external, ram_poke);
if (status < 0) {
logerror("unable to completely upload %s\n", path);
ret = status;
goto exit;
}
}
if (verbose && (ctx.count != 0)) {
logerror("... WROTE: %d bytes, %d segments, avg %d\n",
(int)ctx.total, (int)ctx.count, (int)(ctx.total/ctx.count));
}
/* if required, reset the CPU so it runs what we just uploaded */
if (cpucs_addr && !ezusb_cpucs(device, cpucs_addr, true))
ret = -1;
exit:
fclose(image);
return ret;
}

View File

@ -0,0 +1,114 @@
#ifndef ezusb_H
#define ezusb_H
/*
* Copyright © 2001 Stephen Williams (steve@icarus.com)
* Copyright © 2002 David Brownell (dbrownell@users.sourceforge.net)
* Copyright © 2013 Federico Manzan (f.manzan@gmail.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#if defined(_MSC_VER)
#define __attribute__(x)
#if defined(_PREFAST_)
#pragma warning(disable:28193)
#endif
#endif
#include <stdbool.h>
#define FX_TYPE_UNDEFINED -1
#define FX_TYPE_AN21 0 /* Original AnchorChips parts */
#define FX_TYPE_FX1 1 /* Updated Cypress versions */
#define FX_TYPE_FX2 2 /* USB 2.0 versions */
#define FX_TYPE_FX2LP 3 /* Updated FX2 */
#define FX_TYPE_FX3 4 /* USB 3.0 versions */
#define FX_TYPE_MAX 5
#define FX_TYPE_NAMES { "an21", "fx", "fx2", "fx2lp", "fx3" }
#define IMG_TYPE_UNDEFINED -1
#define IMG_TYPE_HEX 0 /* Intel HEX */
#define IMG_TYPE_IIC 1 /* Cypress 8051 IIC */
#define IMG_TYPE_BIX 2 /* Cypress 8051 BIX */
#define IMG_TYPE_IMG 3 /* Cypress IMG format */
#define IMG_TYPE_MAX 4
#define IMG_TYPE_NAMES { "Intel HEX", "Cypress 8051 IIC", "Cypress 8051 BIX", "Cypress IMG format" }
#ifdef __cplusplus
extern "C" {
#endif
/*
* Automatically identified devices (VID, PID, type, designation).
* TODO: Could use some validation. Also where's the FX2?
*/
typedef struct {
uint16_t vid;
uint16_t pid;
int type;
const char* designation;
} fx_known_device;
#define FX_KNOWN_DEVICES { \
{ 0x0547, 0x2122, FX_TYPE_AN21, "Cypress EZ-USB (2122S)" },\
{ 0x0547, 0x2125, FX_TYPE_AN21, "Cypress EZ-USB (2121S/2125S)" },\
{ 0x0547, 0x2126, FX_TYPE_AN21, "Cypress EZ-USB (2126S)" },\
{ 0x0547, 0x2131, FX_TYPE_AN21, "Cypress EZ-USB (2131Q/2131S/2135S)" },\
{ 0x0547, 0x2136, FX_TYPE_AN21, "Cypress EZ-USB (2136S)" },\
{ 0x0547, 0x2225, FX_TYPE_AN21, "Cypress EZ-USB (2225)" },\
{ 0x0547, 0x2226, FX_TYPE_AN21, "Cypress EZ-USB (2226)" },\
{ 0x0547, 0x2235, FX_TYPE_AN21, "Cypress EZ-USB (2235)" },\
{ 0x0547, 0x2236, FX_TYPE_AN21, "Cypress EZ-USB (2236)" },\
{ 0x04b4, 0x6473, FX_TYPE_FX1, "Cypress EZ-USB FX1" },\
{ 0x04b4, 0x8613, FX_TYPE_FX2LP, "Cypress EZ-USB FX2LP (68013A/68014A/68015A/68016A)" }, \
{ 0x04b4, 0x00f3, FX_TYPE_FX3, "Cypress FX3" },\
}
/*
* This function uploads the firmware from the given file into RAM.
* Stage == 0 means this is a single stage load (or the first of
* two stages). Otherwise it's the second of two stages; the
* caller having preloaded the second stage loader.
*
* The target processor is reset at the end of this upload.
*/
extern int ezusb_load_ram(libusb_device_handle *device,
const char *path, int fx_type, int img_type, int stage);
extern int fx3_load_ram(libusb_device_handle *device, const char *image);
/*
* This function uploads the firmware from the given file into EEPROM.
* This uses the right CPUCS address to terminate the EEPROM load with
* a reset command where FX parts behave differently than FX2 ones.
* The configuration byte is as provided here (zero for an21xx parts)
* and the EEPROM type is set so that the microcontroller will boot
* from it.
*
* The caller must have preloaded a second stage loader that knows
* how to respond to the EEPROM write request.
*/
extern int ezusb_load_eeprom(libusb_device_handle *device,
const char *path, int fx_type, int img_type, int config);
/* Verbosity level (default 1). Can be increased or decreased with options v/q */
extern int verbose;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,47 @@
/*
* logging.c - logging functions
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdio.h>
#include <libusb.h>
#include "logging.h"
void log_error(const char *error_message, const char *function,
const char *file, int line) {
fprintf(stderr, "ERROR - %s in %s at %s:%d\n", error_message, function,
file, line);
return;
}
void log_usb_error(int usb_error_code, const char *function, const char *file,
int line) {
fprintf(stderr, "ERROR - USB error %s in %s at %s:%d\n",
libusb_error_name(usb_error_code), function, file, line);
return;
}
void log_usb_warning(int usb_error_code, const char *function, const char *file,
int line) {
fprintf(stderr, "WARNING - USB warning %s in %s at %s:%d\n",
libusb_error_name(usb_error_code), function, file, line);
return;
}

View File

@ -0,0 +1,43 @@
/*
* logging.h - logging functions
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __LOGGING_H
#define __LOGGING_H
#include <libusb.h>
#ifdef __cplusplus
extern "C" {
#endif
void log_error(const char *error_message, const char *function,
const char *file, int line);
void log_usb_error(int usb_error_code, const char *function, const char *file,
int line);
void log_usb_warning(int usb_error_code, const char *function, const char *file,
int line);
#ifdef __cplusplus
}
#endif
#endif /* __LOGGING_H */

View File

@ -0,0 +1,387 @@
/*
* streaming.c - streaming functions
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/* References:
* - librtlsdr.c: https://github.com/librtlsdr/librtlsdr/blob/development/src/librtlsdr.c
* - Ettus Research UHD libusb1_zero_copy.cpp: https://github.com/EttusResearch/uhd/blob/master/host/lib/transport/libusb1_zero_copy.cpp
*/
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdatomic.h>
#include "streaming.h"
#include "usb_device.h"
#include "usb_device_internals.h"
#include "logging.h"
typedef struct streaming streaming_t;
/* internal functions */
static void streaming_read_async_callback(struct libusb_transfer *transfer);
enum StreamingStatus {
STREAMING_STATUS_OFF,
STREAMING_STATUS_READY,
STREAMING_STATUS_STREAMING,
STREAMING_STATUS_CANCELLED,
STREAMING_STATUS_FAILED = 0xff
};
typedef struct streaming {
enum StreamingStatus status;
int random;
usb_device_t *usb_device;
uint32_t sample_rate;
uint32_t frame_size;
uint32_t num_frames;
sddc_read_async_cb_t callback;
void *callback_context;
uint8_t **frames;
struct libusb_transfer **transfers;
atomic_int active_transfers;
} streaming_t;
static const uint32_t DEFAULT_SAMPLE_RATE = 64000000; /* 64Msps */
static const uint32_t DEFAULT_FRAME_SIZE = (2 * 64000000 / 1000); /* ~ 1 ms */
static const uint32_t DEFAULT_NUM_FRAMES = 96; /* we should not exceed 120 ms in total! */
const unsigned int BULK_XFER_TIMEOUT = 5000; // timeout (in ms) for each bulk transfer
streaming_t *streaming_open_sync(usb_device_t *usb_device)
{
streaming_t *ret_val = 0;
/* we must have a bulk in device to transfer data from */
if (usb_device->bulk_in_endpoint_address == 0) {
log_error("no USB bulk in endpoint found", __func__, __FILE__, __LINE__);
return ret_val;
}
/* we are good here - create and initialize the streaming */
streaming_t *this = (streaming_t *) malloc(sizeof(streaming_t));
this->status = STREAMING_STATUS_READY;
this->random = 0;
this->usb_device = usb_device;
this->sample_rate = DEFAULT_SAMPLE_RATE;
this->frame_size = 0;
this->num_frames = 0;
this->callback = 0;
this->callback_context = 0;
this->frames = 0;
this->transfers = 0;
atomic_init(&this->active_transfers, 0);
ret_val = this;
return ret_val;
}
streaming_t *streaming_open_async(usb_device_t *usb_device, uint32_t frame_size,
uint32_t num_frames, sddc_read_async_cb_t callback,
void *callback_context)
{
streaming_t *ret_val = 0;
/* we must have a bulk in device to transfer data from */
if (usb_device->bulk_in_endpoint_address == 0) {
log_error("no USB bulk in endpoint found", __func__, __FILE__, __LINE__);
return ret_val;
}
/* frame size must be a multiple of max_packet_size * max_burst */
uint32_t max_xfer_size = usb_device->bulk_in_max_packet_size *
usb_device->bulk_in_max_burst;
if ( !max_xfer_size ) {
fprintf(stderr, "ERROR: maximum transfer size is 0. probably not connected at USB 3 port?!\n");
return ret_val;
}
num_frames = num_frames > 0 ? num_frames : DEFAULT_NUM_FRAMES;
frame_size = frame_size > 0 ? frame_size : DEFAULT_FRAME_SIZE;
frame_size = max_xfer_size * ((frame_size +max_xfer_size -1) / max_xfer_size); // round up
int iso_packets_per_frame = frame_size / usb_device->bulk_in_max_packet_size;
fprintf(stderr, "frame_size = %u, iso_packets_per_frame = %d\n", (unsigned)frame_size, iso_packets_per_frame);
if (frame_size % max_xfer_size != 0) {
fprintf(stderr, "frame size must be a multiple of %d\n", max_xfer_size);
return ret_val;
}
/* allocate frames for zerocopy USB bulk transfers */
uint8_t **frames = (uint8_t **) malloc(num_frames * sizeof(uint8_t *));
for (uint32_t i = 0; i < num_frames; ++i) {
frames[i] = libusb_dev_mem_alloc(usb_device->dev_handle, frame_size);
if (frames[i] == 0) {
log_error("libusb_dev_mem_alloc() failed", __func__, __FILE__, __LINE__);
for (uint32_t j = 0; j < i; j++) {
libusb_dev_mem_free(usb_device->dev_handle, frames[j], frame_size);
}
return ret_val;
}
}
/* we are good here - create and initialize the streaming */
streaming_t *this = (streaming_t *) malloc(sizeof(streaming_t));
this->status = STREAMING_STATUS_READY;
this->random = 0;
this->usb_device = usb_device;
this->sample_rate = DEFAULT_SAMPLE_RATE;
this->frame_size = frame_size > 0 ? frame_size : DEFAULT_FRAME_SIZE;
this->num_frames = num_frames > 0 ? num_frames : DEFAULT_NUM_FRAMES;
this->callback = callback;
this->callback_context = callback_context;
this->frames = frames;
/* populate the required libusb_transfer fields */
struct libusb_transfer **transfers = (struct libusb_transfer **) malloc(num_frames * sizeof(struct libusb_transfer *));
for (uint32_t i = 0; i < num_frames; ++i) {
transfers[i] = libusb_alloc_transfer(0); // iso_packets_per_frame ?
libusb_fill_bulk_transfer(transfers[i], usb_device->dev_handle,
usb_device->bulk_in_endpoint_address,
frames[i], frame_size, streaming_read_async_callback,
this, BULK_XFER_TIMEOUT);
}
this->transfers = transfers;
atomic_init(&this->active_transfers, 0);
ret_val = this;
return ret_val;
}
void streaming_close(streaming_t *this)
{
if (this->transfers) {
for (uint32_t i = 0; i < this->num_frames; ++i) {
libusb_free_transfer(this->transfers[i]);
}
free(this->transfers);
}
if (this->frames != 0) {
for (uint32_t i = 0; i < this->num_frames; ++i) {
libusb_dev_mem_free(this->usb_device->dev_handle, this->frames[i],
this->frame_size);
}
free(this->frames);
}
free(this);
return;
}
int streaming_set_sample_rate(streaming_t *this, uint32_t sample_rate)
{
/* no checks yet */
this->sample_rate = sample_rate;
return 0;
}
int streaming_set_random(streaming_t *this, int random)
{
this->random = random;
return 0;
}
int streaming_start(streaming_t *this)
{
if (this->status != STREAMING_STATUS_READY) {
fprintf(stderr, "ERROR - streaming_start() called with streaming status not READY: %d\n", this->status);
return -1;
}
/* if there is no callback, then streaming is synchronous - nothing to do */
if (this->callback == 0) {
this->status = STREAMING_STATUS_STREAMING;
return 0;
}
/* submit all the transfers */
atomic_init(&this->active_transfers, 0);
for (uint32_t i = 0; i < this->num_frames; ++i) {
int ret = libusb_submit_transfer(this->transfers[i]);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
this->status = STREAMING_STATUS_FAILED;
return -1;
}
atomic_fetch_add(&this->active_transfers, 1);
}
this->status = STREAMING_STATUS_STREAMING;
return 0;
}
int streaming_stop(streaming_t *this)
{
/* if there is no callback, then streaming is synchronous - nothing to do */
if (this->callback == 0) {
if (this->status == STREAMING_STATUS_STREAMING) {
this->status = STREAMING_STATUS_READY;
}
return 0;
}
this->status = STREAMING_STATUS_CANCELLED;
/* cancel all the active transfers */
for (uint32_t i = 0; i < this->num_frames; ++i) {
int ret = libusb_cancel_transfer(this->transfers[i]);
if (ret < 0) {
if (ret == LIBUSB_ERROR_NOT_FOUND) {
continue;
}
log_usb_error(ret, __func__, __FILE__, __LINE__);
this->status = STREAMING_STATUS_FAILED;
}
}
/* flush all the events */
struct timeval noblock = { 0, 0 };
int ret = libusb_handle_events_timeout_completed(this->usb_device->context, &noblock, 0);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
this->status = STREAMING_STATUS_FAILED;
}
return 0;
}
int streaming_reset_status(streaming_t *this)
{
switch (this->status) {
case STREAMING_STATUS_READY:
/* nothing to do here */
return 0;
case STREAMING_STATUS_CANCELLED:
case STREAMING_STATUS_FAILED:
if (this->active_transfers > 0) {
fprintf(stderr, "ERROR - streaming_reset_status() called with %d transfers still active\n",
this->active_transfers);
return -1;
}
break;
default:
fprintf(stderr, "ERROR - streaming_reset_status() called with invalid status: %d\n",
this->status);
return -1;
}
/* we are good here; reset the status */
this->status = STREAMING_STATUS_READY;
return 0;
}
int streaming_read_sync(streaming_t *this, uint8_t *data, int length, int *transferred)
{
int ret = libusb_bulk_transfer(this->usb_device->dev_handle,
this->usb_device->bulk_in_endpoint_address,
data, length, transferred, BULK_XFER_TIMEOUT);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
return -1;
}
/* remove ADC randomization */
if (this->random) {
uint16_t *samples = (uint16_t *) data;
int n = *transferred / 2;
for (int i = 0; i < n; ++i) {
if (samples[i] & 1) {
samples[i] ^= 0xfffe;
}
}
}
return 0;
}
/* internal functions */
static void LIBUSB_CALL streaming_read_async_callback(struct libusb_transfer *transfer)
{
streaming_t *this = (streaming_t *) transfer->user_data;
int ret;
switch (transfer->status) {
case LIBUSB_TRANSFER_COMPLETED:
/* success!!! */
if (this->status == STREAMING_STATUS_STREAMING) {
/* remove ADC randomization */
if (this->random) {
uint16_t *samples = (uint16_t *) transfer->buffer;
int n = transfer->actual_length / 2;
for (int i = 0; i < n; ++i) {
if (samples[i] & 1) {
samples[i] ^= 0xfffe;
}
}
}
this->callback(transfer->actual_length, transfer->buffer,
this->callback_context);
ret = libusb_submit_transfer(transfer);
if (ret == 0) {
return;
}
log_usb_error(ret, __func__, __FILE__, __LINE__);
}
break;
case LIBUSB_TRANSFER_CANCELLED:
/* librtlsdr does also ignore LIBUSB_TRANSFER_CANCELLED */
return;
case LIBUSB_TRANSFER_ERROR:
case LIBUSB_TRANSFER_TIMED_OUT:
case LIBUSB_TRANSFER_STALL:
case LIBUSB_TRANSFER_NO_DEVICE:
case LIBUSB_TRANSFER_OVERFLOW:
log_usb_error(transfer->status, __func__, __FILE__, __LINE__);
break;
}
this->status = STREAMING_STATUS_FAILED;
atomic_fetch_sub(&this->active_transfers, 1);
fprintf(stderr, "Cancelling\n");
/* cancel all the active transfers */
for (uint32_t i = 0; i < this->num_frames; ++i) {
int ret = libusb_cancel_transfer(transfer);
if (ret < 0) {
if (ret == LIBUSB_ERROR_NOT_FOUND) {
continue;
}
log_usb_error(ret, __func__, __FILE__, __LINE__);
}
}
return;
}

View File

@ -0,0 +1,63 @@
/*
* streaming.h - streaming functions
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __STREAMING_H
#define __STREAMING_H
#include "usb_device.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct streaming streaming_t;
typedef void (*sddc_read_async_cb_t)(uint32_t data_size, uint8_t *data,
void *context);
streaming_t *streaming_open_sync(usb_device_t *usb_device);
streaming_t *streaming_open_async(usb_device_t *usb_device, uint32_t frame_size,
uint32_t num_frames,
sddc_read_async_cb_t callback,
void *callback_context);
void streaming_close(streaming_t *that);
int streaming_set_sample_rate(streaming_t *that, uint32_t sample_rate);
int streaming_set_random(streaming_t *that, int random);
int streaming_start(streaming_t *that);
int streaming_stop(streaming_t *that);
int streaming_reset_status(streaming_t *that);
int streaming_read_sync(streaming_t *that, uint8_t *data, int length,
int *transferred);
#ifdef __cplusplus
}
#endif
#endif /* __STREAMING_H */

View File

@ -0,0 +1,574 @@
/*
* usb_device.c - Basic USB and USB control functions
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/* References:
* - FX3 SDK for Linux Platforms (https://www.cypress.com/documentation/software-and-drivers/ez-usb-fx3-software-development-kit)
* example: cyusb_linux_1.0.5/src/download_fx3.cpp
*/
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libusb.h>
#include "usb_device.h"
#include "usb_device_internals.h"
#include "ezusb.h"
#include "logging.h"
typedef struct usb_device usb_device_t;
/* internal functions */
static libusb_device_handle *find_usb_device(int index, libusb_context *ctx,
libusb_device **device, int *needs_firmware);
static int load_image(libusb_device_handle *dev_handle,
const char *image, uint32_t size);
static int validate_image(const uint8_t *image, const size_t size);
static int transfer_image(const uint8_t *image,
libusb_device_handle *dev_handle);
static int list_endpoints(struct libusb_endpoint_descriptor endpoints[],
struct libusb_ss_endpoint_companion_descriptor ss_endpoints[],
libusb_device *device);
struct usb_device_id {
uint16_t vid;
uint16_t pid;
int needs_firmware;
};
static struct usb_device_id usb_device_ids[] = {
{ 0x04b4, 0x00f3, 1 }, /* Cypress / FX3 Boot-loader */
{ 0x04b4, 0x00f1, 0 } /* Cypress / FX3 Streamer Example */
};
static int n_usb_device_ids = sizeof(usb_device_ids) / sizeof(usb_device_ids[0]);
int usb_device_count_devices()
{
int ret_val = -1;
int ret = libusb_init(0);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL0;
}
libusb_device **list = 0;
ssize_t nusbdevices = libusb_get_device_list(0, &list);
if (nusbdevices < 0) {
log_usb_error(nusbdevices, __func__, __FILE__, __LINE__);
goto FAIL1;
}
int count = 0;
for (ssize_t i = 0; i < nusbdevices; ++i) {
libusb_device *dev = list[i];
struct libusb_device_descriptor desc;
ret = libusb_get_device_descriptor(dev, &desc);
for (int i = 0; i < n_usb_device_ids; ++i) {
if (desc.idVendor == usb_device_ids[i].vid &&
desc.idProduct == usb_device_ids[i].pid) {
count++;
}
}
}
libusb_free_device_list(list, 1);
ret_val = count;
FAIL1:
libusb_exit(0);
FAIL0:
return ret_val;
}
int usb_device_get_device_list(struct usb_device_info **usb_device_infos)
{
const int MAX_STRING_BYTES = 256;
int ret_val = -1;
if (usb_device_infos == 0) {
log_error("argument usb_device_infos is a null pointer", __func__, __FILE__, __LINE__);
goto FAIL0;
}
int ret = libusb_init(0);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL0;
}
libusb_device **list = 0;
ssize_t nusbdevices = libusb_get_device_list(0, &list);
if (nusbdevices < 0) {
log_usb_error(nusbdevices, __func__, __FILE__, __LINE__);
goto FAIL1;
}
struct usb_device_info *device_infos = (struct usb_device_info *) malloc((nusbdevices + 1) * sizeof(struct usb_device_info));
int count = 0;
for (ssize_t j = 0; j < nusbdevices; ++j) {
libusb_device *device = list[j];
struct libusb_device_descriptor desc;
ret = libusb_get_device_descriptor(device, &desc);
for (int i = 0; i < n_usb_device_ids; ++i) {
if (!(desc.idVendor == usb_device_ids[i].vid &&
desc.idProduct == usb_device_ids[i].pid)) {
continue;
}
libusb_device_handle *dev_handle = 0;
ret = libusb_open(device, &dev_handle);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL2;
}
device_infos[count].manufacturer = (unsigned char *) malloc(MAX_STRING_BYTES);
device_infos[count].manufacturer[0] = '\0';
if (desc.iManufacturer) {
ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iManufacturer,
device_infos[count].manufacturer, MAX_STRING_BYTES);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL3;
}
device_infos[count].manufacturer = (unsigned char *) realloc(device_infos[count].manufacturer, ret);
}
device_infos[count].product = (unsigned char *) malloc(MAX_STRING_BYTES);
device_infos[count].product[0] = '\0';
if (desc.iProduct) {
ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iProduct,
device_infos[count].product, MAX_STRING_BYTES);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL3;
}
device_infos[count].product = (unsigned char *) realloc(device_infos[count].product, ret);
}
device_infos[count].serial_number = (unsigned char *) malloc(MAX_STRING_BYTES);
device_infos[count].serial_number[0] = '\0';
if (desc.iSerialNumber) {
ret = libusb_get_string_descriptor_ascii(dev_handle, desc.iSerialNumber,
device_infos[count].serial_number, MAX_STRING_BYTES);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL3;
}
device_infos[count].serial_number = (unsigned char *) realloc(device_infos[count].serial_number, ret);
}
ret = 0;
FAIL3:
libusb_close(dev_handle);
if (ret < 0) {
goto FAIL2;
}
count++;
}
}
device_infos[count].manufacturer = 0;
device_infos[count].product = 0;
device_infos[count].serial_number = 0;
*usb_device_infos = device_infos;
ret_val = count;
FAIL2:
libusb_free_device_list(list, 1);
FAIL1:
libusb_exit(0);
FAIL0:
return ret_val;
}
int usb_device_free_device_list(struct usb_device_info *usb_device_infos)
{
for (struct usb_device_info *udi = usb_device_infos;
udi->manufacturer || udi->product || udi->serial_number;
++udi) {
if (udi->manufacturer) {
free(udi->manufacturer);
}
if (udi->product) {
free(udi->product);
}
if (udi->serial_number) {
free(udi->serial_number);
}
}
free(usb_device_infos);
return 0;
}
usb_device_t *usb_device_open(int index, const char* image,
uint32_t size)
{
usb_device_t *ret_val = 0;
libusb_context *ctx = 0;
int ret = libusb_init(&ctx);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL0;
}
libusb_device *device;
int needs_firmware = 0;
libusb_device_handle *dev_handle = find_usb_device(index, ctx, &device, &needs_firmware);
if (dev_handle == 0) {
goto FAIL1;
}
if (needs_firmware) {
ret = load_image(dev_handle, image, size);
if (ret != 0) {
log_error("load_image() failed", __func__, __FILE__, __LINE__);
goto FAIL2;
}
/* rescan USB to get a new device handle */
libusb_close(dev_handle);
/* wait unitl firmware is ready */
usleep(500 * 1000L);
needs_firmware = 0;
dev_handle = find_usb_device(index, ctx, &device, &needs_firmware);
if (dev_handle == 0) {
goto FAIL1;
}
if (needs_firmware) {
log_error("device is still in boot loader mode", __func__, __FILE__, __LINE__);
goto FAIL2;
}
}
int speed = libusb_get_device_speed(device);
if ( speed == LIBUSB_SPEED_LOW || speed == LIBUSB_SPEED_FULL || speed == LIBUSB_SPEED_HIGH ) {
log_error("USB 3.x SuperSpeed connection failed", __func__, __FILE__, __LINE__);
goto FAIL2;
}
/* list endpoints */
struct libusb_endpoint_descriptor endpoints[MAX_ENDPOINTS];
struct libusb_ss_endpoint_companion_descriptor ss_endpoints[MAX_ENDPOINTS];
ret = list_endpoints(endpoints, ss_endpoints, device);
if (ret < 0) {
log_error("list_endpoints() failed", __func__, __FILE__, __LINE__);
goto FAIL2;
}
int nendpoints = ret;
uint8_t bulk_in_endpoint_address = 0;
uint16_t bulk_in_max_packet_size = 0;
uint8_t bulk_in_max_burst = 0;
for (int i = 0; i < nendpoints; ++i) {
if ((endpoints[i].bmAttributes & 0x03) == LIBUSB_TRANSFER_TYPE_BULK &&
(endpoints[i].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {
bulk_in_endpoint_address = endpoints[i].bEndpointAddress;
bulk_in_max_packet_size = endpoints[i].wMaxPacketSize;
bulk_in_max_burst = ss_endpoints[i].bLength == 0 ? 0 :
ss_endpoints[i].bMaxBurst;
break;
}
}
if (bulk_in_endpoint_address == 0) {
fprintf(stderr, "ERROR - bulk in endpoint not found\n");
goto FAIL2;
}
/* we are good here - create and initialize the usb_device */
usb_device_t *this = (usb_device_t *) malloc(sizeof(usb_device_t));
this->dev = device;
this->dev_handle = dev_handle;
this->context = ctx;
this->completed = 0;
this->nendpoints = nendpoints;
memset(this->endpoints, 0, sizeof(this->endpoints));
for (int i = 0; i < nendpoints; ++i) {
this->endpoints[i] = endpoints[i];
this->ss_endpoints[i] = ss_endpoints[i];
}
this->bulk_in_endpoint_address = bulk_in_endpoint_address;
this->bulk_in_max_packet_size = bulk_in_max_packet_size;
this->bulk_in_max_burst = bulk_in_max_burst;
ret_val = this;
return ret_val;
FAIL2:
libusb_close(dev_handle);
FAIL1:
libusb_exit(0);
FAIL0:
return ret_val;
}
void usb_device_close(usb_device_t *this)
{
libusb_close(this->dev_handle);
free(this);
libusb_exit(0);
return;
}
int usb_device_handle_events(usb_device_t *this)
{
return libusb_handle_events_completed(this->context, &this->completed);
}
int usb_device_control(usb_device_t *this, uint8_t request, uint16_t value,
uint16_t index, uint8_t *data, uint16_t length, int read) {
const uint8_t bmWriteRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
const uint8_t bmReadRequestType = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
const unsigned int timeout = 5000; // timeout (in ms) for each command
int ret;
if (!read) {
ret = libusb_control_transfer(this->dev_handle, bmWriteRequestType,
request, value, index, data, length,
timeout);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
return -1;
}
}
else
{
ret = libusb_control_transfer(this->dev_handle, bmReadRequestType,
request, value, index, data, length,
timeout);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
return -1;
}
}
return 0;
}
/* internal functions */
static libusb_device_handle *find_usb_device(int index, libusb_context *ctx,
libusb_device **device, int *needs_firmware)
{
libusb_device_handle *ret_val = 0;
*device = 0;
*needs_firmware = 0;
libusb_device **list = 0;
ssize_t nusbdevices = libusb_get_device_list(ctx, &list);
if (nusbdevices < 0) {
log_usb_error(nusbdevices, __func__, __FILE__, __LINE__);
goto FAIL0;
}
int count = 0;
for (ssize_t j = 0; j < nusbdevices; ++j) {
libusb_device *dev = list[j];
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(dev, &desc);
for (int i = 0; i < n_usb_device_ids; ++i) {
if (desc.idVendor == usb_device_ids[i].vid &&
desc.idProduct == usb_device_ids[i].pid) {
if (count == index) {
*device = dev;
*needs_firmware = usb_device_ids[i].needs_firmware;
}
count++;
}
}
}
if (*device == 0) {
fprintf(stderr, "ERROR - usb_device@%d not found\n", index);
goto FAIL1;
}
libusb_device_handle *dev_handle = 0;
int ret = libusb_open(*device, &dev_handle);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAIL1;
}
libusb_free_device_list(list, 1);
ret = libusb_kernel_driver_active(dev_handle, 0);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAILA;
}
if (ret == 1) {
fprintf(stderr, "ERROR - device busy\n");
goto FAILA;
}
ret = libusb_claim_interface(dev_handle, 0);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
goto FAILA;
}
ret_val = dev_handle;
return ret_val;
FAILA:
libusb_close(dev_handle);
return ret_val;
FAIL1:
libusb_free_device_list(list, 1);
FAIL0:
return ret_val;
}
int load_image(libusb_device_handle *dev_handle, const char *image, uint32_t image_size)
{
int ret_val = -1;
const int fx_type = FX_TYPE_FX3;
const int img_type = IMG_TYPE_IMG;
const int stage = 0;
verbose = 1;
ret_val = fx3_load_ram(dev_handle, image);
return ret_val;
}
static int transfer_image(const uint8_t *image,
libusb_device_handle *dev_handle)
{
const uint8_t bmRequestType = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
const uint8_t bRequest = 0xa0; // vendor command
const unsigned int timeout = 5000; // timeout (in ms) for each command
const size_t max_write_size = 2 * 1024; // max write size in bytes
// skip first word with 'CY' magic
uint32_t *current = (uint32_t *) image + 1;
while (1) {
uint32_t loadSz = *current++;
if (loadSz == 0) {
break;
}
uint32_t address = *current++;
unsigned char *data = (unsigned char *) current;
for (size_t nleft = loadSz * 4; nleft > 0; ) {
uint16_t wLength = nleft > max_write_size ? max_write_size : nleft;
int ret = libusb_control_transfer(dev_handle, bmRequestType, bRequest,
address & 0xffff, address >> 16,
data, wLength, timeout);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
return -1;
}
if (!(ret == wLength)) {
fprintf(stderr, "ERROR - libusb_control_transfer() returned less bytes than expected - actual=%hu expected=%hu\n", ret, wLength);
return -1;
}
data += wLength;
nleft -= wLength;
}
current += loadSz;
}
uint32_t entryAddr = *current++;
uint32_t checksum __attribute__((unused)) = *current++;
sleep(1);
int ret = libusb_control_transfer(dev_handle, bmRequestType, bRequest,
entryAddr & 0xffff, entryAddr >> 16,
0, 0, timeout);
if (ret < 0) {
log_usb_warning(ret, __func__, __FILE__, __LINE__);
}
return 0;
}
static int list_endpoints(struct libusb_endpoint_descriptor endpoints[],
struct libusb_ss_endpoint_companion_descriptor ss_endpoints[],
libusb_device *device)
{
struct libusb_config_descriptor *config;
int ret = libusb_get_active_config_descriptor(device, &config);
if (ret < 0) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
return -1;
}
int count = 0;
/* loop through the interfaces */
for (int intf = 0; intf < config->bNumInterfaces; ++intf) {
const struct libusb_interface *interface = &config->interface[intf];
for (int setng = 0; setng < interface->num_altsetting; ++setng) {
const struct libusb_interface_descriptor *setting = &interface->altsetting[setng];
for (int endp = 0; endp < setting->bNumEndpoints; ++endp) {
const struct libusb_endpoint_descriptor *endpoint = &setting->endpoint[endp];
if (count == MAX_ENDPOINTS) {
fprintf(stderr, "WARNING - found too many USB endpoints; returning only the first %d\n", MAX_ENDPOINTS);
return count;
}
endpoints[count] = *endpoint;
struct libusb_ss_endpoint_companion_descriptor *endpoint_ss_companion;
ret = libusb_get_ss_endpoint_companion_descriptor(0, endpoint,
&endpoint_ss_companion);
if (ret < 0 && ret != LIBUSB_ERROR_NOT_FOUND) {
log_usb_error(ret, __func__, __FILE__, __LINE__);
return -1;
}
if (ret == 0) {
ss_endpoints[count] = *endpoint_ss_companion;
} else {
ss_endpoints[count].bLength = 0;
}
libusb_free_ss_endpoint_companion_descriptor(endpoint_ss_companion);
count++;
}
}
}
return count;
}

View File

@ -0,0 +1,60 @@
/*
* usb_device.h - Basic USB and USB control functions
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __USB_DEVICE_H
#define __USB_DEVICE_H
#include <libusb.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct usb_device usb_device_t;
struct usb_device_info {
unsigned char *manufacturer;
unsigned char *product;
unsigned char *serial_number;
};
int usb_device_count_devices();
int usb_device_get_device_list(struct usb_device_info **usb_device_infos);
int usb_device_free_device_list(struct usb_device_info *usb_device_infos);
usb_device_t *usb_device_open(int index, const char* image,
uint32_t size);
int usb_device_handle_events(usb_device_t *t);
void usb_device_close(usb_device_t *t);
int usb_device_control(usb_device_t *t, uint8_t request, uint16_t value,
uint16_t index, uint8_t *data, uint16_t length, int read);
#ifdef __cplusplus
}
#endif
#endif /* __USB_DEVICE_H */

View File

@ -0,0 +1,52 @@
/*
* usb_device_internals.h - internal USB structures to be shared between
* usb_device and usb_streaming
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __USB_DEVICE_INTERNALS_H
#define __USB_DEVICE_INTERNALS_H
#include "usb_device.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct usb_device {
libusb_device *dev;
libusb_device_handle *dev_handle;
libusb_context *context;
int completed;
int nendpoints;
#define MAX_ENDPOINTS (16)
struct libusb_endpoint_descriptor endpoints[MAX_ENDPOINTS];
struct libusb_ss_endpoint_companion_descriptor ss_endpoints[MAX_ENDPOINTS];
uint8_t bulk_in_endpoint_address;
uint16_t bulk_in_max_packet_size;
uint8_t bulk_in_max_burst;
} usb_device_t;
typedef struct usb_device usb_device_t;
#ifdef __cplusplus
}
#endif
#endif /* __USB_DEVICE_INTERNALS_H */

View File

@ -102,7 +102,6 @@ CCyUSBDevice::~CCyUSBDevice(void)
if (hDevNotification) {
if (! UnregisterDeviceNotification(hDevNotification))
throw "Failed to close the device notification handle.";
hDevNotification = 0;
}
@ -2039,12 +2038,12 @@ bool CCyUSBEndPoint::FinishDataXfer(PUCHAR buf, LONG &bufLen, OVERLAPPED *ov, PU
// If a buffer was provided, pass-back the Isoc packet info records
if (pktInfos && (bufLen > 0)) {
ZeroMemory(pktInfos, pTransfer->IsoPacketLength);
//ZeroMemory(pktInfos, pTransfer->IsoPacketLength);
PUCHAR pktPtr = pXmitBuf + pTransfer->IsoPacketOffset;
memcpy(pktInfos, pktPtr, pTransfer->IsoPacketLength);
}
delete[] pXmitBuf; // [] Changed in 1.5.1.3
// delete[] pXmitBuf; // [] Changed in 1.5.1.3
return rResult && (UsbdStatus == 0) && (NtStatus == 0);
}
@ -2057,7 +2056,7 @@ PUCHAR CCyUSBEndPoint::BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov
int iXmitBufSize = sizeof (SINGLE_TRANSFER) + bufLen;
PUCHAR pXmitBuf = new UCHAR[iXmitBufSize];
ZeroMemory (pXmitBuf, iXmitBufSize);
ZeroMemory (pXmitBuf, sizeof(SINGLE_TRANSFER));
PSINGLE_TRANSFER pTransfer = (PSINGLE_TRANSFER) pXmitBuf;
pTransfer->ucEndpointAddress = Address;
@ -2066,7 +2065,7 @@ PUCHAR CCyUSBEndPoint::BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov
pTransfer->BufferLength = bufLen;
// Copy buf into pXmitBuf
UCHAR *ptr = (PUCHAR) pTransfer + pTransfer->BufferOffset;
UCHAR *ptr = (PUCHAR)pXmitBuf + sizeof(SINGLE_TRANSFER);
memcpy (ptr, buf, bufLen);
DWORD dwReturnBytes;
@ -2095,7 +2094,7 @@ PUCHAR CCyUSBEndPoint::BeginDirectXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov)
if ( hDevice == INVALID_HANDLE_VALUE ) return NULL;
int iXmitBufSize = sizeof (SINGLE_TRANSFER);
PUCHAR pXmitBuf = new UCHAR[iXmitBufSize];
PUCHAR pXmitBuf = (PUCHAR)ov + sizeof(OVERLAPPED);
ZeroMemory (pXmitBuf, iXmitBufSize);
PSINGLE_TRANSFER pTransfer = (PSINGLE_TRANSFER) pXmitBuf;
@ -2334,7 +2333,7 @@ PUCHAR CCyControlEndPoint::BeginDataXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov
int iXmitBufSize = sizeof (SINGLE_TRANSFER) + bufLen;
UCHAR *pXmitBuf = new UCHAR[iXmitBufSize];
ZeroMemory (pXmitBuf, iXmitBufSize);
ZeroMemory (pXmitBuf, sizeof(SINGLE_TRANSFER));
// The Control Endpoint has a 1 sec resolution on its timeout
// But, TimeOut is in milliseconds.
@ -2427,7 +2426,7 @@ PUCHAR CCyIsocEndPoint::BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *o
int iXmitBufSize = sizeof (SINGLE_TRANSFER) + (pkts * sizeof(ISO_PACKET_INFO)) + bufLen;
UCHAR *pXmitBuf = new UCHAR[iXmitBufSize];
ZeroMemory (pXmitBuf, iXmitBufSize);
ZeroMemory (pXmitBuf, sizeof(SINGLE_TRANSFER) + (pkts * sizeof(ISO_PACKET_INFO)));
PSINGLE_TRANSFER pTransfer = (PSINGLE_TRANSFER) pXmitBuf;
pTransfer->ucEndpointAddress = Address;
@ -2946,7 +2945,7 @@ FX3_FWDWNLOAD_ERROR_CODE CCyFX3Device::DownloadUserIMGtoI2CE2PROM(PUCHAR buffer_
//______________________________________________________________________________
FX3_FWDWNLOAD_ERROR_CODE CCyFX3Device::DownloadFwToRam(PUCHAR buffer_p, UINT fw_size, UCHAR opCode)
FX3_FWDWNLOAD_ERROR_CODE CCyFX3Device::DownloadFwToRam(const UCHAR *buffer_p, UINT fw_size, UCHAR opCode)
{
UCHAR downloadBuffer[BUFSIZE_UPORT];
UCHAR uploadbuffer[BUFSIZE_UPORT];
@ -3113,7 +3112,7 @@ FX3_FWDWNLOAD_ERROR_CODE CCyFX3Device::DownloadFw(char *fileName, FX3_FWDWNLOAD_
UINT fwSize = 0;
PUCHAR FwImage;
FILE *FwImagePtr;
int error;
//int error;
// error = fopen_s(&FwImagePtr, fileName, "rb");
FwImagePtr = fopen( fileName, "rb");

View File

@ -580,6 +580,7 @@ public:
~CCyFX3Device(void);
bool IsBootLoaderRunning();
FX3_FWDWNLOAD_ERROR_CODE DownloadFw(char *fileName, FX3_FWDWNLOAD_MEDIA_TYPE enMediaType);
FX3_FWDWNLOAD_ERROR_CODE DownloadFwToRam(const UCHAR *buffer_p, UINT fw_size, UCHAR opCode = 0xA0);
private:
@ -589,7 +590,6 @@ private:
bool DownloadBufferToDevice(UINT start_addr, USHORT count, UCHAR *data_buf, UCHAR opCode);
bool UploadBufferFromDevice(UINT start_addr, USHORT count, UCHAR *data_buf, UCHAR opCode);
FX3_FWDWNLOAD_ERROR_CODE DownloadFwToRam(PUCHAR buffer_p, UINT fw_size, UCHAR opCode);
FX3_FWDWNLOAD_ERROR_CODE DownloadUserIMGtoI2CE2PROM(PUCHAR buffer_p, UINT fw_size, UCHAR opCode);
FX3_FWDWNLOAD_ERROR_CODE DownloadUserIMGtoSPIFLASH(PUCHAR buffer_p, UINT fw_size, UCHAR opCode);

View File

@ -0,0 +1,456 @@
//
// FX3handler.cpp
// 2020 10 12 Oscar Steila ik1xpv
// loading arm code.img from resource by Howard Su and Hayati Ayguen
// This module was previous named:
// openFX3.cpp MIT License Copyright (c) 2016 Booya Corp.
// booyasdr@gmail.com, http://booyasdr.sf.net
// modified 2017 11 30 ik1xpv@gmail.com, http://www.steila.com/blog
//
#include <windows.h>
#include "FX3handler.h"
#include "./CyAPI/CyAPI.h"
#include "./CyAPI/cyioctl.h"
#define RES_BIN_FIRMWARE 2000
fx3class* CreateUsbHandler()
{
return new fx3handler();
}
fx3handler::fx3handler():
fx3dev (nullptr),
Fx3IsOn (false)
{
}
fx3handler::~fx3handler() // reset USB device and exit
{
DbgPrintf("\r\n~fx3handler\r\n");
Close();
}
bool fx3handler::GetFx3Device() {
bool r = false;
if (fx3dev == nullptr) return r; // no device
int n = fx3dev->DeviceCount();
if (n == 0) return r; // no one
// Walk through all devices looking for VENDOR_ID/STREAMER_ID
for (int i = 0; i <= n; i++) {
fx3dev->Open(i); // go down the list of devices to find our device
if ((fx3dev->VendorID == VENDOR_ID) && (fx3dev->ProductID == STREAMER_ID))
{
r = true;
break;
}
if ((fx3dev->VendorID == VENDOR_ID) && (fx3dev->ProductID == BOOTLOADER_ID))
{
r = true;
break;
}
}
if (r == false)
fx3dev->Close();
return r;
}
bool fx3handler::GetFx3DeviceStreamer(void) { // open class
bool r = false;
if (fx3dev == NULL) return r;
int n = fx3dev->DeviceCount();
// Walk through all devices looking for VENDOR_ID/STREAMER_ID
if (n == 0) return r;
// go down the list of devices to find STREAMER_ID device
for (int i = 0; i <= n; i++) {
fx3dev->Open(i);
if ((fx3dev->VendorID == VENDOR_ID) && (fx3dev->ProductID == STREAMER_ID))
{
r = true;
break;
}
}
if (r == false)
fx3dev->Close();
return r;
}
bool fx3handler::Open(uint8_t* fw_data, uint32_t fw_size) {
bool r = false;
fx3dev = new CCyFX3Device; // instantiate the device
if (fx3dev == nullptr) return r; // return if failed
int n = fx3dev->DeviceCount();
if (n == 0) return r; // return if no devices connected
if (!GetFx3Device()) return r; // NO FX3 device connected
#ifdef _DEBUG
if (!fx3dev->IsBootLoaderRunning()) { // if not bootloader device
Control(RESETFX3); // reset the fx3 firmware via CyU3PDeviceReset(false)
DbgPrintf("DEBUG - Reset Firmware\n");
Sleep(300);
fx3dev->Close(); // close class
delete fx3dev; // destroy class
Sleep(300);
fx3dev = new CCyFX3Device; // create class
GetFx3Device(); // open class
}
#endif
FX3_FWDWNLOAD_ERROR_CODE dlf = SUCCESS;
if (fx3dev->IsBootLoaderRunning())
{
dlf = fx3dev->DownloadFwToRam(fw_data, fw_size);
Sleep(500); // wait for download to finish
}
if (dlf != 0)
{
DbgPrintf("MISSING/OLD FIRMWARE\n");
return false;
}
int x = 0;
int maxretry = 30;
CCyFX3Device* expdev = nullptr;
while (x++ < maxretry) // wait new firmware setup
{
bool r = false;
expdev = new CCyFX3Device; // instantiate the device
if (expdev != NULL)
int n = expdev->DeviceCount();
if (n > 0)
{
expdev->Open(0);
// go down the list of devices to find our device
for (int i = 1; i <= n; i++)
{
if ((expdev->VendorID == VENDOR_ID) && (expdev->ProductID == STREAMER_ID))
{
x = maxretry; //got it exit
}
}
}
expdev->Close(); // close class
delete expdev; // destroy class
}
GetFx3DeviceStreamer(); // open class with new ram firmware
if (!fx3dev->IsOpen()) {
DbgPrintf("Failed to open device\n");
return r;
}
EndPt = fx3dev->BulkInEndPt;
if (!EndPt) {
DbgPrintf("No Bulk In end point\n");
return r; // init failed
}
long pktSize = EndPt->MaxPktSize;
EndPt->SetXferSize(transferSize);
long ppx = transferSize / pktSize;
DbgPrintf("buffer transferSize = %d. packet size = %ld. packets per transfer = %ld\n"
, transferSize, pktSize, ppx);
uint8_t data[4];
GetHardwareInfo((uint32_t*)&data);
if (data[1] != FIRMWARE_VER_MAJOR ||
data[2] != FIRMWARE_VER_MINOR)
{
DbgPrintf("Firmware version mismatch %d.%d != %d.%d (actual)\n", FIRMWARE_VER_MAJOR, FIRMWARE_VER_MINOR, data[1], data[2]);
Control(RESETFX3);
return false;
}
Fx3IsOn = true;
return Fx3IsOn; // init success
}
using namespace std;
bool fx3handler::Control(FX3Command command, UINT8 data) { // firmware control BBRF
long lgt = 1;
fx3dev->ControlEndPt->ReqCode = command;
fx3dev->ControlEndPt->Value = (USHORT)0;
fx3dev->ControlEndPt->Index = (USHORT)0;
bool r = fx3dev->ControlEndPt->Write(&data, lgt);
DbgPrintf("FX3FWControl %x .%x %x\n", r, command, data);
if (r == false)
{
Close();
}
return r;
}
bool fx3handler::Control(FX3Command command, UINT32 data) { // firmware control BBRF
long lgt = 4;
fx3dev->ControlEndPt->ReqCode = command;
fx3dev->ControlEndPt->Value = (USHORT)0;
fx3dev->ControlEndPt->Index = (USHORT)0;
bool r = fx3dev->ControlEndPt->Write((PUCHAR)&data, lgt);
DbgPrintf("FX3FWControl %x .%x %x\n", r, command, data);
if (r == false)
{
Close();
}
return r;
}
bool fx3handler::Control(FX3Command command, UINT64 data) { // firmware control BBRF
long lgt = 8;
fx3dev->ControlEndPt->ReqCode = command;
fx3dev->ControlEndPt->Value = (USHORT)0;
fx3dev->ControlEndPt->Index = (USHORT)0;
bool r = fx3dev->ControlEndPt->Write((PUCHAR)&data, lgt);
DbgPrintf("FX3FWControl %x .%x %llx\n", r, command, data);
if (r == false)
{
Close();
}
return r;
}
bool fx3handler::SetArgument(UINT16 index, UINT16 value) { // firmware control BBRF
long lgt = 1;
uint8_t data = 0;
fx3dev->ControlEndPt->ReqCode = SETARGFX3;
fx3dev->ControlEndPt->Value = (USHORT)value;
fx3dev->ControlEndPt->Index = (USHORT)index;
bool r = fx3dev->ControlEndPt->Write((PUCHAR)&data, lgt);
DbgPrintf("SetArgument %x .%x (%x, %x)\n", r, SETARGFX3, index, value);
if (r == false)
{
Close();
}
return r;
}
bool fx3handler::GetHardwareInfo(UINT32* data) { // firmware control BBRF
long lgt = 4;
fx3dev->ControlEndPt->ReqCode = TESTFX3;
#ifdef _DEBUG
fx3dev->ControlEndPt->Value = (USHORT) 1;
#else
fx3dev->ControlEndPt->Value = (USHORT) 0;
#endif
fx3dev->ControlEndPt->Index = (USHORT)0;
bool r = fx3dev->ControlEndPt->Read((PUCHAR)data, lgt);
DbgPrintf("GetHardwareInfo %x .%x %x\n", r, TESTFX3, *data);
if (r == false)
{
Close();
}
return r;
}
bool fx3handler::ReadDebugTrace(uint8_t* pdata, uint8_t len)
{
long lgt = len;
bool r;
fx3dev->ControlEndPt->ReqCode = READINFODEBUG;
fx3dev->ControlEndPt->Value = (USHORT) pdata[0]; // upstream char
r = fx3dev->ControlEndPt->Read((PUCHAR)pdata, lgt);
return r;
}
bool fx3handler::SendI2cbytes(UINT8 i2caddr, UINT8 regaddr, PUINT8 pdata, UINT8 len)
{
bool r = false;
LONG lgt = len;
fx3dev->ControlEndPt->ReqCode = I2CWFX3;
fx3dev->ControlEndPt->Value = (USHORT)i2caddr;
fx3dev->ControlEndPt->Index = (USHORT)regaddr;
Sleep(10);
r = fx3dev->ControlEndPt->Write(pdata, lgt);
if (r == false)
DbgPrintf("\nfx3FWSendI2cbytes 0x%02x regaddr 0x%02x 1data 0x%02x len 0x%02x \n",
i2caddr, regaddr, *pdata, len);
return r;
}
bool fx3handler::ReadI2cbytes(UINT8 i2caddr, UINT8 regaddr, PUINT8 pdata, UINT8 len)
{
bool r = false;
LONG lgt = len;
WORD saveValue, saveIndex;
saveValue = fx3dev->ControlEndPt->Value;
saveIndex = fx3dev->ControlEndPt->Index;
fx3dev->ControlEndPt->ReqCode = I2CRFX3;
fx3dev->ControlEndPt->Value = (USHORT)i2caddr;
fx3dev->ControlEndPt->Index = (USHORT)regaddr;
r = fx3dev->ControlEndPt->Read(pdata, lgt);
if (r == false)
printf("fx3FWReadI2cbytes %x : %02x %02x %02x %02x : %02x\n", r, I2CRFX3, i2caddr, regaddr, len, *pdata);
fx3dev->ControlEndPt->Value = saveValue;
fx3dev->ControlEndPt->Index = saveIndex;
return r;
}
bool fx3handler::Close() {
fx3dev->Close(); // close class
delete fx3dev; // destroy class
Fx3IsOn = false;
return true;
}
#define BLOCK_TIMEOUT (80) // block 65.536 ms timeout is 80
struct ReadContext
{
PUCHAR context;
OVERLAPPED overlap;
SINGLE_TRANSFER transfer;
uint8_t* buffer;
long size;
};
bool fx3handler::BeginDataXfer(UINT8 *buffer, long transferSize, void** context)
{
ReadContext *readContext = (ReadContext *)(*context);
if (!EndPt)
return false;
if (*context == nullptr)
{
// first time call, allocate the context structure
readContext = new ReadContext;
*context = readContext;
memset(&readContext->overlap, 0, sizeof(readContext->overlap));
readContext->overlap.hEvent = CreateEvent(NULL, false, false, NULL);
}
readContext->buffer = buffer;
readContext->size = transferSize;
readContext->context = EndPt->BeginDataXfer(readContext->buffer, transferSize, &readContext->overlap);
if (EndPt->NtStatus || EndPt->UsbdStatus) {// BeginDataXfer failed
DbgPrintf((char*)"Xfer request rejected. 1 STATUS = %ld %ld\n", EndPt->NtStatus, EndPt->UsbdStatus);
return false;
}
return true;
}
bool fx3handler::FinishDataXfer(void** context)
{
ReadContext *readContext = (ReadContext *)(*context);
if (!readContext)
{
return nullptr;
}
if (!EndPt->WaitForXfer(&readContext->overlap, BLOCK_TIMEOUT)) { // block on transfer
DbgPrintf("WaitForXfer timeout. NTSTATUS = 0x%08X\n", EndPt->NtStatus);
EndPt->Abort(); // abort if timeout
return false;
}
auto requested_size = readContext->size;
if (!EndPt->FinishDataXfer(readContext->buffer, readContext->size, &readContext->overlap, readContext->context)) {
DbgPrintf("FinishDataXfer Failed. NTSTATUS = 0x%08X\n", EndPt->NtStatus);
return false;
}
if (readContext->size < requested_size)
DbgPrintf("only read %ld but requested %ld\n", readContext->size, requested_size);
return true;
}
void fx3handler::CleanupDataXfer(void** context)
{
ReadContext *readContext = (ReadContext *)(*context);
CloseHandle(readContext->overlap.hEvent);
delete (readContext);
}
#define USB_READ_CONCURRENT 4
void fx3handler::AdcSamplesProcess()
{
DbgPrintf("AdcSamplesProc thread runs\n");
int buf_idx; // queue index
int read_idx;
void* contexts[USB_READ_CONCURRENT];
memset(contexts, 0, sizeof(contexts));
// Queue-up the first batch of transfer requests
for (int n = 0; n < USB_READ_CONCURRENT; n++) {
auto ptr = inputbuffer->peekWritePtr(n);
if (!BeginDataXfer((uint8_t*)ptr, transferSize, &contexts[n])) {
DbgPrintf("Xfer request rejected.\n");
return;
}
}
read_idx = 0; // context cycle index
buf_idx = 0; // buffer cycle index
// The infinite xfer loop.
while (run) {
if (!FinishDataXfer(&contexts[read_idx])) {
break;
}
inputbuffer->WriteDone();
// Re-submit this queue element to keep the queue full
auto ptr = inputbuffer->peekWritePtr(USB_READ_CONCURRENT - 1);
if (!BeginDataXfer((uint8_t*)ptr, transferSize, &contexts[read_idx])) { // BeginDataXfer failed
DbgPrintf("Xfer request rejected.\n");
break;
}
buf_idx = (buf_idx + 1) % QUEUE_SIZE;
read_idx = (read_idx + 1) % USB_READ_CONCURRENT;
} // End of the infinite loop
for (int n = 0; n < USB_READ_CONCURRENT; n++) {
CleanupDataXfer(&contexts[n]);
}
DbgPrintf("AdcSamplesProc thread_exit\n");
return; // void *
}
void fx3handler::StartStream(ringbuffer<int16_t>& input, int numofblock)
{
// Allocate the context and buffers
inputbuffer = &input;
// create the thread
this->numofblock = numofblock;
run = true;
adc_samples_thread = new std::thread(
[this]() {
this->AdcSamplesProcess();
}
);
}
void fx3handler::StopStream()
{
// set the flag
run = false;
adc_samples_thread->join();
// force exit the thread
inputbuffer = nullptr;
delete adc_samples_thread;
}

View File

@ -0,0 +1,73 @@
#ifndef FX3HANDLER_H
#define FX3HANDLER_H
//
// FX3handler.cpp
// 2020 10 12 Oscar Steila ik1xpv
// loading arm code.img from resource by Howard Su and Hayati Ayguen
// This module was previous named:openFX3.cpp
// MIT License Copyright (c) 2016 Booya Corp.
// booyasdr@gmail.com, http://booyasdr.sf.net
// modified 2017 11 30 ik1xpv@gmail.com, http://www.steila.com/blog
//
#include <sys/stat.h>
#include <iostream>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include "sddc_config.h"
#include "dsp/ringbuffer.h"
#define VENDOR_ID (0x04B4)
#define STREAMER_ID (0x00F1)
#define BOOTLOADER_ID (0x00F3)
#include "FX3Class.h"
class CCyFX3Device;
class CCyUSBEndPoint;
class fx3handler : public fx3class
{
public:
fx3handler();
virtual ~fx3handler(void);
bool Open(uint8_t* fw_data, uint32_t fw_size);
bool IsOn() { return Fx3IsOn; }
bool Control(FX3Command command, uint8_t data);
bool Control(FX3Command command, uint32_t data = 0);
bool Control(FX3Command command, uint64_t data);
bool SetArgument(uint16_t index, uint16_t value);
bool GetHardwareInfo(uint32_t* data);
bool ReadDebugTrace(uint8_t* pdata, uint8_t len);
void StartStream(ringbuffer<int16_t>& input, int numofblock);
void StopStream();
private:
bool SendI2cbytes(uint8_t i2caddr, uint8_t regaddr, uint8_t* pdata, uint8_t len);
bool ReadI2cbytes(uint8_t i2caddr, uint8_t regaddr, uint8_t* pdata, uint8_t len);
bool BeginDataXfer(uint8_t *buffer, long transferSize, void** context);
bool FinishDataXfer(void** context);
void CleanupDataXfer(void** context);
CCyFX3Device* fx3dev;
CCyUSBEndPoint* EndPt;
std::thread *adc_samples_thread;
bool GetFx3Device();
bool GetFx3DeviceStreamer();
bool Fx3IsOn;
bool Close(void);
void AdcSamplesProcess();
ringbuffer<int16_t> *inputbuffer;
int numofblock;
bool run;
};
#endif // FX3HANDLER_H

View File

@ -0,0 +1,191 @@
#pragma once
#include <thread>
#include <mutex>
#include <condition_variable>
const int default_count = 64;
const int spin_count = 100;
#define ALIGN (8)
class ringbufferbase {
public:
ringbufferbase(int count) :
max_count(count),
read_index(0),
write_index(0),
emptyCount(0),
fullCount(0),
writeCount(0)
{
}
int getFullCount() const { return fullCount; }
int getEmptyCount() const { return emptyCount; }
int getWriteCount() const { return writeCount; }
void ReadDone()
{
std::unique_lock<std::mutex> lk(mutex);
if ((write_index + 1) % max_count == read_index)
{
read_index = (read_index + 1) % max_count;
nonfullCV.notify_all();
}
else
{
read_index = (read_index + 1) % max_count;
}
}
void WriteDone()
{
std::unique_lock<std::mutex> lk(mutex);
if (read_index == write_index)
{
write_index = (write_index + 1) % max_count;
nonemptyCV.notify_all();
}
else
{
write_index = (write_index + 1) % max_count;
}
writeCount++;
}
void Stop()
{
std::unique_lock<std::mutex> lk(mutex);
read_index = 0;
write_index = max_count / 2;
nonfullCV.notify_all();
nonemptyCV.notify_all();
}
protected:
void WaitUntilNotEmpty()
{
// if not empty
for (int i = 0; i < spin_count; i++)
{
if (read_index != write_index)
return;
}
if (read_index == write_index)
{
std::unique_lock<std::mutex> lk(mutex);
emptyCount++;
nonemptyCV.wait(lk, [this] {
return read_index != write_index;
});
}
}
void WaitUntilNotFull()
{
for (int i = 0; i < spin_count; i++)
{
if ((write_index + 1) % max_count != read_index)
return;
}
if ((write_index + 1) % max_count == read_index)
{
std::unique_lock<std::mutex> lk(mutex);
fullCount++;
nonfullCV.wait(lk, [this] {
return (write_index + 1) % max_count != read_index;
});
}
}
int max_count;
volatile int read_index;
volatile int write_index;
private:
int emptyCount;
int fullCount;
int writeCount;
std::mutex mutex;
std::condition_variable nonemptyCV;
std::condition_variable nonfullCV;
};
template<typename T> class ringbuffer : public ringbufferbase {
typedef T* TPtr;
public:
ringbuffer(int count = default_count) :
ringbufferbase(count)
{
buffers = new TPtr[max_count];
buffers[0] = nullptr;
}
~ringbuffer()
{
if (buffers[0])
delete[] buffers[0];
delete[] buffers;
}
void setBlockSize(int size)
{
if (block_size != size)
{
block_size = size;
if (buffers[0])
delete[] buffers[0];
int aligned_block_size = (block_size + ALIGN - 1) & (~(ALIGN - 1));
auto data = new T[max_count * aligned_block_size];
for (int i = 0; i < max_count; ++i)
{
buffers[i] = &data[i * aligned_block_size];
}
}
}
T* peekWritePtr(int offset)
{
return buffers[(write_index + max_count + offset) % max_count];
}
T* peekReadPtr(int offset)
{
return buffers[(read_index + max_count + offset) % max_count];
}
T* getWritePtr()
{
// if there is still space
WaitUntilNotFull();
return buffers[(write_index) % max_count];
}
const T* getReadPtr()
{
WaitUntilNotEmpty();
return buffers[read_index];
}
int getBlockSize() const { return block_size; }
private:
int block_size;
TPtr* buffers;
};

View File

@ -0,0 +1,263 @@
#include "license.txt"
/*
The ADC input real stream of 16 bit samples (at Fs = 64 Msps in the example) is converted to:
- 32 Msps float Fs/2 complex stream, or
- 16 Msps float Fs/2 complex stream, or
- 8 Msps float Fs/2 complex stream, or
- 4 Msps float Fs/2 complex stream, or
- 2 Msps float Fs/2 complex stream.
The decimation factor is selectable from HDSDR GUI sampling rate selector
The name r2iq as Real 2 I+Q stream
*/
#include "fft_mt_r2iq.h"
#include "sddc_config.h"
#include "fftw3.h"
#include "RadioHandler.h"
#include "fir.h"
#include <assert.h>
#include <utility>
r2iqControlClass::r2iqControlClass()
{
r2iqOn = false;
randADC = false;
sideband = false;
mdecimation = 0;
mratio[0] = 1; // 1,2,4,8,16
for (int i = 1; i < NDECIDX; i++)
{
mratio[i] = mratio[i - 1] * 2;
}
}
fft_mt_r2iq::fft_mt_r2iq() :
r2iqControlClass(),
filterHw(nullptr)
{
mtunebin = halfFft / 4;
mfftdim[0] = halfFft;
for (int i = 1; i < NDECIDX; i++)
{
mfftdim[i] = mfftdim[i - 1] / 2;
}
GainScale = 0.0f;
#ifndef NDEBUG
int mratio = 1; // 1,2,4,8,16,..
const float Astop = 120.0f;
const float relPass = 0.85f; // 85% of Nyquist should be usable
const float relStop = 1.1f; // 'some' alias back into transition band is OK
printf("\n***************************************************************************\n");
printf("Filter tap estimation, Astop = %.1f dB, relPass = %.2f, relStop = %.2f\n", Astop, relPass, relStop);
for (int d = 0; d < NDECIDX; d++)
{
float Bw = 64.0f / mratio;
int ntaps = KaiserWindow(0, Astop, relPass * Bw / 128.0f, relStop * Bw / 128.0f, nullptr);
printf("decimation %2d: KaiserWindow(Astop = %.1f dB, Fpass = %.3f,Fstop = %.3f, Bw %.3f @ %f ) => %d taps\n",
d, Astop, relPass * Bw, relStop * Bw, Bw, 128.0f, ntaps);
mratio = mratio * 2;
}
printf("***************************************************************************\n");
#endif
}
fft_mt_r2iq::~fft_mt_r2iq()
{
if (filterHw == nullptr)
return;
fftwf_export_wisdom_to_filename("wisdom");
for (int d = 0; d < NDECIDX; d++)
{
fftwf_free(filterHw[d]); // 4096
}
fftwf_free(filterHw);
fftwf_destroy_plan(plan_t2f_r2c);
for (int d = 0; d < NDECIDX; d++)
{
fftwf_destroy_plan(plans_f2t_c2c[d]);
}
for (unsigned t = 0; t < processor_count; t++) {
auto th = threadArgs[t];
fftwf_free(th->ADCinTime);
fftwf_free(th->ADCinFreq);
fftwf_free(th->inFreqTmp);
delete threadArgs[t];
}
}
float fft_mt_r2iq::setFreqOffset(float offset)
{
// align to 1/4 of halfft
this->mtunebin = int(offset * halfFft / 4) * 4; // mtunebin step 4 bin ?
float delta = ((float)this->mtunebin / halfFft) - offset;
float ret = delta * getRatio(); // ret increases with higher decimation
DbgPrintf("offset %f mtunebin %d delta %f (%f)\n", offset, this->mtunebin, delta, ret);
return ret;
}
void fft_mt_r2iq::TurnOn() {
this->r2iqOn = true;
this->bufIdx = 0;
this->lastThread = threadArgs[0];
for (unsigned t = 0; t < processor_count; t++) {
r2iq_thread[t] = std::thread(
[this] (void* arg)
{ return this->r2iqThreadf((r2iqThreadArg*)arg); }, (void*)threadArgs[t]);
}
}
void fft_mt_r2iq::TurnOff(void) {
this->r2iqOn = false;
inputbuffer->Stop();
outputbuffer->Stop();
for (unsigned t = 0; t < processor_count; t++) {
r2iq_thread[t].join();
}
}
bool fft_mt_r2iq::IsOn(void) { return(this->r2iqOn); }
void fft_mt_r2iq::Init(float gain, ringbuffer<int16_t> *input, ringbuffer<float>* obuffers)
{
this->inputbuffer = input; // set to the global exported by main_loop
this->outputbuffer = obuffers; // set to the global exported by main_loop
this->GainScale = gain;
fftwf_import_wisdom_from_filename("wisdom");
// Get the processor count
processor_count = std::thread::hardware_concurrency() - 1;
if (processor_count == 0)
processor_count = 1;
if (processor_count > N_MAX_R2IQ_THREADS)
processor_count = N_MAX_R2IQ_THREADS;
{
fftwf_plan filterplan_t2f_c2c; // time to frequency fft
DbgPrintf((char *) "r2iqCntrl initialization\n");
// DbgPrintf((char *) "RandTable generated\n");
// filters
fftwf_complex *pfilterht; // time filter ht
pfilterht = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*halfFft); // halfFft
filterHw = (fftwf_complex**)fftwf_malloc(sizeof(fftwf_complex*)*NDECIDX);
for (int d = 0; d < NDECIDX; d++)
{
filterHw[d] = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*halfFft); // halfFft
}
filterplan_t2f_c2c = fftwf_plan_dft_1d(halfFft, pfilterht, filterHw[0], FFTW_FORWARD, FFTW_MEASURE);
float *pht = new float[halfFft / 4 + 1];
const float Astop = 120.0f;
const float relPass = 0.85f; // 85% of Nyquist should be usable
const float relStop = 1.1f; // 'some' alias back into transition band is OK
for (int d = 0; d < NDECIDX; d++) // @todo when increasing NDECIDX
{
// @todo: have dynamic bandpass filter size - depending on decimation
// to allow same stopband-attenuation for all decimations
float Bw = 64.0f / mratio[d];
// Bw *= 0.8f; // easily visualize Kaiser filter's response
KaiserWindow(halfFft / 4 + 1, Astop, relPass * Bw / 128.0f, relStop * Bw / 128.0f, pht);
float gainadj = gain * 2048.0f / (float)FFTN_R_ADC; // reference is FFTN_R_ADC == 2048
for (int t = 0; t < halfFft; t++)
{
pfilterht[t][0] = pfilterht[t][1]= 0.0F;
}
for (int t = 0; t < (halfFft/4+1); t++)
{
pfilterht[halfFft-1-t][0] = gainadj * pht[t];
}
fftwf_execute_dft(filterplan_t2f_c2c, pfilterht, filterHw[d]);
}
delete[] pht;
fftwf_destroy_plan(filterplan_t2f_c2c);
fftwf_free(pfilterht);
for (unsigned t = 0; t < processor_count; t++) {
r2iqThreadArg *th = new r2iqThreadArg();
threadArgs[t] = th;
th->ADCinTime = (float*)fftwf_malloc(sizeof(float) * (halfFft + transferSize / 2)); // 2048
th->ADCinFreq = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*(halfFft + 1)); // 1024+1
th->inFreqTmp = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*(halfFft)); // 1024
}
plan_t2f_r2c = fftwf_plan_dft_r2c_1d(2 * halfFft, threadArgs[0]->ADCinTime, threadArgs[0]->ADCinFreq, FFTW_MEASURE);
for (int d = 0; d < NDECIDX; d++)
{
plans_f2t_c2c[d] = fftwf_plan_dft_1d(mfftdim[d], threadArgs[0]->inFreqTmp, threadArgs[0]->inFreqTmp, FFTW_BACKWARD, FFTW_MEASURE);
}
}
}
#ifdef _WIN32
// Windows
#include <intrin.h>
#define cpuid(info, x) __cpuidex(info, x, 0)
#else
// GCC Intrinsics
#include <cpuid.h>
#define cpuid(info, x) __cpuid_count(x, 0, info[0], info[1], info[2], info[3])
#endif
void * fft_mt_r2iq::r2iqThreadf(r2iqThreadArg *th)
{
#ifdef NO_SIMD_OPTIM
DbgPrintf("Hardware Capability: all SIMD features (AVX, AVX2, AVX512) deactivated\n");
return r2iqThreadf_def(th);
#else
int info[4];
bool HW_AVX = false;
bool HW_AVX2 = false;
bool HW_AVX512F = false;
cpuid(info, 0);
int nIds = info[0];
if (nIds >= 0x00000001){
cpuid(info,0x00000001);
HW_AVX = (info[2] & ((int)1 << 28)) != 0;
}
if (nIds >= 0x00000007){
cpuid(info,0x00000007);
HW_AVX2 = (info[1] & ((int)1 << 5)) != 0;
HW_AVX512F = (info[1] & ((int)1 << 16)) != 0;
}
DbgPrintf("Hardware Capability: AVX:%d AVX2:%d AVX512:%d\n", HW_AVX, HW_AVX2, HW_AVX512F);
if (HW_AVX512F)
return r2iqThreadf_avx512(th);
else if (HW_AVX2)
return r2iqThreadf_avx2(th);
else if (HW_AVX)
return r2iqThreadf_avx(th);
else
return r2iqThreadf_def(th);
#endif
}

View File

@ -0,0 +1,127 @@
#pragma once
#include "r2iq.h"
#include "fftw3.h"
#include "sddc_config.h"
#include <algorithm>
#include <string.h>
// use up to this many threads
#define N_MAX_R2IQ_THREADS 1
#define PRINT_INPUT_RANGE 0
static const int halfFft = FFTN_R_ADC / 2; // half the size of the first fft at ADC 64Msps real rate (2048)
static const int fftPerBuf = transferSize / sizeof(short) / (3 * halfFft / 2) + 1; // number of ffts per buffer with 256|768 overlap
class fft_mt_r2iq : public r2iqControlClass
{
public:
fft_mt_r2iq();
virtual ~fft_mt_r2iq();
float setFreqOffset(float offset);
void Init(float gain, ringbuffer<int16_t>* buffers, ringbuffer<float>* obuffers);
void TurnOn();
void TurnOff(void);
bool IsOn(void);
protected:
template<bool rand> void convert_float(const int16_t *input, float* output, int size)
{
for(int m = 0; m < size; m++)
{
int16_t val;
if (rand && (input[m] & 1))
{
val = input[m] ^ (-2);
}
else
{
val = input[m];
}
output[m] = float(val);
}
}
void shift_freq(fftwf_complex* dest, const fftwf_complex* source1, const fftwf_complex* source2, int start, int end)
{
for (int m = start; m < end; m++)
{
// besides circular shift, do complex multiplication with the lowpass filter's spectrum
dest[m][0] = source1[m][0] * source2[m][0] - source1[m][1] * source2[m][1];
dest[m][1] = source1[m][1] * source2[m][0] + source1[m][0] * source2[m][1];
}
}
template<bool flip> void copy(fftwf_complex* dest, const fftwf_complex* source, int count)
{
if (flip)
{
for (int i = 0; i < count; i++)
{
dest[i][0] = source[i][0];
dest[i][1] = -source[i][1];
}
}
else
{
for (int i = 0; i < count; i++)
{
dest[i][0] = source[i][0];
dest[i][1] = source[i][1];
}
}
}
private:
ringbuffer<int16_t>* inputbuffer; // pointer to input buffers
ringbuffer<float>* outputbuffer; // pointer to ouput buffers
int bufIdx; // index to next buffer to be processed
r2iqThreadArg* lastThread;
float GainScale;
int mfftdim [NDECIDX]; // FFT N dimensions: mfftdim[k] = halfFft / 2^k
int mtunebin;
void *r2iqThreadf(r2iqThreadArg *th); // thread function
void * r2iqThreadf_def(r2iqThreadArg *th);
void * r2iqThreadf_avx(r2iqThreadArg *th);
void * r2iqThreadf_avx2(r2iqThreadArg *th);
void * r2iqThreadf_avx512(r2iqThreadArg *th);
fftwf_complex **filterHw; // Hw complex to each decimation ratio
fftwf_plan plan_t2f_r2c; // fftw plan buffers Freq to Time complex to complex per decimation ratio
fftwf_plan *plan_f2t_c2c; // fftw plan buffers Time to Freq real to complex per buffer
fftwf_plan plans_f2t_c2c[NDECIDX];
uint32_t processor_count;
r2iqThreadArg* threadArgs[N_MAX_R2IQ_THREADS];
std::mutex mutexR2iqControl; // r2iq control lock
std::thread r2iq_thread[N_MAX_R2IQ_THREADS]; // thread pointers
};
// assure, that ADC is not oversteered?
struct r2iqThreadArg {
r2iqThreadArg()
{
#if PRINT_INPUT_RANGE
MinMaxBlockCount = 0;
MinValue = 0;
MaxValue = 0;
#endif
}
float *ADCinTime; // point to each threads input buffers [nftt][n]
fftwf_complex *ADCinFreq; // buffers in frequency
fftwf_complex *inFreqTmp; // tmp decimation output buffers (after tune shift)
#if PRINT_INPUT_RANGE
int MinMaxBlockCount;
int16_t MinValue;
int16_t MaxValue;
#endif
};

View File

@ -0,0 +1,9 @@
#include "fft_mt_r2iq.h"
#include "sddc_config.h"
#include "fftw3.h"
#include "RadioHandler.h"
void * fft_mt_r2iq::r2iqThreadf_avx(r2iqThreadArg *th)
{
#include "fft_mt_r2iq_impl.hpp"
}

View File

@ -0,0 +1,9 @@
#include "fft_mt_r2iq.h"
#include "sddc_config.h"
#include "fftw3.h"
#include "RadioHandler.h"
void * fft_mt_r2iq::r2iqThreadf_avx2(r2iqThreadArg *th)
{
#include "fft_mt_r2iq_impl.hpp"
}

View File

@ -0,0 +1,9 @@
#include "fft_mt_r2iq.h"
#include "sddc_config.h"
#include "fftw3.h"
#include "RadioHandler.h"
void * fft_mt_r2iq::r2iqThreadf_avx512(r2iqThreadArg *th)
{
#include "fft_mt_r2iq_impl.hpp"
}

View File

@ -0,0 +1,9 @@
#include "fft_mt_r2iq.h"
#include "sddc_config.h"
#include "fftw3.h"
#include "RadioHandler.h"
void * fft_mt_r2iq::r2iqThreadf_def(r2iqThreadArg *th)
{
#include "fft_mt_r2iq_impl.hpp"
}

View File

@ -0,0 +1,171 @@
{
const int decimate = this->mdecimation;
const int mfft = this->mfftdim[decimate]; // = halfFft / 2^mdecimation
const fftwf_complex* filter = filterHw[decimate];
const bool lsb = this->getSideband();
const auto filter2 = &filter[halfFft - mfft / 2];
plan_f2t_c2c = &plans_f2t_c2c[decimate];
fftwf_complex* pout = nullptr;
int decimate_count = 0;
while (r2iqOn) {
const int16_t *dataADC; // pointer to input data
const int16_t *endloop; // pointer to end data to be copied to beginning
const int _mtunebin = this->mtunebin; // Update LO tune is possible during run
{
std::unique_lock<std::mutex> lk(mutexR2iqControl);
dataADC = inputbuffer->getReadPtr();
if (!r2iqOn)
return 0;
this->bufIdx = (this->bufIdx + 1) % QUEUE_SIZE;
endloop = inputbuffer->peekReadPtr(-1) + transferSamples - halfFft;
}
auto inloop = th->ADCinTime;
// @todo: move the following int16_t conversion to (32-bit) float
// directly inside the following loop (for "k < fftPerBuf")
// just before the forward fft "fftwf_execute_dft_r2c" is called
// idea: this should improve cache/memory locality
#if PRINT_INPUT_RANGE
std::pair<int16_t, int16_t> blockMinMax = std::make_pair<int16_t, int16_t>(0, 0);
#endif
if (!this->getRand()) // plain samples no ADC rand set
{
convert_float<false>(endloop, inloop, halfFft);
#if PRINT_INPUT_RANGE
auto minmax = std::minmax_element(dataADC, dataADC + transferSamples);
blockMinMax.first = *minmax.first;
blockMinMax.second = *minmax.second;
#endif
convert_float<false>(dataADC, inloop + halfFft, transferSamples);
}
else
{
convert_float<true>(endloop, inloop, halfFft);
convert_float<true>(dataADC, inloop + halfFft, transferSamples);
}
#if PRINT_INPUT_RANGE
th->MinValue = std::min(blockMinMax.first, th->MinValue);
th->MaxValue = std::max(blockMinMax.second, th->MaxValue);
++th->MinMaxBlockCount;
if (th->MinMaxBlockCount * processor_count / 3 >= DEFAULT_TRANSFERS_PER_SEC )
{
float minBits = (th->MinValue < 0) ? (log10f((float)(-th->MinValue)) / log10f(2.0f)) : -1.0f;
float maxBits = (th->MaxValue > 0) ? (log10f((float)(th->MaxValue)) / log10f(2.0f)) : -1.0f;
printf("r2iq: min = %d (%.1f bits) %.2f%%, max = %d (%.1f bits) %.2f%%\n",
(int)th->MinValue, minBits, th->MinValue *-100.0f / 32768.0f,
(int)th->MaxValue, maxBits, th->MaxValue * 100.0f / 32768.0f);
th->MinValue = 0;
th->MaxValue = 0;
th->MinMaxBlockCount = 0;
}
#endif
dataADC = nullptr;
inputbuffer->ReadDone();
// decimate in frequency plus tuning
if (decimate_count == 0)
pout = (fftwf_complex*)outputbuffer->getWritePtr();
decimate_count = (decimate_count + 1) & ((1 << decimate) - 1);
// Calculate the parameters for the first half
const auto count = std::min(mfft/2, halfFft - _mtunebin);
const auto source = &th->ADCinFreq[_mtunebin];
// Calculate the parameters for the second half
const auto start = std::max(0, mfft / 2 - _mtunebin);
const auto source2 = &th->ADCinFreq[_mtunebin - mfft / 2];
const auto dest = &th->inFreqTmp[mfft / 2];
for (int k = 0; k < fftPerBuf; k++)
{
// core of fast convolution including filter and decimation
// main part is 'overlap-scrap' (IMHO better name for 'overlap-save'), see
// https://en.wikipedia.org/wiki/Overlap%E2%80%93save_method
{
// FFT first stage: time to frequency, real to complex
// 'full' transformation size: 2 * halfFft
fftwf_execute_dft_r2c(plan_t2f_r2c, th->ADCinTime + (3 * halfFft / 2) * k, th->ADCinFreq);
// result now in th->ADCinFreq[]
// circular shift (mixing in full bins) and low/bandpass filtering (complex multiplication)
{
// circular shift tune fs/2 first half array into th->inFreqTmp[]
shift_freq(th->inFreqTmp, source, filter, 0, count);
if (mfft / 2 != count)
memset(th->inFreqTmp[count], 0, sizeof(float) * 2 * (mfft / 2 - count));
// circular shift tune fs/2 second half array
shift_freq(dest, source2, filter2, start, mfft/2);
if (start != 0)
memset(th->inFreqTmp[mfft / 2], 0, sizeof(float) * 2 * start);
}
// result now in th->inFreqTmp[]
// 'shorter' inverse FFT transform (decimation); frequency (back) to COMPLEX time domain
// transform size: mfft = mfftdim[k] = halfFft / 2^k with k = mdecimation
fftwf_execute_dft(*plan_f2t_c2c, th->inFreqTmp, th->inFreqTmp); // c2c decimation
// result now in th->inFreqTmp[]
}
// postprocessing
// @todo: is it possible to ..
// 1)
// let inverse FFT produce/save it's result directly
// in "this->obuffers[modx] + offset" (pout)
// ( obuffers[] would need to have additional space ..;
// need to move 'scrap' of 'ovelap-scrap'? )
// at least FFTW would allow so,
// see http://www.fftw.org/fftw3_doc/New_002darray-Execute-Functions.html
// attention: multithreading!
// 2)
// could mirroring (lower sideband) get calculated together
// with fine mixer - modifying the mixer frequency? (fs - fc)/fs
// (this would reduce one memory pass)
if (lsb) // lower sideband
{
// mirror just by negating the imaginary Q of complex I/Q
if (k == 0)
{
copy<true>(pout, &th->inFreqTmp[mfft / 4], mfft/2);
}
else
{
copy<true>(pout + mfft / 2 + (3 * mfft / 4) * (k - 1), &th->inFreqTmp[0], (3 * mfft / 4));
}
}
else // upper sideband
{
if (k == 0)
{
copy<false>(pout, &th->inFreqTmp[mfft / 4], mfft/2);
}
else
{
copy<false>(pout + mfft / 2 + (3 * mfft / 4) * (k - 1), &th->inFreqTmp[0], (3 * mfft / 4));
}
}
// result now in this->obuffers[]
}
if (decimate_count == 0) {
outputbuffer->WriteDone();
pout = nullptr;
}
else
{
pout += mfft / 2 + (3 * mfft / 4) * (fftPerBuf - 1);
}
} // while(run)
// DbgPrintf((char *) "r2iqThreadf idx %d pthread_exit %u\n",(int)th->t, pthread_self());
return 0;
}

View File

@ -0,0 +1,105 @@
#include "fir.h"
#include <math.h>
#define K_PI 3.141592653f
#define K_2PI (2*K_PI)
static float Izero(float x)
{
float x2 = x / 2.0f;
float sum = 1.0f;
float ds = 1.0f;
float di = 1.0f;
float errorlimit = 1e-9f;
float tmp;
do
{
tmp = x2 / di;
tmp *= tmp;
ds *= tmp;
sum += ds;
di += 1.0;
} while (ds >= errorlimit * sum);
//qDebug()<<"x="<<x<<" I0="<<sum;
return(sum);
}
////////////////////////////////////////////////////////////////////
// Create a FIR Low Pass filter
// num_taps if > 0, forces filter design to be this number of taps
// if < 0, limits filter design to be max negative number of taps
// Astop = Stopband Atenuation in dB (ie 40dB is 40dB stopband attenuation)
// normFpass = Lowpass passband frequency - relative to samplerate
// normFstop = Lowpass stopband frequency - relative to samplerate
// Coef = pointer to array, where to put the resulting (real) coefficients
// might be nullptr, to estimate the number of coefficients
// return the used/estimated number of coefficients
//
// -------------
// |
// |
// |
// |
// Astop ---------------
// Fpass Fstop
//
////////////////////////////////////////////////////////////////////
int KaiserWindow(int num_taps, float Astop, float normFpass, float normFstop, float *Coef)
{
int n;
float Beta;
float Scale = 1.0f; //+106dB over 1.0 to have high level in wavosaur spectrum analisys out. otherwise set to 1.0
// float Astop = 100.0f; // we want high attenuation 100 dB
//create normalized frequency parameters
float normFcut = (normFstop + normFpass) / 2.0f; //low pass filter 6dB cutoff
//calculate Kaiser-Bessel window shape factor, Beta, from stopband attenuation
if (Astop < 20.96f)
Beta = 0.0f;
else if (Astop >= 50.0f)
Beta = .1102f * (Astop - 8.71f);
else
Beta = .5842f * powf((Astop - 20.96f), 0.4f) + .07886f * (Astop - 20.96f);
/* I used this way but beta is Beta way is better
float Alpha = 3.5;
Beta = K_PI * Alpha;
*/
// now estimate number of filter taps required based on filter specs
int m_NumTaps = (Astop - 8.0) / (2.285*K_2PI*(normFstop - normFpass) ) + 1;
// clamp range of filter taps
if (num_taps < 0 && m_NumTaps > -num_taps)
m_NumTaps = -num_taps;
if (m_NumTaps < 3)
m_NumTaps = 3;
// early exit, if the user only wanted to estimate the number of taps
if (num_taps <= 0 && !Coef)
return m_NumTaps;
if (num_taps > 0)
m_NumTaps = num_taps;
float fCenter = .5f * (float)(m_NumTaps - 1);
float izb = Izero(Beta); //precalculate denominator since is same for all points
for (n = 0; n < m_NumTaps; n++)
{
float x = (float)n - fCenter;
float c;
// create ideal Sinc() LP filter with normFcut
if ((float)n == fCenter) //deal with odd size filter singularity where sin(0)/0==1
c = 2.0f * normFcut;
else
c = (float)sinf(K_2PI * x * normFcut) / (K_PI * x);
//calculate Kaiser window and multiply to get coefficient
x = ((float)n - ((float)m_NumTaps - 1.0f) / 2.0f) / (((float)m_NumTaps - 1.0f) / 2.0f);
Coef[n] = Scale * c * Izero(Beta * sqrtf(1 - (x * x))) / izb;
}
return m_NumTaps;
}

View File

@ -0,0 +1,3 @@
#pragma once
int KaiserWindow(int num_taps, float Astop, float normFpass, float normFstop, float * Coef);

View File

@ -0,0 +1,28 @@
#ifndef LICENSE_H_
#define LICENSE_H_
/*
The MIT License (MIT)
Copyright (c) 2017-2020 Oscar Steila ik1xpv<at>gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.The MIT License (MIT)
The MIT License (MIT)
*/
#endif // LICENSE_H_

View File

@ -0,0 +1,38 @@
Copyright (c) 2020 Dario Mambro ( dario.mambro@gmail.com )
Copyright (c) 2019 Hayati Ayguen ( h_ayguen@web.de )
Copyright (c) 2013 Julien Pommier ( pommier@modartt.com )
Copyright (c) 2004 the University Corporation for Atmospheric
Research ("UCAR"). All rights reserved. Developed by NCAR's
Computational and Information Systems Laboratory, UCAR,
www.cisl.ucar.edu.
Redistribution and use of the Software in source and binary forms,
with or without modification, is permitted provided that the
following conditions are met:
- Neither the names of NCAR's Computational and Information Systems
Laboratory, the University Corporation for Atmospheric Research,
nor the names of its sponsors or contributors may be used to
endorse or promote products derived from this Software without
specific prior written permission.
- Redistributions of source code must retain the above copyright
notices, this list of conditions, and the disclaimer below.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the disclaimer below in the
documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.

View File

@ -0,0 +1,733 @@
/*
Copyright (c) 2020 Hayati Ayguen ( h_ayguen@web.de )
bench for mixer algorithm/implementations
*/
#include "pf_mixer.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <string.h>
//#define HAVE_SYS_TIMES
#ifdef HAVE_SYS_TIMES
# include <sys/times.h>
# include <unistd.h>
#endif
#define BENCH_REF_TRIG_FUNC 1
#define BENCH_OUT_OF_PLACE_ALGOS 0
#define BENCH_INPLACE_ALGOS 1
#define SAVE_BY_DEFAULT 1
#define SAVE_LIMIT_MSPS 16
#if 1
#define BENCH_FILE_SHIFT_MATH_CC "A_shift_math_cc.bin"
#define BENCH_FILE_ADD_FAST_CC "C_shift_addfast_cc.bin"
#define BENCH_FILE_ADD_FAST_INP_C "C_shift_addfast_inp_c.bin"
#define BENCH_FILE_UNROLL_INP_C "D_shift_unroll_inp_c.bin"
#define BENCH_FILE_LTD_UNROLL_INP_C "E_shift_limited_unroll_inp_c.bin"
#define BENCH_FILE_LTD_UNROLL_A_SSE_INP_C "F_shift_limited_unroll_A_sse_inp_c.bin"
#define BENCH_FILE_LTD_UNROLL_B_SSE_INP_C "G_shift_limited_unroll_B_sse_inp_c.bin"
#define BENCH_FILE_LTD_UNROLL_C_SSE_INP_C "H_shift_limited_unroll_C_sse_inp_c.bin"
#define BENCH_FILE_REC_OSC_CC ""
#define BENCH_FILE_REC_OSC_INP_C "I_shift_recursive_osc_inp_c.bin"
#define BENCH_FILE_REC_OSC_SSE_INP_C "J_shift_recursive_osc_sse_inp_c.bin"
#else
#define BENCH_FILE_SHIFT_MATH_CC ""
#define BENCH_FILE_ADD_FAST_CC ""
#define BENCH_FILE_ADD_FAST_INP_C ""
#define BENCH_FILE_UNROLL_INP_C ""
#define BENCH_FILE_LTD_UNROLL_INP_C ""
#define BENCH_FILE_LTD_UNROLL_A_SSE_INP_C ""
#define BENCH_FILE_LTD_UNROLL_B_SSE_INP_C ""
#define BENCH_FILE_LTD_UNROLL_C_SSE_INP_C ""
#define BENCH_FILE_REC_OSC_CC ""
#define BENCH_FILE_REC_OSC_INP_C ""
#define BENCH_FILE_REC_OSC_SSE_INP_C ""
#endif
#if defined(HAVE_SYS_TIMES)
static double ttclk = 0.;
static double uclock_sec(int find_start)
{
struct tms t0, t;
if (ttclk == 0.)
{
ttclk = sysconf(_SC_CLK_TCK);
fprintf(stderr, "sysconf(_SC_CLK_TCK) => %f\n", ttclk);
}
times(&t);
if (find_start)
{
t0 = t;
while (t0.tms_utime == t.tms_utime)
times(&t);
}
/* use only the user time of this process - not realtime, which depends on OS-scheduler .. */
return ((double)t.tms_utime) / ttclk;
}
#elif 0
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes
double uclock_sec(int find_start)
{
FILETIME a, b, c, d;
if (GetProcessTimes(GetCurrentProcess(), &a, &b, &c, &d) != 0)
{
// Returns total user time.
// Can be tweaked to include kernel times as well.
return
(double)(d.dwLowDateTime |
((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001;
}
else {
// Handle error
return 0;
}
}
#else
double uclock_sec(int find_start)
{ return (double)clock()/(double)CLOCKS_PER_SEC; }
#endif
void save(complexf * d, int B, int N, const char * fn)
{
if (!fn || !fn[0])
{
if (! SAVE_BY_DEFAULT)
return;
fn = "bench.bin";
}
FILE* f;
fopen_s(&f, fn, "wb");
if (!f) {
fprintf(stderr, "error writing result to %s\n", fn);
return;
}else
printf( "saving to %s\n", fn);
if ( N >= SAVE_LIMIT_MSPS * 1024 * 1024 )
N = SAVE_LIMIT_MSPS * 1024 * 1024;
for (int off = 0; off + B <= N; off += B)
{
fwrite(d+off, sizeof(complexf), B, f);
}
fclose(f);
}
double bench_shift_math_cc(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
complexf *output = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
phase = shift_math_cc(input+off, output+off, B, -0.0009F, phase);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(output, B, off, BENCH_FILE_SHIFT_MATH_CC);
free(input);
free(output);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_table_cc(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
int table_size=65536;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
complexf *output = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_table_data_t table_data = shift_table_init(table_size);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
phase = shift_table_cc(input+off, output+off, B, -0.0009F, table_data, phase);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(output, B, off, NULL);
free(input);
free(output);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_addfast(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
complexf *output = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_addfast_data_t state = shift_addfast_init(-0.0009F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
phase = shift_addfast_cc(input+off, output+off, B, &state, phase);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(output, B, off, BENCH_FILE_ADD_FAST_CC);
free(input);
free(output);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_addfast_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_addfast_data_t state = shift_addfast_init(-0.0009F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
phase = shift_addfast_inp_c(input+off, B, &state, phase);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_ADD_FAST_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_unroll_oop(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
complexf *output = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_unroll_data_t state = shift_unroll_init(-0.0009F, B);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
phase = shift_unroll_cc(input+off, output+off, B, &state, phase);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(output, B, off, NULL);
free(input);
free(output);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_unroll_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_unroll_data_t state = shift_unroll_init(-0.0009F, B);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
phase = shift_unroll_inp_c(input+off, B, &state, phase);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_UNROLL_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_limited_unroll_oop(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
complexf *output = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_limited_unroll_data_t state = shift_limited_unroll_init(-0.0009F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_limited_unroll_cc(input+off, output+off, B, &state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(output, B, off, NULL);
free(input);
free(output);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_limited_unroll_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_limited_unroll_data_t state = shift_limited_unroll_init(-0.0009F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_limited_unroll_inp_c(input+off, B, &state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_LTD_UNROLL_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_limited_unroll_A_sse_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_limited_unroll_A_sse_data_t *state = malloc(sizeof(shift_limited_unroll_A_sse_data_t));
*state = shift_limited_unroll_A_sse_init(-0.0009F, 0.0F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_limited_unroll_A_sse_inp_c(input+off, B, state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_LTD_UNROLL_A_SSE_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_limited_unroll_B_sse_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_limited_unroll_B_sse_data_t *state = malloc(sizeof(shift_limited_unroll_B_sse_data_t));
*state = shift_limited_unroll_B_sse_init(-0.0009F, 0.0F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
//shift_recursive_osc_init(0.0F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_limited_unroll_B_sse_inp_c(input+off, B, state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_LTD_UNROLL_B_SSE_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_limited_unroll_C_sse_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_limited_unroll_C_sse_data_t *state = malloc(sizeof(shift_limited_unroll_C_sse_data_t));
*state = shift_limited_unroll_C_sse_init(-0.0009F, 0.0F);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_limited_unroll_C_sse_inp_c(input+off, B, state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_LTD_UNROLL_C_SSE_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_rec_osc_cc_oop(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
complexf *output = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state, shift_state;
shift_recursive_osc_conf_t gen_conf, shift_conf;
shift_recursive_osc_init(-0.0009F, 0.0F, &shift_conf, &shift_state);
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_recursive_osc_cc(input+off, output+off, B, &shift_conf, &shift_state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_REC_OSC_CC);
save(output, B, off, NULL);
free(input);
free(output);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_rec_osc_cc_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state, shift_state;
shift_recursive_osc_conf_t gen_conf, shift_conf;
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
shift_recursive_osc_init(-0.0009F, 0.0F, &shift_conf, &shift_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_recursive_osc_inp_c(input+off, B, &shift_conf, &shift_state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_REC_OSC_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
double bench_shift_rec_osc_sse_c_inp(int B, int N) {
double t0, t1, tstop, T, nI;
int iter, off;
float phase = 0.0F;
complexf *input = (complexf *)malloc(N * sizeof(complexf));
shift_recursive_osc_t gen_state;
shift_recursive_osc_conf_t gen_conf;
shift_recursive_osc_sse_t *shift_state = malloc(sizeof(shift_recursive_osc_sse_t));
shift_recursive_osc_sse_conf_t shift_conf;
shift_recursive_osc_init(0.001F, 0.0F, &gen_conf, &gen_state);
gen_recursive_osc_c(input, N, &gen_conf, &gen_state);
shift_recursive_osc_sse_init(-0.0009F, 0.0F, &shift_conf, shift_state);
iter = 0;
off = 0;
t0 = uclock_sec(1);
tstop = t0 + 0.5; /* benchmark duration: 500 ms */
do {
// work
shift_recursive_osc_sse_inp_c(input+off, B, &shift_conf, shift_state);
off += B;
++iter;
t1 = uclock_sec(0);
} while ( t1 < tstop && off + B < N );
save(input, B, off, BENCH_FILE_REC_OSC_SSE_INP_C);
free(input);
T = ( t1 - t0 ); /* duration per fft() */
printf("processed %f Msamples in %f ms\n", off * 1E-6, T*1E3);
nI = ((double)iter) * B; /* number of iterations "normalized" to O(N) = N */
return (nI / T); /* normalized iterations per second */
}
int main(int argc, char **argv)
{
double rt;
// process up to 64 MSample (512 MByte) in blocks of 8 kSamples (=64 kByte)
int B = 8 * 1024;
int N = 64 * 1024 * 1024;
int showUsage = 0;
if (argc == 1)
showUsage = 1;
if (1 < argc)
B = atoi(argv[1]);
if (2 < argc)
N = atoi(argv[2]) * 1024 * 1024;
if ( !B || !N || showUsage )
{
fprintf(stderr, "%s [<blockLength in samples> [<total # of MSamples>] ]\n", argv[0]);
if ( !B || !N )
return 0;
}
fprintf(stderr, "processing up to N = %d MSamples with blocke length of %d samples\n",
N / (1024 * 1024), B );
#if BENCH_REF_TRIG_FUNC
printf("\nstarting bench of shift_math_cc (out-of-place) with trig functions ..\n");
rt = bench_shift_math_cc(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
#endif
#if BENCH_OUT_OF_PLACE_ALGOS
printf("starting bench of shift_table_cc (out-of-place) ..\n");
rt = bench_shift_table_cc(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("starting bench of shift_addfast_cc (out-of-place) ..\n");
rt = bench_shift_addfast(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("\nstarting bench of shift_unroll_cc (out-of-place) ..\n");
rt = bench_shift_unroll_oop(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("\nstarting bench of shift_limited_unroll_cc (out-of-place) ..\n");
rt = bench_shift_limited_unroll_oop(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("\nstarting bench of shift_recursive_osc_cc (out-of-place) ..\n");
rt = bench_shift_rec_osc_cc_oop(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
#endif
#if BENCH_INPLACE_ALGOS
printf("starting bench of shift_addfast_inp_c in-place ..\n");
rt = bench_shift_addfast_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("starting bench of shift_unroll_inp_c in-place ..\n");
rt = bench_shift_unroll_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("starting bench of shift_limited_unroll_inp_c in-place ..\n");
rt = bench_shift_limited_unroll_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
if ( have_sse_shift_mixer_impl() )
{
printf("starting bench of shift_limited_unroll_A_sse_inp_c in-place ..\n");
rt = bench_shift_limited_unroll_A_sse_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("starting bench of shift_limited_unroll_B_sse_inp_c in-place ..\n");
rt = bench_shift_limited_unroll_B_sse_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
printf("starting bench of shift_limited_unroll_C_sse_inp_c in-place ..\n");
rt = bench_shift_limited_unroll_C_sse_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
}
printf("starting bench of shift_recursive_osc_cc in-place ..\n");
rt = bench_shift_rec_osc_cc_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
if ( have_sse_shift_mixer_impl() )
{
printf("starting bench of shift_recursive_osc_sse_c in-place ..\n");
rt = bench_shift_rec_osc_sse_c_inp(B, N);
printf(" %f MSamples/sec\n\n", rt * 1E-6);
}
#endif
return 0;
}

View File

@ -0,0 +1,20 @@
#ifndef FMV_H
#if HAVE_FUNC_ATTRIBUTE_IFUNC
#if defined(__has_attribute)
#if __has_attribute(target_clones)
#if defined(__x86_64)
// see https://gcc.gnu.org/wiki/FunctionMultiVersioning
#define PF_TARGET_CLONES __attribute__((target_clones("avx","sse4.2","sse3","sse2","sse","default")))
#define HAVE_PF_TARGET_CLONES 1
#endif
#endif
#endif
#endif
#ifndef PF_TARGET_CLONES
#define PF_TARGET_CLONES
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,283 @@
/*
This software is part of pffft/pfdsp, a set of simple DSP routines.
Copyright (c) 2014, Andras Retzler <randras@sdr.hu>
Copyright (c) 2020 Hayati Ayguen <h_ayguen@web.de>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ANDRAS RETZLER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _PF_MIXER_H_
#define _PF_MIXER_H_
#include <stdio.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
_____ _
/ ____| | |
| | ___ _ __ ___ _ __ | | _____ __
| | / _ \| '_ ` _ \| '_ \| |/ _ \ \/ /
| |___| (_) | | | | | | |_) | | __/> <
\_____\___/|_| |_| |_| .__/|_|\___/_/\_\
| |
|_|
*/
typedef struct complexf_s { float i; float q; } complexf;
// =================================================================================
int have_sse_shift_mixer_impl();
/*********************************************************************/
/**************/
/*** ALGO A ***/
/**************/
float shift_math_cc(complexf *input, complexf* output, int input_size, float rate, float starting_phase);
/*********************************************************************/
/**************/
/*** ALGO B ***/
/**************/
typedef struct shift_table_data_s
{
float* table;
int table_size;
} shift_table_data_t;
void shift_table_deinit(shift_table_data_t table_data);
shift_table_data_t shift_table_init(int table_size);
float shift_table_cc(complexf* input, complexf* output, int input_size, float rate, shift_table_data_t table_data, float starting_phase);
/*********************************************************************/
/**************/
/*** ALGO C ***/
/**************/
typedef struct shift_addfast_data_s
{
float dsin[4];
float dcos[4];
float phase_increment;
} shift_addfast_data_t;
shift_addfast_data_t shift_addfast_init(float rate);
float shift_addfast_cc(complexf *input, complexf* output, int input_size, shift_addfast_data_t* d, float starting_phase);
float shift_addfast_inp_c(complexf *in_out, int N_cplx, shift_addfast_data_t* d, float starting_phase);
/*********************************************************************/
/**************/
/*** ALGO D ***/
/**************/
typedef struct shift_unroll_data_s
{
float* dsin;
float* dcos;
float phase_increment;
int size;
} shift_unroll_data_t;
shift_unroll_data_t shift_unroll_init(float rate, int size);
void shift_unroll_deinit(shift_unroll_data_t* d);
float shift_unroll_cc(complexf *input, complexf* output, int size, shift_unroll_data_t* d, float starting_phase);
float shift_unroll_inp_c(complexf* in_out, int size, shift_unroll_data_t* d, float starting_phase);
/*********************************************************************/
/**************/
/*** ALGO E ***/
/**************/
/* similar to shift_unroll_cc() - but, have fixed and limited precalc size
* idea: smaller cache usage by table
* size must be multiple of CSDR_SHIFT_LIMITED_SIMD (= 4)
*/
#define PF_SHIFT_LIMITED_UNROLL_SIZE 128
#define PF_SHIFT_LIMITED_SIMD_SZ 4
typedef struct shift_limited_unroll_data_s
{
float dcos[PF_SHIFT_LIMITED_UNROLL_SIZE];
float dsin[PF_SHIFT_LIMITED_UNROLL_SIZE];
complexf complex_phase;
float phase_increment;
} shift_limited_unroll_data_t;
shift_limited_unroll_data_t shift_limited_unroll_init(float rate);
/* size must be multiple of PF_SHIFT_LIMITED_SIMD_SZ */
/* starting_phase for next call is kept internal in state */
void shift_limited_unroll_cc(const complexf *input, complexf* output, int size, shift_limited_unroll_data_t* d);
void shift_limited_unroll_inp_c(complexf* in_out, int size, shift_limited_unroll_data_t* d);
/*********************************************************************/
/**************/
/*** ALGO F ***/
/**************/
typedef struct shift_limited_unroll_A_sse_data_s
{
/* small/limited trig table */
float dcos[PF_SHIFT_LIMITED_UNROLL_SIZE+PF_SHIFT_LIMITED_SIMD_SZ];
float dsin[PF_SHIFT_LIMITED_UNROLL_SIZE+PF_SHIFT_LIMITED_SIMD_SZ];
/* 4 times complex phase */
float phase_state_i[PF_SHIFT_LIMITED_SIMD_SZ];
float phase_state_q[PF_SHIFT_LIMITED_SIMD_SZ];
/* N_cplx_per_block times increment - for future parallel variants */
float dcos_blk;
float dsin_blk;
/* */
float phase_increment;
} shift_limited_unroll_A_sse_data_t;
shift_limited_unroll_A_sse_data_t shift_limited_unroll_A_sse_init(float relative_freq, float phase_start_rad);
void shift_limited_unroll_A_sse_inp_c(complexf* in_out, int N_cplx, shift_limited_unroll_A_sse_data_t* d);
/*********************************************************************/
/**************/
/*** ALGO G ***/
/**************/
typedef struct shift_limited_unroll_B_sse_data_s
{
/* small/limited trig table */
float dtrig[PF_SHIFT_LIMITED_UNROLL_SIZE+PF_SHIFT_LIMITED_SIMD_SZ];
/* 4 times complex phase */
float phase_state_i[PF_SHIFT_LIMITED_SIMD_SZ];
float phase_state_q[PF_SHIFT_LIMITED_SIMD_SZ];
/* N_cplx_per_block times increment - for future parallel variants */
float dcos_blk;
float dsin_blk;
/* */
float phase_increment;
} shift_limited_unroll_B_sse_data_t;
shift_limited_unroll_B_sse_data_t shift_limited_unroll_B_sse_init(float relative_freq, float phase_start_rad);
void shift_limited_unroll_B_sse_inp_c(complexf* in_out, int N_cplx, shift_limited_unroll_B_sse_data_t* d);
/*********************************************************************/
/**************/
/*** ALGO H ***/
/**************/
typedef struct shift_limited_unroll_C_sse_data_s
{
/* small/limited trig table - interleaved: 4 cos, 4 sin, 4 cos, .. */
float dinterl_trig[2*(PF_SHIFT_LIMITED_UNROLL_SIZE+PF_SHIFT_LIMITED_SIMD_SZ)];
/* 4 times complex phase */
float phase_state_i[PF_SHIFT_LIMITED_SIMD_SZ];
float phase_state_q[PF_SHIFT_LIMITED_SIMD_SZ];
/* N_cplx_per_block times increment - for future parallel variants */
float dcos_blk;
float dsin_blk;
/* */
float phase_increment;
} shift_limited_unroll_C_sse_data_t;
shift_limited_unroll_C_sse_data_t shift_limited_unroll_C_sse_init(float relative_freq, float phase_start_rad);
void shift_limited_unroll_C_sse_inp_c(complexf* in_out, int N_cplx, shift_limited_unroll_C_sse_data_t* d);
/*********************************************************************/
/**************/
/*** ALGO I ***/
/**************/
/* Recursive Quadrature Oscillator functions "recursive_osc"
* see https://www.vicanek.de/articles/QuadOsc.pdf
*/
#define PF_SHIFT_RECURSIVE_SIMD_SZ 8
typedef struct shift_recursive_osc_s
{
float u_cos[PF_SHIFT_RECURSIVE_SIMD_SZ];
float v_sin[PF_SHIFT_RECURSIVE_SIMD_SZ];
} shift_recursive_osc_t;
typedef struct shift_recursive_osc_conf_s
{
float k1;
float k2;
} shift_recursive_osc_conf_t;
void shift_recursive_osc_init(float rate, float starting_phase, shift_recursive_osc_conf_t *conf, shift_recursive_osc_t *state);
void shift_recursive_osc_update_rate(float rate, shift_recursive_osc_conf_t *conf, shift_recursive_osc_t* state);
/* size must be multiple of PF_SHIFT_LIMITED_SIMD_SZ */
/* starting_phase for next call is kept internal in state */
void shift_recursive_osc_cc(const complexf *input, complexf* output, int size, const shift_recursive_osc_conf_t *conf, shift_recursive_osc_t* state);
void shift_recursive_osc_inp_c(complexf* output, int size, const shift_recursive_osc_conf_t *conf, shift_recursive_osc_t* state);
void gen_recursive_osc_c(complexf* output, int size, const shift_recursive_osc_conf_t *conf, shift_recursive_osc_t* state);
/*********************************************************************/
/**************/
/*** ALGO J ***/
/**************/
#define PF_SHIFT_RECURSIVE_SIMD_SSE_SZ 4
typedef struct shift_recursive_osc_sse_s
{
float u_cos[PF_SHIFT_RECURSIVE_SIMD_SSE_SZ];
float v_sin[PF_SHIFT_RECURSIVE_SIMD_SSE_SZ];
} shift_recursive_osc_sse_t;
typedef struct shift_recursive_osc_sse_conf_s
{
float k1;
float k2;
} shift_recursive_osc_sse_conf_t;
void shift_recursive_osc_sse_init(float rate, float starting_phase, shift_recursive_osc_sse_conf_t *conf, shift_recursive_osc_sse_t *state);
void shift_recursive_osc_sse_update_rate(float rate, shift_recursive_osc_sse_conf_t *conf, shift_recursive_osc_sse_t* state);
void shift_recursive_osc_sse_inp_c(complexf* in_out, int N_cplx, const shift_recursive_osc_sse_conf_t *conf, shift_recursive_osc_sse_t* state_ext);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,50 @@
#ifndef R2IQ_H
#define R2IQ_H
#include "license.txt"
#define NDECIDX 7 //number of srate
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include "dsp/ringbuffer.h"
struct r2iqThreadArg;
class r2iqControlClass {
public:
r2iqControlClass();
virtual ~r2iqControlClass() {}
int getRatio() {return mratio [mdecimation];}
void updateRand(bool v) { this->randADC = v; }
bool getRand() const { return this->randADC; }
void setSideband(bool lsb) { this->sideband = lsb; }
bool getSideband() const { return this->sideband; }
void setDecimate(int dec) {this->mdecimation = dec; }
virtual void Init(float gain, ringbuffer<int16_t>* input, ringbuffer<float>* obuffers) {}
virtual void TurnOn() { this->r2iqOn = true; }
virtual void TurnOff(void) { this->r2iqOn = false; }
virtual bool IsOn(void) { return this->r2iqOn; }
virtual void DataReady(void) {}
virtual float setFreqOffset(float offset) { return 0; };
protected:
int mdecimation ; // selected decimation ratio
// 64 Msps: 0 => 32Msps, 1=> 16Msps, 2 = 8Msps, 3 = 4Msps, 4 = 2Msps
// 128 Msps: 0 => 64Msps, 1 => 32Msps, 2=> 16Msps, 3 = 8Msps, 4 = 4Msps, 5 = 2Msps
bool r2iqOn; // r2iq on flag
int mratio [NDECIDX]; // ratio
private:
bool randADC; // randomized ADC output
bool sideband;
};
#endif

View File

@ -0,0 +1,145 @@
#include "RadioHandler.h"
#define R820T_FREQ (32000000) // R820T reference frequency
#define R820T2_IF_CARRIER (4570000)
const float BBRF103Radio::steps[BBRF103Radio::step_size] = {
0.0f, 0.9f, 1.4f, 2.7f, 3.7f, 7.7f, 8.7f, 12.5f, 14.4f, 15.7f,
16.6f, 19.7f, 20.7f, 22.9f, 25.4f, 28.0f, 29.7f, 32.8f,
33.8f, 36.4f, 37.2f, 38.6f, 40.2f, 42.1f, 43.4f, 43.9f,
44.5f, 48.0f, 49.6f
};
const float BBRF103Radio::if_steps[BBRF103Radio::if_step_size] = {
-4.7f, -2.1f, 0.5f, 3.5f, 7.7f, 11.2f, 13.6f, 14.9f, 16.3f, 19.5f, 23.1f, 26.5f, 30.0f, 33.7f, 37.2f, 40.8f
};
const float BBRF103Radio::hfsteps[3] = {
-20.0f, -10.0f, 0.0f
};
BBRF103Radio::BBRF103Radio(fx3class* fx3)
: RadioHardware(fx3)
{
}
void BBRF103Radio::Initialize(uint32_t adc_rate)
{
this->SampleRate = adc_rate;
Fx3->Control(STARTADC, adc_rate);
}
rf_mode BBRF103Radio::PrepareLo(uint64_t freq)
{
if (freq < 10 * 1000) return NOMODE;
if (freq > 1750 * 1000 * 1000) return NOMODE;
if ( freq >= this->SampleRate / 2)
return VHFMODE;
else
return HFMODE;
}
bool BBRF103Radio::UpdatemodeRF(rf_mode mode)
{
if (mode == VHFMODE)
{
// switch to VHF Attenna
FX3UnsetGPIO(ATT_SEL0 | ATT_SEL1);
// Initialize Tuner
return Fx3->Control(TUNERINIT, (uint32_t)R820T_FREQ);
}
else if (mode == HFMODE ) // (mode == HFMODE || mode == VLFMODE) no more VLFMODE
{
// Stop Tuner
Fx3->Control(TUNERSTDBY);
// switch to HF Attenna
return FX3SetGPIO(ATT_SEL0 | ATT_SEL1);
}
return false;
}
bool BBRF103Radio::UpdateattRF(int att)
{
if (gpios & (ATT_SEL0 | ATT_SEL1)) {
// this is in HF mode
if (att > 2) att = 2;
if (att < 0) att = 0;
switch (att)
{
case 1: //11
gpios |= ATT_SEL0 | ATT_SEL1;
break;
case 0: //01
gpios |= ATT_SEL0;
gpios &= ~ATT_SEL1;
break;
case 2: //10
default:
gpios |= ATT_SEL1;
gpios &= ~ATT_SEL0;
break;
}
return Fx3->Control(GPIOFX3, gpios);
}
else {
uint16_t index = att;
// this is in VHF mode
return Fx3->SetArgument(R82XX_ATTENUATOR, index);
}
}
uint64_t BBRF103Radio::TuneLo(uint64_t freq)
{
if (gpios & (ATT_SEL0 | ATT_SEL1)) {
// this is in HF mode
return 0;
}
else {
// this is in VHF mode
Fx3->Control(TUNERTUNE, freq);
return freq - R820T2_IF_CARRIER;
}
}
int BBRF103Radio::getRFSteps(const float** steps )
{
if (gpios & (ATT_SEL0 | ATT_SEL1)) {
*steps = this->hfsteps;
return 3;
}
else
{
*steps = this->steps;
return step_size;
}
}
int BBRF103Radio::getIFSteps(const float** steps )
{
if (gpios & (ATT_SEL0 | ATT_SEL1)) {
return 0;
}
else
{
*steps = this->if_steps;
return if_step_size;
}
}
bool BBRF103Radio::UpdateGainIF(int attIndex)
{
if (gpios & (ATT_SEL0 | ATT_SEL1)) {
// this is in HF mode
return false;
}
else {
// this is in VHF mode
return Fx3->SetArgument(R82XX_VGA, (uint16_t)attIndex);
}
}

View File

@ -0,0 +1,50 @@
#include "RadioHandler.h"
HF103Radio::HF103Radio(fx3class* fx3)
: RadioHardware(fx3)
{
// initialize steps
for (uint8_t i = 0 ; i < step_size; i++) {
this->steps[step_size - i - 1] = -(
((i & 0x01) != 0) * 0.5f +
((i & 0x02) != 0) * 1.0f +
((i & 0x04) != 0) * 2.0f +
((i & 0x08) != 0) * 4.0f +
((i & 0x010) != 0) * 8.0f +
((i & 0x020) != 0) * 16.0f
);
}
}
rf_mode HF103Radio::PrepareLo(uint64_t freq)
{
if (freq > 32 * 1000 * 1000) return NOMODE;
return HFMODE;
}
bool HF103Radio::UpdatemodeRF(rf_mode mode)
{
if (mode == VHFMODE)
return false;
return true;
}
bool HF103Radio::UpdateattRF(int att)
{
if (att > step_size - 1) att = step_size - 1;
if (att < 0) att = 0;
uint8_t d = step_size - att - 1;
DbgPrintf("UpdateattRF %f \n", this->steps[att]);
return Fx3->SetArgument(DAT31_ATT, d);
}
int HF103Radio::getRFSteps(const float** steps )
{
*steps = this->steps;
return step_size;
}

View File

@ -0,0 +1,178 @@
#include "RadioHandler.h"
#define R828D_FREQ (16000000) // R820T reference frequency
#define R828D_IF_CARRIER (4570000)
#define HIGH_MODE 0x80
#define LOW_MODE 0x00
#define GAIN_SWEET_POINT 18
#define HIGH_GAIN_RATIO (0.409f)
#define LOW_GAIN_RATIO (0.059f)
#define MODE HIGH_MODE
const float RX888R2Radio::vhf_rf_steps[RX888R2Radio::vhf_rf_step_size] = {
0.0f, 0.9f, 1.4f, 2.7f, 3.7f, 7.7f, 8.7f, 12.5f, 14.4f, 15.7f,
16.6f, 19.7f, 20.7f, 22.9f, 25.4f, 28.0f, 29.7f, 32.8f,
33.8f, 36.4f, 37.2f, 38.6f, 40.2f, 42.1f, 43.4f, 43.9f,
44.5f, 48.0f, 49.6f};
const float RX888R2Radio::vhf_if_steps[RX888R2Radio::vhf_if_step_size] = {
-4.7f, -2.1f, 0.5f, 3.5f, 7.7f, 11.2f, 13.6f, 14.9f, 16.3f, 19.5f, 23.1f, 26.5f, 30.0f, 33.7f, 37.2f, 40.8f};
RX888R2Radio::RX888R2Radio(fx3class *fx3)
: RadioHardware(fx3)
{
for (uint8_t i = 0; i < hf_rf_step_size; i++)
{
this->hf_rf_steps[hf_rf_step_size - i - 1] = -(
((i & 0x01) != 0) * 0.5f +
((i & 0x02) != 0) * 1.0f +
((i & 0x04) != 0) * 2.0f +
((i & 0x08) != 0) * 4.0f +
((i & 0x010) != 0) * 8.0f +
((i & 0x020) != 0) * 16.0f);
}
for (uint8_t i = 0; i < hf_if_step_size; i++)
{
if (i > GAIN_SWEET_POINT)
this->hf_if_steps[i] = 20.0f * log10f(HIGH_GAIN_RATIO * (i - GAIN_SWEET_POINT + 3));
else
this->hf_if_steps[i] = 20.0f * log10f(LOW_GAIN_RATIO * (i + 1));
}
}
void RX888R2Radio::Initialize(uint32_t adc_rate)
{
SampleRate = adc_rate;
Fx3->Control(STARTADC, adc_rate);
}
rf_mode RX888R2Radio::PrepareLo(uint64_t freq)
{
if (freq < 10 * 1000) return NOMODE;
if (freq > 1750 * 1000 * 1000) return NOMODE;
if ( freq >= this->SampleRate / 2)
return VHFMODE;
else
return HFMODE;
}
bool RX888R2Radio::UpdatemodeRF(rf_mode mode)
{
if (mode == VHFMODE)
{
// disable HF by set max ATT
UpdateattRF(0); // max att 0 -> -31.5 dB
// switch to VHF Attenna
FX3SetGPIO(VHF_EN);
// high gain, 0db
uint8_t gain = 0x80 | 3;
Fx3->SetArgument(AD8340_VGA, gain);
// Enable Tuner reference clock
uint32_t ref = R828D_FREQ;
return Fx3->Control(TUNERINIT, ref); // Initialize Tuner
}
else if (mode == HFMODE)
{
Fx3->Control(TUNERSTDBY); // Stop Tuner
return FX3UnsetGPIO(VHF_EN); // switch to HF Attenna
}
return false;
}
bool RX888R2Radio::UpdateattRF(int att)
{
if (!(gpios & VHF_EN))
{
// hf mode
if (att > hf_rf_step_size - 1)
att = hf_rf_step_size - 1;
if (att < 0)
att = 0;
uint8_t d = hf_rf_step_size - att - 1;
DbgPrintf("UpdateattRF %f \n", this->hf_rf_steps[att]);
return Fx3->SetArgument(DAT31_ATT, d);
}
else
{
uint16_t index = att;
// this is in VHF mode
return Fx3->SetArgument(R82XX_ATTENUATOR, index);
}
}
uint64_t RX888R2Radio::TuneLo(uint64_t freq)
{
if (!(gpios & VHF_EN))
{
// this is in HF mode
return 0;
}
else
{
// this is in VHF mode
Fx3->Control(TUNERTUNE, freq);
return freq - R828D_IF_CARRIER;
}
}
int RX888R2Radio::getRFSteps(const float **steps)
{
if (!(gpios & VHF_EN))
{
// hf mode
*steps = this->hf_rf_steps;
return hf_rf_step_size;
}
else
{
*steps = this->vhf_rf_steps;
return vhf_rf_step_size;
}
}
int RX888R2Radio::getIFSteps(const float **steps)
{
if (!(gpios & VHF_EN))
{
*steps = this->hf_if_steps;
return hf_if_step_size;
}
else
{
*steps = this->vhf_if_steps;
return vhf_if_step_size;
}
}
bool RX888R2Radio::UpdateGainIF(int gain_index)
{
if (!(gpios & VHF_EN))
{
// this is in HF mode
uint8_t gain;
if (gain_index > GAIN_SWEET_POINT)
gain = HIGH_MODE | (gain_index - GAIN_SWEET_POINT + 3);
else
gain = LOW_MODE | (gain_index + 1);
DbgPrintf("UpdateGainIF %d \n", gain);
return Fx3->SetArgument(AD8340_VGA, gain);
}
else
{
// this is in VHF mode
return Fx3->SetArgument(R82XX_VGA, (uint16_t)gain_index);
}
}

View File

@ -0,0 +1,210 @@
#include "RadioHandler.h"
#define REFCLK_FREQ (27000000) // R820T reference frequency
#define IF_FREQ (20000000)
#define HIGH_MODE 0x80
#define LOW_MODE 0x00
#define GAIN_SWEET_POINT 18
#define HIGH_GAIN_RATIO (0.409f)
#define LOW_GAIN_RATIO (0.059f)
#define MODE HIGH_MODE
const float RX888R3Radio::vhf_rf_steps[RX888R3Radio::vhf_rf_step_size] = {
0.0f, 0.9f, 1.4f, 2.7f, 3.7f, 7.7f, 8.7f, 12.5f, 14.4f, 15.7f,
16.6f, 19.7f, 20.7f, 22.9f, 25.4f, 28.0f, 29.7f, 32.8f,
33.8f, 36.4f, 37.2f, 38.6f, 40.2f, 42.1f, 43.4f, 43.9f,
44.5f, 48.0f, 49.6f};
const float RX888R3Radio::vhf_if_steps[RX888R3Radio::vhf_if_step_size] = {
-4.7f, -2.1f, 0.5f, 3.5f, 7.7f, 11.2f, 13.6f, 14.9f, 16.3f, 19.5f, 23.1f, 26.5f, 30.0f, 33.7f, 37.2f, 40.8f};
RX888R3Radio::RX888R3Radio(fx3class *fx3)
: RadioHardware(fx3)
{
for (uint8_t i = 0; i < hf_rf_step_size; i++)
{
this->hf_rf_steps[hf_rf_step_size - i - 1] = -(
((i & 0x01) != 0) * 0.5f +
((i & 0x02) != 0) * 1.0f +
((i & 0x04) != 0) * 2.0f +
((i & 0x08) != 0) * 4.0f +
((i & 0x010) != 0) * 8.0f +
((i & 0x020) != 0) * 16.0f);
}
for (uint8_t i = 0; i < hf_if_step_size; i++)
{
if (i > GAIN_SWEET_POINT)
this->hf_if_steps[i] = 20.0f * log10f(HIGH_GAIN_RATIO * (i - GAIN_SWEET_POINT + 3));
else
this->hf_if_steps[i] = 20.0f * log10f(LOW_GAIN_RATIO * (i + 1));
}
}
void RX888R3Radio::Initialize(uint32_t adc_rate)
{
SampleRate = adc_rate;
Fx3->Control(STARTADC, adc_rate);
}
rf_mode RX888R3Radio::PrepareLo(uint64_t freq)
{
if (freq < 10 * 1000) return NOMODE;
if (freq > 2150ll * 1000 * 1000) return NOMODE;
if ( freq >= 220 * 1000 * 1000)
return VHFMODE;
else
return HFMODE;
}
bool RX888R3Radio::UpdatemodeRF(rf_mode mode)
{
if (mode == VHFMODE)
{
// disable HF by set max ATT
UpdateattRF(0); // max att 0 -> -31.5 dB
// switch to VHF Attenna
FX3SetGPIO(VHF_EN);
// high gain, 0db
uint8_t gain = 0x80 | 3;
Fx3->SetArgument(AD8340_VGA, gain);
// Enable Tuner reference clock
uint32_t ref = REFCLK_FREQ;
return Fx3->Control(TUNERINIT, ref); // Initialize Tuner
}
else if (mode == HFMODE)
{
Fx3->Control(TUNERSTDBY); // Stop Tuner
return FX3UnsetGPIO(VHF_EN); // switch to HF Attenna
}
return false;
}
bool RX888R3Radio::UpdateattRF(int att)
{
if (!(gpios & VHF_EN))
{
// hf mode
if (att > hf_rf_step_size - 1)
att = hf_rf_step_size - 1;
if (att < 0)
att = 0;
uint8_t d = hf_rf_step_size - att - 1;
DbgPrintf("UpdateattRF %f \n", this->hf_rf_steps[att]);
return Fx3->SetArgument(DAT31_ATT, d);
}
else
{
// uint16_t index = att;
// this is in VHF mode
// return Fx3->SetArgument(R82XX_ATTENUATOR, index);
return false;
}
}
#define M(x) ((x)*1000000)
uint64_t RX888R3Radio::TuneLo(uint64_t freq)
{
if (!(gpios & VHF_EN))
{
// this is in HF mode
// set bpf
int sel;
// set preselector
if (freq > M(64) && freq <= M(128))
sel = 0b001; // FM undersampling
else if (SampleRate < M(32))
sel = 0b101;
else
sel = 0b011;
Fx3->SetArgument(PRESELECTOR, sel);
if (freq < M(64))
return 0;
else if (freq < M(128))
return M(64);
else if (freq < M(192))
return M(64 * 2);
else if (freq < M(256))
return M(64 * 3);
return 0;
}
else
{
// this is in VHF mode
uint64_t targetVCO = freq + IF_FREQ;
uint32_t hardwareVCO = targetVCO / 1000000; // convert to MHz
int offset = targetVCO % 1000000;
DbgPrintf("Target VCO = %luHZ, hardware VCO= %dMHX, Actual IF = %dHZ\n", freq + IF_FREQ, hardwareVCO, IF_FREQ - offset);
Fx3->Control(TUNERTUNE, hardwareVCO);
return freq - (IF_FREQ - offset);
}
}
int RX888R3Radio::getRFSteps(const float **steps)
{
if (!(gpios & VHF_EN))
{
// hf mode
*steps = this->hf_rf_steps;
return hf_rf_step_size;
}
else
{
*steps = this->vhf_rf_steps;
return vhf_rf_step_size;
}
}
int RX888R3Radio::getIFSteps(const float **steps)
{
if (!(gpios & VHF_EN))
{
*steps = this->hf_if_steps;
return hf_if_step_size;
}
else
{
*steps = this->vhf_if_steps;
return vhf_if_step_size;
}
}
bool RX888R3Radio::UpdateGainIF(int gain_index)
{
if (!(gpios & VHF_EN))
{
// this is in HF mode
uint8_t gain;
if (gain_index > GAIN_SWEET_POINT)
gain = HIGH_MODE | (gain_index - GAIN_SWEET_POINT + 3);
else
gain = LOW_MODE | (gain_index + 1);
DbgPrintf("UpdateGainIF %d \n", gain);
return Fx3->SetArgument(AD8340_VGA, gain);
}
else
{
// this is in VHF mode
// return Fx3->SetArgument(R82XX_VGA, (uint16_t)gain_index);
return false;
}
}

View File

@ -0,0 +1 @@
#include "RadioHandler.h"

View File

@ -0,0 +1,118 @@
#include "RadioHandler.h"
#define ADC_FREQ (128u*1000*1000)
#define IF_FREQ (ADC_FREQ / 4)
#define HIGH_MODE 0x80
#define LOW_MODE 0x00
#define MODE HIGH_MODE
RX999Radio::RX999Radio(fx3class *fx3)
: RadioHardware(fx3)
{
// high mode gain = 0.409, start=-30
// low mode gain = 0.059, start = -30
#if (MODE == HIGH_MODE)
float ratio = 0.409f;
#else
float ratio = 0.059f;
#endif
for (uint8_t i = 0; i < if_step_size; i++)
{
this->if_steps[i] = -30.0f + ratio * (i + 1);
}
}
void RX999Radio::Initialize(uint32_t adc_rate)
{
SampleRate = adc_rate;
Fx3->Control(STARTADC, adc_rate);
}
rf_mode RX999Radio::PrepareLo(uint64_t freq)
{
if (freq < 10 * 1000) return NOMODE;
if (freq > 6000ll * 1000 * 1000) return NOMODE;
if ( freq >= this->SampleRate / 2)
return VHFMODE;
else
return HFMODE;
}
bool RX999Radio::UpdatemodeRF(rf_mode mode)
{
if (mode == VHFMODE)
{
// switch to VHF Attenna
FX3SetGPIO(VHF_EN);
// Initialize VCO
// Initialize Mixer
return Fx3->Control(TUNERINIT, (uint32_t)0);
}
else if (mode == HFMODE)
{
Fx3->Control(TUNERSTDBY);
return FX3UnsetGPIO(VHF_EN); // switch to HF Attenna
}
return false;
}
bool RX999Radio::UpdateattRF(int att)
{
return false;
}
uint64_t RX999Radio::TuneLo(uint64_t freq)
{
if (!(gpios & VHF_EN))
{
// this is in HF mode
return 0;
}
else
{
int sel;
// set preselector
if (freq <= 120*1000*1000) sel = 0b111;
else if (freq <= 250*1000*1000) sel = 0b101;
else if (freq <= 300*1000*1000) sel = 0b110;
else if (freq <= 380*1000*1000) sel = 0b100;
else if (freq <= 500*1000*1000) sel = 0b000;
else if (freq <= 1000ll*1000*1000) sel = 0b010;
else if (freq <= 2000ll*1000*1000) sel = 0b001;
else sel = 0b011;
Fx3->Control(TUNERTUNE, freq + IF_FREQ);
Fx3->SetArgument(PRESELECTOR, sel);
// Set VCXO
return freq - IF_FREQ;
}
}
int RX999Radio::getRFSteps(const float **steps)
{
return 0;
}
int RX999Radio::getIFSteps(const float **steps)
{
*steps = this->if_steps;
return if_step_size;
}
bool RX999Radio::UpdateGainIF(int gain_index)
{
uint8_t gain = MODE | (gain_index + 1);
DbgPrintf("UpdateGainIF %d \n", gain);
return Fx3->SetArgument(AD8340_VGA, gain);
}

View File

@ -0,0 +1,120 @@
#include "RadioHandler.h"
#define ADC_FREQ (64u*1000*1000)
#define IF_FREQ (ADC_FREQ / 4)
#define HIGH_MODE 0x80
#define LOW_MODE 0x00
#define MODE HIGH_MODE
RXLucyRadio::RXLucyRadio(fx3class *fx3)
: RadioHardware(fx3)
{
// initialize steps
for (uint8_t i = 0; i < if_step_size; i++) {
this->if_steps[if_step_size - i - 1] = -(
((i & 0x01) != 0) * 0.5f +
((i & 0x02) != 0) * 1.0f +
((i & 0x04) != 0) * 2.0f +
((i & 0x08) != 0) * 4.0f +
((i & 0x010) != 0) * 8.0f +
((i & 0x020) != 0) * 16.0f
);
}
for (uint8_t i = 0; i < step_size; i++)
{
this->steps[step_size - i - 1] = -1.0f * i;
}
}
void RXLucyRadio::Initialize(uint32_t adc_rate)
{
SampleRate = adc_rate;
Fx3->Control(STARTADC, adc_rate);
}
rf_mode RXLucyRadio::PrepareLo(uint64_t freq)
{
if (freq < 35000ll * 1000) return NOMODE;
if (freq > 6000ll * 1000 * 1000) return NOMODE;
if ( freq >= this->SampleRate / 2)
return VHFMODE;
else
return HFMODE;
}
bool RXLucyRadio::UpdateattRF(int att)
{
if (att > step_size - 1) att = step_size - 1;
if (att < 0) att = 0;
uint8_t d = step_size - att - 1;
DbgPrintf("UpdateattRF %f \n", this->steps[att]);
return Fx3->SetArgument(VHF_ATTENUATOR, d);
}
bool RXLucyRadio::UpdateGainIF(int att) //HF103 now
{
if (att > if_step_size - 1) att = if_step_size - 1;
if (att < 0) att = 0;
uint8_t d = if_step_size - att - 1;
DbgPrintf("UpdateattRF %f \n", this->if_steps[att]);
return Fx3->SetArgument(DAT31_ATT, d);
}
uint64_t RXLucyRadio::TuneLo(uint64_t freq)
{
if (!(gpios & VHF_EN))
{
// this is in HF mode
return 0;
}
else
{
Fx3->Control(TUNERTUNE, freq + IF_FREQ);
// Set VCXO
return freq - IF_FREQ;
}
}
bool RXLucyRadio::UpdatemodeRF(rf_mode mode)
{
if (mode == VHFMODE)
{
// switch to VHF Attenna
FX3SetGPIO(VHF_EN);
// Initialize VCO
// Initialize Mixer
return Fx3->Control(TUNERINIT, (uint32_t)0);
}
else if (mode == HFMODE)
{
Fx3->Control(TUNERSTDBY);
return FX3UnsetGPIO(VHF_EN); // switch to HF Attenna
}
return false;
}
int RXLucyRadio::getRFSteps(const float **steps)
{
*steps = this->steps;
return step_size;
}
int RXLucyRadio::getIFSteps(const float** steps)
{
*steps = this->if_steps;
return if_step_size;
}

View File

@ -0,0 +1,22 @@
#include "RadioHandler.h"
bool RadioHardware::FX3SetGPIO(uint32_t mask)
{
gpios |= mask;
return Fx3->Control(GPIOFX3, gpios);
}
bool RadioHardware::FX3UnsetGPIO(uint32_t mask)
{
gpios &= ~mask;
return Fx3->Control(GPIOFX3, gpios);
}
RadioHardware::~RadioHardware()
{
if (Fx3) {
FX3SetGPIO(SHDWN);
}
}

View File

@ -0,0 +1,8 @@
#include "license.txt"
#include "sddc_config.h"
bool saveADCsamplesflag = false;
uint32_t adcnominalfreq = DEFAULT_ADC_FREQ;
uint32_t MIN_ADC_FREQ = 50000000; // ADC sampling frequency minimum
uint32_t MAX_ADC_FREQ = 140000000; // ADC sampling frequency minimum
uint32_t N2_BANDSWITCH = 80000000; // threshold 5 or 6 SR bandwidths

View File

@ -0,0 +1,94 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include "license.txt"
#include "../Interface.h"
#include <math.h> // atan => PI
#include <thread>
#include <mutex>
#include <condition_variable>
//#define _DEBUG // defined in VS configuration
#ifdef __cplusplus
inline void null_func(const char *format, ...) { }
#define DbgEmpty null_func
#else
#define DbgEmpty { }
#endif
// macro to call callback function with just status extHWstatusT
#define EXTIO_STATUS_CHANGE( CB, STATUS ) \
do { \
SendMessage(h_dialog, WM_USER + 1, STATUS, 0); \
if (CB) { \
DbgPrintf("<==CALLBACK: %s\n", #STATUS); \
CB( -1, STATUS, 0, NULL );\
}\
}while(0)
#ifdef VERBOSE_DEBUG
#define EnterFunction() \
DbgPrintf("==>%s\n", __FUNCDNAME__)
#define EnterFunction1(v1) \
DbgPrintf("==>%s(%d)\n", __FUNCDNAME__, (v1))
#else
#define EnterFunction()
#define EnterFunction1(v1)
#endif
#ifdef _DEBUG
#define DbgPrintf (printf)
#else
#define DbgPrintf DbgEmpty
#endif
#define VERSION (1.2) // Dll version number x.xx
#define SWVERSION "1.2.1"
#define SETTINGS_IDENTIFIER "sddc_1.06"
#define SWNAME "ExtIO_sddc.dll"
#define QUEUE_SIZE 32
#define WIDEFFTN // test FFTN 8192
#define FFTN_R_ADC (8192) // FFTN used for ADC real stream DDC tested at 2048, 8192, 32768, 131072
// GAINFACTORS to be adjusted with lab reference source measured with HDSDR Smeter rms mode
#define BBRF103_GAINFACTOR (7.8e-8f) // BBRF103
#define HF103_GAINFACTOR (1.14e-8f) // HF103
#define RX888_GAINFACTOR (0.695e-8f) // RX888
#define RX888mk2_GAINFACTOR (1.08e-8f) // RX888mk2
enum rf_mode { NOMODE = 0, HFMODE = 0x1, VHFMODE = 0x2 };
#define HF_HIGH (32000000) // 32M
#define MW_HIGH ( 2000000)
#define EXT_BLOCKLEN 512 * 64 /* 32768 only multiples of 512 */
#define RFDDCNAME ("NVIA L768M256")
#define RFDDCVER ("v 1.0")
// URL definitions
#define URL1B "16bit SDR Receiver"
#define URL1 "<a>http://www.hdsdr.de/</a>"
#define URL_HDSR "http://www.hdsdr.de/"
#define URL_HDSDRA "<a>http://www.hdsdr.de/</a>"
extern bool saveADCsamplesflag;
extern uint32_t adcnominalfreq;
const uint32_t transferSize = 131072;
const uint32_t transferSamples = 131072 / sizeof(int16_t);
const uint32_t DEFAULT_ADC_FREQ = 64000000; // ADC sampling frequency
const uint32_t DEFAULT_TRANSFERS_PER_SEC = DEFAULT_ADC_FREQ / transferSamples;
extern uint32_t MIN_ADC_FREQ; // ADC sampling frequency minimum
extern uint32_t MAX_ADC_FREQ; // ADC sampling frequency minimum
extern uint32_t N2_BANDSWITCH; // threshold 5 or 6 SR bandwidths
#endif // _CONFIG_H_

View File

@ -0,0 +1,44 @@
/*
HWSDRtable.h v1.2
Hardware detection of BBRF103 family SDRs
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| SDR | MODEL | GPIO | GPIO | GPIO | GPIO | GPIO | GPIO | GPIO | GPIO | USED BY |
| | # | 33 | 36 | 45 | 50 | 51 | 52 | 53 | 54 | |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| BBRF103 | 0x01 | - | - | - | pd* | - | - | - | LED | Oscar Steila |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| HF103 | 0x02 | - | - | - | - | pd* | - | - | LED | Oscar Steila |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| RX888 | 0x03 | - | - | pd | - | - | - | - | - | Justin Peng / Howard Su |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| RX888 r2 | 0x04 | - | pd | - | - | - | - | - | - | Justin Peng / Howard Su |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| RX999 | 0x05 | pd | - | - | - | - | - | - | - | Justin Peng / Howard Su |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| LUCY | 0x06 | - | - | - | - | - | pd+ | pd+ | - | Wiktor Starzak |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| --- | - | - | - | - | pd | pd | pd | pd | LED | Oscar Steila |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| SDR-HF | - | - | - | - | pu | pd | pd | pd | LED | Pieter Ibelings |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
| | | - | - | | | | | | | ... |
+--------------+-------+------+------+------+------+------+------+------+------+----------------------------+
Where:
- floating no connection
pu 1k resistor pull-up to 3V3
pd 1k resistor pull-down to GND
pd* 1k resistor pull-down to GND to be added with a patch
pd+ 1k resistor pull-down all pd+ are connected to the same pull-down
LED plus resistor to 3V3 connected to GPIO54 on FX3 SuperSpeed Kit
The 1k value is low enough to be able to detect the resistor using the GPIO internal programmable pull-up pull-down vs a floating pin.
The value is high enough to not disturb use of GPIOs for other purpose after detection.
*/
// TODO

View File

@ -0,0 +1,154 @@
#pragma once
#define FIRMWARE_VER_MAJOR 2
#define FIRMWARE_VER_MINOR 1
// HF103 commands !!!
enum FX3Command {
// Start GPII engine and stream the data from ADC
// WRITE: UINT32
STARTFX3 = 0xAA,
// Stop GPII engine
// WRITE: UINT32
STOPFX3 = 0xAB,
// Get the information of device
// including model, version
// READ: UINT32
TESTFX3 = 0xAC,
// Control GPIOs
// WRITE: UINT32
GPIOFX3 = 0xAD,
// Write data to I2c bus
// WRITE: DATA
// INDEX: reg
// VALUE: i2c_addr
I2CWFX3 = 0xAE,
// Read data from I2c bus
// READ: DATA
// INDEX: reg
// VALUE: i2c_addr
I2CRFX3 = 0xAF,
// Reset USB chip and get back to bootloader mode
// WRITE: NONE
RESETFX3 = 0xB1,
// Set Argument, packet Index/Vaule contains the data
// WRITE: (Additional Data)
// INDEX: Argument_index
// VALUE: arguement value
SETARGFX3 = 0xB6,
// Start ADC with the specific frequency
// Optional, if ADC is running with crystal, this is not needed.
// WRITE: UINT32 -> adc frequency
STARTADC = 0xB2,
// R82XX family Tuner functions
// Initialize R82XX tuner
// WRITE: NONE
TUNERINIT = 0xB4,
// Tune to a sepcific frequency
// WRITE: UINT64
TUNERTUNE = 0xB5,
// Stop Tuner
// WRITE: NONE
TUNERSTDBY = 0xB8,
// Read Debug string if any
// READ:
READINFODEBUG = 0xBA,
};
#define OUTXIO0 (1U << 0) // ATT_LE
#define OUTXIO1 (1U << 1) // ATT_CLK
#define OUTXIO2 (1U << 2) // ATT_DATA
#define OUTXIO3 (1U << 3) // SEL0
#define OUTXIO4 (1U << 4) // SEL1
#define OUTXIO5 (1U << 5) // SHDWN
#define OUTXIO6 (1U << 6) // DITH
#define OUTXIO7 (1U << 7) // RAND
#define OUTXIO8 (1U << 8) // 256
#define OUTXIO9 (1U << 9) // 512
#define OUTXI10 (1U << 10) // 1024
#define OUTXI11 (1U << 11) // 2048
#define OUTXI12 (1U << 12) // 4096
#define OUTXI13 (1U << 13) // 8192
#define OUTXI14 (1U << 14) // 16384
#define OUTXI15 (1U << 15) // 32768
#define OUTXI16 (1U << 16)
enum GPIOPin {
SHDWN = OUTXIO5,
DITH = OUTXIO6,
RANDO = OUTXIO7,
BIAS_HF = OUTXIO8,
BIAS_VHF = OUTXIO9,
LED_YELLOW = OUTXI10,
LED_RED = OUTXI11,
LED_BLUE = OUTXI12,
ATT_SEL0 = OUTXI13,
ATT_SEL1 = OUTXI14,
// RX888r2
VHF_EN = OUTXI15,
PGA_EN = OUTXI16,
};
enum RadioModel {
NORADIO = 0x00,
BBRF103 = 0x01,
HF103 = 0x02,
RX888 = 0x03,
RX888r2 = 0x04,
RX999 = 0x05,
RXLUCY = 0x06,
RX888r3 = 0x07,
};
enum ArgumentList {
// Set R8xx lna/mixer gain
// value: 0-29
R82XX_ATTENUATOR = 1,
// Set R8xx vga gain
// value: 0-15
R82XX_VGA = 2,
// Set R8xx sideband
// value: 0/1
R82XX_SIDEBAND = 3,
// Set R8xx harmonic
// value: 0/1
R82XX_HARMONIC = 4,
// Set DAT-31 Att
// Value: 0-63
DAT31_ATT = 10,
// Set AD8340 chip vga
// Value: 0-255
AD8340_VGA = 11,
// Preselector
// Value: 0-2
PRESELECTOR = 12,
// VHFATT
// Value: 0-15
VHF_ATTENUATOR = 13,
};
#define _DEBUG_USB_
#define MAXLEN_D_USB (100)

View File

@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) 2017-2020 Oscar Steila ik1xpv<at>gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.The MIT License (MIT)
The MIT License (MIT)

View File

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.13)
if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif (MSVC)
include_directories("." "../Core")
add_library(sddc SHARED
libsddc.cpp
)
if (MSVC)
target_link_libraries(sddc PUBLIC Setupapi.lib)
else()
target_include_directories(sddc PUBLIC "${LIBUSB_INCLUDE_DIR}")
target_link_directories(sddc PUBLIC "${LIBUSB_LIBRARY_DIRS}")
target_link_libraries(sddc PUBLIC ${LIBUSB_LIBRARIES})
endif (MSVC)
target_include_directories(sddc PUBLIC "${LIBFFTW_INCLUDE_DIR}")
target_link_directories(sddc PUBLIC "${LIBFFTW_LIBRARY_DIRS}")
target_link_libraries(sddc PUBLIC ${LIBFFTW_LIBRARIES})
target_link_libraries(sddc PRIVATE SDDC_CORE)
set_target_properties(sddc PROPERTIES VERSION ${PROJECT_VERSION})
set_target_properties(sddc PROPERTIES SOVERSION 0)
# applications
add_executable(sddc_test sddc_test.c)
target_link_libraries(sddc_test PRIVATE sddc ${ASANLIB})
add_executable(sddc_stream_test sddc_stream_test.c wavewrite.c)
target_link_libraries(sddc_stream_test sddc ${ASANLIB})
add_executable(sddc_vhf_stream_test sddc_vhf_stream_test.c wavewrite.c)
target_link_libraries(sddc_vhf_stream_test sddc ${ASANLIB})

View File

@ -0,0 +1,396 @@
#include "libsddc.h"
#include "sddc_config.h"
#include "r2iq.h"
#include "RadioHandler.h"
struct sddc
{
SDDCStatus status;
RadioHandlerClass* handler;
uint8_t led;
int samplerateidx;
double freq;
sddc_read_async_cb_t callback;
void *callback_context;
};
sddc_t *current_running;
static void Callback(const float* data, uint32_t len)
{
}
class rawdata : public r2iqControlClass {
void Init(float gain, ringbuffer<int16_t>* buffers, ringbuffer<float>* obuffers) override
{
idx = 0;
}
void TurnOn() override
{
this->r2iqOn = true;
idx = 0;
}
private:
int idx;
};
int sddc_get_device_count()
{
return 1;
}
int sddc_get_device_info(struct sddc_device_info **sddc_device_infos)
{
auto ret = new sddc_device_info();
const char *todo = "TODO";
ret->manufacturer = todo;
ret->product = todo;
ret->serial_number = todo;
*sddc_device_infos = ret;
return 1;
}
int sddc_free_device_info(struct sddc_device_info *sddc_device_infos)
{
delete sddc_device_infos;
return 0;
}
sddc_t *sddc_open(int index, const char* imagefile)
{
auto ret_val = new sddc_t();
fx3class *fx3 = CreateUsbHandler();
if (fx3 == nullptr)
{
return nullptr;
}
// open the firmware
unsigned char* res_data;
uint32_t res_size;
FILE *fp = fopen(imagefile, "rb");
if (fp == nullptr)
{
return nullptr;
}
fseek(fp, 0, SEEK_END);
res_size = ftell(fp);
res_data = (unsigned char*)malloc(res_size);
fseek(fp, 0, SEEK_SET);
if (fread(res_data, 1, res_size, fp) != res_size)
return nullptr;
bool openOK = fx3->Open(res_data, res_size);
if (!openOK)
return nullptr;
ret_val->handler = new RadioHandlerClass();
if (ret_val->handler->Init(fx3, Callback, new rawdata()))
{
ret_val->status = SDDC_STATUS_READY;
ret_val->samplerateidx = 0;
}
return ret_val;
}
void sddc_close(sddc_t *that)
{
if (that->handler)
delete that->handler;
delete that;
}
enum SDDCStatus sddc_get_status(sddc_t *t)
{
return t->status;
}
enum SDDCHWModel sddc_get_hw_model(sddc_t *t)
{
switch(t->handler->getModel())
{
case RadioModel::BBRF103:
return HW_BBRF103;
case RadioModel::HF103:
return HW_HF103;
case RadioModel::RX888:
return HW_RX888;
case RadioModel::RX888r2:
return HW_RX888R2;
case RadioModel::RX888r3:
return HW_RX888R3;
case RadioModel::RX999:
return HW_RX999;
default:
return HW_NORADIO;
}
}
const char *sddc_get_hw_model_name(sddc_t *t)
{
return t->handler->getName();
}
uint16_t sddc_get_firmware(sddc_t *t)
{
return t->handler->GetFirmware();
}
const double *sddc_get_frequency_range(sddc_t *t)
{
return nullptr;
}
enum RFMode sddc_get_rf_mode(sddc_t *t)
{
switch(t->handler->GetmodeRF())
{
case HFMODE:
return RFMode::HF_MODE;
case VHFMODE:
return RFMode::VHF_MODE;
default:
return RFMode::NO_RF_MODE;
}
}
int sddc_set_rf_mode(sddc_t *t, enum RFMode rf_mode)
{
switch (rf_mode)
{
case VHF_MODE:
t->handler->UpdatemodeRF(VHFMODE);
break;
case HF_MODE:
t->handler->UpdatemodeRF(HFMODE);
default:
return -1;
}
return 0;
}
/* LED functions */
int sddc_led_on(sddc_t *t, uint8_t led_pattern)
{
if (led_pattern & YELLOW_LED)
t->handler->uptLed(0, true);
if (led_pattern & RED_LED)
t->handler->uptLed(1, true);
if (led_pattern & BLUE_LED)
t->handler->uptLed(2, true);
t->led |= led_pattern;
return 0;
}
int sddc_led_off(sddc_t *t, uint8_t led_pattern)
{
if (led_pattern & YELLOW_LED)
t->handler->uptLed(0, false);
if (led_pattern & RED_LED)
t->handler->uptLed(1, false);
if (led_pattern & BLUE_LED)
t->handler->uptLed(2, false);
t->led &= ~led_pattern;
return 0;
}
int sddc_led_toggle(sddc_t *t, uint8_t led_pattern)
{
t->led = t->led ^ led_pattern;
if (t->led & YELLOW_LED)
t->handler->uptLed(0, false);
if (t->led & RED_LED)
t->handler->uptLed(1, false);
if (t->led & BLUE_LED)
t->handler->uptLed(2, false);
return 0;
}
/* ADC functions */
int sddc_get_adc_dither(sddc_t *t)
{
return t->handler->GetDither();
}
int sddc_set_adc_dither(sddc_t *t, int dither)
{
t->handler->UptDither(dither != 0);
return 0;
}
int sddc_get_adc_random(sddc_t *t)
{
return t->handler->GetRand();
}
int sddc_set_adc_random(sddc_t *t, int random)
{
t->handler->UptRand(random != 0);
return 0;
}
/* HF block functions */
double sddc_get_hf_attenuation(sddc_t *t)
{
return 0;
}
int sddc_set_hf_attenuation(sddc_t *t, double attenuation)
{
return 0;
}
int sddc_get_hf_bias(sddc_t *t)
{
return t->handler->GetBiasT_HF();
}
int sddc_set_hf_bias(sddc_t *t, int bias)
{
t->handler->UpdBiasT_HF(bias != 0);
return 0;
}
/* VHF block and VHF/UHF tuner functions */
double sddc_get_tuner_frequency(sddc_t *t)
{
return t->freq;
}
int sddc_set_tuner_frequency(sddc_t *t, double frequency)
{
t->freq = t->handler->TuneLO((int64_t)frequency);
return 0;
}
int sddc_get_tuner_rf_attenuations(sddc_t *t, const double *attenuations[])
{
return 0;
}
double sddc_get_tuner_rf_attenuation(sddc_t *t)
{
return 0;
}
int sddc_set_tuner_rf_attenuation(sddc_t *t, double attenuation)
{
//TODO, convert double to index
t->handler->UpdateattRF(5);
return 0;
}
int sddc_get_tuner_if_attenuations(sddc_t *t, const double *attenuations[])
{
// TODO
return 0;
}
double sddc_get_tuner_if_attenuation(sddc_t *t)
{
return 0;
}
int sddc_set_tuner_if_attenuation(sddc_t *t, double attenuation)
{
return 0;
}
int sddc_get_vhf_bias(sddc_t *t)
{
return t->handler->GetBiasT_VHF();
}
int sddc_set_vhf_bias(sddc_t *t, int bias)
{
t->handler->UpdBiasT_VHF(bias != 0);
return 0;
}
double sddc_get_sample_rate(sddc_t *t)
{
return 0;
}
int sddc_set_sample_rate(sddc_t *t, double sample_rate)
{
switch((int64_t)sample_rate)
{
case 32000000:
t->samplerateidx = 0;
break;
case 16000000:
t->samplerateidx = 1;
break;
case 8000000:
t->samplerateidx = 2;
break;
case 4000000:
t->samplerateidx = 3;
break;
case 2000000:
t->samplerateidx = 4;
break;
default:
return -1;
}
return 0;
}
int sddc_set_async_params(sddc_t *t, uint32_t frame_size,
uint32_t num_frames, sddc_read_async_cb_t callback,
void *callback_context)
{
// TODO: ignore frame_size, num_frames
t->callback = callback;
t->callback_context = callback_context;
return 0;
}
int sddc_start_streaming(sddc_t *t)
{
current_running = t;
t->handler->Start(t->samplerateidx);
return 0;
}
int sddc_handle_events(sddc_t *t)
{
return 0;
}
int sddc_stop_streaming(sddc_t *t)
{
t->handler->Stop();
current_running = nullptr;
return 0;
}
int sddc_reset_status(sddc_t *t)
{
return 0;
}
int sddc_read_sync(sddc_t *t, uint8_t *data, int length, int *transferred)
{
return 0;
}

View File

@ -0,0 +1,171 @@
/*
* libsddc - low level functions for wideband SDR receivers like
* BBRF103, RX-666, RX888, HF103, etc
*
* Copyright (C) 2020 by Franco Venturi
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#ifndef __LIBSDDC_H
#define __LIBSDDC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct sddc sddc_t;
struct sddc_device_info {
const char *manufacturer;
const char *product;
const char *serial_number;
};
enum SDDCStatus {
SDDC_STATUS_OFF,
SDDC_STATUS_READY,
SDDC_STATUS_STREAMING,
SDDC_STATUS_FAILED = 0xff
};
enum SDDCHWModel {
HW_NORADIO,
HW_BBRF103,
HW_HF103,
HW_RX888,
HW_RX888R2,
HW_RX999,
HW_RX888R3,
};
enum RFMode {
NO_RF_MODE,
HF_MODE,
VHF_MODE
};
enum LEDColors {
YELLOW_LED = 0x01,
RED_LED = 0x02,
BLUE_LED = 0x04
};
/* basic functions */
int sddc_get_device_count();
int sddc_get_device_info(struct sddc_device_info **sddc_device_infos);
int sddc_free_device_info(struct sddc_device_info *sddc_device_infos);
sddc_t *sddc_open(int index, const char* imagefile);
void sddc_close(sddc_t *t);
enum SDDCStatus sddc_get_status(sddc_t *t);
enum SDDCHWModel sddc_get_hw_model(sddc_t *t);
const char *sddc_get_hw_model_name(sddc_t *t);
uint16_t sddc_get_firmware(sddc_t *t);
const double *sddc_get_frequency_range(sddc_t *t);
enum RFMode sddc_get_rf_mode(sddc_t *t);
int sddc_set_rf_mode(sddc_t *t, enum RFMode rf_mode);
/* LED functions */
int sddc_led_on(sddc_t *t, uint8_t led_pattern);
int sddc_led_off(sddc_t *t, uint8_t led_pattern);
int sddc_led_toggle(sddc_t *t, uint8_t led_pattern);
/* ADC functions */
int sddc_get_adc_dither(sddc_t *t);
int sddc_set_adc_dither(sddc_t *t, int dither);
int sddc_get_adc_random(sddc_t *t);
int sddc_set_adc_random(sddc_t *t, int random);
/* HF block functions */
double sddc_get_hf_attenuation(sddc_t *t);
int sddc_set_hf_attenuation(sddc_t *t, double attenuation);
int sddc_get_hf_bias(sddc_t *t);
int sddc_set_hf_bias(sddc_t *t, int bias);
/* VHF block and VHF/UHF tuner functions */
double sddc_get_tuner_frequency(sddc_t *t);
int sddc_set_tuner_frequency(sddc_t *t, double frequency);
int sddc_get_tuner_rf_attenuations(sddc_t *t, const double *attenuations[]);
double sddc_get_tuner_rf_attenuation(sddc_t *t);
int sddc_set_tuner_rf_attenuation(sddc_t *t, double attenuation);
int sddc_get_tuner_if_attenuations(sddc_t *t, const double *attenuations[]);
double sddc_get_tuner_if_attenuation(sddc_t *t);
int sddc_set_tuner_if_attenuation(sddc_t *t, double attenuation);
int sddc_get_vhf_bias(sddc_t *t);
int sddc_set_vhf_bias(sddc_t *t, int bias);
/* streaming functions */
typedef void (*sddc_read_async_cb_t)(uint32_t data_size, uint8_t *data,
void *context);
double sddc_get_sample_rate(sddc_t *t);
int sddc_set_sample_rate(sddc_t *t, double sample_rate);
int sddc_set_async_params(sddc_t *t, uint32_t frame_size,
uint32_t num_frames, sddc_read_async_cb_t callback,
void *callback_context);
int sddc_start_streaming(sddc_t *t);
int sddc_handle_events(sddc_t *t);
int sddc_stop_streaming(sddc_t *t);
int sddc_reset_status(sddc_t *t);
int sddc_read_sync(sddc_t *t, uint8_t *data, int length, int *transferred);
#ifdef __cplusplus
}
#endif
#endif /* __LIBSDDC_H */

View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2019 by Hayati Ayguen <h_ayguen@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __WAVEHDR_H
#define __WAVEHDR_H
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#pragma pack(push)
#pragma pack(1)
typedef struct
{
char ID[4];
uint32_t size;
} chunk_hdr;
typedef struct
{
uint16_t wYear; /* 1601 through 30827 */
uint16_t wMonth; /* 1..12 */
uint16_t wDayOfWeek; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */
uint16_t wDay; /* 1 .. 31 */
uint16_t wHour; /* 0 .. 23 */
uint16_t wMinute; /* 0 .. 59 */
uint16_t wSecond; /* 0 .. 59 */
uint16_t wMilliseconds; /* 0 .. 999 */
} Wind_SystemTime;
typedef struct
{
/* RIFF header */
chunk_hdr hdr; /* ID == "RIFF" string, size == full filesize - 8 bytes (maybe with some byte missing...) */
char waveID[4]; /* "WAVE" string */
} riff_chunk;
typedef struct
{
/* FMT header */
chunk_hdr hdr; /* ID == "fmt " */
int16_t wFormatTag;
int16_t nChannels;
int32_t nSamplesPerSec;
int32_t nAvgBytesPerSec;
int16_t nBlockAlign;
int16_t nBitsPerSample;
} fmt_chunk;
typedef struct
{
/* auxi header - used by SpectraVue / rfspace / HDSDR / ELAD FDM .. */
chunk_hdr hdr; /* ="auxi" (chunk rfspace) */
Wind_SystemTime StartTime;
Wind_SystemTime StopTime;
uint32_t centerFreq; /* receiver center frequency */
uint32_t ADsamplerate; /* A/D sample frequency before downsampling */
uint32_t IFFrequency; /* IF freq if an external down converter is used */
uint32_t Bandwidth; /* displayable BW if you want to limit the display to less than Nyquist band */
int32_t IQOffset; /* DC offset of the I and Q channels in 1/1000's of a count */
int32_t Unused2;
int32_t Unused3;
int32_t Unused4;
int32_t Unused5;
} auxi_chunk;
typedef struct
{
/* DATA header */
chunk_hdr hdr; /* ="data" */
} data_chunk;
typedef struct
{
riff_chunk r;
fmt_chunk f;
auxi_chunk a;
data_chunk d;
} waveFileHeader;
#pragma pack(pop)
#endif /* __WAVEHDR_H */
// vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab

View File

@ -0,0 +1,232 @@
/*
* Copyright (C) 2019 by Hayati Ayguen <h_ayguen@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "wavewrite.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#ifndef _WIN32
#include <unistd.h>
#include <sys/time.h>
#else
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#include <process.h>
#define _USE_MATH_DEFINES
#include <stdint.h> // portable: uint64_t MSVC: __int64
int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
// This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
// until 00:00:00 January 1, 1970
static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;
GetSystemTime( &system_time );
SystemTimeToFileTime( &system_time, &file_time );
time = ((uint64_t)file_time.dwLowDateTime ) ;
time += ((uint64_t)file_time.dwHighDateTime) << 32;
tp->tv_sec = (long) ((time - EPOCH) / 10000000L);
tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
return 0;
}
#endif
#include <math.h>
#include "wavehdr.h"
static waveFileHeader waveHdr;
static uint32_t waveDataSize = 0;
int waveHdrStarted = 0;
static void waveSetCurrTime(Wind_SystemTime *p)
{
struct timeval tv;
struct tm t;
gettimeofday(&tv, NULL);
p->wMilliseconds = tv.tv_usec / 1000;
#ifdef _WIN32
t = *gmtime(&tv.tv_sec);
#else
gmtime_r(&tv.tv_sec, &t);
#endif
p->wYear = t.tm_year + 1900; /* 1601 through 30827 */
p->wMonth = t.tm_mon + 1; /* 1..12 */
p->wDayOfWeek = t.tm_wday; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */
p->wDay = t.tm_mday; /* 1 .. 31 */
p->wHour = t.tm_hour; /* 0 .. 23 */
p->wMinute = t.tm_min; /* 0 .. 59 */
p->wSecond = t.tm_sec; /* 0 .. 59 */
}
static void waveSetStartTimeInt(time_t tim, double fraction, Wind_SystemTime *p)
{
struct tm t = *gmtime( &tim );
p->wYear = t.tm_year + 1900; /* 1601 through 30827 */
p->wMonth = t.tm_mon + 1; /* 1..12 */
p->wDayOfWeek = t.tm_wday; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */
p->wDay = t.tm_mday; /* 1 .. 31 */
p->wHour = t.tm_hour; /* 0 .. 23 */
p->wMinute = t.tm_min; /* 0 .. 59 */
p->wSecond = t.tm_sec; /* 0 .. 59 */
p->wMilliseconds = (int)( fraction * 1000.0 );
if (p->wMilliseconds >= 1000)
p->wMilliseconds = 999;
}
void waveSetStartTime(time_t tim, double fraction)
{
waveSetStartTimeInt(tim, fraction, &waveHdr.a.StartTime );
waveHdr.a.StopTime = waveHdr.a.StartTime; /* to fix */
}
void wavePrepareHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels)
{
int bytesPerSample = bitsPerSample / 8;
int bytesPerFrame = bytesPerSample * numChannels;
memcpy( waveHdr.r.hdr.ID, "RIFF", 4 );
waveHdr.r.hdr.size = sizeof(waveFileHeader) - 8; /* to fix */
memcpy( waveHdr.r.waveID, "WAVE", 4 );
memcpy( waveHdr.f.hdr.ID, "fmt ", 4 );
waveHdr.f.hdr.size = 16;
waveHdr.f.wFormatTag = 1; /* PCM */
waveHdr.f.nChannels = numChannels; /* I and Q channels */
waveHdr.f.nSamplesPerSec = samplerate;
waveHdr.f.nAvgBytesPerSec = samplerate * bytesPerFrame;
waveHdr.f.nBlockAlign = waveHdr.f.nChannels;
waveHdr.f.nBitsPerSample = bitsPerSample;
memcpy( waveHdr.a.hdr.ID, "auxi", 4 );
waveHdr.a.hdr.size = 2 * sizeof(Wind_SystemTime) + 9 * sizeof(int32_t); /* = 2 * 16 + 9 * 4 = 68 */
waveSetCurrTime( &waveHdr.a.StartTime );
waveHdr.a.StopTime = waveHdr.a.StartTime; /* to fix */
waveHdr.a.centerFreq = freq;
waveHdr.a.ADsamplerate = samplerate;
waveHdr.a.IFFrequency = 0;
waveHdr.a.Bandwidth = 0;
waveHdr.a.IQOffset = 0;
waveHdr.a.Unused2 = 0;
waveHdr.a.Unused3 = 0;
waveHdr.a.Unused4 = 0;
waveHdr.a.Unused5 = 0;
memcpy( waveHdr.d.hdr.ID, "data", 4 );
waveHdr.d.hdr.size = 0; /* to fix later */
waveDataSize = 0;
}
void waveWriteHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels, FILE * f)
{
if (f != stdout) {
assert( !waveHdrStarted );
wavePrepareHeader(samplerate, freq, bitsPerSample, numChannels);
fwrite(&waveHdr, sizeof(waveFileHeader), 1, f);
waveHdrStarted = 1;
}
}
int waveWriteSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData)
{
size_t nw;
switch (waveHdr.f.nBitsPerSample)
{
case 0:
default:
return 1;
case 8:
/* no endian conversion needed for single bytes */
nw = fwrite(vpData, sizeof(uint8_t), numSamples, f);
waveDataSize += sizeof(uint8_t) * numSamples;
return (nw == numSamples) ? 0 : 1;
case 16:
/* TODO: endian conversion needed */
nw = fwrite(vpData, sizeof(int16_t), numSamples, f);
waveDataSize += sizeof(int16_t) * numSamples;
if ( needCleanData )
{
/* TODO: convert back endianness */
}
return (nw == numSamples) ? 0 : 1;
}
}
int waveWriteFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData)
{
size_t nw;
switch (waveHdr.f.nBitsPerSample)
{
case 0:
default:
return 1;
case 8:
/* no endian conversion needed for single bytes */
nw = fwrite(vpData, waveHdr.f.nChannels * sizeof(uint8_t), numFrames, f);
waveDataSize += waveHdr.f.nChannels * sizeof(uint8_t) * numFrames;
return (nw == numFrames) ? 0 : 1;
case 16:
/* TODO: endian conversion needed */
nw = fwrite(vpData, waveHdr.f.nChannels * sizeof(int16_t), numFrames, f);
waveDataSize += waveHdr.f.nChannels * sizeof(int16_t) * numFrames;
if ( needCleanData )
{
/* TODO: convert back endianness */
}
return (nw == numFrames) ? 0 : 1;
}
}
int waveFinalizeHeader(FILE * f)
{
if (f != stdout) {
assert( waveHdrStarted );
waveSetCurrTime( &waveHdr.a.StopTime );
waveHdr.d.hdr.size = waveDataSize;
waveHdr.r.hdr.size += waveDataSize;
/* fprintf(stderr, "waveFinalizeHeader(): datasize = %d\n", waveHdr.dataSize); */
waveHdrStarted = 0;
if ( fseek(f, 0, SEEK_SET) )
return 1;
if ( 1 != fwrite(&waveHdr, sizeof(waveFileHeader), 1, f) )
return 1;
/* fprintf(stderr, "waveFinalizeHeader(): success writing header\n"); */
return 0;
}
return 1;
}
// vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2019 by Hayati Ayguen <h_ayguen@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __WAVEWRITE_H
#define __WAVEWRITE_H
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int waveHdrStarted;
/*!
* helper functions to write and finalize wave headers
* with compatibility to some SDR programs - showing frequency:
* raw sample data still have to be written by caller to FILE*.
* call waveWriteHeader() before writing anything to to file
* and call waveFinalizeHeader() afterwards,
* stdout/stderr can't be used, because seek to begin isn't possible.
*
*/
void waveWriteHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels, FILE * f);
/* waveWriteFrames() writes (numFrames * numChannels) samples
* waveWriteSamples()
* both return 0, when no errors occured
*/
int waveWriteFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData);
int waveWriteSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData); /* returns 0, when no errors occured */
void waveSetStartTime(time_t t, double fraction);
int waveFinalizeHeader(FILE * f); /* returns 0, when no errors occured */
#ifdef __cplusplus
}
#endif
#endif /*__WAVEWRITE_H*/

248
sddc_source/src/main.cpp Normal file
View File

@ -0,0 +1,248 @@
#include <imgui.h>
#include <spdlog/spdlog.h>
#include <module.h>
#include <gui/gui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <gui/style.h>
#include <config.h>
#include <options.h>
#include <gui/widgets/stepped_slider.h>
#include <libsddc.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO {
/* Name: */ "sddc_source",
/* Description: */ "SDDC source module for SDR++",
/* Author: */ "Ryzerth;pkuznetsov",
/* Version: */ 0, 1, 0,
/* Max instances */ 1
};
ConfigManager config;
const char* AGG_MODES_STR = "Off\0Low\0High\0";
class AirspyHFSourceModule : public ModuleManager::Instance {
public:
AirspyHFSourceModule(std::string name) {
this->name = name;
sampleRate = 768000.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();
selectFirst();
core::setInputSampleRate(sampleRate);
sigpath::sourceManager.registerSource("SDDC", &handler);
}
~AirspyHFSourceModule() {
}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
void refresh() {
devListTxt = "";
devCount = sddc_get_device_count();
for (int i = 0; i < devCount; i++) {
// Open device
sddc_t* dev = sddc_open(i, "../sddc_source/res/firmwares/SDDC_FX3.img");
if (dev == NULL) { continue; }
// Get device name (check if implemented)
const char* name = sddc_get_hw_model_name(dev);
if (name == NULL) {
sddc_close(dev);
continue;
}
// Add to list
char tmp[256];
sprintf(tmp, "%s (%d)", name, i);
devNames.push_back(name);
devListTxt += name;
devListTxt += '\0';
sddc_close(dev);
}
}
void selectFirst() {
if (devCount != 0) {
selectById(0);
}
}
void selectByName(std::string name) {
for (int i = 0; i < devCount; i++) {
if (devNames[i] == name) {
selectById(i);
break;
}
}
}
void selectById(int id) {
if (id < 0 || id >= devCount) {
selectedDevName = "";
return;
}
devId = id;
selectedDevName = devNames[id];
}
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) {
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
spdlog::info("AirspyHFSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
spdlog::info("AirspyHFSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedDevName == "") { return; }
// Start device
_this->running = true;
spdlog::info("AirspyHFSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
if (!_this->running) {
return;
}
_this->running = false;
_this->stream.stopWriter();
// Stop device
_this->stream.clearWriteStop();
spdlog::info("AirspyHFSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
if (_this->running) {
// Tune device
}
_this->freq = freq;
spdlog::info("AirspyHFSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
AirspyHFSourceModule* _this = (AirspyHFSourceModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvailWidth();
if (_this->running) { style::beginDisabled(); }
ImGui::SetNextItemWidth(menuWidth);
if (ImGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
// Select here
}
if (ImGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
_this->sampleRate = _this->sampleRateList[_this->srId];
core::setInputSampleRate(_this->sampleRate);
// Select SR here
}
ImGui::SameLine();
float refreshBtnWdith = menuWidth - ImGui::GetCursorPosX();
if (ImGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name), ImVec2(refreshBtnWdith, 0))) {
_this->refresh();
// Reselect and reset samplerate if it changed
}
if (_this->running) { style::endDisabled(); }
// All other controls
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
int devId = 0;
int srId = 0;
sddc_t* openDev;
int devCount = 0;
std::vector<std::string> devNames;
std::string selectedDevName = "";
std::string devListTxt;
std::vector<uint32_t> sampleRateList;
std::string sampleRateListTxt;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(options::opts.root + "/sddc_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new AirspyHFSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
delete (AirspyHFSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}