mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-25 02:18:30 +01:00
Push before merge
This commit is contained in:
parent
0b276bed1d
commit
c0244e819e
@ -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)
|
||||
|
@ -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)
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
@ -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);
|
||||
}
|
@ -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
|
@ -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_() {
|
||||
|
||||
}
|
46
sddc_source/CMakeLists.txt
Normal file
46
sddc_source/CMakeLists.txt
Normal 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)
|
BIN
sddc_source/res/firmwares/SDDC_FX3.img
Normal file
BIN
sddc_source/res/firmwares/SDDC_FX3.img
Normal file
Binary file not shown.
78
sddc_source/src/libsddc/CMakeLists.txt
Normal file
78
sddc_source/src/libsddc/CMakeLists.txt
Normal 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)
|
35
sddc_source/src/libsddc/Core/CMakeLists.txt
Normal file
35
sddc_source/src/libsddc/Core/CMakeLists.txt
Normal 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()
|
37
sddc_source/src/libsddc/Core/FX3Class.h
Normal file
37
sddc_source/src/libsddc/Core/FX3Class.h
Normal 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
|
35
sddc_source/src/libsddc/Core/PScope_uti.cpp
Normal file
35
sddc_source/src/libsddc/Core/PScope_uti.cpp
Normal 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);
|
||||
}
|
||||
|
9
sddc_source/src/libsddc/Core/PScope_uti.h
Normal file
9
sddc_source/src/libsddc/Core/PScope_uti.h
Normal 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_
|
440
sddc_source/src/libsddc/Core/RadioHandler.cpp
Normal file
440
sddc_source/src/libsddc/Core/RadioHandler.cpp
Normal 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);
|
||||
}
|
341
sddc_source/src/libsddc/Core/RadioHandler.h
Normal file
341
sddc_source/src/libsddc/Core/RadioHandler.h
Normal 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
|
95
sddc_source/src/libsddc/Core/arch/linux/FX3handler.cpp
Normal file
95
sddc_source/src/libsddc/Core/arch/linux/FX3handler.cpp
Normal 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;
|
||||
}
|
44
sddc_source/src/libsddc/Core/arch/linux/FX3handler.h
Normal file
44
sddc_source/src/libsddc/Core/arch/linux/FX3handler.h
Normal 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
|
815
sddc_source/src/libsddc/Core/arch/linux/ezusb.c
Normal file
815
sddc_source/src/libsddc/Core/arch/linux/ezusb.c
Normal 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;
|
||||
}
|
114
sddc_source/src/libsddc/Core/arch/linux/ezusb.h
Normal file
114
sddc_source/src/libsddc/Core/arch/linux/ezusb.h
Normal 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
|
47
sddc_source/src/libsddc/Core/arch/linux/logging.c
Normal file
47
sddc_source/src/libsddc/Core/arch/linux/logging.c
Normal 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;
|
||||
}
|
43
sddc_source/src/libsddc/Core/arch/linux/logging.h
Normal file
43
sddc_source/src/libsddc/Core/arch/linux/logging.h
Normal 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 */
|
387
sddc_source/src/libsddc/Core/arch/linux/streaming.c
Normal file
387
sddc_source/src/libsddc/Core/arch/linux/streaming.c
Normal 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;
|
||||
}
|
63
sddc_source/src/libsddc/Core/arch/linux/streaming.h
Normal file
63
sddc_source/src/libsddc/Core/arch/linux/streaming.h
Normal 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 */
|
574
sddc_source/src/libsddc/Core/arch/linux/usb_device.c
Normal file
574
sddc_source/src/libsddc/Core/arch/linux/usb_device.c
Normal 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;
|
||||
}
|
60
sddc_source/src/libsddc/Core/arch/linux/usb_device.h
Normal file
60
sddc_source/src/libsddc/Core/arch/linux/usb_device.h
Normal 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 */
|
@ -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 */
|
@ -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.
|
||||
@ -2412,14 +2411,14 @@ PUCHAR CCyIsocEndPoint::BeginBufferedXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *o
|
||||
{
|
||||
if ( hDevice == INVALID_HANDLE_VALUE ) return NULL;
|
||||
|
||||
int pkts;
|
||||
if(MaxPktSize)
|
||||
pkts = bufLen / MaxPktSize; // Number of packets implied by bufLen & pktSize
|
||||
else
|
||||
{
|
||||
pkts = 0;
|
||||
return NULL;
|
||||
}
|
||||
int pkts;
|
||||
if(MaxPktSize)
|
||||
pkts = bufLen / MaxPktSize; // Number of packets implied by bufLen & pktSize
|
||||
else
|
||||
{
|
||||
pkts = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bufLen % MaxPktSize) pkts++;
|
||||
|
||||
@ -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;
|
||||
@ -2467,14 +2466,14 @@ PUCHAR CCyIsocEndPoint::BeginDirectXfer(PUCHAR buf, LONG bufLen, OVERLAPPED *ov)
|
||||
{
|
||||
if ( hDevice == INVALID_HANDLE_VALUE ) return NULL;
|
||||
|
||||
int pkts;
|
||||
if(MaxPktSize)
|
||||
pkts = bufLen / MaxPktSize; // Number of packets implied by bufLen & pktSize
|
||||
else
|
||||
{
|
||||
pkts = 0;
|
||||
return NULL;
|
||||
}
|
||||
int pkts;
|
||||
if(MaxPktSize)
|
||||
pkts = bufLen / MaxPktSize; // Number of packets implied by bufLen & pktSize
|
||||
else
|
||||
{
|
||||
pkts = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bufLen % MaxPktSize) pkts++;
|
||||
|
||||
@ -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");
|
@ -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);
|
||||
|
456
sddc_source/src/libsddc/Core/arch/win32/FX3handler.cpp
Normal file
456
sddc_source/src/libsddc/Core/arch/win32/FX3handler.cpp
Normal 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;
|
||||
}
|
73
sddc_source/src/libsddc/Core/arch/win32/FX3handler.h
Normal file
73
sddc_source/src/libsddc/Core/arch/win32/FX3handler.h
Normal 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
|
191
sddc_source/src/libsddc/Core/dsp/ringbuffer.h
Normal file
191
sddc_source/src/libsddc/Core/dsp/ringbuffer.h
Normal 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;
|
||||
};
|
263
sddc_source/src/libsddc/Core/fft_mt_r2iq.cpp
Normal file
263
sddc_source/src/libsddc/Core/fft_mt_r2iq.cpp
Normal 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
|
||||
}
|
127
sddc_source/src/libsddc/Core/fft_mt_r2iq.h
Normal file
127
sddc_source/src/libsddc/Core/fft_mt_r2iq.h
Normal 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
|
||||
};
|
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_avx.cpp
Normal file
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_avx.cpp
Normal 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"
|
||||
}
|
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_avx2.cpp
Normal file
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_avx2.cpp
Normal 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"
|
||||
}
|
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_avx512.cpp
Normal file
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_avx512.cpp
Normal 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"
|
||||
}
|
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_def.cpp
Normal file
9
sddc_source/src/libsddc/Core/fft_mt_r2iq_def.cpp
Normal 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"
|
||||
}
|
171
sddc_source/src/libsddc/Core/fft_mt_r2iq_impl.hpp
Normal file
171
sddc_source/src/libsddc/Core/fft_mt_r2iq_impl.hpp
Normal 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;
|
||||
}
|
105
sddc_source/src/libsddc/Core/fir.cpp
Normal file
105
sddc_source/src/libsddc/Core/fir.cpp
Normal 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;
|
||||
}
|
3
sddc_source/src/libsddc/Core/fir.h
Normal file
3
sddc_source/src/libsddc/Core/fir.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int KaiserWindow(int num_taps, float Astop, float normFpass, float normFstop, float * Coef);
|
28
sddc_source/src/libsddc/Core/license.txt
Normal file
28
sddc_source/src/libsddc/Core/license.txt
Normal 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_
|
38
sddc_source/src/libsddc/Core/pffft/LICENSE.txt
Normal file
38
sddc_source/src/libsddc/Core/pffft/LICENSE.txt
Normal 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.
|
||||
|
733
sddc_source/src/libsddc/Core/pffft/bench_mixers.c
Normal file
733
sddc_source/src/libsddc/Core/pffft/bench_mixers.c
Normal 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;
|
||||
}
|
||||
|
20
sddc_source/src/libsddc/Core/pffft/fmv.h
Normal file
20
sddc_source/src/libsddc/Core/pffft/fmv.h
Normal 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
|
1148
sddc_source/src/libsddc/Core/pffft/pf_mixer.cpp
Normal file
1148
sddc_source/src/libsddc/Core/pffft/pf_mixer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
283
sddc_source/src/libsddc/Core/pffft/pf_mixer.h
Normal file
283
sddc_source/src/libsddc/Core/pffft/pf_mixer.h
Normal 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
|
50
sddc_source/src/libsddc/Core/r2iq.h
Normal file
50
sddc_source/src/libsddc/Core/r2iq.h
Normal 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
|
145
sddc_source/src/libsddc/Core/radio/BBRF103Radio.cpp
Normal file
145
sddc_source/src/libsddc/Core/radio/BBRF103Radio.cpp
Normal 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);
|
||||
}
|
||||
}
|
50
sddc_source/src/libsddc/Core/radio/HF103Radio.cpp
Normal file
50
sddc_source/src/libsddc/Core/radio/HF103Radio.cpp
Normal 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;
|
||||
}
|
178
sddc_source/src/libsddc/Core/radio/RX888R2Radio.cpp
Normal file
178
sddc_source/src/libsddc/Core/radio/RX888R2Radio.cpp
Normal 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);
|
||||
}
|
||||
}
|
210
sddc_source/src/libsddc/Core/radio/RX888R3Radio.cpp
Normal file
210
sddc_source/src/libsddc/Core/radio/RX888R3Radio.cpp
Normal 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;
|
||||
}
|
||||
}
|
1
sddc_source/src/libsddc/Core/radio/RX888Radio.cpp
Normal file
1
sddc_source/src/libsddc/Core/radio/RX888Radio.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "RadioHandler.h"
|
118
sddc_source/src/libsddc/Core/radio/RX999Radio.cpp
Normal file
118
sddc_source/src/libsddc/Core/radio/RX999Radio.cpp
Normal 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);
|
||||
}
|
120
sddc_source/src/libsddc/Core/radio/RXLucy.cpp
Normal file
120
sddc_source/src/libsddc/Core/radio/RXLucy.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
22
sddc_source/src/libsddc/Core/radio/RadioHardware.cpp
Normal file
22
sddc_source/src/libsddc/Core/radio/RadioHardware.cpp
Normal 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);
|
||||
}
|
||||
}
|
8
sddc_source/src/libsddc/Core/sddc_config.cpp
Normal file
8
sddc_source/src/libsddc/Core/sddc_config.cpp
Normal 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
|
94
sddc_source/src/libsddc/Core/sddc_config.h
Normal file
94
sddc_source/src/libsddc/Core/sddc_config.h
Normal 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_
|
||||
|
44
sddc_source/src/libsddc/HWSDRtable.h
Normal file
44
sddc_source/src/libsddc/HWSDRtable.h
Normal 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
|
154
sddc_source/src/libsddc/Interface.h
Normal file
154
sddc_source/src/libsddc/Interface.h
Normal 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)
|
||||
|
||||
|
23
sddc_source/src/libsddc/LICENSE.txt
Normal file
23
sddc_source/src/libsddc/LICENSE.txt
Normal 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)
|
||||
|
37
sddc_source/src/libsddc/libsddc/CMakeLists.txt
Normal file
37
sddc_source/src/libsddc/libsddc/CMakeLists.txt
Normal 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})
|
396
sddc_source/src/libsddc/libsddc/libsddc.cpp
Normal file
396
sddc_source/src/libsddc/libsddc/libsddc.cpp
Normal 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;
|
||||
}
|
171
sddc_source/src/libsddc/libsddc/libsddc.h
Normal file
171
sddc_source/src/libsddc/libsddc/libsddc.h
Normal 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 */
|
102
sddc_source/src/libsddc/libsddc/wavehdr.h
Normal file
102
sddc_source/src/libsddc/libsddc/wavehdr.h
Normal 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
|
||||
|
232
sddc_source/src/libsddc/libsddc/wavewrite.c
Normal file
232
sddc_source/src/libsddc/libsddc/wavewrite.c
Normal 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
|
55
sddc_source/src/libsddc/libsddc/wavewrite.h
Normal file
55
sddc_source/src/libsddc/libsddc/wavewrite.h
Normal 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
248
sddc_source/src/main.cpp
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user