diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2016-12-23 22:27:03 +0100 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2016-12-23 22:27:03 +0100 | 
| commit | 2b014e2f00df81eefe977b901ab601ea11ccb895 (patch) | |
| tree | a5c6fe371838ff3e153c99c6a87554b55b2284d1 | |
| parent | 1d2c8bf95e8d9c7c6f282f266b149bc82f1c8371 (diff) | |
| download | dabmux-2b014e2f00df81eefe977b901ab601ea11ccb895.tar.gz dabmux-2b014e2f00df81eefe977b901ab601ea11ccb895.tar.bz2 dabmux-2b014e2f00df81eefe977b901ab601ea11ccb895.zip  | |
Add EDI fragment interleaver
| -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; +}; + +} +  | 
