mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 10:49:11 +01:00 
			
		
		
		
	Added M17 decoder module (unfinished)
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -11,4 +11,5 @@ build/
 | 
			
		||||
root_dev/
 | 
			
		||||
Folder.DotSettings.user
 | 
			
		||||
CMakeSettings.json
 | 
			
		||||
poggers_decoder
 | 
			
		||||
poggers_decoder
 | 
			
		||||
m17_decoder/libcorrect
 | 
			
		||||
@@ -33,6 +33,7 @@ option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depend
 | 
			
		||||
 | 
			
		||||
# Decoders
 | 
			
		||||
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
 | 
			
		||||
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (no dependencies required)" OFF)
 | 
			
		||||
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
 | 
			
		||||
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
 | 
			
		||||
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" ON)
 | 
			
		||||
@@ -133,6 +134,10 @@ if (OPT_BUILD_FALCON9_DECODER)
 | 
			
		||||
add_subdirectory("falcon9_decoder")
 | 
			
		||||
endif (OPT_BUILD_FALCON9_DECODER)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_M17_DECODER)
 | 
			
		||||
add_subdirectory("m17_decoder")
 | 
			
		||||
endif (OPT_BUILD_M17_DECODER)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_METEOR_DEMODULATOR)
 | 
			
		||||
add_subdirectory("meteor_demodulator")
 | 
			
		||||
endif (OPT_BUILD_METEOR_DEMODULATOR)
 | 
			
		||||
@@ -191,7 +196,7 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
 | 
			
		||||
    add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.dylib\" \"$<TARGET_FILE_DIR:sdrpp>\")
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON
 | 
			
		||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/Users/Alex/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_M17_DECODER=ON
 | 
			
		||||
 | 
			
		||||
# Install directives
 | 
			
		||||
install(TARGETS sdrpp DESTINATION bin)
 | 
			
		||||
 
 | 
			
		||||
@@ -73,4 +73,74 @@ namespace dsp {
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class DCBlocker : public generic_block<DCBlocker> {
 | 
			
		||||
    public:
 | 
			
		||||
        DCBlocker() {}
 | 
			
		||||
 | 
			
		||||
        DCBlocker(stream<float>* in, float rate) { init(in, rate); }
 | 
			
		||||
 | 
			
		||||
        void init(stream<float>* in, float rate) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            correctionRate = rate;
 | 
			
		||||
            offset = 0;
 | 
			
		||||
            generic_block<DCBlocker>::registerInput(_in);
 | 
			
		||||
            generic_block<DCBlocker>::registerOutput(&out);
 | 
			
		||||
            generic_block<DCBlocker>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<float>* in) {
 | 
			
		||||
            assert(generic_block<DCBlocker>::_block_init);
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<DCBlocker>::ctrlMtx);
 | 
			
		||||
            generic_block<DCBlocker>::tempStop();
 | 
			
		||||
            generic_block<DCBlocker>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<DCBlocker>::registerInput(_in);
 | 
			
		||||
            generic_block<DCBlocker>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setCorrectionRate(float rate) {
 | 
			
		||||
            correctionRate = rate;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            if (bypass) {
 | 
			
		||||
                memcpy(out.writeBuf, _in->readBuf, count * sizeof(complex_t));
 | 
			
		||||
 | 
			
		||||
                _in->flush();
 | 
			
		||||
 | 
			
		||||
                if (!out.swap(count)) { return -1; }
 | 
			
		||||
 | 
			
		||||
                return count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < count; i++) {
 | 
			
		||||
                out.writeBuf[i] = _in->readBuf[i] - offset;
 | 
			
		||||
                offset = offset + (out.writeBuf[i] * correctionRate);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            if (!out.swap(count)) { return -1; }
 | 
			
		||||
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<float> out;
 | 
			
		||||
 | 
			
		||||
        // TEMPORARY FOR DEBUG PURPOSES
 | 
			
		||||
        bool bypass = false;
 | 
			
		||||
        float offset;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        stream<float>* _in;
 | 
			
		||||
        float correctionRate = 0.00001;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
#include <dsp/conversion.h>
 | 
			
		||||
#include <dsp/audio.h>
 | 
			
		||||
#include <dsp/stereo_fm.h>
 | 
			
		||||
#include <dsp/correction.h>
 | 
			
		||||
 | 
			
		||||
#define FAST_ATAN2_COEF1    FL_M_PI / 4.0f
 | 
			
		||||
#define FAST_ATAN2_COEF2    3.0f * FAST_ATAN2_COEF1
 | 
			
		||||
@@ -371,11 +372,11 @@ namespace dsp {
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class MSKDemod : public generic_hier_block<MSKDemod> {
 | 
			
		||||
    class FSKDemod : public generic_hier_block<FSKDemod> {
 | 
			
		||||
    public:
 | 
			
		||||
        MSKDemod() {}
 | 
			
		||||
        FSKDemod() {}
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
        FSKDemod(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, omegaGain, muGain, omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -391,47 +392,47 @@ namespace dsp {
 | 
			
		||||
            recov.init(&demod.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
 | 
			
		||||
            out = &recov.out;
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<MSKDemod>::registerBlock(&demod);
 | 
			
		||||
            generic_hier_block<MSKDemod>::registerBlock(&recov);
 | 
			
		||||
            generic_hier_block<MSKDemod>::_block_init = true;
 | 
			
		||||
            generic_hier_block<FSKDemod>::registerBlock(&demod);
 | 
			
		||||
            generic_hier_block<FSKDemod>::registerBlock(&recov);
 | 
			
		||||
            generic_hier_block<FSKDemod>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<complex_t>* input) {
 | 
			
		||||
            assert((generic_hier_block<MSKDemod>::_block_init));
 | 
			
		||||
            assert((generic_hier_block<FSKDemod>::_block_init));
 | 
			
		||||
            demod.setInput(input);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setSampleRate(float sampleRate) {
 | 
			
		||||
            assert(generic_hier_block<MSKDemod>::_block_init);
 | 
			
		||||
            generic_hier_block<MSKDemod>::tempStop();
 | 
			
		||||
            assert(generic_hier_block<FSKDemod>::_block_init);
 | 
			
		||||
            generic_hier_block<FSKDemod>::tempStop();
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            demod.setSampleRate(_sampleRate);
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
            generic_hier_block<MSKDemod>::tempStart();
 | 
			
		||||
            generic_hier_block<FSKDemod>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setDeviation(float deviation) {
 | 
			
		||||
            assert(generic_hier_block<MSKDemod>::_block_init);
 | 
			
		||||
            assert(generic_hier_block<FSKDemod>::_block_init);
 | 
			
		||||
            _deviation = deviation;
 | 
			
		||||
            demod.setDeviation(deviation);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setBaudRate(float baudRate, float omegaRelLimit) {
 | 
			
		||||
            assert(generic_hier_block<MSKDemod>::_block_init);
 | 
			
		||||
            assert(generic_hier_block<FSKDemod>::_block_init);
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setMMGains(float omegaGain, float myGain) {
 | 
			
		||||
            assert(generic_hier_block<MSKDemod>::_block_init);
 | 
			
		||||
            assert(generic_hier_block<FSKDemod>::_block_init);
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = myGain;
 | 
			
		||||
            recov.setGains(_omegaGain, _muGain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setOmegaRelLimit(float omegaRelLimit) {
 | 
			
		||||
            assert(generic_hier_block<MSKDemod>::_block_init);
 | 
			
		||||
            assert(generic_hier_block<FSKDemod>::_block_init);
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            recov.setOmegaRelLimit(_omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
@@ -450,6 +451,105 @@ namespace dsp {
 | 
			
		||||
        float _omegaRelLimit;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class GFSKDemod : public generic_hier_block<GFSKDemod> {
 | 
			
		||||
    public:
 | 
			
		||||
        GFSKDemod() {}
 | 
			
		||||
 | 
			
		||||
        GFSKDemod(stream<complex_t>* input, float sampleRate, float deviation, float rrcAlpha, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
 | 
			
		||||
            init(input, sampleRate, deviation, rrcAlpha, baudRate, omegaGain, muGain, omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<complex_t>* input, float sampleRate, float deviation, float rrcAlpha, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) {
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            _deviation = deviation;
 | 
			
		||||
            _rrcAlpha = rrcAlpha;
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = muGain;
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            
 | 
			
		||||
            demod.init(input, _sampleRate, _deviation);
 | 
			
		||||
            rrc.init(31, _sampleRate, _baudRate, _rrcAlpha);
 | 
			
		||||
            fir.init(&demod.out, &rrc);
 | 
			
		||||
            recov.init(&fir.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit);
 | 
			
		||||
            out = &recov.out;
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<GFSKDemod>::registerBlock(&demod);
 | 
			
		||||
            generic_hier_block<GFSKDemod>::registerBlock(&fir);
 | 
			
		||||
            generic_hier_block<GFSKDemod>::registerBlock(&recov);
 | 
			
		||||
            generic_hier_block<GFSKDemod>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<complex_t>* input) {
 | 
			
		||||
            assert((generic_hier_block<GFSKDemod>::_block_init));
 | 
			
		||||
            demod.setInput(input);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setSampleRate(float sampleRate) {
 | 
			
		||||
            assert(generic_hier_block<GFSKDemod>::_block_init);
 | 
			
		||||
            generic_hier_block<GFSKDemod>::tempStop();
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
            demod.setSampleRate(_sampleRate);
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
            rrc.setSampleRate(_sampleRate);
 | 
			
		||||
            fir.updateWindow(&rrc);
 | 
			
		||||
            generic_hier_block<GFSKDemod>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setDeviation(float deviation) {
 | 
			
		||||
            assert(generic_hier_block<GFSKDemod>::_block_init);
 | 
			
		||||
            _deviation = deviation;
 | 
			
		||||
            demod.setDeviation(deviation);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setRRCAlpha(float rrcAlpha) {
 | 
			
		||||
            assert(generic_hier_block<GFSKDemod>::_block_init);
 | 
			
		||||
            _rrcAlpha = rrcAlpha;
 | 
			
		||||
            rrc.setAlpha(_rrcAlpha);
 | 
			
		||||
            fir.updateWindow(&rrc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setBaudRate(float baudRate, float omegaRelLimit) {
 | 
			
		||||
            assert(generic_hier_block<GFSKDemod>::_block_init);
 | 
			
		||||
            _baudRate = baudRate;
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            generic_hier_block<GFSKDemod>::tempStop();
 | 
			
		||||
            recov.setOmega(_sampleRate / _baudRate, _omegaRelLimit);
 | 
			
		||||
            rrc.setBaudRate(_baudRate);
 | 
			
		||||
            fir.updateWindow(&rrc);
 | 
			
		||||
            generic_hier_block<GFSKDemod>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setMMGains(float omegaGain, float myGain) {
 | 
			
		||||
            assert(generic_hier_block<GFSKDemod>::_block_init);
 | 
			
		||||
            _omegaGain = omegaGain;
 | 
			
		||||
            _muGain = myGain;
 | 
			
		||||
            recov.setGains(_omegaGain, _muGain);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setOmegaRelLimit(float omegaRelLimit) {
 | 
			
		||||
            assert(generic_hier_block<GFSKDemod>::_block_init);
 | 
			
		||||
            _omegaRelLimit = omegaRelLimit;
 | 
			
		||||
            recov.setOmegaRelLimit(_omegaRelLimit);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<float>* out = NULL;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        FloatFMDemod demod;
 | 
			
		||||
        RRCTaps rrc;
 | 
			
		||||
        FIR<float> fir;
 | 
			
		||||
        MMClockRecovery<float> recov;
 | 
			
		||||
 | 
			
		||||
        float _sampleRate;
 | 
			
		||||
        float _deviation;
 | 
			
		||||
        float _rrcAlpha;
 | 
			
		||||
        float _baudRate;
 | 
			
		||||
        float _omegaGain;
 | 
			
		||||
        float _muGain;
 | 
			
		||||
        float _omegaRelLimit;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template<int ORDER, bool OFFSET>
 | 
			
		||||
    class PSKDemod : public generic_hier_block<PSKDemod<ORDER, OFFSET>> {
 | 
			
		||||
    public:
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,54 @@ namespace dsp {
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    template <class T>
 | 
			
		||||
    class StreamDoubler : public generic_block<StreamDoubler<T>> {
 | 
			
		||||
    public:
 | 
			
		||||
        StreamDoubler() {}
 | 
			
		||||
 | 
			
		||||
        StreamDoubler(stream<T>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        void init(stream<T>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<StreamDoubler>::registerInput(_in);
 | 
			
		||||
            generic_block<StreamDoubler>::registerOutput(&outA);
 | 
			
		||||
            generic_block<StreamDoubler>::registerOutput(&outB);
 | 
			
		||||
            generic_block<StreamDoubler>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<T>* in) {
 | 
			
		||||
            assert(generic_block<StreamDoubler>::_block_init);
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<StreamDoubler>::ctrlMtx);
 | 
			
		||||
            generic_block<StreamDoubler>::tempStop();
 | 
			
		||||
            generic_block<StreamDoubler>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<StreamDoubler>::registerInput(_in);
 | 
			
		||||
            generic_block<StreamDoubler>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<T> outA;
 | 
			
		||||
        stream<T> outB;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            memcpy(outA.writeBuf, _in->readBuf, count * sizeof(T));
 | 
			
		||||
            memcpy(outB.writeBuf, _in->readBuf, count * sizeof(T));
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            if (!outA.swap(count)) { return -1; }
 | 
			
		||||
            if (!outB.swap(count)) { return -1; }
 | 
			
		||||
            
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<T>* _in;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // NOTE: I'm not proud of this, it's BAD and just taken from the previous DSP, but it works...
 | 
			
		||||
    template <class T>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								m17_decoder/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								m17_decoder/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(m17_decoder)
 | 
			
		||||
 | 
			
		||||
add_subdirectory("libcorrect")
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc")
 | 
			
		||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O3 -std=c++17 -Wno-unused-command-line-argument -undefined dynamic_lookup")
 | 
			
		||||
else ()
 | 
			
		||||
    set(CMAKE_CXX_FLAGS "-O3 -std=c++17")
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
 | 
			
		||||
 | 
			
		||||
include_directories("src/")
 | 
			
		||||
include_directories("libcorrect/include")
 | 
			
		||||
 | 
			
		||||
include_directories("C:/Users/ryzerth/Documents/Code/codec2/src")
 | 
			
		||||
 | 
			
		||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
 | 
			
		||||
 | 
			
		||||
add_library(m17_decoder SHARED ${SRC})
 | 
			
		||||
target_link_libraries(m17_decoder PRIVATE sdrpp_core)
 | 
			
		||||
target_link_libraries(m17_decoder PRIVATE correct_static)
 | 
			
		||||
set_target_properties(m17_decoder PROPERTIES PREFIX "")
 | 
			
		||||
 | 
			
		||||
target_link_directories(sdrpp_core PUBLIC "C:/Users/ryzerth/Documents/Code/codec2/build/src")
 | 
			
		||||
target_link_libraries(m17_decoder PRIVATE libcodec2)
 | 
			
		||||
 | 
			
		||||
# Install directives
 | 
			
		||||
install(TARGETS m17_decoder DESTINATION lib/sdrpp/plugins)
 | 
			
		||||
							
								
								
									
										482
									
								
								m17_decoder/src/m17dsp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										482
									
								
								m17_decoder/src/m17dsp.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,482 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <dsp/block.h>
 | 
			
		||||
#include <volk/volk.h>
 | 
			
		||||
#include <codec2.h>
 | 
			
		||||
#include <dsp/demodulator.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
    #include <correct.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define M17_DEVIATION               2400.0f
 | 
			
		||||
#define M17_BAUDRATE                4800.0f
 | 
			
		||||
#define M17_RRC_ALPHA               0.5f
 | 
			
		||||
#define M17_4FSK_HIGH_CUT           0.52f
 | 
			
		||||
 | 
			
		||||
#define M17_SYNC_SIZE               16
 | 
			
		||||
#define M17_LICH_SIZE               96
 | 
			
		||||
#define M17_PAYLOAD_SIZE            144
 | 
			
		||||
#define M17_ENCODED_PAYLOAD_SIZE    296
 | 
			
		||||
#define M17_RAW_FRAME_SIZE          384
 | 
			
		||||
#define M17_CUT_FRAME_SIZE          368
 | 
			
		||||
 | 
			
		||||
const uint8_t M17_LSF_SYNC[16] = {      0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1 };
 | 
			
		||||
const uint8_t M17_STF_SYNC[16] = {      1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1 };
 | 
			
		||||
const uint8_t M17_PKF_SYNC[16] = {      0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
 | 
			
		||||
 | 
			
		||||
const uint8_t M17_SCRAMBLER[368] = {    1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1,
 | 
			
		||||
                                        1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
 | 
			
		||||
                                        1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
                                        1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0,
 | 
			
		||||
                                        1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0,
 | 
			
		||||
                                        1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0,
 | 
			
		||||
                                        1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,
 | 
			
		||||
                                        1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
                                        0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0,
 | 
			
		||||
                                        0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1,
 | 
			
		||||
                                        1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
                                        1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
 | 
			
		||||
                                        0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1,
 | 
			
		||||
                                        0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
 | 
			
		||||
                                        0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0,
 | 
			
		||||
                                        1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0,
 | 
			
		||||
                                        0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1,
 | 
			
		||||
                                        1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
 | 
			
		||||
                                        1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1,
 | 
			
		||||
                                        1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
 | 
			
		||||
                                        0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0,
 | 
			
		||||
                                        0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1,
 | 
			
		||||
                                        0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1 };
 | 
			
		||||
 | 
			
		||||
const uint16_t M17_INTERLEAVER[368] = { 0, 137, 90, 227, 180, 317, 270, 39, 360, 129, 82, 219, 172, 309, 262, 31,
 | 
			
		||||
                                        352, 121, 74, 211, 164, 301, 254, 23, 344, 113, 66, 203, 156, 293, 246, 15,
 | 
			
		||||
                                        336, 105, 58, 195, 148, 285, 238, 7, 328, 97, 50, 187, 140, 277, 230, 367,
 | 
			
		||||
                                        320, 89, 42, 179, 132, 269, 222, 359, 312, 81, 34, 171, 124, 261, 214, 351,
 | 
			
		||||
                                        304, 73, 26, 163, 116, 253, 206, 343, 296, 65, 18, 155, 108, 245, 198, 335,
 | 
			
		||||
                                        288, 57, 10, 147, 100, 237, 190, 327, 280, 49, 2, 139, 92, 229, 182, 319,
 | 
			
		||||
                                        272, 41, 362, 131, 84, 221, 174, 311, 264, 33, 354, 123, 76, 213, 166, 303,
 | 
			
		||||
                                        256, 25, 346, 115, 68, 205, 158, 295, 248, 17, 338, 107, 60, 197, 150, 287,
 | 
			
		||||
                                        240, 9, 330, 99, 52, 189, 142, 279, 232, 1, 322, 91, 44, 181, 134, 271,
 | 
			
		||||
                                        224, 361, 314, 83, 36, 173, 126, 263, 216, 353, 306, 75, 28, 165, 118, 255,
 | 
			
		||||
                                        208, 345, 298, 67, 20, 157, 110, 247, 200, 337, 290, 59, 12, 149, 102, 239,
 | 
			
		||||
                                        192, 329, 282, 51, 4, 141, 94, 231, 184, 321, 274, 43, 364, 133, 86, 223,
 | 
			
		||||
                                        176, 313, 266, 35, 356, 125, 78, 215, 168, 305, 258, 27, 348, 117, 70, 207,
 | 
			
		||||
                                        160, 297, 250, 19, 340, 109, 62, 199, 152, 289, 242, 11, 332, 101, 54, 191,
 | 
			
		||||
                                        144, 281, 234, 3, 324, 93, 46, 183, 136, 273, 226, 363, 316, 85, 38, 175,
 | 
			
		||||
                                        128, 265, 218, 355, 308, 77, 30, 167, 120, 257, 210, 347, 300, 69, 22, 159,
 | 
			
		||||
                                        112, 249, 202, 339, 292, 61, 14, 151, 104, 241, 194, 331, 284, 53, 6, 143,
 | 
			
		||||
                                        96, 233, 186, 323, 276, 45, 366, 135, 88, 225, 178, 315, 268, 37, 358, 127,
 | 
			
		||||
                                        80, 217, 170, 307, 260, 29, 350, 119, 72, 209, 162, 299, 252, 21, 342, 111,
 | 
			
		||||
                                        64, 201, 154, 291, 244, 13, 334, 103, 56, 193, 146, 283, 236, 5, 326, 95,
 | 
			
		||||
                                        48, 185, 138, 275, 228, 365, 318, 87, 40, 177, 130, 267, 220, 357, 310, 79,
 | 
			
		||||
                                        32, 169, 122, 259, 212, 349, 302, 71, 24, 161, 114, 251, 204, 341, 294, 63,
 | 
			
		||||
                                        16, 153, 106, 243, 196, 333, 286, 55, 8, 145, 98, 235, 188, 325, 278, 47 };
 | 
			
		||||
 | 
			
		||||
const uint8_t M17_PUNCTURING_P1[61] = { 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
                                        1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
                                        1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1,
 | 
			
		||||
                                        1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1 };
 | 
			
		||||
 | 
			
		||||
const uint8_t M17_PUNCTURING_P2[12] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 };
 | 
			
		||||
 | 
			
		||||
static const correct_convolutional_polynomial_t correct_conv_m17_polynomial[] = {0b11001, 0b10111};
 | 
			
		||||
 | 
			
		||||
namespace dsp {
 | 
			
		||||
    class M17Slice4FSK : public generic_block<M17Slice4FSK> {
 | 
			
		||||
    public:
 | 
			
		||||
        M17Slice4FSK() {}
 | 
			
		||||
 | 
			
		||||
        M17Slice4FSK(stream<float>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        void init(stream<float>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<M17Slice4FSK>::registerInput(_in);
 | 
			
		||||
            generic_block<M17Slice4FSK>::registerOutput(&out);
 | 
			
		||||
            generic_block<M17Slice4FSK>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<float>* in) {
 | 
			
		||||
            assert(generic_block<M17Slice4FSK>::_block_init);
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<M17Slice4FSK>::ctrlMtx);
 | 
			
		||||
            generic_block<M17Slice4FSK>::tempStop();
 | 
			
		||||
            generic_block<M17Slice4FSK>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<M17Slice4FSK>::registerInput(_in);
 | 
			
		||||
            generic_block<M17Slice4FSK>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            float val;
 | 
			
		||||
            for (int i = 0; i < count; i++) {
 | 
			
		||||
                val = _in->readBuf[i];
 | 
			
		||||
                out.writeBuf[i*2] = (val < 0.0f);
 | 
			
		||||
                out.writeBuf[(i*2)+1] = (fabsf(val) > M17_4FSK_HIGH_CUT);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            if (!out.swap(count * 2)) { return -1; }
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<uint8_t> out;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        stream<float>* _in;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class M17FrameDemux : public generic_block<M17FrameDemux> {
 | 
			
		||||
    public:
 | 
			
		||||
        M17FrameDemux() {}
 | 
			
		||||
 | 
			
		||||
        M17FrameDemux(stream<uint8_t>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        ~M17FrameDemux() {
 | 
			
		||||
            if (!generic_block<M17FrameDemux>::_block_init) { return; }
 | 
			
		||||
            generic_block<M17FrameDemux>::stop();
 | 
			
		||||
            delete[] delay;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<uint8_t>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
 | 
			
		||||
            delay = new uint8_t[STREAM_BUFFER_SIZE];
 | 
			
		||||
 | 
			
		||||
            generic_block<M17FrameDemux>::registerInput(_in);
 | 
			
		||||
            generic_block<M17FrameDemux>::registerOutput(&linkSetupOut);
 | 
			
		||||
            generic_block<M17FrameDemux>::registerOutput(&lichOut);
 | 
			
		||||
            generic_block<M17FrameDemux>::registerOutput(&streamOut);
 | 
			
		||||
            generic_block<M17FrameDemux>::registerOutput(&packetOut);
 | 
			
		||||
            generic_block<M17FrameDemux>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<uint8_t>* in) {
 | 
			
		||||
            assert(generic_block<M17FrameDemux>::_block_init);
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<M17FrameDemux>::ctrlMtx);
 | 
			
		||||
            generic_block<M17FrameDemux>::tempStop();
 | 
			
		||||
            generic_block<M17FrameDemux>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<M17FrameDemux>::registerInput(_in);
 | 
			
		||||
            generic_block<M17FrameDemux>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            memcpy(&delay[M17_SYNC_SIZE], _in->readBuf, count);
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < count;) {
 | 
			
		||||
                if (detect) {
 | 
			
		||||
                    if (outCount < M17_SYNC_SIZE) { outCount++; i++; }
 | 
			
		||||
                    else {
 | 
			
		||||
                        int id = M17_INTERLEAVER[outCount - M17_SYNC_SIZE];
 | 
			
		||||
 | 
			
		||||
                        if (type == 0) {
 | 
			
		||||
                            linkSetupOut.writeBuf[id] = delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (type == 1 && id < M17_LICH_SIZE) {
 | 
			
		||||
                            lichOut.writeBuf[id/8] |= (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]) << (7 - (id%8));
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (type == 1) {
 | 
			
		||||
                            streamOut.writeBuf[id - M17_LICH_SIZE] = (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]);
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (type == 2) {
 | 
			
		||||
                            packetOut.writeBuf[id] = (delay[i++] ^ M17_SCRAMBLER[outCount - M17_SYNC_SIZE]);
 | 
			
		||||
                        }
 | 
			
		||||
    
 | 
			
		||||
                        outCount++;
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    if (outCount >= M17_RAW_FRAME_SIZE) {
 | 
			
		||||
                        detect = false;
 | 
			
		||||
                        if (type == 0) {
 | 
			
		||||
                            if (!linkSetupOut.swap(M17_CUT_FRAME_SIZE)) { return -1; }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (type == 1) {
 | 
			
		||||
                            if (!lichOut.swap(12)) {return -1; }
 | 
			
		||||
                            if (!streamOut.swap(M17_CUT_FRAME_SIZE)) {return -1; }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (type == 2) {
 | 
			
		||||
                            if (!packetOut.swap(M17_CUT_FRAME_SIZE)) { return -1; }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Check for link setup syncword
 | 
			
		||||
                if (!memcmp(&delay[i], M17_LSF_SYNC, M17_SYNC_SIZE)) {
 | 
			
		||||
                    detect = true;
 | 
			
		||||
                    outCount = 0;
 | 
			
		||||
                    type = 0;
 | 
			
		||||
                    spdlog::warn("Found sync frame");
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Check for stream syncword
 | 
			
		||||
                if (!memcmp(&delay[i], M17_STF_SYNC, M17_SYNC_SIZE)) {
 | 
			
		||||
                    detect = true;
 | 
			
		||||
                    outCount = 0;
 | 
			
		||||
                    type = 1;
 | 
			
		||||
                    memset(lichOut.writeBuf, 0, 12);
 | 
			
		||||
                    spdlog::warn("Found stream frame");
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Check for packet syncword
 | 
			
		||||
                if (!memcmp(&delay[i], M17_PKF_SYNC, M17_SYNC_SIZE)) {
 | 
			
		||||
                    detect = true;
 | 
			
		||||
                    outCount = 0;
 | 
			
		||||
                    type = 2;
 | 
			
		||||
                    spdlog::warn("Found packet frame");
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                i++;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            memmove(delay, &delay[count], 16);
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<uint8_t> linkSetupOut;
 | 
			
		||||
        stream<uint8_t> lichOut;
 | 
			
		||||
        stream<uint8_t> streamOut;
 | 
			
		||||
        stream<uint8_t> packetOut;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        stream<uint8_t>* _in;
 | 
			
		||||
 | 
			
		||||
        uint8_t* delay;
 | 
			
		||||
 | 
			
		||||
        bool detect = false;
 | 
			
		||||
        int type;
 | 
			
		||||
 | 
			
		||||
        int outCount = 0;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class M17PayloadFEC : public generic_block<M17PayloadFEC> {
 | 
			
		||||
    public:
 | 
			
		||||
        M17PayloadFEC() {}
 | 
			
		||||
 | 
			
		||||
        M17PayloadFEC(stream<uint8_t>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        ~M17PayloadFEC() {
 | 
			
		||||
            if (!generic_block<M17PayloadFEC>::_block_init) { return; }
 | 
			
		||||
            generic_block<M17PayloadFEC>::stop();
 | 
			
		||||
            correct_convolutional_destroy(conv);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<uint8_t>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
            
 | 
			
		||||
            conv = correct_convolutional_create(2, 5, correct_conv_m17_polynomial);
 | 
			
		||||
 | 
			
		||||
            generic_block<M17PayloadFEC>::registerInput(_in);
 | 
			
		||||
            generic_block<M17PayloadFEC>::registerOutput(&out);
 | 
			
		||||
            generic_block<M17PayloadFEC>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<uint8_t>* in) {
 | 
			
		||||
            assert(generic_block<M17PayloadFEC>::_block_init);
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<M17PayloadFEC>::ctrlMtx);
 | 
			
		||||
            generic_block<M17PayloadFEC>::tempStop();
 | 
			
		||||
            generic_block<M17PayloadFEC>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<M17PayloadFEC>::registerInput(_in);
 | 
			
		||||
            generic_block<M17PayloadFEC>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            // Depuncture the data
 | 
			
		||||
            int inOffset = 0;
 | 
			
		||||
            for (int i = 0; i < M17_ENCODED_PAYLOAD_SIZE; i++) {
 | 
			
		||||
                if (!M17_PUNCTURING_P2[i % 12]) {
 | 
			
		||||
                    depunctured[i] = 0;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                depunctured[i] = _in->readBuf[inOffset++];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Pack into bytes
 | 
			
		||||
            for (int i = 0; i < M17_ENCODED_PAYLOAD_SIZE; i++) {
 | 
			
		||||
                if (!(i%8)) { packed[i/8] = 0; }
 | 
			
		||||
                packed[i/8] |= depunctured[i] << (7 - (i%8));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Run through convolutional decoder
 | 
			
		||||
            correct_convolutional_decode(conv, packed, M17_ENCODED_PAYLOAD_SIZE, out.writeBuf);
 | 
			
		||||
            
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            if (!out.swap(M17_PAYLOAD_SIZE / 8)) { return -1; }
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<uint8_t> out;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        stream<uint8_t>* _in;
 | 
			
		||||
 | 
			
		||||
        uint8_t depunctured[296];
 | 
			
		||||
        uint8_t packed[37];
 | 
			
		||||
 | 
			
		||||
        correct_convolutional* conv;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class M17Codec2Decode : public generic_block<M17Codec2Decode> {
 | 
			
		||||
    public:
 | 
			
		||||
        M17Codec2Decode() {}
 | 
			
		||||
 | 
			
		||||
        M17Codec2Decode(stream<uint8_t>* in) { init(in); }
 | 
			
		||||
 | 
			
		||||
        ~M17Codec2Decode() {
 | 
			
		||||
            if (!generic_block<M17Codec2Decode>::_block_init) { return; }
 | 
			
		||||
            generic_block<M17Codec2Decode>::stop();
 | 
			
		||||
            codec2_destroy(codec);
 | 
			
		||||
            delete[] int16Audio;
 | 
			
		||||
            delete[] floatAudio;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<uint8_t>* in) {
 | 
			
		||||
            _in = in;
 | 
			
		||||
 | 
			
		||||
            codec = codec2_create(CODEC2_MODE_3200);
 | 
			
		||||
            sampsPerC2Frame = codec2_samples_per_frame(codec);
 | 
			
		||||
            sampsPerC2FrameDouble = sampsPerC2Frame * 2;
 | 
			
		||||
            int16Audio = new int16_t[sampsPerC2FrameDouble];
 | 
			
		||||
            floatAudio = new float[sampsPerC2FrameDouble];
 | 
			
		||||
 | 
			
		||||
            generic_block<M17Codec2Decode>::registerInput(_in);
 | 
			
		||||
            generic_block<M17Codec2Decode>::registerOutput(&out);
 | 
			
		||||
            generic_block<M17Codec2Decode>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<uint8_t>* in) {
 | 
			
		||||
            assert(generic_block<M17Codec2Decode>::_block_init);
 | 
			
		||||
            std::lock_guard<std::mutex> lck(generic_block<M17Codec2Decode>::ctrlMtx);
 | 
			
		||||
            generic_block<M17Codec2Decode>::tempStop();
 | 
			
		||||
            generic_block<M17Codec2Decode>::unregisterInput(_in);
 | 
			
		||||
            _in = in;
 | 
			
		||||
            generic_block<M17Codec2Decode>::registerInput(_in);
 | 
			
		||||
            generic_block<M17Codec2Decode>::tempStart();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int run() {
 | 
			
		||||
            int count = _in->read();
 | 
			
		||||
            if (count < 0) { return -1; }
 | 
			
		||||
 | 
			
		||||
            // Decode both parts using codec
 | 
			
		||||
            codec2_decode(codec, int16Audio, &_in->readBuf[2]);
 | 
			
		||||
            codec2_decode(codec, &int16Audio[sampsPerC2Frame], &_in->readBuf[2+8]);
 | 
			
		||||
 | 
			
		||||
            // Convert to float
 | 
			
		||||
            volk_16i_s32f_convert_32f(floatAudio, int16Audio, 32768.0f, sampsPerC2FrameDouble);
 | 
			
		||||
 | 
			
		||||
            // Interleave into stereo samples
 | 
			
		||||
            volk_32f_x2_interleave_32fc((lv_32fc_t*)out.writeBuf, floatAudio, floatAudio, sampsPerC2FrameDouble);
 | 
			
		||||
 | 
			
		||||
            _in->flush();
 | 
			
		||||
 | 
			
		||||
            if (!out.swap(sampsPerC2FrameDouble)) { return -1; }
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<stereo_t> out;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        stream<uint8_t>* _in;
 | 
			
		||||
 | 
			
		||||
        int16_t* int16Audio;
 | 
			
		||||
        float* floatAudio;
 | 
			
		||||
 | 
			
		||||
        CODEC2* codec;
 | 
			
		||||
        int sampsPerC2Frame = 0;
 | 
			
		||||
        int sampsPerC2FrameDouble = 0;
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class M17Decoder : public generic_hier_block<M17Decoder> {
 | 
			
		||||
    public:
 | 
			
		||||
        M17Decoder() {}
 | 
			
		||||
 | 
			
		||||
        M17Decoder(stream<complex_t>* input, float sampleRate) {
 | 
			
		||||
            init(input, sampleRate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(stream<complex_t>* input, float sampleRate) {
 | 
			
		||||
            _sampleRate = sampleRate;
 | 
			
		||||
 | 
			
		||||
            demod.init(input, _sampleRate, M17_DEVIATION);
 | 
			
		||||
            rrc.init(31, _sampleRate, M17_BAUDRATE, M17_RRC_ALPHA);
 | 
			
		||||
            fir.init(&demod.out, &rrc);
 | 
			
		||||
            recov.init(&fir.out, _sampleRate / M17_BAUDRATE, 1e-6f, 0.01f, 0.01f);
 | 
			
		||||
            doubler.init(&recov.out);
 | 
			
		||||
            slice.init(&doubler.outA);
 | 
			
		||||
            demux.init(&slice.out);
 | 
			
		||||
            payloadFEC.init(&demux.streamOut);
 | 
			
		||||
            decodeAudio.init(&payloadFEC.out);
 | 
			
		||||
 | 
			
		||||
            ns0.init(&demux.linkSetupOut);
 | 
			
		||||
            ns1.init(&demux.lichOut);
 | 
			
		||||
            ns2.init(&demux.packetOut);
 | 
			
		||||
 | 
			
		||||
            diagOut = &doubler.outB;
 | 
			
		||||
            out = &decodeAudio.out;
 | 
			
		||||
            
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&demod);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&fir);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&recov);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&doubler);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&slice);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&demux);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&payloadFEC);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&decodeAudio);
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&ns0);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&ns1);
 | 
			
		||||
            generic_hier_block<M17Decoder>::registerBlock(&ns2);
 | 
			
		||||
 | 
			
		||||
            generic_hier_block<M17Decoder>::_block_init = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void setInput(stream<complex_t>* input) {
 | 
			
		||||
            assert(generic_hier_block<M17Decoder>::_block_init);
 | 
			
		||||
            demod.setInput(input);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stream<float>* diagOut = NULL;
 | 
			
		||||
        stream<stereo_t>* out = NULL;
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        FloatFMDemod demod;
 | 
			
		||||
        RRCTaps rrc;
 | 
			
		||||
        FIR<float> fir;
 | 
			
		||||
        MMClockRecovery<float> recov;
 | 
			
		||||
        StreamDoubler<float> doubler;
 | 
			
		||||
        M17Slice4FSK slice;
 | 
			
		||||
        M17FrameDemux demux;
 | 
			
		||||
        M17PayloadFEC payloadFEC;
 | 
			
		||||
        M17Codec2Decode decodeAudio;
 | 
			
		||||
 | 
			
		||||
        NullSink<uint8_t> ns0;
 | 
			
		||||
        NullSink<uint8_t> ns1;
 | 
			
		||||
        NullSink<uint8_t> ns2;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        float _sampleRate;        
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										196
									
								
								m17_decoder/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								m17_decoder/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,196 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <config.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <options.h>
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#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/symbol_diagram.h>
 | 
			
		||||
#include <m17dsp.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b)    ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO {
 | 
			
		||||
    /* Name:            */ "m17_decoder",
 | 
			
		||||
    /* Description:     */ "M17 Digital Voice Decoder for SDR++",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ -1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConfigManager config;
 | 
			
		||||
 | 
			
		||||
#define INPUT_SAMPLE_RATE 14400
 | 
			
		||||
 | 
			
		||||
class M17DecoderModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    M17DecoderModule(std::string name) : diag(1.0f, 480) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        // Load config
 | 
			
		||||
 | 
			
		||||
        vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 9600, INPUT_SAMPLE_RATE, 9600, 9600, true);
 | 
			
		||||
        vfo->setSnapInterval(500);
 | 
			
		||||
 | 
			
		||||
        // Intialize DSP here
 | 
			
		||||
        decoder.init(vfo->output, INPUT_SAMPLE_RATE);
 | 
			
		||||
 | 
			
		||||
        resampWin.init(4000, 4000, audioSampRate);
 | 
			
		||||
        resamp.init(decoder.out, &resampWin, 8000, audioSampRate);
 | 
			
		||||
        resampWin.setSampleRate(8000 * resamp.getInterpolation());
 | 
			
		||||
        resamp.updateWindow(&resampWin);
 | 
			
		||||
 | 
			
		||||
        reshape.init(decoder.diagOut, 480, 0);
 | 
			
		||||
        diagHandler.init(&reshape.out, _diagHandler, this);
 | 
			
		||||
 | 
			
		||||
        // Start DSO Here
 | 
			
		||||
        decoder.start();
 | 
			
		||||
        resamp.start();
 | 
			
		||||
        reshape.start();
 | 
			
		||||
        diagHandler.start();
 | 
			
		||||
 | 
			
		||||
        // Setup audio stream
 | 
			
		||||
        srChangeHandler.ctx = this;
 | 
			
		||||
        srChangeHandler.handler = sampleRateChangeHandler;
 | 
			
		||||
        stream.init(&resamp.out, &srChangeHandler, audioSampRate);
 | 
			
		||||
        sigpath::sinkManager.registerStream(name, &stream);
 | 
			
		||||
 | 
			
		||||
        stream.start();
 | 
			
		||||
               
 | 
			
		||||
        gui::menu.registerEntry(name, menuHandler, this, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~M17DecoderModule() {
 | 
			
		||||
        // Stop DSP Here
 | 
			
		||||
        decoder.stop();
 | 
			
		||||
        resamp.stop();
 | 
			
		||||
        reshape.stop();
 | 
			
		||||
        diagHandler.stop();
 | 
			
		||||
 | 
			
		||||
        stream.stop();
 | 
			
		||||
 | 
			
		||||
        sigpath::vfoManager.deleteVFO(vfo);
 | 
			
		||||
        gui::menu.removeEntry(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void postInit() {}
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
        double bw = gui::waterfall.getBandwidth();
 | 
			
		||||
        vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw/2.0, bw/2.0), 9600, INPUT_SAMPLE_RATE, 9600, 9600, true);
 | 
			
		||||
        vfo->setSnapInterval(500);
 | 
			
		||||
 | 
			
		||||
        // Set Input of demod here
 | 
			
		||||
        decoder.setInput(vfo->output);
 | 
			
		||||
 | 
			
		||||
        // Start DSP here
 | 
			
		||||
        decoder.start();
 | 
			
		||||
        resamp.start();
 | 
			
		||||
        reshape.start();
 | 
			
		||||
        diagHandler.start();
 | 
			
		||||
 | 
			
		||||
        enabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void disable() {
 | 
			
		||||
        // Stop DSP here
 | 
			
		||||
        decoder.stop();
 | 
			
		||||
        resamp.stop();
 | 
			
		||||
        reshape.stop();
 | 
			
		||||
        diagHandler.stop();
 | 
			
		||||
 | 
			
		||||
        sigpath::vfoManager.deleteVFO(vfo);
 | 
			
		||||
        enabled = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isEnabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static void menuHandler(void* ctx) {
 | 
			
		||||
        M17DecoderModule* _this = (M17DecoderModule*)ctx;
 | 
			
		||||
 | 
			
		||||
        float menuWidth = ImGui::GetContentRegionAvailWidth();
 | 
			
		||||
 | 
			
		||||
        if (!_this->enabled) { style::beginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::SetNextItemWidth(menuWidth);
 | 
			
		||||
        _this->diag.draw();
 | 
			
		||||
 | 
			
		||||
        if (!_this->enabled) { style::endDisabled(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void _diagHandler(float* data, int count, void* ctx) {
 | 
			
		||||
        M17DecoderModule* _this = (M17DecoderModule*)ctx;
 | 
			
		||||
        float* buf = _this->diag.acquireBuffer();
 | 
			
		||||
        memcpy(buf, data, count * sizeof(float));
 | 
			
		||||
        _this->diag.releaseBuffer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void sampleRateChangeHandler(float sampleRate, void* ctx) {
 | 
			
		||||
        M17DecoderModule* _this = (M17DecoderModule*)ctx;
 | 
			
		||||
        // TODO: If too slow, change all demods here and not when setting
 | 
			
		||||
        _this->audioSampRate = sampleRate;
 | 
			
		||||
        _this->resampWin.setCutoff(std::min<float>(sampleRate/2, 4000));
 | 
			
		||||
        _this->resamp.tempStop();
 | 
			
		||||
        _this->resamp.setOutSampleRate(sampleRate);
 | 
			
		||||
        _this->resampWin.setSampleRate(8000 * _this->resamp.getInterpolation());
 | 
			
		||||
        _this->resamp.updateWindow(&_this->resampWin);
 | 
			
		||||
        _this->resamp.tempStart();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string name;
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
 | 
			
		||||
    // DSP Chain
 | 
			
		||||
    VFOManager::VFO* vfo;
 | 
			
		||||
 | 
			
		||||
    dsp::M17Decoder decoder;
 | 
			
		||||
    
 | 
			
		||||
    dsp::Reshaper<float> reshape;
 | 
			
		||||
    dsp::HandlerSink<float> diagHandler;
 | 
			
		||||
 | 
			
		||||
    dsp::filter_window::BlackmanWindow resampWin;
 | 
			
		||||
    dsp::PolyphaseResampler<dsp::stereo_t> resamp;
 | 
			
		||||
 | 
			
		||||
    ImGui::SymbolDiagram diag;
 | 
			
		||||
 | 
			
		||||
    double audioSampRate = 48000;
 | 
			
		||||
    EventHandler<float> srChangeHandler;
 | 
			
		||||
    SinkManager::Stream stream;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _INIT_() {
 | 
			
		||||
    // Create default recording directory
 | 
			
		||||
    json def = json({});
 | 
			
		||||
    config.setPath(options::opts.root + "/m17_decoder_config.json");
 | 
			
		||||
    config.load(def);
 | 
			
		||||
    config.enableAutoSave();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
 | 
			
		||||
    return new M17DecoderModule(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
 | 
			
		||||
    delete (M17DecoderModule*)instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _END_() {
 | 
			
		||||
    config.disableAutoSave();
 | 
			
		||||
    config.save();
 | 
			
		||||
}
 | 
			
		||||
@@ -295,6 +295,7 @@ Modules in beta are still included in releases for the most part but not enabled
 | 
			
		||||
| Name                | Stage      | Dependencies | Option                        | Built by default| Built in Release | Enabled in SDR++ by default |
 | 
			
		||||
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
 | 
			
		||||
| falcon9_decoder     | Unfinished | ffplay       | OPT_BUILD_FALCON9_DECODER     | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
| m17_decoder         | Unfinished | -            | OPT_BUILD_M17_DECODER         | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
| meteor_demodulator  | Working    | -            | OPT_BUILD_METEOR_DEMODULATOR  | ✅              | ✅              | ⛔                         |
 | 
			
		||||
| radio               | Working    | -            | OPT_BUILD_RADIO               | ✅              | ✅              | ✅                         |
 | 
			
		||||
| weather_sat_decoder | Unfinished | -            | OPT_BUILD_WEATHER_SAT_DECODER | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user