summaryrefslogtreecommitdiffstats
path: root/lib/edi/PFT.hpp
blob: aa5b9d39eee0504ef249471128115ed98cbc7d88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* ------------------------------------------------------------------
 * Copyright (C) 2017 AVT GmbH - Fabien Vercasson
 * Copyright (C) 2021 Matthias P. Braendli
 *                    matthias.braendli@mpb.li
 *
 * http://opendigitalradio.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */

#pragma once
#include <cstdio>
#include <cstdint>
#include <vector>
#include <map>
#include <string>

namespace EdiDecoder {
namespace PFT {

using pseq_t = uint16_t;
using findex_t = uint32_t; // findex is a 24-bit value

class Fragment
{
    public:
        int received_on_port = 0;

        // Load the data for one fragment from buf into
        // the Fragment.
        // \returns the number of bytes of useful data found in buf
        // A non-zero return value doesn't imply a valid fragment
        // the isValid() method must be used to verify this.
        size_t loadData(const std::vector<uint8_t> &buf, int received_on_port);
        size_t loadData(const std::vector<uint8_t> &buf);

        bool isValid() const { return _valid; }
        pseq_t Pseq() const { return _Pseq; }
        findex_t Findex() const { return _Findex; }
        findex_t Fcount() const { return _Fcount; }
        bool FEC() const { return _FEC; }
        uint16_t Plen() const { return _Plen; }
        uint8_t RSk() const { return _RSk; }
        uint8_t RSz() const { return _RSz; }
        const std::vector<uint8_t>& payload() const
            { return _payload; }

        bool checkConsistency(const Fragment& other) const;

    private:
        std::vector<uint8_t> _payload;

        pseq_t _Pseq = 0;
        findex_t _Findex = 0;
        findex_t _Fcount = 0;
        bool _FEC = false;
        bool _Addr = false;
        uint16_t _Plen = 0;
        uint8_t _RSk = 0;
        uint8_t _RSz = 0;
        uint16_t _Source = 0;
        uint16_t _Dest = 0;
        bool _valid = false;
};

/* The AFBuilder collects Fragments and builds an Application Frame
 * out of them. It does error correction if necessary
 */
class AFBuilder
{
    public:
        enum class decode_attempt_result_t {
            yes,   // The AF packet can be build because all fragments are present
            maybe, // RS decoding may correctly decode the AF packet
            no,    // Not enough fragments present to permit RS
        };

        static std::string dar_to_string(decode_attempt_result_t dar) {
            switch (dar) {
                case decode_attempt_result_t::yes: return "y";
                case decode_attempt_result_t::no: return "n";
                case decode_attempt_result_t::maybe: return "m";
            }
            return "?";
        }

        AFBuilder(pseq_t Pseq, findex_t Fcount, size_t lifetime);

        void pushPFTFrag(const Fragment &frag);

        /* Assess if it may be possible to decode this AF packet */
        decode_attempt_result_t canAttemptToDecode();

        /* Try to build the AF with received fragments.
         * Apply error correction if necessary (missing packets/CRC errors)
         * \return an empty vector if building the AF is not possible
         */
        std::vector<uint8_t> extractAF();

        std::pair<findex_t, findex_t>
            numberOfFragments(void) const {
                return {_fragments.size(), _Fcount};
            }

        std::string visualise();

        std::string visualise_fragment_origins() const;

        /* The user of this instance can keep track of the lifetime of this
         * builder
         */
        size_t lifeTime;

    private:

        // A map from fragment index to fragment
        std::map<findex_t, Fragment> _fragments;

        // cached version of decoded AF packet
        mutable std::vector<uint8_t> _af_packet;

        pseq_t _Pseq;
        findex_t _Fcount;
};

struct afpacket_pft_t
{
    // validity of the struct is given by af_packet begin empty or not.
    std::vector<uint8_t> af_packet;
    pseq_t pseq = 0;
};

class PFT
{
    public:
        void pushPFTFrag(const Fragment &fragment);

        /* Try to build the AF packet for the next pseq. This might
         * skip one or more pseq according to the maximum delay setting.
         *
         * \return an empty vector if building the AF is not possible
         */
        afpacket_pft_t getNextAFPacket();

        /* Set the maximum delay in number of AF Packets before we
         * abandon decoding a given pseq.
         */
        void setMaxDelay(size_t num_af_packets);

        /* Enable verbose fprintf */
        void setVerbose(bool enable);

    private:
        void incrementNextPseq();

        pseq_t m_next_pseq;
        size_t m_max_delay = 10; // in AF packets

        // Keep one AFBuilder for each Pseq
        std::map<pseq_t, AFBuilder> m_afbuilders;

        bool m_verbose = 0;
};

}

}