summaryrefslogtreecommitdiffstats
path: root/lib/edi/common.hpp
blob: c3e6c4099df4ef802a1edb9e31826cae2749b5a6 (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
/*
   Copyright (C) 2020
   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 "PFT.hpp"
#include <functional>
#include <map>
#include <chrono>
#include <string>
#include <array>
#include <vector>
#include <cstddef>
#include <ctime>

namespace EdiDecoder {

struct frame_timestamp_t {
    uint32_t seconds = 0;
    uint32_t utco = 0;
    uint32_t tsta = 0xFFFFFF; // According to EN 300 797 Annex B

    bool valid() const;
    std::string to_string() const;
    std::time_t to_unix_epoch() const;
    std::chrono::system_clock::time_point to_system_clock() const;

    double diff_s(const frame_timestamp_t& other) const;

    frame_timestamp_t& operator+=(const std::chrono::milliseconds& ms);

    static frame_timestamp_t from_unix_epoch(std::time_t time, uint32_t tai_utc_offset, uint32_t tsta);

    friend bool operator==(const frame_timestamp_t& l, const frame_timestamp_t& r) {
        return (l.seconds - l.utco) == (r.seconds - r.utco) and l.tsta == r.tsta;
    }

    friend bool operator!=(const frame_timestamp_t& l, const frame_timestamp_t& r) {
        return not (l == r);
    }

    friend bool operator< (const frame_timestamp_t& l, const frame_timestamp_t& r) {
        if (l.seconds - l.utco == r.seconds - r.utco) {
            return l.tsta < r.tsta;
        }
        return l.seconds - l.utco < r.seconds - r.utco;
    }

    friend bool operator<= (const frame_timestamp_t& l, const frame_timestamp_t& r) {
        return l < r or l == r;
    }

    friend bool operator> (const frame_timestamp_t& l, const frame_timestamp_t& r) {
        return r < l;
    }

    friend bool operator>= (const frame_timestamp_t& l, const frame_timestamp_t& r) {
        return l > r or l == r;
    }
};

using tag_name_t = std::array<uint8_t, 4>;

std::string tag_name_to_human_readable(const tag_name_t& name);

struct Packet {
    std::vector<uint8_t> buf;
    int received_on_port;

    Packet(std::vector<uint8_t>&& b) : buf(b), received_on_port(0) { }
    Packet() {}
};

struct seq_info_t {
    bool seq_valid = false;
    uint16_t seq = 0;
    bool pseq_valid = false;
    uint16_t pseq = 0;
};

/* The TagDispatcher takes care of decoding EDI, with or without PFT, and
 * will call functions when TAGs are encountered.
 *
 * PF packets are handed over to the PFT decoder, which will in turn return
 * AF packets. AF packets are directly dispatched to the TAG functions.
 */
class TagDispatcher {
    public:
        TagDispatcher(std::function<void()>&& af_packet_completed);

        void set_verbose(bool verbose);

        /* Push bytes into the decoder. The buf can contain more
         * than a single packet. This is useful when reading from streams
         * (files, TCP). Pushing an empty buf will clear the internal decoder
         * state to ensure realignment (e.g. on stream reconnection)
         */
        void push_bytes(const std::vector<uint8_t> &buf);

        /* Push a complete packet into the decoder. Useful for UDP and other
         * datagram-oriented protocols.
         */
        void push_packet(const Packet &packet);

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

        /* Handler function for a tag. The first argument contains the tag value,
         * the second argument contains the tag name */
        using tag_handler = std::function<bool(const std::vector<uint8_t>&, const tag_name_t&)>;

        /* Register a handler for a tag. If the tag string can be length 0, 1, 2, 3 or 4.
         * If is shorter than 4, it will perform a longest match on the tag name.
         */
        void register_tag(const std::string& tag, tag_handler&& h);

        /* The complete tagpacket can also be retrieved */
        using tagpacket_handler = std::function<void(const std::vector<uint8_t>&)>;
        void register_tagpacket_handler(tagpacket_handler&& h);

        seq_info_t get_seq_info() const {
            return m_last_sequences;
        }

    private:
        enum class decode_state_e {
            Ok, MissingData, Error
        };
        struct decode_result_t {
            decode_result_t(decode_state_e _st, size_t _num_bytes_consumed) :
                st(_st), num_bytes_consumed(_num_bytes_consumed) {}
            decode_state_e st;
            size_t num_bytes_consumed;
        };

        decode_result_t decode_afpacket(const std::vector<uint8_t> &input_data);
        bool decode_tagpacket(const std::vector<uint8_t> &payload);

        PFT::PFT m_pft;
        seq_info_t m_last_sequences;
        std::vector<uint8_t> m_input_data;
        std::map<std::string, tag_handler> m_handlers;
        std::function<void()> m_af_packet_completed;
        tagpacket_handler m_tagpacket_handler;

        std::vector<std::string> m_ignored_tags;
};

// Data carried inside the ODRv EDI TAG
struct odr_version_data {
    std::string version;
    uint32_t uptime_s;
};

odr_version_data parse_odr_version_data(const std::vector<uint8_t>& data);

}