summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias (think) <matthias@mpb.li>2012-07-11 12:00:56 +0200
committerMatthias (think) <matthias@mpb.li>2012-07-11 12:00:56 +0200
commit4b814d92fe9787bf72ed3a9632e0866f4cedd27f (patch)
treefeb407e9f2c0658dda896c332864fe6d265aaece /src
parente92f9c408634810828e75d4ad6da408e1c142195 (diff)
downloaddabmod-4b814d92fe9787bf72ed3a9632e0866f4cedd27f.tar.gz
dabmod-4b814d92fe9787bf72ed3a9632e0866f4cedd27f.tar.bz2
dabmod-4b814d92fe9787bf72ed3a9632e0866f4cedd27f.zip
added crc-dabmod patch
Diffstat (limited to 'src')
-rw-r--r--src/BlockPartitioner.cpp12
-rw-r--r--src/BlockPartitioner.h4
-rw-r--r--src/CicEqualizer.cpp19
-rw-r--r--src/DabMod.cpp156
-rw-r--r--src/DabModulator.cpp62
-rw-r--r--src/DabModulator.h14
-rw-r--r--src/Eti.h40
-rw-r--r--src/EtiReader.cpp48
-rw-r--r--src/EtiReader.h26
-rw-r--r--src/FIRFilter.cpp311
-rw-r--r--src/FIRFilter.h157
-rw-r--r--src/Makefile.am3
-rw-r--r--src/Makefile.in54
-rw-r--r--src/OutputUHD.cpp466
-rw-r--r--src/OutputUHD.h167
-rw-r--r--src/PcDebug.h4
-rw-r--r--src/TimestampDecoder.cpp210
-rw-r--r--src/TimestampDecoder.h154
18 files changed, 1839 insertions, 68 deletions
diff --git a/src/BlockPartitioner.cpp b/src/BlockPartitioner.cpp
index bb16ae2..2ae2e7a 100644
--- a/src/BlockPartitioner.cpp
+++ b/src/BlockPartitioner.cpp
@@ -29,7 +29,7 @@
#include <assert.h>
-BlockPartitioner::BlockPartitioner(unsigned mode, unsigned fct) :
+BlockPartitioner::BlockPartitioner(unsigned mode, unsigned phase) :
ModMux(ModFormat(0), ModFormat(0)),
d_mode(mode)
{
@@ -67,7 +67,7 @@ BlockPartitioner::BlockPartitioner(unsigned mode, unsigned fct) :
}
d_cifNb = 0;
// For Synchronisation purpose, count nb of CIF to drop
- d_cifInit = fct % d_cifCount;
+ d_cifPhase = phase % d_cifCount;
d_cifSize = 864 * 8;
myInputFormat.size(d_cifSize);
@@ -110,10 +110,10 @@ int BlockPartitioner::process(std::vector<Buffer*> dataIn, Buffer* dataOut)
"BlockPartitioner::process input size not valid!");
}
- // Synchronize first CIF
- if (d_cifInit != 0) {
- if (++d_cifInit == d_cifCount) {
- d_cifInit = 0;
+ // Synchronize CIF phase
+ if (d_cifPhase != 0) {
+ if (++d_cifPhase == d_cifCount) {
+ d_cifPhase = 0;
}
// Drop CIF
return 0;
diff --git a/src/BlockPartitioner.h b/src/BlockPartitioner.h
index ec788bf..021a1a6 100644
--- a/src/BlockPartitioner.h
+++ b/src/BlockPartitioner.h
@@ -35,7 +35,7 @@
class BlockPartitioner : public ModMux
{
public:
- BlockPartitioner(unsigned mode, unsigned fct);
+ BlockPartitioner(unsigned mode, unsigned phase);
virtual ~BlockPartitioner();
BlockPartitioner(const BlockPartitioner&);
BlockPartitioner& operator=(const BlockPartitioner&);
@@ -48,7 +48,7 @@ protected:
size_t d_ficSize;
size_t d_cifCount;
size_t d_cifNb;
- size_t d_cifInit;
+ size_t d_cifPhase;
size_t d_cifSize;
size_t d_outputFramesize;
size_t d_outputFramecount;
diff --git a/src/CicEqualizer.cpp b/src/CicEqualizer.cpp
index 06f4550..52da55e 100644
--- a/src/CicEqualizer.cpp
+++ b/src/CicEqualizer.cpp
@@ -36,22 +36,21 @@ CicEqualizer::CicEqualizer(size_t nbCarriers, size_t spacing, int R) :
nbCarriers, spacing, R, this);
myFilter = new float[nbCarriers];
- int M = 1;
- int N = 4;
- float pi = 4.0f * atanf(1.0f);
+ const int M = 1;
+ const int N = 4;
+ const float pi = 4.0f * atanf(1.0f);
for (size_t i = 0; i < nbCarriers; ++i) {
int k = i < (nbCarriers + 1) / 2
? i + ((nbCarriers & 1) ^ 1)
: i - (int)nbCarriers;
float angle = pi * k / spacing;
if (k == 0) {
- myFilter[i] = R;
- } else {
- myFilter[i] = sinf(angle / R) / sinf(angle * M);
- }
- myFilter[i] = fabsf(myFilter[i]) * R;
- myFilter[i] = powf(myFilter[i], N);
- myFilter[i] *= 0.5f;
+ myFilter[i] = 1.0f;
+ } else {
+ myFilter[i] = sinf(angle / R) / sinf(angle * M);
+ myFilter[i] = fabsf(myFilter[i]) * R * M;
+ myFilter[i] = powf(myFilter[i], N);
+ }
PDEBUG("HCic[%zu -> %i] = %f (%f dB) -> angle: %f\n",
i, k,myFilter[i], 20.0 * log10(myFilter[i]), angle);
}
diff --git a/src/DabMod.cpp b/src/DabMod.cpp
index db0c712..f9bc3c9 100644
--- a/src/DabMod.cpp
+++ b/src/DabMod.cpp
@@ -2,6 +2,9 @@
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Her Majesty the Queen in Right of Canada (Communications Research
Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
This file is part of CRC-DADMOD.
@@ -29,7 +32,9 @@
#include "DabModulator.h"
#include "InputMemory.h"
#include "OutputFile.h"
+#include "OutputUHD.h"
#include "PcDebug.h"
+#include "TimestampDecoder.h"
#include <complex>
#include <stdlib.h>
@@ -69,10 +74,14 @@ void printUsage(char* progName, FILE* out = stderr)
{
fprintf(out, "Usage:\n");
fprintf(out, "\t%s"
- " [input [output]]"
+ " [input]"
+ " (-f filename | -u uhddevice -F frequency) "
+ " [-G txgain]"
+ " [-o offset]"
+ " [-O offsetfile]"
+ " [-T filter_taps_file]"
" [-a amplitude]"
" [-c clockrate]"
- " [-f]"
" [-g gainMode]"
" [-h]"
" [-l]"
@@ -80,17 +89,23 @@ void printUsage(char* progName, FILE* out = stderr)
" [-r samplingRate]"
"\n", progName);
fprintf(out, "Where:\n");
- fprintf(out, "input: ETI input filename (default: stdin).\n");
- fprintf(out, "output: COFDM output filename (default: stdout).\n");
- fprintf(out, "-a: Apply amplitude gain.\n");
- fprintf(out, "-c: Set the DAC clock rate.\n");
- fprintf(out, "-f: (deprecated) Set fifo input.\n");
- fprintf(out, "-g: Set computation gain mode: "
+ fprintf(out, "input: ETI input filename (default: stdin).\n");
+ fprintf(out, "-f name: Use file output with given filename. (use /dev/stdout for standard output)\n");
+ fprintf(out, "-u device: Use UHD output with given device string. (use "" for default device)\n");
+ fprintf(out, "-F frequency: Set the transmit frequency when using UHD output. (mandatory option when using UHD)\n");
+ fprintf(out, "-G txgain: Set the transmit gain for the UHD driver (default: 0)\n");
+ fprintf(out, "-o: (UHD only) Set the timestamp offset added to the timestamp in the ETI. The offset is a double.\n");
+ fprintf(out, "-O: (UHD only) Set the file containing the timestamp offset added to the timestamp in the ETI. The file is read every six seconds, and must contain a double value.\n");
+ fprintf(out, " Specifying either -o or -O make DABMOD mute frames that do not contain a valid timestamp.\n");
+ fprintf(out, "-T taps_file: Enable filtering before the output, using the specified file containing the filter taps.\n");
+ fprintf(out, "-a gain: Apply digital amplitude gain.\n");
+ fprintf(out, "-c rate: Set the DAC clock rate and enable Cic Equalisation.\n");
+ fprintf(out, "-g: Set computation gain mode: "
"%u FIX, %u MAX, %u VAR\n", GAIN_FIX, GAIN_MAX, GAIN_VAR);
- fprintf(out, "-h: Print this help.\n");
- fprintf(out, "-l: Loop file when reach end of file.\n");
- fprintf(out, "-m: Set DAB mode: (0: auto, 1-4: force).\n");
- fprintf(out, "-r: Set output sampling rate (default: 2048000).\n");
+ fprintf(out, "-h: Print this help.\n");
+ fprintf(out, "-l: Loop file when reach end of file.\n");
+ fprintf(out, "-m mode: Set DAB mode: (0: auto, 1-4: force).\n");
+ fprintf(out, "-r rate: Set output sampling rate (default: 2048000).\n");
}
@@ -124,7 +139,15 @@ int main(int argc, char* argv[])
int ret = 0;
bool loop = false;
char* inputName;
+
char* outputName;
+ char* outputDevice;
+ int useFileOutput = 0;
+ int useUHDOutput = 0;
+ double uhdFrequency = 0.0;
+ int uhdTxGain = 0;
+ bool uhd_mute_no_timestamps = false;
+
FILE* inputFile = NULL;
uint32_t sync = 0;
uint32_t nbFrames = 0;
@@ -137,15 +160,26 @@ int main(int argc, char* argv[])
GainMode gainMode = GAIN_VAR;
Buffer data;
+ char* filterTapsFilename = NULL;
+
+ // To handle the timestamp offset of the modulator
+ struct modulator_offset_config modconf;
+ modconf.use_offset_file = false;
+ modconf.use_offset_fixed = false;
+
Flowgraph* flowgraph = NULL;
DabModulator* modulator = NULL;
InputMemory* input = NULL;
- OutputFile* output = NULL;
+ ModOutput* output = NULL;
signal(SIGINT, signalHandler);
+ // Set timezone to UTC
+ setenv("TZ", "", 1);
+ tzset();
+
while (true) {
- int c = getopt(argc, argv, "a:c:fg:hlm:r:V");
+ int c = getopt(argc, argv, "a:c:f:F:g:G:hlm:o:O:r:T:u:V");
if (c == -1) {
break;
}
@@ -157,20 +191,60 @@ int main(int argc, char* argv[])
clockRate = strtol(optarg, NULL, 0);
break;
case 'f':
- fprintf(stderr, "Option -f deprecated!\n");
+ if (useUHDOutput) {
+ fprintf(stderr, "Options -u and -f are mutually exclusive\n");
+ goto END_MAIN;
+ }
+ outputName = optarg;
+ useFileOutput = 1;
+ break;
+ case 'F':
+ uhdFrequency = strtof(optarg, NULL);
break;
case 'g':
gainMode = (GainMode)strtol(optarg, NULL, 0);
break;
+ case 'G':
+ uhdTxGain = (int)strtol(optarg, NULL, 10);
+ break;
case 'l':
loop = true;
break;
+ case 'o':
+ if (modconf.use_offset_file)
+ {
+ fprintf(stderr, "Options -o and -O are mutually exclusive\n");
+ goto END_MAIN;
+ }
+ modconf.use_offset_fixed = true;
+ modconf.offset_fixed = strtod(optarg, NULL);
+ break;
+ case 'O':
+ if (modconf.use_offset_fixed)
+ {
+ fprintf(stderr, "Options -o and -O are mutually exclusive\n");
+ goto END_MAIN;
+ }
+ modconf.use_offset_file = true;
+ modconf.offset_filename = optarg;
+ break;
case 'm':
dabMode = strtol(optarg, NULL, 0);
break;
case 'r':
outputRate = strtol(optarg, NULL, 0);
break;
+ case 'T':
+ filterTapsFilename = optarg;
+ break;
+ case 'u':
+ if (useFileOutput) {
+ fprintf(stderr, "Options -u and -f are mutually exclusive\n");
+ goto END_MAIN;
+ }
+ outputDevice = optarg;
+ useUHDOutput = 1;
+ break;
case 'V':
printVersion();
goto END_MAIN;
@@ -186,18 +260,22 @@ int main(int argc, char* argv[])
goto END_MAIN;
}
}
+
+ // When using offset, enable frame muting
+ uhd_mute_no_timestamps = (modconf.use_offset_file || modconf.use_offset_fixed);
+
+ if (!(modconf.use_offset_file || modconf.use_offset_fixed)) {
+ fprintf(stderr, "No Modulator offset defined, setting to 0\n");
+ modconf.use_offset_fixed = true;
+ modconf.offset_fixed = 0;
+ }
+
// Setting ETI input filename
if (optind < argc) {
inputName = argv[optind++];
} else {
inputName = (char*)"/dev/stdin";
}
- // Setting COFDM output filename
- if (optind < argc) {
- outputName = argv[optind++];
- } else {
- outputName = (char*)"/dev/stdout";
- }
// Checking unused arguments
if (optind != argc) {
fprintf(stderr, "Invalid arguments:");
@@ -210,15 +288,25 @@ int main(int argc, char* argv[])
goto END_MAIN;
}
+ if (!useFileOutput && !useUHDOutput) {
+ fprintf(stderr, "Must specify output !");
+ goto END_MAIN;
+ }
+
// Print settings
fprintf(stderr, "Input\n");
fprintf(stderr, " Name: %s\n", inputName);
fprintf(stderr, "Output\n");
- fprintf(stderr, " Name: %s\n", outputName);
+ if (useUHDOutput) {
+ fprintf(stderr, " UHD, Device: %s\n", outputDevice);
+ }
+ else if (useFileOutput) {
+ fprintf(stderr, " Name: %s\n", outputName);
+ }
fprintf(stderr, " Sampling rate: ");
if (outputRate > 1000) {
if (outputRate > 1000000) {
- fprintf(stderr, "%.3g mHz\n", outputRate / 1000000.0f);
+ fprintf(stderr, "%.3g MHz\n", outputRate / 1000000.0f);
} else {
fprintf(stderr, "%.3g kHz\n", outputRate / 1000.0f);
}
@@ -234,18 +322,32 @@ int main(int argc, char* argv[])
ret = -1;
goto END_MAIN;
}
- // Opening COFDM output file
- if (outputName != NULL) {
- output = new OutputFile(outputName);
+
+ if (useFileOutput) {
+ // Opening COFDM output file
+ if (outputName != NULL) {
+ fprintf(stderr, "Using file output\n");
+ output = new OutputFile(outputName);
+ }
+ }
+ else if (useUHDOutput) {
+ fprintf(stderr, "Using UHD output\n");
+ amplitude /= 32000.0f;
+ output = new OutputUHD(outputDevice, outputRate, uhdFrequency, uhdTxGain, uhd_mute_no_timestamps);
}
flowgraph = new Flowgraph();
data.setLength(6144);
input = new InputMemory(&data);
- modulator = new DabModulator(outputRate, clockRate, dabMode, gainMode, amplitude);
+ modulator = new DabModulator(modconf, outputRate, clockRate,
+ dabMode, gainMode, amplitude, filterTapsFilename);
flowgraph->connect(input, modulator);
flowgraph->connect(modulator, output);
+ if (useUHDOutput) {
+ ((OutputUHD*)output)->setETIReader(modulator->getEtiReader());
+ }
+
try {
while (running) {
enum EtiStreamType {
diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp
index 6396a4d..e186fd3 100644
--- a/src/DabModulator.cpp
+++ b/src/DabModulator.cpp
@@ -2,6 +2,9 @@
Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
Her Majesty the Queen in Right of Canada (Communications Research
Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
This file is part of CRC-DADMOD.
@@ -38,19 +41,27 @@
#include "GuardIntervalInserter.h"
#include "Resampler.h"
#include "ConvEncoder.h"
+#include "FIRFilter.h"
#include "PuncturingEncoder.h"
#include "TimeInterleaver.h"
+#include "TimestampDecoder.h"
-DabModulator::DabModulator(unsigned outputRate, unsigned clockRate,
- unsigned dabMode, GainMode gainMode, float factor) :
+DabModulator::DabModulator(
+ struct modulator_offset_config& modconf,
+ unsigned outputRate, unsigned clockRate,
+ unsigned dabMode, GainMode gainMode, float factor,
+ char* filterTapsFilename
+ ) :
ModCodec(ModFormat(1), ModFormat(0)),
myOutputRate(outputRate),
myClockRate(clockRate),
myDabMode(dabMode),
myGainMode(gainMode),
myFactor(factor),
- myFlowgraph(NULL)
+ myEtiReader(EtiReader(modconf)),
+ myFlowgraph(NULL),
+ myFilterTapsFilename(filterTapsFilename)
{
PDEBUG("DabModulator::DabModulator(%u, %u, %u, %u) @ %p\n",
outputRate, clockRate, dabMode, gainMode, this);
@@ -147,12 +158,13 @@ int DabModulator::process(Buffer* const dataIn, Buffer* dataOut)
OfdmGenerator* cifOfdm = NULL;
GainControl* cifGain = NULL;
GuardIntervalInserter* cifGuard = NULL;
+ FIRFilter* cifFilter = NULL;
Resampler* cifRes = NULL;
cifPrbs = new PrbsGenerator(864 * 8, 0x110);
cifMux = new FrameMultiplexer(myFicSizeOut + 864 * 8,
&myEtiReader.getSubchannels());
- cifPart = new BlockPartitioner(mode, myEtiReader.getFct());
+ cifPart = new BlockPartitioner(mode, myEtiReader.getFp());
cifMap = new QpskSymbolMapper(myNbCarriers);
cifRef = new PhaseReference(mode);
cifFreq = new FrequencyInterleaver(mode);
@@ -162,15 +174,28 @@ int DabModulator::process(Buffer* const dataIn, Buffer* dataOut)
(1 + myNbSymbols) * myNbCarriers * sizeof(complexf));
if (myClockRate) {
- cifCicEq = new CicEqualizer(myNbCarriers,
- (float)mySpacing * (float)myOutputRate / 2048000.0f,
- myClockRate / myOutputRate);
+ unsigned ratio = myClockRate / myOutputRate;
+ ratio /= 4; // FPGA DUC
+ if (myClockRate == 400000000) { // USRP2
+ if (ratio & 1) { // odd
+ cifCicEq = new CicEqualizer(myNbCarriers,
+ (float)mySpacing * (float)myOutputRate / 2048000.0f,
+ ratio);
+ } // even, no filter
+ } else {
+ cifCicEq = new CicEqualizer(myNbCarriers,
+ (float)mySpacing * (float)myOutputRate / 2048000.0f,
+ ratio);
+ }
}
cifOfdm = new OfdmGenerator((1 + myNbSymbols), myNbCarriers, mySpacing);
cifGain = new GainControl(mySpacing, myGainMode, myFactor);
cifGuard = new GuardIntervalInserter(myNbSymbols, mySpacing,
myNullSize, mySymSize);
+ if (myFilterTapsFilename != NULL) {
+ cifFilter = new FIRFilter(myFilterTapsFilename);
+ }
myOutput = new OutputMemory();
if (myOutputRate != 2048000) {
@@ -312,11 +337,24 @@ int DabModulator::process(Buffer* const dataIn, Buffer* dataOut)
}
myFlowgraph->connect(cifOfdm, cifGain);
myFlowgraph->connect(cifGain, cifGuard);
- if (cifRes != NULL) {
- myFlowgraph->connect(cifGuard, cifRes);
- myFlowgraph->connect(cifRes, myOutput);
- } else {
- myFlowgraph->connect(cifGuard, myOutput);
+
+ if (myFilterTapsFilename != NULL) {
+ myFlowgraph->connect(cifGuard, cifFilter);
+ if (cifRes != NULL) {
+ myFlowgraph->connect(cifFilter, cifRes);
+ myFlowgraph->connect(cifRes, myOutput);
+ } else {
+ myFlowgraph->connect(cifFilter, myOutput);
+ }
+ }
+ else { //no filtering
+ if (cifRes != NULL) {
+ myFlowgraph->connect(cifGuard, cifRes);
+ myFlowgraph->connect(cifRes, myOutput);
+ } else {
+ myFlowgraph->connect(cifGuard, myOutput);
+ }
+
}
}
diff --git a/src/DabModulator.h b/src/DabModulator.h
index 9112960..0eecfa4 100644
--- a/src/DabModulator.h
+++ b/src/DabModulator.h
@@ -2,6 +2,9 @@
Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
Her Majesty the Queen in Right of Canada (Communications Research
Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
This file is part of CRC-DADMOD.
@@ -39,14 +42,20 @@
class DabModulator : public ModCodec
{
public:
- DabModulator(unsigned outputRate = 2048000, unsigned clockRate = 0,
- unsigned dabMode = 0, GainMode gainMode = GAIN_VAR, float factor = 1.0);
+ DabModulator(
+ struct modulator_offset_config& modconf,
+ unsigned outputRate = 2048000, unsigned clockRate = 0,
+ unsigned dabMode = 0, GainMode gainMode = GAIN_VAR,
+ float factor = 1.0, char* filterTapsFilename = NULL);
DabModulator(const DabModulator& copy);
virtual ~DabModulator();
int process(Buffer* const dataIn, Buffer* dataOut);
const char* name() { return "DabModulator"; }
+ /* Required to get the timestamp */
+ EtiReader* getEtiReader() { return &myEtiReader; }
+
protected:
void setMode(unsigned mode);
@@ -58,6 +67,7 @@ protected:
EtiReader myEtiReader;
Flowgraph* myFlowgraph;
OutputMemory* myOutput;
+ char* myFilterTapsFilename;
size_t myNbSymbols;
size_t myNbCarriers;
diff --git a/src/Eti.h b/src/Eti.h
index fd5fc88..698f9cb 100644
--- a/src/Eti.h
+++ b/src/Eti.h
@@ -1,6 +1,9 @@
/*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
This file is part of CRC-DADMOD.
@@ -39,6 +42,7 @@ typedef DWORD32 uint32_t;
# define PACKED __attribute__ ((packed))
#endif
+#include <time.h>
//definitions des structures des champs du ETI(NI, G703)
@@ -92,5 +96,41 @@ struct eti_TIST {
uint32_t TIST;
} PACKED;
+struct eti_MNSC_TIME_0 {
+ uint32_t type:4;
+ uint32_t identifier:4;
+ uint32_t rfa:8;
+} PACKED;
+
+struct eti_MNSC_TIME_1 {
+ uint32_t second_unit:4;
+ uint32_t second_tens:3;
+ uint32_t accuracy:1;
+
+ uint32_t minute_unit:4;
+ uint32_t minute_tens:3;
+ uint32_t sync_to_frame:1;
+} PACKED;
+
+struct eti_MNSC_TIME_2 {
+ uint32_t hour_unit:4;
+ uint32_t hour_tens:4;
+
+ uint32_t day_unit:4;
+ uint32_t day_tens:4;
+} PACKED;
+
+struct eti_MNSC_TIME_3 {
+ uint32_t month_unit:4;
+ uint32_t month_tens:4;
+
+ uint32_t year_unit:4;
+ uint32_t year_tens:4;
+} PACKED;
+
+struct eti_extension_TIME {
+ uint32_t TIME_SECONDS;
+} PACKED;
+
#endif // ETI_H
diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp
index d65dbe0..7e7d4af 100644
--- a/src/EtiReader.cpp
+++ b/src/EtiReader.cpp
@@ -1,6 +1,9 @@
/*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty
the Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
This file is part of CRC-DADMOD.
@@ -21,10 +24,12 @@
#include "EtiReader.h"
#include "PcDebug.h"
+#include "TimestampDecoder.h"
#include <stdexcept>
#include <sys/types.h>
#include <string.h>
+#include <arpa/inet.h>
enum ETI_READER_STATE {
@@ -42,15 +47,15 @@ enum ETI_READER_STATE {
};
-EtiReader::EtiReader() : state(EtiReaderStateSync),
- myFicSource(NULL)
+EtiReader::EtiReader(struct modulator_offset_config& modconf) :
+ state(EtiReaderStateSync), myFicSource(NULL)
{
PDEBUG("EtiReader::EtiReader()\n");
myCurrentFrame = 0;
+ myTimestampDecoder = new TimestampDecoder(modconf);
}
-
EtiReader::~EtiReader()
{
PDEBUG("EtiReader::~EtiReader()\n");
@@ -76,9 +81,9 @@ unsigned EtiReader::getMode()
}
-unsigned EtiReader::getFct()
+unsigned EtiReader::getFp()
{
- return eti_fc.FCT;
+ return eti_fc.FP;
}
@@ -266,5 +271,38 @@ int EtiReader::process(Buffer* dataIn)
}
}
+ // Update timestamps
+ myTimestampDecoder->updateTimestampEti(eti_fc.FP & 0x3,
+ eti_eoh.MNSC,
+ getPPSOffset());
+
+ if (getFCT() % 125 == 0) //every 3 seconds is fine enough
+ {
+ myTimestampDecoder->updateModulatorOffset();
+ }
+
return dataIn->getLength() - input_size;
}
+
+bool EtiReader::sourceContainsTimestamp()
+{
+ return (ntohl(eti_tist.TIST) & 0xFFFFFF) != 0xFFFFFF;
+ /* See ETS 300 799, Annex C.2.2 */
+}
+
+double EtiReader::getPPSOffset()
+{
+ if (!sourceContainsTimestamp())
+ return 0.0;
+
+ uint32_t timestamp = ntohl(eti_tist.TIST) & 0xFFFFFF;
+ //fprintf(stderr, "TIST 0x%x\n", timestamp);
+ double pps = timestamp / 16384000.0; // seconds
+
+ return pps;
+}
+
+uint32_t EtiReader::getFCT()
+{
+ return eti_fc.FCT;
+}
diff --git a/src/EtiReader.h b/src/EtiReader.h
index 7e172db..5f4897b 100644
--- a/src/EtiReader.h
+++ b/src/EtiReader.h
@@ -1,6 +1,9 @@
/*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty
the Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
*/
/*
This file is part of CRC-DADMOD.
@@ -30,6 +33,7 @@
#include "Eti.h"
#include "FicSource.h"
#include "SubchannelSource.h"
+#include "TimestampDecoder.h"
#include <vector>
#include <stdint.h>
@@ -51,21 +55,39 @@ protected:
eti_TIST eti_tist;
FicSource* myFicSource;
std::vector<SubchannelSource*> mySources;
+ TimestampDecoder* myTimestampDecoder;
public:
- EtiReader();
+ EtiReader(struct modulator_offset_config& modconf);
virtual ~EtiReader();
EtiReader(const EtiReader&);
EtiReader& operator=(const EtiReader&);
FicSource* getFic();
unsigned getMode();
- unsigned getFct();
+ unsigned getFp();
const std::vector<SubchannelSource*>& getSubchannels();
int process(Buffer* dataIn);
+ void calculateTimestamp(struct frame_timestamp& ts)
+ {
+ myTimestampDecoder->calculateTimestamp(ts);
+ }
+
+ /* Return the frame counter */
+ uint32_t getFCT();
+
+ /* Returns true if we have valid time stamps in the ETI*/
+ bool sourceContainsTimestamp();
+
+protected:
+ /* Transform the ETI TIST to a PPS offset in ms */
+ double getPPSOffset();
+
private:
size_t myCurrentFrame;
+ bool time_ext_enabled;
+ unsigned long timestamp_seconds;
};
diff --git a/src/FIRFilter.cpp b/src/FIRFilter.cpp
new file mode 100644
index 0000000..2ba4294
--- /dev/null
+++ b/src/FIRFilter.cpp
@@ -0,0 +1,311 @@
+/*
+ Copyright (C) 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in
+ Right of Canada (Communications Research Center Canada)
+
+ Written by
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
+
+ This block implements a FIR filter. The real filter taps are given
+ as floats, and the block can take advantage of SSE.
+ For better performance, filtering is done in another thread, leading
+ to a pipeline delay of two calls to FIRFilter::process
+ */
+/*
+ This file is part of CRC-DADMOD.
+
+ CRC-DADMOD is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ CRC-DADMOD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CRC-DADMOD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "FIRFilter.h"
+#include "PcDebug.h"
+
+#include <stdio.h>
+#include <stdexcept>
+
+#include <iostream>
+#include <fstream>
+
+#ifdef __SSE__
+# include <xmmintrin.h>
+#endif
+
+
+#include <sys/time.h>
+
+void FIRFilterWorker::process(struct FIRFilterWorkerData *fwd)
+{
+ size_t i;
+ struct timespec time_start;
+ struct timespec time_end;
+
+ // This thread creates the dataOut buffer, and deletes
+ // the incoming buffer
+
+ while(running) {
+ Buffer* dataIn;
+ fwd->input_queue.wait_and_pop(dataIn);
+
+ Buffer* dataOut;
+ dataOut = new Buffer();
+ dataOut->setLength(dataIn->getLength());
+
+ PDEBUG("FIRFilterWorker: dataIn->getLength() %d\n", dataIn->getLength());
+
+#if __SSE__
+ // The SSE accelerated version cannot work on the complex values,
+ // it is necessary to do the convolution on the real and imaginary
+ // parts separately. Thankfully, the taps are real, simplifying the
+ // procedure.
+
+ const float* in = reinterpret_cast<const float*>(dataIn->getData());
+ float* out = reinterpret_cast<float*>(dataOut->getData());
+ size_t sizeIn = dataIn->getLength() / sizeof(float);
+
+ if ((uintptr_t)(&out[0]) % 16 != 0) {
+ fprintf(stderr, "FIRFilterWorker: out not aligned %p ", out);
+ throw std::runtime_error("FIRFilterWorker: out not aligned");
+ }
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_start);
+
+ __m128 SSEout;
+ __m128 SSEtaps;
+ __m128 SSEin;
+ for (i = 0; i < sizeIn - 2*fwd->n_taps; i += 4) {
+ SSEout = _mm_setr_ps(0,0,0,0);
+
+ for (int j = 0; j < fwd->n_taps; j++) {
+ if ((uintptr_t)(&in[i+2*j]) % 16 == 0) {
+ SSEin = _mm_load_ps(&in[i+2*j]); //faster when aligned
+ }
+ else {
+ SSEin = _mm_loadu_ps(&in[i+2*j]);
+ }
+
+ SSEtaps = _mm_load1_ps(&fwd->taps[j]);
+
+ SSEout = _mm_add_ps(SSEout, _mm_mul_ps(SSEin, SSEtaps));
+ }
+ _mm_store_ps(&out[i], SSEout);
+ }
+
+ for (; i < sizeIn; i++) {
+ out[i] = 0.0;
+ for (int j = 0; i+2*j < sizeIn; j++) {
+ out[i] += in[i+2*j] * fwd->taps[j];
+ }
+ }
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_end);
+
+#else
+ // No SSE ? Loop unrolling should make this faster. As for the SSE,
+ // the real and imaginary parts are calculated separately.
+ const float* in = reinterpret_cast<const float*>(dataIn->getData());
+ float* out = reinterpret_cast<float*>(dataOut->getData());
+ size_t sizeIn = dataIn->getLength() / sizeof(float);
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_start);
+
+ // Convolve by aligning both frame and taps at zero.
+ for (i = 0; i < sizeIn - 2*fwd->n_taps; i += 4) {
+ out[i] = 0.0;
+ out[i+1] = 0.0;
+ out[i+2] = 0.0;
+ out[i+3] = 0.0;
+
+ for (int j = 0; j < fwd->n_taps; j++) {
+ out[i] += in[i + 2*j] * fwd->taps[j];
+ out[i+1] += in[i+1 + 2*j] * fwd->taps[j];
+ out[i+2] += in[i+2 + 2*j] * fwd->taps[j];
+ out[i+3] += in[i+3 + 2*j] * fwd->taps[j];
+ }
+ }
+
+ // At the end of the frame, we cut the convolution off.
+ // The beginning of the next frame starts with a NULL symbol
+ // anyway.
+ for (; i < sizeIn; i++) {
+ out[i] = 0.0;
+ for (int j = 0; i+2*j < sizeIn; j++) {
+ out[i] += in[i+2*j] * fwd->taps[j];
+ }
+ }
+
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time_end);
+
+
+#endif
+
+ // The following implementations are for debugging only.
+#if 0
+ // Same thing as above, without loop unrolling. For debugging.
+ const float* in = reinterpret_cast<const float*>(dataIn->getData());
+ float* out = reinterpret_cast<float*>(dataOut->getData());
+ size_t sizeIn = dataIn->getLength() / sizeof(float);
+
+ for (i = 0; i < sizeIn - 2*fwd->n_taps; i += 1) {
+ out[i] = 0.0;
+
+ for (int j = 0; j < fwd->n_taps; j++) {
+ out[i] += in[i+2*j] * fwd->taps[j];
+ }
+ }
+
+ for (; i < sizeIn; i++) {
+ out[i] = 0.0;
+ for (int j = 0; i+2*j < sizeIn; j++) {
+ out[i] += in[i+2*j] * fwd->taps[j];
+ }
+ }
+
+#elif 0
+ // An unrolled loop, but this time, the input data is cast to complex float.
+ // Makes indices more natural. For debugging.
+ const complexf* in = reinterpret_cast<const complexf*>(dataIn->getData());
+ complexf* out = reinterpret_cast<complexf*>(dataOut->getData());
+ size_t sizeIn = dataIn->getLength() / sizeof(complexf);
+
+ for (i = 0; i < sizeIn - fwd->n_taps; i += 4) {
+ out[i] = 0.0;
+ out[i+1] = 0.0;
+ out[i+2] = 0.0;
+ out[i+3] = 0.0;
+
+ for (int j = 0; j < fwd->n_taps; j++) {
+ out[i] += in[i+j ] * fwd->taps[j];
+ out[i+1] += in[i+1+j] * fwd->taps[j];
+ out[i+2] += in[i+2+j] * fwd->taps[j];
+ out[i+3] += in[i+3+j] * fwd->taps[j];
+ }
+ }
+
+ for (; i < sizeIn; i++) {
+ out[i] = 0.0;
+ for (int j = 0; j+i < sizeIn; j++) {
+ out[i] += in[i+j] * fwd->taps[j];
+ }
+ }
+
+#elif 0
+ // Simple implementation. Slow. For debugging.
+ const complexf* in = reinterpret_cast<const complexf*>(dataIn->getData());
+ complexf* out = reinterpret_cast<complexf*>(dataOut->getData());
+ size_t sizeIn = dataIn->getLength() / sizeof(complexf);
+
+ for (i = 0; i < sizeIn - fwd->n_taps; i += 1) {
+ out[i] = 0.0;
+
+ for (int j = 0; j < fwd->n_taps; j++) {
+ out[i] += in[i+j ] * fwd->taps[j];
+ }
+ }
+
+ for (; i < sizeIn; i++) {
+ out[i] = 0.0;
+ for (int j = 0; j+i < sizeIn; j++) {
+ out[i] += in[i+j] * fwd->taps[j];
+ }
+ }
+#endif
+
+ calculationTime += (time_end.tv_sec - time_start.tv_sec) * 1000000000L +
+ time_end.tv_nsec - time_start.tv_nsec;
+ fwd->output_queue.push(dataOut);
+ delete dataIn;
+ }
+}
+
+
+FIRFilter::FIRFilter(char* taps_file) :
+ ModCodec(ModFormat(sizeof(complexf)), ModFormat(sizeof(complexf)))
+{
+ PDEBUG("FIRFilter::FIRFilter(%s) @ %p\n",
+ taps_file, this);
+
+ number_of_runs = 0;
+
+ std::ifstream taps_fstream(taps_file);
+ if(!taps_fstream) {
+ fprintf(stderr, "FIRFilter: file %s could not be opened !\n", taps_file);
+ throw std::runtime_error("FIRFilter: Could not open file with taps! ");
+ }
+ int n_taps;
+ taps_fstream >> n_taps;
+
+ my_Ntaps = n_taps;
+
+ fprintf(stderr, "FIRFilter: Reading %d taps...\n", my_Ntaps);
+
+ myFilter = new float[my_Ntaps];
+
+ int n;
+ for (n = 0; n < n_taps; n++) {
+ taps_fstream >> myFilter[n];
+ PDEBUG("FIRFilter: tap: %f\n", myFilter[n] );
+ if (taps_fstream.eof()) {
+ fprintf(stderr, "FIRFilter: file %s should contains %d taps, but EOF reached "\
+ "after %d taps !\n", taps_file, n_taps, n);
+ throw std::runtime_error("FIRFilter: filtertaps file invalid ! ");
+ }
+ }
+
+ firwd.taps = myFilter;
+ firwd.n_taps = my_Ntaps;
+
+ PDEBUG("FIRFilter: Starting worker\n" );
+ worker.start(&firwd);
+}
+
+
+FIRFilter::~FIRFilter()
+{
+ PDEBUG("FIRFilter::~FIRFilter() @ %p\n", this);
+
+ worker.stop();
+
+ if (myFilter != NULL) {
+ delete[] myFilter;
+ }
+}
+
+
+int FIRFilter::process(Buffer* const dataIn, Buffer* dataOut)
+{
+ PDEBUG("FIRFilter::process(dataIn: %p, dataOut: %p)\n",
+ dataIn, dataOut);
+
+ // This thread creates the dataIn buffer, and deletes
+ // the outgoing buffer
+
+ Buffer* inbuffer = new Buffer(dataIn->getLength(), dataIn->getData());
+
+ firwd.input_queue.push(inbuffer);
+
+ if (number_of_runs > 2) {
+ Buffer* outbuffer;
+ firwd.output_queue.wait_and_pop(outbuffer);
+
+ dataOut->setData(outbuffer->getData(), outbuffer->getLength());
+
+ delete outbuffer;
+ }
+ else {
+ dataOut->setLength(dataIn->getLength());
+ memset(dataOut->getData(), 0, dataOut->getLength());
+ number_of_runs++;
+ }
+
+ return dataOut->getLength();
+
+}
diff --git a/src/FIRFilter.h b/src/FIRFilter.h
new file mode 100644
index 0000000..397301d
--- /dev/null
+++ b/src/FIRFilter.h
@@ -0,0 +1,157 @@
+/*
+ Copyright (C) 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in
+ Right of Canada (Communications Research Center Canada)
+
+ Written by
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of CRC-DADMOD.
+
+ CRC-DADMOD is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ CRC-DADMOD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CRC-DADMOD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FIRFILTER_H
+#define FIRFILTER_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <boost/thread.hpp>
+#include <queue>
+
+#include "ModCodec.h"
+
+#include <sys/types.h>
+#include <complex>
+
+#include <time.h>
+#include <cstdio>
+
+//#define MDEBUG(fmt, args...) fprintf (stderr, fmt , ## args)
+#define MDEBUG(fmt, args...)
+
+typedef std::complex<float> complexf;
+
+template<typename T>
+class ThreadsafeQueue
+{
+private:
+ std::queue<T> the_queue;
+ mutable boost::mutex the_mutex;
+ boost::condition_variable the_condition_variable;
+public:
+ void push(T const& val)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ the_queue.push(val);
+ lock.unlock();
+ the_condition_variable.notify_one();
+ }
+
+ bool empty() const
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ return the_queue.empty();
+ }
+
+ bool try_pop(T& popped_value)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ if(the_queue.empty())
+ {
+ return false;
+ }
+
+ popped_value = the_queue.front();
+ the_queue.pop();
+ return true;
+ }
+
+ void wait_and_pop(T& popped_value)
+ {
+ boost::mutex::scoped_lock lock(the_mutex);
+ while(the_queue.empty())
+ {
+ the_condition_variable.wait(lock);
+ }
+
+ popped_value = the_queue.front();
+ the_queue.pop();
+ }
+};
+
+struct FIRFilterWorkerData {
+ ThreadsafeQueue<Buffer*> input_queue;
+ ThreadsafeQueue<Buffer*> output_queue;
+ float* taps;
+ int n_taps;
+};
+
+class FIRFilterWorker {
+ public:
+ FIRFilterWorker () {
+ running = false;
+ calculationTime = 0;
+ }
+
+ ~FIRFilterWorker() {
+ MDEBUG("~FIRFilterWorker: Total elapsed thread time filtering: %zu\n", calculationTime);
+ }
+
+ void start(struct FIRFilterWorkerData *firworkerdata) {
+ running = true;
+ fir_thread = boost::thread(&FIRFilterWorker::process, this, firworkerdata);
+ }
+
+ void stop() {
+ running = false;
+ fir_thread.interrupt();
+ fir_thread.join();
+ }
+
+ void process(struct FIRFilterWorkerData *fwd);
+
+
+ private:
+ time_t calculationTime;
+ struct FIRFilterWorkerData *workerdata;
+ bool running;
+ boost::thread fir_thread;
+};
+
+
+class FIRFilter : public ModCodec
+{
+public:
+ FIRFilter(char* taps_file);
+ virtual ~FIRFilter();
+ FIRFilter(const FIRFilter&);
+ FIRFilter& operator=(const FIRFilter&);
+
+ int process(Buffer* const dataIn, Buffer* dataOut);
+ const char* name() { return "FIRFilter"; }
+
+protected:
+ int my_Ntaps;
+ float* myFilter;
+
+ FIRFilterWorker worker;
+ int number_of_runs;
+ struct FIRFilterWorkerData firwd;
+};
+
+
+#endif //FIRFILTER_H
diff --git a/src/Makefile.am b/src/Makefile.am
index a46bec5..c82842c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ crc_dabmod_SOURCES = DabMod.cpp \
EtiReader.cpp EtiReader.h \
Eti.cpp Eti.h \
FicSource.cpp FicSource.h \
+ FIRFilter.cpp FIRFilter.h \
ModInput.cpp ModInput.h \
PuncturingRule.cpp PuncturingRule.h \
PuncturingEncoder.cpp PuncturingEncoder.h \
@@ -54,6 +55,8 @@ crc_dabmod_SOURCES = DabMod.cpp \
Flowgraph.cpp Flowgraph.h \
GainControl.cpp GainControl.h \
OutputMemory.cpp OutputMemory.h \
+ TimestampDecoder.h TimestampDecoder.cpp \
+ OutputUHD.cpp OutputUHD.h \
ModOutput.cpp ModOutput.h \
InputMemory.cpp InputMemory.h \
OutputFile.cpp OutputFile.h \
diff --git a/src/Makefile.in b/src/Makefile.in
index 5a95e45..6620494 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -73,14 +73,16 @@ am_crc_dabmod_OBJECTS = crc_dabmod-DabMod.$(OBJEXT) \
crc_dabmod-Buffer.$(OBJEXT) crc_dabmod-ModCodec.$(OBJEXT) \
crc_dabmod-ModPlugin.$(OBJEXT) crc_dabmod-ModFormat.$(OBJEXT) \
crc_dabmod-EtiReader.$(OBJEXT) crc_dabmod-Eti.$(OBJEXT) \
- crc_dabmod-FicSource.$(OBJEXT) crc_dabmod-ModInput.$(OBJEXT) \
+ crc_dabmod-FicSource.$(OBJEXT) crc_dabmod-FIRFilter.$(OBJEXT) \
+ crc_dabmod-ModInput.$(OBJEXT) \
crc_dabmod-PuncturingRule.$(OBJEXT) \
crc_dabmod-PuncturingEncoder.$(OBJEXT) \
crc_dabmod-SubchannelSource.$(OBJEXT) \
crc_dabmod-Flowgraph.$(OBJEXT) \
crc_dabmod-GainControl.$(OBJEXT) \
crc_dabmod-OutputMemory.$(OBJEXT) \
- crc_dabmod-ModOutput.$(OBJEXT) \
+ crc_dabmod-TimestampDecoder.$(OBJEXT) \
+ crc_dabmod-OutputUHD.$(OBJEXT) crc_dabmod-ModOutput.$(OBJEXT) \
crc_dabmod-InputMemory.$(OBJEXT) \
crc_dabmod-OutputFile.$(OBJEXT) \
crc_dabmod-FrameMultiplexer.$(OBJEXT) \
@@ -263,6 +265,7 @@ crc_dabmod_SOURCES = DabMod.cpp \
EtiReader.cpp EtiReader.h \
Eti.cpp Eti.h \
FicSource.cpp FicSource.h \
+ FIRFilter.cpp FIRFilter.h \
ModInput.cpp ModInput.h \
PuncturingRule.cpp PuncturingRule.h \
PuncturingEncoder.cpp PuncturingEncoder.h \
@@ -270,6 +273,8 @@ crc_dabmod_SOURCES = DabMod.cpp \
Flowgraph.cpp Flowgraph.h \
GainControl.cpp GainControl.h \
OutputMemory.cpp OutputMemory.h \
+ TimestampDecoder.h TimestampDecoder.cpp \
+ OutputUHD.cpp OutputUHD.h \
ModOutput.cpp ModOutput.h \
InputMemory.cpp InputMemory.h \
OutputFile.cpp OutputFile.h \
@@ -417,6 +422,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-DifferentialModulator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-Eti.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-EtiReader.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-FIRFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-FicSource.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-Flowgraph.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-FrameMultiplexer.Po@am__quote@
@@ -434,6 +440,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-OfdmGenerator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-OutputFile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-OutputMemory.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-OutputUHD.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-PhaseReference.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-PrbsGenerator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-PuncturingEncoder.Po@am__quote@
@@ -443,6 +450,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-SignalMultiplexer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-SubchannelSource.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-TimeInterleaver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-TimestampDecoder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-kiss_fft.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-kiss_fftr.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc_dabmod-kiss_fftsimd.Po@am__quote@
@@ -658,6 +666,20 @@ crc_dabmod-FicSource.obj: FicSource.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-FicSource.obj `if test -f 'FicSource.cpp'; then $(CYGPATH_W) 'FicSource.cpp'; else $(CYGPATH_W) '$(srcdir)/FicSource.cpp'; fi`
+crc_dabmod-FIRFilter.o: FIRFilter.cpp
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-FIRFilter.o -MD -MP -MF $(DEPDIR)/crc_dabmod-FIRFilter.Tpo -c -o crc_dabmod-FIRFilter.o `test -f 'FIRFilter.cpp' || echo '$(srcdir)/'`FIRFilter.cpp
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-FIRFilter.Tpo $(DEPDIR)/crc_dabmod-FIRFilter.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FIRFilter.cpp' object='crc_dabmod-FIRFilter.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-FIRFilter.o `test -f 'FIRFilter.cpp' || echo '$(srcdir)/'`FIRFilter.cpp
+
+crc_dabmod-FIRFilter.obj: FIRFilter.cpp
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-FIRFilter.obj -MD -MP -MF $(DEPDIR)/crc_dabmod-FIRFilter.Tpo -c -o crc_dabmod-FIRFilter.obj `if test -f 'FIRFilter.cpp'; then $(CYGPATH_W) 'FIRFilter.cpp'; else $(CYGPATH_W) '$(srcdir)/FIRFilter.cpp'; fi`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-FIRFilter.Tpo $(DEPDIR)/crc_dabmod-FIRFilter.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='FIRFilter.cpp' object='crc_dabmod-FIRFilter.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-FIRFilter.obj `if test -f 'FIRFilter.cpp'; then $(CYGPATH_W) 'FIRFilter.cpp'; else $(CYGPATH_W) '$(srcdir)/FIRFilter.cpp'; fi`
+
crc_dabmod-ModInput.o: ModInput.cpp
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-ModInput.o -MD -MP -MF $(DEPDIR)/crc_dabmod-ModInput.Tpo -c -o crc_dabmod-ModInput.o `test -f 'ModInput.cpp' || echo '$(srcdir)/'`ModInput.cpp
@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-ModInput.Tpo $(DEPDIR)/crc_dabmod-ModInput.Po
@@ -756,6 +778,34 @@ crc_dabmod-OutputMemory.obj: OutputMemory.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-OutputMemory.obj `if test -f 'OutputMemory.cpp'; then $(CYGPATH_W) 'OutputMemory.cpp'; else $(CYGPATH_W) '$(srcdir)/OutputMemory.cpp'; fi`
+crc_dabmod-TimestampDecoder.o: TimestampDecoder.cpp
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-TimestampDecoder.o -MD -MP -MF $(DEPDIR)/crc_dabmod-TimestampDecoder.Tpo -c -o crc_dabmod-TimestampDecoder.o `test -f 'TimestampDecoder.cpp' || echo '$(srcdir)/'`TimestampDecoder.cpp
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-TimestampDecoder.Tpo $(DEPDIR)/crc_dabmod-TimestampDecoder.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='TimestampDecoder.cpp' object='crc_dabmod-TimestampDecoder.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-TimestampDecoder.o `test -f 'TimestampDecoder.cpp' || echo '$(srcdir)/'`TimestampDecoder.cpp
+
+crc_dabmod-TimestampDecoder.obj: TimestampDecoder.cpp
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-TimestampDecoder.obj -MD -MP -MF $(DEPDIR)/crc_dabmod-TimestampDecoder.Tpo -c -o crc_dabmod-TimestampDecoder.obj `if test -f 'TimestampDecoder.cpp'; then $(CYGPATH_W) 'TimestampDecoder.cpp'; else $(CYGPATH_W) '$(srcdir)/TimestampDecoder.cpp'; fi`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-TimestampDecoder.Tpo $(DEPDIR)/crc_dabmod-TimestampDecoder.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='TimestampDecoder.cpp' object='crc_dabmod-TimestampDecoder.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-TimestampDecoder.obj `if test -f 'TimestampDecoder.cpp'; then $(CYGPATH_W) 'TimestampDecoder.cpp'; else $(CYGPATH_W) '$(srcdir)/TimestampDecoder.cpp'; fi`
+
+crc_dabmod-OutputUHD.o: OutputUHD.cpp
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-OutputUHD.o -MD -MP -MF $(DEPDIR)/crc_dabmod-OutputUHD.Tpo -c -o crc_dabmod-OutputUHD.o `test -f 'OutputUHD.cpp' || echo '$(srcdir)/'`OutputUHD.cpp
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-OutputUHD.Tpo $(DEPDIR)/crc_dabmod-OutputUHD.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='OutputUHD.cpp' object='crc_dabmod-OutputUHD.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-OutputUHD.o `test -f 'OutputUHD.cpp' || echo '$(srcdir)/'`OutputUHD.cpp
+
+crc_dabmod-OutputUHD.obj: OutputUHD.cpp
+@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-OutputUHD.obj -MD -MP -MF $(DEPDIR)/crc_dabmod-OutputUHD.Tpo -c -o crc_dabmod-OutputUHD.obj `if test -f 'OutputUHD.cpp'; then $(CYGPATH_W) 'OutputUHD.cpp'; else $(CYGPATH_W) '$(srcdir)/OutputUHD.cpp'; fi`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-OutputUHD.Tpo $(DEPDIR)/crc_dabmod-OutputUHD.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='OutputUHD.cpp' object='crc_dabmod-OutputUHD.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o crc_dabmod-OutputUHD.obj `if test -f 'OutputUHD.cpp'; then $(CYGPATH_W) 'OutputUHD.cpp'; else $(CYGPATH_W) '$(srcdir)/OutputUHD.cpp'; fi`
+
crc_dabmod-ModOutput.o: ModOutput.cpp
@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(crc_dabmod_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT crc_dabmod-ModOutput.o -MD -MP -MF $(DEPDIR)/crc_dabmod-ModOutput.Tpo -c -o crc_dabmod-ModOutput.o `test -f 'ModOutput.cpp' || echo '$(srcdir)/'`ModOutput.cpp
@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/crc_dabmod-ModOutput.Tpo $(DEPDIR)/crc_dabmod-ModOutput.Po
diff --git a/src/OutputUHD.cpp b/src/OutputUHD.cpp
new file mode 100644
index 0000000..9e14a4f
--- /dev/null
+++ b/src/OutputUHD.cpp
@@ -0,0 +1,466 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of CRC-DADMOD.
+
+ CRC-DADMOD is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ CRC-DADMOD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CRC-DADMOD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define ENABLE_UHD 1
+
+#include "OutputUHD.h"
+#include "PcDebug.h"
+
+#include <iostream>
+#include <assert.h>
+#include <stdexcept>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+
+typedef std::complex<float> complexf;
+
+OutputUHD::OutputUHD(char* device, unsigned sampleRate,
+ double frequency, int txgain, bool muteNoTimestamps) :
+ ModOutput(ModFormat(1), ModFormat(0)),
+ mySampleRate(sampleRate), myTxGain(txgain),
+ myFrequency(frequency), mute_no_timestamps(muteNoTimestamps)
+{
+ MDEBUG("OutputUHD::OutputUHD(device: %s) @ %p\n",
+ device, this);
+
+ myDevice = device;
+
+#if ENABLE_UHD
+ uhd::set_thread_priority_safe();
+
+ //create a usrp device
+ MDEBUG("OutputUHD:Creating the usrp device with: %s...\n",
+ myDevice.c_str());
+ myUsrp = uhd::usrp::multi_usrp::make(myDevice);
+ MDEBUG("OutputUHD:Using device: %s...\n", myUsrp->get_pp_string().c_str());
+
+ MDEBUG("OutputUHD:Setting REFCLK and PPS input...\n");
+ uhd::clock_config_t clock_config;
+ clock_config.ref_source = uhd::clock_config_t::REF_SMA;
+ clock_config.pps_source = uhd::clock_config_t::PPS_SMA;
+ clock_config.pps_polarity = uhd::clock_config_t::PPS_POS;
+ myUsrp->set_clock_config(clock_config, uhd::usrp::multi_usrp::ALL_MBOARDS);
+
+ std::cerr << "UHD clock source is " <<
+ myUsrp->get_clock_source(0) << std::endl;
+
+ std::cerr << "UHD time source is " <<
+ myUsrp->get_time_source(0) << std::endl;
+
+ //set the tx sample rate
+ MDEBUG("OutputUHD:Setting rate to %d...\n", mySampleRate);
+ myUsrp->set_tx_rate(mySampleRate);
+ MDEBUG("OutputUHD:Actual TX Rate: %f Msps...\n", myUsrp->get_tx_rate());
+
+ //set the centre frequency
+ MDEBUG("OutputUHD:Setting freq to %f...\n", myFrequency);
+ myUsrp->set_tx_freq(myFrequency);
+ MDEBUG("OutputUHD:Actual frequency: %f\n", myUsrp->get_tx_freq());
+
+ myUsrp->set_tx_gain(myTxGain);
+ MDEBUG("OutputUHD:Actual TX Gain: %f ...\n", myUsrp->get_tx_gain());
+
+
+ /* handling time for synchronisation: wait until the next full
+ * second, and set the USRP time at next PPS */
+ struct timespec now;
+ time_t seconds;
+ if (clock_gettime(CLOCK_REALTIME, &now)) {
+ fprintf(stderr, "errno: %d\n", errno);
+ perror("OutputUHD:Error: could not get time: ");
+ }
+ else {
+ seconds = now.tv_sec;
+
+ MDEBUG("OutputUHD:sec+1: %ld ; now: %ld ...\n", seconds+1, now.tv_sec);
+ while (seconds + 1 > now.tv_sec) {
+ usleep(1);
+ if (clock_gettime(CLOCK_REALTIME, &now)) {
+ fprintf(stderr, "errno: %d\n", errno);
+ perror("OutputUHD:Error: could not get time: ");
+ break;
+ }
+ }
+ MDEBUG("OutputUHD:sec+1: %ld ; now: %ld ...\n", seconds+1, now.tv_sec);
+ /* We are now shortly after the second change. */
+
+#warning "TODO usleep for USRP time setting !"
+ //usleep(200000); // 200ms, we want the PPS to be later
+ myUsrp->set_time_unknown_pps(uhd::time_spec_t(seconds + 2));
+ fprintf(stderr, "OutputUHD: Setting USRP time next pps to %f\n",
+ uhd::time_spec_t(seconds + 2).get_real_secs());
+ }
+
+ usleep(1e6);
+ fprintf(stderr, "OutputUHD: USRP time %f\n",
+ myUsrp->get_time_now().get_real_secs());
+
+
+ // preparing output thread worker data
+ uwd.myUsrp = myUsrp;
+#else
+ fprintf(stderr, "OutputUHD: UHD initialisation disabled at compile-time\n");
+#endif
+
+ uwd.frame0.ts.timestamp_valid = false;
+ uwd.frame1.ts.timestamp_valid = false;
+ uwd.sampleRate = mySampleRate;
+ uwd.sourceContainsTimestamp = false;
+ uwd.muteNoTimestamps = mute_no_timestamps;
+
+ // Since we don't know the buffer size, we cannot initialise
+ // the buffers here
+ first_run = true;
+
+ shared_ptr<barrier> b(new barrier(2));
+ my_sync_barrier = b;
+ uwd.sync_barrier = b;
+
+ worker.start(&uwd);
+
+ MDEBUG("OutputUHD:UHD ready.\n");
+}
+
+
+OutputUHD::~OutputUHD()
+{
+ MDEBUG("OutputUHD::~OutputUHD() @ %p\n", this);
+ worker.stop();
+}
+
+int OutputUHD::process(Buffer* dataIn, Buffer* dataOut)
+{
+ struct frame_timestamp ts;
+
+ // On the first call, we must do some allocation and we must fill
+ // the first buffer
+ // We will only wait on the barrier on the subsequent calls to
+ // OutputUHD::process
+ if (first_run) {
+ fprintf(stderr, "OutUHD.process:Initialising...\n");
+
+ uwd.bufsize = dataIn->getLength();
+ uwd.frame0.buf = malloc(uwd.bufsize);
+ uwd.frame1.buf = malloc(uwd.bufsize);
+
+ uwd.sourceContainsTimestamp = myEtiReader->sourceContainsTimestamp();
+
+ // The worker begins by transmitting buf0
+ memcpy(uwd.frame0.buf, dataIn->getData(), uwd.bufsize);
+
+ myEtiReader->calculateTimestamp(ts);
+ uwd.frame0.ts = ts;
+ uwd.frame0.fct = myEtiReader->getFCT();
+
+ activebuffer = 1;
+
+ lastLen = uwd.bufsize;
+ first_run = false;
+ fprintf(stderr, "OutUHD.process:Initialising complete.\n");
+ }
+ else {
+
+ if (lastLen != dataIn->getLength()) {
+ // I expect that this never happens.
+ fprintf(stderr,
+ "OutUHD.process:AAAAH PANIC input length changed from %zu to %zu !\n",
+ lastLen, dataIn->getLength());
+ throw std::runtime_error("Non-constant input length!");
+ }
+ //fprintf(stderr, "OutUHD.process:Waiting for barrier\n");
+ my_sync_barrier.get()->wait();
+
+ // write into the our buffer while
+ // the worker sends the other.
+
+ myEtiReader->calculateTimestamp(ts);
+ uwd.sourceContainsTimestamp = myEtiReader->sourceContainsTimestamp();
+
+ if (activebuffer == 0) {
+ memcpy(uwd.frame0.buf, dataIn->getData(), uwd.bufsize);
+
+ uwd.frame0.ts = ts;
+ uwd.frame0.fct = myEtiReader->getFCT();
+ }
+ else if (activebuffer == 1) {
+ memcpy(uwd.frame1.buf, dataIn->getData(), uwd.bufsize);
+
+ uwd.frame1.ts = ts;
+ uwd.frame1.fct = myEtiReader->getFCT();
+ }
+
+ activebuffer = (activebuffer + 1) % 2;
+ }
+
+ return uwd.bufsize;
+
+}
+
+void UHDWorker::process(struct UHDWorkerData *uwd)
+{
+ int workerbuffer = 0;
+ time_t tx_second = 0;
+ double pps_offset = 0;
+ double last_pps = 2.0;
+ double usrp_time;
+
+ //const struct timespec hundred_nano = {0, 100};
+
+ size_t sizeIn;
+ struct UHDWorkerFrameData* frame;
+
+ size_t num_acc_samps; //number of accumulated samples
+ int write_fail_count;
+
+#if ENABLE_UHD
+ // Transmit timeout
+ const double timeout = 0.2;
+
+ uhd::stream_args_t stream_args("fc32"); //complex floats
+ uhd::tx_streamer::sptr myTxStream = uwd->myUsrp->get_tx_stream(stream_args);
+ size_t bufsize = myTxStream->get_max_num_samps();
+#endif
+
+ const complexf* in;
+
+ uhd::tx_metadata_t md;
+ md.start_of_burst = false;
+ md.end_of_burst = false;
+
+ while (running) {
+ md.has_time_spec = false;
+ md.time_spec = uhd::time_spec_t(0.0);
+ num_acc_samps = 0;
+ write_fail_count = 0;
+
+ /* Wait for barrier */
+ // this wait will hopefully always be the second one
+ // because modulation should be quicker than transmission
+ //fprintf(stderr, "Worker:Waiting for barrier\n");
+ uwd->sync_barrier.get()->wait();
+
+ if (workerbuffer == 0) {
+ frame = &(uwd->frame0);
+ }
+ else if (workerbuffer == 1) {
+ frame = &(uwd->frame1);
+ }
+ else {
+ fprintf(stderr, "UHDWorker.process: workerbuffer: %d\n", workerbuffer);
+ perror("UHDWorker.process: workerbuffer is neither 0 nor 1!\n");
+ }
+
+ in = reinterpret_cast<const complexf*>(frame->buf);
+ pps_offset = frame->ts.timestamp_pps_offset;
+ //
+ // Tx second from MNSC
+ tx_second = frame->ts.timestamp_sec;
+
+ sizeIn = uwd->bufsize / sizeof(complexf);
+
+#if ENABLE_UHD
+ // Check for ref_lock
+ if (! uwd->myUsrp->get_mboard_sensor("ref_locked", 0).to_bool()) {
+ fprintf(stderr, "UHDWorker: RefLock lost !\n");
+ }
+
+ usrp_time = uwd->myUsrp->get_time_now().get_real_secs();
+#else
+ usrp_time = 0;
+#endif
+
+ if (uwd->sourceContainsTimestamp) {
+ if (!frame->ts.timestamp_valid) {
+ /* We have not received a full timestamp through
+ * MNSC. We sleep through the frame.
+ */
+ fprintf(stderr, "UHDOut: Throwing sample %d away: incomplete timestamp %zu + %f\n",
+ frame->fct, tx_second, pps_offset);
+ usleep(20000);
+ goto loopend;
+ }
+
+ md.has_time_spec = true;
+ md.time_spec = uhd::time_spec_t(tx_second, pps_offset);
+
+ // md is defined, let's do some checks
+ if (md.time_spec.get_real_secs() + 0.2 < usrp_time) {
+ fprintf(stderr,
+ "* Timestamp in the past! offset: %f"
+ " (%f) frame %d tx_second %zu; pps %f\n",
+ md.time_spec.get_real_secs() - usrp_time,
+ usrp_time, frame->fct, tx_second, pps_offset);
+ goto loopend; //skip the frame
+ }
+
+#if ENABLE_UHD
+ if (md.time_spec.get_real_secs() > usrp_time + TIMESTAMP_MARGIN_FUTURE) {
+ fprintf(stderr,
+ "* Timestamp too far in the future! offset: %f\n",
+ md.time_spec.get_real_secs() - usrp_time);
+ usleep(20000); //sleep so as to fill buffers
+ }
+
+ if (md.time_spec.get_real_secs() > usrp_time + TIMESTAMP_ABORT_FUTURE) {
+ fprintf(stderr,
+ "* Timestamp way too far in the future! offset: %f\n",
+ md.time_spec.get_real_secs() - usrp_time);
+ fprintf(stderr, "* Aborting\n");
+ throw std::runtime_error("Timestamp error. Aborted.");
+ }
+#endif
+
+ if (frame->fct % 50 < 4) {
+ fprintf(stderr, "UHDOut (%f): frame %d tx_second %zu; pps %.9f\n",
+ usrp_time,
+ frame->fct, tx_second, pps_offset);
+ }
+
+ }
+ else { // !uwd->sourceContainsTimestamp
+ if (uwd->muteNoTimestamps) {
+ /* There was some error decoding the timestamp
+ */
+ fprintf(stderr, "UHDOut: Muting sample %d : no timestamp\n",
+ frame->fct);
+ usleep(20000);
+ goto loopend;
+ }
+ }
+
+#if ENABLE_UHD
+ PDEBUG("UHDWorker::process:max_num_samps: %zu.\n",
+ myTxStream->get_max_num_samps());
+
+ /*
+ size_t num_tx_samps = myTxStream->send(
+ dataIn, sizeIn, md, timeout);
+
+ MDEBUG("UHDWorker::process:num_tx_samps: %zu.\n", num_tx_samps);
+ */
+ while (running && (num_acc_samps < sizeIn)) {
+ size_t samps_to_send = std::min(sizeIn - num_acc_samps, bufsize);
+
+ //ensure the the last packet has EOB set if the timestamps has been refreshed
+ //and needs to be reconsidered.
+ md.end_of_burst = (frame->ts.timestamp_refresh && (samps_to_send <= bufsize));
+
+ //send a single packet
+ size_t num_tx_samps = myTxStream->send(
+ &in[num_acc_samps],
+ samps_to_send, md, timeout);
+
+ num_acc_samps += num_tx_samps;
+
+ md.time_spec = uhd::time_spec_t(tx_second, pps_offset)
+ + uhd::time_spec_t(0, num_acc_samps/uwd->sampleRate);
+
+ /*
+ fprintf(stderr, "*** pps_offset %f, md.time_spec %f, usrp->now %f\n",
+ pps_offset,
+ md.time_spec.get_real_secs(),
+ uwd->myUsrp->get_time_now().get_real_secs());
+ // */
+
+
+ if (num_tx_samps == 0) {
+#if 1
+ fprintf(stderr,
+ "UHDWorker::process() unable to write to device, skipping frame!\n");
+ break;
+#else
+ // This has been disabled, because if there is a write failure,
+ // we'd better not insist and try to go on transmitting future
+ // frames.
+ // The goal is not to try to send by all means possible. It's
+ // more important to make sure the SFN is not disturbed.
+
+ fprintf(stderr, "F");
+ nanosleep(&hundred_nano, NULL);
+ write_fail_count++;
+ if (write_fail_count >= 3) {
+ double ts = md.time_spec.get_real_secs();
+ double t_usrp = uwd->myUsrp->get_time_now().get_real_secs();
+
+ fprintf(stderr, "*** USRP write fail count %d\n", write_fail_count);
+ fprintf(stderr, "*** delta %f, md.time_spec %f, usrp->now %f\n",
+ ts - t_usrp,
+ ts, t_usrp);
+
+ fprintf(stderr, "UHDWorker::process() unable to write to device, skipping frame!\n");
+ break;
+ }
+#endif
+ }
+
+ //std::cerr << std::endl << "Waiting for async burst ACK... " << std::flush;
+ uhd::async_metadata_t async_md;
+ if (uwd->myUsrp->get_device()->recv_async_msg(async_md, 0)) {
+ std::string PREFIX = "### asyncronous UHD message : ";
+ switch (async_md.event_code) {
+ case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
+ std::cerr << PREFIX << "Underflow" << std::endl;
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
+ std::cerr << PREFIX << "Packet loss between host and device." << std::endl;
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
+ std::cerr << PREFIX << "Packet had time that was late." << std::endl;
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
+ std::cerr << PREFIX << "Underflow occurred inside a packet." << std::endl;
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
+ std::cerr << PREFIX << "Packet loss within a burst." << std::endl;
+ break;
+ default:
+ std::cerr << PREFIX << "unknown event code" << std::endl;
+ break;
+ }
+ }
+
+ /*
+ bool got_async_burst_ack = false;
+ //loop through all messages for the ACK packet (may have underflow messages in queue)
+ while (not got_async_burst_ack and uwd->myUsrp->get_device()->recv_async_msg(async_md, 0.2)){
+ got_async_burst_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK);
+ }
+ //std::cerr << (got_async_burst_ack? "success" : "fail") << std::endl;
+ // */
+
+
+ }
+#endif
+
+ last_pps = pps_offset;
+
+loopend:
+ // swap buffers
+ workerbuffer = (workerbuffer + 1) % 2;
+ }
+}
diff --git a/src/OutputUHD.h b/src/OutputUHD.h
new file mode 100644
index 0000000..eea594f
--- /dev/null
+++ b/src/OutputUHD.h
@@ -0,0 +1,167 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
+
+DESCRIPTION:
+ It is an output driver for the USRP family of devices, and uses
+ the UHD library. This version is multi-threaded. One thread runs
+ the whole modulator, and the second threads sends the data to the
+ device. The two threads are synchronised using a barrier. Communication
+ between the two threads is implemented using double buffering. At the
+ barrier, they exchange the buffers.
+*/
+
+/*
+ This file is part of CRC-DADMOD.
+
+ CRC-DADMOD is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ CRC-DADMOD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CRC-DADMOD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OUTPUT_UHD_H
+#define OUTPUT_UHD_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/utils/safe_main.hpp>
+#include <uhd/usrp/multi_usrp.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/barrier.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "ModOutput.h"
+#include "EtiReader.h"
+#include "TimestampDecoder.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#define MDEBUG(fmt, args...) fprintf (LOG, fmt , ## args)
+//#define MDEBUG(fmt, args...)
+
+// If the timestamp is further in the future than
+// 100 seconds, abort
+#define TIMESTAMP_ABORT_FUTURE 100
+
+// Add a delay to increase buffers when
+// frames are too far in the future
+#define TIMESTAMP_MARGIN_FUTURE 0.5
+
+typedef std::complex<float> complexf;
+
+using namespace boost;
+
+struct UHDWorkerFrameData {
+ // Buffer holding frame data
+ void* buf;
+
+ // Full timestamp
+ struct frame_timestamp ts;
+
+ // Frame counter
+ uint32_t fct;
+};
+
+
+struct UHDWorkerData {
+ uhd::usrp::multi_usrp::sptr myUsrp;
+ unsigned sampleRate;
+
+ // Double buffering between the two threads
+ // Each buffer contains one OFDM frame, and it's
+ // associated timestamp
+ // A full timestamp contains a TIST according to standard
+ // and time information within MNSC with tx_second.
+ bool sourceContainsTimestamp;
+
+ // When working with timestamps, mute the frames that
+ // do not have a timestamp
+ bool muteNoTimestamps;
+
+ struct UHDWorkerFrameData frame0;
+ struct UHDWorkerFrameData frame1;
+ size_t bufsize; // in bytes
+
+ // A barrier to synchronise the two threads
+ shared_ptr<barrier> sync_barrier;
+};
+
+
+class UHDWorker {
+ public:
+ UHDWorker () {
+ running = false;
+ }
+
+ void start(struct UHDWorkerData *uhdworkerdata) {
+ running = true;
+ uhd_thread = boost::thread(&UHDWorker::process, this, uhdworkerdata);
+ }
+
+ void stop() {
+ running = false;
+ uhd_thread.interrupt();
+ uhd_thread.join();
+ }
+
+ void process(struct UHDWorkerData *uhdworkerdata);
+
+
+ private:
+ struct UHDWorkerData *workerdata;
+ bool running;
+ boost::thread uhd_thread;
+
+ uhd::tx_streamer::sptr myTxStream;
+};
+
+
+class OutputUHD: public ModOutput {
+ public:
+ OutputUHD(char* device, unsigned sampleRate, double frequency, int txgain,
+ bool muteNoTimestamps);
+ ~OutputUHD();
+
+ int process(Buffer* dataIn, Buffer* dataOut);
+
+ const char* name() { return "OutputUHD"; }
+
+ void setETIReader(EtiReader *etiReader) {
+ myEtiReader = etiReader;
+ }
+
+ protected:
+ EtiReader *myEtiReader;
+ std::string myDevice;
+ unsigned mySampleRate;
+ int myTxGain;
+ double myFrequency;
+ uhd::usrp::multi_usrp::sptr myUsrp;
+ shared_ptr<barrier> my_sync_barrier;
+ UHDWorker worker;
+ bool first_run;
+ struct UHDWorkerData uwd;
+ int activebuffer;
+ bool mute_no_timestamps;
+
+ size_t lastLen;
+};
+
+
+#endif // OUTPUT_UHD_H
diff --git a/src/PcDebug.h b/src/PcDebug.h
index 6c6d30c..e4bfba3 100644
--- a/src/PcDebug.h
+++ b/src/PcDebug.h
@@ -74,4 +74,8 @@
# endif
#endif
+void mpbdebugopen();
+void mpbdebug(const char* message, int val);
+
+
#endif // PC_DEBUG_H
diff --git a/src/TimestampDecoder.cpp b/src/TimestampDecoder.cpp
new file mode 100644
index 0000000..3dd64bf
--- /dev/null
+++ b/src/TimestampDecoder.cpp
@@ -0,0 +1,210 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of CRC-DADMOD.
+
+ CRC-DADMOD is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ CRC-DADMOD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CRC-DADMOD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TimestampDecoder.h"
+#include "Eti.h"
+#include <sys/types.h>
+#include "PcDebug.h"
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <boost/lexical_cast.hpp>
+
+//#define MDEBUG(fmt, args...) fprintf (LOG, fmt , ## args)
+#define MDEBUG(fmt, args...) PDEBUG(fmt, ## args)
+
+
+void TimestampDecoder::calculateTimestamp(struct frame_timestamp& ts)
+{
+ ts.timestamp_valid = full_timestamp_received_mnsc;
+ ts.timestamp_sec = time_secs;
+ ts.timestamp_pps_offset = time_pps;
+
+ ts.timestamp_refresh = offset_changed;
+ offset_changed = false;
+
+ ts += timestamp_offset;
+ //ts.print("calc2 ");
+}
+
+void TimestampDecoder::pushMNSCData(int framephase, uint16_t mnsc)
+{
+ struct eti_MNSC_TIME_0 *mnsc0;
+ struct eti_MNSC_TIME_1 *mnsc1;
+ struct eti_MNSC_TIME_2 *mnsc2;
+ struct eti_MNSC_TIME_3 *mnsc3;
+
+ switch (framephase)
+ {
+ case 0:
+ mnsc0 = (struct eti_MNSC_TIME_0*)&mnsc;
+ enableDecode = (mnsc0->type == 0) &&
+ (mnsc0->identifier == 0);
+ gmtime_r(0, &temp_time);
+ break;
+
+ case 1:
+ mnsc1 = (struct eti_MNSC_TIME_1*)&mnsc;
+ temp_time.tm_sec = mnsc1->second_tens * 10 + mnsc1->second_unit;
+ temp_time.tm_min = mnsc1->minute_tens * 10 + mnsc1->minute_unit;
+
+ if (!mnsc1->sync_to_frame)
+ {
+ enableDecode = false;
+ PDEBUG("TimestampDecoder: MNSC time info is not synchronised to frame\n");
+ }
+
+ break;
+
+ case 2:
+ mnsc2 = (struct eti_MNSC_TIME_2*)&mnsc;
+ temp_time.tm_hour = mnsc2->hour_tens * 10 + mnsc2->hour_unit;
+ temp_time.tm_mday = mnsc2->day_tens * 10 + mnsc2->day_unit;
+ break;
+
+ case 3:
+ mnsc3 = (struct eti_MNSC_TIME_3*)&mnsc;
+ temp_time.tm_mon = (mnsc3->month_tens * 10 + mnsc3->month_unit) - 1;
+ temp_time.tm_year = (mnsc3->year_tens * 10 + mnsc3->year_unit) + 100;
+
+ if (enableDecode)
+ {
+ full_timestamp_received_mnsc = true;
+ updateTimestampSeconds(mktime(&temp_time));
+ }
+ break;
+ }
+
+ MDEBUG("TimestampDecoder::pushMNSCData(%d, 0x%x)\n", framephase, mnsc);
+ MDEBUG(" -> %s\n", asctime(&temp_time));
+ MDEBUG(" -> %zu\n", mktime(&temp_time));
+}
+
+void TimestampDecoder::updateTimestampSeconds(uint32_t secs)
+{
+ MDEBUG("TimestampDecoder::updateTimestampSeconds(%d)\n", secs);
+ if (inhibit_second_update > 0)
+ {
+ inhibit_second_update--;
+ }
+ else
+ {
+ time_secs = secs;
+ }
+}
+
+void TimestampDecoder::updateTimestampPPS(double pps)
+{
+ MDEBUG("TimestampDecoder::updateTimestampPPS(%f)\n", pps);
+
+ if (time_pps > pps) // Second boundary crossed
+ {
+ MDEBUG("TimestampDecoder::updateTimestampPPS crossed second\n", pps);
+
+ // The second for the next eight frames will not
+ // be defined by the MNSC
+ inhibit_second_update = 2;
+ time_secs += 1;
+ }
+
+ time_pps = pps;
+
+}
+
+void TimestampDecoder::updateTimestampEti(int framephase, uint16_t mnsc, double pps)
+{
+ updateTimestampPPS(pps);
+ pushMNSCData(framephase, mnsc);
+}
+
+
+bool TimestampDecoder::updateModulatorOffset()
+{
+ using namespace std;
+ using boost::lexical_cast;
+ using boost::bad_lexical_cast;
+
+ if (modconfig.use_offset_fixed)
+ {
+ timestamp_offset = modconfig.offset_fixed;
+ PDEBUG("Setting timestamp offset to %f\n", timestamp_offset);
+ return true;
+ }
+ else if (modconfig.use_offset_file)
+ {
+ bool r = false;
+ double newoffset;
+
+ std::string filedata;
+ ifstream filestream;
+
+ try
+ {
+ filestream.open(modconfig.offset_filename);
+ if (!filestream.eof())
+ {
+ getline(filestream, filedata);
+ try
+ {
+ newoffset = lexical_cast<double>(filedata);
+ r = true;
+ }
+ catch (bad_lexical_cast& e)
+ {
+ fprintf(stderr, "Error parsing timestamp offset from file\n");
+ r = false;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Error reading from timestamp offset file: eof reached\n");
+ r = false;
+ }
+ filestream.close();
+ }
+ catch (exception& e)
+ {
+ fprintf(stderr, "Error opening timestamp offset file\n");
+ r = false;
+ }
+
+
+ if (r)
+ {
+ if (timestamp_offset != newoffset)
+ {
+ timestamp_offset = newoffset;
+ fprintf(stderr, "TimestampDecoder::updateTimestampOffset:" \
+ "new offset is %f\n", timestamp_offset);
+ offset_changed = true;
+ }
+
+ }
+
+ return r;
+ }
+ else {
+ return false;
+ }
+}
diff --git a/src/TimestampDecoder.h b/src/TimestampDecoder.h
new file mode 100644
index 0000000..3a1fb1b
--- /dev/null
+++ b/src/TimestampDecoder.h
@@ -0,0 +1,154 @@
+/*
+ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Her Majesty the
+ Queen in Right of Canada (Communications Research Center Canada)
+
+ Includes modifications for which no copyright is claimed
+ 2012, Matthias P. Braendli, matthias.braendli@mpb.li
+ */
+/*
+ This file is part of CRC-DADMOD.
+
+ CRC-DADMOD is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ CRC-DADMOD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with CRC-DADMOD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TIMESTAMP_DECODER_H
+#define TIMESTAMP_DECODER_H
+
+#include "Eti.h"
+#include <time.h>
+#include <math.h>
+#include <stdio.h>
+
+struct modulator_offset_config
+{
+ bool use_offset_fixed;
+ double offset_fixed;
+
+ bool use_offset_file;
+ char* offset_filename;
+};
+
+struct frame_timestamp
+{
+ uint32_t timestamp_sec;
+ double timestamp_pps_offset;
+ bool timestamp_valid;
+ bool timestamp_refresh;
+
+ struct frame_timestamp operator=(const struct frame_timestamp &rhs)
+ {
+ if (this != &rhs) {
+ this->timestamp_sec = rhs.timestamp_sec;
+ this->timestamp_pps_offset = rhs.timestamp_pps_offset;
+ this->timestamp_valid = rhs.timestamp_valid;
+ this->timestamp_refresh = rhs.timestamp_refresh;
+ }
+
+ return *this;
+ }
+
+ struct frame_timestamp& operator+=(const double& diff)
+ {
+ double offset_pps, offset_secs;
+ offset_pps = modf(diff, &offset_secs);
+
+ this->timestamp_sec += lrintf(offset_secs);
+ this->timestamp_pps_offset += offset_pps;
+
+ while (this->timestamp_pps_offset > 1)
+ {
+ this->timestamp_pps_offset -= 1.0;
+ this->timestamp_sec += 1;
+ };
+ return *this;
+ }
+
+ const struct frame_timestamp operator+(const double diff)
+ {
+ struct frame_timestamp ts = *this;
+ ts += diff;
+ return ts;
+ }
+
+ void print(const char* t)
+ {
+ fprintf(stderr,
+ "%s <struct frame_timestamp(%s, %d, %.9f)>\n",
+ t, this->timestamp_valid ? "valid" : "invalid",
+ this->timestamp_sec, this->timestamp_pps_offset);
+ }
+};
+
+/* This module decodes MNSC time information */
+class TimestampDecoder
+{
+ public:
+ TimestampDecoder(struct modulator_offset_config& config):
+ modconfig(config)
+ {
+ inhibit_second_update = 0;
+ time_pps = 0.0;
+ time_secs = 0;
+ enableDecode = false;
+ full_timestamp_received_mnsc = false;
+ gmtime_r(0, &temp_time);
+ offset_changed = false;
+ };
+
+ /* Calculate the timestamp for the current frame. */
+ void calculateTimestamp(struct frame_timestamp& ts);
+
+ /* Update timestamp data from data in ETI */
+ void updateTimestampEti(int framephase, uint16_t mnsc, double pps);
+
+ /* Update the modulator timestamp offset according to the modconf
+ */
+ bool updateModulatorOffset();
+
+ protected:
+ /* Push a new MNSC field into the decoder */
+ void pushMNSCData(int framephase, uint16_t mnsc);
+
+ /* Each frame contains the TIST field with the PPS offset.
+ * For each frame, this function must be called to update
+ * the timestamp
+ */
+ void updateTimestampPPS(double pps);
+
+ /* Update the timestamp when a full set of MNSC data is
+ * known. This function can be called at most every four
+ * frames when the data is transferred using the MNSC.
+ */
+ void updateTimestampSeconds(uint32_t secs);
+
+ struct tm temp_time;
+ uint32_t time_secs;
+ double time_pps;
+ double timestamp_offset;
+ int inhibit_second_update;
+ bool offset_changed;
+
+ /* configuration for the offset management */
+ struct modulator_offset_config& modconfig;
+
+ /* When the type or identifier don't match, the decoder must
+ * be disabled
+ */
+ bool enableDecode;
+
+ /* Disable timstamps until full time has been received in mnsc */
+ bool full_timestamp_received_mnsc;
+};
+
+#endif