Skip to content
This repository has been archived by the owner on Nov 20, 2023. It is now read-only.

Commit

Permalink
Demodulator rewrite
Browse files Browse the repository at this point in the history
This makes demodulators far more generic as well as clarifying names
  • Loading branch information
Xerbo committed Feb 26, 2022
1 parent 47a835c commit f76ef41
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 218 deletions.
2 changes: 1 addition & 1 deletion src/digital/blocks.h
Expand Up @@ -106,7 +106,7 @@ class FengyunViterbi : public Block<complex, uint8_t> {

class VCDUExtractor : public Block<uint8_t, uint8_t> {
public:
VCDUExtractor(bool cadu = false) : Block(1024), use_cadu(cadu) { }
VCDUExtractor(std::string extension) : Block(1024), use_cadu(extension != "vcdu") { }

size_t work(const uint8_t *in, uint8_t *out, size_t n) {
uint8_t frame[1024];
Expand Down
161 changes: 46 additions & 115 deletions src/dsp.cpp
Expand Up @@ -20,127 +20,58 @@
#include "util/fir_taps.h"
#include <QFileInfo>

PMDemodulator::PMDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname)
: dc(0.001f),
pll(loop(0.005f), M_TAUf32 * 150e3f/SAMP_RATE),
ft(2.0f*M_PIf32 * -665.4e3/SAMP_RATE),
rrc(make_rrc(1.0, SAMP_RATE, 665.4e3, 0.6, 51)),
BiphaseDemodulator::BiphaseDemodulator(float samp_rate,
float sym_rate,
std::shared_ptr<FileReader> source,
std::string output_filename)
: dc_blocker(0.001f),
pll(loop(0.005f), M_TAUf32 * 150e3f/samp_rate),
translator(M_TAUf32 * -sym_rate/samp_rate),
rrc(make_rrc(1.0, samp_rate, sym_rate, 0.6, 51)),
agc(0.001f, 0.707f),
costas(2, loop(0.005f), 0.0f),
clock(2, SAMP_RATE/665.4e3, loop(0.01f)),
out(ofname) {
costas_loop(2, loop(0.005f), 0.0f),
clock_recovery(2, samp_rate/sym_rate, loop(0.01f)),
out(output_filename) {

file = std::move(source);

dc.in_pipe = file->out_pipe;
pll.in_pipe = dc.out_pipe;
ft.in_pipe = pll.out_pipe;
rrc.in_pipe = ft.out_pipe;
agc.in_pipe = rrc.out_pipe;
costas.in_pipe = agc.out_pipe;
clock.in_pipe = costas.out_pipe;
slicer.in_pipe = clock.out_pipe;
out.in_pipe = slicer.out_pipe;

blocks = { file.get(), &dc, &pll, &ft, &rrc, &agc, &costas, &clock, &slicer, &out };
for (BlockInterface *block : blocks) {
block->start();
}
}

void PMDemodulator::stop() {
for (BlockInterface *block : blocks) {
block->stop();
}
connect(dc_blocker, file);
connect(pll, dc_blocker);
connect(translator, pll);
connect(rrc, translator);
connect(agc, rrc);
connect(costas_loop, agc);
connect(clock_recovery, costas_loop);
connect(slicer, clock_recovery);
connect(out, slicer);
start();
}

MetopDemodulator::MetopDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname)
: dc(0.001f),
rrc(make_rrc(1.0, SAMP_RATE, 2.3333e6, 0.6, 51)),
costas(4, loop(0.005f), M_TAUf32 * 150e3f/SAMP_RATE),
clock(4, SAMP_RATE/2.3333e6, loop(0.01f)),
deframer(QFileInfo(QString::fromStdString(ofname)).suffix() == "cadu"),
out(ofname) {
template<class SymbolHandler, class Deframer>
PSKDemodulator<SymbolHandler, Deframer>::PSKDemodulator(float samp_rate,
float sym_rate,
size_t order,
bool suppress_carrier,
std::shared_ptr<FileReader> source,
std::string output_filename)
: dc_blocker(0.001f),
rrc(make_rrc(1.0, samp_rate, sym_rate, 0.6, 51)),
costas_loop(order, loop(0.005f), M_TAUf32 * 150e3f/samp_rate, suppress_carrier),
clock_recovery(order, samp_rate/sym_rate, loop(0.01f)),
deframer(QFileInfo(QString::fromStdString(output_filename)).suffix().toStdString()),
out(output_filename) {

file = std::move(source);

dc.in_pipe = file->out_pipe;
agc.in_pipe = dc.out_pipe;
rrc.in_pipe = agc.out_pipe;
costas.in_pipe = rrc.out_pipe;
clock.in_pipe = costas.out_pipe;
viterbi.in_pipe = clock.out_pipe;
deframer.in_pipe = viterbi.out_pipe;
out.in_pipe = deframer.out_pipe;

blocks = { file.get(), &dc, &agc, &rrc, &costas, &clock, &viterbi, &deframer, &out };
for (BlockInterface *block : blocks) {
block->start();
}
connect(dc_blocker, file);
connect(agc, dc_blocker);
connect(rrc, agc);
connect(costas_loop, rrc);
connect(clock_recovery, costas_loop);
connect(symbol_handler, clock_recovery);
connect(deframer, symbol_handler);
connect(out, deframer);
start();
}

void MetopDemodulator::stop() {
for (BlockInterface *block : blocks) {
block->stop();
}
}

FengyunDemodulator::FengyunDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname)
: dc(0.001f),
rrc(make_rrc(1.0, SAMP_RATE, 2.8e6, 0.8, 51)),
costas(4, loop(0.005f), M_TAUf32 * 150e3f/SAMP_RATE),
clock(4, SAMP_RATE/2.8e6, loop(0.01f)),
deframer(QFileInfo(QString::fromStdString(ofname)).suffix() == "cadu"),
out(ofname) {

file = std::move(source);

dc.in_pipe = file->out_pipe;
agc.in_pipe = dc.out_pipe;
rrc.in_pipe = agc.out_pipe;
costas.in_pipe = rrc.out_pipe;
clock.in_pipe = costas.out_pipe;
viterbi.in_pipe = clock.out_pipe;
deframer.in_pipe = viterbi.out_pipe;
out.in_pipe = deframer.out_pipe;

blocks = { file.get(), &dc, &agc, &rrc, &costas, &clock, &viterbi, &deframer, &out };
for (BlockInterface *block : blocks) {
block->start();
}
}

void FengyunDemodulator::stop() {
for (BlockInterface *block : blocks) {
block->stop();
}
}

GACDemodulator::GACDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname)
: dc(0.001f),
rrc(make_rrc(1.0, SAMP_RATE, 2661.6e3, 0.6, 51)),
costas(2, loop(0.005f), M_TAUf32 * 150e3f/SAMP_RATE, true),
clock(2, SAMP_RATE/2661.6e3, loop(0.01f)),
out(ofname) {

file = std::move(source);

dc.in_pipe = file->out_pipe;
agc.in_pipe = dc.out_pipe;
rrc.in_pipe = agc.out_pipe;
costas.in_pipe = rrc.out_pipe;
clock.in_pipe = costas.out_pipe;
slicer.in_pipe = clock.out_pipe;
out.in_pipe = slicer.out_pipe;

blocks = { file.get(), &dc, &agc, &rrc, &costas, &clock, &slicer, &out };
for (BlockInterface *block : blocks) {
block->start();
}
}

void GACDemodulator::stop() {
for (BlockInterface *block : blocks) {
block->stop();
}
}
template class PSKDemodulator<MetopViterbi, VCDUExtractor>;
template class PSKDemodulator<FengyunViterbi, VCDUExtractor>;
template class PSKDemodulator<BinarySlicer, Passthrough<uint8_t>>;
146 changes: 52 additions & 94 deletions src/dsp.h
Expand Up @@ -19,6 +19,7 @@
#ifndef DSP_H
#define DSP_H

#include <set>
#include "io/reader.h"
#include "dsp/dc_blocker.h"
#include "dsp/carrier_pll.h"
Expand All @@ -39,129 +40,86 @@

class Demodulator {
public:
bool running = true;
std::shared_ptr<FileReader> file;

virtual std::vector<complex> &symbols()=0;
virtual std::vector<complex> &freq()=0;
virtual bool is_running()=0;
virtual void stop()=0;
virtual std::vector<complex> &baseband()=0;
virtual ~Demodulator() { }
};

class PMDemodulator : public Demodulator {
public:
PMDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname);
std::vector<complex> &symbols() {
return slicer.in;
void stop() {
for (BlockInterface *block : blocks) block->stop();
}
std::vector<complex> &freq() {
return pll.in;
void start() {
for (BlockInterface *block : blocks) block->start();
}
bool is_running() {
return file->neof;
return !file->eof;
}
void stop();
private:
FastDCBlocker dc;
CarrierPLL pll;
FrequencyTranslator ft;
FIRFilter rrc;
AGC agc;
#ifdef EXPERIMENTAL
CostasLoopSSE costas;
#else
CostasLoop costas;
#endif
SymbolSync clock;
BinarySlicer slicer;
FileWriter<uint8_t> out;
std::vector<BlockInterface *> blocks;
};

class MetopDemodulator : public Demodulator {
public:
MetopDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname);
std::vector<complex> &symbols() {
return viterbi.in;
}
std::vector<complex> &freq() {
return agc.in;
}
bool is_running() {
return file->neof;
protected:
std::set<BlockInterface *> blocks;

template<class A, class B> void connect(A *a, B *b) {
blocks.insert(a);
blocks.insert(b);
a->in_pipe = b->out_pipe;
}
void stop();
private:
FastDCBlocker dc;
AGC agc;
FIRFilter rrc;
#ifdef EXPERIMENTAL
CostasLoopSSE costas;
#else
CostasLoop costas;
#endif
SymbolSync clock;
MetopViterbi viterbi;
VCDUExtractor deframer;
FileWriter<uint8_t> out;
std::vector<BlockInterface *> blocks;
template<class A, class B> void connect(A &a, std::shared_ptr<B> &b) { connect(&a, b.get()); }
template<class A, class B> void connect(std::shared_ptr<A> &a, B &b) { connect(a.get(), &b); }
template<class A, class B> void connect(A &a, B &b) { connect(&a, &b); }
};

class FengyunDemodulator : public Demodulator {
class BiphaseDemodulator : public Demodulator {
public:
FengyunDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname);
std::vector<complex> &symbols() {
return viterbi.in;
}
std::vector<complex> &freq() {
return agc.in;
}
bool is_running() {
return file->neof;
}
void stop();
BiphaseDemodulator(float samp_rate,
float sym_rate,
std::shared_ptr<FileReader> source,
std::string output_filename);
std::vector<complex> &symbols() { return slicer.in; }
std::vector<complex> &baseband() { return pll.in; }

private:
FastDCBlocker dc;
AGC agc;
FastDCBlocker dc_blocker;
CarrierPLL pll;
FrequencyTranslator translator;
FIRFilter rrc;
AGC agc;
#ifdef EXPERIMENTAL
CostasLoopSSE costas;
CostasLoopSSE costas_loop;
#else
CostasLoop costas;
CostasLoop costas_loop;
#endif
SymbolSync clock;
FengyunViterbi viterbi;
VCDUExtractor deframer;
SymbolSync clock_recovery;
BinarySlicer slicer;
FileWriter<uint8_t> out;
std::vector<BlockInterface *> blocks;

};

class GACDemodulator : public Demodulator {
template<class SymbolHandler, class Deframer>
class PSKDemodulator : public Demodulator {
public:
GACDemodulator(float SAMP_RATE, std::shared_ptr<FileReader> source, std::string ofname);
std::vector<complex> &symbols() {
return slicer.in;
}
std::vector<complex> &freq() {
return agc.in;
}
bool is_running() {
return file->neof;
}
void stop();
PSKDemodulator(float samp_rate,
float sym_rate,
size_t order,
bool suppress_carrier,
std::shared_ptr<FileReader> source,
std::string output_filename);
std::vector<complex> &symbols() { return symbol_handler.in; }
std::vector<complex> &baseband() { return agc.in; }

private:
FastDCBlocker dc;
FastDCBlocker dc_blocker;
AGC agc;
FIRFilter rrc;
#ifdef EXPERIMENTAL
CostasLoopSSE costas;
CostasLoopSSE costas_loop;
#else
CostasLoop costas;
CostasLoop costas_loop;
#endif
SymbolSync clock;
BinarySlicer slicer;
SymbolSync clock_recovery;
SymbolHandler symbol_handler;
Deframer deframer;
FileWriter<uint8_t> out;
std::vector<BlockInterface *> blocks;
};

#endif
12 changes: 12 additions & 0 deletions src/dsp/block.h
Expand Up @@ -133,4 +133,16 @@ class Block : public BlockInterface {
virtual void work([[maybe_unused]] const A *in, [[maybe_unused]] size_t n) { throw std::runtime_error("No matching function with signature void(const A* in, size_t n"); };
};

// Block where both pipes point to the same address in memory
// to simply passthrough the pipe pointer
template<typename T>
class Passthrough : public BlockInterface {
public:
Passthrough(...) : out_pipe(in_pipe) { }
void start() {}
void stop() {}
std::shared_ptr<Pipe<T>> &out_pipe;
std::shared_ptr<Pipe<T>> in_pipe;
};

#endif
2 changes: 1 addition & 1 deletion src/io/raw_impl.h
Expand Up @@ -55,7 +55,7 @@ class RawFileReader: public FileReader {
ptr[i] = std::complex<float>((float)buffer[i*2]*a + b, (float)buffer[i*2+1]*a + b);
}

neof = !stream.eof();
eof = stream.eof();
return stream.gcount()/2/sizeof(T);
}
private:
Expand Down

0 comments on commit f76ef41

Please sign in to comment.