/*
Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty
the Queen in Right of Canada (Communications Research Center Canada)
Copyright (C) 2016
Matthias P. Braendli, matthias.braendli@mpb.li
http://opendigitalradio.org
*/
/*
This file is part of ODR-DabMod.
ODR-DabMod 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.
ODR-DabMod 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 ODR-DabMod. If not, see .
*/
#include "EtiReader.h"
#include "Log.h"
#include "PcDebug.h"
#include "TimestampDecoder.h"
#include
#include
#include
#include
#include
using namespace std;
enum ETI_READER_STATE {
EtiReaderStateNbFrame,
EtiReaderStateFrameSize,
EtiReaderStateSync,
EtiReaderStateFc,
EtiReaderStateNst,
EtiReaderStateEoh,
EtiReaderStateFic,
EtiReaderStateSubch,
EtiReaderStateEof,
EtiReaderStateTist,
EtiReaderStatePad
};
EtiReader::EtiReader(
double& tist_offset_s,
unsigned tist_delay_stages) :
state(EtiReaderStateSync),
myTimestampDecoder(tist_offset_s, tist_delay_stages)
{
PDEBUG("EtiReader::EtiReader()\n");
rcs.enrol(&myTimestampDecoder);
myCurrentFrame = 0;
eti_fc_valid = false;
}
std::shared_ptr& EtiReader::getFic()
{
return myFicSource;
}
unsigned EtiReader::getMode()
{
if (not eti_fc_valid) {
throw std::runtime_error("Trying to access Mode before it is ready!");
}
return eti_fc.MID;
}
unsigned EtiReader::getFp()
{
if (not eti_fc_valid) {
throw std::runtime_error("Trying to access FP before it is ready!");
}
return eti_fc.FP;
}
const std::vector >& EtiReader::getSubchannels()
{
return mySources;
}
int EtiReader::process(const Buffer* dataIn)
{
PDEBUG("EtiReader::process(dataIn: %p)\n", dataIn);
PDEBUG(" state: %u\n", state);
const unsigned char* in = reinterpret_cast(dataIn->getData());
size_t input_size = dataIn->getLength();
while (input_size > 0) {
switch (state) {
case EtiReaderStateNbFrame:
if (input_size < 4) {
return dataIn->getLength() - input_size;
}
nb_frames = *(uint32_t*)in;
input_size -= 4;
in += 4;
state = EtiReaderStateFrameSize;
PDEBUG("Nb frames: %i\n", nb_frames);
break;
case EtiReaderStateFrameSize:
if (input_size < 2) {
return dataIn->getLength() - input_size;
}
framesize = *(uint16_t*)in;
input_size -= 2;
in += 2;
state = EtiReaderStateSync;
PDEBUG("Framesize: %i\n", framesize);
break;
case EtiReaderStateSync:
if (input_size < 4) {
return dataIn->getLength() - input_size;
}
framesize = 6144;
memcpy(&eti_sync, in, 4);
input_size -= 4;
framesize -= 4;
in += 4;
state = EtiReaderStateFc;
PDEBUG("Sync.err: 0x%.2x\n", eti_sync.ERR);
PDEBUG("Sync.fsync: 0x%.6x\n", eti_sync.FSYNC);
break;
case EtiReaderStateFc:
if (input_size < 4) {
return dataIn->getLength() - input_size;
}
memcpy(&eti_fc, in, 4);
eti_fc_valid = true;
input_size -= 4;
framesize -= 4;
in += 4;
state = EtiReaderStateNst;
PDEBUG("Fc.fct: 0x%.2x\n", eti_fc.FCT);
PDEBUG("Fc.ficf: %u\n", eti_fc.FICF);
PDEBUG("Fc.nst: %u\n", eti_fc.NST);
PDEBUG("Fc.fp: 0x%x\n", eti_fc.FP);
PDEBUG("Fc.mid: %u\n", eti_fc.MID);
PDEBUG("Fc.fl: %u\n", eti_fc.getFrameLength());
if (!eti_fc.FICF) {
throw std::runtime_error("FIC must be present to modulate!");
}
if (not myFicSource) {
myFicSource = make_shared(eti_fc);
}
break;
case EtiReaderStateNst:
if (input_size < 4 * (size_t)eti_fc.NST) {
return dataIn->getLength() - input_size;
}
if ((eti_stc.size() != eti_fc.NST) ||
(memcmp(&eti_stc[0], in, 4 * eti_fc.NST))) {
PDEBUG("New stc!\n");
eti_stc.resize(eti_fc.NST);
memcpy(&eti_stc[0], in, 4 * eti_fc.NST);
mySources.clear();
for (unsigned i = 0; i < eti_fc.NST; ++i) {
mySources.push_back(
make_shared(eti_stc[i]));
PDEBUG("Sstc %u:\n", i);
PDEBUG(" Stc%i.scid: %i\n", i, eti_stc[i].SCID);
PDEBUG(" Stc%i.sad: %u\n", i, eti_stc[i].getStartAddress());
PDEBUG(" Stc%i.tpl: 0x%.2x\n", i, eti_stc[i].TPL);
PDEBUG(" Stc%i.stl: %u\n", i, eti_stc[i].getSTL());
}
}
input_size -= 4 * eti_fc.NST;
framesize -= 4 * eti_fc.NST;
in += 4 * eti_fc.NST;
state = EtiReaderStateEoh;
break;
case EtiReaderStateEoh:
if (input_size < 4) {
return dataIn->getLength() - input_size;
}
memcpy(&eti_eoh, in, 4);
input_size -= 4;
framesize -= 4;
in += 4;
state = EtiReaderStateFic;
PDEBUG("Eoh.mnsc: 0x%.4x\n", eti_eoh.MNSC);
PDEBUG("Eoh.crc: 0x%.4x\n", eti_eoh.CRC);
break;
case EtiReaderStateFic:
if (eti_fc.MID == 3) {
if (input_size < 128) {
return dataIn->getLength() - input_size;
}
PDEBUG("Writting 128 bytes of FIC channel data\n");
Buffer fic = Buffer(128, in);
myFicSource->loadFicData(fic);
input_size -= 128;
framesize -= 128;
in += 128;
} else {
if (input_size < 96) {
return dataIn->getLength() - input_size;
}
PDEBUG("Writting 96 bytes of FIC channel data\n");
Buffer fic = Buffer(96, in);
myFicSource->loadFicData(fic);
input_size -= 96;
framesize -= 96;
in += 96;
}
state = EtiReaderStateSubch;
break;
case EtiReaderStateSubch:
for (size_t i = 0; i < eti_stc.size(); ++i) {
unsigned size = mySources[i]->framesize();
PDEBUG("Writting %i bytes of subchannel data\n", size);
Buffer subch = Buffer(size, in);
mySources[i]->loadSubchannelData(subch);
input_size -= size;
framesize -= size;
in += size;
}
state = EtiReaderStateEof;
break;
case EtiReaderStateEof:
if (input_size < 4) {
return dataIn->getLength() - input_size;
}
memcpy(&eti_eof, in, 4);
input_size -= 4;
framesize -= 4;
in += 4;
state = EtiReaderStateTist;
PDEBUG("Eof.crc: %#.4x\n", eti_eof.CRC);
PDEBUG("Eof.rfu: %#.4x\n", eti_eof.RFU);
break;
case EtiReaderStateTist:
if (input_size < 4) {
return dataIn->getLength() - input_size;
}
memcpy(&eti_tist, in, 4);
input_size -= 4;
framesize -= 4;
in += 4;
state = EtiReaderStatePad;
PDEBUG("Tist: 0x%.6x\n", eti_tist.TIST);
break;
case EtiReaderStatePad:
if (framesize > 0) {
--input_size;
--framesize;
++in;
} else {
state = EtiReaderStateSync;
}
break;
default:
// throw std::runtime_error("Invalid state!");
PDEBUG("Invalid state (%i)!", state);
input_size = 0;
}
}
// Update timestamps
myTimestampDecoder.updateTimestampEti(eti_fc.FP & 0x3,
eti_eoh.MNSC, getPPSOffset(), eti_fc.FCT);
return dataIn->getLength() - input_size;
}
bool EtiReader::sourceContainsTimestamp()
{
return (ntohl(eti_tist.TIST) & 0xFFFFFF) != 0xFFFFFF;
/* See ETS 300 799, Annex C.2.2 */
}
uint32_t EtiReader::getPPSOffset()
{
if (!sourceContainsTimestamp()) {
//fprintf(stderr, "****** SOURCE NO TS\n");
return 0.0;
}
uint32_t timestamp = ntohl(eti_tist.TIST) & 0xFFFFFF;
//fprintf(stderr, "****** TIST 0x%x\n", timestamp);
return timestamp;
}