diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2017-01-06 11:35:35 +0100 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2017-01-06 11:35:35 +0100 |
commit | 3633bcc99aedda5d9ea36c143fa339139c763d3e (patch) | |
tree | 6c296bee8cfb6aabb292fe6fc040708c7e4d2e7a | |
parent | 67c82c97dfcfc68d4bd71f5773d21c34c8733c83 (diff) | |
download | dabmod-3633bcc99aedda5d9ea36c143fa339139c763d3e.tar.gz dabmod-3633bcc99aedda5d9ea36c143fa339139c763d3e.tar.bz2 dabmod-3633bcc99aedda5d9ea36c143fa339139c763d3e.zip |
Replace EDI-to-ETI converter with a dedicated EDI source
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | lib/edi/ETIDecoder.cpp | 40 | ||||
-rw-r--r-- | lib/edi/ETIDecoder.hpp | 88 | ||||
-rw-r--r-- | lib/edi/ETIWriter.cpp | 286 | ||||
-rw-r--r-- | lib/edi/ETIWriter.hpp | 119 | ||||
-rw-r--r-- | src/Buffer.cpp | 20 | ||||
-rw-r--r-- | src/Buffer.h | 10 | ||||
-rw-r--r-- | src/DabMod.cpp | 209 | ||||
-rw-r--r-- | src/DabModulator.cpp | 26 | ||||
-rw-r--r-- | src/DabModulator.h | 12 | ||||
-rw-r--r-- | src/EtiReader.cpp | 268 | ||||
-rw-r--r-- | src/EtiReader.h | 116 | ||||
-rw-r--r-- | src/FicSource.cpp | 6 | ||||
-rw-r--r-- | src/FicSource.h | 2 | ||||
-rw-r--r-- | src/FrameMultiplexer.cpp | 24 | ||||
-rw-r--r-- | src/FrameMultiplexer.h | 13 | ||||
-rw-r--r-- | src/InputReader.h | 23 | ||||
-rw-r--r-- | src/OutputUHD.cpp | 10 | ||||
-rw-r--r-- | src/OutputUHD.h | 4 | ||||
-rw-r--r-- | src/SubchannelSource.cpp | 18 | ||||
-rw-r--r-- | src/SubchannelSource.h | 6 |
22 files changed, 679 insertions, 626 deletions
@@ -1,6 +1,6 @@ autom4te.cache -src/*.o +*.o stamp-h1 .deps .dirstamp diff --git a/Makefile.am b/Makefile.am index 793710d..c0dbe46 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,7 +80,6 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ src/OutputUHD.h \ src/InputMemory.cpp \ src/InputMemory.h \ - src/InputEdiReader.cpp \ src/InputFileReader.cpp \ src/InputTcpReader.cpp \ src/InputZeroMQReader.cpp \ @@ -140,8 +139,6 @@ odr_dabmod_SOURCES = src/DabMod.cpp \ lib/edi/ETIDecoder.cpp \ lib/edi/ETIDecoder.hpp \ lib/edi/eti.hpp \ - lib/edi/ETIWriter.cpp \ - lib/edi/ETIWriter.hpp \ lib/edi/PFT.cpp \ lib/edi/PFT.hpp \ lib/fec/char.h \ diff --git a/lib/edi/ETIDecoder.cpp b/lib/edi/ETIDecoder.cpp index c79054b..baede11 100644 --- a/lib/edi/ETIDecoder.cpp +++ b/lib/edi/ETIDecoder.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2016 + Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -30,8 +30,9 @@ namespace EdiDecoder { using namespace std; -ETIDecoder::ETIDecoder(ETIWriter& eti_writer) : - m_eti_writer(eti_writer) +ETIDecoder::ETIDecoder(DataCollector& data_collector) : + m_data_collector(data_collector), + m_last_seq(0) { } @@ -41,7 +42,7 @@ void ETIDecoder::push_bytes(const vector<uint8_t> &buf) while (m_input_data.size() > 2) { if (m_input_data[0] == 'A' and m_input_data[1] == 'F') { - decode_state_t st = decode_afpacket(m_input_data); + const decode_state_t st = decode_afpacket(m_input_data); if (st.num_bytes_consumed == 0 and not st.complete) { // We need to refill our buffer @@ -57,7 +58,7 @@ void ETIDecoder::push_bytes(const vector<uint8_t> &buf) } if (st.complete) { - m_eti_writer.assemble(); + m_data_collector.assemble(); } } @@ -85,7 +86,7 @@ void ETIDecoder::push_bytes(const vector<uint8_t> &buf) decode_state_t st = decode_afpacket(af); if (st.complete) { - m_eti_writer.assemble(); + m_data_collector.assemble(); } } @@ -107,7 +108,7 @@ void ETIDecoder::push_packet(const vector<uint8_t> &buf) const decode_state_t st = decode_afpacket(buf); if (st.complete) { - m_eti_writer.assemble(); + m_data_collector.assemble(); } } @@ -121,10 +122,10 @@ void ETIDecoder::push_packet(const vector<uint8_t> &buf) auto af = m_pft.getNextAFPacket(); if (not af.empty()) { - decode_state_t st = decode_afpacket(af); + const decode_state_t st = decode_afpacket(af); if (st.complete) { - m_eti_writer.assemble(); + m_data_collector.assemble(); } } } @@ -144,7 +145,7 @@ void ETIDecoder::setMaxDelay(int num_af_packets) #define AFPACKET_HEADER_LEN 10 // includes SYNC -decode_state_t ETIDecoder::decode_afpacket( +ETIDecoder::decode_state_t ETIDecoder::decode_afpacket( const std::vector<uint8_t> &input_data) { if (input_data.size() < AFPACKET_HEADER_LEN) { @@ -276,7 +277,7 @@ bool ETIDecoder::decode_starptr(const vector<uint8_t> &value) uint16_t major = read_16b(value.begin() + 4); uint16_t minor = read_16b(value.begin() + 6); - m_eti_writer.update_protocol(protocol, major, minor); + m_data_collector.update_protocol(protocol, major, minor); return true; } @@ -330,8 +331,9 @@ bool ETIDecoder::decode_deti(const vector<uint8_t> &value) to_string(expected_length)); } - m_eti_writer.update_err(stat); - m_eti_writer.update_mnsc(mnsc); + etiLog.level(debug) << "EDI DETI"; + m_data_collector.update_err(stat); + m_data_collector.update_mnsc(mnsc); size_t i = 2 + 4; @@ -342,7 +344,7 @@ bool ETIDecoder::decode_deti(const vector<uint8_t> &value) uint32_t seconds = read_32b(value.begin() + i); i += 4; - m_eti_writer.update_edi_time(utco, seconds); + m_data_collector.update_edi_time(utco, seconds); fc.tsta = read_24b(value.begin() + i); i += 3; @@ -360,7 +362,7 @@ bool ETIDecoder::decode_deti(const vector<uint8_t> &value) fic.begin()); i += fic_length; - m_eti_writer.update_fic(fic); + m_data_collector.update_fic(fic); } if (rfudf) { @@ -368,7 +370,7 @@ bool ETIDecoder::decode_deti(const vector<uint8_t> &value) // high 16 bits: RFU in LIDATA EOH // low 8 bits: RFU in TIST (not supported) - m_eti_writer.update_rfu(rfud >> 8); + m_data_collector.update_rfu(rfud >> 8); if ((rfud & 0xFF) != 0xFF) { etiLog.level(warn) << "EDI: RFU in TIST not supported"; } @@ -376,7 +378,7 @@ bool ETIDecoder::decode_deti(const vector<uint8_t> &value) i += 3; } - m_eti_writer.update_fc_data(fc); + m_data_collector.update_fc_data(fc); return true; } @@ -387,6 +389,7 @@ bool ETIDecoder::decode_estn(const vector<uint8_t> &value, uint8_t n) eti_stc_data stc; + stc.stream_index = n - 1; // n is 1-indexed stc.scid = (sstc >> 18) & 0x3F; stc.sad = (sstc >> 8) & 0x3FF; stc.tpl = (sstc >> 2) & 0x3F; @@ -399,7 +402,8 @@ bool ETIDecoder::decode_estn(const vector<uint8_t> &value, uint8_t n) value.end(), back_inserter(stc.mst)); - m_eti_writer.add_subchannel(stc); + etiLog.level(debug) << "EDI ESTn " << (int)stc.stream_index; + m_data_collector.add_subchannel(stc); return true; } diff --git a/lib/edi/ETIDecoder.hpp b/lib/edi/ETIDecoder.hpp index cec284f..9624c48 100644 --- a/lib/edi/ETIDecoder.hpp +++ b/lib/edi/ETIDecoder.hpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2016 + Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -24,21 +24,84 @@ #include <deque> #include <string> #include <vector> -#include "ETIWriter.hpp" #include "PFT.hpp" +#include "eti.hpp" namespace EdiDecoder { -struct decode_state_t { - decode_state_t(bool _complete, size_t _num_bytes_consumed) : - complete(_complete), num_bytes_consumed(_num_bytes_consumed) {} - bool complete; - size_t num_bytes_consumed; +// Information for Frame Characterisation available in +// EDI. +// +// Number of streams is given separately, and frame length +// is calculated in the DataCollector +struct eti_fc_data { + bool atstf; + uint32_t tsta; + bool ficf; + uint16_t dflc; + uint8_t mid; + uint8_t fp; + + uint8_t fct(void) const { return dflc % 250; } }; +// Information for a subchannel available in EDI +struct eti_stc_data { + uint8_t stream_index; + uint8_t scid; + uint8_t sad; + uint8_t tpl; + std::vector<uint8_t> mst; + + // Return the length of the MST in multiples of 64 bits + uint16_t stl(void) const { return mst.size() / 8; } +}; + +/* A class that receives multiplex data must implement the interface described + * in the DataCollector. This can be e.g. a converter to ETI, or something that + * prepares data structures for a modulator. + */ +class DataCollector { + public: + // Tell the ETIWriter what EDI protocol we receive in *ptr. + // This is not part of the ETI data, but is used as check + virtual void update_protocol( + const std::string& proto, + uint16_t major, + uint16_t minor) = 0; + + // Update the data for the frame characterisation + virtual void update_fc_data(const eti_fc_data& fc_data) = 0; + + virtual void update_fic(const std::vector<uint8_t>& fic) = 0; + + virtual void update_err(uint8_t err) = 0; + + // In addition to TSTA in ETI, EDI also transports more time + // stamp information. + virtual void update_edi_time( + uint32_t utco, + uint32_t seconds) = 0; + + virtual void update_mnsc(uint16_t mnsc) = 0; + + virtual void update_rfu(uint16_t rfu) = 0; + + virtual void add_subchannel(const eti_stc_data& stc) = 0; + + // Tell the ETIWriter that the AFPacket is complete + virtual void assemble(void) = 0; +}; + +/* The ETIDecoder takes care of decoding the EDI TAGs related to the transport + * of ETI(NI) data inside AF and PF packets. + * + * PF packets are handed over to the PFT decoder, which will in turn return + * AF packets. AF packets are directly handled (TAG extraction) here. + */ class ETIDecoder { public: - ETIDecoder(ETIWriter& eti_writer); + ETIDecoder(DataCollector& data_collector); /* Push bytes into the decoder. The buf can contain more * than a single packet. This is useful when reading from streams @@ -57,6 +120,13 @@ class ETIDecoder { void setMaxDelay(int num_af_packets); private: + struct decode_state_t { + decode_state_t(bool _complete, size_t _num_bytes_consumed) : + complete(_complete), num_bytes_consumed(_num_bytes_consumed) {} + bool complete; + size_t num_bytes_consumed; + }; + decode_state_t decode_afpacket(const std::vector<uint8_t> &input_data); bool decode_tagpacket(const std::vector<uint8_t> &payload); bool decode_starptr(const std::vector<uint8_t> &value); @@ -64,7 +134,7 @@ class ETIDecoder { bool decode_estn(const std::vector<uint8_t> &value, uint8_t n); bool decode_stardmy(const std::vector<uint8_t> &value); - ETIWriter& m_eti_writer; + DataCollector& m_data_collector; PFT::PFT m_pft; diff --git a/lib/edi/ETIWriter.cpp b/lib/edi/ETIWriter.cpp deleted file mode 100644 index bd051dd..0000000 --- a/lib/edi/ETIWriter.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - Copyright (C) 2016 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://opendigitalradio.org - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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 this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#include "ETIWriter.hpp" -#include "crc.h" -#include "Log.h" -#include <stdio.h> -#include <cassert> -#include <stdexcept> -#include <sstream> - -namespace EdiDecoder { - -using namespace std; - -void ETIWriter::update_protocol( - const std::string& proto, - uint16_t major, - uint16_t minor) -{ - m_proto_valid = (proto == "DETI" and major == 0 and minor == 0); - - if (not m_proto_valid) { - throw std::invalid_argument("Wrong EDI protocol"); - } -} - -void ETIWriter::reinit() -{ - m_proto_valid = false; - m_fc_valid = false; - m_fic.clear(); - m_etiFrame.clear(); - m_subchannels.clear(); -} - -void ETIWriter::update_err(uint8_t err) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot update ERR before protocol"); - } - m_err = err; -} - -void ETIWriter::update_fc_data(const eti_fc_data& fc_data) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot update FC before protocol"); - } - - m_fc_valid = false; - m_fc = fc_data; - - if (not m_fc.ficf) { - throw std::invalid_argument("FIC must be present"); - } - - if (m_fc.mid > 4) { - throw std::invalid_argument("Invalid MID"); - } - - if (m_fc.fp > 7) { - throw std::invalid_argument("Invalid FP"); - } - - m_fc_valid = true; -} - -void ETIWriter::update_fic(const std::vector<uint8_t>& fic) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot update FIC before protocol"); - } - - m_fic = fic; -} - -void ETIWriter::update_edi_time( - uint32_t utco, - uint32_t seconds) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot update time before protocol"); - } - - m_utco = utco; - m_seconds = seconds; - - // TODO check validity - m_time_valid = true; - -} - -void ETIWriter::update_mnsc(uint16_t mnsc) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot update MNSC before protocol"); - } - - m_mnsc = mnsc; -} - -void ETIWriter::update_rfu(uint16_t rfu) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot update RFU before protocol"); - } - - m_rfu = rfu; -} - -void ETIWriter::add_subchannel(const eti_stc_data& stc) -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot add subchannel before protocol"); - } - - m_subchannels.push_back(stc); - - if (m_subchannels.size() > 64) { - throw std::invalid_argument("Too many subchannels"); - } - -} - -void ETIWriter::assemble() -{ - if (not m_proto_valid) { - throw std::logic_error("Cannot assemble ETI before protocol"); - } - - if (not m_fc_valid) { - throw std::logic_error("Cannot assemble ETI without FC"); - } - - if (m_fic.empty()) { - throw std::logic_error("Cannot assemble ETI without FIC data"); - } - - // Accept zero subchannels, because of an edge-case that can happen - // during reconfiguration. See ETS 300 799 Clause 5.3.3 - - // TODO check time validity - - // ETS 300 799 Clause 5.3.2, but we don't support not having - // a FIC - if ( (m_fc.mid == 3 and m_fic.size() != 32 * 4) or - (m_fc.mid != 3 and m_fic.size() != 24 * 4) ) { - stringstream ss; - ss << "Invalid FIC length " << m_fic.size() << - " for MID " << m_fc.mid; - throw std::invalid_argument(ss.str()); - } - - - std::vector<uint8_t> eti; - eti.reserve(6144); - - eti.push_back(m_err); - - // FSYNC - if (m_fc.fct() % 2 == 1) { - eti.push_back(0xf8); - eti.push_back(0xc5); - eti.push_back(0x49); - } - else { - eti.push_back(0x07); - eti.push_back(0x3a); - eti.push_back(0xb6); - } - - // LIDATA - // FC - eti.push_back(m_fc.fct()); - - const uint8_t NST = m_subchannels.size(); - - eti.push_back((m_fc.ficf << 7) | NST); - - // We need to pack: - // FP 3 bits - // MID 2 bits - // FL 11 bits - - // FL: EN 300 799 5.3.6 - uint16_t FL = NST + 1 + m_fic.size(); - for (const auto& subch : m_subchannels) { - FL += subch.mst.size(); - } - - const uint16_t fp_mid_fl = (m_fc.fp << 13) | (m_fc.mid << 11) | FL; - - eti.push_back(fp_mid_fl >> 8); - eti.push_back(fp_mid_fl & 0xFF); - - // STC - for (const auto& subch : m_subchannels) { - eti.push_back( (subch.scid << 2) | (subch.sad & 0x300) ); - eti.push_back( subch.sad & 0xff ); - eti.push_back( (subch.tpl << 2) | ((subch.stl() & 0x300) >> 8) ); - eti.push_back( subch.stl() & 0xff ); - } - - // EOH - // MNSC - eti.push_back(m_mnsc >> 8); - eti.push_back(m_mnsc & 0xFF); - - // CRC - // Calculate CRC from eti[4] to current position - uint16_t eti_crc = 0xFFFF; - eti_crc = crc16(eti_crc, &eti[4], eti.size() - 4); - eti_crc ^= 0xffff; - eti.push_back(eti_crc >> 8); - eti.push_back(eti_crc & 0xFF); - - const size_t mst_start = eti.size(); - // MST - // FIC data - copy(m_fic.begin(), m_fic.end(), back_inserter(eti)); - - // Data stream - for (const auto& subch : m_subchannels) { - copy(subch.mst.begin(), subch.mst.end(), back_inserter(eti)); - } - - // EOF - // CRC - uint16_t mst_crc = 0xFFFF; - mst_crc = crc16(mst_crc, &eti[mst_start], eti.size() - mst_start); - mst_crc ^= 0xffff; - eti.push_back(mst_crc >> 8); - eti.push_back(mst_crc & 0xFF); - - // RFU - eti.push_back(m_rfu >> 8); - eti.push_back(m_rfu); - - // TIST - eti.push_back(m_fc.tsta >> 24); - eti.push_back((m_fc.tsta >> 16) & 0xFF); - eti.push_back((m_fc.tsta >> 8) & 0xFF); - eti.push_back(m_fc.tsta & 0xFF); - - if (eti.size() > 6144) { - throw std::logic_error("ETI frame cannot be longer than 6144"); - } - - eti.resize(6144, 0x55); - - m_etiFrame = eti; - -} - -std::vector<uint8_t> ETIWriter::getEtiFrame() -{ - if (m_etiFrame.empty()) { - return {}; - } - - vector<uint8_t> eti(move(m_etiFrame)); - reinit(); - - return eti; -} - -} - diff --git a/lib/edi/ETIWriter.hpp b/lib/edi/ETIWriter.hpp deleted file mode 100644 index aa907fc..0000000 --- a/lib/edi/ETIWriter.hpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - Copyright (C) 2016 - Matthias P. Braendli, matthias.braendli@mpb.li - - http://opendigitalradio.org - - This program 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 2 of the License, or - (at your option) any later version. - - This program 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 this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#pragma once - -#include <stdint.h> -#include <string> -#include <vector> -#include <list> -#include "eti.hpp" - -namespace EdiDecoder { - -// Information for Frame Characterisation available in -// EDI. -// -// Number of streams is given separately, and frame length -// is calculeted in the ETIWriter -struct eti_fc_data { - bool atstf; - uint32_t tsta; - bool ficf; - uint16_t dflc; - uint8_t mid; - uint8_t fp; - - uint8_t fct(void) const { return dflc % 250; } -}; - -// Information for a subchannel available in EDI -struct eti_stc_data { - uint8_t scid; - uint8_t sad; - uint8_t tpl; - std::vector<uint8_t> mst; - - // Return the length of the MST in multiples of 64 bits - uint16_t stl(void) const { return mst.size() / 8; } -}; - -class ETIWriter { - public: - // Tell the ETIWriter what EDI protocol we receive in *ptr. - // This is not part of the ETI data, but is used as check - void update_protocol( - const std::string& proto, - uint16_t major, - uint16_t minor); - - // Update the data for the frame characterisation - void update_fc_data(const eti_fc_data& fc_data); - - void update_fic(const std::vector<uint8_t>& fic); - - void update_err(uint8_t err); - - // In addition to TSTA in ETI, EDI also transports more time - // stamp information. - void update_edi_time( - uint32_t utco, - uint32_t seconds); - - void update_mnsc(uint16_t mnsc); - - void update_rfu(uint16_t rfu); - - void add_subchannel(const eti_stc_data& stc); - - // Tell the ETIWriter that the AFPacket is complete - void assemble(void); - - // Return the assembled ETI frame or an empty frame if not ready - std::vector<uint8_t> getEtiFrame(void); - - private: - void reinit(void); - - bool m_proto_valid = false; - - uint8_t m_err; - - bool m_fc_valid = false; - eti_fc_data m_fc; - - // m_fic is valid if non-empty - std::vector<uint8_t> m_fic; - - std::vector<uint8_t> m_etiFrame; - - std::list<eti_stc_data> m_subchannels; - - bool m_time_valid = false; - uint32_t m_utco; - uint32_t m_seconds; - - uint16_t m_mnsc = 0xffff; - - // 16 bits: RFU field in EOH - uint16_t m_rfu = 0xffff; -}; - -} diff --git a/src/Buffer.cpp b/src/Buffer.cpp index fa7f52f..8631c42 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -2,6 +2,11 @@ Copyright (C) 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. @@ -44,6 +49,16 @@ Buffer::Buffer(size_t len, const void *data) setData(data, len); } +Buffer::Buffer(const std::vector<uint8_t> &vec) +{ + PDEBUG("Buffer::Buffer(vector [%zu])\n", vec.size()); + + this->len = 0; + this->size = 0; + this->data = NULL; + setData(vec.data(), vec.size()); +} + Buffer::~Buffer() { @@ -58,6 +73,11 @@ Buffer &Buffer::operator=(const Buffer ©) return *this; } +Buffer &Buffer::operator=(const std::vector<uint8_t> ©) +{ + setData(copy.data(), copy.size()); + return *this; +} Buffer &Buffer::operator+=(const Buffer ©) { diff --git a/src/Buffer.h b/src/Buffer.h index c4c73ce..8c5c768 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -2,6 +2,11 @@ Copyright (C) 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. @@ -27,6 +32,7 @@ #endif #include <unistd.h> +#include <vector> #include <memory> /* Buffer is a container for a byte array, that is memcpy'ed @@ -50,7 +56,8 @@ class Buffer { public: using sptr = std::shared_ptr<Buffer>; - Buffer(const Buffer& copy); + Buffer(const Buffer& copy) = default; + Buffer(const std::vector<uint8_t> &vec); Buffer(size_t len = 0, const void *data = NULL); ~Buffer(); @@ -62,6 +69,7 @@ class Buffer { */ void setData(const void *data, size_t len); Buffer &operator=(const Buffer ©); + Buffer &operator=(const std::vector<uint8_t> ©); /* Concatenate the current data with the new data given. * Reallocates memory if needed. diff --git a/src/DabMod.cpp b/src/DabMod.cpp index 2dea12e..e48e748 100644 --- a/src/DabMod.cpp +++ b/src/DabMod.cpp @@ -187,7 +187,6 @@ int launch_modulator(int argc, char* argv[]) auto inputZeroMQReader = make_shared<InputZeroMQReader>(); #endif - auto inputEdiReader = make_shared<InputEdiReader>(); auto inputTcpReader = make_shared<InputTcpReader>(); struct sigaction sa; @@ -691,6 +690,12 @@ int launch_modulator(int argc, char* argv[]) fprintf(stderr, "%zu Hz\n", outputRate); } + + EtiReader etiReader(tist_offset_s, tist_delay_stages); + EdiReader ediReader; + EdiDecoder::ETIDecoder ediInput(ediReader); + EdiUdpInput ediUdpInput(ediInput); + if (inputTransport == "file") { // Opening ETI input file if (inputFileReader.Open(inputName, loop) == -1) { @@ -717,8 +722,7 @@ int launch_modulator(int argc, char* argv[]) m.inputReader = inputTcpReader.get(); } else if (inputTransport == "edi") { - inputEdiReader->Open(inputName); - m.inputReader = inputEdiReader.get(); + ediUdpInput.Open(inputName); } else { @@ -772,94 +776,139 @@ int launch_modulator(int argc, char* argv[]) } set_thread_name("modulator"); - while (run_again) { - Flowgraph flowgraph; + if (ediUdpInput.isEnabled()) { + while (run_again) { + Flowgraph flowgraph; - m.flowgraph = &flowgraph; - m.data.setLength(6144); + etiLog.level(debug) << "Build mod"; + auto modulator = make_shared<DabModulator>( + ediReader, tiiConfig, outputRate, clockRate, + dabMode, gainMode, digitalgain, normalise, + filterTapsFilename); - auto input = make_shared<InputMemory>(&m.data); - auto modulator = make_shared<DabModulator>( - tist_offset_s, tist_delay_stages, - tiiConfig, outputRate, clockRate, dabMode, gainMode, - digitalgain, normalise, filterTapsFilename); + etiLog.level(debug) << "Connect"; + if (format_converter) { + flowgraph.connect(modulator, format_converter); + flowgraph.connect(format_converter, output); + } + else { + flowgraph.connect(modulator, output); + } - flowgraph.connect(input, modulator); - if (format_converter) { - flowgraph.connect(modulator, format_converter); - flowgraph.connect(format_converter, output); - } - else { - flowgraph.connect(modulator, output); - } + etiLog.level(debug) << "SetETISource"; #if defined(HAVE_OUTPUT_UHD) - if (useUHDOutput) { - ((OutputUHD*)output.get())->setETIReader(modulator->getEtiReader()); + if (useUHDOutput) { + ((OutputUHD*)output.get())->setETISource(modulator->getEtiSource()); + } +#endif + + etiLog.level(debug) << "Loop"; + size_t framecount = 0; + while (true) { + while (not ediReader.isFrameReady()) { + ediUdpInput.rxPacket(); + } + etiLog.level(debug) << "Frame Ready"; + framecount++; + flowgraph.run(); + etiLog.level(debug) << "now clear"; + ediReader.clearFrame(); + + /* Check every once in a while if the remote control + * is still working */ + if ((framecount % 250) == 0) { + rcs.check_faults(); + } + } + } + } + else { + while (run_again) { + Flowgraph flowgraph; + + m.flowgraph = &flowgraph; + m.data.setLength(6144); + + auto input = make_shared<InputMemory>(&m.data); + auto modulator = make_shared<DabModulator>( + etiReader, tiiConfig, outputRate, clockRate, + dabMode, gainMode, digitalgain, normalise, + filterTapsFilename); + + if (format_converter) { + flowgraph.connect(modulator, format_converter); + flowgraph.connect(format_converter, output); + } + else { + flowgraph.connect(modulator, output); + } + +#if defined(HAVE_OUTPUT_UHD) + if (useUHDOutput) { + ((OutputUHD*)output.get())->setETISource(modulator->getEtiSource()); + } #endif - m.inputReader->PrintInfo(); - - run_modulator_state_t st = run_modulator(m); - etiLog.log(trace, "DABMOD,run_modulator() = %d", st); - - switch (st) { - case run_modulator_state_t::failure: - etiLog.level(error) << "Modulator failure."; - run_again = false; - ret = 1; - break; - case run_modulator_state_t::again: - etiLog.level(warn) << "Restart modulator."; - run_again = false; - if (inputTransport == "file") { - if (inputFileReader.Open(inputName, loop) == -1) { - etiLog.level(error) << "Unable to open input file!"; - ret = 1; + m.inputReader->PrintInfo(); + + run_modulator_state_t st = run_modulator(m); + etiLog.log(trace, "DABMOD,run_modulator() = %d", st); + + switch (st) { + case run_modulator_state_t::failure: + etiLog.level(error) << "Modulator failure."; + run_again = false; + ret = 1; + break; + case run_modulator_state_t::again: + etiLog.level(warn) << "Restart modulator."; + run_again = false; + if (inputTransport == "file") { + if (inputFileReader.Open(inputName, loop) == -1) { + etiLog.level(error) << "Unable to open input file!"; + ret = 1; + } + else { + run_again = true; + } } - else { + else if (inputTransport == "zeromq") { +#if defined(HAVE_ZEROMQ) run_again = true; + // Create a new input reader + inputZeroMQReader = make_shared<InputZeroMQReader>(); + inputZeroMQReader->Open(inputName, inputMaxFramesQueued); + m.inputReader = inputZeroMQReader.get(); +#endif } - } - else if (inputTransport == "zeromq") { -#if defined(HAVE_ZEROMQ) + else if (inputTransport == "tcp") { + inputTcpReader = make_shared<InputTcpReader>(); + inputTcpReader->Open(inputName); + m.inputReader = inputTcpReader.get(); + } + break; + case run_modulator_state_t::reconfigure: + etiLog.level(warn) << "Detected change in ensemble configuration."; + /* We can keep the input in this care */ run_again = true; - // Create a new input reader - inputZeroMQReader = make_shared<InputZeroMQReader>(); - inputZeroMQReader->Open(inputName, inputMaxFramesQueued); - m.inputReader = inputZeroMQReader.get(); -#endif - } - else if (inputTransport == "tcp") { - inputTcpReader = make_shared<InputTcpReader>(); - inputTcpReader->Open(inputName); - m.inputReader = inputTcpReader.get(); - } - break; - case run_modulator_state_t::reconfigure: - etiLog.level(warn) << "Detected change in ensemble configuration."; - /* We can keep the input in this care */ - run_again = true; - break; - case run_modulator_state_t::normal_end: - default: - etiLog.level(info) << "modulator stopped."; - ret = 0; - run_again = false; - break; - } - - fprintf(stderr, "\n\n"); - etiLog.level(info) << m.framecount << " DAB frames encoded"; - etiLog.level(info) << ((float)m.framecount * 0.024f) << " seconds encoded"; - - m.data.setLength(0); - } + break; + case run_modulator_state_t::normal_end: + default: + etiLog.level(info) << "modulator stopped."; + ret = 0; + run_again = false; + break; + } + + fprintf(stderr, "\n\n"); + etiLog.level(info) << m.framecount << " DAB frames encoded"; + etiLog.level(info) << ((float)m.framecount * 0.024f) << " seconds encoded"; - //////////////////////////////////////////////////////////////////////// - // Cleaning things - //////////////////////////////////////////////////////////////////////// + m.data.setLength(0); + } + } etiLog.level(info) << "Terminating"; return ret; @@ -867,6 +916,7 @@ int launch_modulator(int argc, char* argv[]) run_modulator_state_t run_modulator(modulator_data& m) { + auto ret = run_modulator_state_t::failure; try { while (running) { @@ -887,9 +937,6 @@ run_modulator_state_t run_modulator(modulator_data& m) PDEBUG("* Read frame %lu\n", m.framecount); PDEBUG("*****************************************\n"); - //////////////////////////////////////////////////////////////// - // Processing data - //////////////////////////////////////////////////////////////// m.flowgraph->run(); /* Check every once in a while if the remote control diff --git a/src/DabModulator.cpp b/src/DabModulator.cpp index 6c28a2e..cbe8f07 100644 --- a/src/DabModulator.cpp +++ b/src/DabModulator.cpp @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2015 + Copyright (C) 2016 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -55,21 +55,21 @@ #include "Log.h" DabModulator::DabModulator( - double& tist_offset_s, unsigned tist_delay_stages, + EtiSource& etiSource, tii_config_t& tiiConfig, unsigned outputRate, unsigned clockRate, unsigned dabMode, GainMode gainMode, float& digGain, float normalise, const std::string& filterTapsFilename ) : - ModCodec(), + ModInput(), myOutputRate(outputRate), myClockRate(clockRate), myDabMode(dabMode), myGainMode(gainMode), myDigGain(digGain), myNormalise(normalise), - myEtiReader(tist_offset_s, tist_delay_stages), + myEtiSource(etiSource), myFlowgraph(NULL), myFilterTapsFilename(filterTapsFilename), myTiiConfig(tiiConfig) @@ -134,16 +134,14 @@ void DabModulator::setMode(unsigned mode) } -int DabModulator::process(Buffer* const dataIn, Buffer* dataOut) +int DabModulator::process(Buffer* dataOut) { using namespace std; - PDEBUG("DabModulator::process(dataIn: %p, dataOut: %p)\n", - dataIn, dataOut); + PDEBUG("DabModulator::process(dataOut: %p)\n", dataOut); - myEtiReader.process(dataIn); if (myFlowgraph == NULL) { - unsigned mode = myEtiReader.getMode(); + unsigned mode = myEtiSource.getMode(); if (myDabMode != 0) { mode = myDabMode; } else if (mode == 0) { @@ -156,10 +154,8 @@ int DabModulator::process(Buffer* const dataIn, Buffer* dataOut) // CIF data initialisation //////////////////////////////////////////////////////////////// auto cifPrbs = make_shared<PrbsGenerator>(864 * 8, 0x110); - auto cifMux = make_shared<FrameMultiplexer>( - myFicSizeOut + 864 * 8, myEtiReader.getSubchannels()); - - auto cifPart = make_shared<BlockPartitioner>(mode, myEtiReader.getFp()); + auto cifMux = make_shared<FrameMultiplexer>(myEtiSource); + auto cifPart = make_shared<BlockPartitioner>(mode, myEtiSource.getFp()); auto cifMap = make_shared<QpskSymbolMapper>(myNbCarriers); auto cifRef = make_shared<PhaseReference>(mode); @@ -229,7 +225,7 @@ int DabModulator::process(Buffer* const dataIn, Buffer* dataOut) //////////////////////////////////////////////////////////////// // Processing FIC //////////////////////////////////////////////////////////////// - shared_ptr<FicSource> fic(myEtiReader.getFic()); + shared_ptr<FicSource> fic(myEtiSource.getFic()); //////////////////////////////////////////////////////////////// // Data initialisation //////////////////////////////////////////////////////////////// @@ -269,7 +265,7 @@ int DabModulator::process(Buffer* const dataIn, Buffer* dataOut) //////////////////////////////////////////////////////////////// // Configuring subchannels //////////////////////////////////////////////////////////////// - for (const auto& subchannel : myEtiReader.getSubchannels()) { + for (const auto& subchannel : myEtiSource.getSubchannels()) { //////////////////////////////////////////////////////////// // Data initialisation diff --git a/src/DabModulator.h b/src/DabModulator.h index 77c0457..d768875 100644 --- a/src/DabModulator.h +++ b/src/DabModulator.h @@ -3,7 +3,7 @@ Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2015 + Copyright (C) 2016 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -45,11 +45,11 @@ #include "TII.h" -class DabModulator : public ModCodec +class DabModulator : public ModInput { public: DabModulator( - double& tist_offset_s, unsigned tist_delay_stages, + EtiSource& etiSource, tii_config_t& tiiConfig, unsigned outputRate, unsigned clockRate, unsigned dabMode, GainMode gainMode, @@ -59,11 +59,11 @@ public: DabModulator& operator=(const DabModulator& other) = delete; virtual ~DabModulator(); - int process(Buffer* const dataIn, Buffer* dataOut); + int process(Buffer* dataOut); const char* name() { return "DabModulator"; } /* Required to get the timestamp */ - EtiReader* getEtiReader() { return &myEtiReader; } + EtiSource* getEtiSource() { return &myEtiSource; } protected: void setMode(unsigned mode); @@ -74,7 +74,7 @@ protected: GainMode myGainMode; float& myDigGain; float myNormalise; - EtiReader myEtiReader; + EtiSource& myEtiSource; Flowgraph* myFlowgraph; OutputMemory* myOutput; std::string myFilterTapsFilename; diff --git a/src/EtiReader.cpp b/src/EtiReader.cpp index 010124c..3207a1f 100644 --- a/src/EtiReader.cpp +++ b/src/EtiReader.cpp @@ -34,6 +34,7 @@ #include <sys/types.h> #include <string.h> #include <arpa/inet.h> +#include <regex> using namespace std; @@ -56,17 +57,14 @@ EtiReader::EtiReader( double& tist_offset_s, unsigned tist_delay_stages) : state(EtiReaderStateSync), - myTimestampDecoder(tist_offset_s, tist_delay_stages) + myTimestampDecoder(tist_offset_s, tist_delay_stages), + myCurrentFrame(0), + eti_fc_valid(false) { - PDEBUG("EtiReader::EtiReader()\n"); - rcs.enrol(&myTimestampDecoder); - - myCurrentFrame = 0; - eti_fc_valid = false; } -std::shared_ptr<FicSource>& EtiReader::getFic() +std::shared_ptr<FicSource>& EtiSource::getFic() { return myFicSource; } @@ -90,7 +88,7 @@ unsigned EtiReader::getFp() } -const std::vector<std::shared_ptr<SubchannelSource> >& EtiReader::getSubchannels() +const std::vector<std::shared_ptr<SubchannelSource> > EtiReader::getSubchannels() const { return mySources; } @@ -158,7 +156,9 @@ int EtiReader::process(const Buffer* dataIn) throw std::runtime_error("FIC must be present to modulate!"); } if (not myFicSource) { - myFicSource = make_shared<FicSource>(eti_fc); + unsigned ficf = eti_fc.FICF; + unsigned mid = eti_fc.MID; + myFicSource = make_shared<FicSource>(ficf, mid); } break; case EtiReaderStateNst: @@ -173,8 +173,12 @@ int EtiReader::process(const Buffer* dataIn) mySources.clear(); for (unsigned i = 0; i < eti_fc.NST; ++i) { + const auto tpl = eti_stc[i].TPL; mySources.push_back( - make_shared<SubchannelSource>(eti_stc[i])); + make_shared<SubchannelSource>( + eti_stc[i].getStartAddress(), + eti_stc[i].getSTL(), + tpl)); 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()); @@ -300,3 +304,247 @@ uint32_t EtiReader::getPPSOffset() return timestamp; } + +unsigned EdiReader::getMode() +{ + if (not m_fc_valid) { + assert(false); + throw std::runtime_error("Trying to access Mode before it is ready!"); + } + return m_fc.mid; +} + + +unsigned EdiReader::getFp() +{ + if (not m_fc_valid) { + throw std::runtime_error("Trying to access FP before it is ready!"); + } + return m_fc.fp; +} + +const std::vector<std::shared_ptr<SubchannelSource> > EdiReader::getSubchannels() const +{ + std::vector<std::shared_ptr<SubchannelSource> > sources; + + sources.resize(m_sources.size()); + for (const auto s : m_sources) { + if (s.first < sources.size()) { + sources.at(s.first) = s.second; + } + else { + throw std::runtime_error("Missing subchannel data in EDI source"); + } + } + + return sources; +} + +bool EdiReader::isFrameReady() +{ + return m_frameReady; +} + +void EdiReader::clearFrame() +{ + m_frameReady = false; + m_proto_valid = false; + m_fc_valid = false; + m_fic.clear(); +} + +void EdiReader::update_protocol( + const std::string& proto, + uint16_t major, + uint16_t minor) +{ + m_proto_valid = (proto == "DETI" and major == 0 and minor == 0); + + if (not m_proto_valid) { + throw std::invalid_argument("Wrong EDI protocol"); + } +} + +void EdiReader::update_err(uint8_t err) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot update ERR before protocol"); + } + m_err = err; +} + +void EdiReader::update_fc_data(const EdiDecoder::eti_fc_data& fc_data) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot update FC before protocol"); + } + + m_fc_valid = false; + m_fc = fc_data; + + if (not m_fc.ficf) { + throw std::invalid_argument("FIC must be present"); + } + + if (m_fc.mid > 4) { + throw std::invalid_argument("Invalid MID"); + } + + if (m_fc.fp > 7) { + throw std::invalid_argument("Invalid FP"); + } + + m_fc_valid = true; +} + +void EdiReader::update_fic(const std::vector<uint8_t>& fic) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot update FIC before protocol"); + } + m_fic = fic; +} + +void EdiReader::update_edi_time( + uint32_t utco, + uint32_t seconds) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot update time before protocol"); + } + + m_utco = utco; + m_seconds = seconds; + + // TODO check validity + m_time_valid = true; +} + +void EdiReader::update_mnsc(uint16_t mnsc) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot update MNSC before protocol"); + } + + m_mnsc = mnsc; +} + +void EdiReader::update_rfu(uint16_t rfu) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot update RFU before protocol"); + } + + m_rfu = rfu; +} + +void EdiReader::add_subchannel(const EdiDecoder::eti_stc_data& stc) +{ + if (not m_proto_valid) { + throw std::logic_error("Cannot add subchannel before protocol"); + } + + if (m_sources.count(stc.stream_index) == 0) { + m_sources[stc.stream_index] = make_shared<SubchannelSource>(stc.sad, stc.stl(), stc.tpl); + } + + auto& source = m_sources[stc.stream_index]; + + if (source->framesize() != stc.mst.size()) { + throw std::invalid_argument( + "EDI: MST data length inconsistent with FIC"); + } + source->loadSubchannelData(stc.mst); + + if (m_sources.size() > 64) { + throw std::invalid_argument("Too many subchannels"); + } +} + +void EdiReader::assemble() +{ + etiLog.level(debug) << "Calling assemble"; + if (not m_proto_valid) { + throw std::logic_error("Cannot assemble EDI data before protocol"); + } + + if (not m_fc_valid) { + throw std::logic_error("Cannot assemble EDI data without FC"); + } + + if (m_fic.empty()) { + throw std::logic_error("Cannot assemble EDI data without FIC"); + } + + // ETS 300 799 Clause 5.3.2, but we don't support not having + // a FIC + if ( (m_fc.mid == 3 and m_fic.size() != 32 * 4) or + (m_fc.mid != 3 and m_fic.size() != 24 * 4) ) { + stringstream ss; + ss << "Invalid FIC length " << m_fic.size() << + " for MID " << m_fc.mid; + throw std::invalid_argument(ss.str()); + } + + if (not myFicSource) { + myFicSource = make_shared<FicSource>(m_fc.ficf, m_fc.mid); + } + + myFicSource->loadFicData(m_fic); + + // Accept zero subchannels, because of an edge-case that can happen + // during reconfiguration. See ETS 300 799 Clause 5.3.3 + + // TODO check time validity + + /* TODO timestamp + myTimestampDecoder.updateTimestampEti( + eti_fc.FP & 0x3, + eti_eoh.MNSC, getPPSOffset(), eti_fc.FCT); + */ + m_frameReady = true; +} + +EdiUdpInput::EdiUdpInput(EdiDecoder::ETIDecoder& decoder) : + m_enabled(false), + m_port(0), + m_decoder(decoder) { } + +int EdiUdpInput::Open(const std::string& uri) +{ + etiLog.level(info) << "Opening EDI :" << uri; + + int ret = 1; + + const std::regex re_udp("udp://:([0-9]+)"); + std::smatch m; + if (std::regex_match(uri, m, re_udp)) { + m_port = std::stoi(m[1].str()); + + etiLog.level(info) << "EDI port :" << m_port; + ret = m_sock.reinit(m_port, "0.0.0.0"); + m_enabled = (ret == 0); + } + + return ret; +} + +void EdiUdpInput::rxPacket() +{ + const size_t packsize = 8192; + UdpPacket packet(packsize); + + int ret = m_sock.receive(packet); + if (ret == 0) { + const auto &buf = packet.getBuffer(); + if (packet.getSize() == packsize) { + fprintf(stderr, "Warning, possible UDP truncation\n"); + } + + m_decoder.push_packet(buf); + } + else { + fprintf(stderr, "Socket error: %s\n", inetErrMsg); + } +} + diff --git a/src/EtiReader.h b/src/EtiReader.h index cfc03af..04d627d 100644 --- a/src/EtiReader.h +++ b/src/EtiReader.h @@ -36,6 +36,8 @@ #include "FicSource.h" #include "SubchannelSource.h" #include "TimestampDecoder.h" +#include "lib/edi/ETIDecoder.hpp" +#include "lib/UdpSocket.h" #include <vector> #include <memory> @@ -43,26 +45,42 @@ #include <sys/types.h> -class EtiReader +class EtiSource +{ +public: + virtual unsigned getMode() = 0; + virtual unsigned getFp() = 0; + + virtual bool sourceContainsTimestamp() = 0; + virtual void calculateTimestamp(struct frame_timestamp& ts) = 0; + + virtual std::shared_ptr<FicSource>& getFic(void); + virtual const std::vector<std::shared_ptr<SubchannelSource> > getSubchannels() const = 0; + +protected: + std::shared_ptr<FicSource> myFicSource; +}; + +class EtiReader : public EtiSource { public: EtiReader( double& tist_offset_s, unsigned tist_delay_stages); - std::shared_ptr<FicSource>& getFic(); - unsigned getMode(); - unsigned getFp(); - const std::vector<std::shared_ptr<SubchannelSource> >& getSubchannels(); + virtual unsigned getMode(); + virtual unsigned getFp(); int process(const Buffer* dataIn); - void calculateTimestamp(struct frame_timestamp& ts) + virtual void calculateTimestamp(struct frame_timestamp& ts) { myTimestampDecoder.calculateTimestamp(ts); } /* Returns true if we have valid time stamps in the ETI*/ - bool sourceContainsTimestamp(); + virtual bool sourceContainsTimestamp(); + + virtual const std::vector<std::shared_ptr<SubchannelSource> > getSubchannels() const; private: /* Transform the ETI TIST to a PPS offset in units of 1/16384000 s */ @@ -78,13 +96,93 @@ private: eti_EOH eti_eoh; eti_EOF eti_eof; eti_TIST eti_tist; - std::shared_ptr<FicSource> myFicSource; - std::vector<std::shared_ptr<SubchannelSource> > mySources; TimestampDecoder myTimestampDecoder; size_t myCurrentFrame; bool eti_fc_valid; + + std::vector<std::shared_ptr<SubchannelSource> > mySources; +}; + +class EdiReader : public EtiSource, public EdiDecoder::DataCollector +{ +public: + virtual unsigned getMode(); + virtual unsigned getFp(); + virtual bool sourceContainsTimestamp() { return false; } + virtual void calculateTimestamp(struct frame_timestamp& ts) + { /* TODO */ } + virtual const std::vector<std::shared_ptr<SubchannelSource> > getSubchannels() const; + + virtual bool isFrameReady(void); + virtual void clearFrame(void); + + // Tell the ETIWriter what EDI protocol we receive in *ptr. + // This is not part of the ETI data, but is used as check + virtual void update_protocol( + const std::string& proto, + uint16_t major, + uint16_t minor); + + // Update the data for the frame characterisation + virtual void update_fc_data(const EdiDecoder::eti_fc_data& fc_data); + + virtual void update_fic(const std::vector<uint8_t>& fic); + + virtual void update_err(uint8_t err); + + // In addition to TSTA in ETI, EDI also transports more time + // stamp information. + virtual void update_edi_time( + uint32_t utco, + uint32_t seconds); + + virtual void update_mnsc(uint16_t mnsc); + + virtual void update_rfu(uint16_t rfu); + + virtual void add_subchannel(const EdiDecoder::eti_stc_data& stc); + + // Tell the ETIWriter that the AFPacket is complete + virtual void assemble(void); +private: + bool m_proto_valid = false; + bool m_frameReady = false; + + uint8_t m_err; + + bool m_fc_valid = false; + EdiDecoder::eti_fc_data m_fc; + + std::vector<uint8_t> m_fic; + + bool m_time_valid = false; + uint32_t m_utco; + uint32_t m_seconds; + + uint16_t m_mnsc = 0xffff; + + // 16 bits: RFU field in EOH + uint16_t m_rfu = 0xffff; + + std::map<uint8_t, std::shared_ptr<SubchannelSource> > m_sources; }; +class EdiUdpInput { + public: + EdiUdpInput(EdiDecoder::ETIDecoder& decoder); + int Open(const std::string& uri); + + bool isEnabled(void) const { return m_enabled; } + + void rxPacket(void); + + private: + bool m_enabled; + int m_port; + + UdpSocket m_sock; + EdiDecoder::ETIDecoder& m_decoder; +}; diff --git a/src/FicSource.cpp b/src/FicSource.cpp index 85614f6..92932ec 100644 --- a/src/FicSource.cpp +++ b/src/FicSource.cpp @@ -39,7 +39,7 @@ const std::vector<PuncturingRule>& FicSource::get_rules() } -FicSource::FicSource(eti_FC &fc) : +FicSource::FicSource(unsigned ficf, unsigned mid) : ModInput() { // PDEBUG("FicSource::FicSource(...)\n"); @@ -47,13 +47,13 @@ FicSource::FicSource(eti_FC &fc) : // PDEBUG(" Framesize: %i\n", d_framesize); // PDEBUG(" Protection: %i\n", d_protection); - if (fc.FICF == 0) { + if (ficf == 0) { d_framesize = 0; d_buffer.setLength(0); return; } - if (fc.MID == 3) { + if (mid == 3) { d_framesize = 32 * 4; d_puncturing_rules.emplace_back(29 * 16, 0xeeeeeeee); d_puncturing_rules.emplace_back(3 * 16, 0xeeeeeeec); diff --git a/src/FicSource.h b/src/FicSource.h index cb1d3a8..77ac741 100644 --- a/src/FicSource.h +++ b/src/FicSource.h @@ -39,7 +39,7 @@ class FicSource : public ModInput { public: - FicSource(eti_FC &fc); + FicSource(unsigned ficf, unsigned mid); size_t getFramesize(); const std::vector<PuncturingRule>& get_rules(); diff --git a/src/FrameMultiplexer.cpp b/src/FrameMultiplexer.cpp index c8ee299..d16ee44 100644 --- a/src/FrameMultiplexer.cpp +++ b/src/FrameMultiplexer.cpp @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -37,24 +37,12 @@ typedef std::complex<float> complexf; FrameMultiplexer::FrameMultiplexer( - size_t framesize, - const std::vector<std::shared_ptr<SubchannelSource> >& subchannels) : + const EtiSource& etiSource) : ModMux(), - d_frameSize(framesize), - mySubchannels(subchannels) + m_etiSource(etiSource) { - PDEBUG("FrameMultiplexer::FrameMultiplexer(%zu) @ %p\n", framesize, this); - -} - - -FrameMultiplexer::~FrameMultiplexer() -{ - PDEBUG("FrameMultiplexer::~FrameMultiplexer() @ %p\n", this); - } - // dataIn[0] -> PRBS // dataIn[1+] -> subchannels int FrameMultiplexer::process(std::vector<Buffer*> dataIn, Buffer* dataOut) @@ -81,12 +69,14 @@ int FrameMultiplexer::process(std::vector<Buffer*> dataIn, Buffer* dataOut) // Write PRBS memcpy(out, (*in)->getData(), (*in)->getLength()); ++in; + // Write subchannel - if (mySubchannels.size() != dataIn.size() - 1) { + const auto subchannels = m_etiSource.getSubchannels(); + if (subchannels.size() != dataIn.size() - 1) { throw std::out_of_range( "FrameMultiplexer detected subchannel size change!"); } - auto subchannel = mySubchannels.begin(); + auto subchannel = subchannels.begin(); while (in != dataIn.end()) { if ((*subchannel)->framesizeCu() * 8 != (*in)->getLength()) { throw std::out_of_range( diff --git a/src/FrameMultiplexer.h b/src/FrameMultiplexer.h index e01f4bf..680cdc7 100644 --- a/src/FrameMultiplexer.h +++ b/src/FrameMultiplexer.h @@ -2,7 +2,7 @@ Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2016 + Copyright (C) 2017 Matthias P. Braendli, matthias.braendli@mpb.li http://opendigitalradio.org @@ -33,6 +33,7 @@ #include "ModPlugin.h" #include "SubchannelSource.h" +#include "EtiReader.h" #include <memory> #include <sys/types.h> @@ -42,19 +43,13 @@ class FrameMultiplexer : public ModMux { public: FrameMultiplexer( - size_t frameSize, - const std::vector<std::shared_ptr<SubchannelSource> >& subchannels); - virtual ~FrameMultiplexer(); - FrameMultiplexer(const FrameMultiplexer&) = delete; - FrameMultiplexer& operator=(const FrameMultiplexer&) = delete; - + const EtiSource& etiSource); int process(std::vector<Buffer*> dataIn, Buffer* dataOut); const char* name() { return "FrameMultiplexer"; } protected: - size_t d_frameSize; - const std::vector<std::shared_ptr<SubchannelSource> >& mySubchannels; + const EtiSource& m_etiSource; }; diff --git a/src/InputReader.h b/src/InputReader.h index fddfccf..f0e7197 100644 --- a/src/InputReader.h +++ b/src/InputReader.h @@ -245,28 +245,5 @@ class InputZeroMQReader : public InputReader struct InputZeroMQThreadData workerdata_; }; -class InputEdiReader : public InputReader -{ -public: - InputEdiReader(); - - int Open(const std::string& uri); - - int GetNextFrame(void* buffer); - - void PrintInfo(void); - -private: - void rx_packet(void); - - std::vector<uint8_t> getEtiFrame(void); - - EdiDecoder::ETIWriter m_writer; - EdiDecoder::ETIDecoder m_decoder; - - int m_port; - UdpSocket m_sock; -}; - #endif diff --git a/src/OutputUHD.cpp b/src/OutputUHD.cpp index 55a24b2..26283a8 100644 --- a/src/OutputUHD.cpp +++ b/src/OutputUHD.cpp @@ -273,9 +273,9 @@ OutputUHD::~OutputUHD() } -void OutputUHD::setETIReader(EtiReader *etiReader) +void OutputUHD::setETISource(EtiSource *etiSource) { - myEtiReader = etiReader; + myEtiSource = etiSource; } int transmission_frame_duration_ms(unsigned int dabMode) @@ -331,7 +331,7 @@ int OutputUHD::process(Buffer* dataIn) // we only set the delay buffer from the dab mode signaled in ETI if the // dab mode was not set in contructor if (myTFDurationMs == 0) { - SetDelayBuffer(myEtiReader->getMode()); + SetDelayBuffer(myEtiSource->getMode()); } worker.start(&uwd); @@ -350,7 +350,7 @@ int OutputUHD::process(Buffer* dataIn) } uwd.sourceContainsTimestamp = myConf.enableSync && - myEtiReader->sourceContainsTimestamp(); + myEtiSource->sourceContainsTimestamp(); if (uwd.check_gpsfix) { @@ -387,7 +387,7 @@ int OutputUHD::process(Buffer* dataIn) frame.buf.begin()); } - myEtiReader->calculateTimestamp(frame.ts); + myEtiSource->calculateTimestamp(frame.ts); if (!uwd.running) { worker.stop(); diff --git a/src/OutputUHD.h b/src/OutputUHD.h index 9987c79..ed68aaa 100644 --- a/src/OutputUHD.h +++ b/src/OutputUHD.h @@ -225,7 +225,7 @@ class OutputUHD: public ModOutput, public RemoteControllable { const char* name() { return "OutputUHD"; } - void setETIReader(EtiReader *etiReader); + void setETISource(EtiSource *etiSource); /*********** REMOTE CONTROL ***************/ @@ -242,7 +242,7 @@ class OutputUHD: public ModOutput, public RemoteControllable { OutputUHD(const OutputUHD& other) = delete; OutputUHD& operator=(const OutputUHD& other) = delete; - EtiReader *myEtiReader; + EtiSource *myEtiSource; OutputUHDConfig& myConf; uhd::usrp::multi_usrp::sptr myUsrp; std::shared_ptr<boost::barrier> mySyncBarrier; diff --git a/src/SubchannelSource.cpp b/src/SubchannelSource.cpp index 7632de5..82636f9 100644 --- a/src/SubchannelSource.cpp +++ b/src/SubchannelSource.cpp @@ -65,11 +65,15 @@ const std::vector<PuncturingRule>& SubchannelSource::get_rules() const } -SubchannelSource::SubchannelSource(eti_STC &stc) : +SubchannelSource::SubchannelSource( + uint8_t sad, + uint16_t stl, + uint8_t tpl + ) : ModInput(), - d_start_address(stc.getStartAddress()), - d_framesize(stc.getSTL() * 8), - d_protection(stc.TPL) + d_start_address(sad), + d_framesize(stl * 8), + d_protection(tpl) { PDEBUG("SubchannelSource::SubchannelSource(...) @ %p\n", this); PDEBUG(" Start address: %zu\n", d_start_address); @@ -109,7 +113,7 @@ SubchannelSource::SubchannelSource(eti_STC &stc) : fprintf(stderr, "Protection form(%zu), option(%zu) and level(%zu)\n", protectionForm(), protectionOption(), protectionLevel()); - fprintf(stderr, "Subchannel TPL: 0x%x (%u)\n", stc.TPL, stc.TPL); + fprintf(stderr, "Subchannel TPL: 0x%x (%u)\n", tpl, tpl); throw std::runtime_error("SubchannelSource::SubchannelSource " "unknown protection level!"); } @@ -143,7 +147,7 @@ SubchannelSource::SubchannelSource(eti_STC &stc) : fprintf(stderr, "Protection form(%zu), option(%zu) and level(%zu)\n", protectionForm(), protectionOption(), protectionLevel()); - fprintf(stderr, "Subchannel TPL: 0x%x (%u)\n", stc.TPL, stc.TPL); + fprintf(stderr, "Subchannel TPL: 0x%x (%u)\n", tpl, tpl); throw std::runtime_error("SubchannelSource::SubchannelSource " "unknown protection level!"); } @@ -151,7 +155,7 @@ SubchannelSource::SubchannelSource(eti_STC &stc) : fprintf(stderr, "Protection form(%zu), option(%zu) and level(%zu)\n", protectionForm(), protectionOption(), protectionLevel()); - fprintf(stderr, "Subchannel TPL: 0x%x (%u)\n", stc.TPL, stc.TPL); + fprintf(stderr, "Subchannel TPL: 0x%x (%u)\n", tpl, tpl); throw std::runtime_error("SubchannelSource::SubchannelSource " "unknown protection option!"); } diff --git a/src/SubchannelSource.h b/src/SubchannelSource.h index a38c281..2dab941 100644 --- a/src/SubchannelSource.h +++ b/src/SubchannelSource.h @@ -41,7 +41,11 @@ class SubchannelSource : public ModInput { public: - SubchannelSource(eti_STC &stc); + SubchannelSource( + uint8_t sad, + uint16_t stl, + uint8_t tpl + ); size_t startAddress() const; size_t framesize() const; |