From 2432390600c3d04f6f15fc332399e7eed8baa938 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 13 Dec 2023 23:25:18 +0100 Subject: [PATCH 01/65] Completely redid the RDS demod --- core/src/dsp/demod/broadcast_fm.h | 50 ++-- core/src/dsp/loop/phase_control_loop.h | 5 + core/src/utils/riff.cpp | 8 + core/src/utils/riff.h | 23 ++ decoder_modules/atv_decoder/src/chroma_pll.h | 63 +++++ .../atv_decoder/src/chrominance_filter.h | 239 ++++++++++++++++++ decoder_modules/atv_decoder/src/linesync.h | 193 ++++++++++++++ decoder_modules/atv_decoder/src/main.cpp | 181 ++++++++----- decoder_modules/radio/src/demodulators/wfm.h | 183 ++++++++++++-- decoder_modules/radio/src/rds.cpp | 83 ++++-- decoder_modules/radio/src/rds.h | 153 +++++++++-- source_modules/bladerf_source/src/main.cpp | 2 +- 12 files changed, 1027 insertions(+), 156 deletions(-) create mode 100644 decoder_modules/atv_decoder/src/chroma_pll.h create mode 100644 decoder_modules/atv_decoder/src/chrominance_filter.h create mode 100644 decoder_modules/atv_decoder/src/linesync.h diff --git a/core/src/dsp/demod/broadcast_fm.h b/core/src/dsp/demod/broadcast_fm.h index 7e995e9d..45312f30 100644 --- a/core/src/dsp/demod/broadcast_fm.h +++ b/core/src/dsp/demod/broadcast_fm.h @@ -49,6 +49,7 @@ namespace dsp::demod { audioFirTaps = taps::lowPass(15000.0, 4000.0, _samplerate); alFir.init(NULL, audioFirTaps); arFir.init(NULL, audioFirTaps); + xlator.init(NULL, -57000.0, samplerate); rdsResamp.init(NULL, samplerate, 5000.0); lmr = buffer::alloc(STREAM_BUFFER_SIZE); @@ -56,9 +57,9 @@ namespace dsp::demod { r = buffer::alloc(STREAM_BUFFER_SIZE); lprDelay.out.free(); - lmrDelay.out.free(); arFir.out.free(); alFir.out.free(); + xlator.out.free(); rdsResamp.out.free(); base_type::init(in); @@ -92,6 +93,7 @@ namespace dsp::demod { alFir.setTaps(audioFirTaps); arFir.setTaps(audioFirTaps); + xlator.setOffset(-57000.0, samplerate); rdsResamp.setInSamplerate(samplerate); reset(); @@ -139,7 +141,7 @@ namespace dsp::demod { base_type::tempStart(); } - inline int process(int count, complex_t* in, stereo_t* out, int& rdsOutCount, float* rdsout = NULL) { + inline int process(int count, complex_t* in, stereo_t* out, int& rdsOutCount, complex_t* rdsout = NULL) { // Demodulate demod.process(count, in, demod.out.writeBuf); if (_stereo) { @@ -152,24 +154,24 @@ namespace dsp::demod { // Delay lprDelay.process(count, demod.out.writeBuf, demod.out.writeBuf); - lmrDelay.process(count, rtoc.out.writeBuf, rtoc.out.writeBuf); + lmrDelay.process(count, rtoc.out.writeBuf, lmrDelay.out.writeBuf); // conjugate PLL output to down convert twice the L-R signal math::Conjugate::process(count, pilotPLL.out.writeBuf, pilotPLL.out.writeBuf); - math::Multiply::process(count, rtoc.out.writeBuf, pilotPLL.out.writeBuf, rtoc.out.writeBuf); - math::Multiply::process(count, rtoc.out.writeBuf, pilotPLL.out.writeBuf, rtoc.out.writeBuf); + math::Multiply::process(count, lmrDelay.out.writeBuf, pilotPLL.out.writeBuf, lmrDelay.out.writeBuf); + math::Multiply::process(count, lmrDelay.out.writeBuf, pilotPLL.out.writeBuf, lmrDelay.out.writeBuf); // Do RDS demod if (_rdsOut) { - // Since the PLL output is no longer needed after this, use it as the output - math::Multiply::process(count, rtoc.out.writeBuf, pilotPLL.out.writeBuf, pilotPLL.out.writeBuf); - convert::ComplexToReal::process(count, pilotPLL.out.writeBuf, rdsout); - volk_32f_s32f_multiply_32f(rdsout, rdsout, 100.0, count); - rdsOutCount = rdsResamp.process(count, rdsout, rdsout); + // Translate to 0Hz + xlator.process(count, rtoc.out.writeBuf, rtoc.out.writeBuf); + + // Resample to the output samplerate + rdsOutCount = rdsResamp.process(count, rtoc.out.writeBuf, rdsout); } // Convert output back to real for further processing - convert::ComplexToReal::process(count, rtoc.out.writeBuf, lmr); + convert::ComplexToReal::process(count, lmrDelay.out.writeBuf, lmr); // Amplify by 2x volk_32f_s32f_multiply_32f(lmr, lmr, 2.0f, count); @@ -193,24 +195,11 @@ namespace dsp::demod { // Convert to complex rtoc.process(count, demod.out.writeBuf, rtoc.out.writeBuf); - // Filter out pilot and run through PLL - pilotFir.process(count, rtoc.out.writeBuf, pilotFir.out.writeBuf); - pilotPLL.process(count, pilotFir.out.writeBuf, pilotPLL.out.writeBuf); + // Translate to 0Hz + xlator.process(count, rtoc.out.writeBuf, rtoc.out.writeBuf); - // Delay - lprDelay.process(count, demod.out.writeBuf, demod.out.writeBuf); - lmrDelay.process(count, rtoc.out.writeBuf, rtoc.out.writeBuf); - - // conjugate PLL output to down convert twice the L-R signal - math::Conjugate::process(count, pilotPLL.out.writeBuf, pilotPLL.out.writeBuf); - math::Multiply::process(count, rtoc.out.writeBuf, pilotPLL.out.writeBuf, rtoc.out.writeBuf); - math::Multiply::process(count, rtoc.out.writeBuf, pilotPLL.out.writeBuf, rtoc.out.writeBuf); - - // Since the PLL output is no longer needed after this, use it as the output - math::Multiply::process(count, rtoc.out.writeBuf, pilotPLL.out.writeBuf, pilotPLL.out.writeBuf); - convert::ComplexToReal::process(count, pilotPLL.out.writeBuf, rdsout); - volk_32f_s32f_multiply_32f(rdsout, rdsout, 100.0, count); - rdsOutCount = rdsResamp.process(count, rdsout, rdsout); + // Resample to the output samplerate + rdsOutCount = rdsResamp.process(count, rtoc.out.writeBuf, rdsout); } // Filter if needed @@ -240,7 +229,7 @@ namespace dsp::demod { return count; } - stream rdsOut; + stream rdsOut; protected: double _deviation; @@ -253,13 +242,14 @@ namespace dsp::demod { tap pilotFirTaps; filter::FIR pilotFir; convert::RealToComplex rtoc; + channel::FrequencyXlator xlator; loop::PLL pilotPLL; math::Delay lprDelay; math::Delay lmrDelay; tap audioFirTaps; filter::FIR arFir; filter::FIR alFir; - multirate::RationalResampler rdsResamp; + multirate::RationalResampler rdsResamp; float* lmr; float* l; diff --git a/core/src/dsp/loop/phase_control_loop.h b/core/src/dsp/loop/phase_control_loop.h index f5999bb8..575da130 100644 --- a/core/src/dsp/loop/phase_control_loop.h +++ b/core/src/dsp/loop/phase_control_loop.h @@ -65,6 +65,11 @@ namespace dsp::loop { if constexpr(CLAMP_PHASE) { clampPhase(); } } + inline void advancePhase() { + phase += freq; + if constexpr(CLAMP_PHASE) { clampPhase(); } + } + T freq; T phase; diff --git a/core/src/utils/riff.cpp b/core/src/utils/riff.cpp index 055a7f9e..7487731b 100644 --- a/core/src/utils/riff.cpp +++ b/core/src/utils/riff.cpp @@ -7,6 +7,14 @@ namespace riff { const char* LIST_SIGNATURE = "LIST"; const size_t RIFF_LABEL_SIZE = 4; + // Writer::Writer(const Writer&& b) { + // //file = std::move(b.file); + // } + + Writer::~Writer() { + close(); + } + bool Writer::open(std::string path, const char form[4]) { std::lock_guard lck(mtx); diff --git a/core/src/utils/riff.h b/core/src/utils/riff.h index e47ccf03..5e946d8e 100644 --- a/core/src/utils/riff.h +++ b/core/src/utils/riff.h @@ -20,6 +20,10 @@ namespace riff { class Writer { public: + Writer() {} + // Writer(const Writer&& b); + ~Writer(); + bool open(std::string path, const char form[4]); bool isOpen(); void close(); @@ -40,4 +44,23 @@ namespace riff { std::ofstream file; std::stack chunks; }; + + // class Reader { + // public: + // Reader(); + // Reader(const Reader&& b); + // ~Reader(); + + // bool open(std::string path); + // bool isOpen(); + // void close(); + + // const std::string& form(); + + // private: + + // std::string _form; + // std::recursive_mutex mtx; + // std::ofstream file; + // }; } \ 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 new file mode 100644 index 00000000..ae8b9942 --- /dev/null +++ b/decoder_modules/atv_decoder/src/chroma_pll.h @@ -0,0 +1,63 @@ +#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 new file mode 100644 index 00000000..07b303a7 --- /dev/null +++ b/decoder_modules/atv_decoder/src/chrominance_filter.h @@ -0,0 +1,239 @@ +#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/linesync.h b/decoder_modules/atv_decoder/src/linesync.h new file mode 100644 index 00000000..99c1275c --- /dev/null +++ b/decoder_modules/atv_decoder/src/linesync.h @@ -0,0 +1,193 @@ +#pragma once +#include +#include +#include +#include +#include + +class LineSync : public dsp::Processor { + using base_type = dsp::Processor; +public: + LineSync() {} + + LineSync(dsp::stream* in, double omega, double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) { init(in, omega, omegaGain, muGain, omegaRelLimit, interpPhaseCount, interpTapCount); } + + ~LineSync() { + if (!base_type::_block_init) { return; } + base_type::stop(); + dsp::multirate::freePolyphaseBank(interpBank); + dsp::buffer::free(buffer); + } + + void init(dsp::stream* in, double omega, double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) { + _omega = omega; + _omegaGain = omegaGain; + _muGain = muGain; + _omegaRelLimit = omegaRelLimit; + _interpPhaseCount = interpPhaseCount; + _interpTapCount = interpTapCount; + + pcl.init(_muGain, _omegaGain, 0.0, 0.0, 1.0, _omega, _omega * (1.0 - omegaRelLimit), _omega * (1.0 + omegaRelLimit)); + generateInterpTaps(); + buffer = dsp::buffer::alloc(STREAM_BUFFER_SIZE + _interpTapCount); + bufStart = &buffer[_interpTapCount - 1]; + + base_type::init(in); + } + + void setOmegaGain(double omegaGain) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + _omegaGain = omegaGain; + pcl.setCoefficients(_muGain, _omegaGain); + } + + void setMuGain(double muGain) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + _muGain = muGain; + pcl.setCoefficients(_muGain, _omegaGain); + } + + void setOmegaRelLimit(double omegaRelLimit) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + _omegaRelLimit = omegaRelLimit; + pcl.setFreqLimits(_omega * (1.0 - _omegaRelLimit), _omega * (1.0 + _omegaRelLimit)); + } + + void setSyncLevel(float level) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + syncLevel = level; + } + + void setInterpParams(int interpPhaseCount, int interpTapCount) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + base_type::tempStop(); + _interpPhaseCount = interpPhaseCount; + _interpTapCount = interpTapCount; + dsp::multirate::freePolyphaseBank(interpBank); + dsp::buffer::free(buffer); + generateInterpTaps(); + buffer = dsp::buffer::alloc(STREAM_BUFFER_SIZE + _interpTapCount); + bufStart = &buffer[_interpTapCount - 1]; + base_type::tempStart(); + } + + void reset() { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + base_type::tempStop(); + offset = 0; + pcl.phase = 0.0f; + pcl.freq = _omega; + base_type::tempStart(); + } + + int run() { + int count = base_type::_in->read(); + if (count < 0) { return -1; } + + // Copy data to work buffer + memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float)); + + if (test2) { + test2 = false; + offset += 5; + } + + // Process all samples + while (offset < count) { + // Calculate new output value + int phase = std::clamp(floorf(pcl.phase * (float)_interpPhaseCount), 0, _interpPhaseCount - 1); + float outVal; + volk_32f_x2_dot_prod_32f(&outVal, &buffer[offset], interpBank.phases[phase], _interpTapCount); + base_type::out.writeBuf[outCount++] = outVal; + + // If the end of the line is reached, process it and determin error + float error = 0; + if (outCount >= 720) { + // Compute averages. + float left = 0.0f, right = 0.0f; + for (int i = (720-17); i < 720; i++) { + left += base_type::out.writeBuf[i]; + } + for (int i = 0; i < 27; i++) { + left += base_type::out.writeBuf[i]; + } + for (int i = 27; i < (54+17); i++) { + right += base_type::out.writeBuf[i]; + } + left *= (1.0f/44.0f); + right *= (1.0f/44.0f); + + // If the sync is present, compute error + if ((left < syncLevel && right < syncLevel) && !forceLock) { + error = (left + syncBias - right); + locked = true; + } + else { + locked = false; + } + + if (++counter >= 100) { + counter = 0; + //flog::warn("Left: {}, Right: {}, Error: {}, Freq: {}, Phase: {}", left, right, error, pcl.freq, pcl.phase); + } + + // Output line + if (!base_type::out.swap(outCount)) { break; } + outCount = 0; + } + + // Advance symbol offset and phase + pcl.advance(error); + float delta = floorf(pcl.phase); + offset += delta; + pcl.phase -= delta; + } + offset -= count; + + // Update delay buffer + memmove(buffer, &buffer[count], (_interpTapCount - 1) * sizeof(float)); + + // Swap if some data was generated + base_type::_in->flush(); + return outCount; + } + + bool locked = false; + bool test2 = false; + + float syncBias = 0.0f; + bool forceLock = false; + + int counter = 0; + +protected: + void generateInterpTaps() { + double bw = 0.5 / (double)_interpPhaseCount; + dsp::tap lp = dsp::taps::windowedSinc(_interpPhaseCount * _interpTapCount, dsp::math::hzToRads(bw, 1.0), dsp::window::nuttall, _interpPhaseCount); + interpBank = dsp::multirate::buildPolyphaseBank(_interpPhaseCount, lp); + dsp::taps::free(lp); + } + + dsp::multirate::PolyphaseBank interpBank; + dsp::loop::PhaseControlLoop pcl; + + double _omega; + double _omegaGain; + double _muGain; + double _omegaRelLimit; + int _interpPhaseCount; + int _interpTapCount; + + int offset = 0; + int outCount = 0; + float* buffer; + float* bufStart; + + float syncLevel = -0.03f; +}; \ No newline at end of file diff --git a/decoder_modules/atv_decoder/src/main.cpp b/decoder_modules/atv_decoder/src/main.cpp index c5938f34..9ae9856d 100644 --- a/decoder_modules/atv_decoder/src/main.cpp +++ b/decoder_modules/atv_decoder/src/main.cpp @@ -10,6 +10,15 @@ #include #include +#include "linesync.h" +#include +#include +#include +#include + +#include "chrominance_filter.h" + +#include "chroma_pll.h" #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -17,7 +26,8 @@ SDRPP_MOD_INFO{/* Name: */ "atv_decoder", /* Description: */ "ATV decoder for SDR++", /* Author: */ "Ryzerth", /* Version: */ 0, 1, 0, - /* Max instances */ -1}; + /* Max instances */ -1 +}; #define SAMPLE_RATE (625.0f * 720.0f * 25.0f) @@ -29,9 +39,16 @@ class ATVDecoderModule : public ModuleManager::Instance { vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 8000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true); demod.init(vfo->output, SAMPLE_RATE, SAMPLE_RATE / 2.0f); - sink.init(&demod.out, handler, this); + sync.init(&demod.out, 1.0f, 1e-6, 1.0, 0.05); + sink.init(&sync.out, handler, this); + + r2c.init(NULL); + chromaTaps = dsp::taps::fromArray(CHROMA_FIR_SIZE, CHROMA_FIR); + fir.init(NULL, chromaTaps); + pll.init(NULL, 0.01, 0.0, dsp::math::hzToRads(4433618.75, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*0.90, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*1.1, SAMPLE_RATE)); demod.start(); + sync.start(); sink.start(); gui::menu.registerEntry(name, menuHandler, this, this); @@ -47,9 +64,13 @@ class ATVDecoderModule : public ModuleManager::Instance { void postInit() {} - void enable() { enabled = true; } + void enable() { + enabled = true; + } - void disable() { enabled = false; } + void disable() { + enabled = false; + } bool isEnabled() { return enabled; } @@ -61,6 +82,8 @@ class ATVDecoderModule : public ModuleManager::Instance { style::beginDisabled(); } + // Ideal width for testing: 750pixels + ImGui::FillWidth(); _this->img.draw(); @@ -76,6 +99,28 @@ class ATVDecoderModule : public ModuleManager::Instance { ImGui::FillWidth(); ImGui::SliderFloat("##spanLvl", &_this->spanLvl, 0, 1.0); + ImGui::LeftLabel("Sync Bias"); + ImGui::FillWidth(); + ImGui::SliderFloat("##syncBias", &_this->sync.syncBias,-0.1, 0.1); + + if (ImGui::Button("Test2")) { + _this->sync.test2 = true; + } + + if (ImGui::Button("Switch frame")) { + std::lock_guard lck(_this->evenFrameMtx); + _this->evenFrame = !_this->evenFrame; + } + + if (_this->sync.locked) { + ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked"); + } + else { + ImGui::TextUnformatted("Not locked"); + } + + ImGui::Checkbox("Force Lock", &_this->sync.forceLock); + if (!_this->enabled) { style::endDisabled(); } @@ -84,70 +129,66 @@ class ATVDecoderModule : public ModuleManager::Instance { static void handler(float *data, int count, void *ctx) { ATVDecoderModule *_this = (ATVDecoderModule *)ctx; - uint8_t *buf = (uint8_t *)_this->img.buffer; - float val; - float imval; - int pos = 0; + // Convert line to complex + _this->r2c.process(720, data, _this->r2c.out.writeBuf); + + // Isolate the chroma subcarrier + _this->fir.process(720, _this->r2c.out.writeBuf, _this->fir.out.writeBuf); + + // Run chroma carrier through the PLL + _this->pll.process(720, _this->fir.out.writeBuf, _this->pll.out.writeBuf, ((_this->ypos%2)==1) ^ _this->evenFrame); + + // Render line to the image without color + //int lypos = _this->ypos - 1; + //if (lypos < 0) { lypos = 624; } + //uint32_t* lastLine = &((uint32_t *)_this->img.buffer)[(lypos < 313) ? (lypos*720*2) : ((((lypos - 313)*2)+1)*720) ]; + //uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos < 313) ? (_this->ypos*720*2) : ((((_this->ypos - 313)*2)+1)*720) ]; + + uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[_this->ypos*720]; + for (int i = 0; i < count; i++) { - val = data[i]; - // Sync - if (val < _this->sync_level) { - _this->sync_count++; - } - else { - if (_this->sync_count >= 300) { - _this->short_sync = 0; - } - else if (_this->sync_count >= 33) { - if (_this->short_sync == 5) { - _this->even_field = false; - _this->ypos = 0; - _this->img.swap(); - buf = (uint8_t *)_this->img.buffer; - } - else if (_this->short_sync == 4) { - _this->even_field = true; - _this->ypos = 0; - } - _this->xpos = 0; - _this->short_sync = 0; - } - else if (_this->sync_count >= 15) { - _this->short_sync++; - } - _this->sync_count = 0; + //float imval = std::clamp((data[i] - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255); + uint32_t re = std::clamp((_this->pll.out.writeBuf[i].re - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255); + uint32_t im = std::clamp((_this->pll.out.writeBuf[i].im - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255); + currentLine[i] = 0xFF000000 | (im << 8) | re; + } + + // Vertical scan logic + _this->ypos++; + bool rollover = _this->ypos >= 625; + if (rollover) { + { + std::lock_guard lck(_this->evenFrameMtx); + _this->evenFrame = !_this->evenFrame; } + _this->ypos = 0; + _this->img.swap(); + } + // Measure vsync levels + float sync0 = 0.0f, sync1 = 0.0f; + for (int i = 0; i < 306; i++) { + sync0 += data[i]; + } + for (int i = (720/2); i < ((720/2)+306); i++) { + sync1 += data[i]; + } + sync0 *= (1.0f/305.0f); + sync1 *= (1.0f/305.0f); - // Draw - imval = std::clamp((val - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255); - if (_this->even_field) { - pos = ((720 * _this->ypos * 2) + _this->xpos) * 4; - } - else { - pos = ((720 * (_this->ypos * 2 + 1)) + _this->xpos) * 4; - } + // Save sync detection to history + _this->syncHistory >>= 2; + _this->syncHistory |= (((uint16_t)(sync1 < _this->sync_level)) << 9) | (((uint16_t)(sync0 < _this->sync_level)) << 8); - buf[pos] = imval; - buf[pos + 1] = imval; - buf[pos + 2] = imval; - buf[pos + 3] = imval; - - // Image logic - _this->xpos++; - if (_this->xpos >= 720) { - _this->ypos++; - _this->xpos = 0; - } - if (_this->ypos >= 312) { - _this->ypos = 0; - _this->xpos = 0; - _this->even_field = !_this->even_field; - if (_this->even_field) { - _this->img.swap(); - buf = (uint8_t *)_this->img.buffer; - } + // Trigger vsync in case one is detected + // TODO: Also sync with odd field + if (!rollover && _this->syncHistory == 0b0000011111) { + { + std::lock_guard lck(_this->evenFrameMtx); + _this->evenFrame = !_this->evenFrame; } + _this->ypos = 0; + _this->img.swap(); } } @@ -156,19 +197,27 @@ class ATVDecoderModule : public ModuleManager::Instance { VFOManager::VFO *vfo = NULL; dsp::demod::Quadrature demod; + LineSync sync; dsp::sink::Handler sink; - - int xpos = 0; + dsp::convert::RealToComplex r2c; + dsp::tap chromaTaps; + dsp::filter::FIR fir; + dsp::loop::ChromaPLL pll; int ypos = 0; - bool even_field = false; - float sync_level = -0.3f; + bool evenFrame = false; + std::mutex evenFrameMtx; + + float sync_level = -0.06f; int sync_count = 0; int short_sync = 0; float minLvl = 0.0f; float spanLvl = 1.0f; + bool lockedLines = 0; + uint16_t syncHistory = 0; + ImGui::ImageDisplay img; }; diff --git a/decoder_modules/radio/src/demodulators/wfm.h b/decoder_modules/radio/src/demodulators/wfm.h index 2761b28e..8c2c03ea 100644 --- a/decoder_modules/radio/src/demodulators/wfm.h +++ b/decoder_modules/radio/src/demodulators/wfm.h @@ -2,11 +2,13 @@ #include "../demod.h" #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -14,9 +16,9 @@ namespace demod { class WFM : public Demodulator { public: - WFM() {} + WFM() : diag(0.5, 4096) {} - WFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, double audioSR) { + WFM(std::string name, ConfigManager* config, dsp::stream* input, double bandwidth, double audioSR) : diag(0.5, 4096) { init(name, config, input, bandwidth, audioSR); } @@ -45,33 +47,66 @@ namespace demod { if (config->conf[name][getName()].contains("rds")) { _rds = config->conf[name][getName()]["rds"]; } + if (config->conf[name][getName()].contains("rdsInfo")) { + _rdsInfo = config->conf[name][getName()]["rdsInfo"]; + } _config->release(modified); // Define structure demod.init(input, bandwidth / 2.0f, getIFSampleRate(), _stereo, _lowPass, _rds); - recov.init(&demod.rdsOut, 5000.0 / 2375, omegaGain, muGain, 0.01); - slice.init(&recov.out); - manch.init(&slice.out); - diff.init(&manch.out, 2); + agc.init(&demod.rdsOut, 1.0, 1e6, 0.1); + costas.init(&agc.out, 0.005f); + + taps = dsp::taps::bandPass(0, 2375, 100, 5000); + fir.init(&costas.out, taps); + double baudfreq = dsp::math::hzToRads(2375.0/2.0, 5000); + costas2.init(&fir.out, 0.01, 0.0, baudfreq, baudfreq - (baudfreq*0.1), baudfreq + (baudfreq*0.1)); + + c2r.init(&costas2.out); + recov.init(&c2r.out, 5000.0 / (2375.0 / 2.0), 1e-6, 0.01, 0.01); + slice.init(&doubler.outA); + diff.init(&slice.out, 2); hs.init(&diff.out, rdsHandler, this); + + doubler.init(&recov.out); + reshape.init(&doubler.outB, 4096, (1187 / 30) - 4096); + diagHandler.init(&reshape.out, _diagHandler, this); + diag.lines.push_back(-0.8); + diag.lines.push_back(0.8); } void start() { + agc.start(); + costas.start(); + fir.start(); + costas2.start(); + c2r.start(); demod.start(); recov.start(); slice.start(); - manch.start(); diff.start(); hs.start(); + + doubler.start(); + reshape.start(); + diagHandler.start(); } void stop() { + agc.stop(); + costas.stop(); + fir.stop(); + costas2.stop(); + c2r.stop(); demod.stop(); recov.stop(); slice.stop(); - manch.stop(); diff.stop(); hs.stop(); + + c2r.stop(); + reshape.stop(); + diagHandler.stop(); } void showMenu() { @@ -94,14 +129,105 @@ namespace demod { _config->release(true); } - // if (_rds) { - // if (rdsDecode.countryCodeValid()) { ImGui::Text("Country code: %d", rdsDecode.getCountryCode()); } - // if (rdsDecode.programCoverageValid()) { ImGui::Text("Program coverage: %d", rdsDecode.getProgramCoverage()); } - // if (rdsDecode.programRefNumberValid()) { ImGui::Text("Reference number: %d", rdsDecode.getProgramRefNumber()); } - // if (rdsDecode.programTypeValid()) { ImGui::Text("Program type: %d", rdsDecode.getProgramType()); } - // if (rdsDecode.PSNameValid()) { ImGui::Text("Program name: [%s]", rdsDecode.getPSName().c_str()); } - // if (rdsDecode.radioTextValid()) { ImGui::Text("Radiotext: [%s]", rdsDecode.getRadioText().c_str()); } - // } + // TODO: This will break when the entire radio module is disabled + if (!_rds) { ImGui::BeginDisabled(); } + if (ImGui::Checkbox(("Advanced RDS Info##_radio_wfm_rds_info_" + name).c_str(), &_rdsInfo)) { + _config->acquire(); + _config->conf[name][getName()]["rdsInfo"] = _rdsInfo; + _config->release(true); + } + if (!_rds) { ImGui::EndDisabled(); } + + float menuWidth = ImGui::GetContentRegionAvail().x; + + if (_rds && _rdsInfo) { + ImGui::BeginTable(("##radio_wfm_rds_info_tbl_" + name).c_str(), 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders); + if (rdsDecode.piCodeValid()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("PI Code"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("0x%04X (%s)", rdsDecode.getPICode(), rdsDecode.getCallsign().c_str()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Country Code"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%d", rdsDecode.getCountryCode()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Program Coverage"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s (%d)", rds::AREA_COVERAGE_TO_STR[rdsDecode.getProgramCoverage()], rdsDecode.getProgramCoverage()); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Reference Number"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%d", rdsDecode.getProgramRefNumber()); + } + else { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("PI Code"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("0x---- (----)"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Country Code"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("--"); // TODO: String + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Program Coverage"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("------- (--)"); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Reference Number"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("--"); + } + + if (rdsDecode.programTypeValid()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Program Type"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s (%d)", rds::PROGRAM_TYPE_US_TO_STR[rdsDecode.getProgramType()], rdsDecode.getProgramType()); + } + else { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Program Type"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("------- (--)"); // TODO: String + } + + if (rdsDecode.musicValid()) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Music"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", rdsDecode.getMusic() ? "Yes":"No"); + } + else { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted("Music"); + ImGui::TableSetColumnIndex(1); + ImGui::TextUnformatted("---"); + } + + ImGui::EndTable(); + + ImGui::SetNextItemWidth(menuWidth); + diag.draw(); + } } void setBandwidth(double bandwidth) { @@ -145,6 +271,14 @@ namespace demod { _this->rdsDecode.process(data, count); } + // DEBUGGING ONLY + static void _diagHandler(float* data, int count, void* ctx) { + WFM* _this = (WFM*)ctx; + float* buf = _this->diag.acquireBuffer(); + memcpy(buf, data, count * sizeof(float)); + _this->diag.releaseBuffer(); + } + static void fftRedraw(ImGui::WaterFall::FFTRedrawArgs args, void* ctx) { WFM* _this = (WFM*)ctx; if (!_this->_rds) { return; } @@ -186,13 +320,23 @@ namespace demod { } dsp::demod::BroadcastFM demod; - dsp::clock_recovery::FD recov; + dsp::loop::FastAGC agc; + dsp::loop::Costas<2> costas; + dsp::tap taps; + dsp::filter::FIR fir; + dsp::loop::Costas<2> costas2; + dsp::convert::ComplexToReal c2r; + dsp::clock_recovery::MM recov; dsp::digital::BinarySlicer slice; - dsp::digital::ManchesterDecoder manch; dsp::digital::DifferentialDecoder diff; dsp::sink::Handler hs; EventHandler fftRedrawHandler; + dsp::routing::Doubler doubler; + dsp::buffer::Reshaper reshape; + dsp::sink::Handler diagHandler; + ImGui::SymbolDiagram diag; + rds::RDSDecoder rdsDecode; ConfigManager* _config = NULL; @@ -200,6 +344,7 @@ namespace demod { bool _stereo = false; bool _lowPass = true; bool _rds = false; + bool _rdsInfo = false; float muGain = 0.01; float omegaGain = (0.01*0.01)/4.0; diff --git a/decoder_modules/radio/src/rds.cpp b/decoder_modules/radio/src/rds.cpp index f15fd74f..c2f37e3a 100644 --- a/decoder_modules/radio/src/rds.cpp +++ b/decoder_modules/radio/src/rds.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace rds { std::map SYNDROMES = { { 0b1111011000, BLOCK_TYPE_A }, @@ -54,18 +56,26 @@ namespace rds { type = (BlockType)((lastType + 1) % _BLOCK_TYPE_COUNT); } - // Save block while correcting errors (NOT YET) + // Save block while correcting errors (NOT YET) <- idk why the "not yet is here", TODO: find why blocks[type] = correctErrors(shiftReg, type, blockAvail[type]); - // Update continous group count - if (type == BLOCK_TYPE_A) { contGroup = 1; } - else if (type == BLOCK_TYPE_B && lastType == BLOCK_TYPE_A) { contGroup++; } + // If block type is A, decode it directly, otherwise, update continous count + if (type == BLOCK_TYPE_A) { + decodeBlockA(); + } + else if (type == BLOCK_TYPE_B) { contGroup = 1; } else if ((type == BLOCK_TYPE_C || type == BLOCK_TYPE_CP) && lastType == BLOCK_TYPE_B) { contGroup++; } else if (type == BLOCK_TYPE_D && (lastType == BLOCK_TYPE_C || lastType == BLOCK_TYPE_CP)) { contGroup++; } - else { contGroup = 0; } + else { + // If block B is available, decode it alone. + if (contGroup == 1) { + decodeBlockB(); + } + contGroup = 0; + } // If we've got an entire group, process it - if (contGroup >= 4) { + if (contGroup >= 3) { contGroup = 0; decodeGroup(); } @@ -124,26 +134,45 @@ namespace rds { return out; } - void RDSDecoder::decodeGroup() { + void RDSDecoder::decodeBlockA() { + // If it didn't decode properly return + if (!blockAvail[BLOCK_TYPE_A]) { return; } + + // Update timeout std::lock_guard lck(groupMtx); auto now = std::chrono::high_resolution_clock::now(); - anyGroupLastUpdate = now; - - // Make sure blocks A and B are available - if (!blockAvail[BLOCK_TYPE_A] || !blockAvail[BLOCK_TYPE_B]) { return; } + blockALastUpdate = now; // Decode PI code + piCode = (blocks[BLOCK_TYPE_A] >> 10) & 0xFFFF; countryCode = (blocks[BLOCK_TYPE_A] >> 22) & 0xF; programCoverage = (AreaCoverage)((blocks[BLOCK_TYPE_A] >> 18) & 0xF); programRefNumber = (blocks[BLOCK_TYPE_A] >> 10) & 0xFF; + decodeCallsign(); + } + + void RDSDecoder::decodeBlockB() { + // If it didn't decode properly return + if (!blockAvail[BLOCK_TYPE_B]) { return; } // Decode group type and version - uint8_t groupType = (blocks[BLOCK_TYPE_B] >> 22) & 0xF; - GroupVersion groupVer = (GroupVersion)((blocks[BLOCK_TYPE_B] >> 21) & 1); + groupType = (blocks[BLOCK_TYPE_B] >> 22) & 0xF; + groupVer = (GroupVersion)((blocks[BLOCK_TYPE_B] >> 21) & 1); // Decode traffic program and program type trafficProgram = (blocks[BLOCK_TYPE_B] >> 20) & 1; programType = (ProgramType)((blocks[BLOCK_TYPE_B] >> 15) & 0x1F); + } + + void RDSDecoder::decodeGroup() { + std::lock_guard lck(groupMtx); + auto now = std::chrono::high_resolution_clock::now(); + + // Make sure blocks B is available + if (!blockAvail[BLOCK_TYPE_B]) { return; } + + // Decode block B + decodeBlockB(); if (groupType == 0) { group0LastUpdate = now; @@ -202,9 +231,33 @@ namespace rds { } } - bool RDSDecoder::anyGroupValid() { + void RDSDecoder::decodeCallsign() { + // Determin first better based on offset + bool w = (piCode >= 21672); + callsign = w ? 'W' : 'K'; + + // Base25 decode the rest + std::string restStr; + int rest = piCode - (w ? 21672 : 4096); + while (rest) { + restStr += 'A' + (rest % 26); + rest /= 26; + } + + // Reorder chars + for (int i = restStr.size() - 1; i >= 0; i--) { + callsign += restStr[i]; + } + } + + bool RDSDecoder::blockAValid() { auto now = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(now - anyGroupLastUpdate)).count() < 5000.0; + return (std::chrono::duration_cast(now - blockALastUpdate)).count() < 5000.0; + } + + bool RDSDecoder::blockBValid() { + auto now = std::chrono::high_resolution_clock::now(); + return (std::chrono::duration_cast(now - blockALastUpdate)).count() < 5000.0; } bool RDSDecoder::group0Valid() { diff --git a/decoder_modules/radio/src/rds.h b/decoder_modules/radio/src/rds.h index 075197f4..0ecbb423 100644 --- a/decoder_modules/radio/src/rds.h +++ b/decoder_modules/radio/src/rds.h @@ -20,22 +20,42 @@ namespace rds { }; enum AreaCoverage { - AREA_COVERAGE_LOCAL, - AREA_COVERAGE_INTERNATIONAL, - AREA_COVERAGE_NATIONAL, - AREA_COVERAGE_SUPRA_NATIONAL, - AREA_COVERAGE_REGIONAL1, - AREA_COVERAGE_REGIONAL2, - AREA_COVERAGE_REGIONAL3, - AREA_COVERAGE_REGIONAL4, - AREA_COVERAGE_REGIONAL5, - AREA_COVERAGE_REGIONAL6, - AREA_COVERAGE_REGIONAL7, - AREA_COVERAGE_REGIONAL8, - AREA_COVERAGE_REGIONAL9, - AREA_COVERAGE_REGIONAL10, - AREA_COVERAGE_REGIONAL11, - AREA_COVERAGE_REGIONAL12 + AREA_COVERAGE_INVALID = -1, + AREA_COVERAGE_LOCAL = 0, + AREA_COVERAGE_INTERNATIONAL = 1, + AREA_COVERAGE_NATIONAL = 2, + AREA_COVERAGE_SUPRA_NATIONAL = 3, + AREA_COVERAGE_REGIONAL1 = 4, + AREA_COVERAGE_REGIONAL2 = 5, + AREA_COVERAGE_REGIONAL3 = 6, + AREA_COVERAGE_REGIONAL4 = 7, + AREA_COVERAGE_REGIONAL5 = 8, + AREA_COVERAGE_REGIONAL6 = 9, + AREA_COVERAGE_REGIONAL7 = 10, + AREA_COVERAGE_REGIONAL8 = 11, + AREA_COVERAGE_REGIONAL9 = 12, + AREA_COVERAGE_REGIONAL10 = 13, + AREA_COVERAGE_REGIONAL11 = 14, + AREA_COVERAGE_REGIONAL12 = 15 + }; + + inline const char* AREA_COVERAGE_TO_STR[] = { + "Local", + "International", + "National", + "Supra-National", + "Regional 1", + "Regional 2", + "Regional 3", + "Regional 4", + "Regional 5", + "Regional 6", + "Regional 7", + "Regional 8", + "Regional 9", + "Regional 10", + "Regional 11", + "Regional 12", }; enum ProgramType { @@ -108,6 +128,76 @@ namespace rds { PROGRAM_TYPE_EU_ALARM = 31 }; + inline const char* PROGRAM_TYPE_EU_TO_STR[] = { + "None", + "News", + "Current Affairs", + "Information", + "Sports", + "Education", + "Drama", + "Culture", + "Science", + "Varied", + "Pop Music", + "Rock Music", + "Easy Listening Music", + "Light Classical", + "Serious Classical", + "Other Music", + "Weather", + "Finance", + "Children Program", + "Social Affairs", + "Religion", + "Phone-in", + "Travel", + "Leisure", + "Jazz Music", + "Country Music", + "National Music", + "Oldies Music", + "Folk Music", + "Documentary", + "Alarm Test", + "Alarm", + }; + + inline const char* PROGRAM_TYPE_US_TO_STR[] = { + "None", + "News", + "Information", + "Sports", + "Talk", + "Rock", + "Classic Rock", + "Adult Hits", + "Soft Rock", + "Top 40", + "Country", + "Oldies", + "Soft", + "Nostalgia", + "Jazz", + "Classical", + "Rythm and Blues", + "Soft Rythm and Blues", + "Foreign Language", + "Religious Music", + "Religious Talk", + "Personality", + "Public", + "College", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Weather", + "Emergency Test", + "Emergency", + }; + enum DecoderIdentification { DECODER_IDENT_STEREO = (1 << 0), DECODER_IDENT_ARTIFICIAL_HEAD = (1 << 1), @@ -119,13 +209,14 @@ namespace rds { public: void process(uint8_t* symbols, int count); - bool countryCodeValid() { std::lock_guard lck(groupMtx); return anyGroupValid(); } + bool piCodeValid() { std::lock_guard lck(groupMtx); return blockAValid(); } + uint16_t getPICode() { std::lock_guard lck(groupMtx); return piCode; } uint8_t getCountryCode() { std::lock_guard lck(groupMtx); return countryCode; } - bool programCoverageValid() { std::lock_guard lck(groupMtx); return anyGroupValid(); } uint8_t getProgramCoverage() { std::lock_guard lck(groupMtx); return programCoverage; } - bool programRefNumberValid() { std::lock_guard lck(groupMtx); return anyGroupValid(); } uint8_t getProgramRefNumber() { std::lock_guard lck(groupMtx); return programRefNumber; } - bool programTypeValid() { std::lock_guard lck(groupMtx); return anyGroupValid(); } + std::string getCallsign() { std::lock_guard lck(groupMtx); return callsign; } + + bool programTypeValid() { std::lock_guard lck(groupMtx); return blockBValid(); } ProgramType getProgramType() { std::lock_guard lck(groupMtx); return programType; } bool musicValid() { std::lock_guard lck(groupMtx); return group0Valid(); } @@ -139,9 +230,14 @@ namespace rds { private: static uint16_t calcSyndrome(uint32_t block); static uint32_t correctErrors(uint32_t block, BlockType type, bool& recovered); + void decodeBlockA(); + void decodeBlockB(); void decodeGroup(); - bool anyGroupValid(); + void decodeCallsign(); + + bool blockAValid(); + bool blockBValid(); bool group0Valid(); bool group2Valid(); @@ -154,17 +250,24 @@ namespace rds { uint32_t blocks[_BLOCK_TYPE_COUNT]; bool blockAvail[_BLOCK_TYPE_COUNT]; - // All groups + // Block A (All groups) std::mutex groupMtx; - std::chrono::time_point anyGroupLastUpdate; + std::chrono::time_point blockALastUpdate{}; // 1970-01-01 + uint16_t piCode; uint8_t countryCode; AreaCoverage programCoverage; uint8_t programRefNumber; + std::string callsign; + + // Block B (All groups) + std::chrono::time_point blockBLastUpdate{}; // 1970-01-01 + uint8_t groupType; + GroupVersion groupVer; bool trafficProgram; ProgramType programType; // Group type 0 - std::chrono::time_point group0LastUpdate; + std::chrono::time_point group0LastUpdate{}; // 1970-01-01 bool trafficAnnouncement; bool music; uint8_t decoderIdent; @@ -172,7 +275,7 @@ namespace rds { std::string programServiceName = " "; // Group type 2 - std::chrono::time_point group2LastUpdate; + std::chrono::time_point group2LastUpdate{}; // 1970-01-01 bool rtAB = false; std::string radioText = " "; diff --git a/source_modules/bladerf_source/src/main.cpp b/source_modules/bladerf_source/src/main.cpp index d723f88b..8bb343d3 100644 --- a/source_modules/bladerf_source/src/main.cpp +++ b/source_modules/bladerf_source/src/main.cpp @@ -347,7 +347,7 @@ private: static void start(void* ctx) { BladeRFSourceModule* _this = (BladeRFSourceModule*)ctx; if (_this->running) { return; } - if (_this->devCount == 0) { return; } + if (_this->devCount <= 0) { return; } // Open device bladerf_devinfo info = _this->devInfoList[_this->devId]; From f3c5b2c31f7ded08bf0d67c308f962d97ea35f9b Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Tue, 26 Dec 2023 23:10:44 +0100 Subject: [PATCH 02/65] Fixed #1259 --- source_modules/hackrf_source/src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source_modules/hackrf_source/src/main.cpp b/source_modules/hackrf_source/src/main.cpp index a70fd579..abb4bcc5 100644 --- a/source_modules/hackrf_source/src/main.cpp +++ b/source_modules/hackrf_source/src/main.cpp @@ -137,6 +137,10 @@ public: hackrf_device_list_t* _devList = hackrf_device_list(); for (int i = 0; i < _devList->devicecount; i++) { + // Skip devices that are in use + if (_devList->serial_numbers[i] == NULL) { continue; } + + // Save the device serial number devList.push_back(_devList->serial_numbers[i]); devListTxt += (char*)(_devList->serial_numbers[i] + 16); devListTxt += '\0'; From b1030cbdfb8b83486beecd0e1085a25fe74f3d85 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 27 Dec 2023 03:53:43 +0100 Subject: [PATCH 03/65] Implement #1260 --- source_modules/rtl_sdr_source/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source_modules/rtl_sdr_source/src/main.cpp b/source_modules/rtl_sdr_source/src/main.cpp index de6d5686..dba27610 100644 --- a/source_modules/rtl_sdr_source/src/main.cpp +++ b/source_modules/rtl_sdr_source/src/main.cpp @@ -523,8 +523,8 @@ private: RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx; int sampCount = len / 2; for (int i = 0; i < sampCount; i++) { - _this->stream.writeBuf[i].re = (float)(buf[i * 2] - 127) / 128.0f; - _this->stream.writeBuf[i].im = (float)(buf[(i * 2) + 1] - 127) / 128.0f; + _this->stream.writeBuf[i].re = ((float)buf[i * 2] - 127.4) / 128.0f; + _this->stream.writeBuf[i].im = ((float)buf[(i * 2) + 1] - 127.4) / 128.0f; } if (!_this->stream.swap(sampCount)) { return; } } From 505cbb0ba2c728700cf896a3c132e6939bae2e3a Mon Sep 17 00:00:00 2001 From: Wil Clouser Date: Thu, 4 Jan 2024 11:48:39 -0800 Subject: [PATCH 04/65] add additional library to debian install instructions --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a5dbac7a..36312fd9 100644 --- a/readme.md +++ b/readme.md @@ -44,7 +44,7 @@ Download the latest release from [the Releases page](https://github.com/Alexandr Then, run: ```sh -sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev +sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev sudo dpkg -i sdrpp_debian_amd64.deb ``` From a91434c5fe066345fa6cfe483bc6b06ad2f4eb64 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 17 Jan 2024 06:28:16 +0100 Subject: [PATCH 05/65] fix windows CI (hopefully) and rebuild with Android BlogV4 support --- .github/workflows/build_all.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 849d0d55..d958835e 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -34,7 +34,7 @@ jobs: - name: Patch Pothos with earlier libusb version working-directory: ${{runner.workspace}} - run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" + run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/" - name: Download SDRPlay API run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip @@ -64,7 +64,7 @@ jobs: run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install . - name: Install libperseus-sdr - run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr" + run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr" - name: Prepare CMake working-directory: ${{runner.workspace}}/build From 6fc41a81a73b0fd30b6b2ba97b6dd808b70184a9 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 17 Jan 2024 06:41:25 +0100 Subject: [PATCH 06/65] fix build on android due to new librtlsdr function name --- source_modules/rtl_sdr_source/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_modules/rtl_sdr_source/src/main.cpp b/source_modules/rtl_sdr_source/src/main.cpp index dba27610..b7ce64cb 100644 --- a/source_modules/rtl_sdr_source/src/main.cpp +++ b/source_modules/rtl_sdr_source/src/main.cpp @@ -171,7 +171,7 @@ public: #ifndef __ANDROID__ int oret = rtlsdr_open(&openDev, id); #else - int oret = rtlsdr_open_fd(&openDev, devFd); + int oret = rtlsdr_open_sys_dev(&openDev, devFd); #endif if (oret < 0) { From 052167962dbf9adc2a02825f2f428e7613255d50 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 17 Jan 2024 06:52:08 +0100 Subject: [PATCH 07/65] fix the rtlsdr source on android again --- source_modules/rtl_sdr_source/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_modules/rtl_sdr_source/src/main.cpp b/source_modules/rtl_sdr_source/src/main.cpp index b7ce64cb..7ef3951c 100644 --- a/source_modules/rtl_sdr_source/src/main.cpp +++ b/source_modules/rtl_sdr_source/src/main.cpp @@ -285,7 +285,7 @@ private: #ifndef __ANDROID__ int oret = rtlsdr_open(&_this->openDev, _this->devId); #else - int oret = rtlsdr_open_fd(&_this->openDev, _this->devFd); + int oret = rtlsdr_open_sys_dev(&_this->openDev, _this->devFd); #endif if (oret < 0) { From 5a1945f7798fe7fd4603615931423b96a931bcd7 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 17 Jan 2024 07:07:24 +0100 Subject: [PATCH 08/65] allow running code on older macos version (test) --- .github/workflows/build_all.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index d958835e..3654b8f0 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -85,7 +85,7 @@ jobs: path: ${{runner.workspace}}/sdrpp_windows_x64.zip build_macos: - runs-on: macos-11 + runs-on: macos-latest steps: - uses: actions/checkout@v3 @@ -100,29 +100,29 @@ jobs: run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako - name: Install volk - run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ + run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ - name: Install SDRplay API run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target / - name: Install libiio - run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ + run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ - name: Install libad9361 - run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ + run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ - name: Install LimeSuite - run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ + run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ - name: Install libperseus run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd .. - name: Install more recent librtlsdr - run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../ + run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../ - name: Prepare CMake working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release + run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release - name: Build working-directory: ${{runner.workspace}}/build From eabb842b6beaea7064eb93706d19d4f0348cd198 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 19 Jan 2024 19:11:01 +0100 Subject: [PATCH 09/65] switched MacOS CI to debug mode --- .github/workflows/build_all.yml | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 3654b8f0..178b93f8 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -122,7 +122,7 @@ jobs: - name: Prepare CMake working-directory: ${{runner.workspace}}/build - run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release + run: cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release - name: Build working-directory: ${{runner.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index a9c67193..e5cce405 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ if (${CMAKE_BUILD_TYPE} MATCHES "Debug") if (MSVC) set(SDRPP_COMPILER_FLAGS /std:c++17 /EHsc) elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(SDRPP_COMPILER_FLAGS -g -Og -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup) + set(SDRPP_COMPILER_FLAGS -g -Og -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup -fsanitize=address) else () set(SDRPP_COMPILER_FLAGS -g -Og -std=c++17) endif () From fd5813df6dd339bdf0c3bea88a9322edd0aac95c Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 19 Jan 2024 19:43:31 +0100 Subject: [PATCH 10/65] testing something silly --- macos/bundle_utils.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos/bundle_utils.sh b/macos/bundle_utils.sh index a14a1dd0..1cbaa531 100644 --- a/macos/bundle_utils.sh +++ b/macos/bundle_utils.sh @@ -10,7 +10,7 @@ bundle_is_not_to_be_installed() { # NOTE: Customize this list to exclude libraries you don't want copied into the bundle if [ "$1" = "libsdrpp_core.dylib" ]; then echo 1; fi if [ "$1" = "OpenGL" ]; then echo 1; fi - if [ "$1" = "libc++.1.dylib" ]; then echo 1; fi + # if [ "$1" = "libc++.1.dylib" ]; then echo 1; fi if [ "$1" = "libSystem.B.dylib" ]; then echo 1; fi if [ "$1" = "Cocoa" ]; then echo 1; fi if [ "$1" = "IOKit" ]; then echo 1; fi From 5e299d9d2302544f7fee63d1ec5ca103fe50bd55 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 19 Jan 2024 20:01:05 +0100 Subject: [PATCH 11/65] testing something silly 2 --- macos/bundle_utils.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/macos/bundle_utils.sh b/macos/bundle_utils.sh index 1cbaa531..937f8279 100644 --- a/macos/bundle_utils.sh +++ b/macos/bundle_utils.sh @@ -10,7 +10,6 @@ bundle_is_not_to_be_installed() { # NOTE: Customize this list to exclude libraries you don't want copied into the bundle if [ "$1" = "libsdrpp_core.dylib" ]; then echo 1; fi if [ "$1" = "OpenGL" ]; then echo 1; fi - # if [ "$1" = "libc++.1.dylib" ]; then echo 1; fi if [ "$1" = "libSystem.B.dylib" ]; then echo 1; fi if [ "$1" = "Cocoa" ]; then echo 1; fi if [ "$1" = "IOKit" ]; then echo 1; fi @@ -22,7 +21,6 @@ bundle_is_not_to_be_installed() { if [ "$1" = "CoreAudio" ]; then echo 1; fi if [ "$1" = "AudioToolbox" ]; then echo 1; fi if [ "$1" = "AudioUnit" ]; then echo 1; fi - if [ "$1" = "libobjc.A.dylib" ]; then echo 1; fi if [ "$1" = "CFNetwork" ]; then echo 1; fi if [ "$1" = "SystemConfiguration" ]; then echo 1; fi if [ "$1" = "Security" ]; then echo 1; fi From 5e195a0d439486ad3cc1890616de15c7471728d7 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 19 Jan 2024 20:53:25 +0100 Subject: [PATCH 12/65] testing something for macos (do not use this CI build) --- macos/bundle_utils.sh | 2 ++ src/main.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/macos/bundle_utils.sh b/macos/bundle_utils.sh index 937f8279..a14a1dd0 100644 --- a/macos/bundle_utils.sh +++ b/macos/bundle_utils.sh @@ -10,6 +10,7 @@ bundle_is_not_to_be_installed() { # NOTE: Customize this list to exclude libraries you don't want copied into the bundle if [ "$1" = "libsdrpp_core.dylib" ]; then echo 1; fi if [ "$1" = "OpenGL" ]; then echo 1; fi + if [ "$1" = "libc++.1.dylib" ]; then echo 1; fi if [ "$1" = "libSystem.B.dylib" ]; then echo 1; fi if [ "$1" = "Cocoa" ]; then echo 1; fi if [ "$1" = "IOKit" ]; then echo 1; fi @@ -21,6 +22,7 @@ bundle_is_not_to_be_installed() { if [ "$1" = "CoreAudio" ]; then echo 1; fi if [ "$1" = "AudioToolbox" ]; then echo 1; fi if [ "$1" = "AudioUnit" ]; then echo 1; fi + if [ "$1" = "libobjc.A.dylib" ]; then echo 1; fi if [ "$1" = "CFNetwork" ]; then echo 1; fi if [ "$1" = "SystemConfiguration" ]; then echo 1; fi if [ "$1" = "Security" ]; then echo 1; fi diff --git a/src/main.cpp b/src/main.cpp index 9fc1638c..5e6e1838 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,30 @@ #include #include +#include + +extern char **environ; + +int main(int argc, char* argv[], char* envp[]) { + FILE* dump = fopen("/Users/ryzerth/dump.txt", "wb"); + + char buf[1024]; + fprintf(dump, "Working directory: %s\n\n", getcwd(buf, 1023)); + + fprintf(dump, "Arguments:\n"); + fprintf(dump, "----------\n"); + for (int i = 0; i < argc; i++) { + fprintf(dump, "%d: '%s'\n", i, argv[i]); + } + fprintf(dump, "\n"); + + fprintf(dump, "Environment Variables:\n"); + fprintf(dump, "----------\n"); + for (char** env = environ; *env; env++) { + fprintf(dump, "%s\n", *env); + } + fprintf(dump, "\n"); + + fclose(dump); -int main(int argc, char* argv[]) { return sdrpp_main(argc, argv); } \ No newline at end of file From fa76b4e865aeb6cebf0340bf135cd049b426c4f0 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 21 Jan 2024 20:19:46 +0100 Subject: [PATCH 13/65] Made the contributing policy more clear --- .github/pull_request_template.md | 5 +-- contributing.md | 69 +++----------------------------- 2 files changed, 6 insertions(+), 68 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6d274b1e..45e53543 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,4 @@ # Important -Only minor bug fixes and bandplans are accepted. - -Pull requests adding features or any bug fix that requires significant code changes will be automatically rejected. - +Only bandplan, colormaps and themes are accepted. Code pull requests are **NOT welcome**. Open an issue requesting a feature or discussing a possible bugfix instead. \ No newline at end of file diff --git a/contributing.md b/contributing.md index d6ece389..42167aa0 100644 --- a/contributing.md +++ b/contributing.md @@ -1,65 +1,6 @@ # Pull Requests -**I DO NOT ACCEPT PULL-REQUEST FOR FEATURES OR BUGFIXES REQUIRING SIGNIFICANT CODE/STRUCTURE CHANGES.** -**SUCH PULL REQUESTS WILL BE CLOSED AUTOMATICALLY. OPEN AN ISSUE DETAILING FEATURE REQUESTS OR POTENTIAL BUGFIX INSTEAD.** - -# Code Style - -## Naming Convention - -- Files: `snake_case.h` `snake_case.cpp` -- Namespaces: `CamelCase` -- Classes: `CamelCase` -- Structs: `CamelCase_t` -- Members: `camelCase` -- Enum: `SNAKE_CASE` -- Macros: `SNAKE_CASE` - -## Brace Style - -```c++ -int myFunction() { - if (shortIf) { shortFunctionName(); } - - if (longIf) { - longFunction(); - otherStuff(); - myLongFunction(); - } -} -``` - -Note: If it makes the code cleaner, remember to use the `?` keyword instead of a `if else` statement. - -## Pointers - -Please use `type* name` for pointers. - -## Structure - -Headers and their associated C++ files shall be in the same directory. All headers must use `#pragma once` instead of other include guards. Only include files in a header that are being used in that header. Include the rest in the associated C++ file. - -# Modules - -## Module Naming Convention - -All modules names must be `snake_case`. If the module is a source, it must end with `_source`. If it is a sink, it must end with `_sink`. - -For example, lets take the module named `cool_source`: - -- Directory: `cool_source` -- Class: `CoolSourceModule` -- Binary: `cool_source.` - -## Integration into main repository - -If the module meets the code quality requirements, it may be added to the official repository. A module that doesn't require any external dependencies that the core doesn't already use may be enabled for build by default. Otherwise, they must be disabled for build by default with a `OPT_BUILD_MODULE_NAME` variable set to `OFF`. - -# JSON Formatting - -The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSON files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses. - -**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity** +Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead. ## Band Frequency Allocation @@ -119,8 +60,8 @@ Please follow this guide to properly format the JSON files for custom color maps } ``` -# Best Practices +# JSON Formatting -* All additions and/or bug fixes to the core must not add additional dependencies. -* Use VSCode for development, VS seems to cause issues. -* DO NOT use libboost for any code meant for this repository +The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSON files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses. + +**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity** \ No newline at end of file From 23ae66151b35750be4b83cf2d3020b947c55e07a Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Sun, 21 Jan 2024 10:32:43 -0800 Subject: [PATCH 14/65] Make compatible with rtaudio API 5 and 6. Recent rtaudio changed the API to not throw exceptions anymore and also have DeviceIDs not queried by index but IDs that are provided separately ( https://github.com/thestk/rtaudio/releases ). Adapt the code-base to be compatible with the old and the new API as we have to exepect that in a transition period both APIs are common on various build platforms. --- sink_modules/audio_sink/src/main.cpp | 36 ++++++++++++++++++++---- source_modules/audio_source/src/main.cpp | 33 +++++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/sink_modules/audio_sink/src/main.cpp b/sink_modules/audio_sink/src/main.cpp index 15b7c260..147e7782 100644 --- a/sink_modules/audio_sink/src/main.cpp +++ b/sink_modules/audio_sink/src/main.cpp @@ -31,6 +31,10 @@ public: monoPacker.init(&s2m.out, 512); stereoPacker.init(_stream->sinkOut, 512); +#if RTAUDIO_VERSION_MAJOR >= 6 + audio.setErrorCallback(&reportErrorsAsException); +#endif + bool created = false; std::string device = ""; config.acquire(); @@ -42,12 +46,15 @@ public: device = config.conf[_streamName]["device"]; config.release(created); - int count = audio.getDeviceCount(); RtAudio::DeviceInfo info; +#if RTAUDIO_VERSION_MAJOR >= 6 + for (int i : audio.getDeviceIds()) { +#else + int count = audio.getDeviceCount(); for (int i = 0; i < count; i++) { +#endif try { info = audio.getDeviceInfo(i); - if (!info.probed) { continue; } if (info.outputChannels == 0) { continue; } if (info.isDefaultOutput) { defaultDevId = devList.size(); } devList.push_back(info); @@ -55,8 +62,8 @@ public: txtDevList += info.name; txtDevList += '\0'; } - catch (std::exception e) { - flog::error("AudioSinkModule Error getting audio device info: {0}", e.what()); + catch (const std::exception& e) { + flog::error("AudioSinkModule Error getting audio device info: id={0}: {1}", i, e.what()); } } selectByName(device); @@ -156,6 +163,23 @@ public: } } +#if RTAUDIO_VERSION_MAJOR >= 6 + static void reportErrorsAsException(RtAudioErrorType type, + const std::string& errorText) { + switch (type) { + case RtAudioErrorType::RTAUDIO_NO_ERROR: + return; + case RtAudioErrorType::RTAUDIO_WARNING: + case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND: + case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT: + flog::warn("AudioSink: {0} ({1})", errorText, (int)type); + break; + default: + throw std::runtime_error(errorText); + } + } +#endif + private: bool doStart() { RtAudio::StreamParameters parameters; @@ -172,8 +196,8 @@ private: audio.startStream(); stereoPacker.start(); } - catch (RtAudioError& e) { - flog::error("Could not open audio device"); + catch (const std::exception& e) { + flog::error("Could not open audio device {0}", e.what()); return false; } diff --git a/source_modules/audio_source/src/main.cpp b/source_modules/audio_source/src/main.cpp index 99efa379..b7abe253 100644 --- a/source_modules/audio_source/src/main.cpp +++ b/source_modules/audio_source/src/main.cpp @@ -35,6 +35,10 @@ public: AudioSourceModule(std::string name) { this->name = name; +#if RTAUDIO_VERSION_MAJOR >= 6 + audio.setErrorCallback(&reportErrorsAsException); +#endif + sampleRate = 48000.0; handler.ctx = this; @@ -83,21 +87,25 @@ public: void refresh() { devices.clear(); +#if RTAUDIO_VERSION_MAJOR >= 6 + for (int i : audio.getDeviceIds()) { +#else int count = audio.getDeviceCount(); for (int i = 0; i < count; i++) { +#endif try { // Get info auto info = audio.getDeviceInfo(i); // Check that it has a stereo input - if (info.probed && info.inputChannels < 2) { continue; } + if (info.inputChannels < 2) { continue; } // Save info DeviceInfo dinfo = { info, i }; devices.define(info.name, info.name, dinfo); } - catch (std::exception e) { - flog::error("Error getting audio device info: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Error getting audio device info: id={0}: {1}", i, e.what()); } } } @@ -254,6 +262,23 @@ private: return 0; } +#if RTAUDIO_VERSION_MAJOR >= 6 + static void reportErrorsAsException(RtAudioErrorType type, + const std::string& errorText) { + switch (type) { + case RtAudioErrorType::RTAUDIO_NO_ERROR: + return; + case RtAudioErrorType::RTAUDIO_WARNING: + case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND: + case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT: + flog::warn("AudioSource: {0} ({1})", errorText, (int)type); + break; + default: + throw std::runtime_error(errorText); + } + } +#endif + std::string name; bool enabled = true; dsp::stream stream; @@ -290,4 +315,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { MOD_EXPORT void _END_() { config.disableAutoSave(); config.save(); -} \ No newline at end of file +} From 8cefeadbd477ab4c2a9eb0c07cb2d4696a8e05c3 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 21 Jan 2024 23:48:04 +0100 Subject: [PATCH 15/65] Fix styling, modern computers can display more than 40 columns --- sink_modules/audio_sink/src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sink_modules/audio_sink/src/main.cpp b/sink_modules/audio_sink/src/main.cpp index 147e7782..c7098c89 100644 --- a/sink_modules/audio_sink/src/main.cpp +++ b/sink_modules/audio_sink/src/main.cpp @@ -164,8 +164,7 @@ public: } #if RTAUDIO_VERSION_MAJOR >= 6 - static void reportErrorsAsException(RtAudioErrorType type, - const std::string& errorText) { + static void reportErrorsAsException(RtAudioErrorType type, const std::string& errorText) { switch (type) { case RtAudioErrorType::RTAUDIO_NO_ERROR: return; From f197cf6bd9fa1caf5127f64602b8daad0c4c77fd Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 21 Jan 2024 23:49:23 +0100 Subject: [PATCH 16/65] Fix styling in the other file --- source_modules/audio_source/src/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source_modules/audio_source/src/main.cpp b/source_modules/audio_source/src/main.cpp index b7abe253..cbaf08b8 100644 --- a/source_modules/audio_source/src/main.cpp +++ b/source_modules/audio_source/src/main.cpp @@ -263,8 +263,7 @@ private: } #if RTAUDIO_VERSION_MAJOR >= 6 - static void reportErrorsAsException(RtAudioErrorType type, - const std::string& errorText) { + static void reportErrorsAsException(RtAudioErrorType type, const std::string& errorText) { switch (type) { case RtAudioErrorType::RTAUDIO_NO_ERROR: return; From 5a003e99d2b4337a2a8d24a9587d4e8019e62659 Mon Sep 17 00:00:00 2001 From: Henner Zeller Date: Sun, 21 Jan 2024 15:23:45 -0800 Subject: [PATCH 17/65] Address review comments. --- sink_modules/audio_sink/src/main.cpp | 11 +++++++---- source_modules/audio_source/src/main.cpp | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/sink_modules/audio_sink/src/main.cpp b/sink_modules/audio_sink/src/main.cpp index c7098c89..a4c82118 100644 --- a/sink_modules/audio_sink/src/main.cpp +++ b/sink_modules/audio_sink/src/main.cpp @@ -32,7 +32,7 @@ public: stereoPacker.init(_stream->sinkOut, 512); #if RTAUDIO_VERSION_MAJOR >= 6 - audio.setErrorCallback(&reportErrorsAsException); + audio.setErrorCallback(&errorCallback); #endif bool created = false; @@ -55,6 +55,9 @@ public: #endif try { info = audio.getDeviceInfo(i); +#if !defined(RTAUDIO_VERSION_MAJOR) || RTAUDIO_VERSION_MAJOR < 6 + if (!info.probed) { continue; } +#endif if (info.outputChannels == 0) { continue; } if (info.isDefaultOutput) { defaultDevId = devList.size(); } devList.push_back(info); @@ -63,7 +66,7 @@ public: txtDevList += '\0'; } catch (const std::exception& e) { - flog::error("AudioSinkModule Error getting audio device info: id={0}: {1}", i, e.what()); + flog::error("AudioSinkModule Error getting audio device ({}) info: {}", i, e.what()); } } selectByName(device); @@ -164,14 +167,14 @@ public: } #if RTAUDIO_VERSION_MAJOR >= 6 - static void reportErrorsAsException(RtAudioErrorType type, const std::string& errorText) { + static void errorCallback(RtAudioErrorType type, const std::string& errorText) { switch (type) { case RtAudioErrorType::RTAUDIO_NO_ERROR: return; case RtAudioErrorType::RTAUDIO_WARNING: case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND: case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT: - flog::warn("AudioSink: {0} ({1})", errorText, (int)type); + flog::warn("AudioSinkModule Warning: {} ({})", errorText, (int)type); break; default: throw std::runtime_error(errorText); diff --git a/source_modules/audio_source/src/main.cpp b/source_modules/audio_source/src/main.cpp index cbaf08b8..e573a818 100644 --- a/source_modules/audio_source/src/main.cpp +++ b/source_modules/audio_source/src/main.cpp @@ -36,7 +36,7 @@ public: this->name = name; #if RTAUDIO_VERSION_MAJOR >= 6 - audio.setErrorCallback(&reportErrorsAsException); + audio.setErrorCallback(&errorCallback); #endif sampleRate = 48000.0; @@ -97,6 +97,9 @@ public: // Get info auto info = audio.getDeviceInfo(i); +#if !defined(RTAUDIO_VERSION_MAJOR) || RTAUDIO_VERSION_MAJOR < 6 + if (!info.probed) { continue; } +#endif // Check that it has a stereo input if (info.inputChannels < 2) { continue; } @@ -105,7 +108,7 @@ public: devices.define(info.name, info.name, dinfo); } catch (const std::exception& e) { - flog::error("Error getting audio device info: id={0}: {1}", i, e.what()); + flog::error("Error getting audio device ({}) info: {}", i, e.what()); } } } @@ -263,14 +266,14 @@ private: } #if RTAUDIO_VERSION_MAJOR >= 6 - static void reportErrorsAsException(RtAudioErrorType type, const std::string& errorText) { + static void errorCallback(RtAudioErrorType type, const std::string& errorText) { switch (type) { case RtAudioErrorType::RTAUDIO_NO_ERROR: return; case RtAudioErrorType::RTAUDIO_WARNING: case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND: case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT: - flog::warn("AudioSource: {0} ({1})", errorText, (int)type); + flog::warn("AudioSourceModule Warning: {} ({})", errorText, (int)type); break; default: throw std::runtime_error(errorText); From 3e58d4ba31eb0e78e85afdd811d80a32249fce19 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 02:10:48 +0100 Subject: [PATCH 18/65] fix missing return statement --- core/src/dsp/chain.h | 2 ++ core/src/utils/proto/http.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/src/dsp/chain.h b/core/src/dsp/chain.h index 1d4e9509..21e2a588 100644 --- a/core/src/dsp/chain.h +++ b/core/src/dsp/chain.h @@ -163,10 +163,12 @@ namespace dsp { private: Processor* blockBefore(Processor* block) { + // TODO: This is wrong and must be fixed when I get more time for (auto& ln : links) { if (ln == block) { return NULL; } if (states[ln]) { return ln; } } + return NULL; } Processor* blockAfter(Processor* block) { diff --git a/core/src/utils/proto/http.cpp b/core/src/utils/proto/http.cpp index 8821f976..528de15b 100644 --- a/core/src/utils/proto/http.cpp +++ b/core/src/utils/proto/http.cpp @@ -257,6 +257,7 @@ namespace net::http { // Deserialize req.deserialize(respData); + return 0; // Might wanna return size instead } int Client::sendResponseHeader(ResponseHeader& resp) { @@ -274,6 +275,7 @@ namespace net::http { // Deserialize resp.deserialize(respData); + return 0; // Might wanna return size instead } int Client::sendChunkHeader(ChunkHeader& chdr) { From 691216a298bff79faac4ac49b4a17adf65758100 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 17:31:09 +0100 Subject: [PATCH 19/65] fix incorrect formatting #1288 this time in the right branch... --- .../spectran_http_source/src/spectran_http_client.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source_modules/spectran_http_source/src/spectran_http_client.cpp b/source_modules/spectran_http_source/src/spectran_http_client.cpp index 77a19328..5b6fe142 100644 --- a/source_modules/spectran_http_source/src/spectran_http_client.cpp +++ b/source_modules/spectran_http_source/src/spectran_http_client.cpp @@ -1,5 +1,6 @@ #include "spectran_http_client.h" #include +#include SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream* stream) { this->stream = stream; @@ -50,10 +51,10 @@ void SpectranHTTPClient::setCenterFrequency(uint64_t freq) { // Make request net::http::RequestHeader rqhdr(net::http::METHOD_PUT, "/control", host); char buf[1024]; - sprintf(buf, "{\"frequencyCenter\":%d,\"frequencySpan\":%d,\"type\":\"capture\"}", freq, _samplerate); + sprintf(buf, "{\"frequencyCenter\":%" PRIu64 ",\"frequencySpan\":%" PRIu64 ",\"type\":\"capture\"}", freq, _samplerate); std::string data = buf; char lenBuf[16]; - sprintf(lenBuf, "%d", data.size()); + sprintf(lenBuf, "%" PRIu64, data.size()); rqhdr.setField("Content-Length", lenBuf); controlHttp.sendRequestHeader(rqhdr); controlSock->sendstr(data); From 74ae8a45d9fe4a6cc1f82d857657148ba8bc5612 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 18:17:23 +0100 Subject: [PATCH 20/65] fix incorrect formatting again for #1288 --- .../spectran_http_source/src/spectran_http_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_modules/spectran_http_source/src/spectran_http_client.cpp b/source_modules/spectran_http_source/src/spectran_http_client.cpp index 5b6fe142..3b51f2d0 100644 --- a/source_modules/spectran_http_source/src/spectran_http_client.cpp +++ b/source_modules/spectran_http_source/src/spectran_http_client.cpp @@ -54,7 +54,7 @@ void SpectranHTTPClient::setCenterFrequency(uint64_t freq) { sprintf(buf, "{\"frequencyCenter\":%" PRIu64 ",\"frequencySpan\":%" PRIu64 ",\"type\":\"capture\"}", freq, _samplerate); std::string data = buf; char lenBuf[16]; - sprintf(lenBuf, "%" PRIu64, data.size()); + sprintf(lenBuf, "%" PRIu64, (uint64_t)data.size()); rqhdr.setField("Content-Length", lenBuf); controlHttp.sendRequestHeader(rqhdr); controlSock->sendstr(data); From 93cafe7109302a1eadba1c11fc0963349a601594 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 19:22:26 +0100 Subject: [PATCH 21/65] fix typo referenced in #1083 --- core/src/dsp/chain.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/dsp/chain.h b/core/src/dsp/chain.h index 21e2a588..a3f5dc04 100644 --- a/core/src/dsp/chain.h +++ b/core/src/dsp/chain.h @@ -93,7 +93,7 @@ namespace dsp { void disableBlock(Processor* block, Func onOutputChange) { // Check that the block is part of the chain if (!blockExists(block)) { - throw std::runtime_error("[chain] Tried to enable a block that isn't part of the chain"); + throw std::runtime_error("[chain] Tried to disable a block that isn't part of the chain"); } // If already disabled, don't do anything From 159f59b8589c6080c23e582725672ae46fd26cda Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 19:34:56 +0100 Subject: [PATCH 22/65] fix return warning for #1234 --- core/src/dsp/demod/ssb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/dsp/demod/ssb.h b/core/src/dsp/demod/ssb.h index 59ef16b6..88a43220 100644 --- a/core/src/dsp/demod/ssb.h +++ b/core/src/dsp/demod/ssb.h @@ -110,7 +110,7 @@ namespace dsp::demod { else if (_mode == Mode::LSB) { return -_bandwidth / 2.0; } - else if (_mode == Mode::DSB) { + else { return 0.0; } } From 27ab5bf3c194169ddf60ca312723fce96149cc8e Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 19:46:01 +0100 Subject: [PATCH 23/65] Fix exceptions referenced in #1287 --- core/src/command_args.cpp | 4 ++-- core/src/config.cpp | 4 ++-- core/src/utils/networking.cpp | 2 +- misc_modules/rigctl_client/src/main.cpp | 4 ++-- misc_modules/rigctl_server/src/main.cpp | 4 ++-- source_modules/airspy_source/src/main.cpp | 4 ++-- source_modules/airspyhf_source/src/main.cpp | 4 ++-- source_modules/audio_source/src/main.cpp | 6 +++--- source_modules/file_source/src/main.cpp | 4 ++-- source_modules/rfspace_source/src/main.cpp | 4 ++-- source_modules/rtl_tcp_source/src/main.cpp | 4 ++-- source_modules/sdrpp_server_source/src/main.cpp | 4 ++-- source_modules/spyserver_source/src/main.cpp | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/core/src/command_args.cpp b/core/src/command_args.cpp index a25120fa..9b3174ce 100644 --- a/core/src/command_args.cpp +++ b/core/src/command_args.cpp @@ -88,7 +88,7 @@ int CommandArgsParser::parse(int argc, char* argv[]) { try { carg.ival = std::stoi(arg); } - catch (std::exception e) { + catch (const std::exception& e) { printf("Invalid argument, failed to parse integer\n"); showHelp(); return -1; @@ -98,7 +98,7 @@ int CommandArgsParser::parse(int argc, char* argv[]) { try { carg.fval = std::stod(arg); } - catch (std::exception e) { + catch (const std::exception& e) { printf("Invalid argument, failed to parse float\n"); showHelp(); return -1; diff --git a/core/src/config.cpp b/core/src/config.cpp index dbfdbe64..6de1888f 100644 --- a/core/src/config.cpp +++ b/core/src/config.cpp @@ -36,8 +36,8 @@ void ConfigManager::load(json def, bool lock) { file >> conf; file.close(); } - catch (std::exception e) { - flog::error("Config file '{0}' is corrupted, resetting it", path); + catch (const std::exception& e) { + flog::error("Config file '{}' is corrupted, resetting it: {}", path, e.what()); conf = def; save(false); } diff --git a/core/src/utils/networking.cpp b/core/src/utils/networking.cpp index a5e727d5..8e89c809 100644 --- a/core/src/utils/networking.cpp +++ b/core/src/utils/networking.cpp @@ -320,7 +320,7 @@ namespace net { } entry.handler(std::move(client), entry.ctx); } - catch (std::exception e) { + catch (const std::exception& e) { listening = false; return; } diff --git a/misc_modules/rigctl_client/src/main.cpp b/misc_modules/rigctl_client/src/main.cpp index 3df6c077..83350ba5 100644 --- a/misc_modules/rigctl_client/src/main.cpp +++ b/misc_modules/rigctl_client/src/main.cpp @@ -80,8 +80,8 @@ public: try { client = net::rigctl::connect(host, port); } - catch (std::exception e) { - flog::error("Could not connect: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Could not connect: {}", e.what()); return; } diff --git a/misc_modules/rigctl_server/src/main.cpp b/misc_modules/rigctl_server/src/main.cpp index d0e4db8a..3673d4dd 100644 --- a/misc_modules/rigctl_server/src/main.cpp +++ b/misc_modules/rigctl_server/src/main.cpp @@ -200,8 +200,8 @@ private: listener = net::listen(hostname, port); listener->acceptAsync(clientHandler, this); } - catch (std::exception e) { - flog::error("Could not start rigctl server: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Could not start rigctl server: {}", e.what()); } } diff --git a/source_modules/airspy_source/src/main.cpp b/source_modules/airspy_source/src/main.cpp index 5457e1dd..28474820 100644 --- a/source_modules/airspy_source/src/main.cpp +++ b/source_modules/airspy_source/src/main.cpp @@ -141,10 +141,10 @@ public: return; } } - catch (std::exception e) { + catch (const std::exception& e) { char buf[1024]; sprintf(buf, "%016" PRIX64, serial); - flog::error("Could not open Airspy {0}", buf); + flog::error("Could not open Airspy {}", buf); } selectedSerial = serial; diff --git a/source_modules/airspyhf_source/src/main.cpp b/source_modules/airspyhf_source/src/main.cpp index 066866a2..cd03b3fe 100644 --- a/source_modules/airspyhf_source/src/main.cpp +++ b/source_modules/airspyhf_source/src/main.cpp @@ -144,10 +144,10 @@ public: return; } } - catch (std::exception e) { + catch (const std::exception& e) { char buf[1024]; sprintf(buf, "%016" PRIX64, serial); - flog::error("Could not open Airspy HF+ {0}", buf); + flog::error("Could not open Airspy HF+ {}", buf); } selectedSerial = serial; diff --git a/source_modules/audio_source/src/main.cpp b/source_modules/audio_source/src/main.cpp index e573a818..df4701e8 100644 --- a/source_modules/audio_source/src/main.cpp +++ b/source_modules/audio_source/src/main.cpp @@ -200,11 +200,11 @@ private: _this->audio.startStream(); _this->running = true; } - catch (std::exception e) { - flog::error("Error opening audio device: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Error opening audio device: {}", e.what()); } - flog::info("AudioSourceModule '{0}': Start!", _this->name); + flog::info("AudioSourceModule '{}': Start!", _this->name); } static void stop(void* ctx) { diff --git a/source_modules/file_source/src/main.cpp b/source_modules/file_source/src/main.cpp index 0628a412..2b51639a 100644 --- a/source_modules/file_source/src/main.cpp +++ b/source_modules/file_source/src/main.cpp @@ -139,8 +139,8 @@ private: //gui::freqSelect.maxFreq = _this->centerFreq + (_this->sampleRate/2); //gui::freqSelect.limitFreq = true; } - catch (std::exception& e) { - flog::error("Error: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Error: {}", e.what()); } config.acquire(); config.conf["path"] = _this->fileSelect.path; diff --git a/source_modules/rfspace_source/src/main.cpp b/source_modules/rfspace_source/src/main.cpp index d28f1ddb..e0078efb 100644 --- a/source_modules/rfspace_source/src/main.cpp +++ b/source_modules/rfspace_source/src/main.cpp @@ -154,8 +154,8 @@ private: _this->client = rfspace::connect(_this->hostname, _this->port, &_this->stream); _this->deviceInit(); } - catch (std::exception e) { - flog::error("Could not connect to SDR: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Could not connect to SDR: {}", e.what()); } } else if (connected && SmGui::Button("Disconnect##rfspace_source")) { diff --git a/source_modules/rtl_tcp_source/src/main.cpp b/source_modules/rtl_tcp_source/src/main.cpp index 7fe58b94..a1afea57 100644 --- a/source_modules/rtl_tcp_source/src/main.cpp +++ b/source_modules/rtl_tcp_source/src/main.cpp @@ -132,8 +132,8 @@ private: try { _this->client = rtltcp::connect(&_this->stream, _this->ip, _this->port); } - catch (std::exception e) { - flog::error("Could connect to RTL-TCP server: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Could connect to RTL-TCP server: {}", e.what()); return; } diff --git a/source_modules/sdrpp_server_source/src/main.cpp b/source_modules/sdrpp_server_source/src/main.cpp index af4cfcc6..38c187de 100644 --- a/source_modules/sdrpp_server_source/src/main.cpp +++ b/source_modules/sdrpp_server_source/src/main.cpp @@ -233,8 +233,8 @@ private: client = server::connect(hostname, port, &stream); deviceInit(); } - catch (std::exception e) { - flog::error("Could not connect to SDR: {0}", e.what()); + catch (const std::exception& e) { + flog::error("Could not connect to SDR: {}", e.what()); if (!strcmp(e.what(), "Server busy")) { serverBusy = true; } } } diff --git a/source_modules/spyserver_source/src/main.cpp b/source_modules/spyserver_source/src/main.cpp index 59b2214f..d4f87559 100644 --- a/source_modules/spyserver_source/src/main.cpp +++ b/source_modules/spyserver_source/src/main.cpp @@ -283,8 +283,8 @@ private: flog::info("Connected to server"); } } - catch (std::exception e) { - flog::error("Could not connect to spyserver {0}", e.what()); + catch (const std::exception& e) { + flog::error("Could not connect to spyserver {}", e.what()); } } From 854ed89b824928201b5aa72d1497575000be377f Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 20:30:53 +0100 Subject: [PATCH 24/65] prepare for merge back --- .github/workflows/build_all.yml | 2 +- CMakeLists.txt | 2 +- src/main.cpp | 24 ------------------------ 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 178b93f8..3654b8f0 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -122,7 +122,7 @@ jobs: - name: Prepare CMake working-directory: ${{runner.workspace}}/build - run: cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release + run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release - name: Build working-directory: ${{runner.workspace}}/build diff --git a/CMakeLists.txt b/CMakeLists.txt index e5cce405..a9c67193 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ if (${CMAKE_BUILD_TYPE} MATCHES "Debug") if (MSVC) set(SDRPP_COMPILER_FLAGS /std:c++17 /EHsc) elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(SDRPP_COMPILER_FLAGS -g -Og -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup -fsanitize=address) + set(SDRPP_COMPILER_FLAGS -g -Og -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup) else () set(SDRPP_COMPILER_FLAGS -g -Og -std=c++17) endif () diff --git a/src/main.cpp b/src/main.cpp index 5e6e1838..7bc9d13d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,30 +1,6 @@ #include #include -#include - -extern char **environ; int main(int argc, char* argv[], char* envp[]) { - FILE* dump = fopen("/Users/ryzerth/dump.txt", "wb"); - - char buf[1024]; - fprintf(dump, "Working directory: %s\n\n", getcwd(buf, 1023)); - - fprintf(dump, "Arguments:\n"); - fprintf(dump, "----------\n"); - for (int i = 0; i < argc; i++) { - fprintf(dump, "%d: '%s'\n", i, argv[i]); - } - fprintf(dump, "\n"); - - fprintf(dump, "Environment Variables:\n"); - fprintf(dump, "----------\n"); - for (char** env = environ; *env; env++) { - fprintf(dump, "%s\n", *env); - } - fprintf(dump, "\n"); - - fclose(dump); - return sdrpp_main(argc, argv); } \ No newline at end of file From 4b77d8c395c2794641434d60496f0288be4afb90 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 22 Jan 2024 20:31:18 +0100 Subject: [PATCH 25/65] prepare for merge back part 2, electric boogaloo --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 7bc9d13d..9fc1638c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ #include #include -int main(int argc, char* argv[], char* envp[]) { +int main(int argc, char* argv[]) { return sdrpp_main(argc, argv); } \ No newline at end of file From 708f74e179222976cb11301f29e9b8e8fc063434 Mon Sep 17 00:00:00 2001 From: Davide Rovelli <103165301+daviderud@users.noreply.github.com> Date: Wed, 24 Jan 2024 06:14:00 +0100 Subject: [PATCH 26/65] additions and corrections --- root/res/bandplans/netherlands.json | 448 +++++++++++++++++++++++++--- 1 file changed, 410 insertions(+), 38 deletions(-) diff --git a/root/res/bandplans/netherlands.json b/root/res/bandplans/netherlands.json index da226678..3a7908b4 100644 --- a/root/res/bandplans/netherlands.json +++ b/root/res/bandplans/netherlands.json @@ -103,11 +103,29 @@ }, { "start": 283500, - "end": 526500, + "end": 472000, "type": "aviation", "name": "Aeronautical Radionavigation / Maritime" }, { + "start": 472000, + "end": 475000, + "type": "amateur", + "name": "|635m Ham Band CW" + }, + { + "start": 475000, + "end": 479000, + "type": "amateur1", + "name": "635m Ham Band CW, Digimodes|" + }, + { + "start": 479000, + "end": 526500, + "type": "aviation", + "name": "Aeronautical Radionavigation / Maritime" + }, + { "start": 526500, "end": 1606500, "type": "broadcast", @@ -284,7 +302,7 @@ { "start": 3754500, "end": 3757500, - "type": "amateur1", + "type": "utility", "name": "The Pip (Night)" }, { @@ -744,19 +762,31 @@ "name": "Ch. 2 (60m) (Not NL)" }, { - "start": 5351500, - "end": 5366500, + "start": 5349400, + "end": 5351500, "type": "amateur1", - "name": "60m Ham Band NL" - }, + "name": "60m Ham Band (Not NL)" + }, { - "start": 5357000, - "end": 5359900, + "start": 5351500, + "end": 5354000, "type": "amateur", - "name": "Ch. 3 (60m) (Not NL)" + "name": "| 60m Ham Band CW" }, { - "start": 5359900, + "start": 5354000, + "end": 5366000, + "type": "amateur1", + "name": "60m HAM Alle modes (USB)" + }, + { + "start": 5366000, + "end": 5366500, + "type": "amateur", + "name": "60m HAM Weak NB modes |" + }, + { + "start": 5366500, "end": 5371500, "type": "amateur1", "name": "60m Ham Band (Not NL)" @@ -3047,17 +3077,11 @@ "type": "utility", "name": "Radio Astronomy" }, - { - "start": 2300000000, - "end": 2310000000, - "type": "amateur", - "name": "|13cm Ham Band" - }, { "start": 2320000000, "end": 2332500000, - "type": "satellite", - "name": "Satellite Digital Audio Radio Service (SDARS)" + "type": "amateur", + "name": "|13cm Ham Band Satellite Digital Audio Radio Service (SDARS)" }, { "start": 2332500000, @@ -3067,21 +3091,9 @@ }, { "start": 2345000000, - "end": 2360000000, - "type": "aviation", - "name": "Aviation Service and the Wireless Communications Service (WCS)" - }, - { - "start": 2360000000, - "end": 2390000000, - "type": "aviation", - "name": "Aviation" - }, - { - "start": 2390000000, "end": 2393750000, "type": "amateur", - "name": "13cm Ham Band (Upper)| Analog and Digital" + "name": "13cm Ham Band Analog and Digital" }, { "start": 2393750000, @@ -3093,25 +3105,25 @@ "start": 2394750000, "end": 2400000000, "type": "amateur", - "name": "Analog and Digital 13 cm HAM Band|" + "name": "Analog and Digital 13cm HAM Band" }, { "start": 2400000000, "end": 2401000000, "type": "utility", - "name": "Shared Satellite and 13cm ISM" + "name": "Shared Satellite, 13cm ISM and Ham" }, { "start": 2401000000, "end": 2410000000, "type": "utility", - "name": "WiFi shared with Satellite and 13cm ISM" + "name": "WiFi shared with Satellite, 13cm ISM and Ham" }, { "start": 2410000000, "end": 2411900000, "type": "utility", - "name": "WiFi shared with Broadband Modes and 13cm ISM" + "name": "WiFi shared with Broadband Modes, 13cm ISM and Ham" }, { "start": 2411900000, @@ -3122,8 +3134,8 @@ { "start": 2412100000, "end": 2450000000, - "type": "utility", - "name": "WiFi shared with Broadband Modes 13cm ISM |13cm Ham Band" + "type": "amateur", + "name": "WiFi shared with Broadband Modes 13cm ISM, 13cm Ham Band|" }, { "start": 2450000000, @@ -3172,6 +3184,366 @@ "end": 2500000000, "type": "utility", "name": "ISM Band (13cm)" + }, + { + "name": "IMT", + "type": "cellular", + "start": 2500000000, + "end": 2544500000 + }, + { + "name": "Radioastronomy", + "type": "utility", + "start": 2690000000, + "end": 2700000000 + }, + { + "name": "Radar meteo", + "type": "military", + "start": 2700000000, + "end": 2900000000 + }, + { + "name": "Maritime Radar", + "type": "marine", + "start": 2900000000, + "end": 3400000000 + }, + { + "name": "Radio Ham 9 cm band", + "type": "amateur", + "start": 3400000000, + "end": 3475000000 + }, + { + "name": "Digital Networks", + "type": "utility", + "start": 3475000000, + "end": 4200000000 + }, + { + "name": "Altimeters", + "type": "aviation", + "start": 4200000000, + "end": 4400000000 + }, + { + "name": "Feeder link", + "type": "satellite", + "start": 5150000000, + "end": 5250000000 + }, + { + "name": "Digital networks and 802.11", + "type": "utility", + "start": 5250000000, + "end": 5650000000 + }, + { + "name": "Radio Ham 6 cm band", + "type": "amateur", + "start": 5650000000, + "end": 5850000000 + }, + { + "name": "Digital Networks and LPR", + "type": "utility", + "start": 5925000000, + "end": 7750000000 + }, + { + "name": "LPR", + "type": "utility", + "start": 7750000000, + "end": 7975000000 + }, + { + "name": "Remote sensing", + "type": "utility", + "start": 7975000000, + "end": 8215000000 + }, + { + "name": "TLPR and SRD", + "type": "utility", + "start": 8215000000, + "end": 8650000000 + }, + { + "name": "Radar Doppler", + "type": "aviation", + "start": 8650000000, + "end": 8850000000 + }, + { + "name": "Maritime Radar", + "type": "marine", + "start": 8850000000, + "end": 9000000000 + }, + { + "name": "Radar and transponder SART", + "type": "marine", + "start": 9000000000, + "end": 9500000000 + }, + { + "name": "TLPR and SRD", + "type": "utility", + "start": 9500000000, + "end": 10000000000 + }, + { + "name": "Radio Ham 3 cm band", + "type": "amateur", + "start": 10000000000, + "end": 10500000000 + }, + { + "name": "Point to point TV networks", + "type": "utility", + "start": 10500000000, + "end": 10680000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 10680000000, + "end": 11700000000 + }, + { + "name": "TV satellite", + "type": "satellite", + "start": 11700000000, + "end": 12500000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 12500000000, + "end": 13250000000 + }, + { + "name": "Satellite Uplink", + "type": "satellite", + "start": 14000000000, + "end": 14500000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 14500000000, + "end": 14620000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 15230000000, + "end": 15350000000 + }, + { + "name": "Digital network (poit to point)", + "type": "utility", + "start": 17100000000, + "end": 19300000000 + }, + { + "name": "Feeder link", + "type": "satellite", + "start": 19300000000, + "end": 19700000000 + }, + { + "name": "HEST, LEST, ESIM, ESOMP", + "type": "satellite", + "start": 19700000000, + "end": 20200000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 22000000000, + "end": 22330000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 22674750000, + "end": 2283350000 + }, + { + "name": "Digital network (fixed), SAP/SAB", + "type": "utility", + "start": 22926750000, + "end": 23150000000 + }, + { + "name": "Digital network (fixed)", + "type": "utility", + "start": 23150000000, + "end": 23338000000 + }, + { + "name": "ISM, SRD and LPR", + "type": "utility", + "start": 24000000000, + "end": 24450000000 + }, + { + "name": "Digital network (point to point, multipoint)", + "type": "utility", + "start": 24450000000, + "end": 25109000000 + }, + { + "name": "LPR, SRD and SRR", + "type": "utility", + "start": 25109000000, + "end": 2544500000 + }, + { + "name": "Network (point to point, multipoint)", + "type": "utility", + "start": 2544500000, + "end": 2611700000 + }, + { + "name": "LPR, SRD and SRR", + "type": "utility", + "start": 2611700000, + "end": 2650000000 + }, + { + "name": "Terrestrial electric utility", + "type": "utility", + "start": 26500000000, + "end": 27500000000 + }, + { + "name": "Network (point to point, multipoint)", + "type": "utility", + "start": 27500000000, + "end": 29100000000 + }, + { + "name": "Network (point to point, multipoint)", + "type": "utility", + "start": 29100000000, + "end": 29500000000 + }, + { + "name": "Network (point to point, multipoint)", + "type": "utility", + "start": 31000000000, + "end": 31300000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 31983000000, + "end": 32599000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 32795000000, + "end": 33400000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 37338000000, + "end": 38300000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 38590000000, + "end": 39500000000 + }, + { + "name": "FWS systems (fixed)", + "type": "utility", + "start": 40500000000, + "end": 43500000000 + }, + { + "name": "Radio Ham 6 mm band", + "type": "amateur", + "start": 47000000000, + "end": 47200000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 51400000000, + "end": 52600000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 55780000000, + "end": 61000000000 + }, + { + "name": "ISM", + "type": "utility", + "start": 61000000000, + "end": 64000000000 + }, + { + "name": "Network (high density, fixed)", + "type": "utility", + "start": 64000000000, + "end": 66000000000 + }, + { + "name": "Links (high density, fixed)", + "type": "utility", + "start": 71000000000, + "end": 74000000000 + }, + { + "name": "LPR, SRD, SRR, TLPR, vehichle radar", + "type": "utility", + "start": 74000000000, + "end": 76500000000 + }, + { + "name": "Radio Ham 4 mm band", + "type": "amateur", + "start": 76500000000, + "end": 81500000000 + }, + { + "name": "Links (high density, fixed)", + "type": "utility", + "start": 84000000000, + "end": 86000000000 + }, + { + "name": "ISM", + "type": "utility", + "start": 120200000000, + "end": 122250000000 + }, + { + "name": "Radio Ham 2.5 mm band", + "type": "amateur", + "start": 122250000000, + "end": 123000000000 + }, + { + "name": "Radio Ham 2 mm band", + "type": "amateur", + "start": 134000000000, + "end": 141000000000 + }, + { + "name": "Radio Ham 1 mm band", + "type": "amateur", + "start": 241000000000, + "end": 250000000000 } - ] + ] } \ No newline at end of file From af8c085d43589b7a3440e8666e4e5433c0522198 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 24 Jan 2024 18:14:29 +0100 Subject: [PATCH 27/65] switch osx to macos --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 36312fd9..833512c7 100644 --- a/readme.md +++ b/readme.md @@ -15,7 +15,7 @@ SDR++ is a cross-platform and open source SDR software with the aim of being blo * Multi VFO * Wide hardware support (both through SoapySDR and dedicated modules) * SIMD accelerated DSP -* Cross-platform (Windows, Linux, OSX and BSD) +* Cross-platform (Windows, Linux, MacOS and BSD) * Full waterfall update when possible. Makes browsing signals easier and more pleasant * Modular design (easily write your own plugins) From 118e56897c1ef37f3f41144d7139e0cb9f963731 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 25 Jan 2024 15:41:23 +0100 Subject: [PATCH 28/65] Add ubuntu mantic CI build --- .github/workflows/build_all.yml | 25 +++++++++++++++++- docker_builds/ubuntu_mantic/Dockerfile | 4 +++ docker_builds/ubuntu_mantic/do_build.sh | 35 +++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 docker_builds/ubuntu_mantic/Dockerfile create mode 100644 docker_builds/ubuntu_mantic/do_build.sh diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 3654b8f0..c5dc6b7a 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -269,6 +269,28 @@ jobs: with: name: sdrpp_ubuntu_jammy_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb + + build_ubuntu_mantic: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Create Docker Image + run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && docker build . --tag sdrpp_build + + - name: Run Container + run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh + + - name: Recover Deb Archive + working-directory: ${{runner.workspace}} + run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ + + - name: Save Deb Archive + uses: actions/upload-artifact@v3 + with: + name: sdrpp_ubuntu_mantic_amd64 + path: ${{runner.workspace}}/sdrpp_debian_amd64.deb build_raspios_bullseye_armhf: runs-on: ARM @@ -325,7 +347,7 @@ jobs: path: ${{runner.workspace}}/sdrpp.apk create_full_archive: - needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_raspios_bullseye_armhf', 'build_android'] + needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_ubuntu_mantic', 'build_raspios_bullseye_armhf', 'build_android'] runs-on: ubuntu-latest steps: @@ -343,6 +365,7 @@ jobs: mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb && mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb && mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb && + mv sdrpp_ubuntu_mantic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_mantic_amd64.deb && mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb && mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk diff --git a/docker_builds/ubuntu_mantic/Dockerfile b/docker_builds/ubuntu_mantic/Dockerfile new file mode 100644 index 00000000..629e263e --- /dev/null +++ b/docker_builds/ubuntu_mantic/Dockerfile @@ -0,0 +1,4 @@ +FROM ubuntu:mantic +ENV DEBIAN_FRONTEND=noninteractive +COPY do_build.sh /root +RUN chmod +x /root/do_build.sh \ No newline at end of file diff --git a/docker_builds/ubuntu_mantic/do_build.sh b/docker_builds/ubuntu_mantic/do_build.sh new file mode 100644 index 00000000..b67517a9 --- /dev/null +++ b/docker_builds/ubuntu_mantic/do_build.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e +cd /root + +# Install dependencies and tools +apt update +apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ + libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ + libcodec2-dev autoconf libtool xxd + +# Install SDRPlay libraries +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run +7z x ./SDRplay_RSP_API-Linux-3.07.1.run +7z x ./SDRplay_RSP_API-Linux-3.07.1 +cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +cp inc/* /usr/include/ + +# Install libperseus +git clone https://github.com/Microtelecom/libperseus-sdr +cd libperseus-sdr +autoreconf -i +./configure +make +make install +ldconfig +cd .. + +cd SDRPlusPlus +mkdir build +cd build +cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON +make VERBOSE=1 -j2 + +cd .. +sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev' \ No newline at end of file From 97c1a132a575c71c8dc46c3be7f0e66781e83c00 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 25 Jan 2024 19:48:04 +0100 Subject: [PATCH 29/65] move zoom waterfall's zoom function in the cpp file to avoid annoying recomps --- core/src/gui/widgets/waterfall.cpp | 33 +++++++++++++++++++++++++++--- core/src/gui/widgets/waterfall.h | 27 ------------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/core/src/gui/widgets/waterfall.cpp b/core/src/gui/widgets/waterfall.cpp index de31b524..f2556905 100644 --- a/core/src/gui/widgets/waterfall.cpp +++ b/core/src/gui/widgets/waterfall.cpp @@ -62,6 +62,33 @@ inline void printAndScale(double freq, char* buf) { } } +inline void doZoom(int offset, int width, int inSize, int outSize, float* in, float* out) { + // NOTE: REMOVE THAT SHIT, IT'S JUST A HACKY FIX + if (offset < 0) { + offset = 0; + } + if (width > 524288) { + width = 524288; + } + + float factor = (float)width / (float)outSize; + float sFactor = ceilf(factor); + float uFactor; + float id = offset; + float maxVal; + int sId; + for (int i = 0; i < outSize; i++) { + maxVal = -INFINITY; + sId = (int)id; + uFactor = (sId + sFactor > inSize) ? sFactor - ((sId + sFactor) - inSize) : sFactor; + for (int j = 0; j < uFactor; j++) { + if (in[sId + j] > maxVal) { maxVal = in[sId + j]; } + } + out[i] = maxVal; + id += factor; + } +} + namespace ImGui { WaterFall::WaterFall() { fftMin = -70.0; @@ -586,7 +613,7 @@ namespace ImGui { for (int i = 0; i < count; i++) { drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize; drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2); - doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[((i + currentFFTLine) % waterfallHeight) * rawFFTSize], tempData); + doZoom(drawDataStart, drawDataSize, rawFFTSize, dataWidth, &rawFFTs[((i + currentFFTLine) % waterfallHeight) * rawFFTSize], tempData); for (int j = 0; j < dataWidth; j++) { pixel = (std::clamp(tempData[j], waterfallMin, waterfallMax) - waterfallMin) / dataRange; waterfallFb[(i * dataWidth) + j] = waterfallPallet[(int)(pixel * (WATERFALL_RESOLUTION - 1))]; @@ -867,7 +894,7 @@ namespace ImGui { int drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2); if (waterfallVisible) { - doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT); + doZoom(drawDataStart, drawDataSize, rawFFTSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT); memmove(&waterfallFb[dataWidth], waterfallFb, dataWidth * (waterfallHeight - 1) * sizeof(uint32_t)); float pixel; float dataRange = waterfallMax - waterfallMin; @@ -879,7 +906,7 @@ namespace ImGui { waterfallUpdate = true; } else { - doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs, latestFFT); + doZoom(drawDataStart, drawDataSize, rawFFTSize, dataWidth, rawFFTs, latestFFT); fftLines = 1; } diff --git a/core/src/gui/widgets/waterfall.h b/core/src/gui/widgets/waterfall.h index a54df011..fd8c2198 100644 --- a/core/src/gui/widgets/waterfall.h +++ b/core/src/gui/widgets/waterfall.h @@ -90,33 +90,6 @@ namespace ImGui { float* getFFTBuffer(); void pushFFT(); - inline void doZoom(int offset, int width, int outWidth, float* data, float* out) { - // NOTE: REMOVE THAT SHIT, IT'S JUST A HACKY FIX - if (offset < 0) { - offset = 0; - } - if (width > 524288) { - width = 524288; - } - - float factor = (float)width / (float)outWidth; - float sFactor = ceilf(factor); - float uFactor; - float id = offset; - float maxVal; - int sId; - for (int i = 0; i < outWidth; i++) { - maxVal = -INFINITY; - sId = (int)id; - uFactor = (sId + sFactor > rawFFTSize) ? sFactor - ((sId + sFactor) - rawFFTSize) : sFactor; - for (int j = 0; j < uFactor; j++) { - if (data[sId + j] > maxVal) { maxVal = data[sId + j]; } - } - out[i] = maxVal; - id += factor; - } - } - void updatePallette(float colors[][3], int colorCount); void updatePalletteFromArray(float* colors, int colorCount); From 3aa167701ea4a7edb5060954a2c3e8a0e89415c9 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 25 Jan 2024 20:27:04 +0100 Subject: [PATCH 30/65] fix networking library to allow multicast --- core/src/utils/net.cpp | 31 ++++++++++++++++++++++++------- core/src/utils/net.h | 20 +++++++++++++------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/core/src/utils/net.cpp b/core/src/utils/net.cpp index 3ce6b5c2..4095e7ea 100644 --- a/core/src/utils/net.cpp +++ b/core/src/utils/net.cpp @@ -86,14 +86,14 @@ namespace net { addr.sin_port = htons(port); } - std::string Address::getIPStr() { + std::string Address::getIPStr() const { char buf[128]; IP_t ip = getIP(); sprintf(buf, "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); return buf; } - IP_t Address::getIP() { + IP_t Address::getIP() const { return htonl(addr.sin_addr.s_addr); } @@ -101,7 +101,7 @@ namespace net { addr.sin_addr.s_addr = htonl(ip); } - int Address::getPort() { + int Address::getPort() const { return htons(addr.sin_port); } @@ -109,6 +109,11 @@ namespace net { addr.sin_port = htons(port); } + bool Address::isMulticast() const { + IP_t ip = getIP(); + return (ip >> 28) == 0b1110; + } + // === Socket functions === Socket::Socket(SockHandle_t sock, const Address* raddr) { @@ -160,8 +165,8 @@ namespace net { // Set timeout timeval tv; - tv.tv_sec = 0; - tv.tv_usec = timeout * 1000; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - tv.tv_sec*1000) * 1000; // Wait for data int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL); @@ -225,8 +230,8 @@ namespace net { // Define timeout timeval tv; - tv.tv_sec = 0; - tv.tv_usec = timeout * 1000; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout - tv.tv_sec*1000) * 1000; // Wait for data or error if (timeout != NONBLOCKING) { @@ -382,6 +387,18 @@ namespace net { // Create socket SockHandle_t s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + // If the remote address is multicast, allow multicast connections +#ifdef _WIN32 + const char enable = raddr.isMulticast(); +#else + int enable = raddr.isMulticast(); +#endif + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + closeSocket(s); + throw std::runtime_error("Could not configure socket"); + return NULL; + } + // Bind socket to local port if (bind(s, (sockaddr*)&laddr.addr, sizeof(sockaddr_in))) { closeSocket(s); diff --git a/core/src/utils/net.h b/core/src/utils/net.h index 2196fc09..a2137441 100644 --- a/core/src/utils/net.h +++ b/core/src/utils/net.h @@ -67,13 +67,13 @@ namespace net { * Get the IP address. * @return IP address in standard string format. */ - std::string getIPStr(); + std::string getIPStr() const; /** * Get the IP address. * @return IP address in host byte order. */ - IP_t getIP(); + IP_t getIP() const; /** * Set the IP address. @@ -85,7 +85,7 @@ namespace net { * Get the TCP/UDP port. * @return TCP/UDP port number. */ - int getPort(); + int getPort() const; /** * Set the TCP/UDP port. @@ -93,6 +93,12 @@ namespace net { */ void setPort(int port); + /** + * Check if the address is multicast. + * @return True if multicast, false if not. + */ + bool isMulticast() const; + struct sockaddr_in addr; }; @@ -246,7 +252,7 @@ namespace net { /** * Create UDP socket. - * @param raddr Remote address. + * @param raddr Remote address. Set to a multicast address to allow multicast. * @param laddr Local address to bind the socket to. * @return Socket instance on success, Throws runtime_error otherwise. */ @@ -254,7 +260,7 @@ namespace net { /** * Create UDP socket. - * @param rhost Remote hostname or IP address. + * @param rhost Remote hostname or IP address. Set to a multicast address to allow multicast. * @param rport Remote port. * @param laddr Local address to bind the socket to. * @return Socket instance on success, Throws runtime_error otherwise. @@ -263,7 +269,7 @@ namespace net { /** * Create UDP socket. - * @param raddr Remote address. + * @param raddr Remote address. Set to a multicast address to allow multicast. * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). * @return Socket instance on success, Throws runtime_error otherwise. @@ -272,7 +278,7 @@ namespace net { /** * Create UDP socket. - * @param rhost Remote hostname or IP address. + * @param rhost Remote hostname or IP address. Set to a multicast address to allow multicast. * @param rport Remote port. * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). From 68bf2fc16f84312ceabe6d4732abd2c3040c03b3 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 25 Jan 2024 21:34:13 +0100 Subject: [PATCH 31/65] fix bad network lib broadcast implementation --- core/src/utils/net.cpp | 31 +++++++++++++------------------ core/src/utils/net.h | 18 ++++++------------ 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/core/src/utils/net.cpp b/core/src/utils/net.cpp index 4095e7ea..4df9d9ff 100644 --- a/core/src/utils/net.cpp +++ b/core/src/utils/net.cpp @@ -109,11 +109,6 @@ namespace net { addr.sin_port = htons(port); } - bool Address::isMulticast() const { - IP_t ip = getIP(); - return (ip >> 28) == 0b1110; - } - // === Socket functions === Socket::Socket(SockHandle_t sock, const Address* raddr) { @@ -380,7 +375,7 @@ namespace net { return connect(Address(host, port)); } - std::shared_ptr openudp(const Address& raddr, const Address& laddr) { + std::shared_ptr openudp(const Address& raddr, const Address& laddr, bool allowBroadcast) { // Init library if needed init(); @@ -388,12 +383,12 @@ namespace net { SockHandle_t s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // If the remote address is multicast, allow multicast connections -#ifdef _WIN32 - const char enable = raddr.isMulticast(); -#else - int enable = raddr.isMulticast(); -#endif - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + #ifdef _WIN32 + const char enable = allowBroadcast; + #else + int enable = allowBroadcast; + #endif + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int)) < 0) { closeSocket(s); throw std::runtime_error("Could not configure socket"); return NULL; @@ -410,15 +405,15 @@ namespace net { return std::make_shared(s, &raddr); } - std::shared_ptr openudp(std::string rhost, int rport, const Address& laddr) { - return openudp(Address(rhost, rport), laddr); + std::shared_ptr openudp(std::string rhost, int rport, const Address& laddr, bool allowBroadcast) { + return openudp(Address(rhost, rport), laddr, allowBroadcast); } - std::shared_ptr openudp(const Address& raddr, std::string lhost, int lport) { - return openudp(raddr, Address(lhost, lport)); + std::shared_ptr openudp(const Address& raddr, std::string lhost, int lport, bool allowBroadcast) { + return openudp(raddr, Address(lhost, lport), allowBroadcast); } - std::shared_ptr openudp(std::string rhost, int rport, std::string lhost, int lport) { - return openudp(Address(rhost, rport), Address(lhost, lport)); + std::shared_ptr openudp(std::string rhost, int rport, std::string lhost, int lport, bool allowBroadcast) { + return openudp(Address(rhost, rport), Address(lhost, lport), allowBroadcast); } } diff --git a/core/src/utils/net.h b/core/src/utils/net.h index a2137441..72d6d073 100644 --- a/core/src/utils/net.h +++ b/core/src/utils/net.h @@ -93,12 +93,6 @@ namespace net { */ void setPort(int port); - /** - * Check if the address is multicast. - * @return True if multicast, false if not. - */ - bool isMulticast() const; - struct sockaddr_in addr; }; @@ -256,7 +250,7 @@ namespace net { * @param laddr Local address to bind the socket to. * @return Socket instance on success, Throws runtime_error otherwise. */ - std::shared_ptr openudp(const Address& raddr, const Address& laddr); + std::shared_ptr openudp(const Address& raddr, const Address& laddr, bool allowBroadcast = false); /** * Create UDP socket. @@ -265,24 +259,24 @@ namespace net { * @param laddr Local address to bind the socket to. * @return Socket instance on success, Throws runtime_error otherwise. */ - std::shared_ptr openudp(std::string rhost, int rport, const Address& laddr); + std::shared_ptr openudp(std::string rhost, int rport, const Address& laddr, bool allowBroadcast = false); /** * Create UDP socket. - * @param raddr Remote address. Set to a multicast address to allow multicast. + * @param raddr Remote address. Set to a multicast or broadcast address to allow multicast. * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). * @return Socket instance on success, Throws runtime_error otherwise. */ - std::shared_ptr openudp(const Address& raddr, std::string lhost = "0.0.0.0", int lport = 0); + std::shared_ptr openudp(const Address& raddr, std::string lhost = "0.0.0.0", int lport = 0, bool allowBroadcast = false); /** * Create UDP socket. - * @param rhost Remote hostname or IP address. Set to a multicast address to allow multicast. + * @param rhost Remote hostname or IP address. Set to a multicast or broadcast address to allow multicast. * @param rport Remote port. * @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any). * @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically). * @return Socket instance on success, Throws runtime_error otherwise. */ - std::shared_ptr openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0); + std::shared_ptr openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0, bool allowBroadcast = false); } \ No newline at end of file From 255988ee46c510222f2cc47dcdeafa2f15d65e7d Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 25 Jan 2024 22:27:59 +0100 Subject: [PATCH 32/65] fix hermes lite enumeration issues and fix copy paste fail in network library --- core/src/utils/net.cpp | 2 +- source_modules/hermes_source/src/hermes.cpp | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/src/utils/net.cpp b/core/src/utils/net.cpp index 4df9d9ff..99ff8390 100644 --- a/core/src/utils/net.cpp +++ b/core/src/utils/net.cpp @@ -390,7 +390,7 @@ namespace net { #endif if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int)) < 0) { closeSocket(s); - throw std::runtime_error("Could not configure socket"); + throw std::runtime_error("Could not enable broadcast on socket"); return NULL; } diff --git a/source_modules/hermes_source/src/hermes.cpp b/source_modules/hermes_source/src/hermes.cpp index 6b02a762..68ad7827 100644 --- a/source_modules/hermes_source/src/hermes.cpp +++ b/source_modules/hermes_source/src/hermes.cpp @@ -205,8 +205,8 @@ namespace hermes { } std::vector discover() { - // TODO: Maybe try to instead detect on each interface as a work around for 0.0.0.0 not receiving anything? - auto sock = net::openudp("0.0.0.0", 1024); + // Open a UDP broadcast socket (TODO: Figure out why 255.255.255.255 doesn't work on windows with local = 0.0.0.0) + auto sock = net::openudp("255.255.255.255", 1024, "0.0.0.0", 0, true); // Build discovery packet uint8_t discoveryPkt[64]; @@ -225,6 +225,7 @@ namespace hermes { } } + // Await all responses std::vector devices; while (true) { // Wait for a response @@ -258,7 +259,9 @@ namespace hermes { devices.push_back(info); } - + + // Close broadcast socket + sock->close(); return devices; } From f5adc7c5870e0cdcc4c6c1276ff910ef332c20d0 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Thu, 25 Jan 2024 23:28:03 +0100 Subject: [PATCH 33/65] fix hermes lite buffering --- source_modules/hermes_source/src/hermes.cpp | 28 ++++++++++++++++----- source_modules/hermes_source/src/hermes.h | 17 +++++++------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/source_modules/hermes_source/src/hermes.cpp b/source_modules/hermes_source/src/hermes.cpp index 68ad7827..a96c7fdb 100644 --- a/source_modules/hermes_source/src/hermes.cpp +++ b/source_modules/hermes_source/src/hermes.cpp @@ -2,6 +2,13 @@ #include namespace hermes { + const int SAMPLERATE_LIST[] = { + 48000, + 96000, + 192000, + 384000 + }; + Client::Client(std::shared_ptr sock) { this->sock = sock; @@ -33,6 +40,7 @@ namespace hermes { void Client::setSamplerate(HermesLiteSamplerate samplerate) { writeReg(0, (uint32_t)samplerate << 24); + blockSize = SAMPLERATE_LIST[samplerate] / 200; } void Client::setFrequency(double freq) { @@ -157,6 +165,8 @@ namespace hermes { void Client::worker() { uint8_t rbuf[2048]; MetisUSBPacket* pkt = (MetisUSBPacket*)rbuf; + int sampleCount = 0; + while (true) { // Wait for a packet or exit if connection closed int len = sock->recv(rbuf, 2048); @@ -183,9 +193,10 @@ namespace hermes { flog::warn("Got response! Reg={0}, Seq={1}", reg, (uint32_t)htonl(pkt->seq)); } - // Decode and send IQ to stream + // Decode and save IQ to buffer uint8_t* iq = &frame[8]; - for (int i = 0; i < 63; i++) { + dsp::complex_t* writeBuf = &out.writeBuf[sampleCount]; + for (int i = 0; i < HERMES_SAMPLES_PER_FRAME; i++) { // Convert to 32bit int32_t si = ((uint32_t)iq[(i*8) + 0] << 16) | ((uint32_t)iq[(i*8) + 1] << 8) | (uint32_t)iq[(i*8) + 2]; int32_t sq = ((uint32_t)iq[(i*8) + 3] << 16) | ((uint32_t)iq[(i*8) + 4] << 8) | (uint32_t)iq[(i*8) + 5]; @@ -195,11 +206,16 @@ namespace hermes { sq = (sq << 8) >> 8; // Convert to float (IQ swapped for some reason) - out.writeBuf[i].im = (float)si / (float)0x1000000; - out.writeBuf[i].re = (float)sq / (float)0x1000000; + writeBuf[i].im = (float)si / (float)0x1000000; + writeBuf[i].re = (float)sq / (float)0x1000000; + } + sampleCount += HERMES_SAMPLES_PER_FRAME; + + // If enough samples are in the buffer, send to stream + if (sampleCount >= blockSize) { + out.swap(sampleCount); + sampleCount = 0; } - out.swap(63); - // TODO: Buffer the data to avoid having a very high DSP frame rate } } } diff --git a/source_modules/hermes_source/src/hermes.h b/source_modules/hermes_source/src/hermes.h index 0d76ea7f..78628f10 100644 --- a/source_modules/hermes_source/src/hermes.h +++ b/source_modules/hermes_source/src/hermes.h @@ -7,11 +7,12 @@ #include #include -#define HERMES_METIS_REPEAT 5 -#define HERMES_METIS_TIMEOUT 1000 -#define HERMES_METIS_SIGNATURE 0xEFFE -#define HERMES_HPSDR_USB_SYNC 0x7F -#define HERMES_I2C_DELAY 50 +#define HERMES_METIS_REPEAT 5 +#define HERMES_METIS_TIMEOUT 1000 +#define HERMES_METIS_SIGNATURE 0xEFFE +#define HERMES_HPSDR_USB_SYNC 0x7F +#define HERMES_I2C_DELAY 50 +#define HERMES_SAMPLES_PER_FRAME 63 namespace hermes { enum MetisPacketType { @@ -140,7 +141,7 @@ namespace hermes { dsp::stream out; - //private: + private: void sendMetisUSB(uint8_t endpoint, void* frame0, void* frame1 = NULL); void sendMetisControl(MetisControl ctrl); @@ -149,12 +150,12 @@ namespace hermes { void writeI2C(I2CPort port, uint8_t addr, uint8_t reg, uint8_t data); - - void worker(); double freq = 0; + int blockSize = 63; + std::thread workerThread; std::shared_ptr sock; uint32_t usbSeq = 0; From db3fbd2975aab5b9662beb34ca1870e975a23aee Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 26 Jan 2024 01:40:17 +0100 Subject: [PATCH 34/65] update issue template to reflect that fact that filling it out is not optional --- .github/ISSUE_TEMPLATE/bug_report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2d553f2b..d80ed1bd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,8 @@ assignees: '' --- +# WARNING: Filling out the template below is NOT optional. Issues not filling out this template will be closed without review. + FIRST: Before reporting any bug, make sure that the bug you are reporting has not been reported before. Also, try to use the [nightly version](https://www.sdrpp.org/nightly) if possible in case I've already fixed the bug. **Hardware** From 0a3d1de02f01046a3bbf6493262f826366a1cd86 Mon Sep 17 00:00:00 2001 From: Davide Rovelli <103165301+daviderud@users.noreply.github.com> Date: Fri, 26 Jan 2024 07:43:01 +0100 Subject: [PATCH 35/65] removed tabs and unnecessary pipes --- root/res/bandplans/netherlands.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/root/res/bandplans/netherlands.json b/root/res/bandplans/netherlands.json index 3a7908b4..1f845021 100644 --- a/root/res/bandplans/netherlands.json +++ b/root/res/bandplans/netherlands.json @@ -111,21 +111,21 @@ "start": 472000, "end": 475000, "type": "amateur", - "name": "|635m Ham Band CW" - }, + "name": "635m Ham Band CW" + }, { "start": 475000, "end": 479000, "type": "amateur1", - "name": "635m Ham Band CW, Digimodes|" - }, + "name": "635m Ham Band CW, Digimodes" + }, { "start": 479000, "end": 526500, "type": "aviation", "name": "Aeronautical Radionavigation / Maritime" }, - { + { "start": 526500, "end": 1606500, "type": "broadcast", @@ -747,7 +747,7 @@ "start": 5330500, "end": 5333400, "type": "amateur", - "name": "|60m Ham Band Ch. 1 (Not NL)" + "name": "60m Ham Band Ch. 1 (Not NL)" }, { "start": 5333400, @@ -766,12 +766,12 @@ "end": 5351500, "type": "amateur1", "name": "60m Ham Band (Not NL)" - }, + }, { "start": 5351500, "end": 5354000, "type": "amateur", - "name": "| 60m Ham Band CW" + "name": "60m Ham Band CW" }, { "start": 5354000, @@ -783,7 +783,7 @@ "start": 5366000, "end": 5366500, "type": "amateur", - "name": "60m HAM Weak NB modes |" + "name": "60m HAM Weak NB modes" }, { "start": 5366500, @@ -807,7 +807,7 @@ "start": 5403500, "end": 5406400, "type": "amateur", - "name": "Ch. 5 60m Ham Band (Not NL)|" + "name": "Ch. 5 60m Ham Band (Not NL)" }, { "start": 5406400, @@ -3545,5 +3545,5 @@ "start": 241000000000, "end": 250000000000 } - ] + ] } \ No newline at end of file From 859af77bd3123e41fb1665fd6942c541cb42b790 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 26 Jan 2024 16:57:11 +0100 Subject: [PATCH 36/65] revamp RFspace source to use new networking library and fix buffering --- source_modules/hermes_source/src/hermes.cpp | 1 + source_modules/rfspace_source/src/main.cpp | 4 +- .../rfspace_source/src/rfspace_client.cpp | 189 +++++++++++------- .../rfspace_source/src/rfspace_client.h | 35 ++-- 4 files changed, 140 insertions(+), 89 deletions(-) diff --git a/source_modules/hermes_source/src/hermes.cpp b/source_modules/hermes_source/src/hermes.cpp index a96c7fdb..b7d967a9 100644 --- a/source_modules/hermes_source/src/hermes.cpp +++ b/source_modules/hermes_source/src/hermes.cpp @@ -173,6 +173,7 @@ namespace hermes { if (len <= 0) { break; } // Ignore anything that's not a USB packet + // TODO: Gotta check the endpoint if (htons(pkt->hdr.signature) != HERMES_METIS_SIGNATURE || pkt->hdr.type != METIS_PKT_USB) { continue; } diff --git a/source_modules/rfspace_source/src/main.cpp b/source_modules/rfspace_source/src/main.cpp index e0078efb..f2e74661 100644 --- a/source_modules/rfspace_source/src/main.cpp +++ b/source_modules/rfspace_source/src/main.cpp @@ -231,7 +231,7 @@ private: } // Create samplerate list - auto srs = client->getValidSampleRates(); + auto srs = client->getSamplerates(); sampleRates.clear(); for (auto& sr : srs) { sampleRates.define(sr, getBandwdithScaled(sr), sr); @@ -317,7 +317,7 @@ private: dsp::stream stream; SourceManager::SourceHandler handler; - rfspace::RFspaceClient client; + std::shared_ptr client; }; MOD_EXPORT void _INIT_() { diff --git a/source_modules/rfspace_source/src/rfspace_client.cpp b/source_modules/rfspace_source/src/rfspace_client.cpp index fce0b4e3..71c01345 100644 --- a/source_modules/rfspace_source/src/rfspace_client.cpp +++ b/source_modules/rfspace_source/src/rfspace_client.cpp @@ -6,15 +6,13 @@ using namespace std::chrono_literals; namespace rfspace { - RFspaceClientClass::RFspaceClientClass(net::Conn conn, net::Conn udpConn, dsp::stream* out) { - client = std::move(conn); - udpClient = std::move(udpConn); + Client::Client(std::shared_ptr tcp, std::shared_ptr udp, dsp::stream* out) { + this->tcp = tcp; + this->udp = udp; output = out; // Allocate buffers - rbuffer = new uint8_t[RFSPACE_MAX_SIZE]; sbuffer = new uint8_t[RFSPACE_MAX_SIZE]; - ubuffer = new uint8_t[RFSPACE_MAX_SIZE]; // Clear write stop of stream just in case output->clearWriteStop(); @@ -22,9 +20,9 @@ namespace rfspace { // Send UDP packet so that a router opens the port sendDummyUDP(); - // Start readers - client->readAsync(sizeof(tcpHeader), (uint8_t*)&tcpHeader, tcpHandler, this); - udpClient->readAsync(RFSPACE_MAX_SIZE, ubuffer, udpHandler, this); + // Start workers + tcpWorkerThread = std::thread(&Client::tcpWorker, this); + udpWorkerThread = std::thread(&Client::udpWorker, this); // Get device ID and wait for response getControlItem(RFSPACE_CTRL_ITEM_PROD_ID, NULL, 0); @@ -43,22 +41,20 @@ namespace rfspace { setPort(RFSPACE_RF_PORT_1); // Start heartbeat - heartBeatThread = std::thread(&RFspaceClientClass::heartBeatWorker, this); + heartBeatThread = std::thread(&Client::heartBeatWorker, this); } - RFspaceClientClass::~RFspaceClientClass() { + Client::~Client() { close(); - delete[] rbuffer; delete[] sbuffer; - delete[] ubuffer; } - void RFspaceClientClass::sendDummyUDP() { + void Client::sendDummyUDP() { uint8_t dummy = 0x5A; - udpClient->write(1, &dummy); + udp->send(&dummy, 1); } - int RFspaceClientClass::getControlItem(ControlItem item, void* param, int len) { + int Client::getControlItem(ControlItem item, void* param, int len) { // Build packet uint16_t* header = (uint16_t*)&sbuffer[0]; uint16_t* item_val = (uint16_t*)&sbuffer[2]; @@ -66,12 +62,12 @@ namespace rfspace { *item_val = item; // Send packet - client->write(4, sbuffer); + tcp->send(sbuffer, 4); return -1; } - void RFspaceClientClass::setControlItem(ControlItem item, void* param, int len) { + void Client::setControlItem(ControlItem item, void* param, int len) { // Build packet uint16_t* header = (uint16_t*)&sbuffer[0]; uint16_t* item_val = (uint16_t*)&sbuffer[2]; @@ -80,10 +76,10 @@ namespace rfspace { memcpy(&sbuffer[4], param, len); // Send packet - client->write(len + 4, sbuffer); + tcp->send(sbuffer, len + 4); } - void RFspaceClientClass::setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len) { + void Client::setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len) { // Build packet uint16_t* header = (uint16_t*)&sbuffer[0]; uint16_t* item_val = (uint16_t*)&sbuffer[2]; @@ -94,10 +90,10 @@ namespace rfspace { memcpy(&sbuffer[5], param, len); // Send packet - client->write(len + 5, sbuffer); + tcp->send(sbuffer, len + 5); } - std::vector RFspaceClientClass::getValidSampleRates() { + std::vector Client::getSamplerates() { std::vector sr; switch (deviceId) { @@ -119,92 +115,145 @@ namespace rfspace { return sr; } - void RFspaceClientClass::setFrequency(uint64_t freq) { + void Client::setFrequency(uint64_t freq) { setControlItemWithChanID(RFSPACE_CTRL_ITEM_NCO_FREQUENCY, 0, &freq, 5); } - void RFspaceClientClass::setPort(RFPort port) { + void Client::setPort(RFPort port) { uint8_t value = port; setControlItemWithChanID(RFSPACE_CTRL_ITEM_RF_PORT, 0, &value, sizeof(value)); } - void RFspaceClientClass::setGain(int8_t gain) { + void Client::setGain(int8_t gain) { setControlItemWithChanID(RFSPACE_CTRL_ITEM_RF_GAIN, 0, &gain, sizeof(gain)); } - void RFspaceClientClass::setSampleRate(uint32_t sampleRate) { + void Client::setSampleRate(uint32_t sampleRate) { + // Acquire the buffer variables + std::lock_guard lck(bufferMtx); + + // Update block size + blockSize = sampleRate / 200; + + // Send samplerate to device setControlItemWithChanID(RFSPACE_CTRL_ITEM_IQ_SAMP_RATE, 0, &sampleRate, sizeof(sampleRate)); } - void RFspaceClientClass::start(SampleFormat sampleFormat, SampleDepth sampleDepth) { + void Client::start(SampleFormat sampleFormat, SampleDepth sampleDepth) { + // Acquire the buffer variables + std::lock_guard lck(bufferMtx); + + // Reset buffer + inBuffer = 0; + + // Start device uint8_t args[4] = { (uint8_t)sampleFormat, (uint8_t)RFSPACE_STATE_RUN, (uint8_t)sampleDepth, 0 }; setControlItem(RFSPACE_CTRL_ITEM_STATE, args, sizeof(args)); } - void RFspaceClientClass::stop() { + void Client::stop() { uint8_t args[4] = { 0, RFSPACE_STATE_IDLE, 0, 0 }; setControlItem(RFSPACE_CTRL_ITEM_STATE, args, sizeof(args)); } - void RFspaceClientClass::close() { + void Client::close() { + // Stop UDP worker output->stopWriter(); + udp->close(); + if (udpWorkerThread.joinable()) { udpWorkerThread.join(); } + output->clearWriteStop(); + + // Stop heartbeat worker stopHeartBeat = true; heartBeatCnd.notify_all(); if (heartBeatThread.joinable()) { heartBeatThread.join(); } - client->close(); - udpClient->close(); - output->clearWriteStop(); + + // Stop TCP worker + tcp->close(); + if (tcpWorkerThread.joinable()) { tcpWorkerThread.join(); } } - bool RFspaceClientClass::isOpen() { - return client->isOpen(); + bool Client::isOpen() { + return tcp->isOpen() || udp->isOpen(); } - void RFspaceClientClass::tcpHandler(int count, uint8_t* buf, void* ctx) { - RFspaceClientClass* _this = (RFspaceClientClass*)ctx; - uint8_t type = _this->tcpHeader >> 13; - uint16_t size = _this->tcpHeader & 0b1111111111111; + void Client::tcpWorker() { + // Allocate receive buffer + uint8_t* buffer = new uint8_t[RFSPACE_MAX_SIZE]; - // Read the rest of the data - if (size > 2) { - _this->client->read(size - 2, &_this->rbuffer[2]); - } + // Receive loop + while (true) { + // Receive header + uint16_t header; + if (tcp->recv((uint8_t*)&header, sizeof(uint16_t), true) <= 0) { break; } - // flog::warn("TCP received: {0} {1}", type, size); + // Decode header + uint8_t type = header >> 13; + uint16_t size = header & 0b1111111111111; - // Check for a device ID - uint16_t* controlItem = (uint16_t*)&_this->rbuffer[2]; - if (type == RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP && *controlItem == RFSPACE_CTRL_ITEM_PROD_ID) { - { - std::lock_guard lck(_this->devIdMtx); - _this->deviceId = (DeviceID)*(uint32_t*)&_this->rbuffer[4]; - _this->devIdAvailable = true; + // Receive data + if (tcp->recv(buffer, size - 2, true, RFSPACE_TIMEOUT_MS) <= 0) { break; } + + // Check for a device ID + uint16_t* controlItem = (uint16_t*)&buffer[0]; + if (type == RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP && *controlItem == RFSPACE_CTRL_ITEM_PROD_ID) { + { + std::lock_guard lck(devIdMtx); + deviceId = (DeviceID)*(uint32_t*)&buffer[2]; + devIdAvailable = true; + } + devIdCnd.notify_all(); } - _this->devIdCnd.notify_all(); } - // Restart an async read - _this->client->readAsync(sizeof(_this->tcpHeader), (uint8_t*)&_this->tcpHeader, tcpHandler, _this); + // Free receive buffer + delete[] buffer; } - void RFspaceClientClass::udpHandler(int count, uint8_t* buf, void* ctx) { - RFspaceClientClass* _this = (RFspaceClientClass*)ctx; - uint16_t hdr = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8); - uint8_t type = hdr >> 13; - uint16_t size = hdr & 0b1111111111111; + void Client::udpWorker() { + // Allocate receive buffer + uint8_t* buffer = new uint8_t[RFSPACE_MAX_SIZE]; + uint16_t* header = (uint16_t*)&buffer[0]; - if (type == RFSPACE_MSG_TYPE_T2H_DATA_ITEM_0) { - int16_t* samples = (int16_t*)&buf[4]; - int sampCount = (size - 4) / (2 * sizeof(int16_t)); - volk_16i_s32f_convert_32f((float*)_this->output->writeBuf, samples, 32768.0f, sampCount * 2); - _this->output->swap(sampCount); + // Receive loop + while (true) { + // Receive datagram + int rsize = udp->recv(buffer, RFSPACE_MAX_SIZE); + if (rsize <= 0) { break; } + + // Decode header + uint8_t type = (*header) >> 13; + uint16_t size = (*header) & 0b1111111111111; + + if (rsize != size) { + flog::error("Datagram size mismatch: {} vs {}", rsize, size); + continue; + } + + // Check for a sample packet + if (type == RFSPACE_MSG_TYPE_T2H_DATA_ITEM_0) { + // Acquire the buffer variables + std::lock_guard lck(bufferMtx); + + // Convert samples to complex float + int16_t* samples = (int16_t*)&buffer[4]; + int sampCount = (size - 4) / (2 * sizeof(int16_t)); + volk_16i_s32f_convert_32f((float*)&output->writeBuf[inBuffer], samples, 32768.0f, sampCount * 2); + inBuffer += sampCount; + + // Send out samples if enough are buffered + if (inBuffer >= blockSize) { + if (!output->swap(inBuffer)) { break; }; + inBuffer = 0; + } + } } - // Restart an async read - _this->udpClient->readAsync(RFSPACE_MAX_SIZE, _this->ubuffer, udpHandler, _this); + // Free receive buffer + delete[] buffer; } - void RFspaceClientClass::heartBeatWorker() { + void Client::heartBeatWorker() { uint8_t dummy[4]; while (true) { getControlItem(RFSPACE_CTRL_ITEM_STATE, dummy, sizeof(dummy)); @@ -216,11 +265,9 @@ namespace rfspace { } } - RFspaceClient connect(std::string host, uint16_t port, dsp::stream* out) { - net::Conn conn = net::connect(host, port); - if (!conn) { return NULL; } - net::Conn udpConn = net::openUDP("0.0.0.0", port, host, port, true); - if (!udpConn) { return NULL; } - return RFspaceClient(new RFspaceClientClass(std::move(conn), std::move(udpConn), out)); + std::shared_ptr connect(std::string host, uint16_t port, dsp::stream* out) { + auto tcp = net::connect(host, port); + auto udp = net::openudp(host, port, "0.0.0.0", port); + return std::make_shared(tcp, udp, out); } } diff --git a/source_modules/rfspace_source/src/rfspace_client.h b/source_modules/rfspace_source/src/rfspace_client.h index fc27fbc1..04aa2dc0 100644 --- a/source_modules/rfspace_source/src/rfspace_client.h +++ b/source_modules/rfspace_source/src/rfspace_client.h @@ -1,9 +1,9 @@ #pragma once -#include +#include #include #include -#include -#include +#include +#include #define RFSPACE_MAX_SIZE 8192 #define RFSPACE_HEARTBEAT_INTERVAL_MS 1000 @@ -96,10 +96,10 @@ namespace rfspace { RFSPACE_CTRL_ITEM_ERROR_LOG = 0x0410 }; - class RFspaceClientClass { + class Client { public: - RFspaceClientClass(net::Conn conn, net::Conn udpConn, dsp::stream* out); - ~RFspaceClientClass(); + Client(std::shared_ptr tcp, std::shared_ptr udp, dsp::stream* out); + ~Client(); void sendDummyUDP(); @@ -107,7 +107,7 @@ namespace rfspace { void setControlItem(ControlItem item, void* param, int len); void setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len); - std::vector getValidSampleRates(); + std::vector getSamplerates(); void setFrequency(uint64_t freq); void setPort(RFPort port); @@ -123,21 +123,22 @@ namespace rfspace { DeviceID deviceId; private: - static void tcpHandler(int count, uint8_t* buf, void* ctx); - static void udpHandler(int count, uint8_t* buf, void* ctx); + void tcpWorker(); + void udpWorker(); void heartBeatWorker(); - net::Conn client; - net::Conn udpClient; + std::shared_ptr tcp; + std::shared_ptr udp; dsp::stream* output; uint16_t tcpHeader; uint16_t udpHeader; - uint8_t* rbuffer = NULL; uint8_t* sbuffer = NULL; - uint8_t* ubuffer = NULL; + + std::thread tcpWorkerThread; + std::thread udpWorkerThread; std::thread heartBeatThread; std::mutex heartBeatMtx; @@ -147,10 +148,12 @@ namespace rfspace { bool devIdAvailable = false; std::condition_variable devIdCnd; std::mutex devIdMtx; + + std::mutex bufferMtx; + int blockSize = 256; + int inBuffer = 0; }; - typedef std::unique_ptr RFspaceClient; - - RFspaceClient connect(std::string host, uint16_t port, dsp::stream* out); + std::shared_ptr connect(std::string host, uint16_t port, dsp::stream* out); } From eb36f86d41ba7a7829466913b732319d3cb7f66b Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 26 Jan 2024 17:21:51 +0100 Subject: [PATCH 37/65] added missing include --- source_modules/rfspace_source/src/rfspace_client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source_modules/rfspace_source/src/rfspace_client.h b/source_modules/rfspace_source/src/rfspace_client.h index 04aa2dc0..29715a2a 100644 --- a/source_modules/rfspace_source/src/rfspace_client.h +++ b/source_modules/rfspace_source/src/rfspace_client.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include From 09f4071803494b2461a4f96610b0ecda7918d725 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 26 Jan 2024 18:47:13 +0100 Subject: [PATCH 38/65] Switch SDRplay API in Linux nightly builds to version 3.12 --- docker_builds/debian_bookworm/do_build.sh | 8 ++++---- docker_builds/debian_bullseye/do_build.sh | 8 ++++---- docker_builds/debian_buster/do_build.sh | 8 ++++---- docker_builds/debian_sid/do_build.sh | 8 ++++---- docker_builds/ubuntu_bionic/do_build.sh | 8 ++++---- docker_builds/ubuntu_focal/do_build.sh | 8 ++++---- docker_builds/ubuntu_jammy/do_build.sh | 8 ++++---- docker_builds/ubuntu_mantic/do_build.sh | 8 ++++---- 8 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docker_builds/debian_bookworm/do_build.sh b/docker_builds/debian_bookworm/do_build.sh index 6509c192..080604fd 100644 --- a/docker_builds/debian_bookworm/do_build.sh +++ b/docker_builds/debian_bookworm/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus diff --git a/docker_builds/debian_bullseye/do_build.sh b/docker_builds/debian_bullseye/do_build.sh index 6509c192..080604fd 100644 --- a/docker_builds/debian_bullseye/do_build.sh +++ b/docker_builds/debian_bullseye/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus diff --git a/docker_builds/debian_buster/do_build.sh b/docker_builds/debian_buster/do_build.sh index 0a90aa11..0392de02 100644 --- a/docker_builds/debian_buster/do_build.sh +++ b/docker_builds/debian_buster/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus diff --git a/docker_builds/debian_sid/do_build.sh b/docker_builds/debian_sid/do_build.sh index b67517a9..f4673de6 100644 --- a/docker_builds/debian_sid/do_build.sh +++ b/docker_builds/debian_sid/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus diff --git a/docker_builds/ubuntu_bionic/do_build.sh b/docker_builds/ubuntu_bionic/do_build.sh index c63d8434..f418a06a 100644 --- a/docker_builds/ubuntu_bionic/do_build.sh +++ b/docker_builds/ubuntu_bionic/do_build.sh @@ -15,10 +15,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libcodec2-dev libudev-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install a more recent libusb version diff --git a/docker_builds/ubuntu_focal/do_build.sh b/docker_builds/ubuntu_focal/do_build.sh index 6509c192..080604fd 100644 --- a/docker_builds/ubuntu_focal/do_build.sh +++ b/docker_builds/ubuntu_focal/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus diff --git a/docker_builds/ubuntu_jammy/do_build.sh b/docker_builds/ubuntu_jammy/do_build.sh index 6509c192..080604fd 100644 --- a/docker_builds/ubuntu_jammy/do_build.sh +++ b/docker_builds/ubuntu_jammy/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus diff --git a/docker_builds/ubuntu_mantic/do_build.sh b/docker_builds/ubuntu_mantic/do_build.sh index b67517a9..f4673de6 100644 --- a/docker_builds/ubuntu_mantic/do_build.sh +++ b/docker_builds/ubuntu_mantic/do_build.sh @@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l libcodec2-dev autoconf libtool xxd # Install SDRPlay libraries -wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1.run -7z x ./SDRplay_RSP_API-Linux-3.07.1 -cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so +wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1.run +7z x ./SDRplay_RSP_API-Linux-3.12.1 +cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so cp inc/* /usr/include/ # Install libperseus From f6052d913aab2587f6a7ee3c0c782e9088308065 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 26 Jan 2024 19:00:36 +0100 Subject: [PATCH 39/65] bump version of updated modules --- source_modules/hermes_source/src/main.cpp | 2 +- source_modules/rfspace_source/src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source_modules/hermes_source/src/main.cpp b/source_modules/hermes_source/src/main.cpp index 243d6417..8bf82d3d 100644 --- a/source_modules/hermes_source/src/main.cpp +++ b/source_modules/hermes_source/src/main.cpp @@ -17,7 +17,7 @@ SDRPP_MOD_INFO{ /* Name: */ "hermes_source", /* Description: */ "Hermes Lite 2 source module for SDR++", /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 0, + /* Version: */ 0, 1, 1, /* Max instances */ 1 }; diff --git a/source_modules/rfspace_source/src/main.cpp b/source_modules/rfspace_source/src/main.cpp index f2e74661..f3b7973a 100644 --- a/source_modules/rfspace_source/src/main.cpp +++ b/source_modules/rfspace_source/src/main.cpp @@ -17,7 +17,7 @@ SDRPP_MOD_INFO{ /* Name: */ "rfspace_source", /* Description: */ "RFspace source module for SDR++", /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 0, + /* Version: */ 0, 1, 1, /* Max instances */ 1 }; From 38a95b40112d51b8ea16493026a9c0fb0075c4b4 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Fri, 26 Jan 2024 19:31:32 +0100 Subject: [PATCH 40/65] update SDRplay API in MacOS CI --- .github/workflows/build_all.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index c5dc6b7a..867fcf07 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -103,7 +103,7 @@ jobs: run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ - name: Install SDRplay API - run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target / + run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.12.1.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.12.1.pkg -target / - name: Install libiio run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ From 06b7ad5c984558b84b8ed07b098dc82e30c9c215 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 16:34:54 +0100 Subject: [PATCH 41/65] remove old useless debug code from audio sink --- sink_modules/audio_sink/src/main.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sink_modules/audio_sink/src/main.cpp b/sink_modules/audio_sink/src/main.cpp index a4c82118..9ac793bb 100644 --- a/sink_modules/audio_sink/src/main.cpp +++ b/sink_modules/audio_sink/src/main.cpp @@ -224,14 +224,6 @@ private: int count = _this->stereoPacker.out.read(); if (count < 0) { return 0; } - // For debug purposes only... - // if (nBufferFrames != count) { flog::warn("Buffer size mismatch, wanted {0}, was asked for {1}", count, nBufferFrames); } - // for (int i = 0; i < count; i++) { - // if (_this->stereoPacker.out.readBuf[i].l == NAN || _this->stereoPacker.out.readBuf[i].r == NAN) { flog::error("NAN in audio data"); } - // if (_this->stereoPacker.out.readBuf[i].l == INFINITY || _this->stereoPacker.out.readBuf[i].r == INFINITY) { flog::error("INFINITY in audio data"); } - // if (_this->stereoPacker.out.readBuf[i].l == -INFINITY || _this->stereoPacker.out.readBuf[i].r == -INFINITY) { flog::error("-INFINITY in audio data"); } - // } - memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t)); _this->stereoPacker.out.flush(); return 0; From cd996292bc20655c919bed026e43a13bb235b443 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 21:12:26 +0100 Subject: [PATCH 42/65] revamp plutosdr samplerate selection code --- source_modules/plutosdr_source/src/main.cpp | 85 +++++++++++---------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 72a93ade..14887dbe 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -7,7 +7,7 @@ #include #include #include - +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -32,6 +32,7 @@ public: PlutoSDRSourceModule(std::string name) { this->name = name; + // Load configuration config.acquire(); std::string _ip = config.conf["IP"]; strcpy(&ip[3], _ip.c_str()); @@ -40,26 +41,22 @@ public: gain = config.conf["gain"]; config.release(); - // Generate the samplerate list and find srId - bool found = false; - int id = 0; - for (double sr = 1000000; sr <= 20000000; sr += 500000) { - sampleRates.push_back(sr); - sampleRatesTxt += getBandwdithScaled(sr); - sampleRatesTxt += '\0'; - - if (sr == sampleRate) { - found = true; - srId = id; - } - - id++; + // Define valid samplerates + for (double sr = 1000000.0; sr <= 61440000.0; sr += 500000.0) { + samplerates.define(sr, getBandwdithScaled(sr), sr); } - if (!found) { + samplerates.define(61440000, getBandwdithScaled(61440000.0), 61440000.0); + + // Set samplerate ID + if (samplerates.keyExists(sampleRate)) { + srId = samplerates.keyId(sampleRate); + } + else { srId = 0; - sampleRate = sampleRates[0]; + sampleRate = samplerates.value(srId); } + // Register source handler.ctx = this; handler.selectHandler = menuSelected; handler.deselectHandler = menuDeselected; @@ -120,7 +117,7 @@ private: PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; if (_this->running) { return; } - // TODO: INIT CONTEXT HERE + // Open device _this->ctx = iio_create_context_from_uri(_this->ip); if (_this->ctx == NULL) { flog::error("Could not open pluto"); @@ -139,10 +136,11 @@ private: return; } - // Configure pluto + // Enable RX channel and disable TX iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage1", true), "powerdown", true); iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage0", true), "powerdown", false); + // Configure RX channel iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "rf_port_select", "A_BALANCED"); iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "sampling_frequency", round(_this->sampleRate)); // Sample rate @@ -150,6 +148,7 @@ private: iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); // gain ad9361_set_bb_rate(_this->phy, round(_this->sampleRate)); + // Start worker thread _this->running = true; _this->workerThread = std::thread(worker, _this); flog::info("PlutoSDRSourceModule '{0}': Start!", _this->name); @@ -158,12 +157,14 @@ private: static void stop(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; if (!_this->running) { return; } + + // Stop worker thread _this->running = false; _this->stream.stopWriter(); _this->workerThread.join(); _this->stream.clearWriteStop(); - // DESTROY CONTEXT HERE + // Close device if (_this->ctx != NULL) { iio_context_destroy(_this->ctx); _this->ctx = NULL; @@ -176,7 +177,7 @@ private: PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; _this->freq = freq; if (_this->running) { - // SET PLUTO FREQ HERE + // Tune device iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(freq)); } flog::info("PlutoSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); @@ -196,8 +197,8 @@ private: SmGui::LeftLabel("Samplerate"); SmGui::FillWidth(); - if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) { - _this->sampleRate = _this->sampleRates[_this->srId]; + if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->samplerates.txt)) { + _this->sampleRate = _this->samplerates.value(_this->srId); core::setInputSampleRate(_this->sampleRate); config.acquire(); config.conf["sampleRate"] = _this->sampleRate; @@ -235,38 +236,40 @@ private: PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; int blockSize = _this->sampleRate / 200.0f; - struct iio_channel *rx0_i, *rx0_q; - struct iio_buffer* rxbuf; - - rx0_i = iio_device_find_channel(_this->dev, "voltage0", 0); - rx0_q = iio_device_find_channel(_this->dev, "voltage1", 0); + // Acquire channels + iio_channel* rx0_i = iio_device_find_channel(_this->dev, "voltage0", 0); + iio_channel* rx0_q = iio_device_find_channel(_this->dev, "voltage1", 0); + // Start streaming iio_channel_enable(rx0_i); iio_channel_enable(rx0_q); - rxbuf = iio_device_create_buffer(_this->dev, blockSize, false); + // Allocate buffer + iio_buffer* rxbuf = iio_device_create_buffer(_this->dev, blockSize, false); if (!rxbuf) { flog::error("Could not create RX buffer"); return; } while (true) { - // Read samples here - // TODO: RECEIVE HERE + // Read samples iio_buffer_refill(rxbuf); + // Get buffer pointer int16_t* buf = (int16_t*)iio_buffer_first(rxbuf, rx0_i); - for (int i = 0; i < blockSize; i++) { - _this->stream.writeBuf[i].re = (float)buf[i * 2] / 32768.0f; - _this->stream.writeBuf[i].im = (float)buf[(i * 2) + 1] / 32768.0f; - } - + // Convert samples to CF32 volk_16i_s32f_convert_32f((float*)_this->stream.writeBuf, buf, 32768.0f, blockSize * 2); + // Send out the samples if (!_this->stream.swap(blockSize)) { break; }; } + // Stop streaming + iio_channel_disable(rx0_i); + iio_channel_disable(rx0_q); + + // Free buffer iio_buffer_destroy(rxbuf); } @@ -276,10 +279,11 @@ private: float sampleRate; SourceManager::SourceHandler handler; std::thread workerThread; - struct iio_context* ctx = NULL; - struct iio_device* phy = NULL; - struct iio_device* dev = NULL; + iio_context* ctx = NULL; + iio_device* phy = NULL; + iio_device* dev = NULL; bool running = false; + bool ipMode = true; double freq; char ip[1024] = "ip:192.168.2.1"; @@ -287,8 +291,7 @@ private: float gain = 0; int srId = 0; - std::vector sampleRates; - std::string sampleRatesTxt; + OptionList samplerates; }; MOD_EXPORT void _INIT_() { From e9ec79f6ef47ae3558cfb090c9d1c6e16fd8dbb1 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 21:29:44 +0100 Subject: [PATCH 43/65] modernise gain mode selection for plutosdr --- source_modules/plutosdr_source/src/main.cpp | 25 +++++++++++---------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 14887dbe..713c1ca0 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -19,12 +19,6 @@ SDRPP_MOD_INFO{ /* Max instances */ 1 }; -const char* gainModes[] = { - "manual", "fast_attack", "slow_attack", "hybrid" -}; - -const char* gainModesTxt = "Manual\0Fast Attack\0Slow Attack\0Hybrid\0"; - ConfigManager config; class PlutoSDRSourceModule : public ModuleManager::Instance { @@ -56,6 +50,12 @@ public: sampleRate = samplerates.value(srId); } + // Define gain modes + gainModes.define(0, "Manual", "manual"); + gainModes.define(1, "Fast Attack", "fast_attack"); + gainModes.define(2, "Slow Attack", "slow_attack"); + gainModes.define(3, "Hybrid", "hybrid"); + // Register source handler.ctx = this; handler.selectHandler = menuSelected; @@ -142,10 +142,10 @@ private: // Configure RX channel iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "rf_port_select", "A_BALANCED"); - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "sampling_frequency", round(_this->sampleRate)); // Sample rate - iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", gainModes[_this->gainMode]); // manual gain - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); // gain + iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq + iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "sampling_frequency", round(_this->sampleRate)); // Sample rate + iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode + iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); // gain ad9361_set_bb_rate(_this->phy, round(_this->sampleRate)); // Start worker thread @@ -209,9 +209,9 @@ private: SmGui::LeftLabel("Gain Mode"); SmGui::FillWidth(); SmGui::ForceSync(); - if (SmGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, gainModesTxt)) { + if (SmGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, _this->gainModes.txt)) { if (_this->running) { - iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", gainModes[_this->gainMode]); + iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); } config.acquire(); config.conf["gainMode"] = _this->gainMode; @@ -292,6 +292,7 @@ private: int srId = 0; OptionList samplerates; + OptionList gainModes; }; MOD_EXPORT void _INIT_() { From 08e75b6d1404265fff4f65f246e2726235f25395 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 21:30:06 +0100 Subject: [PATCH 44/65] fix gain mode selection not always applying --- source_modules/plutosdr_source/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 713c1ca0..fcf5ceac 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -144,8 +144,8 @@ private: iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "rf_port_select", "A_BALANCED"); iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "sampling_frequency", round(_this->sampleRate)); // Sample rate - iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); // gain + iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode ad9361_set_bb_rate(_this->phy, round(_this->sampleRate)); // Start worker thread From bc8baca19071515cca8047d030ab77c5152d9bdb Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 21:35:13 +0100 Subject: [PATCH 45/65] clean up plutosdr source code --- source_modules/plutosdr_source/src/main.cpp | 22 +++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index fcf5ceac..d2868900 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -117,12 +117,14 @@ private: PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; if (_this->running) { return; } - // Open device + // Open context _this->ctx = iio_create_context_from_uri(_this->ip); if (_this->ctx == NULL) { flog::error("Could not open pluto"); return; } + + // Get phy and device handle _this->phy = iio_context_find_device(_this->ctx, "ad9361-phy"); if (_this->phy == NULL) { flog::error("Could not connect to pluto phy"); @@ -136,16 +138,19 @@ private: return; } + // Get RX channel + _this->rxChan = iio_device_find_channel(_this->phy, "voltage0", false); + // Enable RX channel and disable TX iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage1", true), "powerdown", true); iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage0", true), "powerdown", false); // Configure RX channel - iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "rf_port_select", "A_BALANCED"); - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "sampling_frequency", round(_this->sampleRate)); // Sample rate - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); // gain - iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode + iio_channel_attr_write(_this->rxChan, "rf_port_select", "A_BALANCED"); + iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq + iio_channel_attr_write_longlong(_this->rxChan, "sampling_frequency", round(_this->sampleRate)); // Sample rate + iio_channel_attr_write_longlong(_this->rxChan, "hardwaregain", round(_this->gain)); // Gain + iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode ad9361_set_bb_rate(_this->phy, round(_this->sampleRate)); // Start worker thread @@ -211,7 +216,7 @@ private: SmGui::ForceSync(); if (SmGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, _this->gainModes.txt)) { if (_this->running) { - iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); + iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); } config.acquire(); config.conf["gainMode"] = _this->gainMode; @@ -223,7 +228,7 @@ private: SmGui::FillWidth(); if (SmGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76)) { if (_this->running) { - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); + iio_channel_attr_write_longlong(_this->rxChan, "hardwaregain", round(_this->gain)); } config.acquire(); config.conf["gain"] = _this->gain; @@ -282,6 +287,7 @@ private: iio_context* ctx = NULL; iio_device* phy = NULL; iio_device* dev = NULL; + iio_channel* rxChan = NULL; bool running = false; bool ipMode = true; From 00e68320551eb431cbe743e7ee8ea85e6a4c1ce9 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 22:49:39 +0100 Subject: [PATCH 46/65] add plutosdr bandwidth selection for #563 --- source_modules/plutosdr_source/src/main.cpp | 95 ++++++++++++++++----- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index d2868900..e6abc8dd 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -15,7 +15,7 @@ SDRPP_MOD_INFO{ /* Name: */ "plutosdr_source", /* Description: */ "PlutoSDR source module for SDR++", /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 0, + /* Version: */ 0, 2, 0, /* Max instances */ 1 }; @@ -28,15 +28,26 @@ public: // Load configuration config.acquire(); - std::string _ip = config.conf["IP"]; - strcpy(&ip[3], _ip.c_str()); - sampleRate = config.conf["sampleRate"]; - gainMode = config.conf["gainMode"]; - gain = config.conf["gain"]; + if (config.conf.contains("IP")) { + std::string _ip = config.conf["IP"]; + strcpy(&ip[3], _ip.c_str()); + } + if (config.conf.contains("sampleRate")) { + sampleRate = config.conf["sampleRate"]; + } + if (config.conf.contains("bandwidth")) { + bandwidth = config.conf["bandwidth"]; + } + if (config.conf.contains("gainMode")) { + gainMode = config.conf["gainMode"]; + } + if (config.conf.contains("gain")) { + gain = config.conf["gain"]; + } config.release(); // Define valid samplerates - for (double sr = 1000000.0; sr <= 61440000.0; sr += 500000.0) { + for (int sr = 1000000; sr <= 61440000; sr += 500000) { samplerates.define(sr, getBandwdithScaled(sr), sr); } samplerates.define(61440000, getBandwdithScaled(61440000.0), 61440000.0); @@ -50,6 +61,21 @@ public: sampleRate = samplerates.value(srId); } + // Define valid bandwidths + bandwidths.define(0, "Auto", 0); + for (int bw = 1000000.0; bw <= 52000000; bw += 500000) { + bandwidths.define(bw, getBandwdithScaled(bw), bw); + } + + // Set bandwidth ID + if (bandwidths.keyExists(bandwidth)) { + bwId = bandwidths.keyId(bandwidth); + } + else { + bwId = 0; + bandwidth = bandwidths.value(bwId); + } + // Define gain modes gainModes.define(0, "Manual", "manual"); gainModes.define(1, "Fast Attack", "fast_attack"); @@ -138,19 +164,24 @@ private: return; } - // Get RX channel + // Get RX channels _this->rxChan = iio_device_find_channel(_this->phy, "voltage0", false); + _this->rxLO = iio_device_find_channel(_this->phy, "altvoltage0", true); - // Enable RX channel and disable TX + // Enable RX LO and disable TX iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage1", true), "powerdown", true); - iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage0", true), "powerdown", false); + iio_channel_attr_write_bool(_this->rxLO, "powerdown", false); // Configure RX channel iio_channel_attr_write(_this->rxChan, "rf_port_select", "A_BALANCED"); - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq - iio_channel_attr_write_longlong(_this->rxChan, "sampling_frequency", round(_this->sampleRate)); // Sample rate - iio_channel_attr_write_longlong(_this->rxChan, "hardwaregain", round(_this->gain)); // Gain - iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode + iio_channel_attr_write_longlong(_this->rxLO, "frequency", round(_this->freq)); // Freq + iio_channel_attr_write_bool(_this->rxChan, "filter_fir_en", true); // Digital filter + iio_channel_attr_write_longlong(_this->rxChan, "sampling_frequency", round(_this->sampleRate)); // Sample rate + iio_channel_attr_write_longlong(_this->rxChan, "hardwaregain", round(_this->gain)); // Gain + iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode + _this->setBandwidth(_this->bandwidth); + + // Configure the ADC filters ad9361_set_bb_rate(_this->phy, round(_this->sampleRate)); // Start worker thread @@ -211,6 +242,18 @@ private: } if (_this->running) { SmGui::EndDisabled(); } + SmGui::LeftLabel("Bandwidth"); + SmGui::FillWidth(); + if (SmGui::Combo(CONCAT("##_pluto_bw_", _this->name), &_this->bwId, _this->bandwidths.txt)) { + _this->bandwidth = _this->bandwidths.value(_this->bwId); + if (_this->running) { + _this->setBandwidth(_this->bandwidth); + } + config.acquire(); + config.conf["bandwidth"] = _this->bandwidth; + config.release(true); + } + SmGui::LeftLabel("Gain Mode"); SmGui::FillWidth(); SmGui::ForceSync(); @@ -237,6 +280,15 @@ private: if (_this->gainMode) { SmGui::EndDisabled(); } } + void setBandwidth(int bw) { + if (bw > 0) { + iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", bw); + } + else { + iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", sampleRate); + } + } + static void worker(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; int blockSize = _this->sampleRate / 200.0f; @@ -256,6 +308,7 @@ private: return; } + // Receive loop while (true) { // Read samples iio_buffer_refill(rxbuf); @@ -281,32 +334,32 @@ private: std::string name; bool enabled = true; dsp::stream stream; - float sampleRate; SourceManager::SourceHandler handler; std::thread workerThread; iio_context* ctx = NULL; iio_device* phy = NULL; iio_device* dev = NULL; + iio_channel* rxLO = NULL; iio_channel* rxChan = NULL; bool running = false; - bool ipMode = true; double freq; char ip[1024] = "ip:192.168.2.1"; + float sampleRate = 4000000; + int bandwidth = 0; int gainMode = 0; float gain = 0; + int srId = 0; + int bwId = 0; OptionList samplerates; + OptionList bandwidths; OptionList gainModes; }; MOD_EXPORT void _INIT_() { - json defConf; - defConf["IP"] = "192.168.2.1"; - defConf["sampleRate"] = 4000000.0f; - defConf["gainMode"] = 0; - defConf["gain"] = 0.0f; + json defConf = {}; config.setPath(core::args["root"].s() + "/plutosdr_source_config.json"); config.load(defConf); config.enableAutoSave(); From e6a02a3944d5a7af2f7b7ee1023e909cb2a16e90 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sat, 27 Jan 2024 22:52:11 +0100 Subject: [PATCH 47/65] fix bad plutosdr bandwidth selection at very high samplerates --- source_modules/plutosdr_source/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index e6abc8dd..d0684758 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -285,7 +285,7 @@ private: iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", bw); } else { - iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", sampleRate); + iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", std::min(sampleRate, 52000000)); } } From 31c9e5767e43b26bb085c62379906ca67f5ed3a6 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 00:03:04 +0100 Subject: [PATCH 48/65] beginning of PlutoSDR context enumeration --- source_modules/plutosdr_source/src/main.cpp | 75 +++++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index d0684758..206ce84a 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -28,10 +28,6 @@ public: // Load configuration config.acquire(); - if (config.conf.contains("IP")) { - std::string _ip = config.conf["IP"]; - strcpy(&ip[3], _ip.c_str()); - } if (config.conf.contains("sampleRate")) { sampleRate = config.conf["sampleRate"]; } @@ -82,6 +78,12 @@ public: gainModes.define(2, "Slow Attack", "slow_attack"); gainModes.define(3, "Hybrid", "hybrid"); + // Enumerate devices + refresh(); + + // Select device + // TODO + // Register source handler.ctx = this; handler.selectHandler = menuSelected; @@ -128,6 +130,41 @@ private: return std::string(buf); } + void refresh() { + // Clear device list + devices.clear(); + + // Create scan context + iio_scan_context* sctx = iio_create_scan_context(NULL, 0); + if (!sctx) { + flog::error("Failed get scan context"); + return; + } + + // Enumerate devices + iio_context_info** ctxInfoList; + ssize_t count = iio_scan_context_get_info_list(sctx, &ctxInfoList); + if (count < 0) { + flog::error("Failed to enumerate contexts"); + return; + } + for (ssize_t i = 0; i < count; i++) { + iio_context_info* info = ctxInfoList[i]; + std::string desc = iio_context_info_get_description(info); + std::string uri = iio_context_info_get_uri(info); + + devices.define(uri, name, uri); + } + iio_context_info_list_free(ctxInfoList); + + // Destroy scan context + iio_scan_context_destroy(sctx); + } + + void select(const std::string& nuri) { + uri = nuri; + } + static void menuSelected(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; core::setInputSampleRate(_this->sampleRate); @@ -144,9 +181,9 @@ private: if (_this->running) { return; } // Open context - _this->ctx = iio_create_context_from_uri(_this->ip); + _this->ctx = iio_create_context_from_uri(_this->uri.c_str()); if (_this->ctx == NULL) { - flog::error("Could not open pluto"); + flog::error("Could not open pluto ({})", _this->uri); return; } @@ -223,16 +260,13 @@ private: PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; if (_this->running) { SmGui::BeginDisabled(); } - SmGui::LeftLabel("IP"); SmGui::FillWidth(); - if (SmGui::InputText(CONCAT("##_pluto_ip_", _this->name), &_this->ip[3], 16)) { - config.acquire(); - config.conf["IP"] = &_this->ip[3]; - config.release(true); + SmGui::ForceSync(); + if (SmGui::Combo("##plutosdr_dev_sel", &_this->devId, _this->devices.txt)) { + _this->select(_this->devices.value(_this->devId)); + // TODO: Save } - SmGui::LeftLabel("Samplerate"); - SmGui::FillWidth(); if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->samplerates.txt)) { _this->sampleRate = _this->samplerates.value(_this->srId); core::setInputSampleRate(_this->sampleRate); @@ -240,6 +274,16 @@ private: config.conf["sampleRate"] = _this->sampleRate; config.release(true); } + + // Refresh button + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_pluto_refr_", _this->name))) { + _this->refresh(); + _this->select(_this->uri); + + } if (_this->running) { SmGui::EndDisabled(); } SmGui::LeftLabel("Bandwidth"); @@ -343,16 +387,19 @@ private: iio_channel* rxChan = NULL; bool running = false; + std::string uri = ""; + double freq; - char ip[1024] = "ip:192.168.2.1"; float sampleRate = 4000000; int bandwidth = 0; int gainMode = 0; float gain = 0; + int devId = 0; int srId = 0; int bwId = 0; + OptionList devices; OptionList samplerates; OptionList bandwidths; OptionList gainModes; From 175992b08109a98f44455b87b6d49e464cba68ee Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 00:25:46 +0100 Subject: [PATCH 49/65] add android workaround --- source_modules/plutosdr_source/src/main.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 206ce84a..2a79f73f 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -153,12 +153,18 @@ private: std::string desc = iio_context_info_get_description(info); std::string uri = iio_context_info_get_uri(info); - devices.define(uri, name, uri); + devices.define(uri, desc, uri); } iio_context_info_list_free(ctxInfoList); // Destroy scan context iio_scan_context_destroy(sctx); + +#ifdef __ANDROID__ + // On Android, a default IP entry must be made (TODO: This is not ideal since the IP cannot be changed) + const char* androidURI = "ip:192.168.2.1"; + devices.define(androidURI, "Default (192.168.2.1)", androidURI); +#endif } void select(const std::string& nuri) { From 32cbd726fd19bc27ebdeffe5216a646a6c0e9453 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 02:16:20 +0100 Subject: [PATCH 50/65] implement enumeration and settings for plutosdr source --- source_modules/plutosdr_source/src/main.cpp | 196 ++++++++++++-------- 1 file changed, 121 insertions(+), 75 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 2a79f73f..868a4333 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -26,63 +27,32 @@ public: PlutoSDRSourceModule(std::string name) { this->name = name; - // Load configuration - config.acquire(); - if (config.conf.contains("sampleRate")) { - sampleRate = config.conf["sampleRate"]; - } - if (config.conf.contains("bandwidth")) { - bandwidth = config.conf["bandwidth"]; - } - if (config.conf.contains("gainMode")) { - gainMode = config.conf["gainMode"]; - } - if (config.conf.contains("gain")) { - gain = config.conf["gain"]; - } - config.release(); - // Define valid samplerates for (int sr = 1000000; sr <= 61440000; sr += 500000) { samplerates.define(sr, getBandwdithScaled(sr), sr); } samplerates.define(61440000, getBandwdithScaled(61440000.0), 61440000.0); - // Set samplerate ID - if (samplerates.keyExists(sampleRate)) { - srId = samplerates.keyId(sampleRate); - } - else { - srId = 0; - sampleRate = samplerates.value(srId); - } - // Define valid bandwidths bandwidths.define(0, "Auto", 0); for (int bw = 1000000.0; bw <= 52000000; bw += 500000) { bandwidths.define(bw, getBandwdithScaled(bw), bw); } - // Set bandwidth ID - if (bandwidths.keyExists(bandwidth)) { - bwId = bandwidths.keyId(bandwidth); - } - else { - bwId = 0; - bandwidth = bandwidths.value(bwId); - } - // Define gain modes - gainModes.define(0, "Manual", "manual"); - gainModes.define(1, "Fast Attack", "fast_attack"); - gainModes.define(2, "Slow Attack", "slow_attack"); - gainModes.define(3, "Hybrid", "hybrid"); + gainModes.define("manual", "Manual", "manual"); + gainModes.define("fast_attack", "Fast Attack", "fast_attack"); + gainModes.define("slow_attack", "Slow Attack", "slow_attack"); + gainModes.define("hybrid", "Hybrid", "hybrid"); // Enumerate devices refresh(); // Select device - // TODO + config.acquire(); + uri = config.conf["device"]; + config.release(); + select(uri); // Register source handler.ctx = this; @@ -168,12 +138,66 @@ private: } void select(const std::string& nuri) { + // If no device is available, give up + if (devices.empty()) { + uri.clear(); + return; + } + + // If the device is not available, select the first one + if (!devices.keyExists(nuri)) { + select(devices.key(0)); + } + + // Update URI uri = nuri; + + // TODO: Enumerate capabilities + + // Load device config + config.acquire(); + if (config.conf["devices"][uri.c_str()].contains("samplerate")) { + // Select given samplerate or default if invalid + samplerate = config.conf["devices"][uri]["samplerate"]; + if (samplerates.keyExists(samplerate)) { + srId = samplerates.keyId(samplerate); + } + else { + srId = 0; + samplerate = samplerates.value(srId); + } + } + if (config.conf["devices"][uri].contains("bandwidth")) { + // Select given bandwidth or default if invalid + bandwidth = config.conf["devices"][uri]["bandwidth"]; + if (bandwidths.keyExists(bandwidth)) { + bwId = bandwidths.keyId(bandwidth); + } + else { + bwId = 0; + bandwidth = bandwidths.value(bwId); + } + } + if (config.conf["devices"][uri].contains("gainMode")) { + // Select given gain mode or default if invalid + std::string gm = config.conf["devices"][uri]["gainMode"]; + if (gainModes.keyExists(gm)) { + gmId = gainModes.keyId(gm); + } + else { + gmId = 0; + } + } + if (config.conf["devices"][uri].contains("gain")) { + gain = config.conf["devices"][uri]["gain"]; + gain = std::clamp(gain, -1.0f, 73.0f); + } + config.release(); } static void menuSelected(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; - core::setInputSampleRate(_this->sampleRate); + core::setInputSampleRate(_this->samplerate); flog::info("PlutoSDRSourceModule '{0}': Menu Select!", _this->name); } @@ -217,15 +241,15 @@ private: // Configure RX channel iio_channel_attr_write(_this->rxChan, "rf_port_select", "A_BALANCED"); - iio_channel_attr_write_longlong(_this->rxLO, "frequency", round(_this->freq)); // Freq - iio_channel_attr_write_bool(_this->rxChan, "filter_fir_en", true); // Digital filter - iio_channel_attr_write_longlong(_this->rxChan, "sampling_frequency", round(_this->sampleRate)); // Sample rate - iio_channel_attr_write_longlong(_this->rxChan, "hardwaregain", round(_this->gain)); // Gain - iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); // Gain mode + iio_channel_attr_write_longlong(_this->rxLO, "frequency", round(_this->freq)); // Freq + iio_channel_attr_write_bool(_this->rxChan, "filter_fir_en", true); // Digital filter + iio_channel_attr_write_longlong(_this->rxChan, "sampling_frequency", round(_this->samplerate)); // Sample rate + iio_channel_attr_write_double(_this->rxChan, "hardwaregain", _this->gain); // Gain + iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gmId).c_str()); // Gain mode _this->setBandwidth(_this->bandwidth); // Configure the ADC filters - ad9361_set_bb_rate(_this->phy, round(_this->sampleRate)); + ad9361_set_bb_rate(_this->phy, round(_this->samplerate)); // Start worker thread _this->running = true; @@ -257,7 +281,7 @@ private: _this->freq = freq; if (_this->running) { // Tune device - iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(freq)); + iio_channel_attr_write_longlong(_this->rxLO, "frequency", round(freq)); } flog::info("PlutoSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); } @@ -270,15 +294,19 @@ private: SmGui::ForceSync(); if (SmGui::Combo("##plutosdr_dev_sel", &_this->devId, _this->devices.txt)) { _this->select(_this->devices.value(_this->devId)); - // TODO: Save + config.acquire(); + config.conf["device"] = _this->devices.key(_this->devId); + config.release(true); } if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->samplerates.txt)) { - _this->sampleRate = _this->samplerates.value(_this->srId); - core::setInputSampleRate(_this->sampleRate); - config.acquire(); - config.conf["sampleRate"] = _this->sampleRate; - config.release(true); + _this->samplerate = _this->samplerates.value(_this->srId); + core::setInputSampleRate(_this->samplerate); + if (!_this->uri.empty()) { + config.acquire(); + config.conf["devices"][_this->uri]["samplerate"] = _this->samplerate; + config.release(true); + } } // Refresh button @@ -299,35 +327,41 @@ private: if (_this->running) { _this->setBandwidth(_this->bandwidth); } - config.acquire(); - config.conf["bandwidth"] = _this->bandwidth; - config.release(true); + if (!_this->uri.empty()) { + config.acquire(); + config.conf["devices"][_this->uri]["bandwidth"] = _this->bandwidth; + config.release(true); + } } SmGui::LeftLabel("Gain Mode"); SmGui::FillWidth(); SmGui::ForceSync(); - if (SmGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, _this->gainModes.txt)) { + if (SmGui::Combo(CONCAT("##_pluto_gainmode_select_", _this->name), &_this->gmId, _this->gainModes.txt)) { if (_this->running) { - iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gainMode).c_str()); + iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gmId).c_str()); + } + if (!_this->uri.empty()) { + config.acquire(); + config.conf["devices"][_this->uri]["gainMode"] = _this->gainModes.key(_this->gmId); + config.release(true); } - config.acquire(); - config.conf["gainMode"] = _this->gainMode; - config.release(true); } - SmGui::LeftLabel("PGA Gain"); - if (_this->gainMode) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("Gain"); + if (_this->gmId) { SmGui::BeginDisabled(); } SmGui::FillWidth(); - if (SmGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76)) { + if (SmGui::SliderFloatWithSteps(CONCAT("##_pluto_gain__", _this->name), &_this->gain, -1.0f, 73.0f, 1.0f, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) { if (_this->running) { - iio_channel_attr_write_longlong(_this->rxChan, "hardwaregain", round(_this->gain)); + iio_channel_attr_write_double(_this->rxChan, "hardwaregain", _this->gain); + } + if (!_this->uri.empty()) { + config.acquire(); + config.conf["devices"][_this->uri]["gain"] = _this->gain; + config.release(true); } - config.acquire(); - config.conf["gain"] = _this->gain; - config.release(true); } - if (_this->gainMode) { SmGui::EndDisabled(); } + if (_this->gmId) { SmGui::EndDisabled(); } } void setBandwidth(int bw) { @@ -335,13 +369,13 @@ private: iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", bw); } else { - iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", std::min(sampleRate, 52000000)); + iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", std::min(samplerate, 52000000)); } } static void worker(void* ctx) { PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; - int blockSize = _this->sampleRate / 200.0f; + int blockSize = _this->samplerate / 200.0f; // Acquire channels iio_channel* rx0_i = iio_device_find_channel(_this->dev, "voltage0", 0); @@ -396,26 +430,38 @@ private: std::string uri = ""; double freq; - float sampleRate = 4000000; + int samplerate = 4000000; int bandwidth = 0; - int gainMode = 0; - float gain = 0; + float gain = -1; int devId = 0; int srId = 0; int bwId = 0; + int gmId = 0; OptionList devices; OptionList samplerates; OptionList bandwidths; - OptionList gainModes; + OptionList gainModes; }; MOD_EXPORT void _INIT_() { json defConf = {}; + defConf["device"] = ""; + defConf["devices"] = {}; config.setPath(core::args["root"].s() + "/plutosdr_source_config.json"); config.load(defConf); config.enableAutoSave(); + + // Reset the configuration if the old format is still used + config.acquire(); + if (!config.conf.contains("device") || !config.conf.contains("devices")) { + config.conf = defConf; + config.release(true); + } + else { + config.release(); + } } MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { From 1cd8c2510a1634fea65f0672478e2ef26cc6ef7f Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 14:38:36 +0100 Subject: [PATCH 51/65] fix plutosdr source error checking --- source_modules/plutosdr_source/src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 868a4333..3a9a019a 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -380,6 +380,10 @@ private: // Acquire channels iio_channel* rx0_i = iio_device_find_channel(_this->dev, "voltage0", 0); iio_channel* rx0_q = iio_device_find_channel(_this->dev, "voltage1", 0); + if (!rx0_i || !rx0_q) { + flog::error("Failed to acquire RX channels"); + return; + } // Start streaming iio_channel_enable(rx0_i); @@ -399,6 +403,7 @@ private: // Get buffer pointer int16_t* buf = (int16_t*)iio_buffer_first(rxbuf, rx0_i); + if (!buf) { break; } // Convert samples to CF32 volk_16i_s32f_convert_32f((float*)_this->stream.writeBuf, buf, 32768.0f, blockSize * 2); From 6b5de78e80bc6a61bee6470a7767a9fc89bd3af9 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 14:39:01 +0100 Subject: [PATCH 52/65] fix plutosdr source not updating samplerate on select --- source_modules/plutosdr_source/src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 3a9a019a..265ffc17 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -193,6 +193,9 @@ private: gain = std::clamp(gain, -1.0f, 73.0f); } config.release(); + + // Update core samplerate + core::setInputSampleRate(samplerate); } static void menuSelected(void* ctx) { From 97d0a07ec7566c80c5eb59da3a060d098762a2d6 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 15:26:34 +0100 Subject: [PATCH 53/65] fix plutosdr commit persistence --- source_modules/plutosdr_source/src/main.cpp | 100 +++++++++++--------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 265ffc17..8ed917a0 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -50,9 +50,9 @@ public: // Select device config.acquire(); - uri = config.conf["device"]; + devName = config.conf["device"]; config.release(); - select(uri); + select(devName); // Register source handler.ctx = this; @@ -121,9 +121,9 @@ private: for (ssize_t i = 0; i < count; i++) { iio_context_info* info = ctxInfoList[i]; std::string desc = iio_context_info_get_description(info); - std::string uri = iio_context_info_get_uri(info); + std::string duri = iio_context_info_get_uri(info); - devices.define(uri, desc, uri); + devices.define(desc, desc, duri); } iio_context_info_list_free(ctxInfoList); @@ -133,54 +133,46 @@ private: #ifdef __ANDROID__ // On Android, a default IP entry must be made (TODO: This is not ideal since the IP cannot be changed) const char* androidURI = "ip:192.168.2.1"; - devices.define(androidURI, "Default (192.168.2.1)", androidURI); + const char* androidName = "Default (192.168.2.1)"; + devices.define(androidName, androidName, androidURI); #endif } - void select(const std::string& nuri) { + void select(const std::string& name) { // If no device is available, give up if (devices.empty()) { - uri.clear(); + devName.clear(); return; } // If the device is not available, select the first one - if (!devices.keyExists(nuri)) { + if (!devices.keyExists(name)) { select(devices.key(0)); } // Update URI - uri = nuri; + devName = name; + uri = devices.value(devices.keyId(name)); // TODO: Enumerate capabilities + // Load defaults + samplerate = 4000000; + bandwidth = 0; + gmId = 0; + gain = -1.0f; + // Load device config config.acquire(); - if (config.conf["devices"][uri.c_str()].contains("samplerate")) { - // Select given samplerate or default if invalid - samplerate = config.conf["devices"][uri]["samplerate"]; - if (samplerates.keyExists(samplerate)) { - srId = samplerates.keyId(samplerate); - } - else { - srId = 0; - samplerate = samplerates.value(srId); - } + if (config.conf["devices"][devName].contains("samplerate")) { + samplerate = config.conf["devices"][devName]["samplerate"]; } - if (config.conf["devices"][uri].contains("bandwidth")) { - // Select given bandwidth or default if invalid - bandwidth = config.conf["devices"][uri]["bandwidth"]; - if (bandwidths.keyExists(bandwidth)) { - bwId = bandwidths.keyId(bandwidth); - } - else { - bwId = 0; - bandwidth = bandwidths.value(bwId); - } + if (config.conf["devices"][devName].contains("bandwidth")) { + bandwidth = config.conf["devices"][devName]["bandwidth"]; } - if (config.conf["devices"][uri].contains("gainMode")) { + if (config.conf["devices"][devName].contains("gainMode")) { // Select given gain mode or default if invalid - std::string gm = config.conf["devices"][uri]["gainMode"]; + std::string gm = config.conf["devices"][devName]["gainMode"]; if (gainModes.keyExists(gm)) { gmId = gainModes.keyId(gm); } @@ -188,11 +180,29 @@ private: gmId = 0; } } - if (config.conf["devices"][uri].contains("gain")) { - gain = config.conf["devices"][uri]["gain"]; + if (config.conf["devices"][devName].contains("gain")) { + gain = config.conf["devices"][devName]["gain"]; gain = std::clamp(gain, -1.0f, 73.0f); } config.release(); + + // Update samplerate ID + if (samplerates.keyExists(samplerate)) { + srId = samplerates.keyId(samplerate); + } + else { + srId = 0; + samplerate = samplerates.value(srId); + } + + // Update bandwidth ID + if (bandwidths.keyExists(bandwidth)) { + bwId = bandwidths.keyId(bandwidth); + } + else { + bwId = 0; + bandwidth = bandwidths.value(bwId); + } // Update core samplerate core::setInputSampleRate(samplerate); @@ -213,6 +223,9 @@ private: PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx; if (_this->running) { return; } + // If no device is selected, give up + if (_this->devName.empty() || _this->uri.empty()) { return; } + // Open context _this->ctx = iio_create_context_from_uri(_this->uri.c_str()); if (_this->ctx == NULL) { @@ -296,7 +309,7 @@ private: SmGui::FillWidth(); SmGui::ForceSync(); if (SmGui::Combo("##plutosdr_dev_sel", &_this->devId, _this->devices.txt)) { - _this->select(_this->devices.value(_this->devId)); + _this->select(_this->devices.key(_this->devId)); config.acquire(); config.conf["device"] = _this->devices.key(_this->devId); config.release(true); @@ -305,9 +318,9 @@ private: if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->samplerates.txt)) { _this->samplerate = _this->samplerates.value(_this->srId); core::setInputSampleRate(_this->samplerate); - if (!_this->uri.empty()) { + if (!_this->devName.empty()) { config.acquire(); - config.conf["devices"][_this->uri]["samplerate"] = _this->samplerate; + config.conf["devices"][_this->devName]["samplerate"] = _this->samplerate; config.release(true); } } @@ -318,7 +331,7 @@ private: SmGui::ForceSync(); if (SmGui::Button(CONCAT("Refresh##_pluto_refr_", _this->name))) { _this->refresh(); - _this->select(_this->uri); + _this->select(_this->devName); } if (_this->running) { SmGui::EndDisabled(); } @@ -330,9 +343,9 @@ private: if (_this->running) { _this->setBandwidth(_this->bandwidth); } - if (!_this->uri.empty()) { + if (!_this->devName.empty()) { config.acquire(); - config.conf["devices"][_this->uri]["bandwidth"] = _this->bandwidth; + config.conf["devices"][_this->devName]["bandwidth"] = _this->bandwidth; config.release(true); } } @@ -344,9 +357,9 @@ private: if (_this->running) { iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gmId).c_str()); } - if (!_this->uri.empty()) { + if (!_this->devName.empty()) { config.acquire(); - config.conf["devices"][_this->uri]["gainMode"] = _this->gainModes.key(_this->gmId); + config.conf["devices"][_this->devName]["gainMode"] = _this->gainModes.key(_this->gmId); config.release(true); } } @@ -358,9 +371,9 @@ private: if (_this->running) { iio_channel_attr_write_double(_this->rxChan, "hardwaregain", _this->gain); } - if (!_this->uri.empty()) { + if (!_this->devName.empty()) { config.acquire(); - config.conf["devices"][_this->uri]["gain"] = _this->gain; + config.conf["devices"][_this->devName]["gain"] = _this->gain; config.release(true); } } @@ -435,6 +448,7 @@ private: iio_channel* rxChan = NULL; bool running = false; + std::string devName = ""; std::string uri = ""; double freq; From bc77bab45fdfa6935dad671ddbcd18f675d6b8a1 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 17:23:18 +0100 Subject: [PATCH 54/65] improved plutosdr device naming --- source_modules/plutosdr_source/src/main.cpp | 95 +++++++++++++++------ 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/source_modules/plutosdr_source/src/main.cpp b/source_modules/plutosdr_source/src/main.cpp index 8ed917a0..c0af537c 100644 --- a/source_modules/plutosdr_source/src/main.cpp +++ b/source_modules/plutosdr_source/src/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) @@ -50,9 +51,9 @@ public: // Select device config.acquire(); - devName = config.conf["device"]; + devDesc = config.conf["device"]; config.release(); - select(devName); + select(devDesc); // Register source handler.ctx = this; @@ -111,6 +112,11 @@ private: return; } + // Create parsing regexes + std::regex backendRgx(".+(?=:)", std::regex::ECMAScript); + std::regex modelRgx("\\(.+(?=\\),)", std::regex::ECMAScript); + std::regex serialRgx("serial=[0-9A-Za-z]+", std::regex::ECMAScript); + // Enumerate devices iio_context_info** ctxInfoList; ssize_t count = iio_scan_context_get_info_list(sctx, &ctxInfoList); @@ -123,7 +129,42 @@ private: std::string desc = iio_context_info_get_description(info); std::string duri = iio_context_info_get_uri(info); - devices.define(desc, desc, duri); + // If the device is not a plutosdr, don't include it + if (desc.find("PlutoSDR") == std::string::npos) { + flog::warn("Ignored IIO device: [{}] {}", duri, desc); + continue; + } + + // Extract the backend + std::string backend = "unknown"; + std::smatch backendMatch; + if (std::regex_search(duri, backendMatch, backendRgx)) { + backend = backendMatch[0]; + } + + // Extract the model + std::string model = "Unknown"; + std::smatch modelMatch; + if (std::regex_search(desc, modelMatch, modelRgx)) { + model = modelMatch[0]; + int parenthPos = model.find('('); + if (parenthPos != std::string::npos) { + model = model.substr(parenthPos+1); + } + } + + // Extract the serial + std::string serial = "unknown"; + std::smatch serialMatch; + if (std::regex_search(desc, serialMatch, serialRgx)) { + serial = serialMatch[0].str().substr(7); + } + + // Construct the device name + std::string devName = '(' + backend + ") " + model + " [" + serial + ']'; + + // Save device + devices.define(desc, devName, duri); } iio_context_info_list_free(ctxInfoList); @@ -138,21 +179,21 @@ private: #endif } - void select(const std::string& name) { + void select(const std::string& desc) { // If no device is available, give up if (devices.empty()) { - devName.clear(); + devDesc.clear(); return; } // If the device is not available, select the first one - if (!devices.keyExists(name)) { + if (!devices.keyExists(desc)) { select(devices.key(0)); } // Update URI - devName = name; - uri = devices.value(devices.keyId(name)); + devDesc = desc; + uri = devices.value(devices.keyId(desc)); // TODO: Enumerate capabilities @@ -164,15 +205,15 @@ private: // Load device config config.acquire(); - if (config.conf["devices"][devName].contains("samplerate")) { - samplerate = config.conf["devices"][devName]["samplerate"]; + if (config.conf["devices"][devDesc].contains("samplerate")) { + samplerate = config.conf["devices"][devDesc]["samplerate"]; } - if (config.conf["devices"][devName].contains("bandwidth")) { - bandwidth = config.conf["devices"][devName]["bandwidth"]; + if (config.conf["devices"][devDesc].contains("bandwidth")) { + bandwidth = config.conf["devices"][devDesc]["bandwidth"]; } - if (config.conf["devices"][devName].contains("gainMode")) { + if (config.conf["devices"][devDesc].contains("gainMode")) { // Select given gain mode or default if invalid - std::string gm = config.conf["devices"][devName]["gainMode"]; + std::string gm = config.conf["devices"][devDesc]["gainMode"]; if (gainModes.keyExists(gm)) { gmId = gainModes.keyId(gm); } @@ -180,8 +221,8 @@ private: gmId = 0; } } - if (config.conf["devices"][devName].contains("gain")) { - gain = config.conf["devices"][devName]["gain"]; + if (config.conf["devices"][devDesc].contains("gain")) { + gain = config.conf["devices"][devDesc]["gain"]; gain = std::clamp(gain, -1.0f, 73.0f); } config.release(); @@ -224,7 +265,7 @@ private: if (_this->running) { return; } // If no device is selected, give up - if (_this->devName.empty() || _this->uri.empty()) { return; } + if (_this->devDesc.empty() || _this->uri.empty()) { return; } // Open context _this->ctx = iio_create_context_from_uri(_this->uri.c_str()); @@ -318,9 +359,9 @@ private: if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->samplerates.txt)) { _this->samplerate = _this->samplerates.value(_this->srId); core::setInputSampleRate(_this->samplerate); - if (!_this->devName.empty()) { + if (!_this->devDesc.empty()) { config.acquire(); - config.conf["devices"][_this->devName]["samplerate"] = _this->samplerate; + config.conf["devices"][_this->devDesc]["samplerate"] = _this->samplerate; config.release(true); } } @@ -331,7 +372,7 @@ private: SmGui::ForceSync(); if (SmGui::Button(CONCAT("Refresh##_pluto_refr_", _this->name))) { _this->refresh(); - _this->select(_this->devName); + _this->select(_this->devDesc); } if (_this->running) { SmGui::EndDisabled(); } @@ -343,9 +384,9 @@ private: if (_this->running) { _this->setBandwidth(_this->bandwidth); } - if (!_this->devName.empty()) { + if (!_this->devDesc.empty()) { config.acquire(); - config.conf["devices"][_this->devName]["bandwidth"] = _this->bandwidth; + config.conf["devices"][_this->devDesc]["bandwidth"] = _this->bandwidth; config.release(true); } } @@ -357,9 +398,9 @@ private: if (_this->running) { iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gmId).c_str()); } - if (!_this->devName.empty()) { + if (!_this->devDesc.empty()) { config.acquire(); - config.conf["devices"][_this->devName]["gainMode"] = _this->gainModes.key(_this->gmId); + config.conf["devices"][_this->devDesc]["gainMode"] = _this->gainModes.key(_this->gmId); config.release(true); } } @@ -371,9 +412,9 @@ private: if (_this->running) { iio_channel_attr_write_double(_this->rxChan, "hardwaregain", _this->gain); } - if (!_this->devName.empty()) { + if (!_this->devDesc.empty()) { config.acquire(); - config.conf["devices"][_this->devName]["gain"] = _this->gain; + config.conf["devices"][_this->devDesc]["gain"] = _this->gain; config.release(true); } } @@ -448,7 +489,7 @@ private: iio_channel* rxChan = NULL; bool running = false; - std::string devName = ""; + std::string devDesc = ""; std::string uri = ""; double freq; From 54276177ae7a1f215edc79f6597695f98d75d8bc Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 17:25:27 +0100 Subject: [PATCH 55/65] update github action version --- .github/workflows/build_all.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index 867fcf07..a92b1880 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -18,7 +18,7 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build @@ -88,7 +88,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build @@ -142,7 +142,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build @@ -164,7 +164,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/debian_bullseye && docker build . --tag sdrpp_build @@ -186,7 +186,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && docker build . --tag sdrpp_build @@ -208,7 +208,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/debian_sid && docker build . --tag sdrpp_build @@ -230,7 +230,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_focal && docker build . --tag sdrpp_build @@ -252,7 +252,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_jammy && docker build . --tag sdrpp_build @@ -274,7 +274,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Docker Image run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && docker build . --tag sdrpp_build @@ -296,7 +296,7 @@ jobs: runs-on: ARM steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Create Build Environment run: rm -rf ${{runner.workspace}}/build && cmake -E make_directory ${{runner.workspace}}/build @@ -323,7 +323,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Fetch container working-directory: ${{runner.workspace}} @@ -390,7 +390,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install codespell run: sudo apt update -y && sudo apt install -y codespell @@ -402,7 +402,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Run check_clang_format run: cd $GITHUB_WORKSPACE && chmod +x ./check_clang_format.sh && ./check_clang_format.sh || true From 11a7c382e8734e0bdef4e00dd235fb391b7904b2 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 17:28:07 +0100 Subject: [PATCH 56/65] update more github action versions --- .github/workflows/build_all.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_all.yml b/.github/workflows/build_all.yml index a92b1880..03a97fa5 100644 --- a/.github/workflows/build_all.yml +++ b/.github/workflows/build_all.yml @@ -79,7 +79,7 @@ jobs: run: '&($Env:GITHUB_WORKSPACE + "/make_windows_package.ps1") ./build ($Env:GITHUB_WORKSPACE + "/root")' - name: Save Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_windows_x64 path: ${{runner.workspace}}/sdrpp_windows_x64.zip @@ -133,7 +133,7 @@ jobs: run: cd $GITHUB_WORKSPACE && sh make_macos_bundle.sh ${{runner.workspace}}/build ./SDR++.app && zip -r ${{runner.workspace}}/sdrpp_macos_intel.zip SDR++.app - name: Save Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_macos_intel path: ${{runner.workspace}}/sdrpp_macos_intel.zip @@ -155,7 +155,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_debian_buster_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -177,7 +177,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_debian_bullseye_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -199,7 +199,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_debian_bookworm_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -221,7 +221,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_debian_sid_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -243,7 +243,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_ubuntu_focal_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -265,7 +265,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_ubuntu_jammy_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -287,7 +287,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_ubuntu_mantic_amd64 path: ${{runner.workspace}}/sdrpp_debian_amd64.deb @@ -314,7 +314,7 @@ jobs: run: sh $GITHUB_WORKSPACE/make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev' && mv sdrpp_debian_amd64.deb sdrpp_debian_armhf.deb - name: Save Deb Archive - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_raspios_bullseye_armhf path: ${{runner.workspace}}/sdrpp_debian_armhf.deb @@ -341,7 +341,7 @@ jobs: run: docker cp build:/root/SDRPlusPlus/android/app/build/outputs/apk/debug/app-debug.apk ./ && mv app-debug.apk sdrpp.apk - name: Save APK - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: sdrpp_android path: ${{runner.workspace}}/sdrpp.apk @@ -352,7 +352,7 @@ jobs: steps: - name: Download All Builds - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Create Archive run: > @@ -369,7 +369,7 @@ jobs: mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb && mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: sdrpp_all path: sdrpp_all/ @@ -381,7 +381,7 @@ jobs: steps: - name: Download All Builds - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Update Nightly run: gh release upload nightly sdrpp_all/* -R ${{github.repository}} --clobber From be8edbfa9e9568f260a694250d8b1e4274a8c624 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Sun, 28 Jan 2024 21:46:54 +0100 Subject: [PATCH 57/65] revamp sdr++ server source networking code --- .../sdrpp_server_source/src/main.cpp | 20 +- .../src/sdrpp_server_client.cpp | 187 +++++++++--------- .../src/sdrpp_server_client.h | 22 +-- 3 files changed, 117 insertions(+), 112 deletions(-) diff --git a/source_modules/sdrpp_server_source/src/main.cpp b/source_modules/sdrpp_server_source/src/main.cpp index 38c187de..08c7e063 100644 --- a/source_modules/sdrpp_server_source/src/main.cpp +++ b/source_modules/sdrpp_server_source/src/main.cpp @@ -17,7 +17,7 @@ SDRPP_MOD_INFO{ /* Name: */ "sdrpp_server_source", /* Description: */ "SDR++ Server source module for SDR++", /* Author: */ "Ryzerth", - /* Version: */ 0, 1, 0, + /* Version: */ 0, 2, 0, /* Max instances */ 1 }; @@ -109,10 +109,10 @@ private: SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; if (_this->running) { return; } - // Try to connect if not already connected - if (!_this->client) { + // Try to connect if not already connected (Play button is locked anyway so not sure why I put this here) + if (!_this->connected()) { _this->tryConnect(); - if (!_this->client) { return; } + if (!_this->connected()) { return; } } // Set configuration @@ -127,7 +127,7 @@ private: SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; if (!_this->running) { return; } - if (_this->client) { _this->client->stop(); } + if (_this->connected()) { _this->client->stop(); } _this->running = false; flog::info("SDRPPServerSourceModule '{0}': Stop!", _this->name); @@ -135,7 +135,7 @@ private: static void tune(double freq, void* ctx) { SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; - if (_this->running && _this->client) { + if (_this->running && _this->connected()) { _this->client->setFrequency(freq); } _this->freq = freq; @@ -146,7 +146,7 @@ private: SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx; float menuWidth = ImGui::GetContentRegionAvail().x; - bool connected = (_this->client && _this->client->isOpen()); + bool connected = _this->connected(); gui::mainWindow.playButtonLocked = !connected; ImGui::GenericDialog("##sdrpp_srv_src_err_dialog", _this->serverBusy, GENERIC_DIALOG_BUTTONS_OK, [=](){ @@ -227,6 +227,10 @@ private: } } + bool connected() { + return client && client->isOpen(); + } + void tryConnect() { try { if (client) { client.reset(); } @@ -281,7 +285,7 @@ private: int sampleTypeId; bool compression = false; - server::Client client; + std::shared_ptr client; }; MOD_EXPORT void _INIT_() { diff --git a/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp b/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp index 738e504b..0e25efe7 100644 --- a/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp +++ b/source_modules/sdrpp_server_source/src/sdrpp_server_client.cpp @@ -7,8 +7,8 @@ using namespace std::chrono_literals; namespace server { - ClientClass::ClientClass(net::Conn conn, dsp::stream* out) { - client = std::move(conn); + Client::Client(std::shared_ptr sock, dsp::stream* out) { + this->sock = sock; output = out; // Allocate buffers @@ -37,8 +37,8 @@ namespace server { decomp.start(); link.start(); - // Start readers - client->readAsync(sizeof(PacketHeader), rbuffer, tcpHandler, this); + // Start worker thread + workerThread = std::thread(&Client::worker, this); // Ask for a UI int res = getUI(); @@ -46,14 +46,14 @@ namespace server { else if (res == -2) { throw std::runtime_error("Server busy"); } } - ClientClass::~ClientClass() { + Client::~Client() { close(); ZSTD_freeDCtx(dctx); delete[] rbuffer; delete[] sbuffer; } - void ClientClass::showMenu() { + void Client::showMenu() { std::string diffId = ""; SmGui::DrawListElem diffValue; bool syncRequired = false; @@ -96,8 +96,8 @@ namespace server { } } - void ClientClass::setFrequency(double freq) { - if (!client || !client->isOpen()) { return; } + void Client::setFrequency(double freq) { + if (!isOpen()) { return; } *(double*)s_cmd_data = freq; sendCommand(COMMAND_SET_FREQUENCY, sizeof(double)); auto waiter = awaitCommandAck(COMMAND_SET_FREQUENCY); @@ -105,119 +105,126 @@ namespace server { waiter->handled(); } - double ClientClass::getSampleRate() { + double Client::getSampleRate() { return currentSampleRate; } - void ClientClass::setSampleType(dsp::compression::PCMType type) { + void Client::setSampleType(dsp::compression::PCMType type) { + if (!isOpen()) { return; } s_cmd_data[0] = type; sendCommand(COMMAND_SET_SAMPLE_TYPE, 1); } - void ClientClass::setCompression(bool enabled) { + void Client::setCompression(bool enabled) { + if (!isOpen()) { return; } s_cmd_data[0] = enabled; sendCommand(COMMAND_SET_COMPRESSION, 1); } - void ClientClass::start() { - if (!client || !client->isOpen()) { return; } + void Client::start() { + if (!isOpen()) { return; } sendCommand(COMMAND_START, 0); getUI(); } - void ClientClass::stop() { - if (!client || !client->isOpen()) { return; } + void Client::stop() { + if (!isOpen()) { return; } sendCommand(COMMAND_STOP, 0); getUI(); } - void ClientClass::close() { + void Client::close() { + // Stop worker + decompIn.stopWriter(); + if (sock) { sock->close(); } + if (workerThread.joinable()) { workerThread.join(); } + decompIn.clearWriteStop(); + + // Stop DSP decomp.stop(); link.stop(); - decompIn.stopWriter(); - client->close(); - decompIn.clearWriteStop(); } - bool ClientClass::isOpen() { - return client->isOpen(); + bool Client::isOpen() { + return sock && sock->isOpen(); } - void ClientClass::tcpHandler(int count, uint8_t* buf, void* ctx) { - ClientClass* _this = (ClientClass*)ctx; - - // Read the rest of the data (TODO: CHECK SIZE OR SHIT WILL BE FUCKED) - int len = 0; - int read = 0; - int goal = _this->r_pkt_hdr->size - sizeof(PacketHeader); - while (len < goal) { - read = _this->client->read(goal - len, &buf[sizeof(PacketHeader) + len]); - if (read < 0) { - return; - }; - len += read; - } - _this->bytes += _this->r_pkt_hdr->size; - - if (_this->r_pkt_hdr->type == PACKET_TYPE_COMMAND) { - // TODO: Move to command handler - if (_this->r_cmd_hdr->cmd == COMMAND_SET_SAMPLERATE && _this->r_pkt_hdr->size == sizeof(PacketHeader) + sizeof(CommandHeader) + sizeof(double)) { - _this->currentSampleRate = *(double*)_this->r_cmd_data; - core::setInputSampleRate(_this->currentSampleRate); + void Client::worker() { + while (true) { + // Receive header + if (sock->recv(rbuffer, sizeof(PacketHeader), true) <= 0) { + break; } - else if (_this->r_cmd_hdr->cmd == COMMAND_DISCONNECT) { - flog::error("Asked to disconnect by the server"); - _this->serverBusy = true; - // Cancel waiters + // Receive remaining data + if (sock->recv(&rbuffer[sizeof(PacketHeader)], r_pkt_hdr->size - sizeof(PacketHeader), true, PROTOCOL_TIMEOUT_MS) <= 0) { + break; + } + + // Increment data counter + bytes += r_pkt_hdr->size; + + // Decode packet + if (r_pkt_hdr->type == PACKET_TYPE_COMMAND) { + // TODO: Move to command handler + if (r_cmd_hdr->cmd == COMMAND_SET_SAMPLERATE && r_pkt_hdr->size == sizeof(PacketHeader) + sizeof(CommandHeader) + sizeof(double)) { + currentSampleRate = *(double*)r_cmd_data; + core::setInputSampleRate(currentSampleRate); + } + else if (r_cmd_hdr->cmd == COMMAND_DISCONNECT) { + flog::error("Asked to disconnect by the server"); + serverBusy = true; + + // Cancel waiters + std::vector toBeRemoved; + for (auto& [waiter, cmd] : commandAckWaiters) { + waiter->cancel(); + toBeRemoved.push_back(waiter); + } + + // Remove handled waiters + for (auto& waiter : toBeRemoved) { + commandAckWaiters.erase(waiter); + delete waiter; + } + } + } + else if (r_pkt_hdr->type == PACKET_TYPE_COMMAND_ACK) { + // Notify waiters std::vector toBeRemoved; - for (auto& [waiter, cmd] : _this->commandAckWaiters) { - waiter->cancel(); + for (auto& [waiter, cmd] : commandAckWaiters) { + if (cmd != r_cmd_hdr->cmd) { continue; } + waiter->notify(); toBeRemoved.push_back(waiter); } // Remove handled waiters for (auto& waiter : toBeRemoved) { - _this->commandAckWaiters.erase(waiter); + commandAckWaiters.erase(waiter); delete waiter; } } - } - else if (_this->r_pkt_hdr->type == PACKET_TYPE_COMMAND_ACK) { - // Notify waiters - std::vector toBeRemoved; - for (auto& [waiter, cmd] : _this->commandAckWaiters) { - if (cmd != _this->r_cmd_hdr->cmd) { continue; } - waiter->notify(); - toBeRemoved.push_back(waiter); + else if (r_pkt_hdr->type == PACKET_TYPE_BASEBAND) { + memcpy(decompIn.writeBuf, &rbuffer[sizeof(PacketHeader)], r_pkt_hdr->size - sizeof(PacketHeader)); + if (!decompIn.swap(r_pkt_hdr->size - sizeof(PacketHeader))) { break; } } - - // Remove handled waiters - for (auto& waiter : toBeRemoved) { - _this->commandAckWaiters.erase(waiter); - delete waiter; + else if (r_pkt_hdr->type == PACKET_TYPE_BASEBAND_COMPRESSED) { + size_t outCount = ZSTD_decompressDCtx(dctx, decompIn.writeBuf, STREAM_BUFFER_SIZE, r_pkt_data, r_pkt_hdr->size - sizeof(PacketHeader)); + if (outCount) { + if (!decompIn.swap(outCount)) { break; } + }; + } + else if (r_pkt_hdr->type == PACKET_TYPE_ERROR) { + flog::error("SDR++ Server Error: {0}", rbuffer[sizeof(PacketHeader)]); + } + else { + flog::error("Invalid packet type: {0}", r_pkt_hdr->type); } } - else if (_this->r_pkt_hdr->type == PACKET_TYPE_BASEBAND) { - memcpy(_this->decompIn.writeBuf, &buf[sizeof(PacketHeader)], _this->r_pkt_hdr->size - sizeof(PacketHeader)); - _this->decompIn.swap(_this->r_pkt_hdr->size - sizeof(PacketHeader)); - } - else if (_this->r_pkt_hdr->type == PACKET_TYPE_BASEBAND_COMPRESSED) { - size_t outCount = ZSTD_decompressDCtx(_this->dctx, _this->decompIn.writeBuf, STREAM_BUFFER_SIZE, _this->r_pkt_data, _this->r_pkt_hdr->size - sizeof(PacketHeader)); - if (outCount) { _this->decompIn.swap(outCount); }; - } - else if (_this->r_pkt_hdr->type == PACKET_TYPE_ERROR) { - flog::error("SDR++ Server Error: {0}", buf[sizeof(PacketHeader)]); - } - else { - flog::error("Invalid packet type: {0}", _this->r_pkt_hdr->type); - } - - // Restart an async read - _this->client->readAsync(sizeof(PacketHeader), _this->rbuffer, tcpHandler, _this); } - int ClientClass::getUI() { + int Client::getUI() { + if (!isOpen()) { return -1; } auto waiter = awaitCommandAck(COMMAND_GET_UI); sendCommand(COMMAND_GET_UI, 0); if (waiter->await(PROTOCOL_TIMEOUT_MS)) { @@ -233,37 +240,35 @@ namespace server { return 0; } - void ClientClass::sendPacket(PacketType type, int len) { + void Client::sendPacket(PacketType type, int len) { s_pkt_hdr->type = type; s_pkt_hdr->size = sizeof(PacketHeader) + len; - client->write(s_pkt_hdr->size, sbuffer); + sock->send(sbuffer, s_pkt_hdr->size); } - void ClientClass::sendCommand(Command cmd, int len) { + void Client::sendCommand(Command cmd, int len) { s_cmd_hdr->cmd = cmd; sendPacket(PACKET_TYPE_COMMAND, sizeof(CommandHeader) + len); } - void ClientClass::sendCommandAck(Command cmd, int len) { + void Client::sendCommandAck(Command cmd, int len) { s_cmd_hdr->cmd = cmd; sendPacket(PACKET_TYPE_COMMAND_ACK, sizeof(CommandHeader) + len); } - PacketWaiter* ClientClass::awaitCommandAck(Command cmd) { + PacketWaiter* Client::awaitCommandAck(Command cmd) { PacketWaiter* waiter = new PacketWaiter; commandAckWaiters[waiter] = cmd; return waiter; } - void ClientClass::dHandler(dsp::complex_t *data, int count, void *ctx) { - ClientClass* _this = (ClientClass*)ctx; + void Client::dHandler(dsp::complex_t *data, int count, void *ctx) { + Client* _this = (Client*)ctx; memcpy(_this->output->writeBuf, data, count * sizeof(dsp::complex_t)); _this->output->swap(count); } - Client connect(std::string host, uint16_t port, dsp::stream* out) { - net::Conn conn = net::connect(host, port); - if (!conn) { return NULL; } - return Client(new ClientClass(std::move(conn), out)); + std::shared_ptr connect(std::string host, uint16_t port, dsp::stream* out) { + return std::make_shared(net::connect(host, port), out); } } diff --git a/source_modules/sdrpp_server_source/src/sdrpp_server_client.h b/source_modules/sdrpp_server_source/src/sdrpp_server_client.h index d3b75fb2..d06c8499 100644 --- a/source_modules/sdrpp_server_source/src/sdrpp_server_client.h +++ b/source_modules/sdrpp_server_source/src/sdrpp_server_client.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include #include #include @@ -13,10 +13,6 @@ #include #include -#define RFSPACE_MAX_SIZE 8192 -#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000 -#define RFSPACE_TIMEOUT_MS 3000 - #define PROTOCOL_TIMEOUT_MS 10000 namespace server { @@ -75,10 +71,10 @@ namespace server { std::mutex handledMtx; }; - class ClientClass { + class Client { public: - ClientClass(net::Conn conn, dsp::stream* out); - ~ClientClass(); + Client(std::shared_ptr sock, dsp::stream* out); + ~Client(); void showMenu(); @@ -98,7 +94,7 @@ namespace server { bool serverBusy = false; private: - static void tcpHandler(int count, uint8_t* buf, void* ctx); + void worker(); int getUI(); @@ -112,7 +108,7 @@ namespace server { static void dHandler(dsp::complex_t *data, int count, void *ctx); - net::Conn client; + std::shared_ptr sock; dsp::stream decompIn; dsp::compression::SampleStreamDecompressor decomp; @@ -137,10 +133,10 @@ namespace server { ZSTD_DCtx* dctx; + std::thread workerThread; + double currentSampleRate = 1000000.0; }; - typedef std::unique_ptr Client; - - Client connect(std::string host, uint16_t port, dsp::stream* out); + std::shared_ptr connect(std::string host, uint16_t port, dsp::stream* out); } From ff23d7e43f6f3024f87ab7161d295135def457d9 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 01:40:20 +0100 Subject: [PATCH 58/65] fix warnings --- core/libcorrect/src/convolutional/history_buffer.c | 2 +- misc_modules/frequency_manager/src/main.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/libcorrect/src/convolutional/history_buffer.c b/core/libcorrect/src/convolutional/history_buffer.c index f54ffdd9..52543b09 100644 --- a/core/libcorrect/src/convolutional/history_buffer.c +++ b/core/libcorrect/src/convolutional/history_buffer.c @@ -45,7 +45,7 @@ uint8_t *history_buffer_get_slice(history_buffer *buf) { return buf->history[buf shift_register_t history_buffer_search(history_buffer *buf, const distance_t *distances, unsigned int search_every) { - shift_register_t bestpath; + shift_register_t bestpath = 0; distance_t leasterror = USHRT_MAX; // search for a state with the least error for (shift_register_t state = 0; state < buf->num_states; state += search_every) { diff --git a/misc_modules/frequency_manager/src/main.cpp b/misc_modules/frequency_manager/src/main.cpp index 90d5fb69..f50f8bfd 100644 --- a/misc_modules/frequency_manager/src/main.cpp +++ b/misc_modules/frequency_manager/src/main.cpp @@ -531,7 +531,7 @@ private: ImGui::TableSetColumnIndex(0); if (ImGui::Button(("Import##_freq_mgr_imp_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0)) && !_this->importOpen) { _this->importOpen = true; - _this->importDialog = new pfd::open_file("Import bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, true); + _this->importDialog = new pfd::open_file("Import bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, pfd::opt::multiselect); } ImGui::TableSetColumnIndex(1); @@ -544,7 +544,7 @@ private: } config.release(); _this->exportOpen = true; - _this->exportDialog = new pfd::save_file("Export bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, true); + _this->exportDialog = new pfd::save_file("Export bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }); } if (selectedNames.size() == 0 && _this->selectedListName != "") { style::endDisabled(); } ImGui::EndTable(); @@ -787,7 +787,7 @@ private: void exportBookmarks(std::string path) { std::ofstream fs(path); - exportedBookmarks >> fs; + fs << exportedBookmarks; fs.close(); } From 14cb839863e4b75392b22642f5a371f958625ab4 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 18:43:46 +0100 Subject: [PATCH 59/65] clean up rds code and fix use before init --- decoder_modules/radio/src/demodulators/wfm.h | 67 ++----- decoder_modules/radio/src/rds.cpp | 181 +++++++++++-------- decoder_modules/radio/src/rds.h | 42 +++-- decoder_modules/radio/src/rds_demod.h | 87 +++++++++ 4 files changed, 229 insertions(+), 148 deletions(-) create mode 100644 decoder_modules/radio/src/rds_demod.h diff --git a/decoder_modules/radio/src/demodulators/wfm.h b/decoder_modules/radio/src/demodulators/wfm.h index 8c2c03ea..43a5093d 100644 --- a/decoder_modules/radio/src/demodulators/wfm.h +++ b/decoder_modules/radio/src/demodulators/wfm.h @@ -1,14 +1,7 @@ #pragma once #include "../demod.h" #include -#include -#include -#include -#include -#include -#include -#include -#include +#include "../rds_demod.h" #include #include #include @@ -52,59 +45,30 @@ namespace demod { } _config->release(modified); - // Define structure + // Init DSP demod.init(input, bandwidth / 2.0f, getIFSampleRate(), _stereo, _lowPass, _rds); - agc.init(&demod.rdsOut, 1.0, 1e6, 0.1); - costas.init(&agc.out, 0.005f); - - taps = dsp::taps::bandPass(0, 2375, 100, 5000); - fir.init(&costas.out, taps); - double baudfreq = dsp::math::hzToRads(2375.0/2.0, 5000); - costas2.init(&fir.out, 0.01, 0.0, baudfreq, baudfreq - (baudfreq*0.1), baudfreq + (baudfreq*0.1)); - - c2r.init(&costas2.out); - recov.init(&c2r.out, 5000.0 / (2375.0 / 2.0), 1e-6, 0.01, 0.01); - slice.init(&doubler.outA); - diff.init(&slice.out, 2); - hs.init(&diff.out, rdsHandler, this); - - doubler.init(&recov.out); - reshape.init(&doubler.outB, 4096, (1187 / 30) - 4096); + rdsDemod.init(&demod.rdsOut); + hs.init(&rdsDemod.out, rdsHandler, this); + reshape.init(&rdsDemod.soft, 4096, (1187 / 30) - 4096); diagHandler.init(&reshape.out, _diagHandler, this); + + // Init RDS display diag.lines.push_back(-0.8); diag.lines.push_back(0.8); } void start() { - agc.start(); - costas.start(); - fir.start(); - costas2.start(); - c2r.start(); demod.start(); - recov.start(); - slice.start(); - diff.start(); + rdsDemod.start(); hs.start(); - - doubler.start(); reshape.start(); diagHandler.start(); } void stop() { - agc.stop(); - costas.stop(); - fir.stop(); - costas2.stop(); - c2r.stop(); demod.stop(); - recov.stop(); - slice.stop(); - diff.stop(); + rdsDemod.stop(); hs.stop(); - - c2r.stop(); reshape.stop(); diagHandler.stop(); } @@ -320,24 +284,15 @@ namespace demod { } dsp::demod::BroadcastFM demod; - dsp::loop::FastAGC agc; - dsp::loop::Costas<2> costas; - dsp::tap taps; - dsp::filter::FIR fir; - dsp::loop::Costas<2> costas2; - dsp::convert::ComplexToReal c2r; - dsp::clock_recovery::MM recov; - dsp::digital::BinarySlicer slice; - dsp::digital::DifferentialDecoder diff; + RDSDemod rdsDemod; dsp::sink::Handler hs; EventHandler fftRedrawHandler; - dsp::routing::Doubler doubler; dsp::buffer::Reshaper reshape; dsp::sink::Handler diagHandler; ImGui::SymbolDiagram diag; - rds::RDSDecoder rdsDecode; + rds::Decoder rdsDecode; ConfigManager* _config = NULL; diff --git a/decoder_modules/radio/src/rds.cpp b/decoder_modules/radio/src/rds.cpp index c2f37e3a..354cfddf 100644 --- a/decoder_modules/radio/src/rds.cpp +++ b/decoder_modules/radio/src/rds.cpp @@ -30,7 +30,7 @@ namespace rds { const int DATA_LEN = 16; const int POLY_LEN = 10; - void RDSDecoder::process(uint8_t* symbols, int count) { + void Decoder::process(uint8_t* symbols, int count) { for (int i = 0; i < count; i++) { // Shift in the bit shiftReg = ((shiftReg << 1) & 0x3FFFFFF) | (symbols[i] & 1); @@ -86,7 +86,7 @@ namespace rds { } } - uint16_t RDSDecoder::calcSyndrome(uint32_t block) { + uint16_t Decoder::calcSyndrome(uint32_t block) { uint16_t syn = 0; // Calculate the syndrome using a LFSR @@ -105,7 +105,7 @@ namespace rds { return syn; } - uint32_t RDSDecoder::correctErrors(uint32_t block, BlockType type, bool& recovered) { + uint32_t Decoder::correctErrors(uint32_t block, BlockType type, bool& recovered) { // Subtract the offset from block block ^= (uint32_t)OFFSETS[type]; uint32_t out = block; @@ -134,24 +134,28 @@ namespace rds { return out; } - void RDSDecoder::decodeBlockA() { + void Decoder::decodeBlockA() { + // Acquire lock + std::lock_guard lck(blockAMtx); + // If it didn't decode properly return if (!blockAvail[BLOCK_TYPE_A]) { return; } - // Update timeout - std::lock_guard lck(groupMtx); - auto now = std::chrono::high_resolution_clock::now(); - blockALastUpdate = now; - // Decode PI code piCode = (blocks[BLOCK_TYPE_A] >> 10) & 0xFFFF; countryCode = (blocks[BLOCK_TYPE_A] >> 22) & 0xF; programCoverage = (AreaCoverage)((blocks[BLOCK_TYPE_A] >> 18) & 0xF); programRefNumber = (blocks[BLOCK_TYPE_A] >> 10) & 0xFF; decodeCallsign(); + + // Update timeout + blockALastUpdate = std::chrono::high_resolution_clock::now();; } - void RDSDecoder::decodeBlockB() { + void Decoder::decodeBlockB() { + // Acquire lock + std::lock_guard lck(blockBMtx); + // If it didn't decode properly return if (!blockAvail[BLOCK_TYPE_B]) { return; } @@ -162,76 +166,101 @@ namespace rds { // Decode traffic program and program type trafficProgram = (blocks[BLOCK_TYPE_B] >> 20) & 1; programType = (ProgramType)((blocks[BLOCK_TYPE_B] >> 15) & 0x1F); + + // Update timeout + blockBLastUpdate = std::chrono::high_resolution_clock::now(); } - void RDSDecoder::decodeGroup() { - std::lock_guard lck(groupMtx); - auto now = std::chrono::high_resolution_clock::now(); + void Decoder::decodeGroup0() { + // Acquire lock + std::lock_guard lck(group0Mtx); + // Decode Block B data + trafficAnnouncement = (blocks[BLOCK_TYPE_B] >> 14) & 1; + music = (blocks[BLOCK_TYPE_B] >> 13) & 1; + uint8_t diBit = (blocks[BLOCK_TYPE_B] >> 12) & 1; + uint8_t offset = ((blocks[BLOCK_TYPE_B] >> 10) & 0b11); + uint8_t diOffset = 3 - offset; + uint8_t psOffset = offset * 2; + + // Decode Block C data + if (groupVer == GROUP_VER_A && blockAvail[BLOCK_TYPE_C]) { + alternateFrequency = (blocks[BLOCK_TYPE_C] >> 10) & 0xFFFF; + } + + // Write DI bit to the decoder identification + decoderIdent &= ~(1 << diOffset); + decoderIdent |= (diBit << diOffset); + + // Write chars at offset the PSName + if (blockAvail[BLOCK_TYPE_D]) { + programServiceName[psOffset] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; + programServiceName[psOffset + 1] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; + } + + // Update timeout + group0LastUpdate = std::chrono::high_resolution_clock::now(); + } + + void Decoder::decodeGroup2() { + // Acquire lock + std::lock_guard lck(group2Mtx); + + // Get char offset and write chars in the Radiotext + bool nAB = (blocks[BLOCK_TYPE_B] >> 14) & 1; + uint8_t offset = (blocks[BLOCK_TYPE_B] >> 10) & 0xF; + + // Clear text field if the A/B flag changed + if (nAB != rtAB) { + radioText = " "; + } + rtAB = nAB; + + // Write char at offset in Radiotext + if (groupVer == GROUP_VER_A) { + uint8_t rtOffset = offset * 4; + if (blockAvail[BLOCK_TYPE_C]) { + radioText[rtOffset] = (blocks[BLOCK_TYPE_C] >> 18) & 0xFF; + radioText[rtOffset + 1] = (blocks[BLOCK_TYPE_C] >> 10) & 0xFF; + } + if (blockAvail[BLOCK_TYPE_D]) { + radioText[rtOffset + 2] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; + radioText[rtOffset + 3] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; + } + } + else { + uint8_t rtOffset = offset * 2; + if (blockAvail[BLOCK_TYPE_D]) { + radioText[rtOffset] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; + radioText[rtOffset + 1] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; + } + } + + // Update timeout + group2LastUpdate = std::chrono::high_resolution_clock::now(); + } + + void Decoder::decodeGroup() { // Make sure blocks B is available if (!blockAvail[BLOCK_TYPE_B]) { return; } // Decode block B decodeBlockB(); - - if (groupType == 0) { - group0LastUpdate = now; - trafficAnnouncement = (blocks[BLOCK_TYPE_B] >> 14) & 1; - music = (blocks[BLOCK_TYPE_B] >> 13) & 1; - uint8_t diBit = (blocks[BLOCK_TYPE_B] >> 12) & 1; - uint8_t offset = ((blocks[BLOCK_TYPE_B] >> 10) & 0b11); - uint8_t diOffset = 3 - offset; - uint8_t psOffset = offset * 2; - if (groupVer == GROUP_VER_A && blockAvail[BLOCK_TYPE_C]) { - alternateFrequency = (blocks[BLOCK_TYPE_C] >> 10) & 0xFFFF; - } - - // Write DI bit to the decoder identification - decoderIdent &= ~(1 << diOffset); - decoderIdent |= (diBit << diOffset); - - // Write chars at offset the PSName - if (blockAvail[BLOCK_TYPE_D]) { - programServiceName[psOffset] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; - programServiceName[psOffset + 1] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; - } - } - else if (groupType == 2) { - group2LastUpdate = now; - // Get char offset and write chars in the Radiotext - bool nAB = (blocks[BLOCK_TYPE_B] >> 14) & 1; - uint8_t offset = (blocks[BLOCK_TYPE_B] >> 10) & 0xF; - - // Clear text field if the A/B flag changed - if (nAB != rtAB) { - radioText = " "; - } - rtAB = nAB; - - // Write char at offset in Radiotext - if (groupVer == GROUP_VER_A) { - uint8_t rtOffset = offset * 4; - if (blockAvail[BLOCK_TYPE_C]) { - radioText[rtOffset] = (blocks[BLOCK_TYPE_C] >> 18) & 0xFF; - radioText[rtOffset + 1] = (blocks[BLOCK_TYPE_C] >> 10) & 0xFF; - } - if (blockAvail[BLOCK_TYPE_D]) { - radioText[rtOffset + 2] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; - radioText[rtOffset + 3] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; - } - } - else { - uint8_t rtOffset = offset * 2; - if (blockAvail[BLOCK_TYPE_D]) { - radioText[rtOffset] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; - radioText[rtOffset + 1] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; - } - } + // Decode depending on group type + switch (groupType) { + case 0: + decodeGroup0(); + break; + case 2: + decodeGroup2(); + break; + default: + break; } } - void RDSDecoder::decodeCallsign() { + void Decoder::decodeCallsign() { // Determin first better based on offset bool w = (piCode >= 21672); callsign = w ? 'W' : 'K'; @@ -250,23 +279,23 @@ namespace rds { } } - bool RDSDecoder::blockAValid() { + bool Decoder::blockAValid() { auto now = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(now - blockALastUpdate)).count() < 5000.0; + return (std::chrono::duration_cast(now - blockALastUpdate)).count() < RDS_BLOCK_A_TIMEOUT_MS; } - bool RDSDecoder::blockBValid() { + bool Decoder::blockBValid() { auto now = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(now - blockALastUpdate)).count() < 5000.0; + return (std::chrono::duration_cast(now - blockBLastUpdate)).count() < RDS_BLOCK_B_TIMEOUT_MS; } - bool RDSDecoder::group0Valid() { + bool Decoder::group0Valid() { auto now = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(now - group0LastUpdate)).count() < 5000.0; + return (std::chrono::duration_cast(now - group0LastUpdate)).count() < RDS_GROUP_0_TIMEOUT_MS; } - bool RDSDecoder::group2Valid() { + bool Decoder::group2Valid() { auto now = std::chrono::high_resolution_clock::now(); - return (std::chrono::duration_cast(now - group2LastUpdate)).count() < 5000.0; + return (std::chrono::duration_cast(now - group2LastUpdate)).count() < RDS_GROUP_2_TIMEOUT_MS; } } \ No newline at end of file diff --git a/decoder_modules/radio/src/rds.h b/decoder_modules/radio/src/rds.h index 0ecbb423..8f075f85 100644 --- a/decoder_modules/radio/src/rds.h +++ b/decoder_modules/radio/src/rds.h @@ -4,6 +4,11 @@ #include #include +#define RDS_BLOCK_A_TIMEOUT_MS 5000.0 +#define RDS_BLOCK_B_TIMEOUT_MS 5000.0 +#define RDS_GROUP_0_TIMEOUT_MS 5000.0 +#define RDS_GROUP_2_TIMEOUT_MS 5000.0 + namespace rds { enum BlockType { BLOCK_TYPE_A, @@ -205,33 +210,35 @@ namespace rds { DECODER_IDENT_VARIABLE_PTY = (1 << 0) }; - class RDSDecoder { + class Decoder { public: void process(uint8_t* symbols, int count); - bool piCodeValid() { std::lock_guard lck(groupMtx); return blockAValid(); } - uint16_t getPICode() { std::lock_guard lck(groupMtx); return piCode; } - uint8_t getCountryCode() { std::lock_guard lck(groupMtx); return countryCode; } - uint8_t getProgramCoverage() { std::lock_guard lck(groupMtx); return programCoverage; } - uint8_t getProgramRefNumber() { std::lock_guard lck(groupMtx); return programRefNumber; } - std::string getCallsign() { std::lock_guard lck(groupMtx); return callsign; } + bool piCodeValid() { std::lock_guard lck(blockAMtx); return blockAValid(); } + uint16_t getPICode() { std::lock_guard lck(blockAMtx); return piCode; } + uint8_t getCountryCode() { std::lock_guard lck(blockAMtx); return countryCode; } + uint8_t getProgramCoverage() { std::lock_guard lck(blockAMtx); return programCoverage; } + uint8_t getProgramRefNumber() { std::lock_guard lck(blockAMtx); return programRefNumber; } + std::string getCallsign() { std::lock_guard lck(blockAMtx); return callsign; } - bool programTypeValid() { std::lock_guard lck(groupMtx); return blockBValid(); } - ProgramType getProgramType() { std::lock_guard lck(groupMtx); return programType; } + bool programTypeValid() { std::lock_guard lck(blockBMtx); return blockBValid(); } + ProgramType getProgramType() { std::lock_guard lck(blockBMtx); return programType; } - bool musicValid() { std::lock_guard lck(groupMtx); return group0Valid(); } - bool getMusic() { std::lock_guard lck(groupMtx); return music; } - bool PSNameValid() { std::lock_guard lck(groupMtx); return group0Valid(); } - std::string getPSName() { std::lock_guard lck(groupMtx); return programServiceName; } + bool musicValid() { std::lock_guard lck(group0Mtx); return group0Valid(); } + bool getMusic() { std::lock_guard lck(group0Mtx); return music; } + bool PSNameValid() { std::lock_guard lck(group0Mtx); return group0Valid(); } + std::string getPSName() { std::lock_guard lck(group0Mtx); return programServiceName; } - bool radioTextValid() { std::lock_guard lck(groupMtx); return group2Valid(); } - std::string getRadioText() { std::lock_guard lck(groupMtx); return radioText; } + bool radioTextValid() { std::lock_guard lck(group2Mtx); return group2Valid(); } + std::string getRadioText() { std::lock_guard lck(group2Mtx); return radioText; } private: static uint16_t calcSyndrome(uint32_t block); static uint32_t correctErrors(uint32_t block, BlockType type, bool& recovered); void decodeBlockA(); void decodeBlockB(); + void decodeGroup0(); + void decodeGroup2(); void decodeGroup(); void decodeCallsign(); @@ -251,7 +258,7 @@ namespace rds { bool blockAvail[_BLOCK_TYPE_COUNT]; // Block A (All groups) - std::mutex groupMtx; + std::mutex blockAMtx; std::chrono::time_point blockALastUpdate{}; // 1970-01-01 uint16_t piCode; uint8_t countryCode; @@ -260,6 +267,7 @@ namespace rds { std::string callsign; // Block B (All groups) + std::mutex blockBMtx; std::chrono::time_point blockBLastUpdate{}; // 1970-01-01 uint8_t groupType; GroupVersion groupVer; @@ -267,6 +275,7 @@ namespace rds { ProgramType programType; // Group type 0 + std::mutex group0Mtx; std::chrono::time_point group0LastUpdate{}; // 1970-01-01 bool trafficAnnouncement; bool music; @@ -275,6 +284,7 @@ namespace rds { std::string programServiceName = " "; // Group type 2 + std::mutex group2Mtx; std::chrono::time_point group2LastUpdate{}; // 1970-01-01 bool rtAB = false; std::string radioText = " "; diff --git a/decoder_modules/radio/src/rds_demod.h b/decoder_modules/radio/src/rds_demod.h new file mode 100644 index 00000000..1824aec6 --- /dev/null +++ b/decoder_modules/radio/src/rds_demod.h @@ -0,0 +1,87 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class RDSDemod : public dsp::Processor { + using base_type = dsp::Processor; +public: + RDSDemod() {} + RDSDemod(dsp::stream* in) { init(in); } + ~RDSDemod() {} + + void init(dsp::stream* in) { + // Initialize the DSP + agc.init(NULL, 1.0, 1e6, 0.1); + costas.init(NULL, 0.005f); + taps = dsp::taps::bandPass(0, 2375, 100, 5000); + fir.init(NULL, taps); + double baudfreq = dsp::math::hzToRads(2375.0/2.0, 5000); + costas2.init(NULL, 0.01, 0.0, baudfreq, baudfreq - (baudfreq*0.1), baudfreq + (baudfreq*0.1)); + recov.init(NULL, 5000.0 / (2375.0 / 2.0), 1e-6, 0.01, 0.01); + diff.init(NULL, 2); + + // Free useless buffers + agc.out.free(); + fir.out.free(); + costas2.out.free(); + recov.out.free(); + + // Init the rest + base_type::init(in); + } + + void reset() { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + base_type::tempStop(); + agc.reset(); + costas.reset(); + fir.reset(); + costas2.reset(); + recov.reset(); + diff.reset(); + base_type::tempStart(); + } + + inline int process(int count, dsp::complex_t* in, float* softOut, uint8_t* hardOut) { + count = agc.process(count, in, costas.out.readBuf); + count = costas.process(count, costas.out.readBuf, costas.out.writeBuf); + count = fir.process(count, costas.out.writeBuf, costas.out.writeBuf); + count = costas2.process(count, costas.out.writeBuf, costas.out.readBuf); + count = dsp::convert::ComplexToReal::process(count, costas.out.readBuf, softOut); + count = recov.process(count, softOut, softOut); + count = dsp::digital::BinarySlicer::process(count, softOut, diff.out.readBuf); + count = diff.process(count, diff.out.readBuf, hardOut); + return count; + } + + int run() { + int count = base_type::_in->read(); + if (count < 0) { return -1; } + + count = process(count, base_type::_in->readBuf, soft.writeBuf, base_type::out.writeBuf); + + base_type::_in->flush(); + if (!base_type::out.swap(count)) { return -1; } + if (!soft.swap(count)) { return -1; } + return count; + } + + dsp::stream soft; + +private: + dsp::loop::FastAGC agc; + dsp::loop::Costas<2> costas; + dsp::tap taps; + dsp::filter::FIR fir; + dsp::loop::Costas<2> costas2; + dsp::clock_recovery::MM recov; + dsp::digital::DifferentialDecoder diff; +}; \ No newline at end of file From 2ef8ee362982745ddffb10289d36548a148502c9 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 19:15:45 +0100 Subject: [PATCH 60/65] disable rds symbol diagram data stream when not visible --- decoder_modules/radio/src/demodulators/wfm.h | 9 +++++++-- decoder_modules/radio/src/rds_demod.h | 21 +++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/decoder_modules/radio/src/demodulators/wfm.h b/decoder_modules/radio/src/demodulators/wfm.h index 43a5093d..bcccb8fd 100644 --- a/decoder_modules/radio/src/demodulators/wfm.h +++ b/decoder_modules/radio/src/demodulators/wfm.h @@ -47,7 +47,7 @@ namespace demod { // Init DSP demod.init(input, bandwidth / 2.0f, getIFSampleRate(), _stereo, _lowPass, _rds); - rdsDemod.init(&demod.rdsOut); + rdsDemod.init(&demod.rdsOut, _rdsInfo); hs.init(&rdsDemod.out, rdsHandler, this); reshape.init(&rdsDemod.soft, 4096, (1187 / 30) - 4096); diagHandler.init(&reshape.out, _diagHandler, this); @@ -96,6 +96,7 @@ namespace demod { // TODO: This will break when the entire radio module is disabled if (!_rds) { ImGui::BeginDisabled(); } if (ImGui::Checkbox(("Advanced RDS Info##_radio_wfm_rds_info_" + name).c_str(), &_rdsInfo)) { + setAdvancedRds(_rdsInfo); _config->acquire(); _config->conf[name][getName()]["rdsInfo"] = _rdsInfo; _config->release(true); @@ -229,13 +230,17 @@ namespace demod { demod.setStereo(_stereo); } + void setAdvancedRds(bool enabled) { + rdsDemod.setSoftEnabled(enabled); + _rdsInfo = enabled; + } + private: static void rdsHandler(uint8_t* data, int count, void* ctx) { WFM* _this = (WFM*)ctx; _this->rdsDecode.process(data, count); } - // DEBUGGING ONLY static void _diagHandler(float* data, int count, void* ctx) { WFM* _this = (WFM*)ctx; float* buf = _this->diag.acquireBuffer(); diff --git a/decoder_modules/radio/src/rds_demod.h b/decoder_modules/radio/src/rds_demod.h index 1824aec6..1e188003 100644 --- a/decoder_modules/radio/src/rds_demod.h +++ b/decoder_modules/radio/src/rds_demod.h @@ -13,10 +13,13 @@ class RDSDemod : public dsp::Processor { using base_type = dsp::Processor; public: RDSDemod() {} - RDSDemod(dsp::stream* in) { init(in); } + RDSDemod(dsp::stream* in, bool enableSoft) { init(in, enableSoft); } ~RDSDemod() {} - void init(dsp::stream* in) { + void init(dsp::stream* in, bool enableSoft) { + // Save config + this->enableSoft = enableSoft; + // Initialize the DSP agc.init(NULL, 1.0, 1e6, 0.1); costas.init(NULL, 0.005f); @@ -37,6 +40,14 @@ public: base_type::init(in); } + void setSoftEnabled(bool enable) { + assert(base_type::_block_init); + std::lock_guard lck(base_type::ctrlMtx); + base_type::tempStop(); + enableSoft = enable; + base_type::tempStart(); + } + void reset() { assert(base_type::_block_init); std::lock_guard lck(base_type::ctrlMtx); @@ -70,13 +81,17 @@ public: base_type::_in->flush(); if (!base_type::out.swap(count)) { return -1; } - if (!soft.swap(count)) { return -1; } + if (enableSoft) { + if (!soft.swap(count)) { return -1; } + } return count; } dsp::stream soft; private: + bool enableSoft = false; + dsp::loop::FastAGC agc; dsp::loop::Costas<2> costas; dsp::tap taps; From 120745de19d9c1c86b41d5d711298ff8d47a605f Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 21:00:23 +0100 Subject: [PATCH 61/65] add rds program type name decoding --- decoder_modules/radio/src/rds.cpp | 54 ++++++++++++++++++++++++++++++- decoder_modules/radio/src/rds.h | 12 +++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/decoder_modules/radio/src/rds.cpp b/decoder_modules/radio/src/rds.cpp index 354cfddf..d8f1e9ee 100644 --- a/decoder_modules/radio/src/rds.cpp +++ b/decoder_modules/radio/src/rds.cpp @@ -156,7 +156,7 @@ namespace rds { // Acquire lock std::lock_guard lck(blockBMtx); - // If it didn't decode properly return + // If it didn't decode properly return (TODO: Make sure this is not needed) if (!blockAvail[BLOCK_TYPE_B]) { return; } // Decode group type and version @@ -240,6 +240,48 @@ namespace rds { group2LastUpdate = std::chrono::high_resolution_clock::now(); } + void Decoder::decodeGroup10() { + // Acquire lock + std::lock_guard lck(group10Mtx); + + // Check if the text needs to be cleared + bool ab = (blocks[BLOCK_TYPE_B] >> 14) & 1; + if (ab != ptnAB) { + programTypeName = " "; + } + ptnAB = ab; + + // Decode segment address + bool addr = (blocks[BLOCK_TYPE_B] >> 10) & 1; + + // Save text depending on address + if (addr) { + if (blockAvail[BLOCK_TYPE_C]) { + programTypeName[4] = (blocks[BLOCK_TYPE_C] >> 18) & 0xFF; + programTypeName[5] = (blocks[BLOCK_TYPE_C] >> 10) & 0xFF; + } + if (blockAvail[BLOCK_TYPE_D]) { + programTypeName[6] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; + programTypeName[7] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; + } + } + else { + if (blockAvail[BLOCK_TYPE_C]) { + programTypeName[0] = (blocks[BLOCK_TYPE_C] >> 18) & 0xFF; + programTypeName[1] = (blocks[BLOCK_TYPE_C] >> 10) & 0xFF; + } + if (blockAvail[BLOCK_TYPE_D]) { + programTypeName[2] = (blocks[BLOCK_TYPE_D] >> 18) & 0xFF; + programTypeName[3] = (blocks[BLOCK_TYPE_D] >> 10) & 0xFF; + } + } + + flog::debug("PTN: '{}'", programTypeName); + + // Update timeout + group10LastUpdate = std::chrono::high_resolution_clock::now(); + } + void Decoder::decodeGroup() { // Make sure blocks B is available if (!blockAvail[BLOCK_TYPE_B]) { return; } @@ -247,6 +289,8 @@ namespace rds { // Decode block B decodeBlockB(); + //flog::debug("RDS Group {}{}", groupType, (groupVer == GROUP_VER_A) ? 'A':'B'); + // Decode depending on group type switch (groupType) { case 0: @@ -255,6 +299,9 @@ namespace rds { case 2: decodeGroup2(); break; + case 10: + decodeGroup10(); + break; default: break; } @@ -298,4 +345,9 @@ namespace rds { auto now = std::chrono::high_resolution_clock::now(); return (std::chrono::duration_cast(now - group2LastUpdate)).count() < RDS_GROUP_2_TIMEOUT_MS; } + + bool Decoder::group10Valid() { + auto now = std::chrono::high_resolution_clock::now(); + return (std::chrono::duration_cast(now - group10LastUpdate)).count() < RDS_GROUP_10_TIMEOUT_MS; + } } \ No newline at end of file diff --git a/decoder_modules/radio/src/rds.h b/decoder_modules/radio/src/rds.h index 8f075f85..5b6b851e 100644 --- a/decoder_modules/radio/src/rds.h +++ b/decoder_modules/radio/src/rds.h @@ -8,6 +8,7 @@ #define RDS_BLOCK_B_TIMEOUT_MS 5000.0 #define RDS_GROUP_0_TIMEOUT_MS 5000.0 #define RDS_GROUP_2_TIMEOUT_MS 5000.0 +#define RDS_GROUP_10_TIMEOUT_MS 5000.0 namespace rds { enum BlockType { @@ -232,6 +233,9 @@ namespace rds { bool radioTextValid() { std::lock_guard lck(group2Mtx); return group2Valid(); } std::string getRadioText() { std::lock_guard lck(group2Mtx); return radioText; } + bool programTypeNameValid() { std::lock_guard lck(group10Mtx); return group10Valid(); } + std::string getProgramTypeName() { std::lock_guard lck(group10Mtx); return programTypeName; } + private: static uint16_t calcSyndrome(uint32_t block); static uint32_t correctErrors(uint32_t block, BlockType type, bool& recovered); @@ -239,6 +243,7 @@ namespace rds { void decodeBlockB(); void decodeGroup0(); void decodeGroup2(); + void decodeGroup10(); void decodeGroup(); void decodeCallsign(); @@ -247,6 +252,7 @@ namespace rds { bool blockBValid(); bool group0Valid(); bool group2Valid(); + bool group10Valid(); // State machine uint32_t shiftReg = 0; @@ -289,5 +295,11 @@ namespace rds { bool rtAB = false; std::string radioText = " "; + // Group type 10 + std::mutex group10Mtx; + std::chrono::time_point group10LastUpdate{}; // 1970-01-01 + bool ptnAB = false; + std::string programTypeName = " "; + }; } \ No newline at end of file From f8183739f773b313eda0170987c554c96c28119c Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 21:28:43 +0100 Subject: [PATCH 62/65] add rds region selection --- decoder_modules/radio/src/demodulators/wfm.h | 63 ++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/decoder_modules/radio/src/demodulators/wfm.h b/decoder_modules/radio/src/demodulators/wfm.h index bcccb8fd..e8289b38 100644 --- a/decoder_modules/radio/src/demodulators/wfm.h +++ b/decoder_modules/radio/src/demodulators/wfm.h @@ -7,6 +7,11 @@ #include namespace demod { + enum RDSRegion { + RDS_REGION_EUROPE, + RDS_REGION_NORTH_AMERICA + }; + class WFM : public Demodulator { public: WFM() : diag(0.5, 4096) {} @@ -24,10 +29,18 @@ namespace demod { this->name = name; _config = config; + // Define RDS regions + rdsRegions.define("eu", "Europe", RDS_REGION_EUROPE); + rdsRegions.define("na", "North America", RDS_REGION_NORTH_AMERICA); + + // Register FFT draw handler fftRedrawHandler.handler = fftRedraw; fftRedrawHandler.ctx = this; gui::waterfall.onFFTRedraw.bindHandler(&fftRedrawHandler); + // Default + std::string rdsRegionStr = "eu"; + // Load config _config->acquire(); bool modified = false; @@ -43,8 +56,21 @@ namespace demod { if (config->conf[name][getName()].contains("rdsInfo")) { _rdsInfo = config->conf[name][getName()]["rdsInfo"]; } + if (config->conf[name][getName()].contains("rdsRegion")) { + rdsRegionStr = config->conf[name][getName()]["rdsRegion"]; + } _config->release(modified); + // Load RDS region + if (rdsRegions.keyExists(rdsRegionStr)) { + rdsRegionId = rdsRegions.keyId(rdsRegionStr); + rdsRegion = rdsRegions.value(rdsRegionId); + } + else { + rdsRegion = RDS_REGION_EUROPE; + rdsRegionId = rdsRegions.valueId(rdsRegion); + } + // Init DSP demod.init(input, bandwidth / 2.0f, getIFSampleRate(), _stereo, _lowPass, _rds); rdsDemod.init(&demod.rdsOut, _rdsInfo); @@ -93,7 +119,7 @@ namespace demod { _config->release(true); } - // TODO: This will break when the entire radio module is disabled + // TODO: This might break when the entire radio module is disabled if (!_rds) { ImGui::BeginDisabled(); } if (ImGui::Checkbox(("Advanced RDS Info##_radio_wfm_rds_info_" + name).c_str(), &_rdsInfo)) { setAdvancedRds(_rdsInfo); @@ -101,6 +127,14 @@ namespace demod { _config->conf[name][getName()]["rdsInfo"] = _rdsInfo; _config->release(true); } + ImGui::SameLine(); + ImGui::FillWidth(); + if (ImGui::Combo(("##_radio_wfm_rds_region_" + name).c_str(), &rdsRegionId, rdsRegions.txt)) { + rdsRegion = rdsRegions.value(rdsRegionId); + _config->acquire(); + _config->conf[name][getName()]["rdsRegion"] = rdsRegions.key(rdsRegionId); + _config->release(true); + } if (!_rds) { ImGui::EndDisabled(); } float menuWidth = ImGui::GetContentRegionAvail().x; @@ -112,7 +146,12 @@ namespace demod { ImGui::TableSetColumnIndex(0); ImGui::TextUnformatted("PI Code"); ImGui::TableSetColumnIndex(1); - ImGui::Text("0x%04X (%s)", rdsDecode.getPICode(), rdsDecode.getCallsign().c_str()); + if (rdsRegion == RDS_REGION_NORTH_AMERICA) { + ImGui::Text("0x%04X (%s)", rdsDecode.getPICode(), rdsDecode.getCallsign().c_str()); + } + else { + ImGui::Text("0x%04X", rdsDecode.getPICode()); + } ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -137,7 +176,12 @@ namespace demod { ImGui::TableSetColumnIndex(0); ImGui::TextUnformatted("PI Code"); ImGui::TableSetColumnIndex(1); - ImGui::TextUnformatted("0x---- (----)"); + if (rdsRegion == RDS_REGION_NORTH_AMERICA) { + ImGui::TextUnformatted("0x---- (----)"); + } + else { + ImGui::TextUnformatted("0x----"); + } ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -163,7 +207,12 @@ namespace demod { ImGui::TableSetColumnIndex(0); ImGui::TextUnformatted("Program Type"); ImGui::TableSetColumnIndex(1); - ImGui::Text("%s (%d)", rds::PROGRAM_TYPE_US_TO_STR[rdsDecode.getProgramType()], rdsDecode.getProgramType()); + if (rdsRegion == RDS_REGION_NORTH_AMERICA) { + ImGui::Text("%s (%d)", rds::PROGRAM_TYPE_US_TO_STR[rdsDecode.getProgramType()], rdsDecode.getProgramType()); + } + else { + ImGui::Text("%s (%d)", rds::PROGRAM_TYPE_EU_TO_STR[rdsDecode.getProgramType()], rdsDecode.getProgramType()); + } } else { ImGui::TableNextRow(); @@ -308,6 +357,12 @@ namespace demod { float muGain = 0.01; float omegaGain = (0.01*0.01)/4.0; + int rdsRegionId = 0; + RDSRegion rdsRegion = RDS_REGION_EUROPE; + + OptionList rdsRegions; + + std::string name; }; } \ No newline at end of file From a8ed213ed32643c82e6a4307c5ed1577f92a1291 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 21:49:13 +0100 Subject: [PATCH 63/65] remove useless debug logging --- decoder_modules/radio/src/rds.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/decoder_modules/radio/src/rds.cpp b/decoder_modules/radio/src/rds.cpp index d8f1e9ee..2cf44383 100644 --- a/decoder_modules/radio/src/rds.cpp +++ b/decoder_modules/radio/src/rds.cpp @@ -276,8 +276,6 @@ namespace rds { } } - flog::debug("PTN: '{}'", programTypeName); - // Update timeout group10LastUpdate = std::chrono::high_resolution_clock::now(); } From f0bd17f9f404731c5e946a379bc510996e346568 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 29 Jan 2024 23:57:23 +0100 Subject: [PATCH 64/65] fix north americal RDS callsign decoding --- decoder_modules/radio/src/rds.cpp | 147 ++++++++++++++++++++++++++++-- decoder_modules/radio/src/rds.h | 3 +- 2 files changed, 143 insertions(+), 7 deletions(-) diff --git a/decoder_modules/radio/src/rds.cpp b/decoder_modules/radio/src/rds.cpp index 2cf44383..90bc111d 100644 --- a/decoder_modules/radio/src/rds.cpp +++ b/decoder_modules/radio/src/rds.cpp @@ -22,6 +22,98 @@ namespace rds { { BLOCK_TYPE_D, 0b0110110100 } }; + std::map THREE_LETTER_CALLS = { + { 0x99A5, "KBW" }, + { 0x99A6, "KCY" }, + { 0x9990, "KDB" }, + { 0x99A7, "KDF" }, + { 0x9950, "KEX" }, + { 0x9951, "KFH" }, + { 0x9952, "KFI" }, + { 0x9953, "KGA" }, + { 0x9991, "KGB" }, + { 0x9954, "KGO" }, + { 0x9955, "KGU" }, + { 0x9956, "KGW" }, + { 0x9957, "KGY" }, + { 0x99AA, "KHQ" }, + { 0x9958, "KID" }, + { 0x9959, "KIT" }, + { 0x995A, "KJR" }, + { 0x995B, "KLO" }, + { 0x995C, "KLZ" }, + { 0x995D, "KMA" }, + { 0x995E, "KMJ" }, + { 0x995F, "KNX" }, + { 0x9960, "KOA" }, + { 0x99AB, "KOB" }, + { 0x9992, "KOY" }, + { 0x9993, "KPQ" }, + { 0x9964, "KQV" }, + { 0x9994, "KSD" }, + { 0x9965, "KSL" }, + { 0x9966, "KUJ" }, + { 0x9995, "KUT" }, + { 0x9967, "KVI" }, + { 0x9968, "KWG" }, + { 0x9996, "KXL" }, + { 0x9997, "KXO" }, + { 0x996B, "KYW" }, + { 0x9999, "WBT" }, + { 0x996D, "WBZ" }, + { 0x996E, "WDZ" }, + { 0x996F, "WEW" }, + { 0x999A, "WGH" }, + { 0x9971, "WGL" }, + { 0x9972, "WGN" }, + { 0x9973, "WGR" }, + { 0x999B, "WGY" }, + { 0x9975, "WHA" }, + { 0x9976, "WHB" }, + { 0x9977, "WHK" }, + { 0x9978, "WHO" }, + { 0x999C, "WHP" }, + { 0x999D, "WIL" }, + { 0x997A, "WIP" }, + { 0x99B3, "WIS" }, + { 0x997B, "WJR" }, + { 0x99B4, "WJW" }, + { 0x99B5, "WJZ" }, + { 0x997C, "WKY" }, + { 0x997D, "WLS" }, + { 0x997E, "WLW" }, + { 0x999E, "WMC" }, + { 0x999F, "WMT" }, + { 0x9981, "WOC" }, + { 0x99A0, "WOI" }, + { 0x9983, "WOL" }, + { 0x9984, "WOR" }, + { 0x99A1, "WOW" }, + { 0x99B9, "WRC" }, + { 0x99A2, "WRR" }, + { 0x99A3, "WSB" }, + { 0x99A4, "WSM" }, + { 0x9988, "WWJ" }, + { 0x9989, "WWL" } + }; + + std::map NAT_LOC_LINKED_STATIONS = { + { 0xB01, "NPR-1" }, + { 0xB02, "CBC - Radio One" }, + { 0xB03, "CBC - Radio Two" }, + { 0xB04, "Radio-Canada - Première Chaîne" }, + { 0xB05, "Radio-Canada - Espace Musique" }, + { 0xB06, "CBC" }, + { 0xB07, "CBC" }, + { 0xB08, "CBC" }, + { 0xB09, "CBC" }, + { 0xB0A, "NPR-2" }, + { 0xB0B, "NPR-3" }, + { 0xB0C, "NPR-4" }, + { 0xB0D, "NPR-5" }, + { 0xB0E, "NPR-6" } + }; + // 9876543210 const uint16_t LFSR_POLY = 0b0110111001; const uint16_t IN_POLY = 0b1100011011; @@ -146,7 +238,7 @@ namespace rds { countryCode = (blocks[BLOCK_TYPE_A] >> 22) & 0xF; programCoverage = (AreaCoverage)((blocks[BLOCK_TYPE_A] >> 18) & 0xF); programRefNumber = (blocks[BLOCK_TYPE_A] >> 10) & 0xFF; - decodeCallsign(); + callsign = decodeCallsign(piCode); // Update timeout blockALastUpdate = std::chrono::high_resolution_clock::now();; @@ -287,7 +379,7 @@ namespace rds { // Decode block B decodeBlockB(); - //flog::debug("RDS Group {}{}", groupType, (groupVer == GROUP_VER_A) ? 'A':'B'); + flog::debug("RDS Group {}{}", groupType, (groupVer == GROUP_VER_A) ? 'A':'B'); // Decode depending on group type switch (groupType) { @@ -305,14 +397,14 @@ namespace rds { } } - void Decoder::decodeCallsign() { + std::string Decoder::base26ToCall(uint16_t pi) { // Determin first better based on offset - bool w = (piCode >= 21672); - callsign = w ? 'W' : 'K'; + bool w = (pi >= 21672); + std::string callsign(w ? "W" : "K"); // Base25 decode the rest std::string restStr; - int rest = piCode - (w ? 21672 : 4096); + int rest = pi - (w ? 21672 : 4096); while (rest) { restStr += 'A' + (rest % 26); rest /= 26; @@ -322,6 +414,49 @@ namespace rds { for (int i = restStr.size() - 1; i >= 0; i--) { callsign += restStr[i]; } + + return callsign; + } + + std::string Decoder::decodeCallsign(uint16_t pi) { + if ((pi >> 8) == 0xAF) { + // AFXY -> XY00 + return base26ToCall((pi & 0xFF) << 8); + } + else if ((pi >> 12) == 0xA) { + // AXYZ -> X0YZ + return base26ToCall((((pi >> 8) & 0xF) << 12) | (pi & 0xFF)); + } + else if (pi >= 0x9950 && pi <= 0x9EFF) { + // 3 letter callsigns + if (THREE_LETTER_CALLS.find(pi) != THREE_LETTER_CALLS.end()) { + return THREE_LETTER_CALLS[pi]; + } + else { + return "Not Assigned"; + } + } + else if (pi >= 0x1000 && pi <= 0x994F) { + // Normal encoding + if ((pi & 0xFF) == 0 || ((pi >> 8) & 0xF) == 0) { + return "Not Assigned"; + } + else { + return base26ToCall(pi); + } + } + else if (pi >= 0xB000 && pi <= 0xEFFF) { + uint16_t _pi = ((pi >> 12) << 8) | (pi & 0xFF); + if (NAT_LOC_LINKED_STATIONS.find(_pi) != NAT_LOC_LINKED_STATIONS.end()) { + return NAT_LOC_LINKED_STATIONS[_pi]; + } + else { + return "Not Assigned"; + } + } + else { + return "Not Assigned"; + } } bool Decoder::blockAValid() { diff --git a/decoder_modules/radio/src/rds.h b/decoder_modules/radio/src/rds.h index 5b6b851e..67aab621 100644 --- a/decoder_modules/radio/src/rds.h +++ b/decoder_modules/radio/src/rds.h @@ -246,7 +246,8 @@ namespace rds { void decodeGroup10(); void decodeGroup(); - void decodeCallsign(); + static std::string base26ToCall(uint16_t pi); + static std::string decodeCallsign(uint16_t pi); bool blockAValid(); bool blockBValid(); From a9e59bdf3c939b7c5173b16e7d919570550559e1 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Tue, 30 Jan 2024 00:01:10 +0100 Subject: [PATCH 65/65] removed useless logging again --- decoder_modules/radio/src/rds.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/decoder_modules/radio/src/rds.cpp b/decoder_modules/radio/src/rds.cpp index 90bc111d..577af2c5 100644 --- a/decoder_modules/radio/src/rds.cpp +++ b/decoder_modules/radio/src/rds.cpp @@ -379,8 +379,6 @@ namespace rds { // Decode block B decodeBlockB(); - flog::debug("RDS Group {}{}", groupType, (groupVer == GROUP_VER_A) ? 'A':'B'); - // Decode depending on group type switch (groupType) { case 0: