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
|
/*
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 {
constexpr size_t AFPACKET_HEADER_LEN = 10; // includes SYNC
struct frame_timestamp_t {
uint32_t seconds = 0;
uint32_t utco = 0;
uint32_t tsta = 0xFFFFFF; // According to EN 300 797 Annex B
bool is_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 AF packet can also be retrieved */
using afpacket_handler = std::function<void(std::vector<uint8_t>&&)>;
void register_afpacket_handler(afpacket_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;
afpacket_handler m_afpacket_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);
}
|