mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 08:58:13 +01:00 
			
		
		
		
	Compare commits
	
		
			29 Commits
		
	
	
		
			new_source
			...
			noise_redu
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 87da47f53d | ||
|  | 75050347de | ||
|  | a9f882e5b1 | ||
|  | 3420808f3a | ||
|  | d3d245992d | ||
|  | 9a3414b847 | ||
|  | 90c26f8c1b | ||
|  | ec4dc6cc9e | ||
|  | 93b28d1495 | ||
|  | 84291deaf6 | ||
|  | 21e0696917 | ||
|  | 19247ef4f2 | ||
|  | 1e5601e773 | ||
|  | ae1fd87f02 | ||
|  | ab2aee316c | ||
|  | 109374277e | ||
|  | 692436f6e4 | ||
|  | eccb715d0c | ||
|  | f6f074e0c7 | ||
|  | 50a77a7e60 | ||
|  | a4f3c92a03 | ||
|  | 37920b6476 | ||
|  | 314b8bf72d | ||
|  | 5f0858bab2 | ||
|  | 007761a027 | ||
|  | 801f1be6b2 | ||
|  | 9cc793e328 | ||
|  | 4283cacae6 | ||
|  | 6f9dacdd53 | 
							
								
								
									
										7
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| # 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. | ||||
|  | ||||
| Open an issue requesting a feature or discussing a possible bugfix instead. | ||||
| @@ -13,6 +13,7 @@ endif (USE_BUNDLE_DEFAULTS) | ||||
| file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c") | ||||
|  | ||||
| add_definitions(-DSDRPP_IS_CORE) | ||||
| add_definitions(-DFLOG_ANDROID_TAG="SDR++") | ||||
| if (MSVC) | ||||
|     set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) | ||||
| endif () | ||||
|   | ||||
| @@ -55,24 +55,33 @@ namespace sdrpp_credits { | ||||
|         "Dale L Puckett (K0HYD)", | ||||
|         "Daniele D'Agnelli", | ||||
|         "D. Jones", | ||||
|         "Dexruus", | ||||
|         "EB3FRN", | ||||
|         "Eric Johnson", | ||||
|         "Ernest Murphy (NH7L)", | ||||
|         "Flinger Films", | ||||
|         "Frank Werner (HB9FXQ)", | ||||
|         "gringogrigio", | ||||
|         "Jeff Moe", | ||||
|         "Joe Cupano", | ||||
|         "KD1SQ", | ||||
|         "Kezza", | ||||
|         "Krys Kamieniecki", | ||||
|         "Lee Donaghy", | ||||
|         "Lee KD1SQ", | ||||
|         ".lozenge. (Hank Hill)", | ||||
|         "Martin Herren (HB9FXX)", | ||||
|         "ON4MU", | ||||
|         "Passion-Radio.com", | ||||
|         "Paul Maine", | ||||
|         "Peter Betz", | ||||
|         "Scanner School", | ||||
|         "Scott Palmer", | ||||
|         "SignalsEverywhere", | ||||
|         "Syne Ardwin (WI9SYN)", | ||||
|         "W4IPA", | ||||
|         "William Arcand (W1WRA)", | ||||
|         "Yves Rougy", | ||||
|         "Zipper" | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -67,10 +67,6 @@ namespace dsp::buffer { | ||||
|                 sizes[writeCur] = count; | ||||
|                 writeCur++; | ||||
|                 writeCur = ((writeCur) % TEST_BUFFER_SIZE); | ||||
|  | ||||
|                 // if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) { | ||||
|                 //     flog::warn("Overflow"); | ||||
|                 // } | ||||
|             } | ||||
|             cnd.notify_all(); | ||||
|             _in->flush(); | ||||
|   | ||||
| @@ -9,7 +9,7 @@ namespace dsp::convert { | ||||
|  | ||||
|         StereoToMono(stream<stereo_t>* in) { base_type::init(in); } | ||||
|          | ||||
|         inline int process(int count, const stereo_t* in, float* out) { | ||||
|         static inline int process(int count, const stereo_t* in, float* out) { | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 out[i] = (in[i].l + in[i].r) / 2.0f; | ||||
|             } | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
| #include "quadrature.h" | ||||
| #include "../filter/fir.h" | ||||
| #include "../taps/low_pass.h" | ||||
| #include "../taps/high_pass.h" | ||||
| #include "../taps/band_pass.h" | ||||
| #include "../convert/mono_to_stereo.h" | ||||
|  | ||||
| namespace dsp::demod { | ||||
| @@ -17,22 +19,26 @@ namespace dsp::demod { | ||||
|         ~FM() { | ||||
|             if (!base_type::_block_init) { return; } | ||||
|             base_type::stop(); | ||||
|             dsp::taps::free(lpfTaps); | ||||
|             dsp::taps::free(filterTaps); | ||||
|         } | ||||
|  | ||||
|         void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) { | ||||
|         void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) { | ||||
|             _samplerate = samplerate; | ||||
|             _bandwidth = bandwidth; | ||||
|             _lowPass = lowPass; | ||||
|             _highPass = highPass; | ||||
|  | ||||
|             demod.init(NULL, bandwidth / 2.0, _samplerate); | ||||
|             lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate); | ||||
|             lpf.init(NULL, lpfTaps); | ||||
|             loadDummyTaps(); | ||||
|             fir.init(NULL, filterTaps); | ||||
|  | ||||
|             // Initialize taps | ||||
|             updateFilter(lowPass, highPass); | ||||
|  | ||||
|             if constexpr (std::is_same_v<T, float>) { | ||||
|                 demod.out.free(); | ||||
|             } | ||||
|             lpf.out.free(); | ||||
|             fir.out.free(); | ||||
|  | ||||
|             base_type::init(in); | ||||
|         } | ||||
| @@ -43,9 +49,7 @@ namespace dsp::demod { | ||||
|             base_type::tempStop(); | ||||
|             _samplerate = samplerate; | ||||
|             demod.setDeviation(_bandwidth / 2.0, _samplerate); | ||||
|             dsp::taps::free(lpfTaps); | ||||
|             lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate); | ||||
|             lpf.setTaps(lpfTaps); | ||||
|             updateFilter(_lowPass, _highPass); | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
| @@ -54,19 +58,20 @@ namespace dsp::demod { | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             if (bandwidth == _bandwidth) { return; } | ||||
|             _bandwidth = bandwidth; | ||||
|             std::lock_guard<std::mutex> lck2(lpfMtx); | ||||
|             demod.setDeviation(_bandwidth / 2.0, _samplerate); | ||||
|             dsp::taps::free(lpfTaps); | ||||
|             lpfTaps = dsp::taps::lowPass(_bandwidth / 2, (_bandwidth / 2) * 0.1, _samplerate); | ||||
|             lpf.setTaps(lpfTaps); | ||||
|             updateFilter(_lowPass, _highPass); | ||||
|         } | ||||
|  | ||||
|         void setLowPass(bool lowPass) { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             std::lock_guard<std::mutex> lck2(lpfMtx); | ||||
|             _lowPass = lowPass; | ||||
|             lpf.reset(); | ||||
|             updateFilter(lowPass, _highPass); | ||||
|         } | ||||
|  | ||||
|         void setHighPass(bool highPass) { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             updateFilter(_lowPass, highPass); | ||||
|         } | ||||
|  | ||||
|         void reset() { | ||||
| @@ -74,23 +79,23 @@ namespace dsp::demod { | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|             demod.reset(); | ||||
|             lpf.reset(); | ||||
|             fir.reset(); | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         inline int process(int count, dsp::complex_t* in, T* out) { | ||||
|             if constexpr (std::is_same_v<T, float>) { | ||||
|                 demod.process(count, in, out); | ||||
|                 if (_lowPass) { | ||||
|                     std::lock_guard<std::mutex> lck(lpfMtx); | ||||
|                     lpf.process(count, out, out); | ||||
|                 if (filtering) { | ||||
|                     std::lock_guard<std::mutex> lck(filterMtx); | ||||
|                     fir.process(count, out, out); | ||||
|                 } | ||||
|             } | ||||
|             if constexpr (std::is_same_v<T, stereo_t>) { | ||||
|                 demod.process(count, in, demod.out.writeBuf); | ||||
|                 if (_lowPass) { | ||||
|                     std::lock_guard<std::mutex> lck(lpfMtx); | ||||
|                     lpf.process(count, demod.out.writeBuf, demod.out.writeBuf); | ||||
|                 if (filtering) { | ||||
|                     std::lock_guard<std::mutex> lck(filterMtx); | ||||
|                     fir.process(count, demod.out.writeBuf, demod.out.writeBuf); | ||||
|                 } | ||||
|                 convert::MonoToStereo::process(count, demod.out.writeBuf, out); | ||||
|             } | ||||
| @@ -109,13 +114,50 @@ namespace dsp::demod { | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         void updateFilter(bool lowPass, bool highPass) { | ||||
|             std::lock_guard<std::mutex> lck(filterMtx); | ||||
|  | ||||
|             // Update values | ||||
|             _lowPass = lowPass; | ||||
|             _highPass = highPass; | ||||
|             filtering = (lowPass || highPass); | ||||
|  | ||||
|             // Free filter taps | ||||
|             dsp::taps::free(filterTaps); | ||||
|  | ||||
|             // Generate filter depending on low and high pass settings | ||||
|             if (_lowPass && _highPass) { | ||||
|                 filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate); | ||||
|             } | ||||
|             else if (_highPass) { | ||||
|                 filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate); | ||||
|             } | ||||
|             else if (_lowPass) { | ||||
|                 filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate); | ||||
|             } | ||||
|             else { | ||||
|                 loadDummyTaps(); | ||||
|             } | ||||
|  | ||||
|             // Set filter to use new taps | ||||
|             fir.setTaps(filterTaps); | ||||
|             fir.reset(); | ||||
|         } | ||||
|  | ||||
|         void loadDummyTaps() { | ||||
|             float dummyTap = 1.0f; | ||||
|             filterTaps = dsp::taps::fromArray<float>(1, &dummyTap); | ||||
|         } | ||||
|  | ||||
|         double _samplerate; | ||||
|         double _bandwidth; | ||||
|         bool _lowPass; | ||||
|         bool _highPass; | ||||
|         bool filtering; | ||||
|  | ||||
|         Quadrature demod; | ||||
|         tap<float> lpfTaps; | ||||
|         filter::FIR<float, float> lpf; | ||||
|         std::mutex lpfMtx; | ||||
|         tap<float> filterTaps; | ||||
|         filter::FIR<float, float> fir; | ||||
|         std::mutex filterMtx; | ||||
|     }; | ||||
| } | ||||
| @@ -2,5 +2,6 @@ | ||||
| #include "../multirate/rrc_interpolator.h" | ||||
|  | ||||
| namespace dsp::mod { | ||||
|     // TODO: Check if resample before RRC is better than using the RRC taps as a filter (bandwidth probably not correct for alias-free resampling) | ||||
|     typedef multirate::RRCInterpolator<complex_t> PSK; | ||||
| } | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "../math/hz_to_rads.h" | ||||
|  | ||||
| namespace dsp::mod { | ||||
|     class Quadrature : Processor<float, complex_t> { | ||||
|     class Quadrature : public Processor<float, complex_t> { | ||||
|         using base_type = Processor<float, complex_t>; | ||||
|     public: | ||||
|         Quadrature() {} | ||||
|   | ||||
| @@ -83,8 +83,6 @@ namespace dsp::multirate { | ||||
|             int interp = OutSR / gcd; | ||||
|             int decim = InSR / gcd; | ||||
|  | ||||
|             flog::warn("interp: {0}, decim: {1}", interp, decim); | ||||
|  | ||||
|             // Configure resampler | ||||
|             double tapSamplerate = _symbolrate * (double)interp; | ||||
|             rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount * interp, _rrcBeta, _symbolrate, tapSamplerate); | ||||
|   | ||||
							
								
								
									
										183
									
								
								core/src/dsp/noise_reduction/audio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								core/src/dsp/noise_reduction/audio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | ||||
| #pragma once | ||||
| #include "../processor.h" | ||||
| #include "../window/nuttall.h" | ||||
| #include <fftw3.h> | ||||
| #include "../convert/stereo_to_mono.h" | ||||
|  | ||||
| namespace dsp::noise_reduction { | ||||
|     class Audio : public Processor<stereo_t, stereo_t> { | ||||
|         using base_type = Processor<stereo_t, stereo_t>; | ||||
|     public: | ||||
|         Audio() {} | ||||
|  | ||||
|         Audio(stream<stereo_t>* in, int bins) { init(in, bins); } | ||||
|  | ||||
|         ~Audio() { | ||||
|             if (!base_type::_block_init) { return; } | ||||
|             base_type::stop(); | ||||
|             destroyBuffers(); | ||||
|         } | ||||
|  | ||||
|         void init(stream<stereo_t>* in, int bins) { | ||||
|             _bins = bins; | ||||
|             complexBins = (bins / 2) + 1; | ||||
|             normFactor = 1.0f / (float)_bins; | ||||
|             initBuffers(); | ||||
|             base_type::init(in); | ||||
|         } | ||||
|  | ||||
|         void setBins(int bins) { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|             _bins = bins; | ||||
|             complexBins = (bins / 2) + 1; | ||||
|             normFactor = 1.0f / (float)_bins; | ||||
|             destroyBuffers(); | ||||
|             initBuffers(); | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         void setLevel(float level) { | ||||
|             _level = powf(10.0f, level * 0.1f); | ||||
|         } | ||||
|  | ||||
|         void reset() { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|             buffer::clear(buffer, _bins - 1); | ||||
|             buffer::clear(backFFTIn, _bins); | ||||
|             buffer::clear(noisePrint, _bins); | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         int process(int count, const stereo_t* in, stereo_t* out) { | ||||
|             // Write new input data to buffer | ||||
|             convert::StereoToMono::process(count, in, bufferStart); | ||||
|              | ||||
|             // Iterate the FFT | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 // Apply windows | ||||
|                 volk_32f_x2_multiply_32f(forwFFTIn, &buffer[i], fftWin, _bins); | ||||
|  | ||||
|                 // Do forward FFT | ||||
|                 fftwf_execute(forwardPlan); | ||||
|  | ||||
|                 // Get bin amplitude and square to get power | ||||
|                 volk_32fc_magnitude_32f(ampBuf, (lv_32fc_t*)forwFFTOut, complexBins); | ||||
|  | ||||
|                 // Update noise print using a running average | ||||
|                 volk_32f_s32f_multiply_32f(scaledAmps, ampBuf, alpha, complexBins); | ||||
|                 volk_32f_s32f_multiply_32f(noisePrint, noisePrint, beta, complexBins); | ||||
|                 volk_32f_x2_add_32f(noisePrint, noisePrint, scaledAmps, complexBins); | ||||
|  | ||||
|                 // Clamp amplitudes | ||||
|                 volk_32f_x2_max_32f(ampBuf, ampBuf, noisePrint, complexBins); | ||||
|  | ||||
|                 // Compute Wiener (funny) filter | ||||
|                 volk_32f_x2_subtract_32f(scaledAmps, ampBuf, noisePrint, complexBins); | ||||
|                 volk_32f_x2_divide_32f(scaledAmps, scaledAmps, ampBuf, complexBins); | ||||
|  | ||||
|                 // Apply wiener filter to bins | ||||
|                 volk_32fc_32f_multiply_32fc((lv_32fc_t*)backFFTIn, (lv_32fc_t*)forwFFTOut, scaledAmps, complexBins); | ||||
|  | ||||
|                 // Do reverse FFT and get first element | ||||
|                 fftwf_execute(backwardPlan); | ||||
|                 out[i].l = backFFTOut[_bins / 2]; | ||||
|                 out[i].r = backFFTOut[_bins / 2]; | ||||
|             } | ||||
|  | ||||
|             // Correct amplitude | ||||
|             volk_32f_s32f_multiply_32f((float*)out, (float*)out, normFactor, count*2); | ||||
|  | ||||
|             // Move buffer buffer | ||||
|             memmove(buffer, &buffer[count], (_bins - 1) * sizeof(float)); | ||||
|  | ||||
|             return count; | ||||
|         } | ||||
|  | ||||
|         int run() { | ||||
|             int count = base_type::_in->read(); | ||||
|             if (count < 0) { return -1; } | ||||
|  | ||||
|             process(count, base_type::_in->readBuf, base_type::out.writeBuf); | ||||
|  | ||||
|             // Swap if some data was generated | ||||
|             base_type::_in->flush(); | ||||
|             if (!base_type::out.swap(count)) { return -1; } | ||||
|             return count; | ||||
|         } | ||||
|  | ||||
|     protected: | ||||
|         void initBuffers() { | ||||
|             // Allocate FFT buffers | ||||
|             forwFFTIn = (float*)fftwf_malloc(_bins * sizeof(float)); | ||||
|             forwFFTOut = (complex_t*)fftwf_malloc(_bins * sizeof(complex_t)); | ||||
|             backFFTIn = (complex_t*)fftwf_malloc(_bins * sizeof(complex_t)); | ||||
|             backFFTOut = (float*)fftwf_malloc(_bins * sizeof(float)); | ||||
|  | ||||
|             // Allocate and clear delay buffer | ||||
|             buffer = buffer::alloc<float>(STREAM_BUFFER_SIZE + 64000); | ||||
|             bufferStart = &buffer[_bins - 1]; | ||||
|             buffer::clear(buffer, _bins - 1); | ||||
|  | ||||
|             // Clear backward FFT input | ||||
|             buffer::clear(backFFTIn, _bins); | ||||
|  | ||||
|             // Allocate amplitude buffer | ||||
|             ampBuf = buffer::alloc<float>(_bins); | ||||
|             scaledAmps = buffer::alloc<float>(_bins); | ||||
|             noisePrint = buffer::alloc<float>(_bins); | ||||
|             buffer::clear(noisePrint, _bins); | ||||
|  | ||||
|             // Allocate and generate Window | ||||
|             fftWin = buffer::alloc<float>(_bins); | ||||
|             for (int i = 0; i < _bins; i++) { fftWin[i] = window::nuttall(i, _bins - 1); } | ||||
|  | ||||
|             // Plan FFTs | ||||
|             forwardPlan = fftwf_plan_dft_r2c_1d(_bins, forwFFTIn, (fftwf_complex*)forwFFTOut, FFTW_ESTIMATE); | ||||
|             backwardPlan = fftwf_plan_dft_c2r_1d(_bins, (fftwf_complex*)backFFTIn, backFFTOut, FFTW_ESTIMATE); | ||||
|         } | ||||
|  | ||||
|         void destroyBuffers() { | ||||
|             fftwf_destroy_plan(forwardPlan); | ||||
|             fftwf_destroy_plan(backwardPlan); | ||||
|             fftwf_free(forwFFTIn); | ||||
|             fftwf_free(forwFFTOut); | ||||
|             fftwf_free(backFFTIn); | ||||
|             fftwf_free(backFFTOut); | ||||
|             buffer::free(buffer); | ||||
|             buffer::free(ampBuf); | ||||
|             buffer::free(scaledAmps); | ||||
|             buffer::free(noisePrint); | ||||
|             buffer::free(fftWin); | ||||
|         } | ||||
|  | ||||
|         float _level = 0.0f; | ||||
|  | ||||
|         float* forwFFTIn; | ||||
|         complex_t* forwFFTOut; | ||||
|         complex_t* backFFTIn; | ||||
|         float* backFFTOut; | ||||
|  | ||||
|         fftwf_plan forwardPlan; | ||||
|         fftwf_plan backwardPlan; | ||||
|  | ||||
|         float* buffer; | ||||
|         float* bufferStart; | ||||
|  | ||||
|         float* fftWin; | ||||
|  | ||||
|         float* ampBuf; | ||||
|         float* scaledAmps; | ||||
|         float* noisePrint; | ||||
|  | ||||
|         int _bins; | ||||
|         int complexBins; | ||||
|         float normFactor = 1.0f; | ||||
|  | ||||
|         float alpha = 0.0001f; | ||||
|         float beta = 0.9999f; | ||||
|     };  | ||||
| } | ||||
| @@ -37,21 +37,17 @@ namespace dsp::noise_reduction { | ||||
|  | ||||
|         inline int process(int count, complex_t* in, complex_t* out) { | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 // Get signal amplitude | ||||
|                 // Get signal amplitude and pass value if null | ||||
|                 float inAmp = in[i].amplitude(); | ||||
|  | ||||
|                 // Update average amplitude | ||||
|                 float gain = 1.0f; | ||||
|                 if (inAmp != 0.0f) { | ||||
|                     amp = (amp * _invRate) + (inAmp * _rate); | ||||
|                     float excess = inAmp / amp; | ||||
|                     if (excess > _level) { | ||||
|                         gain = 1.0f / excess; | ||||
|                     } | ||||
|                 if (!inAmp) { | ||||
|                     out[i] = in[i]; | ||||
|                 } | ||||
|                  | ||||
|                 // Scale output by gain | ||||
|                 out[i] = in[i] * gain; | ||||
|  | ||||
|                 // Update running average of amplitude | ||||
|                 amp = (_rate*inAmp) + (_invRate*amp); | ||||
|  | ||||
|                 // Null out if spike (Note: ideally, it should try to guess the real data) | ||||
|                 out[i] = (inAmp > _level*amp) ? complex_t{0.0f,0.0f} : in[i]; | ||||
|             } | ||||
|             return count; | ||||
|         } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ namespace dsp::taps { | ||||
|         if (oddTapCount && !(count % 2)) { count++; } | ||||
|         return windowedSinc<T>(count, (bandStop - bandStart) / 2.0, sampleRate, [=](double n, double N) { | ||||
|             if constexpr (std::is_same_v<T, float>) { | ||||
|                 return cosf(offsetOmega * (float)n) * window::nuttall(n, N); | ||||
|                 return 2.0f * cosf(offsetOmega * (float)n) * window::nuttall(n, N); | ||||
|             } | ||||
|             if constexpr (std::is_same_v<T, complex_t>) { | ||||
|                 // The offset is negative to flip the taps. Complex bandpass are asymetric | ||||
|   | ||||
| @@ -169,7 +169,7 @@ namespace flog { | ||||
|             fprintf(outStream, "] %s\n", out.c_str()); | ||||
| #elif defined(__ANDROID__) | ||||
|             // Print format string | ||||
|             __android_log_buf_print(LOG_ID_DEFAULT, TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n", | ||||
|             __android_log_print(TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n", | ||||
|                     nowc->tm_mday, nowc->tm_mon + 1, nowc->tm_year + 1900, nowc->tm_hour, nowc->tm_min, nowc->tm_sec, 0, TYPE_COLORS[type], TYPE_STR[type], out.c_str()); | ||||
| #else | ||||
|             // Print format string | ||||
|   | ||||
| @@ -288,6 +288,7 @@ namespace net { | ||||
|  | ||||
|         // Save data | ||||
|         for (auto iface = addresses; iface; iface = iface->ifa_next) { | ||||
|             if (!iface->ifa_addr || !iface->ifa_netmask) { continue; } | ||||
|             if (iface->ifa_addr->sa_family != AF_INET) { continue; } | ||||
|             InterfaceInfo info; | ||||
|             info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #include <utils/networking.h> | ||||
| #include <assert.h> | ||||
| #include <utils/flog.h> | ||||
| #include <stdexcept> | ||||
|  | ||||
| namespace net { | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ class OptionList { | ||||
| public: | ||||
|     OptionList() { updateText(); } | ||||
|  | ||||
|     void define(K key, std::string name, T value) { | ||||
|     void define(const K& key, const std::string& name, const T& value) { | ||||
|         if (keyExists(key)) { throw std::runtime_error("Key already exists"); } | ||||
|         if (nameExists(name)) { throw std::runtime_error("Name already exists"); } | ||||
|         if (valueExists(value)) { throw std::runtime_error("Value already exists"); } | ||||
| @@ -18,27 +18,27 @@ public: | ||||
|         updateText(); | ||||
|     } | ||||
|  | ||||
|     void define(std::string name, T value) { | ||||
|     void define(const std::string& name, const T& value) { | ||||
|         define(name, name, value); | ||||
|     } | ||||
|  | ||||
|     void undefined(int id) { | ||||
|     void undefine(int id) { | ||||
|         keys.erase(keys.begin() + id); | ||||
|         names.erase(names.begin() + id); | ||||
|         values.erase(values.begin() + id); | ||||
|         updateText(); | ||||
|     } | ||||
|  | ||||
|     void undefineKey(K key) { | ||||
|         undefined(keyId(key)); | ||||
|     void undefineKey(const K& key) { | ||||
|         undefine(keyId(key)); | ||||
|     } | ||||
|  | ||||
|     void undefineName(std::string name) { | ||||
|         undefined(nameId(name)); | ||||
|     void undefineName(const std::string& name) { | ||||
|         undefine(nameId(name)); | ||||
|     } | ||||
|  | ||||
|     void undefineValue(T value) { | ||||
|         undefined(valueId(value)); | ||||
|     void undefineValue(const T& value) { | ||||
|         undefine(valueId(value)); | ||||
|     } | ||||
|  | ||||
|     void clear() { | ||||
| @@ -48,61 +48,61 @@ public: | ||||
|         updateText(); | ||||
|     } | ||||
|  | ||||
|     int size() { | ||||
|     int size() const { | ||||
|         return keys.size(); | ||||
|     } | ||||
|  | ||||
|     bool empty() { | ||||
|     bool empty() const { | ||||
|         return keys.empty(); | ||||
|     } | ||||
|  | ||||
|     bool keyExists(K key) { | ||||
|     bool keyExists(const K& key) const { | ||||
|         if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool nameExists(std::string name) { | ||||
|     bool nameExists(const std::string& name) const { | ||||
|         if (std::find(names.begin(), names.end(), name) != names.end()) { return true; } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool valueExists(T value) { | ||||
|     bool valueExists(const T& value) const { | ||||
|         if (std::find(values.begin(), values.end(), value) != values.end()) { return true; } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     int keyId(K key) { | ||||
|     int keyId(const K& key) const { | ||||
|         auto it = std::find(keys.begin(), keys.end(), key); | ||||
|         if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); } | ||||
|         return std::distance(keys.begin(), it); | ||||
|     } | ||||
|  | ||||
|     int nameId(std::string name) { | ||||
|     int nameId(const std::string& name) const { | ||||
|         auto it = std::find(names.begin(), names.end(), name); | ||||
|         if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); } | ||||
|         return std::distance(names.begin(), it); | ||||
|     } | ||||
|  | ||||
|     int valueId(T value) { | ||||
|     int valueId(const T& value) const { | ||||
|         auto it = std::find(values.begin(), values.end(), value); | ||||
|         if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); } | ||||
|         return std::distance(values.begin(), it); | ||||
|     } | ||||
|  | ||||
|     K key(int id) { | ||||
|     inline const K& key(int id) const { | ||||
|         return keys[id]; | ||||
|     } | ||||
|  | ||||
|     std::string name(int id) { | ||||
|     inline const std::string& name(int id) const { | ||||
|         return names[id]; | ||||
|     } | ||||
|  | ||||
|     T value(int id) { | ||||
|     inline const T& value(int id) const { | ||||
|         return values[id]; | ||||
|     } | ||||
|  | ||||
|     T operator[](int& id) { | ||||
|         return value(id); | ||||
|     inline const T& operator[](int& id) const { | ||||
|         return values[id]; | ||||
|     } | ||||
|  | ||||
|     const char* txt = NULL; | ||||
|   | ||||
| @@ -214,10 +214,10 @@ private: | ||||
|  | ||||
|         if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) { | ||||
|             if (_this->showLines) { | ||||
|                 _this->diag.lines.push_back(-0.75f); | ||||
|                 _this->diag.lines.push_back(-0.25f); | ||||
|                 _this->diag.lines.push_back(0.25f); | ||||
|                 _this->diag.lines.push_back(0.75f); | ||||
|                 _this->diag.lines.push_back(-1.0); | ||||
|                 _this->diag.lines.push_back(-1.0/3.0); | ||||
|                 _this->diag.lines.push_back(1.0/3.0); | ||||
|                 _this->diag.lines.push_back(1.0); | ||||
|             } | ||||
|             else { | ||||
|                 _this->diag.lines.clear(); | ||||
|   | ||||
| @@ -45,6 +45,7 @@ namespace demod { | ||||
|         virtual int getDefaultDeemphasisMode() = 0; | ||||
|         virtual bool getFMIFNRAllowed() = 0; | ||||
|         virtual bool getNBAllowed() = 0; | ||||
|         virtual bool getAFNRAllowed() = 0; | ||||
|         virtual dsp::stream<dsp::stereo_t>* getOutput() = 0; | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -86,6 +86,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -92,6 +92,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -79,6 +79,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -79,6 +79,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -19,15 +19,17 @@ namespace demod { | ||||
|  | ||||
|             // Load config | ||||
|             _config->acquire(); | ||||
|             bool modified = false; | ||||
|             if (config->conf[name][getName()].contains("lowPass")) { | ||||
|                 _lowPass = config->conf[name][getName()]["lowPass"]; | ||||
|             } | ||||
|             _config->release(modified); | ||||
|             if (config->conf[name][getName()].contains("highPass")) { | ||||
|                 _highPass = config->conf[name][getName()]["highPass"]; | ||||
|             } | ||||
|             _config->release(); | ||||
|  | ||||
|  | ||||
|             // Define structure | ||||
|             demod.init(input, getIFSampleRate(), bandwidth, _lowPass); | ||||
|             demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass); | ||||
|         } | ||||
|  | ||||
|         void start() { demod.start(); } | ||||
| @@ -41,6 +43,12 @@ namespace demod { | ||||
|                 _config->conf[name][getName()]["lowPass"] = _lowPass; | ||||
|                 _config->release(true); | ||||
|             } | ||||
|             if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) { | ||||
|                 demod.setHighPass(_highPass); | ||||
|                 _config->acquire(); | ||||
|                 _config->conf[name][getName()]["highPass"] = _highPass; | ||||
|                 _config->release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         void setBandwidth(double bandwidth) { | ||||
| @@ -67,6 +75,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return true; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
| @@ -75,6 +84,7 @@ namespace demod { | ||||
|         ConfigManager* _config = NULL; | ||||
|  | ||||
|         bool _lowPass = true; | ||||
|         bool _highPass = false; | ||||
|  | ||||
|         std::string name; | ||||
|     }; | ||||
|   | ||||
| @@ -59,6 +59,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -80,6 +80,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return true; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -130,6 +130,7 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; } | ||||
|         bool getFMIFNRAllowed() { return true; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|         // ============= DEDICATED FUNCTIONS ============= | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include <dsp/noise_reduction/noise_blanker.h> | ||||
| #include <dsp/noise_reduction/fm_if.h> | ||||
| #include <dsp/noise_reduction/squelch.h> | ||||
| #include <dsp/noise_reduction/audio.h> | ||||
| #include <dsp/multirate/rational_resampler.h> | ||||
| #include <dsp/filter/deephasis.h> | ||||
| #include <core.h> | ||||
| @@ -83,9 +84,11 @@ public: | ||||
|  | ||||
|         resamp.init(NULL, 250000.0, 48000.0); | ||||
|         deemp.init(NULL, 50e-6, 48000.0); | ||||
|         afNR.init(NULL, 1024); | ||||
|  | ||||
|         afChain.addBlock(&resamp, true); | ||||
|         afChain.addBlock(&deemp, false); | ||||
|         afChain.addBlock(&afNR, false); | ||||
|  | ||||
|         // Initialize the sink | ||||
|         srChangeHandler.ctx = this; | ||||
| @@ -247,6 +250,12 @@ private: | ||||
|             if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); } | ||||
|         } | ||||
|          | ||||
|         // Noise reduction | ||||
|         if (_this->afNRAllowed) { | ||||
|             if (ImGui::Checkbox(("Audio Noise Reduction##_radio_afnr_ena_" + _this->name).c_str(), &_this->afNREnabled)) { | ||||
|                 _this->setAFNREnabled(_this->afNREnabled); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Squelch | ||||
|         if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) { | ||||
| @@ -370,6 +379,8 @@ private: | ||||
|         fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE); | ||||
|         nbAllowed = selectedDemod->getNBAllowed(); | ||||
|         nbEnabled = false; | ||||
|         afNRAllowed = selectedDemod->getAFNRAllowed(); | ||||
|         afNREnabled = false; | ||||
|         nbLevel = 0.0f; | ||||
|         double ifSamplerate = selectedDemod->getIFSampleRate(); | ||||
|         config.acquire(); | ||||
| @@ -411,6 +422,9 @@ private: | ||||
|         if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerLevel")) { | ||||
|             nbLevel = config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"]; | ||||
|         } | ||||
|         if (config.conf[name][selectedDemod->getName()].contains("audioNoiseReductionEnabled")) { | ||||
|             nbEnabled = config.conf[name][selectedDemod->getName()]["audioNoiseReductionEnabled"]; | ||||
|         } | ||||
|         config.release(); | ||||
|  | ||||
|         // Configure VFO | ||||
| @@ -446,7 +460,10 @@ private: | ||||
|             afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); }); | ||||
|  | ||||
|             // Configure deemphasis | ||||
|             setDeemphasisMode(deempModes[deempId]); | ||||
|             setDeemphasisMode(deempAllowed ? deempModes[deempId] : DEEMP_MODE_NONE); | ||||
|  | ||||
|             // Configure AF NR | ||||
|             setAFNREnabled(afNRAllowed && afNREnabled); | ||||
|         } | ||||
|         else { | ||||
|             // Disable everything if post processing is disabled | ||||
| @@ -508,6 +525,17 @@ private: | ||||
|         config.release(true); | ||||
|     } | ||||
|  | ||||
|     void setAFNREnabled(bool enable) { | ||||
|         afNREnabled = enable; | ||||
|         if (!postProcEnabled || !selectedDemod) { return; } | ||||
|         afChain.setBlockEnabled(&afNR, afNREnabled, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); }); | ||||
|  | ||||
|         // Save config | ||||
|         config.acquire(); | ||||
|         config.conf[name][selectedDemod->getName()]["audioNoiseReductionEnabled"] = nbEnabled; | ||||
|         config.release(true); | ||||
|     } | ||||
|  | ||||
|     void setNBEnabled(bool enable) { | ||||
|         nbEnabled = enable; | ||||
|         if (!selectedDemod) { return; } | ||||
| @@ -660,6 +688,7 @@ private: | ||||
|     dsp::chain<dsp::stereo_t> afChain; | ||||
|     dsp::multirate::RationalResampler<dsp::stereo_t> resamp; | ||||
|     dsp::filter::Deemphasis<dsp::stereo_t> deemp; | ||||
|     dsp::noise_reduction::Audio afNR; | ||||
|  | ||||
|     SinkManager::Stream stream; | ||||
|  | ||||
| @@ -683,6 +712,9 @@ private: | ||||
|     int deempId = 0; | ||||
|     bool deempAllowed; | ||||
|  | ||||
|     bool afNREnabled = false; | ||||
|     bool afNRAllowed; | ||||
|  | ||||
|     bool FMIFNRAllowed; | ||||
|     bool FMIFNREnabled = false; | ||||
|     int fmIFPresetId; | ||||
|   | ||||
| @@ -23,7 +23,8 @@ | ||||
| #include <utils/wav.h> | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
| #define SILENCE_LVL 10e-20 | ||||
|  | ||||
| #define SILENCE_LVL 10e-6 | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "recorder", | ||||
| @@ -315,11 +316,11 @@ private: | ||||
|             } | ||||
|             if (_this->recording) { style::endDisabled(); } | ||||
|  | ||||
|             // if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) { | ||||
|             //     config.acquire(); | ||||
|             //     config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence; | ||||
|             //     config.release(true); | ||||
|             // } | ||||
|             if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) { | ||||
|                 config.acquire(); | ||||
|                 config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Record button | ||||
| @@ -338,7 +339,13 @@ private: | ||||
|             uint64_t seconds = _this->writer.getSamplesWritten() / _this->samplerate; | ||||
|             time_t diff = seconds; | ||||
|             tm* dtm = gmtime(&diff); | ||||
|             ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); | ||||
|  | ||||
|             if (_this->ignoreSilence && _this->ignoringSilence) { | ||||
|                 ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Paused %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); | ||||
|             } | ||||
|             else { | ||||
|                 ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -480,13 +487,31 @@ private: | ||||
|  | ||||
|     static void stereoHandler(dsp::stereo_t* data, int count, void* ctx) { | ||||
|         RecorderModule* _this = (RecorderModule*)ctx; | ||||
|         // TODO: Ignore silence | ||||
|         if (_this->ignoreSilence) { | ||||
|             float absMax = 0.0f; | ||||
|             float* _data = (float*)data; | ||||
|             int _count = count * 2; | ||||
|             for (int i = 0; i < _count; i++) { | ||||
|                 float val = fabsf(_data[i]); | ||||
|                 if (val > absMax) { absMax = val; } | ||||
|             } | ||||
|             _this->ignoringSilence = (absMax < SILENCE_LVL); | ||||
|             if (_this->ignoringSilence) { return; } | ||||
|         } | ||||
|         _this->writer.write((float*)data, count); | ||||
|     } | ||||
|  | ||||
|     static void monoHandler(float* data, int count, void* ctx) { | ||||
|         RecorderModule* _this = (RecorderModule*)ctx; | ||||
|         // TODO: Ignore silence | ||||
|         if (_this->ignoreSilence) { | ||||
|             float absMax = 0.0f; | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 float val = fabsf(data[i]); | ||||
|                 if (val > absMax) { absMax = val; } | ||||
|             } | ||||
|             _this->ignoringSilence = (absMax < SILENCE_LVL); | ||||
|             if (_this->ignoringSilence) { return; } | ||||
|         } | ||||
|         _this->writer.write(data, count); | ||||
|     } | ||||
|  | ||||
| @@ -529,6 +554,7 @@ private: | ||||
|     dsp::stereo_t audioLvl = { -100.0f, -100.0f }; | ||||
|  | ||||
|     bool recording = false; | ||||
|     bool ignoringSilence = false; | ||||
|     wav::Writer writer; | ||||
|     std::recursive_mutex recMtx; | ||||
|     dsp::stream<dsp::complex_t>* basebandStream; | ||||
|   | ||||
							
								
								
									
										11
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								readme.md
									
									
									
									
									
								
							| @@ -429,25 +429,34 @@ I will soon publish a contributing.md listing the code style to use. | ||||
| * Dale L Puckett (K0HYD) | ||||
| * [Daniele D'Agnelli](https://linkedin.com/in/dagnelli) | ||||
| * D. Jones | ||||
| * Dexruus | ||||
| * [EB3FRN](https://www.eb3frn.net/) | ||||
| * Eric Johnson | ||||
| * Ernest Murphy (NH7L) | ||||
| * Flinger Films | ||||
| * [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ) | ||||
| * gringogrigio | ||||
| * Jeff Moe | ||||
| * Joe Cupano | ||||
| * KD1SQ | ||||
| * Kezza | ||||
| * Krys Kamieniecki | ||||
| * Lee Donaghy | ||||
| * Lee KD1SQ | ||||
| * .lozenge. (Hank Hill) | ||||
| * Martin Herren (HB9FXX) | ||||
| * ON4MU | ||||
| * [Passion-Radio.com](https://passion-radio.com/) | ||||
| * Paul Maine | ||||
| * Peter Betz | ||||
| * [Scanner School](https://scannerschool.com/) | ||||
| * Scott Palmer | ||||
| * [SignalsEverywhere](https://signalseverywhere.com/) | ||||
| * Syne Ardwin (WI9SYN) | ||||
| * [W4IPA](https://twitter.com/W4IPAstroke5) | ||||
| * [Zipper](github.com/reppiZ) | ||||
| * William Arcand (W1WRA) | ||||
| * [Yves Rougy](https://www.twitch.tv/yorzian) | ||||
| * [Zipper](https://github.com/reppiZ) | ||||
|  | ||||
| ## Contributors | ||||
|  | ||||
|   | ||||
							
								
								
									
										274
									
								
								root/res/bandplans/belgium.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								root/res/bandplans/belgium.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,274 @@ | ||||
| { | ||||
|   "name": "Belgium", | ||||
|   "country_name": "Belgium", | ||||
|   "country_code": "BE", | ||||
|   "author_name": "Bastien Cabay - ON4BCY", | ||||
|   "author_url": "https://qrz.com/db/ON4BCY", | ||||
|   "bands": [ | ||||
|     { | ||||
|       "name": "2200m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 135700, | ||||
|       "end": 137800 | ||||
|     }, | ||||
|     { | ||||
|       "name": "630m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 472000, | ||||
|       "end": 479000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "600m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 501000, | ||||
|       "end": 504000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 526500, | ||||
|       "end": 1606500 | ||||
|     }, | ||||
|     { | ||||
|       "name": "160m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 1810000, | ||||
|       "end": 2000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "80m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 3500000, | ||||
|       "end": 3800000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "60m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 5351500, | ||||
|       "end": 5366500 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 5950000, | ||||
|       "end": 6200000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "40m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 7000000, | ||||
|       "end": 7200000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 7200000, | ||||
|       "end": 7300000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 9500000, | ||||
|       "end": 9900000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "30m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 10100000, | ||||
|       "end": 10150000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 11650000, | ||||
|       "end": 12050000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 13600000, | ||||
|       "end": 13800000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "20m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 14000000, | ||||
|       "end": 14350000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 15100000, | ||||
|       "end": 15600000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 17550000, | ||||
|       "end": 17900000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "17m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 18068000, | ||||
|       "end": 18168000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "15m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 21000000, | ||||
|       "end": 21450000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 21450000, | ||||
|       "end": 21850000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "12m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 24890000, | ||||
|       "end": 24990000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "AM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 25670000, | ||||
|       "end": 26100000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "11m - Citizen Band", | ||||
|       "type": "amateur", | ||||
|       "start": 26960000, | ||||
|       "end": 27410000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "10m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 28000000, | ||||
|       "end": 29700000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "6m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 50000000, | ||||
|       "end": 52000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "4m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 69945000, | ||||
|       "end": 69955000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "4m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 70190000, | ||||
|       "end": 70412500 | ||||
|     }, | ||||
|     { | ||||
|       "name": "FM Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 87500000, | ||||
|       "end": 108000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Space Exploration / Meteorology Sat. / S-PCS", | ||||
|       "type": "satellite", | ||||
|       "start": 137000000, | ||||
|       "end": 138000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "2m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 144000000, | ||||
|       "end": 146000000 | ||||
|     }, | ||||
|  | ||||
|     { | ||||
|       "name": "T-DAB Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 174000000, | ||||
|       "end": 223000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 430000000, | ||||
|       "end": 440000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "PMR446", | ||||
|       "type": "amateur", | ||||
|       "start": 446000000, | ||||
|       "end": 446200000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "DVB-T - Broadcast", | ||||
|       "type": "broadcast", | ||||
|       "start": 470000000, | ||||
|       "end": 790000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "23cm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 1240000000, | ||||
|       "end": 1300000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "13cm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 2300000000, | ||||
|       "end": 2450000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "6cm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 5650000000, | ||||
|       "end": 5850000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "3cm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 10000000000, | ||||
|       "end": 10500000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "1.25cm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 24000000000, | ||||
|       "end": 24250000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "6mm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 47000000000, | ||||
|       "end": 47200000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "4mm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 75500000000, | ||||
|       "end": 81000000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "2.5mm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 122250000000, | ||||
|       "end": 123000000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "2mm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 142000000000, | ||||
|       "end": 149000000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "1mm - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 241000000000, | ||||
|       "end": 250000000000 | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -25,7 +25,7 @@ ConfigManager config; | ||||
| struct DeviceInfo { | ||||
|     RtAudio::DeviceInfo info; | ||||
|     int id; | ||||
|     bool operator==(const struct DeviceInfo& other) { | ||||
|     bool operator==(const struct DeviceInfo& other) const { | ||||
|         return other.id == id; | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -205,6 +205,7 @@ namespace hermes { | ||||
|     } | ||||
|  | ||||
|     std::vector<Info> 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); | ||||
|          | ||||
|         // Build discovery packet | ||||
|   | ||||
| @@ -39,7 +39,7 @@ namespace hermes { | ||||
|         uint8_t gatewareVerMin; | ||||
|         BoardID boardId; | ||||
|  | ||||
|         bool operator==(const Info& b) { | ||||
|         bool operator==(const Info& b) const { | ||||
|             return !memcmp(mac, b.mac, 6); | ||||
|         } | ||||
|     }; | ||||
|   | ||||
| @@ -319,6 +319,7 @@ private: | ||||
|     static void start(void* ctx) { | ||||
|         LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|         if (_this->selectedDevName.empty()) { return; } | ||||
|  | ||||
|         // Open device | ||||
|         _this->openDev = NULL; | ||||
| @@ -329,7 +330,10 @@ private: | ||||
|         if (err) { | ||||
|             LMS_Close(_this->openDev); | ||||
|             LMS_Open(&_this->openDev, _this->devList[_this->devId], NULL); | ||||
|             LMS_Init(_this->openDev); | ||||
|             if (err = LMS_Init(_this->openDev)) { | ||||
|                 flog::error("Failed to re-initialize device ({})", err); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         flog::warn("Channel count: {0}", LMS_GetNumChannels(_this->openDev, false)); | ||||
| @@ -546,4 +550,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -108,12 +108,12 @@ namespace rfspace { | ||||
|             } | ||||
|             break; | ||||
|         case RFSPACE_DEV_ID_NET_SDR: | ||||
|         case RFSPACE_DEV_ID_SDR_IP: | ||||
|         default: | ||||
|             for (int n = 80000000 / (4 * 25); n >= 32000; n /= 2) { | ||||
|                 sr.push_back(n); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         return sr; | ||||
|   | ||||
| @@ -503,7 +503,7 @@ private: | ||||
|     float refStep = 0.5; | ||||
|  | ||||
|     struct SRCombo { | ||||
|         bool operator==(const SRCombo& b) { | ||||
|         bool operator==(const SRCombo& b) const { | ||||
|             return baseId == b.baseId && decimId == b.decimId; | ||||
|         } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user