add sddc source prototype + add new ATV decoder + fix windows builds

This commit is contained in:
AlexandreRouma 2025-04-23 04:49:45 +02:00
parent aa2b4b1c58
commit e75cc7be6f
24 changed files with 2412 additions and 310 deletions

View File

@ -0,0 +1,65 @@
#pragma once
#include <dsp/processor.h>
#include <dsp/math/fast_atan2.h>
#include <dsp/math/hz_to_rads.h>
#include <dsp/math/normalize_phase.h>
namespace dsp::demod {
class Amplitude : public Processor<complex_t, float> {
using base_type = Processor<complex_t, float>;
public:
Amplitude() {}
Amplitude(stream<complex_t>* in, double deviation) { init(in, deviation); }
Amplitude(stream<complex_t>* in, double deviation, double samplerate) { init(in, deviation, samplerate); }
virtual void init(stream<complex_t>* in, double deviation) {
_invDeviation = 1.0 / deviation;
base_type::init(in);
}
virtual void init(stream<complex_t>* in, double deviation, double samplerate) {
init(in, math::hzToRads(deviation, samplerate));
}
void setDeviation(double deviation) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_invDeviation = 1.0 / deviation;
}
void setDeviation(double deviation, double samplerate) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_invDeviation = 1.0 / math::hzToRads(deviation, samplerate);
}
inline int process(int count, complex_t* in, float* out) {
volk_32fc_magnitude_32f(out, (lv_32fc_t*)in, count);
return count;
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
phase = 0.0f;
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
base_type::_in->flush();
if (!base_type::out.swap(count)) { return -1; }
return count;
}
protected:
float _invDeviation;
float phase = 0.0f;
};
}

View File

@ -0,0 +1,176 @@
617 + 6 -> I
304 + 6 -> II
616 + 6 -> III
305 + 6 -> IV
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624
305
306
307
308
309
310
311
312
313
617
618
619
620
621
622
623
624
0
304
305
306
307
308
309
310
311
312
616
617
618
619
620
621
622
623
624

View File

@ -1,63 +0,0 @@
#pragma once
#include <dsp/loop/pll.h>
#include "chrominance_filter.h"
// TODO: Should be 60 but had to try something
#define BURST_START (63+CHROMA_FIR_DELAY)
#define BURST_END (BURST_START+28)
#define A_PHASE ((135.0/180.0)*FL_M_PI)
#define B_PHASE ((-135.0/180.0)*FL_M_PI)
namespace dsp::loop {
class ChromaPLL : public PLL {
using base_type = PLL;
public:
ChromaPLL() {}
ChromaPLL(stream<complex_t>* in, double bandwidth, double initPhase = 0.0, double initFreq = 0.0, double minFreq = -FL_M_PI, double maxFreq = FL_M_PI) {
base_type::init(in, bandwidth, initFreq, initPhase, minFreq, maxFreq);
}
inline int process(int count, complex_t* in, complex_t* out, bool aphase = false) {
// Process the pre-burst section
for (int i = 0; i < BURST_START; i++) {
out[i] = in[i] * math::phasor(-pcl.phase);
pcl.advancePhase();
}
// Process the burst itself
if (aphase) {
for (int i = BURST_START; i < BURST_END; i++) {
complex_t outVal = in[i] * math::phasor(-pcl.phase);
out[i] = outVal;
pcl.advance(math::normalizePhase(outVal.phase() - A_PHASE));
}
}
else {
for (int i = BURST_START; i < BURST_END; i++) {
complex_t outVal = in[i] * math::phasor(-pcl.phase);
out[i] = outVal;
pcl.advance(math::normalizePhase(outVal.phase() - B_PHASE));
}
}
// Process the post-burst section
for (int i = BURST_END; i < count; i++) {
out[i] = in[i] * math::phasor(-pcl.phase);
pcl.advancePhase();
}
return count;
}
inline int processBlank(int count, complex_t* in, complex_t* out) {
for (int i = 0; i < count; i++) {
out[i] = in[i] * math::phasor(-pcl.phase);
pcl.advancePhase();
}
return count;
}
};
}

View File

@ -1,239 +0,0 @@
#pragma once
#include <dsp/types.h>
inline const dsp::complex_t CHROMA_FIR[] = {
{-0.000005461290583903, -0.000011336784355655},
{ 0.000020060944485414, 0.000009851315045203},
{-0.000034177222729438, 0.000007245841504981},
{ 0.000027694034878705, -0.000033114740542635},
{-0.000001217597841648, 0.000039141482370942},
{-0.000008324593371228, -0.000011315001355976},
{-0.000038085228233509, -0.000010585909953738},
{ 0.000114833396071141, -0.000047778708840608},
{-0.000115428390169113, 0.000205816198882814},
{-0.000055467806072871, -0.000356692479491626},
{ 0.000349316846854190, 0.000326162940234916},
{-0.000558465829929114, -0.000048001521408724},
{ 0.000488176200631416, -0.000319593757302922},
{-0.000169437838021935, 0.000501610900725908},
{-0.000131793335799502, -0.000373003580727547},
{ 0.000166817395492786, 0.000105930895534474},
{ 0.000030499908326112, -0.000003048682668943},
{-0.000174999505027919, 0.000168008090089458},
{ 0.000054431163395030, -0.000385174790951272},
{ 0.000215876012859739, 0.000372695852521209},
{-0.000325534912280750, -0.000130173041693966},
{ 0.000154951430569290, -0.000045395998708328},
{ 0.000054324657659002, -0.000076028700470037},
{ 0.000015664427565764, 0.000348002612845696},
{-0.000345943017888332, -0.000402175417043307},
{ 0.000568731727879741, 0.000112347863435682},
{-0.000416485880859085, 0.000211750352828909},
{ 0.000087462353623011, -0.000188197153014309},
{-0.000032082305030264, -0.000136804226080664},
{ 0.000379089999045955, 0.000303466839685362},
{-0.000726760198519770, -0.000007022279302816},
{ 0.000619888661818195, -0.000476871323359809},
{-0.000151885493742993, 0.000595641190573181},
{-0.000100626407015494, -0.000227947144491108},
{-0.000201935458823941, -0.000107628631934340},
{ 0.000680260922139900, -0.000120771182888852},
{-0.000666108629277491, 0.000744775901128973},
{ 0.000067236591919755, -0.001044125966364420},
{ 0.000447037274751822, 0.000651912509450913},
{-0.000262675893448686, -0.000082499729563337},
{-0.000349821460486320, 0.000132102793530818},
{ 0.000507024815168287, -0.000837598610490618},
{ 0.000163814255478652, 0.001346530693477834},
{-0.000970457632383793, -0.000968411010101160},
{ 0.000974834882891140, 0.000116507082762032},
{-0.000225464280571542, 0.000137131865995708},
{-0.000211542240694642, 0.000563783548428947},
{-0.000414412310798766, -0.001309793399193736},
{ 0.001497010004594478, 0.001021907858926259},
{-0.001752019159639658, 0.000116536066154131},
{ 0.000872822027879430, -0.000783952720205569},
{-0.000032439446797970, 0.000184988059956734},
{ 0.000446259382722895, 0.000833040920509238},
{-0.001741577737284306, -0.000764423771425237},
{ 0.002306569133792772, -0.000593352416441601},
{-0.001336084746214192, 0.001744394557524181},
{-0.000015810020735495, -0.001342809547658260},
{ 0.000007636494885364, 0.000009498318627546},
{ 0.001403876768349702, 0.000326101441888391},
{-0.002351020828600226, 0.001098649819278302},
{ 0.001389314639579544, -0.002746943712072884},
{ 0.000526319899588909, 0.002635084366837732},
{-0.001109526585744687, -0.000950323796527721},
{-0.000307792427984886, -0.000013203419520794},
{ 0.001737955094951111, -0.001247368808692850},
{-0.000974502437588420, 0.003352512117661680},
{-0.001462571137390936, -0.003635296917435679},
{ 0.002783459090201693, 0.001604420226187745},
{-0.001471518558760170, 0.000211117948702137},
{-0.000575340825070194, 0.000601820846100026},
{ 0.000302090333345692, -0.003088058972305493},
{ 0.002496092353182990, 0.003912508340989065},
{-0.004645661091012423, -0.001630427298020200},
{ 0.003556824805628799, -0.001209822327859352},
{-0.000744999556260706, 0.001143238699138109},
{ 0.000144278726929409, 0.001638049051599065},
{-0.003025291044450178, -0.003226370992887968},
{ 0.006047866290490120, 0.000927406808799887},
{-0.005338456415106141, 0.003008811999350399},
{ 0.001642959659014839, -0.003972384205231079},
{ 0.000273874932822212, 0.000977326273749033},
{ 0.002315022846573390, 0.001695671268241410},
{-0.006240953957978884, 0.000207330368698293},
{ 0.006164252120861735, -0.005177351717451013},
{-0.001560310257561104, 0.007437030759707700},
{-0.002131333814462852, -0.004317129694157112},
{ 0.000280518918541908, 0.000134405998842553},
{ 0.004612116481180659, -0.001024468120657814},
{-0.005599300279638699, 0.006828277067771868},
{ 0.000228879728552504, -0.010675998154712657},
{ 0.005692081512980654, 0.007582243186569848},
{-0.005100500569859509, -0.001364751685737153},
{-0.000902490398043454, 0.000385770160220703},
{ 0.003673858819546609, -0.006701685283451640},
{ 0.002079056046131593, 0.012568579063417429},
{-0.010730008156911677, -0.009826454574016218},
{ 0.012092401380903161, 0.000921764172237851},
{-0.004714530989129091, 0.003151948807627123},
{-0.001055930168838909, 0.003228576712467020},
{-0.004343270165991213, -0.011924332879354394},
{ 0.016499994418955999, 0.010255324919126899},
{-0.021047239750251585, 0.002309419513135448},
{ 0.011855513874047341, -0.011604071033866310},
{-0.000777842281358575, 0.005916341648175263},
{ 0.004380939277688377, 0.007397670455730446},
{-0.021891594662401131, -0.008509480947490166},
{ 0.032787638290674201, -0.009950745850861956},
{-0.021022579272463194, 0.030030850567389102},
{-0.001508145650189953, -0.027571914870304640},
{ 0.004056649693022923, 0.004624901687718579},
{ 0.025728742586666287, 0.004824671348397606},
{-0.058002700931665603, 0.030198618296813803},
{ 0.043631619628438784, -0.096308304333327280},
{ 0.033451363423624300, 0.136687079396426990},
{-0.129387018420204200, -0.101540513046619400},
{ 0.172881344826560730, -0.000000000000005297},
{-0.129387018420198010, 0.101540513046627330},
{ 0.033451363423615862, -0.136687079396429050},
{ 0.043631619628444723, 0.096308304333324601},
{-0.058002700931667456, -0.030198618296810247},
{ 0.025728742586665992, -0.004824671348399184},
{ 0.004056649693022639, -0.004624901687718827},
{-0.001508145650188251, 0.027571914870304734},
{-0.021022579272465047, -0.030030850567387805},
{ 0.032787638290674812, 0.009950745850859947},
{-0.021891594662400610, 0.008509480947491507},
{ 0.004380939277687923, -0.007397670455730714},
{-0.000777842281358940, -0.005916341648175215},
{ 0.011855513874048058, 0.011604071033865578},
{-0.021047239750251731, -0.002309419513134139},
{ 0.016499994418955360, -0.010255324919127926},
{-0.004343270165990471, 0.011924332879354665},
{-0.001055930168839110, -0.003228576712466955},
{-0.004714530989129287, -0.003151948807626830},
{ 0.012092401380903103, -0.000921764172238603},
{-0.010730008156911072, 0.009826454574016881},
{ 0.002079056046130817, -0.012568579063417559},
{ 0.003673858819547020, 0.006701685283451416},
{-0.000902490398043478, -0.000385770160220647},
{-0.005100500569859424, 0.001364751685737466},
{ 0.005692081512980187, -0.007582243186570198},
{ 0.000228879728553163, 0.010675998154712643},
{-0.005599300279639117, -0.006828277067771524},
{ 0.004612116481180722, 0.001024468120657532},
{ 0.000280518918541900, -0.000134405998842571},
{-0.002131333814462586, 0.004317129694157243},
{-0.001560310257561563, -0.007437030759707604},
{ 0.006164252120862052, 0.005177351717450635},
{-0.006240953957978898, -0.000207330368697911},
{ 0.002315022846573286, -0.001695671268241552},
{ 0.000273874932822152, -0.000977326273749050},
{ 0.001642959659015084, 0.003972384205230976},
{-0.005338456415106324, -0.003008811999350072},
{ 0.006047866290490063, -0.000927406808800258},
{-0.003025291044449980, 0.003226370992888153},
{ 0.000144278726929308, -0.001638049051599074},
{-0.000744999556260777, -0.001143238699138063},
{ 0.003556824805628873, 0.001209822327859134},
{-0.004645661091012323, 0.001630427298020484},
{ 0.002496092353182751, -0.003912508340989219},
{ 0.000302090333345882, 0.003088058972305475},
{-0.000575340825070231, -0.000601820846099991},
{-0.001471518558760183, -0.000211117948702046},
{ 0.002783459090201593, -0.001604420226187919},
{-0.001462571137390710, 0.003635296917435769},
{-0.000974502437588628, -0.003352512117661619},
{ 0.001737955094951189, 0.001247368808692742},
{-0.000307792427984885, 0.000013203419520814},
{-0.001109526585744628, 0.000950323796527789},
{ 0.000526319899588746, -0.002635084366837765},
{ 0.001389314639579712, 0.002746943712072799},
{-0.002351020828600294, -0.001098649819278158},
{ 0.001403876768349682, -0.000326101441888477},
{ 0.000007636494885364, -0.000009498318627546},
{-0.000015810020735412, 0.001342809547658261},
{-0.001336084746214299, -0.001744394557524099},
{ 0.002306569133792808, 0.000593352416441460},
{-0.001741577737284259, 0.000764423771425344},
{ 0.000446259382722843, -0.000833040920509266},
{-0.000032439446797982, -0.000184988059956732},
{ 0.000872822027879478, 0.000783952720205515},
{-0.001752019159639665, -0.000116536066154024},
{ 0.001497010004594416, -0.001021907858926351},
{-0.000414412310798685, 0.001309793399193761},
{-0.000211542240694677, -0.000563783548428934},
{-0.000225464280571550, -0.000137131865995694},
{ 0.000974834882891133, -0.000116507082762092},
{-0.000970457632383734, 0.000968411010101219},
{ 0.000163814255478569, -0.001346530693477844},
{ 0.000507024815168339, 0.000837598610490586},
{-0.000349821460486328, -0.000132102793530797},
{-0.000262675893448681, 0.000082499729563353},
{ 0.000447037274751782, -0.000651912509450940},
{ 0.000067236591919819, 0.001044125966364416},
{-0.000666108629277537, -0.000744775901128932},
{ 0.000680260922139908, 0.000120771182888810},
{-0.000201935458823935, 0.000107628631934352},
{-0.000100626407015480, 0.000227947144491114},
{-0.000151885493743030, -0.000595641190573172},
{ 0.000619888661818225, 0.000476871323359771},
{-0.000726760198519770, 0.000007022279302861},
{ 0.000379089999045936, -0.000303466839685386},
{-0.000032082305030256, 0.000136804226080666},
{ 0.000087462353623023, 0.000188197153014303},
{-0.000416485880859098, -0.000211750352828883},
{ 0.000568731727879734, -0.000112347863435717},
{-0.000345943017888307, 0.000402175417043329},
{ 0.000015664427565742, -0.000348002612845697},
{ 0.000054324657659007, 0.000076028700470034},
{ 0.000154951430569292, 0.000045395998708319},
{-0.000325534912280742, 0.000130173041693986},
{ 0.000215876012859716, -0.000372695852521222},
{ 0.000054431163395054, 0.000385174790951269},
{-0.000174999505027930, -0.000168008090089447},
{ 0.000030499908326113, 0.000003048682668941},
{ 0.000166817395492779, -0.000105930895534485},
{-0.000131793335799479, 0.000373003580727555},
{-0.000169437838021966, -0.000501610900725898},
{ 0.000488176200631435, 0.000319593757302892},
{-0.000558465829929111, 0.000048001521408758},
{ 0.000349316846854170, -0.000326162940234938},
{-0.000055467806072849, 0.000356692479491629},
{-0.000115428390169126, -0.000205816198882806},
{ 0.000114833396071144, 0.000047778708840601},
{-0.000038085228233508, 0.000010585909953741},
{-0.000008324593371228, 0.000011315001355977},
{-0.000001217597841650, -0.000039141482370942},
{ 0.000027694034878707, 0.000033114740542633},
{-0.000034177222729439, -0.000007245841504979},
{ 0.000020060944485413, -0.000009851315045204},
{-0.000005461290583903, 0.000011336784355656},
};
#define CHROMA_FIR_SIZE (sizeof(CHROMA_FIR)/sizeof(dsp::complex_t))
#define CHROMA_FIR_DELAY ((CHROMA_FIR_SIZE-1)/2)

View File

@ -0,0 +1,131 @@
#pragma once
#include <dsp/types.h>
const dsp::complex_t CHROMA_BANDPASS[123] = {
{ -0.000007675039564594f, -0.000017362992335168f },
{ 0.000050180791439308f, -0.000005054021864311f },
{ -0.000022529111707761f, 0.000102942513429095f },
{ -0.000157609487484146f, -0.000092618697641464f },
{ 0.000205649042029007f, -0.000181710515677257f },
{ 0.000143445458895462f, 0.000331994546004200f },
{ -0.000414693079508517f, 0.000038265188132615f },
{ 0.000090081630021837f, -0.000395731646002122f },
{ 0.000257705918065856f, 0.000154354504676150f },
{ -0.000064051192147575f, 0.000055648228186439f },
{ 0.000089938060647145f, 0.000213032074676941f },
{ -0.000604775098099200f, 0.000050706635726124f },
{ 0.000223309865890358f, -0.000944433958755193f },
{ 0.001049943574694384f, 0.000640863688898729f },
{ -0.000983491651119595f, 0.000840133365053179f },
{ -0.000417178588714773f, -0.001011686459999295f },
{ 0.000616677332283103f, -0.000046513429902547f },
{ 0.000018549463752019f, -0.000075619948809012f },
{ 0.000734408386201158f, 0.000456742966201638f },
{ -0.001192460562555901f, 0.001001510577200253f },
{ -0.000729137747758392f, -0.001811046261815935f },
{ 0.001878272869910273f, -0.000125879189667096f },
{ -0.000312873903977849f, 0.001230889889574772f },
{ -0.000142534831707354f, -0.000090307321579771f },
{ -0.000942796972567241f, 0.000778470227412111f },
{ -0.000945381510920278f, -0.002406055808135091f },
{ 0.003537159230775561f, -0.000207350791625892f },
{ -0.000956199555190230f, 0.003634225577771235f },
{ -0.002543835202533561f, -0.001641705037372486f },
{ 0.001064108471592447f, -0.000863770138941644f },
{ -0.000335799601479829f, -0.000876091753216939f },
{ 0.003390761989356699f, -0.000170321604912419f },
{ -0.001408130728751909f, 0.005175554625981795f },
{ -0.005203055300834108f, -0.003419861284250694f },
{ 0.004342719678657084f, -0.003465264906764298f },
{ 0.001143432997855297f, 0.003059520699490539f },
{ 0.000304096484476364f, -0.000012725974706621f },
{ -0.001193870642975282f, 0.004247469277548632f },
{ -0.006681021498855877f, -0.004471771356204969f },
{ 0.007965721969864534f, -0.006247895626072559f },
{ 0.003365883969059717f, 0.009241201835481184f },
{ -0.006835562188141396f, 0.000228798228738161f },
{ 0.000409900284971528f, -0.001412838961851673f },
{ -0.004331406608345981f, -0.002951876085350234f },
{ 0.009290089917766562f, -0.007161958719089258f },
{ 0.005418326020709935f, 0.015272361365960607f },
{ -0.017077565432843410f, 0.000428641984774326f },
{ 0.003850771342644978f, -0.012869517593577566f },
{ 0.004380859690202961f, 0.003039552423897447f },
{ 0.004761181766399753f, -0.003607421240356480f },
{ 0.005926935731028822f, 0.017160134858844222f },
{ -0.028153584885925551f, 0.000471042980325370f },
{ 0.009655944938035437f, -0.031314555422639050f },
{ 0.023930146568136038f, 0.016901617811072800f },
{ -0.012998853255109976f, 0.009678807314399702f },
{ 0.002043176559434885f, 0.006079907699564680f },
{ -0.036686455817128191f, 0.000306882557812233f },
{ 0.021529138474771701f, -0.067800343150283604f },
{ 0.085421344938160879f, 0.061409588050754214f },
{ -0.108166660998898100f, 0.079141989828113088f },
{ -0.047617308971534079f, -0.145721049254261960f },
{ 0.160079041453427080f, -0.000000000000000427f },
{ -0.047617308971533295f, 0.145721049254262240f },
{ -0.108166660998898530f, -0.079141989828112505f },
{ 0.085421344938160546f, -0.061409588050754672f },
{ 0.021529138474772065f, 0.067800343150283493f },
{ -0.036686455817128191f, -0.000306882557812037f },
{ 0.002043176559434853f, -0.006079907699564691f },
{ -0.012998853255110026f, -0.009678807314399631f },
{ 0.023930146568135951f, -0.016901617811072928f },
{ 0.009655944938035604f, 0.031314555422638994f },
{ -0.028153584885925554f, -0.000471042980325220f },
{ 0.005926935731028730f, -0.017160134858844253f },
{ 0.004761181766399772f, 0.003607421240356455f },
{ 0.004380859690202943f, -0.003039552423897470f },
{ 0.003850771342645046f, 0.012869517593577545f },
{ -0.017077565432843413f, -0.000428641984774235f },
{ 0.005418326020709854f, -0.015272361365960637f },
{ 0.009290089917766600f, 0.007161958719089209f },
{ -0.004331406608345964f, 0.002951876085350257f },
{ 0.000409900284971536f, 0.001412838961851670f },
{ -0.006835562188141398f, -0.000228798228738125f },
{ 0.003365883969059667f, -0.009241201835481201f },
{ 0.007965721969864567f, 0.006247895626072517f },
{ -0.006681021498855855f, 0.004471771356205005f },
{ -0.001193870642975304f, -0.004247469277548626f },
{ 0.000304096484476364f, 0.000012725974706619f },
{ 0.001143432997855281f, -0.003059520699490545f },
{ 0.004342719678657102f, 0.003465264906764274f },
{ -0.005203055300834089f, 0.003419861284250722f },
{ -0.001408130728751936f, -0.005175554625981787f },
{ 0.003390761989356700f, 0.000170321604912401f },
{ -0.000335799601479825f, 0.000876091753216940f },
{ 0.001064108471592452f, 0.000863770138941638f },
{ -0.002543835202533552f, 0.001641705037372499f },
{ -0.000956199555190250f, -0.003634225577771230f },
{ 0.003537159230775563f, 0.000207350791625874f },
{ -0.000945381510920265f, 0.002406055808135096f },
{ -0.000942796972567245f, -0.000778470227412106f },
{ -0.000142534831707354f, 0.000090307321579771f },
{ -0.000312873903977856f, -0.001230889889574770f },
{ 0.001878272869910274f, 0.000125879189667086f },
{ -0.000729137747758382f, 0.001811046261815939f },
{ -0.001192460562555906f, -0.001001510577200246f },
{ 0.000734408386201156f, -0.000456742966201642f },
{ 0.000018549463752019f, 0.000075619948809012f },
{ 0.000616677332283103f, 0.000046513429902543f },
{ -0.000417178588714767f, 0.001011686459999298f },
{ -0.000983491651119600f, -0.000840133365053174f },
{ 0.001049943574694380f, -0.000640863688898734f },
{ 0.000223309865890363f, 0.000944433958755192f },
{ -0.000604775098099200f, -0.000050706635726121f },
{ 0.000089938060647144f, -0.000213032074676941f },
{ -0.000064051192147576f, -0.000055648228186438f },
{ 0.000257705918065856f, -0.000154354504676151f },
{ 0.000090081630021839f, 0.000395731646002121f },
{ -0.000414693079508517f, -0.000038265188132613f },
{ 0.000143445458895461f, -0.000331994546004200f },
{ 0.000205649042029008f, 0.000181710515677256f },
{ -0.000157609487484145f, 0.000092618697641465f },
{ -0.000022529111707761f, -0.000102942513429094f },
{ 0.000050180791439308f, 0.000005054021864311f },
{ -0.000007675039564594f, 0.000017362992335168f }
};
#define CHROMA_BANDPASS_SIZE (sizeof(CHROMA_BANDPASS)/sizeof(dsp::complex_t))
#define CHROMA_BANDPASS_DELAY (CHROMA_BANDPASS_SIZE/2)

View File

@ -22,8 +22,16 @@
#define SYNC_LEVEL (-0.428)
#define COLORBURST_START 84
#define COLORBURST_LEN 33
#define MAX_LOCK 1000
dsp::complex_t PHASE_REF[2] = {
{ -0.707106781186547f, 0.707106781186547f },
{ -0.707106781186547f, -0.707106781186547f }
};
class LineSync : public dsp::Processor<float, float> {
using base_type = dsp::Processor<float, float>;
public:

View File

@ -16,15 +16,14 @@
#include <dsp/filter/fir.h>
#include <dsp/taps/from_array.h>
#include "chrominance_filter.h"
#include "chroma_pll.h"
#include "linesync_old.h"
#include "amplitude.h"
#include <dsp/demod/am.h>
#include <dsp/loop/fast_agc.h>
#include "filters.h"
#include <dsp/math/normalize_phase.h>
#include <fstream>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
SDRPP_MOD_INFO{/* Name: */ "atv_decoder",
@ -51,6 +50,8 @@ class ATVDecoderModule : public ModuleManager::Instance {
r2c.init(NULL);
file = std::ofstream("chromasub_diff.bin", std::ios::binary | std::ios::out);
agc.start();
demod.start();
sync.start();
@ -82,6 +83,8 @@ class ATVDecoderModule : public ModuleManager::Instance {
bool isEnabled() { return enabled; }
std::ofstream file;
private:
static void menuHandler(void *ctx) {
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
@ -118,10 +121,17 @@ class ATVDecoderModule : public ModuleManager::Instance {
style::endDisabled();
}
if (ImGui::Button("Close Debug")) {
_this->file.close();
}
ImGui::Text("Gain: %f", _this->gain);
ImGui::Text("Offset: %f", _this->offset);
ImGui::Text("Subcarrier: %f", _this->subcarrierFreq);
}
uint32_t pp = 0;
static void handler(float *data, int count, void *ctx) {
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
@ -158,12 +168,69 @@ class ATVDecoderModule : public ModuleManager::Instance {
// Save sync type to history
_this->syncHistory = (_this->syncHistory << 2) | (longSync << 1) | shortSync;
// If the line has a colorburst, decode it
dsp::complex_t* buf1 = _this->r2c.out.readBuf;
dsp::complex_t* buf2 = _this->r2c.out.writeBuf;
if (true) {
// Convert the line into complex
_this->r2c.process(count, data, buf1);
// Extract the chroma subcarrier (TODO: Optimise by running only where needed)
for (int i = COLORBURST_START; i < count-(CHROMA_BANDPASS_DELAY+1); i++) {
volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&buf2[i], (lv_32fc_t*)&buf1[i - CHROMA_BANDPASS_DELAY], (lv_32fc_t*)CHROMA_BANDPASS, CHROMA_BANDPASS_SIZE);
}
// Down convert the chroma subcarrier (TODO: Optimise by running only where needed)
lv_32fc_t startPhase = { 1.0f, 0.0f };
lv_32fc_t phaseDelta = { sinf(_this->subcarrierFreq), cosf(_this->subcarrierFreq) };
#if VOLK_VERSION >= 030100
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], &phaseDelta, &startPhase, count - COLORBURST_START);
#else
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], phaseDelta, &startPhase, count - COLORBURST_START);
#endif
// Compute the phase of the burst
dsp::complex_t burstAvg = { 0.0f, 0.0f };
volk_32fc_accumulator_s32fc((lv_32fc_t*)&burstAvg, (lv_32fc_t*)&buf2[COLORBURST_START], COLORBURST_LEN);
float burstAmp = burstAvg.amplitude();
if (burstAmp*(1.0f/(float)COLORBURST_LEN) < 0.02f) {
printf("%d\n", _this->line);
}
burstAvg *= (1.0f / (burstAmp*burstAmp));
burstAvg = burstAvg.conj();
// Normalize the chroma data (TODO: Optimise by running only where needed)
volk_32fc_s32fc_multiply_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], *((lv_32fc_t*)&burstAvg), count - COLORBURST_START);
// Compute the frequency error of the burst
float phase = buf2[COLORBURST_START].phase();
float error = 0.0f;
for (int i = COLORBURST_START+1; i < COLORBURST_START+COLORBURST_LEN; i++) {
float cphase = buf2[i].phase();
error += dsp::math::normalizePhase(cphase - phase);
phase = cphase;
}
error *= (1.0f / (float)(COLORBURST_LEN-1));
// Update the subcarrier freq
_this->subcarrierFreq += error*0.0001f;
}
// Render the line if it's visible
if (_this->ypos >= 34 && _this->ypos <= 34+576-1) {
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos - 34)*768];
for (int i = 155; i < (155+768); i++) {
int imval = std::clamp<float>(data[i] * 255.0f, 0, 255);
currentLine[i-155] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
if (_this->colorMode) {
for (int i = 155; i < (155+768); i++) {
int imval1 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].re*5.0f) * 255.0f, 0, 255);
int imval2 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].im*5.0f) * 255.0f, 0, 255);
currentLine[i-155] = 0xFF000000 | (imval2 << 8) | imval1;
}
}
else {
for (int i = 155; i < (155+768); i++) {
int imval = std::clamp<float>(data[i-155+COLORBURST_START] * 255.0f, 0, 255);
currentLine[i-155] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
}
}
}
@ -188,6 +255,7 @@ class ATVDecoderModule : public ModuleManager::Instance {
// Start the odd field
_this->ypos = 1;
_this->line++;
}
else if (rollToEven || syncToEven) {
// Update the vertical lock state
@ -201,12 +269,14 @@ class ATVDecoderModule : public ModuleManager::Instance {
// Start the even field
_this->ypos = 0;
_this->line = 0;
// Swap the video buffer
_this->img.swap();
}
else {
_this->ypos += 2;
_this->line++;
}
}
@ -214,8 +284,10 @@ class ATVDecoderModule : public ModuleManager::Instance {
float offset = 0.0f;
float gain = 1.0f;
uint16_t syncHistory = 0;
int line = 0;
int ypos = 0;
int vlock = 0;
float subcarrierFreq = 0.0f;
std::string name;
bool enabled = true;

View File

@ -3,6 +3,7 @@
#include <gui/gui.h>
#include <gui/style.h>
#include <signal_path/signal_path.h>
#include <chrono>
SDRPP_MOD_INFO{
/* Name: */ "scanner",

View File

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.13)
project(sddc_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
add_subdirectory("./libsddc")
target_link_libraries(sddc_source PRIVATE sddc)

View File

@ -0,0 +1,2 @@
build/
.vscode/

View File

@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.13)
project(libsddc VERSION 0.2.0)
# Options
option(BUILD_SDDC_UTILS "Build SDDC utilities such as sddc_info" ON)
option(INSTALL_UDEV_RULES "Install UDEV rules (Linux only)" ON)
# List all source files
file(GLOB_RECURSE SRC "src/*.c")
# On windows, all symbols must be exported
if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif ()
# Create dynamic libs
add_library(sddc SHARED ${SRC})
# # Set optimisation flags
# if (${CMAKE_BUILD_TYPE} MATCHES "Debug")
# # Debug Flags
# if (MSVC)
# target_compile_options(sddc PRIVATE /EHsc)
# else ()
# target_compile_options(sddc PRIVATE -g -Og)
# endif ()
# else()
# # Normal Flags
# if (MSVC)
# target_compile_options(sddc PRIVATE /O2 /Ob2 /EHsc)
# else ()
# target_compile_options(sddc PRIVATE -O3)
# endif ()
# endif()
# Include the include folder
target_include_directories(sddc PUBLIC "include/")
# Find libusb
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
# Link to libusb
target_link_libraries(sddc PRIVATE PkgConfig::libusb)
# TODO: Have it default instead of override
if (MSVC)
set(CMAKE_INSTALL_PREFIX "C:/Program Files/SDDC/")
set(CMAKE_INSTALL_BINDIR "bin")
set(CMAKE_INSTALL_LIBDIR "lib")
set(CMAKE_INSTALL_INCLUDEDIR "include")
else ()
include(GNUInstallDirs)
endif ()
if (NOT MSVC)
# Configure pkgconfig file
configure_file(${CMAKE_SOURCE_DIR}/libsddc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libsddc.pc @ONLY)
# Install pkgconfig file
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libsddc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif ()
# Install the library
if (MSVC)
install(TARGETS sddc)
else ()
install(TARGETS sddc DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif ()
# Install the headers
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
# On Windows, install dependencies
if (MSVC)
install(FILES $<TARGET_FILE_DIR:sddc>/libusb-1.0.dll DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()
# Build utils if enabled
if (BUILD_SDDC_UTILS)
add_subdirectory("utils/sddc_info")
add_subdirectory("utils/sddc_rx")
endif ()
# # Create uninstall target
# configure_file(${CMAKE_SOURCE_DIR}/cmake/uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake @ONLY)
# add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake)

View File

@ -0,0 +1,32 @@
# http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
STRING(REGEX REPLACE "\n" ";" files "${files}")
FOREACH(file ${files})
MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
IF(EXISTS "$ENV{DESTDIR}${file}")
EXEC_PROGRAM(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
IF(NOT "${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
ENDIF(NOT "${rm_retval}" STREQUAL 0)
ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}")
EXEC_PROGRAM(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
IF(NOT "${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
ENDIF(NOT "${rm_retval}" STREQUAL 0)
ELSE(EXISTS "$ENV{DESTDIR}${file}")
MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
ENDIF(EXISTS "$ENV{DESTDIR}${file}")
ENDFOREACH(file)

View File

@ -0,0 +1,177 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
// Handle inclusion from C++ code
#ifdef __cplusplus
extern "C" {
#endif
#define SDDC_SERIAL_MAX_LEN 256
enum sddc_model {
SDDC_MODEL_UNKNOWN = 0x00,
SDDC_MODEL_BBRF103 = 0x01,
SDDC_MODEL_HF103 = 0x02,
SDDC_MODEL_RX888 = 0x03,
SDDC_MODEL_RX888_MK2 = 0x04,
SDDC_MODEL_RX999 = 0x05,
SDDC_MODEL_RXLUCY = 0x06,
SDDC_MODEL_RX888_MK3 = 0x07
};
typedef enum sddc_model sddc_model_t;
enum sddc_error {
SDDC_ERROR_UNKNOWN = -99,
SDDC_ERROR_NOT_IMPLEMENTED = -98,
SDDC_ERROR_FIRMWARE_UPLOAD_FAILED = -4,
SDDC_ERROR_NOT_FOUND = -3,
SDDC_ERROR_USB_ERROR = -2,
SDDC_ERROR_TIMEOUT = -1,
SDDC_SUCCESS = 0
};
typedef enum sddc_error sddc_error_t;
/**
* Device instance.
*/
struct sddc_dev;
typedef struct sddc_dev sddc_dev_t;
/**
* Device information.
*/
struct sddc_devinfo {
const char serial[SDDC_SERIAL_MAX_LEN];
sddc_model_t model;
int firmwareMajor;
int firmwareMinor;
};
typedef struct sddc_devinfo sddc_devinfo_t;
/**
* Parameter range. A step size of zero means infinitely variable.
*/
struct sddc_range {
double start;
double end;
double step;
};
typedef struct sddc_range sddc_range_t;
/**
* Get the string representation of a device model.
* @param model Model to get the string representation of.
* @return String representation of the model.
*/
const char* sddc_model_to_string(sddc_model_t model);
/**
* Get the string representation of an error.
* @param model Error to get the string representation of.
* @return String representation of the error.
*/
const char* sddc_error_to_string(sddc_error_t error);
/**
* Set the path to the firmware image.
* @param path Path to the firmware image.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_firmware_path(const char* path);
/**
* Get a list of connected devices. The returned list has to be freed using `sddc_free_device_list()` if it isn't empty.
* @param dev_list Pointer to a list of devices.
* @return Number of devices in the list or an error code if an error occured.
*/
int sddc_get_device_list(sddc_devinfo_t** dev_list);
/**
* Free a device list returned by `sddc_get_device_list()`. Attempting to free a list returned empty has no effect.
* @param dev_list Device list to free.
*/
void sddc_free_device_list(sddc_devinfo_t* dev_list);
/**
* Open a device by its serial number.
* @param serial Serial number of the device to open.
* @param dev Pointer to a SDDC device pointer to populate once open.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev);
/**
* Close an opened SDDC device.
* @param dev SDDC Device to close.
*/
void sddc_close(sddc_dev_t* dev);
/**
* Get the range of samplerate supported by a device.
* @param dev SDDC device.
* @return Range of supported samplerates.
*/
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev);
/**
* Set the device's sampling rate.
* @param dev SDDC device.
* @param samplerate Sampling rate.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate);
/**
* Enable the ADC's dithering feature.
* @param dev SDDC device.
* @param enabled True to enable dithering, false to disable it.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled);
/**
* Enable the ADC's randomizer feature.
* @param dev SDDC device.
* @param enabled True to enable randomization, false to disable it.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled);
/**
* Set the LO of the tuner.
* @param dev SDDC device.
* @param frequency Frequency of the LO.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency);
/**
* Start the device.
* @param dev SDDC device.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_start(sddc_dev_t* dev);
/**
* Stop the device.
* @param dev SDDC device.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_stop(sddc_dev_t* dev);
/**
* Receive samples.
* @param dev SDDC device.
* @param samples Buffer to write the samples to.
* @param count Number of samples to read.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count);
// Handle inclusion from C++ code
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -0,0 +1,116 @@
#include "fx3_boot.h"
#include <stdio.h>
#define FX3_TIMEOUT 1000
#define FX3_VENDOR_REQUEST 0xA0
#define FX3_MAX_BLOCK_SIZE 0x1000
int sddc_fx3_boot_mem_read(libusb_device_handle* dev, uint32_t addr, uint8_t* data, uint16_t len) {
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, addr & 0xFFFF, addr >> 16, data, len, FX3_TIMEOUT);
}
int sddc_fx3_boot_mem_write(libusb_device_handle* dev, uint32_t addr, const uint8_t* data, uint16_t len) {
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, addr & 0xFFFF, addr >> 16, data, len, FX3_TIMEOUT);
}
int sddc_fx3_boot_run(libusb_device_handle* dev, uint32_t entry) {
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, entry & 0xFFFF, entry >> 16, NULL, 0, FX3_TIMEOUT);
}
int sddc_fx3_boot_upload_firmware(libusb_device_handle* dev, const char* path) {
// Open the firmware image
FILE* fw = fopen(path, "rb");
if (!fw) {
fprintf(stderr, "Failed to open firmware image\n");
return LIBUSB_ERROR_OTHER;
}
// Read the signature
char sign[2];
int read = fread(sign, 2, 1, fw);
if (read != 1) {
fprintf(stderr, "Failed to read firmware image signature: %d\n", read);
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Check the signature
if (sign[0] != 'C' || sign[1] != 'Y') {
fprintf(stderr, "Firmware image has invalid signature\n");
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Skip useless metadata
int err = fseek(fw, 2, SEEK_CUR);
if (err) {
fprintf(stderr, "Invalid firmware image: %d\n", err);
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Preallocate data buffer
int bufferSize = 0x10000;
uint8_t* buffer = malloc(bufferSize);
// Read every section
while (1) {
// Read the section size
uint32_t sizeWords;
read = fread(&sizeWords, sizeof(uint32_t), 1, fw);
if (read != 1) {
fprintf(stderr, "Invalid firmware image section size\n");
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
uint32_t size = sizeWords << 2;
// Read the section address
uint32_t addr;
read = fread(&addr, sizeof(uint32_t), 1, fw);
if (read != 1) {
fprintf(stderr, "Invalid firmware image section address\n");
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// If the section is a termination section, run the code at the given address
if (!size) {
sddc_fx3_boot_run(dev, addr);
break;
}
// Re-allocate buffer if needed
if (size > bufferSize) {
bufferSize = size;
realloc(buffer, bufferSize);
}
// Read the section data
read = fread(buffer, 1, size, fw);
if (read != size) {
fprintf(stderr, "Failed to read section data: %d\n", read);
fclose(fw);
return LIBUSB_ERROR_OTHER;
}
// Upload it to the chip
for (int i = 0; i < size; i += FX3_MAX_BLOCK_SIZE) {
int left = size - i;
err = sddc_fx3_boot_mem_write(dev, addr + i, &buffer[i], (left > FX3_MAX_BLOCK_SIZE) ? FX3_MAX_BLOCK_SIZE : left);
if (err < LIBUSB_SUCCESS) {
fprintf(stderr, "Failed to write to device memory: %d\n", err);
fclose(fw);
return err;
}
}
}
// TODO: Checksum stuff and verification ideally
// Close the firmware image
fclose(fw);
// Return successfully
return LIBUSB_SUCCESS;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <libusb.h>
#include <stdint.h>
/**
* Read data from the device's memory.
* @param dev Device to read data from.
* @param addr Start address of the data in the device's memory.
* @param data Buffer to write the data into.
* @param len Number of bytes to read.
* @return libusb error code.
*/
int sddc_fx3_boot_mem_read(libusb_device_handle* dev, uint32_t addr, uint8_t* data, uint16_t len);
/**
* Write data to the device's memory.
* @param dev Device to write data to.
* @param addr Start address of the data in the device's memory.
* @param data Buffer to write the data into.
* @param len Number of bytes to read.
* @return libusb error code.
*/
int sddc_fx3_boot_mem_write(libusb_device_handle* dev, uint32_t addr, const uint8_t* data, uint16_t len);
/**
* Execute code on the device.
* @param dev Device to execute code on.
* @param entry Entry point of the code.
* @return libusb error code.
*/
int sddc_fx3_boot_run(libusb_device_handle* dev, uint32_t entry);
/**
* Parse, upload and execute a firmware image.
* @param dev Device to upload the firmware to.
* @param path Path to the firmware image.
* @return libusb error code.
*/
int sddc_fx3_boot_upload_firmware(libusb_device_handle* dev, const char* path);

View File

@ -0,0 +1,451 @@
#include <sddc.h>
#include <stdlib.h>
#include <stdint.h>
#include <libusb.h>
#include <stdio.h>
#include "usb_interface.h"
struct sddc_dev {
// USB handles
struct libusb_device_handle* openDev;
// Device info
sddc_devinfo_t info;
// Device state
bool running;
uint32_t samplerate;
uint64_t tunerFreq;
sddc_gpio_t gpioState;
};
struct libusb_context* ctx = NULL;
char* sddc_firmware_path = NULL;
bool sddc_is_init = false;
void sddc_init() {
// If already initialized, do nothing
if (sddc_is_init) { return; }
// If the firmware isn't already found, find it
if (!sddc_firmware_path) {
// TODO: Find the firmware
}
// Init libusb
libusb_init(&ctx);
}
const char* sddc_model_to_string(sddc_model_t model) {
switch (model) {
case SDDC_MODEL_BBRF103: return "BBRF103";
case SDDC_MODEL_HF103: return "HF103";
case SDDC_MODEL_RX888: return "RX888";
case SDDC_MODEL_RX888_MK2: return "RX888 MK2";
case SDDC_MODEL_RX999: return "RX999";
case SDDC_MODEL_RXLUCY: return "RXLUCY";
case SDDC_MODEL_RX888_MK3: return "RX888 MK3";
default: return "Unknown";
}
}
const char* sddc_error_to_string(sddc_error_t error) {
switch (error) {
case SDDC_ERROR_NOT_IMPLEMENTED: return "Not Implemented";
case SDDC_ERROR_FIRMWARE_UPLOAD_FAILED: return "Firmware Upload Failed";
case SDDC_ERROR_NOT_FOUND: return "Not Found";
case SDDC_ERROR_USB_ERROR: return "USB Error";
case SDDC_ERROR_TIMEOUT: return "Timeout";
case SDDC_SUCCESS: return "Success";
default: return "Unknown";
}
}
sddc_error_t sddc_set_firmware_path(const char* path) {
// Free the old path if it exists
if (sddc_firmware_path) { free(sddc_firmware_path); }
// Allocate the new path
sddc_firmware_path = malloc(strlen(path) + 1);
// Copy the new path
strcpy(sddc_firmware_path, path);
// TODO: Check if the file path exists
return SDDC_SUCCESS;
}
int sddc_get_device_list(sddc_devinfo_t** dev_list) {
// Initialize libsddc in case it isn't already
sddc_init();
// Get a list of USB devices
libusb_device** devices;
int devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
// Initialize all uninitialized devices
bool uninit = false;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If it's not an uninitialized device, go to next device
if (desc.idVendor != SDDC_UNINIT_VID || desc.idProduct != SDDC_UNINIT_PID) { continue; }
// Initialize the device
printf("Found uninitialized device, initializing...\n");
// TODO: Check that the firmware path is valid
sddc_error_t serr = sddc_init_device(dev, sddc_firmware_path);
if (serr != SDDC_SUCCESS) { continue; }
// Set the flag to wait the devices to start up
uninit = true;
}
// If some uninitialized devices were found
if (uninit) {
// Free the device list
libusb_free_device_list(devices, 1);
// Wait for the devices to show back up
#ifdef _WIN32
Sleep(SDDC_INIT_SEARCH_DELAY_MS);
#else
usleep(SDDC_INIT_SEARCH_DELAY_MS * 1000);
#endif
// Attempt to list devices again
devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
}
// Allocate the device list
*dev_list = malloc(devCount * sizeof(sddc_devinfo_t));
// Check each device
int found = 0;
libusb_device_handle* openDev;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If the device is not an SDDC device, go to next device
if (desc.idVendor != SDDC_VID || desc.idProduct != SDDC_PID) { continue; }
// Open the device
err = libusb_open(dev, &openDev);
if (err != LIBUSB_SUCCESS) {
fprintf(stderr, "Failed to open device: %d\n", err);
continue;
}
// Create entry
sddc_devinfo_t* info = &((*dev_list)[found]);
// Get the serial number
err = libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, info->serial, SDDC_SERIAL_MAX_LEN-1);
if (err < LIBUSB_SUCCESS) {
printf("Failed to get descriptor: %d\n", err);
libusb_close(openDev);
continue;
}
// Get the hardware info
sddc_hwinfo_t hwinfo;
err = sddc_fx3_get_info(openDev, &hwinfo, 0);
if (err < LIBUSB_SUCCESS) {
printf("Failed to get device info: %d\n", err);
libusb_close(openDev);
continue;
}
// Save the hardware info
info->model = (sddc_model_t)hwinfo.model;
info->firmwareMajor = hwinfo.firmwareConfigH;
info->firmwareMinor = hwinfo.firmwareConfigL;
// Close the device
libusb_close(openDev);
// Increment device counter
found++;
}
// Free the libusb device list
libusb_free_device_list(devices, 1);
// Return the number of devices found
return found;
}
void sddc_free_device_list(sddc_devinfo_t* dev_list) {
// Free the device list if it exists
if (dev_list) { free(dev_list); };
}
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev) {
// Initialize libsddc in case it isn't already
sddc_init();
// Get a list of USB devices
libusb_device** devices;
int devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
// Initialize all uninitialized devices
bool uninit = false;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If it's not an uninitialized device, go to next device
if (desc.idVendor != SDDC_UNINIT_VID || desc.idProduct != SDDC_UNINIT_PID) { continue; }
// Initialize the device
printf("Found uninitialized device, initializing...\n");
// TODO: Check that the firmware path is valid
sddc_error_t serr = sddc_init_device(dev, sddc_firmware_path);
if (serr != SDDC_SUCCESS) { continue; }
// Set the flag to wait the devices to start up
uninit = true;
}
// If some uninitialized devices were found
if (uninit) {
// Free the device list
libusb_free_device_list(devices, 1);
// Wait for the devices to show back up
#ifdef _WIN32
Sleep(SDDC_INIT_SEARCH_DELAY_MS);
#else
usleep(SDDC_INIT_SEARCH_DELAY_MS * 1000);
#endif
// Attempt to list devices again
devCount = libusb_get_device_list(ctx, &devices);
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
}
// Search through all USB device
bool found = false;
libusb_device_handle* openDev;
for (int i = 0; i < devCount; i++) {
// Get the device from the list
libusb_device* dev = devices[i];
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
struct libusb_device_descriptor desc;
int err = libusb_get_device_descriptor(dev, &desc);
if (err != LIBUSB_SUCCESS) { continue; }
// If the device is not an SDDC device, go to next device
if (desc.idVendor != SDDC_VID || desc.idProduct != SDDC_PID) { continue; }
// Open the device
err = libusb_open(dev, &openDev);
if (err != LIBUSB_SUCCESS) {
fprintf(stderr, "Failed to open device: %d\n", err);
continue;
}
// Get the serial number
char dserial[SDDC_SERIAL_MAX_LEN];
err = libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, dserial, SDDC_SERIAL_MAX_LEN-1);
if (err < LIBUSB_SUCCESS) {
printf("Failed to get descriptor: %d\n", err);
libusb_close(openDev);
continue;
}
// Compare the serial number and give up if not a match
if (strcmp(dserial, serial)) { continue; }
// Get the device info
// TODO
// Set the found flag and stop searching
found = true;
break;
}
// Free the libusb device list
libusb_free_device_list(devices, true);
// If the device was not found, give up
if (!found) { return SDDC_ERROR_NOT_FOUND; }
// Claim the interface
libusb_claim_interface(openDev, 0);
// Allocate the device object
*dev = malloc(sizeof(sddc_dev_t));
// Initialize the device object
(*dev)->openDev = openDev;
//(*dev)->info = ; //TODO
(*dev)->running = false;
(*dev)->samplerate = 128e6;
(*dev)->tunerFreq = 100e6;
(*dev)->gpioState = SDDC_GPIO_SHUTDOWN | SDDC_GPIO_SEL0; // ADC shutdown and HF port selected
// Stop everything in case the device is partially started
printf("Stopping...\n");
sddc_stop(*dev);
// TODO: Setup all of the other state
sddc_gpio_put(*dev, SDDC_GPIO_SEL0, false);
sddc_gpio_put(*dev, SDDC_GPIO_SEL1, true);
sddc_gpio_put(*dev, SDDC_GPIO_VHF_EN, true);
sddc_tuner_start((*dev)->openDev, 16e6);
sddc_tuner_tune((*dev)->openDev, 100e6);
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_R82XX_ATT, 15);
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_R83XX_VGA, 9);
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_AD8340_VGA, 5);
return SDDC_SUCCESS;
}
void sddc_close(sddc_dev_t* dev) {
// Stop everything
sddc_stop(dev);
// Release the interface
libusb_release_interface(dev->openDev, 0);
// Close the USB device
libusb_close(dev->openDev);
// Free the device struct
free(dev);
}
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev) {
// All devices have the same samplerate range
sddc_range_t range = { 8e6, 128e6, 0 };
return range;
}
int sddc_gpio_set(sddc_dev_t* dev, sddc_gpio_t gpios) {
// Update the state
dev->gpioState = gpios;
// Push to the device
sddc_fx3_gpio(dev->openDev, gpios);
}
int sddc_gpio_put(sddc_dev_t* dev, sddc_gpio_t gpios, bool value) {
// Update the state of the given GPIOs only
return sddc_gpio_set(dev, (dev->gpioState & (~gpios)) | (value ? gpios : 0));
}
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate) {
// Update the state
dev->samplerate = samplerate;
// If running, send the new sampling rate to the device
if (dev->running) {
int err = sddc_adc_set_samplerate(dev->openDev, samplerate);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
}
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled) {
// Update the GPIOs according to the desired state
int err = sddc_gpio_put(dev, SDDC_GPIO_DITHER, enabled);
return (err < LIBUSB_SUCCESS) ? SDDC_ERROR_USB_ERROR : SDDC_SUCCESS;
}
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled) {
// Update the GPIOs according to the desired state
int err = sddc_gpio_put(dev, SDDC_GPIO_RANDOM, enabled);
return (err < LIBUSB_SUCCESS) ? SDDC_ERROR_USB_ERROR : SDDC_SUCCESS;
}
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency) {
// Update the state
dev->tunerFreq = frequency;
// If running, send the new frequency to the device
if (dev->running) {
int err = sddc_tuner_tune(dev->openDev, frequency);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
}
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_start(sddc_dev_t* dev) {
// De-assert the shutdown pin
int err = sddc_gpio_put(dev, SDDC_GPIO_SHUTDOWN, false);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Start the tuner (TODO: Check if in VHF mode)
// Start the ADC
err = sddc_adc_set_samplerate(dev->openDev, dev->samplerate);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Start the FX3
err = sddc_fx3_start(dev->openDev);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Update the state
dev->running = true;
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_stop(sddc_dev_t* dev) {
// Stop the FX3
int err = sddc_fx3_stop(dev->openDev);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Stop the tuner
err = sddc_tuner_stop(dev->openDev);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Stop the ADC
err = sddc_adc_set_samplerate(dev->openDev, 0);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Set the GPIOs for standby mode
err = sddc_gpio_put(dev, SDDC_GPIO_SHUTDOWN, true);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
// Update the state
dev->running = false;
// Return successfully
return SDDC_SUCCESS;
}
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count) {
// Read samples from the device
int bytesRead = 0;
int err = libusb_bulk_transfer(dev->openDev, LIBUSB_ENDPOINT_IN | 1, samples, count * sizeof(int16_t), &bytesRead, SDDC_TIMEOUT_MS);
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
return SDDC_SUCCESS;
}

View File

@ -0,0 +1,92 @@
#include "usb_interface.h"
#include "fx3_boot.h"
#include <stdio.h>
sddc_error_t sddc_init_device(libusb_device* dev, const char* firmware) {
// Open the device
libusb_device_handle* openDev;
int err = libusb_open(dev, &openDev);
if (err != LIBUSB_SUCCESS) {
printf("Failed to open device: %d\n", err);
return SDDC_ERROR_USB_ERROR;
}
// Attempt to upload the firmware
err = sddc_fx3_boot_upload_firmware(openDev, firmware);
if (err) {
fprintf(stderr, "Failed to upload firmware to uninitialized device\n");
return SDDC_ERROR_FIRMWARE_UPLOAD_FAILED;
}
// Close the device
libusb_close(openDev);
// Return successfully
return SDDC_SUCCESS;
}
int sddc_fx3_start(libusb_device_handle* dev) {
// Send the start command
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_START, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_stop(libusb_device_handle* dev) {
// Send the stop command
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_STOP, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_get_info(libusb_device_handle* dev, sddc_hwinfo_t* info, char enableDebug) {
// Fetch the info data
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_GET_INFO, enableDebug, 0, info, sizeof(sddc_hwinfo_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_gpio(libusb_device_handle* dev, sddc_gpio_t gpio) {
// Send the GPIO state
uint32_t dword = gpio;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_GPIO, 0, 0, &dword, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len) {
// Send the I2C data
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_I2C_W, addr, reg, data, len, SDDC_TIMEOUT_MS);
}
int sddc_fx3_i2c_read(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len) {
// Fetch the I2C data
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_I2C_R, addr, reg, data, len, SDDC_TIMEOUT_MS);
}
int sddc_fx3_reset(libusb_device_handle* dev) {
// Send the reset command
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_RESET, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_fx3_set_param(libusb_device_handle* dev, sddc_param_t param, uint16_t value) {
// Send the parameter
uint32_t dummy;
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_SET_PARAM, value, param, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_adc_set_samplerate(libusb_device_handle* dev, uint32_t samplerate) {
// Send the samplerate
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_ADC_SET_RATE, 0, 0, &samplerate, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_tuner_start(libusb_device_handle* dev, uint32_t refFreq) {
// Send the reset command
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_START, 0, 0, &refFreq, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}
int sddc_tuner_tune(libusb_device_handle* dev, uint64_t frequency) {
// Send the reset command
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_TUNE, 0, 0, &frequency, sizeof(uint64_t), SDDC_TIMEOUT_MS);
}
int sddc_tuner_stop(libusb_device_handle* dev) {
// Send the reset command
uint32_t dummy; // Because the shitty firmware absolute wants data to stop the tuner, this is dumb...
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_STOP, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
}

View File

@ -0,0 +1,168 @@
#pragma once
#include <sddc.h>
#include <stdint.h>
#include <libusb.h>
#define SDDC_UNINIT_VID 0x04B4
#define SDDC_UNINIT_PID 0x00F3
#define SDDC_VID 0x04B4
#define SDDC_PID 0x00F1
#define SDDC_INIT_SEARCH_DELAY_MS 1000
#define SDDC_TIMEOUT_MS 1000
enum sddc_command {
SDDC_CMD_FX3_START = 0xAA,
SDDC_CMD_FX3_STOP = 0xAB,
SDDC_CMD_FX3_GET_INFO = 0xAC,
SDDC_CMD_FX3_GPIO = 0xAD,
SDDC_CMD_FX3_I2C_W = 0xAE,
SDDC_CMD_FX3_I2C_R = 0xAF,
SDDC_CMD_FX3_RESET = 0xB1,
SDDC_CMD_FX3_SET_PARAM = 0xB6,
SDDC_CMD_ADC_SET_RATE = 0xB2,
SDDC_CMD_TUNER_START = 0xB4,
SDDC_CMD_TUNER_TUNE = 0xB5,
SDDC_CMD_TUNER_STOP = 0xB8,
SDDC_CMD_READ_DEBUG = 0xBA
};
#pragma pack(push, 1)
struct sddc_hwinfo {
uint8_t model;
uint8_t firmwareConfigH;
uint8_t firmwareConfigL;
uint8_t vendorRequestCount;
};
typedef struct sddc_hwinfo sddc_hwinfo_t;
#pragma pack(pop)
enum sddc_gpio {
SDDC_GPIO_NONE = 0,
SDDC_GPIO_ATT_LE = (1 << 0),
SDDC_GPIO_ATT_CLK = (1 << 1),
SDDC_GPIO_ATT_DATA = (1 << 2),
SDDC_GPIO_SEL0 = (1 << 3),
SDDC_GPIO_SEL1 = (1 << 4),
SDDC_GPIO_SHUTDOWN = (1 << 5),
SDDC_GPIO_DITHER = (1 << 6),
SDDC_GPIO_RANDOM = (1 << 7),
SDDC_GPIO_BIAST_HF = (1 << 8),
SDDC_GPIO_BIAST_VHF = (1 << 9),
SDDC_GPIO_LED_YELLOW = (1 << 10),
SDDC_GPIO_LED_RED = (1 << 11),
SDDC_GPIO_LED_BLUE = (1 << 12),
SDDC_GPIO_ATT_SEL0 = (1 << 13),
SDDC_GPIO_ATT_SEL1 = (1 << 14),
SDDC_GPIO_VHF_EN = (1 << 15),
SDDC_GPIO_PGA_EN = (1 << 16),
SDDC_GPIO_ALL = ((1 << 17)-1)
};
typedef enum sddc_gpio sddc_gpio_t;
enum sddc_param {
SDDC_PARAM_R82XX_ATT = 1,
SDDC_PARAM_R83XX_VGA = 2,
SDDC_PARAM_R83XX_SIDEBAND = 3,
SDDC_PARAM_R83XX_HARMONIC = 4,
SDDC_PARAM_DAT31_ATT = 10,
SDDC_PARAM_AD8340_VGA = 11,
SDDC_PARAM_PRESELECTOR = 12,
SDDC_PARAM_VHF_ATT = 13
};
typedef enum sddc_param sddc_param_t;
/**
* Initialize a device with a given firmware.
* @param dev SDDC USB device entry to initialize.
* @param firmware Path to the firmware image.
*/
sddc_error_t sddc_init_device(libusb_device* dev, const char* firmware);
/**
* Start the FX3 streaming.
* @param dev SDDC USB device.
*/
int sddc_fx3_start(libusb_device_handle* dev);
/**
* Stop the FX3 streaming.
* @param dev SDDC USB device.
*/
int sddc_fx3_stop(libusb_device_handle* dev);
/**
* Get the hardware info of a device.
* @param dev SDDC USB device.
* @param info Hardware info struct to write the return info into.
* @param enableDebug 1 to enable hardware debug mode, 0 otherwise.
*/
int sddc_fx3_get_info(libusb_device_handle* dev, sddc_hwinfo_t* info, char enableDebug);
/**
* Set the state of the GPIO pins.
* @param dev SDDC USB device.
* @param gpio State of the GPIO pins.
*/
int sddc_fx3_gpio(libusb_device_handle* dev, sddc_gpio_t gpio);
/**
* Write data to the I2C port.
* @param dev SDDC USB device.
* @param addr Address of the target I2C device.
* @param reg Register to write data to.
* @param data Data buffer to write.
* @param len Number of bytes to write.
*/
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len);
/**
* Read data from the I2C port.
* @param dev SDDC USB device.
* @param addr Address of the target I2C device.
* @param reg Register to read data from.
* @param data Data buffer to read data into.
* @param len Number of bytes to read.
*/
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len);
/**
* Reset the FX3 into bootloader mode.
* @param dev SDDC USB device.
*/
int sddc_fx3_reset(libusb_device_handle* dev);
/**
* Set a device parameter.
* @param dev SDDC USB device.
* @param arg Parameter to set.
* @param value Value to set the parameter to.
*/
int sddc_fx3_set_param(libusb_device_handle* dev, sddc_param_t param, uint16_t value);
/**
* Set the ADC sampling rate.
* @param dev SDDC USB device.
* @param samplerate Sampling rate. 0 to stop the ADC.
*/
int sddc_adc_set_samplerate(libusb_device_handle* dev, uint32_t samplerate);
/**
* Start the tuner.
* @param dev SDDC USB device.
* @param frequency Initial LO frequency.
*/
int sddc_tuner_start(libusb_device_handle* dev, uint32_t refFreq);
/**
* Set the tuner's LO frequency.
* @param dev SDDC USB device.
* @param frequency New LO frequency.
*/
int sddc_tuner_tune(libusb_device_handle* dev, uint64_t frequency);
/**
* Stop the tuner.
* @param dev SDDC USB device.
*/
int sddc_tuner_stop(libusb_device_handle* dev);

View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.13)
project(sddc_info)
# Get source files
file(GLOB_RECURSE SRC "src/*.cpp")
# Create executable
add_executable(${PROJECT_NAME} ${SRC})
# Link to librfnm
target_link_libraries(${PROJECT_NAME} PRIVATE sddc)
# Create install directive
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@ -0,0 +1,35 @@
#include <stdio.h>
#include <sddc.h>
int main() {
// Set firmware image path for debugging
sddc_set_firmware_path("C:/Users/ryzerth/Downloads/SDDC_FX3 (1).img");
// List available devices
sddc_devinfo_t* devList;
int count = sddc_get_device_list(&devList);
if (count < 0) {
fprintf(stderr, "Failed to list devices: %d\n", count);
return -1;
}
else if (!count) {
printf("No device found.\n");
return 0;
}
// Show the devices in the list
for (int i = 0; i < count; i++) {
printf("Serial: %s\n", devList[i].serial);
printf("Hardware: %s\n", sddc_model_to_string(devList[i].model));
printf("Firmware: v%d.%d\n", devList[i].firmwareMajor, devList[i].firmwareMinor);
// Print separator if needed
if (i != count-1) {
printf("\n================================================\n\n");
}
}
// Free the device list and exit
sddc_free_device_list(devList);
return 0;
}

View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.13)
project(sddc_rx)
# Get source files
file(GLOB_RECURSE SRC "src/*.cpp")
# Create executable
add_executable(${PROJECT_NAME} ${SRC})
# Link to librfnm
target_link_libraries(${PROJECT_NAME} PRIVATE sddc)
# Create install directive
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})

View File

@ -0,0 +1,38 @@
#include <stdio.h>
#include <sddc.h>
int main() {
// Set firmware image path for debugging
sddc_set_firmware_path("C:/Users/ryzerth/Downloads/SDDC_FX3 (1).img");
// Open the device
sddc_dev_t* dev;
sddc_error_t err = sddc_open("0009072C00C40C32", &dev);
if (err != SDDC_SUCCESS) {
printf("Failed to open device: %s (%d)\n", sddc_error_to_string(err), err);
return -1;
}
// Configure the device
sddc_set_samplerate(dev, 8e6);
// Start the device
sddc_start(dev);
// Continuous read samples
const int bufSize = 1e6;
int16_t* buffer = new int16_t[bufSize];
while (true) {
// Read
sddc_error_t err = sddc_rx(dev, buffer, bufSize);
printf("Samples received: %d\n", err);
}
// Stop the device
sddc_stop(dev);
// Close the device
sddc_close(dev);
return 0;
}

View File

@ -0,0 +1,512 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include <atomic>
#include <sddc.h>
SDRPP_MOD_INFO{
/* Name: */ "sddc_source",
/* Description: */ "SDDC Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 2, 0,
/* Max instances */ -1
};
ConfigManager config;
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class SDDCSourceModule : public ModuleManager::Instance {
public:
SDDCSourceModule(std::string name) {
this->name = name;
// Set firmware image path for debugging
sddc_set_firmware_path("C:/Users/ryzerth/Downloads/SDDC_FX3 (1).img");
sampleRate = 128e6;
// Initialize the DDC
ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &ddc.out;
// Refresh devices
refresh();
// Select device from config
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
select(devSerial);
sigpath::sourceManager.registerSource("SDDC", &handler);
}
~SDDCSourceModule() {
// Nothing to do
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
enum Port {
PORT_RF,
PORT_HF1,
PORT_HF2
};
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);
}
void refresh() {
devices.clear();
// // Get device list
// sddc_devinfo_t* devList;
// int count = sddc_get_device_list(&devList);
// if (count < 0) {
// flog::error("Failed to list SDDC devices: {}", count);
// return;
// }
// // Add every device found
// for (int i = 0; i < count; i++) {
// // Create device name
// std::string name = sddc_model_to_string(devList[i].model);
// name += '[';
// name += devList[i].serial;
// name += ']';
// // Add an entry to the device list
// devices.define(devList[i].serial, name, devList[i].serial);
// }
devices.define("0009072C00C40C32", "TESTING", "0009072C00C40C32");
// // Free the device list
// sddc_free_device_list(devList);
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the ID in the list
int id = devices.keyId(serial);
// Open the device
sddc_dev_t* dev;
int err = sddc_open(serial.c_str(), &dev);
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Generate samplerate list
samplerates.clear();
samplerates.define(4e6, "4 MHz", 4e6);
samplerates.define(8e6, "8 MHz", 8e6);
samplerates.define(16e6, "16 MHz", 16e6);
samplerates.define(32e6, "32 MHz", 32e6);
samplerates.define(64e6, "64 MHz", 64e6);
// // Define the ports
// ports.clear();
// ports.define("hf", "HF", PORT_RF);
// ports.define("vhf", "VHF", PORT_HF1);
// Close the device
sddc_close(dev);
// Save serial number
selectedSerial = serial;
devId = id;
// Load default options
sampleRate = 64e6;
srId = samplerates.valueId(sampleRate);
// port = PORT_RF;
// portId = ports.valueId(port);
// lnaGain = 0;
// vgaGain = 0;
// Load config
config.acquire();
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
int desiredSr = config.conf["devices"][selectedSerial]["samplerate"];
if (samplerates.keyExists(desiredSr)) {
srId = samplerates.keyId(desiredSr);
sampleRate = samplerates[srId];
}
}
// if (config.conf["devices"][selectedSerial].contains("port")) {
// std::string desiredPort = config.conf["devices"][selectedSerial]["port"];
// if (ports.keyExists(desiredPort)) {
// portId = ports.keyId(desiredPort);
// port = ports[portId];
// }
// }
// if (config.conf["devices"][selectedSerial].contains("lnaGain")) {
// lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX);
// }
// if (config.conf["devices"][selectedSerial].contains("vgaGain")) {
// vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX);
// }
config.release();
// Update the samplerate
core::setInputSampleRate(sampleRate);
}
static void menuSelected(void* ctx) {
SDDCSourceModule* _this = (SDDCSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("SDDCSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
SDDCSourceModule* _this = (SDDCSourceModule*)ctx;
flog::info("SDDCSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
SDDCSourceModule* _this = (SDDCSourceModule*)ctx;
if (_this->running) { return; }
// Open the device
sddc_error_t err = sddc_open(_this->selectedSerial.c_str(), &_this->openDev);
if (err) {
flog::error("Failed to open device: {}", (int)err);
return;
}
// // Get the selected port
// _this->port = _this->ports[_this->portId];
// Configure the device
sddc_set_samplerate(_this->openDev, _this->sampleRate * 2);
// // Configure the DDC
// if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
// // Set the frequency
// fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
// }
// else if (_this->port == PORT_RF) {
// // Set the frequency
// fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
// // Configure and start the DDC for decimation only
// _this->ddc.setInSamplerate(actualSr);
// _this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
// _this->ddc.setOffset(0.0);
// _this->ddc.start();
// }
// else {
// Configure and start the DDC
_this->ddc.setInSamplerate(_this->sampleRate * 2);
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
_this->ddc.setOffset(_this->freq);
_this->ddc.start();
// }
// Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming)
_this->bufferSize = _this->sampleRate / 100.0;
// Start streaming
err = sddc_start(_this->openDev);
if (err) {
flog::error("Failed to start stream: {}", (int)err);
return;
}
// Start worker
_this->run = true;
_this->workerThread = std::thread(&SDDCSourceModule::worker, _this);
_this->running = true;
flog::info("SDDCSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
SDDCSourceModule* _this = (SDDCSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
_this->ddc.out.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->ddc.out.clearWriteStop();
}
else {
_this->ddcIn.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->ddcIn.clearWriteStop();
}
// Stop streaming
sddc_stop(_this->openDev);
// Stop the DDC
_this->ddc.stop();
// Close the device
sddc_close(_this->openDev);
flog::info("SDDCSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
SDDCSourceModule* _this = (SDDCSourceModule*)ctx;
if (_this->running) {
// if (_this->port == PORT_RF) {
// double actual; // Dummy, don't care
// //fobos_rx_set_frequency(_this->openDev, freq, &actual);
// }
// else {
_this->ddc.setOffset(freq);
// }
}
_this->freq = freq;
flog::info("SDDCSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
SDDCSourceModule* _this = (SDDCSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_sddc_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
config.acquire();
config.conf["device"] = _this->selectedSerial;
config.release(true);
}
if (SmGui::Combo(CONCAT("##_sddc_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
if (!_this->selectedSerial.empty()) {
config.acquire();
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_sddc_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
// SmGui::LeftLabel("Antenna Port");
// SmGui::FillWidth();
// if (SmGui::Combo(CONCAT("##_sddc_port_", _this->name), &_this->portId, _this->ports.txt)) {
// if (!_this->selectedSerial.empty()) {
// config.acquire();
// config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId);
// config.release(true);
// }
// }
if (_this->running) { SmGui::EndDisabled(); }
// if (_this->port == PORT_RF) {
// SmGui::LeftLabel("LNA Gain");
// SmGui::FillWidth();
// if (SmGui::SliderInt(CONCAT("##_sddc_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) {
// if (_this->running) {
// fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
// }
// if (!_this->selectedSerial.empty()) {
// config.acquire();
// config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain;
// config.release(true);
// }
// }
// SmGui::LeftLabel("VGA Gain");
// SmGui::FillWidth();
// if (SmGui::SliderInt(CONCAT("##_sddc_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) {
// if (_this->running) {
// fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
// }
// if (!_this->selectedSerial.empty()) {
// config.acquire();
// config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain;
// config.release(true);
// }
// }
// }
}
void worker() {
// // Select different processing depending on the mode
// if (port == PORT_RF && sampleRate >= 50e6) {
// while (run) {
// // Read samples
// unsigned int sampCount = 0;
// int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount);
// if (err) { break; }
// // Send out samples to the core
// if (!ddc.out.swap(sampCount)) { break; }
// }
// }
// else if (port == PORT_RF) {
// while (run) {
// // Read samples
// unsigned int sampCount = 0;
// int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
// if (err) { break; }
// // Send samples to the DDC
// if (!ddcIn.swap(sampCount)) { break; }
// }
// }
// else if (port == PORT_HF1) {
// while (run) {
// // Read samples
// unsigned int sampCount = 0;
// int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
// if (err) { break; }
// // Null out the HF2 samples
// for (int i = 0; i < sampCount; i++) {
// ddcIn.writeBuf[i].im = 0.0f;
// }
// // Send samples to the DDC
// if (!ddcIn.swap(sampCount)) { break; }
// }
// }
// else if (port == PORT_HF2) {
// Allocate the sample buffer
int16_t* buffer = dsp::buffer::alloc<int16_t>(bufferSize);
float* fbuffer = dsp::buffer::alloc<float>(bufferSize);
float* nullBuffer = dsp::buffer::alloc<float>(bufferSize);
// Clear the null buffer
dsp::buffer::clear(nullBuffer, bufferSize);
while (run) {
// Read samples
int err = sddc_rx(openDev, buffer, bufferSize);
if (err) { break; }
// Convert the samples to float
volk_16i_s32f_convert_32f(fbuffer, buffer, 32768.0f, bufferSize);
// Interleave into a complex value
volk_32f_x2_interleave_32fc((lv_32fc_t*)ddcIn.writeBuf, fbuffer, nullBuffer, bufferSize);
// Send samples to the DDC
if (!ddcIn.swap(bufferSize)) { break; }
}
// Free the buffer
dsp::buffer::free(buffer);
// }
}
std::string name;
bool enabled = true;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, std::string> devices;
OptionList<int, int> samplerates;
OptionList<std::string, Port> ports;
int devId = 0;
int srId = 0;
int portId = 0;
Port port;
int lnaGain = 0;
int vgaGain = 0;
std::string selectedSerial;
sddc_dev_t* openDev;
int bufferSize;
std::thread workerThread;
std::atomic<bool> run = false;
dsp::stream<dsp::complex_t> ddcIn;
dsp::channel::RxVFO ddc;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(core::args["root"].s() + "/sddc_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new SDDCSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (SDDCSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}