kopia lustrzana https://github.com/AlexandreRouma/SDRPlusPlus
Porównaj commity
11 Commity
2b752bb267
...
a987c112a3
Autor | SHA1 | Data |
---|---|---|
AlexandreRouma | a987c112a3 | |
AlexandreRouma | f1339f08cf | |
AlexandreRouma | 650a61930c | |
AlexandreRouma | 61ffb3e6bf | |
AlexandreRouma | 9ab3c97c44 | |
AlexandreRouma | edc08ddc08 | |
AlexandreRouma | 95052c34ff | |
AlexandreRouma | 34171d4edc | |
AlexandreRouma | 726e1069bf | |
AlexandreRouma | 61c14bab48 | |
AlexandreRouma | 01ab1831e8 |
|
@ -37,10 +37,10 @@ jobs:
|
|||
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
|
||||
|
||||
- name: Download SDRPlay API
|
||||
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip
|
||||
run: Invoke-WebRequest -Uri "https://www.sdrpp.org/SDRplay.zip" -OutFile ${{runner.workspace}}/SDRplay.zip
|
||||
|
||||
- name: Install SDRPlay API
|
||||
run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/"
|
||||
run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/"
|
||||
|
||||
- name: Download codec2
|
||||
run: git clone https://github.com/AlexandreRouma/codec2
|
||||
|
|
|
@ -17,7 +17,7 @@ option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
|||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
||||
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" on)
|
||||
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
||||
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
|
||||
option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON)
|
||||
|
@ -287,7 +287,12 @@ if (OPT_BUILD_SCHEDULER)
|
|||
add_subdirectory("misc_modules/scheduler")
|
||||
endif (OPT_BUILD_SCHEDULER)
|
||||
|
||||
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
||||
if (MSVC)
|
||||
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
||||
else ()
|
||||
add_executable(sdrpp "src/main.cpp")
|
||||
endif ()
|
||||
|
||||
target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
||||
|
||||
# Compiler arguments
|
||||
|
|
|
@ -12,6 +12,10 @@ namespace dsp::compression {
|
|||
|
||||
void init(stream<complex_t>* in, PCMType pcmType) {
|
||||
_pcmType = pcmType;
|
||||
|
||||
// Set the output buffer size to the max size of a complex buffer + 8 bytes for the header
|
||||
out.setBufferSize(STREAM_BUFFER_SIZE*sizeof(complex_t) + 8);
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ namespace server {
|
|||
// Compress data if needed and fill out header fields
|
||||
if (compression) {
|
||||
bb_pkt_hdr->type = PACKET_TYPE_BASEBAND_COMPRESSED;
|
||||
bb_pkt_hdr->size = sizeof(PacketHeader) + (uint32_t)ZSTD_compressCCtx(cctx, &bbuf[sizeof(PacketHeader)], SERVER_MAX_PACKET_SIZE, data, count, 1);
|
||||
bb_pkt_hdr->size = sizeof(PacketHeader) + (uint32_t)ZSTD_compressCCtx(cctx, &bbuf[sizeof(PacketHeader)], SERVER_MAX_PACKET_SIZE-sizeof(PacketHeader), data, count, 1);
|
||||
}
|
||||
else {
|
||||
bb_pkt_hdr->type = PACKET_TYPE_BASEBAND;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
class POCSAGDecoder : public Decoder {
|
||||
public:
|
||||
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, 544) {
|
||||
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, BAUDRATE) {
|
||||
this->name = name;
|
||||
this->vfo = vfo;
|
||||
|
||||
|
@ -26,7 +26,7 @@ public:
|
|||
vfo->setBandwidthLimits(12500, 12500, true);
|
||||
vfo->setSampleRate(SAMPLERATE, 12500);
|
||||
dsp.init(vfo->output, SAMPLERATE, BAUDRATE);
|
||||
reshape.init(&dsp.soft, 544, 0);
|
||||
reshape.init(&dsp.soft, BAUDRATE, (BAUDRATE / 30.0) - BAUDRATE);
|
||||
dataHandler.init(&dsp.out, _dataHandler, this);
|
||||
diagHandler.init(&reshape.out, _diagHandler, this);
|
||||
|
||||
|
|
|
@ -11,89 +11,6 @@
|
|||
#include <dsp/digital/binary_slicer.h>
|
||||
#include <dsp/routing/doubler.h>
|
||||
|
||||
#include "packet_clock_sync.h"
|
||||
|
||||
inline float PATTERN_DSDSDZED[] = {
|
||||
-1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01,
|
||||
-2.00000000e-01, -2.77555756e-17, 2.00000000e-01, 4.00000000e-01,
|
||||
6.00000000e-01, 8.00000000e-01, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 8.00000000e-01,
|
||||
6.00000000e-01, 4.00000000e-01, 2.00000000e-01, 2.77555756e-17,
|
||||
-2.00000000e-01, -4.00000000e-01, -6.00000000e-01, -8.00000000e-01,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -8.00000000e-01,
|
||||
-6.00000000e-01, -4.00000000e-01, -2.00000000e-01, -2.77555756e-17,
|
||||
2.00000000e-01, 4.00000000e-01, 6.00000000e-01, 8.00000000e-01,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 8.00000000e-01,
|
||||
6.00000000e-01, 4.00000000e-01, 2.00000000e-01, 2.77555756e-17,
|
||||
-2.00000000e-01, -4.00000000e-01, -6.00000000e-01, -8.00000000e-01,
|
||||
-1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01,
|
||||
-2.00000000e-01, -2.77555756e-17, 2.00000000e-01, 4.00000000e-01,
|
||||
6.00000000e-01, 8.00000000e-01, 1.00000000e+00, 8.00000000e-01,
|
||||
6.00000000e-01, 4.00000000e-01, 2.00000000e-01, 2.77555756e-17,
|
||||
-2.00000000e-01, -4.00000000e-01, -6.00000000e-01, -8.00000000e-01,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -8.00000000e-01,
|
||||
-6.00000000e-01, -4.00000000e-01, -2.00000000e-01, -2.77555756e-17,
|
||||
2.00000000e-01, 4.00000000e-01, 6.00000000e-01, 8.00000000e-01,
|
||||
1.00000000e+00, 8.00000000e-01, 6.00000000e-01, 4.00000000e-01,
|
||||
2.00000000e-01, 2.77555756e-17, -2.00000000e-01, -4.00000000e-01,
|
||||
-6.00000000e-01, -8.00000000e-01, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01,
|
||||
-2.00000000e-01, -2.77555756e-17, 2.00000000e-01, 4.00000000e-01,
|
||||
6.00000000e-01, 8.00000000e-01, 1.00000000e+00, 8.00000000e-01,
|
||||
6.00000000e-01, 4.00000000e-01, 2.00000000e-01, 2.77555756e-17,
|
||||
-2.00000000e-01, -4.00000000e-01, -6.00000000e-01, -8.00000000e-01,
|
||||
-1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01,
|
||||
-2.00000000e-01, -2.77555756e-17, 2.00000000e-01, 4.00000000e-01,
|
||||
6.00000000e-01, 8.00000000e-01, 1.00000000e+00, 8.00000000e-01,
|
||||
6.00000000e-01, 4.00000000e-01, 2.00000000e-01, 2.77555756e-17,
|
||||
-2.00000000e-01, -4.00000000e-01, -6.00000000e-01, -8.00000000e-01,
|
||||
-1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01,
|
||||
-2.00000000e-01, -2.77555756e-17, 2.00000000e-01, 4.00000000e-01,
|
||||
6.00000000e-01, 8.00000000e-01, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 8.00000000e-01,
|
||||
6.00000000e-01, 4.00000000e-01, 2.00000000e-01, 2.77555756e-17,
|
||||
-2.00000000e-01, -4.00000000e-01, -6.00000000e-01, -8.00000000e-01,
|
||||
-1.00000000e+00, -8.00000000e-01, -6.00000000e-01, -4.00000000e-01,
|
||||
-2.00000000e-01, -2.77555756e-17, 2.00000000e-01, 4.00000000e-01,
|
||||
6.00000000e-01, 8.00000000e-01, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 1.00000000e+00, 1.00000000e+00, 1.00000000e+00,
|
||||
1.00000000e+00, 8.00000000e-01, 6.00000000e-01, 4.00000000e-01,
|
||||
2.00000000e-01, 2.77555756e-17, -2.00000000e-01, -4.00000000e-01,
|
||||
-6.00000000e-01, -8.00000000e-01, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00, -1.00000000e+00,
|
||||
-1.00000000e+00, -1.00000000e+00, -1.00000000e+00
|
||||
};
|
||||
|
||||
class POCSAGDSP : public dsp::Processor<dsp::complex_t, uint8_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
|
||||
public:
|
||||
|
@ -106,16 +23,12 @@ public:
|
|||
|
||||
// Configure blocks
|
||||
demod.init(NULL, -4500.0, samplerate);
|
||||
//dcBlock.init(NULL, 0.001); // NOTE: DC blocking causes issues because no scrambling, think more about it
|
||||
float taps[] = { 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f };
|
||||
shape = dsp::taps::fromArray<float>(10, taps);
|
||||
fir.init(NULL, shape);
|
||||
//recov.init(NULL, samplerate/baudrate, 1e-4, 1.0, 0.05);
|
||||
|
||||
cs.init(NULL, PATTERN_DSDSDZED, sizeof(PATTERN_DSDSDZED)/sizeof(float), 544, 10);
|
||||
recov.init(NULL, samplerate/baudrate, 1e-4, 1.0, 0.05);
|
||||
|
||||
// Free useless buffers
|
||||
// dcBlock.out.free();
|
||||
fir.out.free();
|
||||
recov.out.free();
|
||||
|
||||
|
@ -125,13 +38,9 @@ public:
|
|||
|
||||
int process(int count, dsp::complex_t* in, float* softOut, uint8_t* out) {
|
||||
count = demod.process(count, in, demod.out.readBuf);
|
||||
//count = dcBlock.process(count, demod.out.readBuf, demod.out.readBuf);
|
||||
count = fir.process(count, demod.out.readBuf, demod.out.readBuf);
|
||||
//count = recov.process(count, demod.out.readBuf, softOut);
|
||||
|
||||
count = cs.process(count, demod.out.readBuf, softOut);
|
||||
|
||||
//dsp::digital::BinarySlicer::process(count, softOut, out);
|
||||
count = recov.process(count, demod.out.readBuf, softOut);
|
||||
dsp::digital::BinarySlicer::process(count, softOut, out);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -146,10 +55,8 @@ public:
|
|||
count = process(count, base_type::_in->readBuf, soft.writeBuf, base_type::out.writeBuf);
|
||||
|
||||
base_type::_in->flush();
|
||||
//if (!base_type::out.swap(count)) { return -1; }
|
||||
|
||||
if (!base_type::out.swap(count)) { return -1; }
|
||||
if (count) { if (!soft.swap(count)) { return -1; } }
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -157,11 +64,8 @@ public:
|
|||
|
||||
private:
|
||||
dsp::demod::Quadrature demod;
|
||||
//dsp::correction::DCBlocker<float> dcBlock;
|
||||
dsp::tap<float> shape;
|
||||
dsp::filter::FIR<float, float> fir;
|
||||
dsp::clock_recovery::MM<float> recov;
|
||||
|
||||
dsp::PacketClockSync cs;
|
||||
|
||||
};
|
|
@ -1,221 +0,0 @@
|
|||
#pragma once
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/buffer/reshaper.h>
|
||||
#include <dsp/multirate/rational_resampler.h>
|
||||
#include <dsp/sink/handler_sink.h>
|
||||
#include <dsp/demod/quadrature.h>
|
||||
#include <dsp/clock_recovery/mm.h>
|
||||
#include <dsp/taps/root_raised_cosine.h>
|
||||
#include <dsp/correction/dc_blocker.h>
|
||||
#include <dsp/loop/fast_agc.h>
|
||||
#include <dsp/digital/binary_slicer.h>
|
||||
#include <dsp/routing/doubler.h>
|
||||
#include <utils/flog.h>
|
||||
#include <fftw3.h>
|
||||
#include <dsp/math/conjugate.h>
|
||||
#include <dsp/math/normalize_phase.h>
|
||||
|
||||
namespace dsp {
|
||||
class PacketClockSync : public dsp::Processor<float, float> {
|
||||
using base_type = dsp::Processor<float, float>;
|
||||
public:
|
||||
PacketClockSync() {}
|
||||
PacketClockSync(dsp::stream<float>* in, float* pattern, int patternLen, int frameLen, float sampsPerSym, float threshold = 0.4f) { init(in, pattern, patternLen, frameLen, sampsPerSym, threshold); }
|
||||
|
||||
// TODO: Free in destroyer
|
||||
|
||||
void init(dsp::stream<float>* in, float* pattern, int patternLen, int frameLen, float sampsPerSym, float threshold = 0.4f) {
|
||||
// Compute the required FFT size and associated delay length
|
||||
fftSize = 512;// TODO: Find smallest power of 2 that fits patternLen
|
||||
delayLen = fftSize - 1;
|
||||
|
||||
// Allocate buffers
|
||||
buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE+delayLen);
|
||||
bufferStart = &buffer[delayLen];
|
||||
this->pattern = dsp::buffer::alloc<float>(patternLen);
|
||||
patternFFT = dsp::buffer::alloc<complex_t>(fftSize);
|
||||
patternFFTAmps = dsp::buffer::alloc<float>(fftSize);
|
||||
fftIn = fftwf_alloc_real(fftSize);
|
||||
fftOut = (complex_t*)fftwf_alloc_complex(fftSize);
|
||||
|
||||
// Copy parameters
|
||||
memcpy(this->pattern, pattern, patternLen*sizeof(float));
|
||||
this->sampsPerSym = sampsPerSym;
|
||||
this->threshold = threshold;
|
||||
this->patternLen = patternLen;
|
||||
this->frameLen = frameLen;
|
||||
|
||||
// Plan FFT
|
||||
plan = fftwf_plan_dft_r2c_1d(fftSize, fftIn, (fftwf_complex*)fftOut, FFTW_ESTIMATE);
|
||||
|
||||
// Pre-compute pattern conjugated FFT
|
||||
// TODO: Offset the pattern to avoid it being cut off (EXTREMELY IMPORTANT)
|
||||
memcpy(fftIn, pattern, patternLen*sizeof(float));
|
||||
memset(&fftIn[patternLen], 0, (fftSize-patternLen)*sizeof(float));
|
||||
fftwf_execute(plan);
|
||||
volk_32fc_conjugate_32fc((lv_32fc_t*)patternFFT, (lv_32fc_t*)fftOut, fftSize);
|
||||
|
||||
// Compute amplitudes of the pattern FFT
|
||||
volk_32fc_magnitude_32f(patternFFTAmps, (lv_32fc_t*)patternFFT, fftSize);
|
||||
|
||||
// Normalize the amplitudes
|
||||
float maxAmp = 0.0f;
|
||||
for (int i = 0; i < fftSize/2; i++) {
|
||||
if (patternFFTAmps[i] > maxAmp) { maxAmp = patternFFTAmps[i]; }
|
||||
}
|
||||
volk_32f_s32f_multiply_32f(patternFFTAmps, patternFFTAmps, 1.0f/maxAmp, fftSize);
|
||||
|
||||
// Initialize the phase control loop
|
||||
float omegaRelLimit = 0.05;
|
||||
pcl.init(1, 10e-4, 0.0, 0.0, 1.0, sampsPerSym, sampsPerSym * (1.0 - omegaRelLimit), sampsPerSym * (1.0 + omegaRelLimit));
|
||||
generateInterpTaps();
|
||||
|
||||
// Init base
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
int process(int count, float* in, float* out) {
|
||||
// Copy to buffer
|
||||
memcpy(bufferStart, in, count * sizeof(float));
|
||||
|
||||
int outCount = 0;
|
||||
|
||||
for (int i = 0; i < count;) {
|
||||
// Run clock recovery if needed
|
||||
while (toRead) {
|
||||
// Interpolate symbol
|
||||
float symbol;
|
||||
int phase = std::clamp<int>(floorf(pcl.phase * (float)interpPhaseCount), 0, interpPhaseCount - 1);
|
||||
volk_32f_x2_dot_prod_32f(&symbol, &buffer[offsetInt], interpBank.phases[phase], interpTapCount);
|
||||
out[outCount++] = symbol;
|
||||
|
||||
// Compute symbol phase error
|
||||
float error = (math::step(lastSymbol) * symbol) - (lastSymbol * math::step(symbol));
|
||||
lastSymbol = symbol;
|
||||
|
||||
// Clamp symbol phase error
|
||||
if (error > 1.0f) { error = 1.0f; }
|
||||
if (error < -1.0f) { error = -1.0f; }
|
||||
|
||||
// Advance symbol offset and phase
|
||||
pcl.advance(error);
|
||||
float delta = floorf(pcl.phase);
|
||||
offsetInt += delta;
|
||||
i = offsetInt;
|
||||
pcl.phase -= delta;
|
||||
|
||||
// Decrement read counter
|
||||
toRead--;
|
||||
|
||||
if (offsetInt >= count) {
|
||||
offsetInt -= count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Measure correlation to the sync pattern
|
||||
float corr;
|
||||
volk_32f_x2_dot_prod_32f(&corr, &buffer[i], pattern, patternLen);
|
||||
|
||||
// If not correlated enough, go to next sample. Otherwise continue with fine detection
|
||||
if (corr/(float)patternLen < threshold) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy samples into FFT input (only the part where we think the pattern is located)
|
||||
// TODO: Instead, check the interval onto which correlation occurs to determine where the pattern is located (IMPORTANT)
|
||||
memcpy(fftIn, &buffer[i], patternLen*sizeof(float));
|
||||
|
||||
// Compute FFT
|
||||
fftwf_execute(plan);
|
||||
|
||||
// Multiply with the conjugated pattern FFT to get the phase offset at each frequency
|
||||
volk_32fc_x2_multiply_32fc((lv_32fc_t*)fftOut, (lv_32fc_t*)fftOut, (lv_32fc_t*)patternFFT, fftSize);
|
||||
|
||||
// Compute the average phase delay rate
|
||||
float last = 0;
|
||||
float rateIntegral = 0;
|
||||
for (int j = 1; j < fftSize/2; j++) {
|
||||
// Compute instantanous rate
|
||||
float currentPhase = fftOut[j].phase();
|
||||
float instantRate = dsp::math::normalizePhase(currentPhase - last);
|
||||
last = currentPhase;
|
||||
|
||||
// Compute current rate guess
|
||||
float rateGuess = rateIntegral / (float)j;
|
||||
|
||||
// Update the rate integral as a weighted average of the current guess and measured rate depending on pattern amplitude
|
||||
rateIntegral += patternFFTAmps[j]*instantRate + (1.0f-patternFFTAmps[j])*rateGuess;
|
||||
}
|
||||
float avgRate = 1.14f*rateIntegral/(float)(fftSize/2);
|
||||
|
||||
// Compute the total offset
|
||||
float offset = (float)i - avgRate*(float)fftSize/(2.0f*FL_M_PI);
|
||||
flog::debug("Detected: {} -> {}", i, offset);
|
||||
|
||||
// Initialize clock recovery
|
||||
offsetInt = floorf(offset) - 3; // TODO: Will be negative sometimes, has to be taken into account
|
||||
pcl.phase = offset - (float)floorf(offset);
|
||||
pcl.freq = sampsPerSym;
|
||||
|
||||
// Start reading symbols
|
||||
toRead = frameLen;
|
||||
}
|
||||
|
||||
// Move unused data
|
||||
memmove(buffer, &buffer[count], delayLen * sizeof(float));
|
||||
|
||||
return outCount;
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
count = process(count, base_type::_in->readBuf, base_type::out.writeBuf);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (count) {
|
||||
if (!base_type::out.swap(count)) { return -1; }
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private:
|
||||
void generateInterpTaps() {
|
||||
double bw = 0.5 / (double)interpPhaseCount;
|
||||
dsp::tap<float> lp = dsp::taps::windowedSinc<float>(interpPhaseCount * interpTapCount, dsp::math::hzToRads(bw, 1.0), dsp::window::nuttall, interpPhaseCount);
|
||||
interpBank = dsp::multirate::buildPolyphaseBank<float>(interpPhaseCount, lp);
|
||||
taps::free(lp);
|
||||
}
|
||||
|
||||
int delayLen;
|
||||
float* buffer = NULL;
|
||||
float* bufferStart = NULL;
|
||||
float* pattern = NULL;
|
||||
int patternLen;
|
||||
bool locked;
|
||||
int fftSize;
|
||||
int frameLen;
|
||||
float threshold;
|
||||
|
||||
float* fftIn = NULL;
|
||||
complex_t* fftOut = NULL;
|
||||
fftwf_plan plan;
|
||||
|
||||
complex_t* patternFFT;
|
||||
float* patternFFTAmps;
|
||||
|
||||
float sampsPerSym;
|
||||
int toRead = 0;
|
||||
|
||||
loop::PhaseControlLoop<float, false> pcl;
|
||||
dsp::multirate::PolyphaseBank<float> interpBank;
|
||||
int interpTapCount = 8;
|
||||
int interpPhaseCount = 128;
|
||||
float lastSymbol = 0.0f;
|
||||
int offsetInt;
|
||||
};
|
||||
}
|
|
@ -408,9 +408,9 @@ private:
|
|||
if (!_this->enabled) { ImGui::EndDisabled(); }
|
||||
}
|
||||
|
||||
void setMode(Mode newMode, bool fromDisabled = false) {
|
||||
void setMode(Mode newMode, bool forceSet = false) {
|
||||
// If there is no mode to change, do nothing
|
||||
if (!fromDisabled && mode == newMode) { return; }
|
||||
if (!forceSet && mode == newMode) { return; }
|
||||
|
||||
// Stop the DSP
|
||||
reshape.stop();
|
||||
|
@ -421,14 +421,13 @@ private:
|
|||
sigpath::vfoManager.deleteVFO(vfo);
|
||||
vfo = NULL;
|
||||
}
|
||||
if (mode == MODE_BASEBAND && !fromDisabled) {
|
||||
if (streamBound) {
|
||||
sigpath::iqFrontEnd.unbindIQStream(&iqStream);
|
||||
streamBound = false;
|
||||
}
|
||||
|
||||
// If the mode was none, we're done
|
||||
if (newMode == MODE_NONE) {
|
||||
return;
|
||||
}
|
||||
if (newMode == MODE_NONE) { return; }
|
||||
|
||||
// Create VFO or bind IQ stream
|
||||
if (newMode == MODE_VFO) {
|
||||
|
@ -441,6 +440,7 @@ private:
|
|||
else {
|
||||
// Bind IQ stream
|
||||
sigpath::iqFrontEnd.bindIQStream(&iqStream);
|
||||
streamBound = true;
|
||||
|
||||
// Set its output as the input to the DSP
|
||||
reshape.setInput(&iqStream);
|
||||
|
@ -509,7 +509,7 @@ private:
|
|||
size = sizeof(int16_t)*2;
|
||||
break;
|
||||
case SAMPLE_TYPE_INT32:
|
||||
volk_32f_s32f_convert_32i((int32_t*)_this->buffer, (float*)data, (float)2147483647.0f, count*2);
|
||||
volk_32f_s32f_convert_32i((int32_t*)_this->buffer, (float*)data, 2147483647.0f, count*2);
|
||||
size = sizeof(int32_t)*2;
|
||||
break;
|
||||
case SAMPLE_TYPE_FLOAT32:
|
||||
|
@ -555,6 +555,7 @@ private:
|
|||
OptionList<int, int> packetSizes;
|
||||
|
||||
VFOManager::VFO* vfo = NULL;
|
||||
bool streamBound = false;
|
||||
dsp::stream<dsp::complex_t> iqStream;
|
||||
dsp::buffer::Reshaper<dsp::complex_t> reshape;
|
||||
dsp::sink::Handler<dsp::complex_t> handler;
|
||||
|
|
|
@ -334,7 +334,7 @@ private:
|
|||
}
|
||||
|
||||
std::map<int, const char*> radioModeToString = {
|
||||
{ RADIO_IFACE_MODE_NFM, "NFM" },
|
||||
{ RADIO_IFACE_MODE_NFM, "FM" },
|
||||
{ RADIO_IFACE_MODE_WFM, "WFM" },
|
||||
{ RADIO_IFACE_MODE_AM, "AM" },
|
||||
{ RADIO_IFACE_MODE_DSB, "DSB" },
|
||||
|
|
|
@ -35,6 +35,13 @@ enum SampleType {
|
|||
SAMPLE_TYPE_FLOAT32
|
||||
};
|
||||
|
||||
const size_t SAMPLE_TYPE_SIZE[] {
|
||||
sizeof(int8_t)*2,
|
||||
sizeof(int16_t)*2,
|
||||
sizeof(int32_t)*2,
|
||||
sizeof(float)*2,
|
||||
};
|
||||
|
||||
class NetworkSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
NetworkSourceModule(std::string name) {
|
||||
|
@ -66,7 +73,7 @@ public:
|
|||
}
|
||||
|
||||
// Define protocols
|
||||
protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
||||
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
||||
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
|
||||
protocols.define("UDP", PROTOCOL_UDP);
|
||||
|
||||
|
@ -157,7 +164,31 @@ private:
|
|||
NetworkSourceModule* _this = (NetworkSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
|
||||
// TODO
|
||||
// Depends on protocol
|
||||
try {
|
||||
if (_this->proto == PROTOCOL_TCP_SERVER) {
|
||||
// Create TCP listener
|
||||
// TODO
|
||||
|
||||
// Start listen worker
|
||||
// TODO
|
||||
}
|
||||
else if (_this->proto == PROTOCOL_TCP_CLIENT) {
|
||||
// Connect to TCP server
|
||||
_this->sock = net::connect(_this->hostname, _this->port);
|
||||
}
|
||||
else if (_this->proto == PROTOCOL_UDP) {
|
||||
// Open UDP socket
|
||||
_this->sock = net::openudp("0.0.0.0", _this->port, _this->hostname, _this->port, true);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
flog::error("Could not start Network Source: {}", e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
// Start receive worker
|
||||
_this->workerThread = std::thread(&NetworkSourceModule::worker, _this);
|
||||
|
||||
_this->running = true;
|
||||
flog::info("NetworkSourceModule '{0}': Start!", _this->name);
|
||||
|
@ -167,8 +198,17 @@ private:
|
|||
NetworkSourceModule* _this = (NetworkSourceModule*)ctx;
|
||||
if (!_this->running) { return; }
|
||||
|
||||
// Stop listen worker
|
||||
// TODO
|
||||
|
||||
// Close connection
|
||||
if (_this->sock) { _this->sock->close(); }
|
||||
|
||||
// Stop worker thread
|
||||
_this->stream.stopWriter();
|
||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||
_this->stream.clearWriteStop();
|
||||
|
||||
_this->running = false;
|
||||
flog::info("NetworkSourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
@ -237,52 +277,43 @@ private:
|
|||
}
|
||||
|
||||
void worker() {
|
||||
int frameSize = samplerate / 200;
|
||||
switch (sampType) {
|
||||
case SAMPLE_TYPE_INT8:
|
||||
frameSize *= 2*sizeof(int8_t);;
|
||||
break;
|
||||
case SAMPLE_TYPE_INT16:
|
||||
frameSize *= 2*sizeof(int16_t);
|
||||
break;
|
||||
case SAMPLE_TYPE_INT32:
|
||||
frameSize *= 2*sizeof(int32_t);
|
||||
break;
|
||||
case SAMPLE_TYPE_FLOAT32:
|
||||
frameSize *= sizeof(dsp::complex_t);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE*sizeof(uint32_t));
|
||||
// Compute sizes
|
||||
int blockSize = samplerate / 200;
|
||||
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
|
||||
int frameSize = blockSize*sampleSize;
|
||||
|
||||
// Allocate receive buffer
|
||||
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
|
||||
|
||||
while (true) {
|
||||
// Read samples from socket
|
||||
int bytes = sock->recv(buffer, frameSize, true);
|
||||
if (bytes <= 0) { break; }
|
||||
|
||||
// Convert to CF32
|
||||
int count;
|
||||
// Convert to CF32 (note: problem if partial sample)
|
||||
int count = bytes / sampleSize;
|
||||
switch (sampType) {
|
||||
case SAMPLE_TYPE_INT8:
|
||||
frameSize *= 2*sizeof(int8_t);;
|
||||
volk_8i_s32f_convert_32f((float*)stream.writeBuf, (int8_t*)buffer, 128.0f, count*2);
|
||||
break;
|
||||
case SAMPLE_TYPE_INT16:
|
||||
frameSize *= 2*sizeof(int16_t);
|
||||
volk_16i_s32f_convert_32f((float*)stream.writeBuf, (int16_t*)buffer, 32768.0f, count*2);
|
||||
break;
|
||||
case SAMPLE_TYPE_INT32:
|
||||
frameSize *= 2*sizeof(int32_t);
|
||||
volk_32i_s32f_convert_32f((float*)stream.writeBuf, (int32_t*)buffer, 2147483647.0f, count*2);
|
||||
break;
|
||||
case SAMPLE_TYPE_FLOAT32:
|
||||
//memcpy(stream.writeBuf, buffer, )
|
||||
memcpy(stream.writeBuf, buffer, bytes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Send out converted samples
|
||||
//if (!stream.swap(bufferSize))
|
||||
if (!stream.swap(count)) { break; }
|
||||
}
|
||||
|
||||
// Free receive buffer
|
||||
dsp::buffer::free(buffer);
|
||||
}
|
||||
|
||||
|
@ -295,7 +326,7 @@ private:
|
|||
|
||||
int samplerate = 1000000;
|
||||
int srId;
|
||||
Protocol proto = PROTOCOL_TCP_SERVER;
|
||||
Protocol proto = PROTOCOL_UDP;
|
||||
int protoId;
|
||||
SampleType sampType = SAMPLE_TYPE_INT16;
|
||||
int sampTypeId;
|
||||
|
@ -306,6 +337,7 @@ private:
|
|||
OptionList<std::string, Protocol> protocols;
|
||||
OptionList<std::string, SampleType> sampleTypes;
|
||||
|
||||
std::thread workerThread;
|
||||
std::thread listenWorkerThread;
|
||||
|
||||
std::mutex sockMtx;
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace server {
|
|||
dctx = ZSTD_createDCtx();
|
||||
|
||||
// Initialize DSP
|
||||
decompIn.setBufferSize((sizeof(dsp::complex_t) * STREAM_BUFFER_SIZE) + 8);
|
||||
decompIn.setBufferSize(STREAM_BUFFER_SIZE*sizeof(dsp::complex_t) + 8);
|
||||
decompIn.clearWriteStop();
|
||||
decomp.init(&decompIn);
|
||||
link.init(&decomp.out, output);
|
||||
|
@ -209,7 +209,7 @@ namespace server {
|
|||
if (!decompIn.swap(r_pkt_hdr->size - sizeof(PacketHeader))) { break; }
|
||||
}
|
||||
else if (r_pkt_hdr->type == PACKET_TYPE_BASEBAND_COMPRESSED) {
|
||||
size_t outCount = ZSTD_decompressDCtx(dctx, decompIn.writeBuf, STREAM_BUFFER_SIZE, r_pkt_data, r_pkt_hdr->size - sizeof(PacketHeader));
|
||||
size_t outCount = ZSTD_decompressDCtx(dctx, decompIn.writeBuf, STREAM_BUFFER_SIZE*sizeof(dsp::complex_t)+8, r_pkt_data, r_pkt_hdr->size - sizeof(PacketHeader));
|
||||
if (outCount) {
|
||||
if (!decompIn.swap(outCount)) { break; }
|
||||
};
|
||||
|
|
|
@ -78,6 +78,10 @@ public:
|
|||
std::string serial = devAddr["serial"];
|
||||
std::string model = devAddr.has_key("product") ? devAddr["product"] : devAddr["type"];
|
||||
sprintf(buf, "USRP %s [%s]", model.c_str(), serial.c_str());
|
||||
|
||||
// Work-around for UHD sometimes reporting the same device twice
|
||||
if (devices.keyExists(serial)) { continue; }
|
||||
|
||||
devices.define(serial, buf, devAddr);
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +111,7 @@ public:
|
|||
channels.clear();
|
||||
auto subdevs = dev->get_rx_subdev_spec();
|
||||
for (int i = 0; i < subdevs.size(); i++) {
|
||||
std::string slot = subdevs[i].db_name;
|
||||
std::string slot = subdevs[i].db_name + ',' + subdevs[i].sd_name;
|
||||
sprintf(buf, "%s [%s]", dev->get_rx_subdev_name(i).c_str(), slot.c_str());
|
||||
channels.define(buf, buf, buf);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue