mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 02:39:11 +01:00 
			
		
		
		
	Bugfix + added meteor demodulator
This commit is contained in:
		@@ -15,6 +15,7 @@ option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhack
 | 
			
		||||
option(OPT_BUILD_RTL_SDR_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON)
 | 
			
		||||
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Depedencies: rtaudio)" ON)
 | 
			
		||||
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder" OFF)
 | 
			
		||||
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module" ON)
 | 
			
		||||
# Core of SDR++
 | 
			
		||||
add_subdirectory("core")
 | 
			
		||||
 | 
			
		||||
@@ -72,6 +73,10 @@ if (OPT_BUILD_FALCON9_DECODER)
 | 
			
		||||
add_subdirectory("falcon9_decoder")
 | 
			
		||||
endif (OPT_BUILD_FALCON9_DECODER)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_METEOR_DEMODULATOR)
 | 
			
		||||
add_subdirectory("meteor_demodulator")
 | 
			
		||||
endif (OPT_BUILD_METEOR_DEMODULATOR)
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
 | 
			
		||||
else()
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,6 @@
 | 
			
		||||
 | 
			
		||||
#include <spdlog/spdlog.h>
 | 
			
		||||
 | 
			
		||||
#define FL_M_PI                3.1415926535f
 | 
			
		||||
 | 
			
		||||
namespace dsp {
 | 
			
		||||
 | 
			
		||||
    class generic_unnamed_block {
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,30 @@ namespace dsp {
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::registerOutput(&out);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setOmega(float omega, float omegaRelLimit) {
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStop();
 | 
			
		||||
            omegaMin = _omega - (_omega * _omegaRelLimit);
 | 
			
		||||
            omegaMax = _omega + (_omega * _omegaRelLimit);
 | 
			
		||||
            _omega = omega;
 | 
			
		||||
            _dynOmega = _omega;
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setGains(float omegaGain, float muGain) {
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStop();
 | 
			
		||||
            _gainOmega = omegaGain;
 | 
			
		||||
            _muGain = muGain;
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setOmegaRelLimit(float omegaRelLimit) {
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStop();
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            omegaMin = _omega - (_omega * _omegaRelLimit);
 | 
			
		||||
            omegaMax = _omega + (_omega * _omegaRelLimit);
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<T>* in) {
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::tempStop();
 | 
			
		||||
            generic_block<MMClockRecovery<T>>::unregisterInput(_in);
 | 
			
		||||
@@ -138,10 +162,10 @@ namespace dsp {
 | 
			
		||||
 | 
			
		||||
                    // Perfrom interpolation the same way as for float values
 | 
			
		||||
                    if (i < 7) {
 | 
			
		||||
                        volk_32fc_32f_dot_prod_32fc(&_p_0T, &delay[i], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
 | 
			
		||||
                        volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&_p_0T, (lv_32fc_t*)&delay[i], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        volk_32fc_32f_dot_prod_32fc(&_p_0T, &_in->readBuf[i - 7], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
 | 
			
		||||
                        volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&_p_0T, (lv_32fc_t*)&_in->readBuf[i - 7], INTERP_TAPS[(int)roundf(_mu * 128.0f)], 8);
 | 
			
		||||
                    }
 | 
			
		||||
                    out.writeBuf[outCount++] = _p_0T;
 | 
			
		||||
 | 
			
		||||
@@ -192,7 +216,7 @@ namespace dsp {
 | 
			
		||||
        int count;
 | 
			
		||||
 | 
			
		||||
        // Delay buffer
 | 
			
		||||
        T delay[15];
 | 
			
		||||
        T delay[1024];
 | 
			
		||||
        int nextOffset = 0;
 | 
			
		||||
 | 
			
		||||
        // Configuration
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ inline float fast_arctan2(float y, float x) {
 | 
			
		||||
    if (y < 0.0f) {
 | 
			
		||||
        return -angle;
 | 
			
		||||
    }
 | 
			
		||||
   return angle;
 | 
			
		||||
    return angle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace dsp {
 | 
			
		||||
@@ -499,21 +499,185 @@ namespace dsp {
 | 
			
		||||
    class MSKDemod : public generic_hier_block<MSKDemod> {
 | 
			
		||||
    public:
 | 
			
		||||
        MSKDemod() {}
 | 
			
		||||
        MSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float baudRate) { init(input, sampleRate, deviation, baudRate); }
 | 
			
		||||
        MSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
 | 
			
		||||
            init(input, sampleRate, deviation, baudRate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<complex_t>* input, float sampleRate, float deviation, float baudRate) {
 | 
			
		||||
            demod.init(input, sampleRate, deviation);
 | 
			
		||||
            recov.init(&demod.out, sampleRate / baudRate, powf(0.01f, 2) / 4.0f, 0.01f, 100e-6f);
 | 
			
		||||
        void init(stream<complex_t>* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            _deviation = deviation;
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = muGain;
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            
 | 
			
		||||
            demod.init(input, _sampleRate, _deviation);
 | 
			
		||||
            recov.init(&demod.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
 | 
			
		||||
            out = &recov.out;
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<MSKDemod>::registerBlock(&demod);
 | 
			
		||||
            generic_hier_block<MSKDemod>::registerBlock(&recov);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setSampleRate(float sampleRate) {
 | 
			
		||||
            generic_hier_block<MSKDemod>::tempStop();
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            demod.setSampleRate(_sampleRate);
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
            generic_hier_block<MSKDemod>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setDeviation(float deviation) {
 | 
			
		||||
            _deviation = deviation;
 | 
			
		||||
            demod.setDeviation(deviation);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setBaudRate(float baudRate, float omegaRelLimit) {
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setMMGains(float omegaGain, float myGain) {
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = myGain;
 | 
			
		||||
            recov.setGains(_omegaGain, _muGain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setOmegaRelLimit(float omegaRelLimit) {
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            recov.setOmegaRelLimit(_omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<float>* out = NULL;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        FloatFMDemod demod;
 | 
			
		||||
        MMClockRecovery<float> recov;
 | 
			
		||||
 | 
			
		||||
        float _sampleRate;
 | 
			
		||||
        float _deviation;
 | 
			
		||||
        float _baudRate;
 | 
			
		||||
        float _omegaGain;
 | 
			
		||||
        float _muGain;
 | 
			
		||||
        float _omegaRelLimit;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template<int ORDER, bool OFFSET>
 | 
			
		||||
    class PSKDemod : public generic_hier_block<PSKDemod<ORDER, OFFSET>> {
 | 
			
		||||
    public:
 | 
			
		||||
        PSKDemod() {}
 | 
			
		||||
        PSKDemod(stream<complex_t>* input, float sampleRate, float baudRate, int RRCTapCount = 32, float RRCAlpha = 0.32f, float agcRate = 10e-4, float costasLoopBw = 0.004f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
 | 
			
		||||
            init(input, sampleRate, deviation, baudRate, RRCTapCount, RRCAlpha, costasLoopBw, omegaGain, muGain, omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<complex_t>* input, float sampleRate, float baudRate, int RRCTapCount = 32, float RRCAlpha = 0.32f, float agcRate = 10e-4, float costasLoopBw = 0.004f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
 | 
			
		||||
            _RRCTapCount = RRCTapCount;
 | 
			
		||||
            _RRCAlpha = RRCAlpha;
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            _agcRate = agcRate;
 | 
			
		||||
            _costasLoopBw = costasLoopBw;
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = muGain;
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            
 | 
			
		||||
            agc.init(input, 1.0f, 65535, _agcRate);
 | 
			
		||||
            taps.init(_RRCTapCount, _sampleRate, _baudRate, _RRCAlpha);
 | 
			
		||||
            rrc.init(&agc.out, &taps);
 | 
			
		||||
            demod.init(&rrc.out, _costasLoopBw);
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&agc);
 | 
			
		||||
            generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&rrc);
 | 
			
		||||
            generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&demod);
 | 
			
		||||
 | 
			
		||||
            if constexpr (OFFSET) {
 | 
			
		||||
                delay.init(&demod.out);
 | 
			
		||||
                recov.init(&delay.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
 | 
			
		||||
                generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&delay);
 | 
			
		||||
            }   
 | 
			
		||||
            else {
 | 
			
		||||
                recov.init(&demod.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<PSKDemod<ORDER, OFFSET>>::registerBlock(&recov);
 | 
			
		||||
 | 
			
		||||
            out = &recov.out;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<complex_t>* input) {
 | 
			
		||||
            agc.setInput(input);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setSampleRate(float sampleRate) {
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            rrc.tempStop();
 | 
			
		||||
            recov.tempStop();
 | 
			
		||||
            taps.setSampleRate(_sampleRate);
 | 
			
		||||
            rrc.updateWindow(&taps);
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
            rrc.tempStart();
 | 
			
		||||
            recov.tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setBaudRate(float baudRate) {
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            rrc.tempStop();
 | 
			
		||||
            recov.tempStop();
 | 
			
		||||
            taps.setBaudRate(_baudRate);
 | 
			
		||||
            rrc.updateWindow(&taps);
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
            rrc.tempStart();
 | 
			
		||||
            recov.tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setRRCParams(int RRCTapCount, float RRCAlpha) {
 | 
			
		||||
            _RRCTapCount = RRCTapCount;
 | 
			
		||||
            _RRCAlpha = RRCAlpha;
 | 
			
		||||
            taps.setTapCount(_RRCTapCount);
 | 
			
		||||
            taps.setAlpha(RRCAlpha);
 | 
			
		||||
            rrc.updateWindow(&taps);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setAgcRate(float agcRate) {
 | 
			
		||||
            _agcRate = agcRate;
 | 
			
		||||
            agc.setRate(_agcRate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setCostasLoopBw(float costasLoopBw) {
 | 
			
		||||
            _costasLoopBw = costasLoopBw;
 | 
			
		||||
            costas.setLoopBandwidth(_costasLoopBw);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setMMGains(float omegaGain, float myGain) {
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = myGain;
 | 
			
		||||
            recov.setGains(_omegaGain, _muGain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setOmegaRelLimit(float omegaRelLimit) {
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            recov.setOmegaRelLimit(_omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<complex_t>* out = NULL;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        dsp::ComplexAGC agc;
 | 
			
		||||
        dsp::RRCTaps taps;
 | 
			
		||||
        dsp::FIR<dsp::complex_t> rrc;
 | 
			
		||||
        CostasLoop<ORDER> demod;
 | 
			
		||||
        DelayImag delay;
 | 
			
		||||
        MMClockRecovery<dsp::complex_t> recov;
 | 
			
		||||
 | 
			
		||||
        int _RRCTapCount;
 | 
			
		||||
        float _RRCAlpha;
 | 
			
		||||
        float _sampleRate;
 | 
			
		||||
        float _agcRate;
 | 
			
		||||
        float _baudRate;
 | 
			
		||||
        float _costasLoopBw;
 | 
			
		||||
        float _omegaGain;
 | 
			
		||||
        float _muGain;
 | 
			
		||||
        float _omegaRelLimit;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
@@ -54,7 +54,8 @@ namespace dsp {
 | 
			
		||||
            for (int i = 0; i < count; i++) {
 | 
			
		||||
 | 
			
		||||
                // Mix the VFO with the input to create the output value
 | 
			
		||||
                outVal = lastVCO * _in->readBuf[i];
 | 
			
		||||
                outVal.re = (lastVCO.re*_in->readBuf[i].re) - (lastVCO.im*_in->readBuf[i].im);
 | 
			
		||||
                outVal.im = (lastVCO.im*_in->readBuf[i].re) + (lastVCO.re*_in->readBuf[i].im);
 | 
			
		||||
                out.writeBuf[i] = outVal;
 | 
			
		||||
 | 
			
		||||
                // Calculate the phase error estimation
 | 
			
		||||
@@ -77,12 +78,12 @@ namespace dsp {
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                if (error > 1.0f) { error = 1.0f; }
 | 
			
		||||
                if (error < -1.0f) { error = -1.0f; }
 | 
			
		||||
                else if (error < -1.0f) { error = -1.0f; }
 | 
			
		||||
                
 | 
			
		||||
                // Integrate frequency and clamp it
 | 
			
		||||
                vcoFrequency += _beta * error;
 | 
			
		||||
                if (vcoFrequency > 1.0f) { vcoFrequency = 1.0f; }
 | 
			
		||||
                if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; }
 | 
			
		||||
                else if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; }
 | 
			
		||||
 | 
			
		||||
                // Calculate new phase and wrap it
 | 
			
		||||
                vcoPhase += vcoFrequency + (_alpha * error);
 | 
			
		||||
@@ -90,8 +91,8 @@ namespace dsp {
 | 
			
		||||
                while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); }
 | 
			
		||||
 | 
			
		||||
                // Calculate output
 | 
			
		||||
                lastVCO.re = cosf(vcoPhase);
 | 
			
		||||
                lastVCO.im = sinf(vcoPhase);
 | 
			
		||||
                lastVCO.re = cosf(-vcoPhase);
 | 
			
		||||
                lastVCO.im = sinf(-vcoPhase);
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
 
 | 
			
		||||
@@ -151,8 +151,14 @@ namespace dsp {
 | 
			
		||||
 | 
			
		||||
        FeedForwardAGC(stream<T>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        ~FeedForwardAGC() {
 | 
			
		||||
            generic_block<FeedForwardAGC<T>>::stop();
 | 
			
		||||
            delete[] buffer;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<T>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            buffer = new T[STREAM_BUFFER_SIZE];
 | 
			
		||||
            generic_block<FeedForwardAGC<T>>::registerInput(_in);
 | 
			
		||||
            generic_block<FeedForwardAGC<T>>::registerOutput(&out);
 | 
			
		||||
        }
 | 
			
		||||
@@ -170,27 +176,108 @@ namespace dsp {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            float level = 1e-4;
 | 
			
		||||
            float level;
 | 
			
		||||
            float val;
 | 
			
		||||
 | 
			
		||||
            // TODO: THIS AGC IS BAAAAAD!!!!
 | 
			
		||||
            // Process buffer
 | 
			
		||||
            memcpy(&buffer[inBuffer], _in->readBuf, count * sizeof(T));
 | 
			
		||||
            inBuffer += count;
 | 
			
		||||
 | 
			
		||||
            // If there aren't enough samples, wait for more
 | 
			
		||||
            if (inBuffer < sampleCount) {
 | 
			
		||||
                _in->flush();
 | 
			
		||||
                return count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int toProcess = (inBuffer - sampleCount) + 1;
 | 
			
		||||
 | 
			
		||||
            if constexpr (std::is_same_v<T, float>) {
 | 
			
		||||
                for (int i = 0; i < count; i++) {
 | 
			
		||||
                    if (fabs(_in->readBuf[i]) > level) { level = fabs(_in->readBuf[i]); }
 | 
			
		||||
                for (int i = 0; i < toProcess; i++) {
 | 
			
		||||
                    level = 1e-4;
 | 
			
		||||
                    for (int j = 0; j < sampleCount; j++) {
 | 
			
		||||
                        val = fabsf(buffer[i + j])
 | 
			
		||||
                        if (val > level) { level = val; }
 | 
			
		||||
                    }
 | 
			
		||||
                    out.writeBuf[i] = buffer[i] / level;
 | 
			
		||||
                }
 | 
			
		||||
                volk_32f_s32f_multiply_32f(out.writeBuf, _in->readBuf, 1.0f / level, count);
 | 
			
		||||
            }
 | 
			
		||||
            if constexpr (std::is_same_v<T, complex_t>) {
 | 
			
		||||
                float reAbs, imAbs, val;
 | 
			
		||||
                for (int i = 0; i < count; i++) {
 | 
			
		||||
                    reAbs = fabs(_in->readBuf[i].re);
 | 
			
		||||
                    imAbs = fabs(_in->readBuf[i].im);
 | 
			
		||||
                    if (reAbs > imAbs) { val = reAbs + 0.4 * imAbs; }
 | 
			
		||||
                    else { val = imAbs + 0.4 * reAbs; }
 | 
			
		||||
                    if (val > level) { level = val; }
 | 
			
		||||
                for (int i = 0; i < toProcess; i++) {
 | 
			
		||||
                    level = 1e-4;
 | 
			
		||||
                    for (int j = 0; j < sampleCount; j++) {
 | 
			
		||||
                        val = buffer[i + j].fastAmplitude();
 | 
			
		||||
                        if (val > level) { level = val; }
 | 
			
		||||
                    }
 | 
			
		||||
                    out.writeBuf[i] = buffer[i] / level;
 | 
			
		||||
                }
 | 
			
		||||
                lv_32fc_t cplxLvl = {1.0f / level, 1.0f / level};
 | 
			
		||||
                volk_32fc_s32fc_multiply_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_in->readBuf, cplxLvl, count);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            // Move rest of buffer
 | 
			
		||||
            memmove(buffer, &buffer[toProcess], (sampleCount - 1) * sizeof(T));
 | 
			
		||||
            inBuffer -= toProcess;
 | 
			
		||||
            
 | 
			
		||||
            if (!out.swap(count)) { return -1; }
 | 
			
		||||
            return toProcess;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<T> out;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        T* buffer;
 | 
			
		||||
        int inBuffer = 0;
 | 
			
		||||
        int sampleCount = 1024;
 | 
			
		||||
        stream<T>* _in;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class ComplexAGC : public generic_block<ComplexAGC> {
 | 
			
		||||
    public:
 | 
			
		||||
        ComplexAGC() {}
 | 
			
		||||
 | 
			
		||||
        ComplexAGC(stream<complex_t>* in, float setPoint, float maxGain, float rate) { init(in, setPoint, maxGain, rate); }
 | 
			
		||||
 | 
			
		||||
        void init(stream<complex_t>* in, float setPoint, float maxGain, float rate) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            _setPoint = setPoint;
 | 
			
		||||
            _maxGain = maxGain;
 | 
			
		||||
            _rate = rate;
 | 
			
		||||
            generic_block<ComplexAGC>::registerInput(_in);
 | 
			
		||||
            generic_block<ComplexAGC>::registerOutput(&out);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<complex_t>* in) {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<ComplexAGC>::ctrlMtx);
 | 
			
		||||
            generic_block<ComplexAGC>::tempStop();
 | 
			
		||||
            generic_block<ComplexAGC>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<ComplexAGC>::registerInput(_in);
 | 
			
		||||
            generic_block<ComplexAGC>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setSetPoint(float setPoint) {
 | 
			
		||||
            _setPoint = setPoint;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setMaxGain(float maxGain) {
 | 
			
		||||
            _maxGain = maxGain;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setRate(float rate) {
 | 
			
		||||
            _rate = rate;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            dsp::complex_t val;
 | 
			
		||||
            for (int i = 0; i < count; i++) {
 | 
			
		||||
                val = _in->readBuf[i] * _gain;
 | 
			
		||||
                out.writeBuf[i] = val;
 | 
			
		||||
                _gain += (_setPoint - val.amplitude()) * _rate;
 | 
			
		||||
                if (_gain > _maxGain) { _gain = _maxGain; }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
@@ -198,13 +285,65 @@ namespace dsp {
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<T> out;
 | 
			
		||||
        stream<complex_t> out;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        stream<T>* _in;
 | 
			
		||||
        float _gain = 1.0f;
 | 
			
		||||
        float _setPoint = 1.0f;
 | 
			
		||||
        float _maxGain = 10e4;
 | 
			
		||||
        float _rate = 10e-4;
 | 
			
		||||
        
 | 
			
		||||
        stream<complex_t>* _in;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class DelayImag : public generic_block<DelayImag> {
 | 
			
		||||
    public:
 | 
			
		||||
        DelayImag() {}
 | 
			
		||||
 | 
			
		||||
        DelayImag(stream<complex_t>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        void init(stream<complex_t>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<DelayImag>::registerInput(_in);
 | 
			
		||||
            generic_block<DelayImag>::registerOutput(&out);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<complex_t>* in) {
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<DelayImag>::ctrlMtx);
 | 
			
		||||
            generic_block<DelayImag>::tempStop();
 | 
			
		||||
            generic_block<DelayImag>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<DelayImag>::registerInput(_in);
 | 
			
		||||
            generic_block<DelayImag>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            dsp::complex_t val;
 | 
			
		||||
            for (int i = 0; i < count; i++) {
 | 
			
		||||
                val = _in->readBuf[i];
 | 
			
		||||
                out.writeBuf[i].re = val.re;
 | 
			
		||||
                out.writeBuf[i].im = lastIm;
 | 
			
		||||
                lastIm = val.im;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
            if (!out.swap(count)) { return -1; }
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<complex_t> out;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        float lastIm = 0.0f;
 | 
			
		||||
        stream<complex_t>* _in;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    template <class T>
 | 
			
		||||
    class Volume : public generic_block<Volume<T>> {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#define FL_M_PI                3.1415926535f
 | 
			
		||||
 | 
			
		||||
namespace dsp {
 | 
			
		||||
    struct complex_t {
 | 
			
		||||
@@ -6,6 +9,10 @@ namespace dsp {
 | 
			
		||||
            return complex_t{re*b, im*b};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        complex_t operator/(const float b) {
 | 
			
		||||
            return complex_t{re/b, im/b};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        complex_t operator*(const complex_t& b) {
 | 
			
		||||
            return complex_t{(re*b.re) - (im*b.im), (im*b.re) + (re*b.im)};
 | 
			
		||||
        }
 | 
			
		||||
@@ -22,6 +29,39 @@ namespace dsp {
 | 
			
		||||
            return complex_t{re, -im};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inline float phase() {
 | 
			
		||||
            return atan2f(im, re);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inline float fastPhase() {
 | 
			
		||||
            float abs_im = fabsf(im);
 | 
			
		||||
            float r, angle;
 | 
			
		||||
            if (re == 0.0f && im == 0.0f) { return 0.0f; }
 | 
			
		||||
            if (re>=0.0f) {
 | 
			
		||||
                r = (re - abs_im) / (re + abs_im);
 | 
			
		||||
                angle = (FL_M_PI / 4.0f) - (FL_M_PI / 4.0f) * r;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                r = (re + abs_im) / (abs_im - re);
 | 
			
		||||
                angle = (3.0f * (FL_M_PI / 4.0f)) - (FL_M_PI / 4.0f) * r;
 | 
			
		||||
            }
 | 
			
		||||
            if (im < 0.0f) {
 | 
			
		||||
                return -angle;
 | 
			
		||||
            }
 | 
			
		||||
            return angle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inline float amplitude() {
 | 
			
		||||
            return sqrt((re*re) + (im*im));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        inline float fastAmplitude() {
 | 
			
		||||
            float re_abs = fabsf(re);
 | 
			
		||||
            float im_abs = fabsf(re);
 | 
			
		||||
            if (re_abs > im_abs) { return re_abs + 0.4f * im_abs; }
 | 
			
		||||
            return im_abs + 0.4f * re_abs; 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float re;
 | 
			
		||||
        float im;
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -144,4 +144,89 @@ namespace dsp {
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class RRCTaps : public filter_window::generic_window {
 | 
			
		||||
        public:
 | 
			
		||||
            RRCTaps() {}
 | 
			
		||||
            RRCTaps(int tapCount, float sampleRate, float baudRate, float alpha) { init(tapCount, sampleRate, baudRate, alpha); }
 | 
			
		||||
 | 
			
		||||
            void init(int tapCount, float sampleRate, float baudRate, float alpha) {
 | 
			
		||||
                _tapCount = tapCount;
 | 
			
		||||
                _sampleRate = sampleRate;
 | 
			
		||||
                _baudRate = baudRate;
 | 
			
		||||
                _alpha = alpha;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int getTapCount() {
 | 
			
		||||
                return _tapCount;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void setSampleRate(float sampleRate) {
 | 
			
		||||
                _sampleRate = sampleRate;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void setBaudRate(float baudRate) {
 | 
			
		||||
                _baudRate = baudRate;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void setTapCount(int count) {
 | 
			
		||||
                _tapCount = count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void setAlpha(float alpha) {
 | 
			
		||||
                _alpha = alpha;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            void createTaps(float* taps, int tapCount, float factor = 1.0f) {
 | 
			
		||||
                // ======== CREDIT: GNU Radio =========
 | 
			
		||||
                tapCount |= 1; // ensure that tapCount is odd
 | 
			
		||||
 | 
			
		||||
                double spb = _sampleRate / _baudRate; // samples per bit/symbol
 | 
			
		||||
                double scale = 0;
 | 
			
		||||
                for (int i = 0; i < tapCount; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    double x1, x2, x3, num, den;
 | 
			
		||||
                    double xindx = i - tapCount / 2;
 | 
			
		||||
                    x1 = FL_M_PI * xindx / spb;
 | 
			
		||||
                    x2 = 4 * _alpha * xindx / spb;
 | 
			
		||||
                    x3 = x2 * x2 - 1;
 | 
			
		||||
 | 
			
		||||
                     // Avoid Rounding errors...
 | 
			
		||||
                    if (fabs(x3) >= 0.000001) {
 | 
			
		||||
                        if (i != tapCount / 2)
 | 
			
		||||
                            num = cos((1 + _alpha) * x1) +
 | 
			
		||||
                                sin((1 - _alpha) * x1) / (4 * _alpha * xindx / spb);
 | 
			
		||||
                        else
 | 
			
		||||
                            num = cos((1 + _alpha) * x1) + (1 - _alpha) * FL_M_PI / (4 * _alpha);
 | 
			
		||||
                        den = x3 * FL_M_PI;
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        if (_alpha == 1)
 | 
			
		||||
                        {
 | 
			
		||||
                            taps[i] = -1;
 | 
			
		||||
                            scale += taps[i];
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        x3 = (1 - _alpha) * x1;
 | 
			
		||||
                        x2 = (1 + _alpha) * x1;
 | 
			
		||||
                        num = (sin(x2) * (1 + _alpha) * FL_M_PI -
 | 
			
		||||
                            cos(x3) * ((1 - _alpha) * FL_M_PI * spb) / (4 * _alpha * xindx) +
 | 
			
		||||
                            sin(x3) * spb * spb / (4 * _alpha * xindx * xindx));
 | 
			
		||||
                        den = -32 * FL_M_PI * _alpha * _alpha * xindx / spb;
 | 
			
		||||
                    }
 | 
			
		||||
                    taps[i] = 4 * _alpha * num / den;
 | 
			
		||||
                    scale += taps[i];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < tapCount; i++) {
 | 
			
		||||
                    taps[i] = taps[i] / scale;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            int _tapCount;
 | 
			
		||||
            float _sampleRate, _baudRate, _alpha;
 | 
			
		||||
 | 
			
		||||
        };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										43
									
								
								core/src/gui/widgets/constellation_diagram.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								core/src/gui/widgets/constellation_diagram.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <gui/widgets/constellation_diagram.h>
 | 
			
		||||
 | 
			
		||||
namespace ImGui {
 | 
			
		||||
    ConstellationDiagram::ConstellationDiagram() {
 | 
			
		||||
        memset(buffer, 0, 1024 * sizeof(dsp::complex_t));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ConstellationDiagram::draw(const ImVec2& size_arg) {
 | 
			
		||||
        std::lock_guard<std::mutex> lck(bufferMtx);
 | 
			
		||||
        ImGuiWindow* window = GetCurrentWindow();
 | 
			
		||||
        ImGuiStyle& style = GetStyle();
 | 
			
		||||
        float pad = style.FramePadding.y;
 | 
			
		||||
        ImVec2 min = window->DC.CursorPos;
 | 
			
		||||
        ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), CalcItemWidth());
 | 
			
		||||
        ImRect bb(min, ImVec2(min.x+size.x, min.y+size.y));
 | 
			
		||||
        float lineHeight = size.y;
 | 
			
		||||
 | 
			
		||||
        ItemSize(size, style.FramePadding.y);
 | 
			
		||||
        if (!ItemAdd(bb, 0)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        window->DrawList->AddRectFilled(min, ImVec2(min.x+size.x, min.y+size.y), IM_COL32(0,0,0,255));
 | 
			
		||||
        ImU32 col = ImGui::GetColorU32(ImGuiCol_CheckMark, 0.7f);
 | 
			
		||||
        float increment = size.x / 1024.0f;
 | 
			
		||||
        for (int i = 0; i < 1024; i++) {
 | 
			
		||||
            if (buffer[i].re > 1.5f || buffer[i].re < -1.5f) { continue; }
 | 
			
		||||
            if (buffer[i].im > 1.5f || buffer[i].im < -1.5f) { continue; }
 | 
			
		||||
            window->DrawList->AddCircleFilled(ImVec2((((buffer[i].re / 1.5f) + 1) * (size.x*0.5f)) + min.x, (((buffer[i].im / 1.5f) + 1) * (size.y*0.5f)) + min.y), 2, col);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dsp::complex_t* ConstellationDiagram::aquireBuffer() {
 | 
			
		||||
        bufferMtx.lock();
 | 
			
		||||
        return buffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ConstellationDiagram::releaseBuffer() {
 | 
			
		||||
        bufferMtx.unlock();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								core/src/gui/widgets/constellation_diagram.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								core/src/gui/widgets/constellation_diagram.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <imgui_internal.h>
 | 
			
		||||
#include <dsp/stream.h>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <dsp/types.h>
 | 
			
		||||
 | 
			
		||||
namespace ImGui {
 | 
			
		||||
    class ConstellationDiagram {
 | 
			
		||||
    public:
 | 
			
		||||
        ConstellationDiagram();
 | 
			
		||||
 | 
			
		||||
        void draw(const ImVec2& size_arg = ImVec2(0, 0));
 | 
			
		||||
 | 
			
		||||
        dsp::complex_t* aquireBuffer();
 | 
			
		||||
 | 
			
		||||
        void releaseBuffer();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        std::mutex bufferMtx;
 | 
			
		||||
        dsp::complex_t buffer[1024];
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								meteor_demodulator/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								meteor_demodulator/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(meteor_demodulator)
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
 | 
			
		||||
else()
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -fpermissive")
 | 
			
		||||
endif (MSVC)
 | 
			
		||||
 | 
			
		||||
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
 | 
			
		||||
 | 
			
		||||
include_directories("src/")
 | 
			
		||||
include_directories("src/libcorrect/")
 | 
			
		||||
 | 
			
		||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
 | 
			
		||||
 | 
			
		||||
add_library(meteor_demodulator SHARED ${SRC})
 | 
			
		||||
target_link_libraries(meteor_demodulator PRIVATE sdrpp_core)
 | 
			
		||||
set_target_properties(meteor_demodulator PROPERTIES PREFIX "")
 | 
			
		||||
 | 
			
		||||
# Install directives
 | 
			
		||||
install(TARGETS meteor_demodulator DESTINATION lib/sdrpp/plugins)
 | 
			
		||||
							
								
								
									
										211
									
								
								meteor_demodulator/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								meteor_demodulator/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <watcher.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <options.h>
 | 
			
		||||
 | 
			
		||||
#include <dsp/pll.h>
 | 
			
		||||
#include <dsp/stream.h>
 | 
			
		||||
#include <dsp/demodulator.h>
 | 
			
		||||
#include <dsp/window.h>
 | 
			
		||||
#include <dsp/resampling.h>
 | 
			
		||||
#include <dsp/processing.h>
 | 
			
		||||
#include <dsp/routing.h>
 | 
			
		||||
#include <dsp/sink.h>
 | 
			
		||||
 | 
			
		||||
#include <gui/widgets/folder_select.h>
 | 
			
		||||
#include <gui/widgets/constellation_diagram.h>
 | 
			
		||||
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b)    ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO {
 | 
			
		||||
    /* Name:            */ "meteor_demodulator",
 | 
			
		||||
    /* Description:     */ "Meteor demodulator for SDR++",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ -1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::string genFileName(std::string prefix, std::string suffix) {
 | 
			
		||||
    time_t now = time(0);
 | 
			
		||||
    tm *ltm = localtime(&now);
 | 
			
		||||
    char buf[1024];
 | 
			
		||||
    sprintf(buf, "%s_%02d-%02d-%02d_%02d-%02d-%02d%s", prefix.c_str(), ltm->tm_hour, ltm->tm_min, ltm->tm_sec, ltm->tm_mday, ltm->tm_mon + 1, ltm->tm_year + 1900, suffix.c_str());
 | 
			
		||||
    return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define INPUT_SAMPLE_RATE 150000
 | 
			
		||||
 | 
			
		||||
class MeteorDemodulatorModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    MeteorDemodulatorModule(std::string name) : folderSelect("%ROOT%/recordings") {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        writeBuffer = new int8_t[STREAM_BUFFER_SIZE];
 | 
			
		||||
 | 
			
		||||
        vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 150000, INPUT_SAMPLE_RATE, 1);
 | 
			
		||||
        demod.init(vfo->output, INPUT_SAMPLE_RATE, 72000.0f, 32, 0.6f, 0.1f, 0.005f);
 | 
			
		||||
        split.init(demod.out);
 | 
			
		||||
        split.bindStream(&symSinkStream);
 | 
			
		||||
        split.bindStream(&sinkStream);
 | 
			
		||||
        reshape.init(&symSinkStream, 1024, (72000 / 30) - 1024);
 | 
			
		||||
        symSink.init(&reshape.out, symSinkHandler, this);
 | 
			
		||||
        sink.init(&sinkStream, sinkHandler, this);
 | 
			
		||||
 | 
			
		||||
        demod.start();
 | 
			
		||||
        split.start();
 | 
			
		||||
        reshape.start();
 | 
			
		||||
        symSink.start();
 | 
			
		||||
        sink.start();
 | 
			
		||||
        
 | 
			
		||||
        gui::menu.registerEntry(name, menuHandler, this, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MeteorDemodulatorModule() {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
        vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 150000, INPUT_SAMPLE_RATE, 1);
 | 
			
		||||
 | 
			
		||||
        demod.setInput(vfo->output);
 | 
			
		||||
 | 
			
		||||
        demod.start();
 | 
			
		||||
        split.start();
 | 
			
		||||
        reshape.start();
 | 
			
		||||
        symSink.start();
 | 
			
		||||
        sink.start();
 | 
			
		||||
 | 
			
		||||
        enabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void disable() {
 | 
			
		||||
        demod.stop();
 | 
			
		||||
        split.stop();
 | 
			
		||||
        reshape.stop();
 | 
			
		||||
        symSink.stop();
 | 
			
		||||
        sink.stop();
 | 
			
		||||
 | 
			
		||||
        sigpath::vfoManager.deleteVFO(vfo);
 | 
			
		||||
        enabled = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isEnabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static void menuHandler(void* ctx) {
 | 
			
		||||
        MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
 | 
			
		||||
 | 
			
		||||
        float menuWidth = ImGui::GetContentRegionAvailWidth();
 | 
			
		||||
 | 
			
		||||
        if (!_this->enabled) { style::beginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth);
 | 
			
		||||
        _this->constDiagram.draw();
 | 
			
		||||
 | 
			
		||||
        _this->folderSelect.render("##meteor-recorder-" + _this->name);
 | 
			
		||||
 | 
			
		||||
        if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::beginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        if (_this->recording) {
 | 
			
		||||
            if (ImGui::Button(CONCAT("Stop##_recorder_rec_", _this->name), ImVec2(menuWidth, 0))) {
 | 
			
		||||
                std::lock_guard<std::mutex> lck(_this->recMtx);
 | 
			
		||||
                _this->recording = false;
 | 
			
		||||
                _this->recFile.close();
 | 
			
		||||
                _this->dataWritten = 0;
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %.2fMB", (float)_this->dataWritten / 1000000.0f);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            if (ImGui::Button(CONCAT("Record##_recorder_rec_", _this->name), ImVec2(menuWidth, 0))) {
 | 
			
		||||
                std::lock_guard<std::mutex> lck(_this->recMtx);
 | 
			
		||||
                _this->dataWritten = 0;
 | 
			
		||||
                std::string filename = genFileName(_this->folderSelect.expandString(_this->folderSelect.path) + "/meteor", ".s");
 | 
			
		||||
                _this->recFile = std::ofstream(filename, std::ios::binary);
 | 
			
		||||
                if (_this->recFile.is_open()) {
 | 
			
		||||
                    spdlog::info("Recording to '{0}'", filename);
 | 
			
		||||
                    _this->recording = true;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    spdlog::error("Could not open file for recording!");
 | 
			
		||||
                }                
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::Text("Idle --.--MB");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::endDisabled(); }        
 | 
			
		||||
 | 
			
		||||
        if (!_this->enabled) { style::endDisabled(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) {
 | 
			
		||||
        MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
 | 
			
		||||
 | 
			
		||||
        dsp::complex_t* buf = _this->constDiagram.aquireBuffer();
 | 
			
		||||
        memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
 | 
			
		||||
        _this->constDiagram.releaseBuffer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void sinkHandler(dsp::complex_t* data, int count, void* ctx) {
 | 
			
		||||
        MeteorDemodulatorModule* _this = (MeteorDemodulatorModule*)ctx;
 | 
			
		||||
        std::lock_guard<std::mutex> lck(_this->recMtx);
 | 
			
		||||
        if (!_this->recording) { return; }
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            _this->writeBuffer[(2 * i)] = data[i].re * 64.0f;
 | 
			
		||||
            _this->writeBuffer[(2 * i) + 1] = data[i].im * 64.0f;
 | 
			
		||||
        }
 | 
			
		||||
        _this->recFile.write((char*)_this->writeBuffer, count * 2);
 | 
			
		||||
        _this->dataWritten += count * 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string name;
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // DSP Chain
 | 
			
		||||
    VFOManager::VFO* vfo;
 | 
			
		||||
    dsp::PSKDemod<4, false> demod;
 | 
			
		||||
    dsp::Splitter<dsp::complex_t> split;
 | 
			
		||||
 | 
			
		||||
    dsp::stream<dsp::complex_t> symSinkStream;
 | 
			
		||||
    dsp::stream<dsp::complex_t> sinkStream;
 | 
			
		||||
    dsp::Reshaper<dsp::complex_t> reshape;
 | 
			
		||||
    dsp::HandlerSink<dsp::complex_t> symSink;
 | 
			
		||||
    dsp::HandlerSink<dsp::complex_t> sink;
 | 
			
		||||
 | 
			
		||||
    ImGui::ConstellationDiagram constDiagram;
 | 
			
		||||
 | 
			
		||||
    FolderSelect folderSelect;
 | 
			
		||||
 | 
			
		||||
    std::mutex recMtx;
 | 
			
		||||
    bool recording = false;
 | 
			
		||||
    uint64_t dataWritten = 0;
 | 
			
		||||
    std::ofstream recFile;
 | 
			
		||||
 | 
			
		||||
    int8_t* writeBuffer;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _INIT_() {
 | 
			
		||||
    // Nothing
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
 | 
			
		||||
    return new MeteorDemodulatorModule(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
 | 
			
		||||
    delete (MeteorDemodulatorModule*)instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _END_() {
 | 
			
		||||
    // Nothing either
 | 
			
		||||
}
 | 
			
		||||
@@ -146,6 +146,7 @@ private:
 | 
			
		||||
        bw = bandWidth;
 | 
			
		||||
        _vfo->setBandwidth(bw);
 | 
			
		||||
        demod.setDeviation(bw / 2.0f);
 | 
			
		||||
        setAudioSampleRate(audioSampRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setSnapInterval(float snapInt) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user