Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Receiver.hxx
Go to the documentation of this file.
1
35#ifndef _DCC_RECEIVER_HXX_
36#define _DCC_RECEIVER_HXX_
37
38#include <fcntl.h>
39#include <unistd.h>
40
42
43#ifdef __FreeRTOS__
44#include "freertos/can_ioctl.h"
45#else
46#include "can_ioctl.h"
47#endif
49#include "dcc/packet.h"
50#include "utils/Crc.hxx"
51
52// If defined, collects samples of timing and state into a ring buffer.
53//#define DCC_DECODER_DEBUG
54
55namespace dcc
56{
57
61{
62public:
65 DccDecoder(unsigned tick_per_usec)
66 {
67 timings_[DCC_ONE].set(tick_per_usec, 52, 64);
68 timings_[DCC_ZERO].set(tick_per_usec, 95, 9900);
69 timings_[MM_PREAMBLE].set(tick_per_usec, 1000, -1);
70 timings_[MM_SHORT].set(tick_per_usec, 20, 32);
71 timings_[MM_LONG].set(tick_per_usec, 200, 216);
72 }
73
75 enum State : uint8_t
76 {
77 UNKNOWN, // 0
78 DCC_PREAMBLE, // 1
79 DCC_END_OF_PREAMBLE, // 2
80 DCC_DATA, // 3
81 DCC_DATA_ONE, // 4
82 DCC_DATA_ZERO, // 5
83 DCC_MAYBE_CUTOUT, // 6
84 DCC_CUTOUT, // 7
85 DCC_PACKET_FINISHED, // 8
86 MM_DATA,
87 MM_ZERO,
88 MM_ONE,
89 MM_PACKET_FINISHED,
90 };
91
94 {
95 return parseState_;
96 }
97
103 {
104 pkt_ = pkt;
105 if (pkt_)
106 {
107 clear_packet();
108 }
109 }
110
115 {
116 return pkt_;
117 };
118
122 void process_data(uint32_t value)
123 {
124#ifdef DCC_DECODER_DEBUG
125 debugLog_.add(value);
126 debugLog_.add(parseState_);
127#endif
128 switch (parseState_)
129 {
130 case DCC_PACKET_FINISHED:
131 case MM_PACKET_FINISHED:
132 case UNKNOWN:
133 {
134 if (timings_[DCC_ONE].match(value))
135 {
136 parseCount_ = 0;
137 parseState_ = DCC_PREAMBLE;
138 return;
139 }
140 if (timings_[MM_PREAMBLE].match(value) && pkt_)
141 {
142 clear_packet();
143 pkt_->packet_header.is_marklin = 1;
144 parseCount_ = 1 << 2;
145 parseState_ = MM_DATA;
146 havePacket_ = true;
147 }
148 break;
149 }
150 case DCC_PREAMBLE:
151 {
152 if (timings_[DCC_ONE].match(value))
153 {
154 parseCount_++;
155 return;
156 }
157 if (timings_[DCC_ZERO].match(value) && (parseCount_ >= 20))
158 {
159 parseState_ = DCC_END_OF_PREAMBLE;
160 return;
161 }
162 break;
163 }
164 case DCC_END_OF_PREAMBLE:
165 {
166 if (timings_[DCC_ZERO].match(value))
167 {
168 parseState_ = DCC_DATA;
169 parseCount_ = 1 << 7;
170 xorState_ = 0;
171 crcState_.init();
172 if (pkt_)
173 {
174 clear_packet();
175 havePacket_ = true;
176 pkt_->packet_header.skip_ec = 1;
177 }
178 else
179 {
180 havePacket_ = false;
181 }
182 return;
183 }
184 break;
185 }
186 case DCC_DATA:
187 {
188 if (timings_[DCC_ONE].match(value))
189 {
190 parseState_ = DCC_DATA_ONE;
191 return;
192 }
193 if (timings_[DCC_ZERO].match(value))
194 {
195 parseState_ = DCC_DATA_ZERO;
196 return;
197 }
198 break;
199 }
200 case DCC_DATA_ONE:
201 {
202 if (timings_[DCC_ONE].match(value))
203 {
204 if (parseCount_)
205 {
206 if (havePacket_)
207 {
209 }
210 parseCount_ >>= 1;
211 parseState_ = DCC_DATA;
212 return;
213 }
214 else
215 {
216 // end of packet 1 bit.
217 if (havePacket_)
218 {
219 if (checkCRC_ && (pkt_->dlc > 6) &&
221 {
222 pkt_->packet_header.csum_error = 1;
223 }
225 if (xorState_)
226 {
227 pkt_->packet_header.csum_error = 1;
228 }
229 pkt_->dlc++;
230 }
231 parseState_ = DCC_MAYBE_CUTOUT;
232 return;
233 }
234 return;
235 }
236 break;
237 }
238 case DCC_DATA_ZERO:
239 {
240 if (timings_[DCC_ZERO].match(value))
241 {
242 if (parseCount_)
243 {
244 // zero bit into data_.
245 parseCount_ >>= 1;
246 }
247 else
248 {
249 // end of byte zero bit. Packet is not finished yet.
250 if (havePacket_)
251 {
253 if ((pkt_->dlc == 0) &&
254 (pkt_->payload[0] == 254 ||
255 pkt_->payload[0] == 253))
256 {
257 checkCRC_ = true;
258 }
259 if (checkCRC_)
260 {
262 }
263 pkt_->dlc++;
265 {
266 havePacket_ = false;
267 }
268 else
269 {
270 pkt_->payload[pkt_->dlc] = 0;
271 }
272 }
273 parseCount_ = 1 << 7;
274 }
275 parseState_ = DCC_DATA;
276 return;
277 }
278 break;
279 }
280 case DCC_MAYBE_CUTOUT:
281 {
282 if (value < timings_[DCC_ZERO].min_value)
283 {
284 parseState_ = DCC_CUTOUT;
285 return;
286 }
287 parseState_ = DCC_PACKET_FINISHED;
288 return;
289 }
290 case DCC_CUTOUT:
291 {
292 parseState_ = DCC_PACKET_FINISHED;
293 return;
294 }
295 case MM_DATA:
296 {
297 if (timings_[MM_LONG].match(value))
298 {
299 parseState_ = MM_ZERO;
300 return;
301 }
302 if (timings_[MM_SHORT].match(value))
303 {
304 parseState_ = MM_ONE;
305 return;
306 }
307 break;
308 }
309 case MM_ZERO:
310 {
311 if (timings_[MM_SHORT].match(value))
312 {
313 // data_[ofs_] |= 0;
314 parseCount_ >>= 1;
315 if (!parseCount_)
316 {
317 if (pkt_->dlc == 2)
318 {
319 parseState_ = MM_PACKET_FINISHED;
320 return;
321 }
322 else
323 {
324 pkt_->dlc++;
325 parseCount_ = 1 << 7;
326 pkt_->payload[pkt_->dlc] = 0;
327 }
328 }
329 parseState_ = MM_DATA;
330 return;
331 }
332 break;
333 }
334 case MM_ONE:
335 {
336 if (timings_[MM_LONG].match(value))
337 {
339 parseCount_ >>= 1;
340 if (!parseCount_)
341 {
342 if (pkt_->dlc == 2)
343 {
344 parseState_ = MM_PACKET_FINISHED;
345 return;
346 }
347 else
348 {
349 pkt_->dlc++;
350 parseCount_ = 1 << 7;
351 pkt_->payload[pkt_->dlc] = 0;
352 }
353 }
354 parseState_ = MM_DATA;
355 return;
356 }
357 break;
358 }
359 }
360 parseState_ = UNKNOWN;
361 return;
362 }
363
367 return (!parseCount_) && // end of byte
368 (parseState_ == DCC_DATA_ONE); // one bit comes
369 }
370
371private:
373 uint8_t parseCount_ = 0;
375 uint8_t havePacket_ : 1;
377 uint8_t checkCRC_ : 1;
379 uint8_t xorState_;
385 DCCPacket* pkt_ = nullptr;
386
389 {
390 pkt_->header_raw_data = 0;
391 pkt_->dlc = 0;
392 pkt_->feedback_key = 0;
393 pkt_->payload[0] = 0;
394 checkCRC_ = 0;
395 xorState_ = 0;
396 crcState_.init();
397 }
398
400 struct Timing
401 {
402 void set(uint32_t tick_per_usec, int min_usec, int max_usec)
403 {
404 if (min_usec < 0)
405 {
406 min_value = 0;
407 }
408 else
409 {
410 min_value = tick_per_usec * min_usec;
411 }
412 if (max_usec < 0)
413 {
414 max_usec = UINT_MAX;
415 }
416 else
417 {
418 max_value = tick_per_usec * max_usec;
419 }
420 }
421
422 bool match(uint32_t value_clocks) const
423 {
424 return min_value <= value_clocks && value_clocks <= max_value;
425 }
426
427 uint32_t min_value;
428 uint32_t max_value;
429 };
430
433 {
434 DCC_ONE = 0,
435 DCC_ZERO,
436 MM_PREAMBLE,
437 MM_SHORT,
438 MM_LONG,
439 MAX_TIMINGS
440 };
442 Timing timings_[MAX_TIMINGS];
443#ifdef DCC_DECODER_DEBUG
444 LogRing<uint16_t, 256> debugLog_;
445#endif
446};
447
457{
458public:
459 DccDecodeFlow(Service *s, const char *dev)
460 : StateFlowBase(s)
461 {
462 fd_ = ::open(dev, O_RDONLY | O_NONBLOCK);
464 start_flow(STATE(register_and_sleep));
465 }
466
467private:
468 Action register_and_sleep()
469 {
470 ::ioctl(fd_, CAN_IOC_READ_ACTIVE, this);
471 return wait_and_call(STATE(data_arrived));
472 }
473
474 Action data_arrived()
475 {
476 while (true)
477 {
478 uint32_t value;
479 int ret = ::read(fd_, &value, sizeof(value));
480 if (ret != 4)
481 {
482 return call_immediately(STATE(register_and_sleep));
483 }
484 debug_data(value);
485 decoder_.process_data(value);
486 if (decoder_.state() == DccDecoder::DCC_PACKET_FINISHED)
487 {
488 dcc_packet_finished(pkt_.payload, pkt_.dlc);
489 }
490 else if (decoder_.state() == DccDecoder::MM_PACKET_FINISHED)
491 {
492 mm_packet_finished(pkt_.payload, pkt_.dlc);
493 }
494
495 static uint8_t x = 0;
496 // MAP_GPIOPinWrite(LED_GREEN, 0);
497 x = ~x;
498 }
499 }
500
501 virtual void dcc_packet_finished(const uint8_t* payload, size_t len) = 0;
502 virtual void mm_packet_finished(const uint8_t* payload, size_t len) = 0;
503 virtual void debug_data(uint32_t value)
504 {
505 }
506
507 int fd_;
508 uint32_t lastValue_ = 0;
509
510protected:
516};
517
518} // namespace dcc
519
520#endif // _DCC_RECEIVER_HXX_
int ioctl(int fd, unsigned long int key,...)
Request and ioctl transaction.
Definition Fileio.cxx:452
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Helper class for computing CRC-8 according to Dallas/Maxim specification for 1-wire protocol.
Definition Crc.hxx:83
bool check_ok()
Checks that the message has a correct CRC.
Definition Crc.hxx:105
void update16(uint8_t message_byte)
Processes one byte of the incoming message.
Definition Crc.hxx:164
void init()
Re-sets the state machine for checksumming a new message.
Definition Crc.hxx:91
Alternative for hundreds of entries.
Definition SimpleLog.hxx:69
Collection of related state machines that pend on incoming messages.
Return type for a state flow callback.
Base class for state machines.
StateFlowBase()
Default constructor.
void start_flow(Callback c)
Resets the flow to the specified state and starts it.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
User-space DCC decoding flow.
Definition Receiver.hxx:457
DCCPacket pkt_
Packet buffer.
Definition Receiver.hxx:512
DccDecoder decoder_
State machine that does the DCC decoding.
Definition Receiver.hxx:515
State machine for decoding a DCC packet flow.
Definition Receiver.hxx:61
void clear_packet()
Sets the input DCC packet to empty.
Definition Receiver.hxx:388
DCCPacket * pkt_
Storage for the current packet.
Definition Receiver.hxx:385
uint8_t checkCRC_
True if we need to check CRC.
Definition Receiver.hxx:377
Timing timings_[MAX_TIMINGS]
The various timings by the standards.
Definition Receiver.hxx:442
State parseState_
State machine for parsing.
Definition Receiver.hxx:383
void set_packet(DCCPacket *pkt)
Sets where to write the decoded DCC data to.
Definition Receiver.hxx:102
DccDecoder(unsigned tick_per_usec)
Definition Receiver.hxx:65
Crc8DallasMaxim crcState_
Checksum state for CRC8 checksum.
Definition Receiver.hxx:381
DCCPacket * pkt()
Retrieves the last applied DCC packet pointer.
Definition Receiver.hxx:114
TimingInfo
Indexes the timing array.
Definition Receiver.hxx:433
uint8_t xorState_
Checksum state for XOR checksum.
Definition Receiver.hxx:379
void process_data(uint32_t value)
Call this function for each time the polarity of the signal changes.
Definition Receiver.hxx:122
bool before_dcc_cutout()
Returns true if we are close to the DCC cutout.
Definition Receiver.hxx:366
State
Internal states of the decoding state machine.
Definition Receiver.hxx:76
uint8_t havePacket_
True if we have storage in the right time for the current packet.
Definition Receiver.hxx:375
uint8_t parseCount_
Counter that works through bit patterns.
Definition Receiver.hxx:373
#define CAN_IOC_READ_ACTIVE
read active ioctl.
#define DCC_PACKET_MAX_PAYLOAD
Maximum number of payload bytes.
Definition packet.h:45
uint8_t skip_ec
typically for DCC packets: 1: do NOT append an EC byte to the end of the packet.
Definition packet.h:66
uint8_t is_marklin
0: DCC packet, 1: motorola packet.
Definition packet.h:62
uint8_t csum_error
1 if there was a checksum error in this packet.
Definition packet.h:76
Stores a DCC packet in memory.
Definition packet.h:55
uintptr_t feedback_key
An opaque key used by the hardware driver to attribute feedback information to the source of the pack...
Definition packet.h:104
uint8_t payload[DCC_PACKET_MAX_PAYLOAD]
Packet payload bytes.
Definition packet.h:97
uint8_t dlc
Specifies the number of used payload bytes.
Definition packet.h:95
Represents the timing of a half-wave of the digital track signal.
Definition Receiver.hxx:401