diff options
-rw-r--r-- | doc/advanced.mux | 7 | ||||
-rw-r--r-- | src/ConfigParser.h | 7 | ||||
-rw-r--r-- | src/DabMultiplexer.cpp | 13 | ||||
-rw-r--r-- | src/DabMultiplexer.h | 9 | ||||
-rw-r--r-- | src/DabMux.cpp | 21 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/dabOutput/dabOutput.h | 4 | ||||
-rw-r--r-- | src/dabOutput/edi/Interleaver.cpp | 114 | ||||
-rw-r--r-- | src/dabOutput/edi/Interleaver.h | 66 |
9 files changed, 226 insertions, 16 deletions
diff --git a/doc/advanced.mux b/doc/advanced.mux index 41a3446..10f0b3f 100644 --- a/doc/advanced.mux +++ b/doc/advanced.mux @@ -399,6 +399,13 @@ outputs { ; Transportation". fec 2 + ; Interleave fragments from several ETI frames so as to reduce the + ; probability of errors when several UDP packets are lost in bursts. + ; This comes at the cost of larger overall latency between multiplexing + ; and modulation. This latency is given in milliseconds, and rounded + ; to nearest multiple of 24ms. Set to 0 to disable the interleaver. + interleave 0 + ; Length of a RS chunk, can be overriden ;default=207 ;chunk_len 207 diff --git a/src/ConfigParser.h b/src/ConfigParser.h index 6399cdd..ea48e94 100644 --- a/src/ConfigParser.h +++ b/src/ConfigParser.h @@ -3,7 +3,7 @@ 2011, 2012 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) - Copyright (C) 2014 + Copyright (C) 2016 Matthias P. Braendli, matthias.braendli@mpb.li The Configuration parser sets up the ensemble according @@ -28,8 +28,7 @@ You should have received a copy of the GNU General Public License along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __CONFIG_PARSER_H_ -#define __CONFIG_PARSER_H_ +#pragma once #include <vector> #include <string> @@ -41,5 +40,3 @@ void parse_ptree(boost::property_tree::ptree& pt, std::shared_ptr<dabEnsemble> ensemble); -#endif - diff --git a/src/DabMultiplexer.cpp b/src/DabMultiplexer.cpp index a0c713b..6d3a51e 100644 --- a/src/DabMultiplexer.cpp +++ b/src/DabMultiplexer.cpp @@ -181,6 +181,10 @@ void DabMultiplexer::prepare() throw e; } } + + if (edi_conf.interleaver_enabled()) { + edi_interleaver.SetLatency(edi_conf.latency_frames); + } #endif // Shift ms by 14 to Timestamp level 2, see below in Section TIST @@ -707,8 +711,13 @@ void DabMultiplexer::mux_frame(std::vector<std::shared_ptr<DabOutput> >& outputs if (edi_conf.enable_pft) { // Apply PFT layer to AF Packet (Reed Solomon FEC and Fragmentation) - vector< edi::PFTFragment > edi_fragments = - edi_pft.Assemble(edi_afpacket); + vector<edi::PFTFragment> edi_fragments = edi_pft.Assemble(edi_afpacket); + + if (edi_conf.interleaver_enabled()) { + edi_interleaver.PushFragments(edi_fragments); + + edi_fragments = edi_interleaver.Interleave(); + } // Send over ethernet for (const auto& edi_frag : edi_fragments) { diff --git a/src/DabMultiplexer.h b/src/DabMultiplexer.h index e2a94f5..b3e432e 100644 --- a/src/DabMultiplexer.h +++ b/src/DabMultiplexer.h @@ -23,8 +23,7 @@ along with ODR-DabMux. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __DAB_MULTIPLEXER_H__ -#define __DAB_MULTIPLEXER_H__ +#pragma once #ifdef HAVE_CONFIG_H # include "config.h" @@ -35,6 +34,7 @@ #include "dabOutput/edi/TagPacket.h" #include "dabOutput/edi/AFPacket.h" #include "dabOutput/edi/PFT.h" +#include "dabOutput/edi/Interleaver.h" #include "fig/FIGCarousel.h" #include "crc.h" #include "utils.h" @@ -102,6 +102,9 @@ class DabMultiplexer : public RemoteControllable { // The AF Packet will be protected with reed-solomon and split in fragments edi::PFT edi_pft; + + // To mitigate for burst packet loss, PFT fragments can be sent out-of-order + edi::Interleaver edi_interleaver; #endif // HAVE_OUTPUT_EDI /* New FIG Carousel */ @@ -130,5 +133,3 @@ class DabMultiplexer : public RemoteControllable { #define DEFAULT_SERVICE_ID 50 #define DEFAULT_PACKET_ADDRESS 0 -#endif - diff --git a/src/DabMux.cpp b/src/DabMux.cpp index 04d1980..450179d 100644 --- a/src/DabMux.cpp +++ b/src/DabMux.cpp @@ -98,10 +98,6 @@ typedef DWORD32 uint32_t; #include "input/Zmq.h" #include "dabOutput/dabOutput.h" -#include "dabOutput/edi/TagItems.h" -#include "dabOutput/edi/TagPacket.h" -#include "dabOutput/edi/AFPacket.h" -#include "dabOutput/edi/PFT.h" #include "crc.h" #include "UdpSocket.h" #include "InetAddress.h" @@ -311,6 +307,20 @@ int main(int argc, char *argv[]) edi_conf.fec = pt_edi.get<unsigned int>("fec", 3); edi_conf.chunk_len = pt_edi.get<unsigned int>("chunk_len", 207); + double interleave_ms = pt_edi.get<double>("interleave", 0); + if (interleave_ms != 0.0) { + if (interleave_ms < 0) { + throw runtime_error("EDI output: negative interleave value is invalid."); + } + + auto latency_rounded = lround(interleave_ms / 24.0); + if (latency_rounded * 24 > 30000) { + throw runtime_error("EDI output: interleaving set for more than 30 seconds!"); + } + + edi_conf.latency_frames = latency_rounded; + } + edi_conf.tagpacket_alignment = pt_edi.get<unsigned int>("tagpacket_alignment", 8); mux.set_edi_config(edi_conf); @@ -417,6 +427,9 @@ int main(int argc, char *argv[]) } etiLog.level(info) << " source port " << edi_dest.source_port; } + if (edi_conf.interleaver_enabled()) { + etiLog.level(info) << " interleave " << edi_conf.latency_frames * 24 << " ms"; + } } #endif diff --git a/src/Makefile.am b/src/Makefile.am index de2aa37..0d1c454 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,7 @@ odr_dabmux_SOURCES =DabMux.cpp DabMux.h \ dabOutput/edi/TagItems.cpp dabOutput/edi/TagItems.h \ dabOutput/edi/TagPacket.cpp dabOutput/edi/TagPacket.h \ dabOutput/edi/PFT.cpp dabOutput/edi/PFT.h \ + dabOutput/edi/Interleaver.cpp dabOutput/edi/Interleaver.h \ ClockTAI.h ClockTAI.cpp \ ConfigParser.cpp ConfigParser.h \ Eti.h Eti.cpp \ diff --git a/src/dabOutput/dabOutput.h b/src/dabOutput/dabOutput.h index a023da9..11b78e6 100644 --- a/src/dabOutput/dabOutput.h +++ b/src/dabOutput/dabOutput.h @@ -69,8 +69,10 @@ struct edi_configuration_t { unsigned int tagpacket_alignment; std::vector<edi_destination_t> destinations; unsigned int dest_port; // common destination port, because it's encoded in the transport layer + unsigned int latency_frames; // if nonzero, enable interleaver with a latency of latency_frames * 24ms - bool enabled() { return destinations.size() > 0; } + bool enabled() const { return destinations.size() > 0; } + bool interleaver_enabled() const { return latency_frames > 0; } }; diff --git a/src/dabOutput/edi/Interleaver.cpp b/src/dabOutput/edi/Interleaver.cpp new file mode 100644 index 0000000..ab3a5fc --- /dev/null +++ b/src/dabOutput/edi/Interleaver.cpp @@ -0,0 +1,114 @@ +/* + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://www.opendigitalradio.org + + EDI output, + Interleaving of PFT fragments to increase robustness against + burst packet loss. + + This is possible because EDI has to assume that fragments may reach + the receiver out of order. + + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "Interleaver.h" + +namespace edi { + +void Interleaver::SetLatency(size_t latency_frames) +{ + m_latency = latency_frames; +} + +void Interleaver::PushFragments(const std::vector<PFTFragment> &fragments) +{ + // Create vectors containing Fcount*latency fragments in total + // and store them into the deque + if (m_buffer.empty()) { + m_buffer.push_back(fragments); + } + else { + auto& last_buffer = m_buffer.back(); + + const bool last_buffer_is_complete = + (last_buffer.size() >= m_fragment_count * m_latency); + + if (last_buffer_is_complete) { + m_buffer.push_back(fragments); + } + else { + std::copy(fragments.begin(), fragments.end(), + std::back_inserter(last_buffer)); + } + } + + m_fragment_count = fragments.size(); +} + +std::vector<PFTFragment> Interleaver::Interleave() +{ + std::vector<PFTFragment> fragments; + + while ( not m_buffer.empty() and + (m_buffer.front().size() >= m_fragment_count * m_latency)) { + + auto& first_buffer = m_buffer.front(); + + assert(first_buffer.size() == m_fragment_count * m_latency); + + /* Assume we have 5 fragments per AF frame, and latency of 3. + * This will give the following strides: + * 0 1 2 + * +-------+-------+---+ + * | 0 1 | 2 3 | 4 | + * | | +---+ | + * | 5 6 | 7 | 8 9 | + * | +---+ | | + * |10 |11 12 |13 14 | + * +---+-------+-------+ + * + * ix will be 0, 5, 10, 1, 6 in the first loop + */ + + for (size_t i = 0; i < m_fragment_count; i++) { + const size_t ix = m_interleave_offset + m_fragment_count * m_stride; + fragments.push_back(first_buffer.at(ix)); + + m_stride += 1; + if (m_stride >= m_latency) { + m_interleave_offset++; + m_stride = 0; + } + } + + if (m_interleave_offset >= m_fragment_count) { + m_interleave_offset = 0; + m_stride = 0; + m_buffer.pop_front(); + } + } + + return fragments; +} + +} + + diff --git a/src/dabOutput/edi/Interleaver.h b/src/dabOutput/edi/Interleaver.h new file mode 100644 index 0000000..1a2e26d --- /dev/null +++ b/src/dabOutput/edi/Interleaver.h @@ -0,0 +1,66 @@ +/* + Copyright (C) 2016 + Matthias P. Braendli, matthias.braendli@mpb.li + + http://www.opendigitalradio.org + + EDI output, + Interleaving of PFT fragments to increase robustness against + burst packet loss. + + This is possible because EDI has to assume that fragments may reach + the receiver out of order. + + */ +/* + This file is part of ODR-DabMux. + + ODR-DabMux 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-DabMux 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-DabMux. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "config.h" +#include <vector> +#include <deque> +#include <stdexcept> +#include <stdint.h> +#include "Log.h" +#include "PFT.h" + +namespace edi { + +class Interleaver { + public: + /* Configure the interleaver to use latency_frames number of AF + * packets for interleaving. Total delay through the interleaver + * will be latency_frames * 24ms + */ + void SetLatency(size_t latency_frames); + + /* Push the fragments for an AF Packet into the interleaver */ + void PushFragments(const std::vector< PFTFragment > &fragments); + + std::vector< PFTFragment > Interleave(void); + + private: + size_t m_latency = 0; + size_t m_fragment_count = 0; + size_t m_interleave_offset = 0; + size_t m_stride = 0; + std::deque<std::vector<PFTFragment> > m_buffer; +}; + +} + |