5
5
#include " Device/Driver.hpp"
6
6
#include " Device/Port/Port.hpp"
7
7
#include " RadioFrequency.hpp"
8
+ #include " NMEA/Info.hpp"
9
+ #include " Operation/Operation.hpp"
10
+ #include " time/TimeoutClock.hpp"
11
+ #include " util/StaticFifoBuffer.hxx"
12
+
13
+ #include < cstdint>
8
14
9
15
static constexpr uint8_t STX = 0x02 ;
10
16
static constexpr uint8_t SYNC = ' r' ;
11
17
12
18
class ATR833Device final : public AbstractDevice {
19
+ static constexpr uint8_t ACK = 0x06 ;
20
+ static constexpr uint8_t NAK = 0x15 ;
21
+ static constexpr uint8_t ALIVE = 0x10 ;
22
+ static constexpr uint8_t EXCHANGE = 0x11 ;
13
23
static constexpr uint8_t SETSTANDBY = 0x12 ;
14
24
static constexpr uint8_t SETACTIVE = 0x13 ;
25
+ static constexpr uint8_t ALLDATA = 0x42 ;
26
+ static constexpr uint8_t REQUESTDATA = 0x82 ;
15
27
16
28
Port &port;
29
+ /* *
30
+ * Buffer which receives the messages send from the radio.
31
+ */
32
+ StaticFifoBuffer<uint8_t , 256u > rx_buf;
17
33
18
34
public:
19
35
explicit ATR833Device (Port &_port):port(_port) {}
@@ -27,6 +43,32 @@ class ATR833Device final : public AbstractDevice {
27
43
bool PutStandbyFrequency (RadioFrequency frequency,
28
44
const TCHAR *name,
29
45
OperationEnvironment &env) override ;
46
+ bool EnableNMEA (OperationEnvironment &env) override ;
47
+ void OnSysTicker () override ;
48
+ void LinkTimeout () override ;
49
+
50
+ private:
51
+ PeriodClock status_clock;
52
+
53
+ /* *
54
+ * Handles responses from the radio.
55
+ */
56
+ void HandleResponse (const uint8_t *data, struct NMEAInfo &info);
57
+ /* *
58
+ * Calculates the length of the message just receiving.
59
+ *
60
+ * @param data Pointer to the first character of the message.
61
+ * @param length Number of characters received.
62
+ * @return Expected message length.
63
+ */
64
+ static size_t ExpectedMsgLength (const uint8_t *data, size_t length);
65
+ /* *
66
+ * Calculates the length of the command message just receiving.
67
+ *
68
+ * @param code Command code received after the STX+'r' character.
69
+ * @return Expected message length after the code character.
70
+ */
71
+ static size_t ExpectedMsgLengthCommand (uint8_t code);
30
72
};
31
73
32
74
class ATRBuffer {
@@ -57,14 +99,150 @@ class ATRBuffer {
57
99
};
58
100
59
101
bool
60
- ATR833Device::DataReceived (std::span<const std::byte>,
102
+ ATR833Device::DataReceived (std::span<const std::byte> s ,
61
103
[[maybe_unused]] NMEAInfo &info) noexcept
62
104
{
63
- // actually made no use of radio information
64
- // TODO: interpret data delivered by ATR833 to display Radio settings...
105
+ assert (!s.empty ());
106
+ const auto *data = s.data ();
107
+ const auto *const end = data + s.size ();
108
+ size_t expected_msg_length{};
109
+
110
+ do {
111
+ // Append new data to the buffer, as much as fits in there
112
+ auto range = rx_buf.Write ();
113
+
114
+ if (range.empty ()) {
115
+ rx_buf.Clear ();
116
+ continue ;
117
+ }
118
+
119
+ size_t nbytes = std::min (range.size (), size_t (end - data));
120
+ memcpy (range.data (), data, nbytes);
121
+ data += nbytes;
122
+ rx_buf.Append (nbytes);
123
+
124
+ for (;;) {
125
+ // Read data from buffer to handle the messages
126
+ range = rx_buf.Read ();;
127
+
128
+ if (range.empty () || range.size () < expected_msg_length)
129
+ break ;
130
+
131
+ expected_msg_length = ExpectedMsgLength (range.data (), range.size ());
132
+
133
+ if (range.size () >= expected_msg_length) {
134
+ if (range.front () == STX) {
135
+ HandleResponse (range.data (), info);
136
+ }
137
+ // Message handled -> remove message
138
+ rx_buf.Consume (expected_msg_length);
139
+ expected_msg_length = 0 ;
140
+ }
141
+ }
142
+ } while (data < end);
143
+
65
144
return true ;
66
145
}
67
146
147
+ /* *
148
+ The expected length of a received message may change,
149
+ when the first character is STX and the second character
150
+ is not received yet.
151
+ */
152
+ size_t
153
+ ATR833Device::ExpectedMsgLength (const uint8_t *data, size_t length)
154
+ {
155
+ assert (data != nullptr );
156
+ assert (length > 0 );
157
+
158
+ if (data[0 ] == STX) {
159
+ if (length > 2 ) {
160
+ return 3 + ExpectedMsgLengthCommand (data[2 ]);
161
+ } else {
162
+ // minimum 3 chars
163
+ return 3 ;
164
+ }
165
+ } else
166
+ return 1 ;
167
+ }
168
+
169
+ size_t
170
+ ATR833Device::ExpectedMsgLengthCommand (uint8_t code)
171
+ {
172
+ switch (code) {
173
+ case SETACTIVE:
174
+ // Active frequency
175
+ return 2 ;
176
+ case SETSTANDBY:
177
+ // Standby frequency
178
+ return 2 ;
179
+ case EXCHANGE:
180
+ // Exchange frequencies
181
+ return 1 ;
182
+ case ALLDATA:
183
+ // receive full dataset (freq, VOL, etc...)
184
+ return 12 ;
185
+ case ACK:
186
+ return 1 ;
187
+ case NAK:
188
+ return 2 ;
189
+ case ALIVE:
190
+ return 1 ;
191
+ default :
192
+ // Received unknown msg id (code)
193
+ return 0 ;
194
+ }
195
+ }
196
+
197
+ void
198
+ ATR833Device::HandleResponse (const uint8_t *data, struct NMEAInfo &info)
199
+ {
200
+ info.alive .Update (info.clock );
201
+
202
+ if (data[2 ] != EXCHANGE && data[2 ] != ALLDATA &&
203
+ data[2 ] != SETACTIVE && data[2 ] != SETSTANDBY)
204
+ return ;
205
+
206
+ if (data[2 ] == SETACTIVE) {
207
+ info.settings .has_active_frequency .Update (info.clock );
208
+ info.settings .active_frequency =
209
+ RadioFrequency::FromMegaKiloHertz (data[3 ], data[4 ] * 5 );
210
+ }
211
+
212
+ if (data[2 ] == SETSTANDBY) {
213
+ info.settings .has_standby_frequency .Update (info.clock );
214
+ info.settings .standby_frequency =
215
+ RadioFrequency::FromMegaKiloHertz (data[3 ], data[4 ] * 5 );
216
+ }
217
+
218
+ if (data[2 ] == EXCHANGE) {
219
+ const auto old_active_freq = info.settings .active_frequency ;
220
+
221
+ info.settings .has_active_frequency .Update (info.clock );
222
+ info.settings .active_frequency = info.settings .standby_frequency ;
223
+
224
+ info.settings .has_standby_frequency .Update (info.clock );
225
+ info.settings .standby_frequency = old_active_freq;
226
+ }
227
+
228
+ if (data[2 ] == ALLDATA) {
229
+ /*
230
+ byte 4: MHz active
231
+ byte 5: kHz/5 active
232
+ byte 6: MHz standby
233
+ byte 7: kHz/5 standby
234
+ byte 8-15: not used by XCSoar (VOL, SQ, VOX, INT, DIM, EXT, spacing, dual)
235
+ */
236
+ info.settings .has_active_frequency .Update (info.clock );
237
+ info.settings .active_frequency =
238
+ RadioFrequency::FromMegaKiloHertz (data[3 ], data[4 ] * 5 );
239
+
240
+ info.settings .has_standby_frequency .Update (info.clock );
241
+ info.settings .standby_frequency =
242
+ RadioFrequency::FromMegaKiloHertz (data[5 ], data[6 ] * 5 );
243
+ }
244
+ }
245
+
68
246
bool
69
247
ATR833Device::PutActiveFrequency (RadioFrequency frequency,
70
248
[[maybe_unused]] const TCHAR *name,
@@ -90,17 +268,46 @@ ATR833Device::PutStandbyFrequency(RadioFrequency frequency,
90
268
return true ;
91
269
}
92
270
271
+ void
272
+ ATR833Device::LinkTimeout ()
273
+ {
274
+ NullOperationEnvironment env;
275
+
276
+ ATRBuffer{ALIVE}.Send (port, env);
277
+ ATRBuffer{REQUESTDATA}.Send (port, env);
278
+ }
279
+
280
+ bool
281
+ ATR833Device::EnableNMEA (OperationEnvironment &env)
282
+ {
283
+ ATRBuffer{ALIVE}.Send (port, env);
284
+ ATRBuffer{REQUESTDATA}.Send (port, env);
285
+
286
+ return true ;
287
+ }
288
+
289
+ void
290
+ ATR833Device::OnSysTicker ()
291
+ {
292
+ // ALIVE shall be send every 5 seconds
293
+ if (status_clock.CheckUpdate (std::chrono::seconds (5 ))) {
294
+ NullOperationEnvironment env;
295
+
296
+ ATRBuffer{ALIVE}.Send (port, env);
297
+
298
+ ATRBuffer{REQUESTDATA}.Send (port, env);
299
+ }
300
+ }
301
+
93
302
static Device *
94
303
ATR833CreateOnPort ([[maybe_unused]] const DeviceConfig &config, Port &com_port)
95
304
{
96
305
return new ATR833Device (com_port);
97
306
}
98
307
99
-
100
308
const DeviceRegister atr833_driver = {
101
309
_T (" ATR833" ),
102
310
_T (" ATR833" ),
103
- DeviceRegister::NO_TIMEOUT |
104
311
DeviceRegister::RAW_GPS_DATA,
105
312
ATR833CreateOnPort,
106
313
};
0 commit comments