diff options
Diffstat (limited to 'lib')
-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 |
4 files changed, 101 insertions, 432 deletions
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; -}; - -} |