Skip to content

Commit 9ce3c1c

Browse files
committed
Merge branch 'feature_extend_ATR833_driver' of https://github.com/Scumi/XCSoar
2 parents 931c84a + 9a3d349 commit 9ce3c1c

File tree

2 files changed

+214
-5
lines changed

2 files changed

+214
-5
lines changed

NEWS.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Version 7.29 - not yet released
44
- fix crash when arc airspace has no center
55
- allow xci files to be downloaded from repository
66
- in OpenAir format use AC as Class and AY as Type (#1118)
7+
* devices
8+
- ATR833: read-out active and standby frequencies
79
* Android
810
- get rid of not usable USB interfaces in the 'Device -> Port' list
911
* Kobo

src/Device/Driver/ATR833.cpp

Lines changed: 212 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,31 @@
55
#include "Device/Driver.hpp"
66
#include "Device/Port/Port.hpp"
77
#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>
814

915
static constexpr uint8_t STX = 0x02;
1016
static constexpr uint8_t SYNC = 'r';
1117

1218
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;
1323
static constexpr uint8_t SETSTANDBY = 0x12;
1424
static constexpr uint8_t SETACTIVE = 0x13;
25+
static constexpr uint8_t ALLDATA = 0x42;
26+
static constexpr uint8_t REQUESTDATA = 0x82;
1527

1628
Port &port;
29+
/**
30+
* Buffer which receives the messages send from the radio.
31+
*/
32+
StaticFifoBuffer<uint8_t, 256u> rx_buf;
1733

1834
public:
1935
explicit ATR833Device(Port &_port):port(_port) {}
@@ -27,6 +43,32 @@ class ATR833Device final : public AbstractDevice {
2743
bool PutStandbyFrequency(RadioFrequency frequency,
2844
const TCHAR *name,
2945
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);
3072
};
3173

3274
class ATRBuffer {
@@ -57,14 +99,150 @@ class ATRBuffer {
5799
};
58100

59101
bool
60-
ATR833Device::DataReceived(std::span<const std::byte>,
102+
ATR833Device::DataReceived(std::span<const std::byte> s,
61103
[[maybe_unused]] NMEAInfo &info) noexcept
62104
{
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+
65144
return true;
66145
}
67146

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+
68246
bool
69247
ATR833Device::PutActiveFrequency(RadioFrequency frequency,
70248
[[maybe_unused]] const TCHAR *name,
@@ -90,17 +268,46 @@ ATR833Device::PutStandbyFrequency(RadioFrequency frequency,
90268
return true;
91269
}
92270

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+
93302
static Device *
94303
ATR833CreateOnPort([[maybe_unused]] const DeviceConfig &config, Port &com_port)
95304
{
96305
return new ATR833Device(com_port);
97306
}
98307

99-
100308
const DeviceRegister atr833_driver = {
101309
_T("ATR833"),
102310
_T("ATR833"),
103-
DeviceRegister::NO_TIMEOUT |
104311
DeviceRegister::RAW_GPS_DATA,
105312
ATR833CreateOnPort,
106313
};

0 commit comments

Comments
 (0)