diff --git a/decoder_modules/atv_decoder/src/amplitude.h b/decoder_modules/atv_decoder/src/amplitude.h new file mode 100644 index 00000000..8acb5c23 --- /dev/null +++ b/decoder_modules/atv_decoder/src/amplitude.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include + +namespace dsp::demod { + class Amplitude : public Processor { + using base_type = Processor; + public: + Amplitude() {} + + Amplitude(stream* in, double deviation) { init(in, deviation); } + + Amplitude(stream* in, double deviation, double samplerate) { init(in, deviation, samplerate); } + + + virtual void init(stream* in, double deviation) { + _invDeviation = 1.0 / deviation; + base_type::init(in); + } + + virtual void init(stream* in, double deviation, double samplerate) { + init(in, math::hzToRads(deviation, samplerate)); + } + + void setDeviation(double deviation) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + _invDeviation = 1.0 / deviation; + } + + void setDeviation(double deviation, double samplerate) { + assert(base_type::_block_init); + std::lock_guard 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 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; + }; +} \ No newline at end of file diff --git a/decoder_modules/atv_decoder/src/burst_blanking.txt b/decoder_modules/atv_decoder/src/burst_blanking.txt new file mode 100644 index 00000000..37cc8a7a --- /dev/null +++ b/decoder_modules/atv_decoder/src/burst_blanking.txt @@ -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 \ No newline at end of file diff --git a/decoder_modules/atv_decoder/src/chroma_pll.h b/decoder_modules/atv_decoder/src/chroma_pll.h deleted file mode 100644 index ae8b9942..00000000 --- a/decoder_modules/atv_decoder/src/chroma_pll.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include -#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* 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; - } - }; -} \ No newline at end of file diff --git a/decoder_modules/atv_decoder/src/chrominance_filter.h b/decoder_modules/atv_decoder/src/chrominance_filter.h deleted file mode 100644 index 07b303a7..00000000 --- a/decoder_modules/atv_decoder/src/chrominance_filter.h +++ /dev/null @@ -1,239 +0,0 @@ -#pragma once -#include - -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) \ No newline at end of file diff --git a/decoder_modules/atv_decoder/src/filters.h b/decoder_modules/atv_decoder/src/filters.h new file mode 100644 index 00000000..f1405d19 --- /dev/null +++ b/decoder_modules/atv_decoder/src/filters.h @@ -0,0 +1,131 @@ +#pragma once +#include + +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) \ No newline at end of file diff --git a/decoder_modules/atv_decoder/src/linesync.h b/decoder_modules/atv_decoder/src/linesync.h index 025365dc..329a7748 100644 --- a/decoder_modules/atv_decoder/src/linesync.h +++ b/decoder_modules/atv_decoder/src/linesync.h @@ -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 { using base_type = dsp::Processor; public: diff --git a/decoder_modules/atv_decoder/src/main.cpp b/decoder_modules/atv_decoder/src/main.cpp index dc9ac673..60a2a7dd 100644 --- a/decoder_modules/atv_decoder/src/main.cpp +++ b/decoder_modules/atv_decoder/src/main.cpp @@ -16,15 +16,14 @@ #include #include -#include "chrominance_filter.h" - -#include "chroma_pll.h" - -#include "linesync_old.h" #include "amplitude.h" #include #include +#include "filters.h" +#include +#include + #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(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(fabsf(buf2[i-155+COLORBURST_START].re*5.0f) * 255.0f, 0, 255); + int imval2 = std::clamp(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(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; diff --git a/misc_modules/scanner/src/main.cpp b/misc_modules/scanner/src/main.cpp index 4694b960..c73ecfba 100644 --- a/misc_modules/scanner/src/main.cpp +++ b/misc_modules/scanner/src/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include SDRPP_MOD_INFO{ /* Name: */ "scanner", diff --git a/source_modules/sddc_source/CMakeLists.txt b/source_modules/sddc_source/CMakeLists.txt new file mode 100644 index 00000000..762bbee4 --- /dev/null +++ b/source_modules/sddc_source/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/.gitignore b/source_modules/sddc_source/libsddc/.gitignore new file mode 100644 index 00000000..01f9cb9f --- /dev/null +++ b/source_modules/sddc_source/libsddc/.gitignore @@ -0,0 +1,2 @@ +build/ +.vscode/ \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/CMakeLists.txt b/source_modules/sddc_source/libsddc/CMakeLists.txt new file mode 100644 index 00000000..d91bccd5 --- /dev/null +++ b/source_modules/sddc_source/libsddc/CMakeLists.txt @@ -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 $/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) \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/cmake/uninstall.cmake b/source_modules/sddc_source/libsddc/cmake/uninstall.cmake new file mode 100644 index 00000000..39f6c6a6 --- /dev/null +++ b/source_modules/sddc_source/libsddc/cmake/uninstall.cmake @@ -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) \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/include/sddc.h b/source_modules/sddc_source/libsddc/include/sddc.h new file mode 100644 index 00000000..ca2acce4 --- /dev/null +++ b/source_modules/sddc_source/libsddc/include/sddc.h @@ -0,0 +1,177 @@ +#pragma once +#include +#include + +// 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 \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/license b/source_modules/sddc_source/libsddc/license new file mode 100644 index 00000000..153d416d --- /dev/null +++ b/source_modules/sddc_source/libsddc/license @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/src/fx3_boot.c b/source_modules/sddc_source/libsddc/src/fx3_boot.c new file mode 100644 index 00000000..c86006d9 --- /dev/null +++ b/source_modules/sddc_source/libsddc/src/fx3_boot.c @@ -0,0 +1,116 @@ +#include "fx3_boot.h" +#include + +#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; +} \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/src/fx3_boot.h b/source_modules/sddc_source/libsddc/src/fx3_boot.h new file mode 100644 index 00000000..c597fc16 --- /dev/null +++ b/source_modules/sddc_source/libsddc/src/fx3_boot.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include + +/** + * 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); \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/src/sddc.c b/source_modules/sddc_source/libsddc/src/sddc.c new file mode 100644 index 00000000..820aa1d5 --- /dev/null +++ b/source_modules/sddc_source/libsddc/src/sddc.c @@ -0,0 +1,451 @@ +#include +#include +#include +#include +#include +#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; +} \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/src/usb_interface.c b/source_modules/sddc_source/libsddc/src/usb_interface.c new file mode 100644 index 00000000..6dc17f6b --- /dev/null +++ b/source_modules/sddc_source/libsddc/src/usb_interface.c @@ -0,0 +1,92 @@ +#include "usb_interface.h" +#include "fx3_boot.h" +#include + +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); +} \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/src/usb_interface.h b/source_modules/sddc_source/libsddc/src/usb_interface.h new file mode 100644 index 00000000..b36c71dc --- /dev/null +++ b/source_modules/sddc_source/libsddc/src/usb_interface.h @@ -0,0 +1,168 @@ +#pragma once +#include +#include +#include + +#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); \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/utils/sddc_info/CMakeLists.txt b/source_modules/sddc_source/libsddc/utils/sddc_info/CMakeLists.txt new file mode 100644 index 00000000..9d1a211d --- /dev/null +++ b/source_modules/sddc_source/libsddc/utils/sddc_info/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/utils/sddc_info/src/main.cpp b/source_modules/sddc_source/libsddc/utils/sddc_info/src/main.cpp new file mode 100644 index 00000000..a74bd643 --- /dev/null +++ b/source_modules/sddc_source/libsddc/utils/sddc_info/src/main.cpp @@ -0,0 +1,35 @@ +#include +#include + +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; +} \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/utils/sddc_rx/CMakeLists.txt b/source_modules/sddc_source/libsddc/utils/sddc_rx/CMakeLists.txt new file mode 100644 index 00000000..6326654e --- /dev/null +++ b/source_modules/sddc_source/libsddc/utils/sddc_rx/CMakeLists.txt @@ -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}) \ No newline at end of file diff --git a/source_modules/sddc_source/libsddc/utils/sddc_rx/src/main.cpp b/source_modules/sddc_source/libsddc/utils/sddc_rx/src/main.cpp new file mode 100644 index 00000000..28096b11 --- /dev/null +++ b/source_modules/sddc_source/libsddc/utils/sddc_rx/src/main.cpp @@ -0,0 +1,38 @@ +#include +#include + +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; +} \ No newline at end of file diff --git a/source_modules/sddc_source/src/main.cpp b/source_modules/sddc_source/src/main.cpp new file mode 100644 index 00000000..c0ddf376 --- /dev/null +++ b/source_modules/sddc_source/src/main.cpp @@ -0,0 +1,512 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX); + // } + // if (config.conf["devices"][selectedSerial].contains("vgaGain")) { + // vgaGain = std::clamp(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(bufferSize); + float* fbuffer = dsp::buffer::alloc(bufferSize); + float* nullBuffer = dsp::buffer::alloc(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 devices; + OptionList samplerates; + OptionList 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 run = false; + + dsp::stream 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(); +} \ No newline at end of file