diff --git a/CMakeLists.txt b/CMakeLists.txt index cd627ae..1a2cb65 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,10 +12,11 @@ option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Depedencies: libbl option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Depedencies: libsdrplay)" OFF) option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Depedencies: libiio, libad9361)" ON) option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" OFF) -option(OPT_BUILD_RTL_SDR_SOURCE "Build HackRF Source Module (Depedencies: libhackrf)" ON) +option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Depedencies: librtlsdr)" 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) +option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF) +option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON) +option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" ON) # Core of SDR++ add_subdirectory("core") @@ -77,6 +78,10 @@ if (OPT_BUILD_METEOR_DEMODULATOR) add_subdirectory("meteor_demodulator") endif (OPT_BUILD_METEOR_DEMODULATOR) +if (OPT_BUILD_WEATHER_SAT_DECODER) +add_subdirectory("weather_sat_decoder") +endif (OPT_BUILD_WEATHER_SAT_DECODER) + if (MSVC) set(CMAKE_CXX_FLAGS "-O2 /std:c++17 /EHsc") else() diff --git a/airspy_source/src/main.cpp b/airspy_source/src/main.cpp index 42324db..4f66b36 100644 --- a/airspy_source/src/main.cpp +++ b/airspy_source/src/main.cpp @@ -27,6 +27,8 @@ public: AirspySourceModule(std::string name) { this->name = name; + airspy_init(); + sampleRate = 10000000.0; handler.ctx = this; @@ -54,7 +56,7 @@ public: } ~AirspySourceModule() { - + airspy_exit(); } void enable() { diff --git a/core/src/dsp/clock_recovery.h b/core/src/dsp/clock_recovery.h index ef87a34..5000fd9 100644 --- a/core/src/dsp/clock_recovery.h +++ b/core/src/dsp/clock_recovery.h @@ -84,6 +84,8 @@ namespace dsp { omegaMax = _omega + (_omega * _omegaRelLimit); _dynOmega = _omega; + memset(delay, 0, 1024 * sizeof(T)); + generic_block>::registerInput(_in); generic_block>::registerOutput(&out); } diff --git a/core/src/dsp/deframing.h b/core/src/dsp/deframing.h index 642d77a..1daa72d 100644 --- a/core/src/dsp/deframing.h +++ b/core/src/dsp/deframing.h @@ -35,7 +35,6 @@ namespace dsp { count = _in->read(); if (count < 0) { return -1; } - // Copy data into work buffer memcpy(bufferStart, _in->readBuf, count - 1); @@ -61,6 +60,7 @@ namespace dsp { // Else, check for a header else if (memcmp(buffer + i, _syncword, _syncLen) == 0) { bitsRead = 0; + //printf("Frame found!\n"); badFrameCount = 0; continue; } @@ -70,6 +70,7 @@ namespace dsp { // try to save if (badFrameCount < 5) { badFrameCount++; + //printf("Frame found!\n"); bitsRead = 0; continue; } @@ -111,4 +112,252 @@ namespace dsp { stream* _in; }; + + inline int MachesterHammingDistance(float* data, uint8_t* syncBits, int n) { + int dist = 0; + for (int i = 0; i < n; i++) { + if ((data[(2*i) + 1] > data[2*i]) != syncBits[i]) { dist++; } + } + return dist; + } + + inline int HammingDistance(uint8_t* data, uint8_t* syncBits, int n) { + int dist = 0; + for (int i = 0; i < n; i++) { + if (data[i] != syncBits[i]) { dist++; } + } + return dist; + } + + class ManchesterDeframer : public generic_block { + public: + ManchesterDeframer() {} + + ManchesterDeframer(stream* in, int frameLen, uint8_t* syncWord, int syncLen) { init(in, frameLen, syncWord, syncLen); } + + void init(stream* in, int frameLen, uint8_t* syncWord, int syncLen) { + _in = in; + _frameLen = frameLen; + _syncword = new uint8_t[syncLen]; + _syncLen = syncLen; + memcpy(_syncword, syncWord, syncLen); + + buffer = new float[STREAM_BUFFER_SIZE + (syncLen * 2)]; + memset(buffer, 0, syncLen * 2 * sizeof(float)); + bufferStart = &buffer[syncLen * 2]; + + generic_block::registerInput(_in); + generic_block::registerOutput(&out); + } + + int run() { + count = _in->read(); + if (count < 0) { return -1; } + + int readable; + + // Copy data into work buffer + memcpy(bufferStart, _in->readBuf, (count - 1) * sizeof(float)); + + // Iterate through all symbols + for (int i = 0; i < count;) { + + // If already in the process of reading bits + if (bitsRead >= 0) { + readable = std::min(count - i, _frameLen - bitsRead); + memcpy(&out.writeBuf[bitsRead], &buffer[i], readable * sizeof(float)); + bitsRead += readable; + i += readable; + if (bitsRead >= _frameLen) { + out.swap(_frameLen); + bitsRead = -1; + } + continue; + } + + // Else, check for a header + if (MachesterHammingDistance(&buffer[i], _syncword, _syncLen) <= 2) { + bitsRead = 0; + continue; + } + + i++; + + } + + // Keep last _syncLen symbols + memcpy(buffer, &_in->readBuf[count - (_syncLen * 2)], _syncLen * 2 * sizeof(float)); + + _in->flush(); + return count; + } + + stream out; + + private: + float* buffer; + float* bufferStart; + uint8_t* _syncword; + int count; + int _frameLen; + int _syncLen; + int bitsRead = -1; + + stream* _in; + + }; + + class SymbolDeframer : public generic_block { + public: + SymbolDeframer() {} + + SymbolDeframer(stream* in, int frameLen, uint8_t* syncWord, int syncLen) { init(in, frameLen, syncWord, syncLen); } + + void init(stream* in, int frameLen, uint8_t* syncWord, int syncLen) { + _in = in; + _frameLen = frameLen; + _syncword = new uint8_t[syncLen]; + _syncLen = syncLen; + memcpy(_syncword, syncWord, syncLen); + + buffer = new uint8_t[STREAM_BUFFER_SIZE + syncLen]; + memset(buffer, 0, syncLen); + bufferStart = &buffer[syncLen]; + + generic_block::registerInput(_in); + generic_block::registerOutput(&out); + } + + int run() { + count = _in->read(); + if (count < 0) { return -1; } + + int readable; + + // Copy data into work buffer + memcpy(bufferStart, _in->readBuf, count - 1); + + // Iterate through all symbols + for (int i = 0; i < count;) { + + // If already in the process of reading bits + if (bitsRead >= 0) { + readable = std::min(count - i, _frameLen - bitsRead); + memcpy(&out.writeBuf[bitsRead], &buffer[i], readable); + bitsRead += readable; + i += readable; + if (bitsRead >= _frameLen) { + out.swap(_frameLen); + bitsRead = -1; + } + continue; + } + + // Else, check for a header + if (HammingDistance(&buffer[i], _syncword, _syncLen) <= 2) { + bitsRead = 0; + continue; + } + + i++; + + } + + // Keep last _syncLen symbols + memcpy(buffer, &_in->readBuf[count - _syncLen], _syncLen); + + _in->flush(); + return count; + } + + stream out; + + private: + uint8_t* buffer; + uint8_t* bufferStart; + uint8_t* _syncword; + int count; + int _frameLen; + int _syncLen; + int bitsRead = -1; + + stream* _in; + + }; + + class ManchesterDecoder : public generic_block { + public: + ManchesterDecoder() {} + + ManchesterDecoder(stream* in, bool inverted) { init(in, inverted); } + + void init(stream* in, bool inverted) { + _in = in; + _inverted = inverted; + generic_block::registerInput(_in); + generic_block::registerOutput(&out); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + if (_inverted) { + for (int i = 0; i < count; i += 2) { + out.writeBuf[i/2] = (_in->readBuf[i + 1] < _in->readBuf[i]); + } + } + else { + for (int i = 0; i < count; i += 2) { + out.writeBuf[i/2] = (_in->readBuf[i + 1] > _in->readBuf[i]); + } + } + + _in->flush(); + out.swap(count / 2); + return count; + } + + stream out; + + private: + stream* _in; + bool _inverted; + + }; + + class BitPacker : public generic_block { + public: + BitPacker() {} + + BitPacker(stream* in) { init(in); } + + void init(stream* in) { + _in = in; + + generic_block::registerInput(_in); + generic_block::registerOutput(&out); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + for (int i = 0; i < count; i++) { + if ((i % 8) == 0) { out.writeBuf[i / 8] = 0; } + out.writeBuf[i / 8] |= (_in->readBuf[i] & 1) << (7 - (i % 8)); + } + + _in->flush(); + out.swap((count / 8) + (((count % 8) == 0) ? 0 : 1)); + return count; + } + + stream out; + + private: + + stream* _in; + + }; } \ No newline at end of file diff --git a/core/src/dsp/demodulator.h b/core/src/dsp/demodulator.h index 59f8bbb..685531d 100644 --- a/core/src/dsp/demodulator.h +++ b/core/src/dsp/demodulator.h @@ -500,7 +500,7 @@ namespace dsp { public: MSKDemod() {} MSKDemod(stream* 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); + init(input, sampleRate, deviation, baudRate, omegaGain, muGain, omegaRelLimit); } void init(stream* input, float sampleRate, float deviation, float baudRate, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) { @@ -567,11 +567,11 @@ namespace dsp { class PSKDemod : public generic_hier_block> { public: PSKDemod() {} - PSKDemod(stream* 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) { + PSKDemod(stream* input, float sampleRate, float baudRate, int RRCTapCount = 31, 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, baudRate, RRCTapCount, RRCAlpha, agcRate, costasLoopBw, omegaGain, muGain, omegaRelLimit); } - void init(stream* 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) { + void init(stream* input, float sampleRate, float baudRate, int RRCTapCount = 31, 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; @@ -680,4 +680,89 @@ namespace dsp { float _muGain; float _omegaRelLimit; }; + + class PMDemod : public generic_hier_block { + public: + PMDemod() {} + PMDemod(stream* input, float sampleRate, float baudRate, float agcRate = 0.02e-3f, float pllLoopBandwidth = (0.06f*0.06f) / 4.0f, int rrcTapCount = 31, float rrcAlpha = 0.6f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) { + init(input, sampleRate, baudRate, agcRate, pllLoopBandwidth, rrcTapCount, rrcAlpha, omegaGain, muGain, omegaRelLimit); + } + + void init(stream* input, float sampleRate, float baudRate, float agcRate = 0.02e-3f, float pllLoopBandwidth = (0.06f*0.06f) / 4.0f, int rrcTapCount = 31, float rrcAlpha = 0.6f, float omegaGain = (0.01*0.01) / 4, float muGain = 0.01f, float omegaRelLimit = 0.005f) { + _sampleRate = sampleRate; + _baudRate = baudRate; + _agcRate = agcRate; + _pllLoopBandwidth = pllLoopBandwidth; + _rrcTapCount = rrcTapCount; + _rrcAlpha = rrcAlpha; + _omegaGain = omegaGain; + _muGain = muGain; + _omegaRelLimit = omegaRelLimit; + + agc.init(input, 1.0f, 65535, _agcRate); + pll.init(&agc.out, _pllLoopBandwidth); + rrcwin.init(_rrcTapCount, _sampleRate, _baudRate, _rrcAlpha); + rrc.init(&pll.out, &rrcwin); + recov.init(&rrc.out, _sampleRate / _baudRate, _omegaGain, _muGain, _omegaRelLimit); + + out = &recov.out; + + generic_hier_block::registerBlock(&agc); + generic_hier_block::registerBlock(&pll); + generic_hier_block::registerBlock(&rrc); + generic_hier_block::registerBlock(&recov); + } + + void setInput(stream* input) { + agc.setInput(input); + } + + void setAgcRate(float agcRate) { + _agcRate = agcRate; + agc.setRate(_agcRate); + } + + void setPllLoopBandwidth(float pllLoopBandwidth) { + _pllLoopBandwidth = pllLoopBandwidth; + pll.setLoopBandwidth(_pllLoopBandwidth); + } + + void setRRCParams(int rrcTapCount, float rrcAlpha) { + _rrcTapCount = rrcTapCount; + _rrcAlpha = rrcAlpha; + rrcwin.setTapCount(_rrcTapCount); + rrcwin.setAlpha(_rrcAlpha); + rrc.updateWindow(&rrcwin); + } + + void setMMGains(float omegaGain, float muGain) { + _omegaGain = omegaGain; + _muGain = muGain; + recov.setGains(_omegaGain, _muGain); + } + + void setOmegaRelLimit(float omegaRelLimit) { + _omegaRelLimit = omegaRelLimit; + recov.setOmegaRelLimit(_omegaRelLimit); + } + + stream* out = NULL; + + private: + dsp::ComplexAGC agc; + dsp::CarrierTrackingPLL pll; + dsp::RRCTaps rrcwin; + dsp::FIR rrc; + dsp::MMClockRecovery recov; + + float _sampleRate; + float _baudRate; + float _agcRate; + float _pllLoopBandwidth; + int _rrcTapCount; + float _rrcAlpha; + float _omegaGain; + float _muGain; + float _omegaRelLimit; + }; } \ No newline at end of file diff --git a/core/src/dsp/pll.h b/core/src/dsp/pll.h index b88380b..5294992 100644 --- a/core/src/dsp/pll.h +++ b/core/src/dsp/pll.h @@ -115,4 +115,206 @@ namespace dsp { stream* _in; }; + + template + class CarrierTrackingPLL: public generic_block> { + public: + CarrierTrackingPLL() {} + CarrierTrackingPLL(stream* in, float loopBandwidth) { init(in, loopBandwidth); } + + void init(stream* in, float loopBandwidth) { + _in = in; + lastVCO.re = 1.0f; + lastVCO.im = 0.0f; + _loopBandwidth = loopBandwidth; + + float dampningFactor = sqrtf(2.0f) / 2.0f; + float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth); + _alpha = (4 * dampningFactor * _loopBandwidth) / denominator; + _beta = (4 * _loopBandwidth * _loopBandwidth) / denominator; + + generic_block>::registerInput(_in); + generic_block>::registerOutput(&out); + } + + void setInput(stream* in) { + generic_block>::tempStop(); + generic_block>::unregisterInput(_in); + _in = in; + generic_block>::registerInput(_in); + generic_block>::tempStart(); + } + + void setLoopBandwidth(float loopBandwidth) { + generic_block>::tempStop(); + _loopBandwidth = loopBandwidth; + float dampningFactor = sqrtf(2.0f) / 2.0f; + float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth); + _alpha = (4 * dampningFactor * _loopBandwidth) / denominator; + _beta = (4 * _loopBandwidth * _loopBandwidth) / denominator; + generic_block>::tempStart(); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + complex_t outVal; + float error; + + for (int i = 0; i < count; i++) { + + // Mix the VFO with the input to create the output value + 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); + + if constexpr (std::is_same_v) { + out.writeBuf[i] = outVal.fastPhase(); + } + if constexpr (std::is_same_v) { + out.writeBuf[i] = outVal; + } + + // Calculate the phase error estimation + // TODO: Figure out why fastPhase doesn't work + error = _in->readBuf[i].phase() - vcoPhase; + if (error > 3.1415926535f) { error -= 2.0f * 3.1415926535f; } + else if (error <= -3.1415926535f) { error += 2.0f * 3.1415926535f; } + + // 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; } + else if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; } + + // Calculate new phase and wrap it + vcoPhase += vcoFrequency + (_alpha * error); + while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); } + while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); } + + // Calculate output + lastVCO.re = cosf(vcoPhase); + lastVCO.im = sinf(vcoPhase); + + } + + _in->flush(); + if (!out.swap(count)) { return -1; } + return count; + } + + stream out; + + private: + float _loopBandwidth = 1.0f; + + float _alpha; // Integral coefficient + float _beta; // Proportional coefficient + float vcoFrequency = 0.0f; + float vcoPhase = 0.0f; + complex_t lastVCO; + + stream* _in; + + }; + + class PLL: public generic_block { + public: + PLL() {} + PLL(stream* in, float loopBandwidth) { init(in, loopBandwidth); } + + void init(stream* in, float loopBandwidth) { + _in = in; + lastVCO.re = 1.0f; + lastVCO.im = 0.0f; + _loopBandwidth = loopBandwidth; + + float dampningFactor = sqrtf(2.0f) / 2.0f; + float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth); + _alpha = (4 * dampningFactor * _loopBandwidth) / denominator; + _beta = (4 * _loopBandwidth * _loopBandwidth) / denominator; + + generic_block::registerInput(_in); + generic_block::registerOutput(&out); + } + + void setInput(stream* in) { + generic_block::tempStop(); + generic_block::unregisterInput(_in); + _in = in; + generic_block::registerInput(_in); + generic_block::tempStart(); + } + + void setLoopBandwidth(float loopBandwidth) { + generic_block::tempStop(); + _loopBandwidth = loopBandwidth; + float dampningFactor = sqrtf(2.0f) / 2.0f; + float denominator = (1.0 + 2.0 * dampningFactor * _loopBandwidth + _loopBandwidth * _loopBandwidth); + _alpha = (4 * dampningFactor * _loopBandwidth) / denominator; + _beta = (4 * _loopBandwidth * _loopBandwidth) / denominator; + generic_block::tempStart(); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + complex_t outVal; + float error; + + for (int i = 0; i < count; i++) { + + // Mix the VFO with the input to create the output value + 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] = lastVCO; + + // Calculate the phase error estimation + // TODO: Figure out why fastPhase doesn't work + error = _in->readBuf[i].phase() - vcoPhase; + if (error > 3.1415926535f) { error -= 2.0f * 3.1415926535f; } + else if (error <= -3.1415926535f) { error += 2.0f * 3.1415926535f; } + + // 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; } + else if (vcoFrequency < -1.0f) { vcoFrequency = -1.0f; } + + // Calculate new phase and wrap it + vcoPhase += vcoFrequency + (_alpha * error); + while (vcoPhase > (2.0f * FL_M_PI)) { vcoPhase -= (2.0f * FL_M_PI); } + while (vcoPhase < (-2.0f * FL_M_PI)) { vcoPhase += (2.0f * FL_M_PI); } + + // Calculate output + lastVCO.re = cosf(vcoPhase); + lastVCO.im = sinf(vcoPhase); + + } + + _in->flush(); + if (!out.swap(count)) { return -1; } + return count; + } + + stream out; + + private: + float _loopBandwidth = 1.0f; + + float _alpha; // Integral coefficient + float _beta; // Proportional coefficient + float vcoFrequency = 0.0f; + float vcoPhase = 0.0f; + complex_t lastVCO; + + stream* _in; + + }; } \ No newline at end of file diff --git a/core/src/dsp/sink.h b/core/src/dsp/sink.h index 218d2c9..97299dc 100644 --- a/core/src/dsp/sink.h +++ b/core/src/dsp/sink.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace dsp { template @@ -128,4 +129,53 @@ namespace dsp { stream* _in; }; + + template + class FileSink : public generic_block> { + public: + FileSink() {} + + FileSink(stream* in, std::string path) { init(in, path); } + + ~FileSink() { + generic_block>::stop(); + if (file.is_open()) { file.close(); } + } + + void init(stream* in, std::string path) { + _in = in; + file = std::ofstream(path, std::ios::binary); + generic_block>::registerInput(_in); + } + + void setInput(stream* in) { + std::lock_guard lck(generic_block>::ctrlMtx); + generic_block>::tempStop(); + generic_block>::unregisterInput(_in); + _in = in; + generic_block>::registerInput(_in); + generic_block>::tempStart(); + } + + bool isOpen() { + return file.is_open(); + } + + int run() { + int count = _in->read(); + if (count < 0) { return -1; } + + if (file.is_open()) { + file.write((char*)_in->readBuf, count * sizeof(T)); + } + + _in->flush(); + return count; + } + + private: + stream* _in; + std::ofstream file; + + }; } \ No newline at end of file diff --git a/core/src/gui/widgets/symbol_diagram.cpp b/core/src/gui/widgets/symbol_diagram.cpp index 9b5cbda..511be1a 100644 --- a/core/src/gui/widgets/symbol_diagram.cpp +++ b/core/src/gui/widgets/symbol_diagram.cpp @@ -1,7 +1,8 @@ #include namespace ImGui { - SymbolDiagram::SymbolDiagram() { + SymbolDiagram::SymbolDiagram(float scale) { + _scale = scale; memset(buffer, 0, 1024 * sizeof(float)); } @@ -23,9 +24,11 @@ namespace ImGui { 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; + float val; for (int i = 0; i < 1024; i++) { - if (buffer[i] > 1.0f || buffer[i] < -1.0f) { continue; } - window->DrawList->AddCircleFilled(ImVec2(((float)i * increment) + min.x, ((buffer[i] + 1) * (size.y*0.5f)) + min.y), 2, col); + val = buffer[i] * _scale; + if (val > 1.0f || val < -1.0f) { continue; } + window->DrawList->AddCircleFilled(ImVec2(((float)i * increment) + min.x, ((val + 1) * (size.y*0.5f)) + min.y), 2, col); } } diff --git a/core/src/gui/widgets/symbol_diagram.h b/core/src/gui/widgets/symbol_diagram.h index 43c9fa3..f4a806e 100644 --- a/core/src/gui/widgets/symbol_diagram.h +++ b/core/src/gui/widgets/symbol_diagram.h @@ -8,7 +8,7 @@ namespace ImGui { class SymbolDiagram { public: - SymbolDiagram(); + SymbolDiagram(float _scale = 1.0f); void draw(const ImVec2& size_arg = ImVec2(0, 0)); @@ -19,6 +19,7 @@ namespace ImGui { private: std::mutex bufferMtx; float buffer[1024]; + float _scale; }; } \ No newline at end of file diff --git a/hackrf_source/src/main.cpp b/hackrf_source/src/main.cpp index 5e43c50..1ac9f66 100644 --- a/hackrf_source/src/main.cpp +++ b/hackrf_source/src/main.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #pragma optimize( "", off ) @@ -20,7 +22,7 @@ SDRPP_MOD_INFO { /* Max instances */ 1 }; -//ConfigManager config; +ConfigManager config; const char* AGG_MODES_STR = "Off\0Low\0High\0"; @@ -36,6 +38,43 @@ const int sampleRates[] = { 2000000, }; +const int bandwidths[] = { + 1750000, + 2500000, + 3500000, + 5000000, + 5500000, + 6000000, + 7000000, + 8000000, + 9000000, + 10000000, + 12000000, + 14000000, + 15000000, + 20000000, + 24000000, + 28000000, +}; + +const char* bandwidthsTxt = "1.75MHz\0" + "2.5MHz\0" + "3.5MHz\0" + "5MHz\0" + "5.5MHz\0" + "6MHz\0" + "7MHz\0" + "8MHz\0" + "9MHz\0" + "10MHz\0" + "12MHz\0" + "14MHz\0" + "15MHz\0" + "20MHz\0" + "24MHz\0" + "28MHz\0" + "Auto\0"; + class HackRFSourceModule : public ModuleManager::Instance { public: HackRFSourceModule(std::string name) { @@ -43,7 +82,9 @@ public: hackrf_init(); + // Select the last samplerate option sampleRate = 2000000; + srId = 6; handler.ctx = this; handler.selectHandler = menuSelected; @@ -56,11 +97,10 @@ public: refresh(); - selectFirst(); - - // config.aquire(); - // std::string serString = config.conf["device"]; - // config.release(); + config.aquire(); + std::string confSerial = config.conf["device"]; + config.release(); + selectBySerial(confSerial); sigpath::sourceManager.registerSource("HackRF", &handler); } @@ -100,8 +140,67 @@ public: void selectFirst() { if (devList.size() != 0) { - selectedSerial = devList[0]; + selectBySerial(devList[0]); + return; } + selectedSerial = ""; + } + + void selectBySerial(std::string serial) { + if (std::find(devList.begin(), devList.end(), serial) == devList.end()) { + selectFirst(); + return; + } + + bool created = false; + config.aquire(); + if (!config.conf["devices"].contains(serial)) { + config.conf["devices"][serial]["sampleRate"] = 2000000; + config.conf["devices"][serial]["biasT"] = false; + config.conf["devices"][serial]["amp"] = false; + config.conf["devices"][serial]["lnaGain"] = 0; + config.conf["devices"][serial]["vgaGain"] = 0; + config.conf["devices"][serial]["bandwidth"] = 16; + } + config.release(created); + + // Set default values + srId = 0; + sampleRate = 2000000; + biasT = false; + amp = false; + lna = 0; + vga = 0; + bwId = 16; + + // Load from config if available and validate + if (config.conf["devices"][serial].contains("sampleRate")) { + int psr = config.conf["devices"][serial]["sampleRate"]; + for (int i = 0; i < 7; i++) { + if (sampleRates[i] == psr) { + sampleRate = psr; + srId = i; + } + } + } + if (config.conf["devices"][serial].contains("biasT")) { + biasT = config.conf["devices"][serial]["biasT"]; + } + if (config.conf["devices"][serial].contains("amp")) { + amp = config.conf["devices"][serial]["amp"]; + } + if (config.conf["devices"][serial].contains("lnaGain")) { + lna = config.conf["devices"][serial]["lnaGain"]; + } + if (config.conf["devices"][serial].contains("vgaGain")) { + vga = config.conf["devices"][serial]["vgaGain"]; + } + if (config.conf["devices"][serial].contains("bandwidth")) { + bwId = config.conf["devices"][serial]["bandwidth"]; + bwId = std::clamp(bwId, 0, 16); + } + + selectedSerial = serial; } private: @@ -116,7 +215,10 @@ private: spdlog::info("HackRFSourceModule '{0}': Menu Deselect!", _this->name); } - + int bandwidthIdToBw(int id) { + if (id == 16) { return hackrf_compute_baseband_filter_bw(sampleRate); } + return bandwidths[id]; + } static void start(void* ctx) { HackRFSourceModule* _this = (HackRFSourceModule*)ctx; @@ -135,12 +237,13 @@ private: } hackrf_set_sample_rate(_this->openDev, _this->sampleRate); - hackrf_set_baseband_filter_bandwidth(_this->openDev, hackrf_compute_baseband_filter_bw(_this->sampleRate)); + hackrf_set_baseband_filter_bandwidth(_this->openDev, _this->bandwidthIdToBw(_this->bwId)); hackrf_set_freq(_this->openDev, _this->freq); + hackrf_set_antenna_enable(_this->openDev, _this->biasT); hackrf_set_amp_enable(_this->openDev, _this->amp); hackrf_set_lna_gain(_this->openDev, _this->lna); - hackrf_set_vga_gain(_this->openDev, _this->lna); + hackrf_set_vga_gain(_this->openDev, _this->vga); hackrf_start_rx(_this->openDev, callback, _this); @@ -179,11 +282,17 @@ private: ImGui::SetNextItemWidth(menuWidth); if (ImGui::Combo(CONCAT("##_hackrf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { _this->selectedSerial = _this->devList[_this->devId]; + config.aquire(); + config.conf["device"] = _this->selectedSerial; + config.release(true); } if (ImGui::Combo(CONCAT("##_hackrf_sr_sel_", _this->name), &_this->srId, sampleRatesTxt)) { _this->sampleRate = sampleRates[_this->srId]; core::setInputSampleRate(_this->sampleRate); + config.aquire(); + config.conf["devices"][_this->selectedSerial]["sampleRate"] = _this->sampleRate; + config.release(true); } ImGui::SameLine(); @@ -194,30 +303,58 @@ private: if (_this->running) { style::endDisabled(); } - ImGui::Text("Amp Enabled"); + ImGui::Text("Bandwidth"); ImGui::SameLine(); - if (ImGui::Checkbox(CONCAT("##_hackrf_amp_", _this->name), &_this->amp)) { + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::Combo(CONCAT("##_hackrf_bw_sel_", _this->name), &_this->bwId, bandwidthsTxt)) { + if (_this->running) { + hackrf_set_baseband_filter_bandwidth(_this->openDev, _this->bandwidthIdToBw(_this->bwId)); + } + config.aquire(); + config.conf["devices"][_this->selectedSerial]["bandwidth"] = _this->bwId; + config.release(true); + } + + if (ImGui::Checkbox(CONCAT("Bias-T##_hackrf_bt_", _this->name), &_this->biasT)) { + if (_this->running) { + hackrf_set_antenna_enable(_this->openDev, _this->biasT); + } + config.aquire(); + config.conf["devices"][_this->selectedSerial]["biasT"] = _this->biasT; + config.release(true); + } + + if (ImGui::Checkbox(CONCAT("Amp Enabled##_hackrf_amp_", _this->name), &_this->amp)) { if (_this->running) { hackrf_set_amp_enable(_this->openDev, _this->amp); } + config.aquire(); + config.conf["devices"][_this->selectedSerial]["amp"] = _this->amp; + config.release(true); } ImGui::Text("LNA Gain"); ImGui::SameLine(); - if (ImGui::SliderInt(CONCAT("##_hackrf_lna_", _this->name), &_this->lna, 0, 40)) { - _this->lna = (_this->lna / 8) * 8; + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::SliderFloatWithSteps(CONCAT("##_hackrf_lna_", _this->name), &_this->lna, 0, 40, 8, "%.0fdB")) { if (_this->running) { hackrf_set_lna_gain(_this->openDev, _this->lna); } + config.aquire(); + config.conf["devices"][_this->selectedSerial]["lnaGain"] = (int)_this->lna; + config.release(true); } - ImGui::Text("LNA Gain"); + ImGui::Text("VGA Gain"); ImGui::SameLine(); - if (ImGui::SliderInt(CONCAT("##_hackrf_vga_", _this->name), &_this->vga, 0, 62)) { - _this->vga = (_this->vga / 2) * 2; + ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); + if (ImGui::SliderFloatWithSteps(CONCAT("##_hackrf_vga_", _this->name), &_this->vga, 0, 62, 2, "%.0fdB")) { if (_this->running) { - hackrf_set_vga_gain(_this->openDev, _this->lna); + hackrf_set_vga_gain(_this->openDev, _this->vga); } + config.aquire(); + config.conf["devices"][_this->selectedSerial]["vgaGain"] = (int)_this->vga; + config.release(true); } } @@ -244,21 +381,23 @@ private: std::string selectedSerial = ""; int devId = 0; int srId = 0; + int bwId = 16; + bool biasT = false; bool amp = false; - int lna = 0; - int vga = 0; + float lna = 0; + float vga = 0; std::vector devList; std::string devListTxt; }; MOD_EXPORT void _INIT_() { -// config.setPath(ROOT_DIR "/airspyhf_config.json"); -// json defConf; -// defConf["device"] = ""; -// defConf["devices"] = json::object(); -// config.load(defConf); -// config.enableAutoSave(); + json def = json({}); + def["devices"] = json({}); + def["device"] = ""; + config.setPath(options::opts.root + "/hackrf_config.json"); + config.load(def); + config.enableAutoSave(); } MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { @@ -270,8 +409,8 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { } MOD_EXPORT void _END_() { - // config.disableAutoSave(); - // config.save(); + config.disableAutoSave(); + config.save(); } #pragma optimize( "", on ) \ No newline at end of file diff --git a/sdrplay_source/src/main.cpp b/sdrplay_source/src/main.cpp index d0765fa..7ff824a 100644 --- a/sdrplay_source/src/main.cpp +++ b/sdrplay_source/src/main.cpp @@ -304,7 +304,9 @@ public: config.conf["devices"][selectedName]["biast"] = false; } else if (openDev.hwVer == SDRPLAY_RSPduo_ID) { - // TODO: Implement + config.conf["devices"][selectedName]["fmNotch"] = false; + config.conf["devices"][selectedName]["dabNotch"] = false; + config.conf["devices"][selectedName]["biast"] = false; } else if (openDev.hwVer == SDRPLAY_RSPdx_ID) { config.conf["devices"][selectedName]["antenna"] = 0; @@ -369,7 +371,15 @@ public: } } else if (openDev.hwVer == SDRPLAY_RSPduo_ID) { - // TODO: Implement + if (config.conf["devices"][selectedName].contains("fmNotch")) { + rspduo_fmNotch = config.conf["devices"][selectedName]["fmNotch"]; + } + if (config.conf["devices"][selectedName].contains("dabNotch")) { + rspduo_dabNotch = config.conf["devices"][selectedName]["dabNotch"]; + } + if (config.conf["devices"][selectedName].contains("biast")) { + rspduo_biasT = config.conf["devices"][selectedName]["biast"]; + } } else if (openDev.hwVer == SDRPLAY_RSPdx_ID) { if (config.conf["devices"][selectedName].contains("antenna")) { @@ -467,6 +477,9 @@ private: sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Rsp2_BiasTControl, sdrplay_api_Update_Ext1_None); sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Rsp2_AntennaControl, sdrplay_api_Update_Ext1_None); sdrplay_api_Update(_this->openDev.dev, _this->openDev.tuner, sdrplay_api_Update_Rsp2_AmPortSelect, sdrplay_api_Update_Ext1_None); + } + else if (_this->openDev.hwVer == SDRPLAY_RSPduo_ID) { + } else if (_this->openDev.hwVer == SDRPLAY_RSPdx_ID) { _this->openDevParams->devParams->rspDxParams.rfNotchEnable = _this->rspdx_fmNotch; @@ -780,7 +793,9 @@ private: int rsp2_antennaPort = 0; // RSP Duo Options - + bool rspduo_fmNotch = false; + bool rspduo_dabNotch = false; + bool rspduo_biasT = false; // RSPdx Options bool rspdx_fmNotch = false;