Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
RailCom.cxx
Go to the documentation of this file.
1
35#include <string.h>
36
37#include "dcc/RailCom.hxx"
38#include "utils/Crc.hxx"
39
40namespace dcc {
41static constexpr uint8_t INV = RailcomDefs::INV;
42static constexpr uint8_t ACK = RailcomDefs::ACK;
43static constexpr uint8_t NACK = RailcomDefs::NACK;
44static constexpr uint8_t BUSY = RailcomDefs::BUSY;
45static constexpr uint8_t RESVD1 = RailcomDefs::RESVD1;
46static constexpr uint8_t RESVD2 = RailcomDefs::RESVD2;
47const uint8_t railcom_decode[256] =
48 // 0|8 1|9 2|a 3|b 4|c 5|d 6|e 7|f
49{ INV, INV, INV, INV, INV, INV, INV, INV, // 0
50 INV, INV, INV, INV, INV, INV, INV, ACK, // 0
51 INV, INV, INV, INV, INV, INV, INV, 0x33, // 1
52 INV, INV, INV, 0x34, INV, 0x35, 0x36, INV, // 1
53 INV, INV, INV, INV, INV, INV, INV, 0x3A, // 2
54 INV, INV, INV, 0x3B, INV, 0x3C, 0x37, INV, // 2
55 INV, INV, INV, 0x3F, INV, 0x3D, 0x38, INV, // 3
56 INV, 0x3E, 0x39, INV, NACK, INV, INV, INV, // 3
57 INV, INV, INV, INV, INV, INV, INV, 0x24, // 4
58 INV, INV, INV, 0x23, INV, 0x22, 0x21, INV, // 4
59 INV, INV, INV, 0x1F, INV, 0x1E, 0x20, INV, // 5
60 INV, 0x1D, 0x1C, INV, 0x1B, INV, INV, INV, // 5
61 INV, INV, INV, 0x19, INV, 0x18, 0x1A, INV, // 6
62 INV, 0x17, 0x16, INV, 0x15, INV, INV, INV, // 6
63 INV, 0x25, 0x14, INV, 0x13, INV, INV, INV, // 7
64 0x32, INV, INV, INV, INV, INV, INV, INV, // 7
65 INV, INV, INV, INV, INV, INV, INV, RESVD2, // 8
66 INV, INV, INV, 0x0E, INV, 0x0D, 0x0C, INV, // 8
67 INV, INV, INV, 0x0A, INV, 0x09, 0x0B, INV, // 9
68 INV, 0x08, 0x07, INV, 0x06, INV, INV, INV, // 9
69 INV, INV, INV, 0x04, INV, 0x03, 0x05, INV, // a
70 INV, 0x02, 0x01, INV, 0x00, INV, INV, INV, // a
71 INV, 0x0F, 0x10, INV, 0x11, INV, INV, INV, // b
72 0x12, INV, INV, INV, INV, INV, INV, INV, // b
73 INV, INV, INV, RESVD1, INV, 0x2B, 0x30, INV, // c
74 INV, 0x2A, 0x2F, INV, 0x31, INV, INV, INV, // c
75 INV, 0x29, 0x2E, INV, 0x2D, INV, INV, INV, // d
76 0x2C, INV, INV, INV, INV, INV, INV, INV, // d
77 INV, BUSY, 0x28, INV, 0x27, INV, INV, INV, // e
78 0x26, INV, INV, INV, INV, INV, INV, INV, // e
79 ACK, INV, INV, INV, INV, INV, INV, INV, // f
80 INV, INV, INV, INV, INV, INV, INV, INV, // f
81};
82
83const uint8_t railcom_encode[64] = {
84 0b10101100,
85 0b10101010,
86 0b10101001,
87 0b10100101,
88 0b10100011,
89 0b10100110,
90 0b10011100,
91 0b10011010,
92 0b10011001,
93 0b10010101,
94 0b10010011,
95 0b10010110,
96 0b10001110,
97 0b10001101,
98 0b10001011,
99 0b10110001,
100 0b10110010,
101 0b10110100,
102 0b10111000,
103 0b01110100,
104 0b01110010,
105 0b01101100,
106 0b01101010,
107 0b01101001,
108 0b01100101,
109 0b01100011,
110 0b01100110,
111 0b01011100,
112 0b01011010,
113 0b01011001,
114 0b01010101,
115 0b01010011,
116 0b01010110,
117 0b01001110,
118 0b01001101,
119 0b01001011,
120 0b01000111,
121 0b01110001,
122 0b11101000,
123 0b11100100,
124 0b11100010,
125 0b11010001,
126 0b11001001,
127 0b11000101,
128 0b11011000,
129 0b11010100,
130 0b11010010,
131 0b11001010,
132 0b11000110,
133 0b11001100,
134 0b01111000,
135 0b00010111,
136 0b00011011,
137 0b00011101,
138 0b00011110,
139 0b00101110,
140 0b00110110,
141 0b00111010,
142 0b00100111,
143 0b00101011,
144 0b00101101,
145 0b00110101,
146 0b00111001,
147 0b00110011,
148};
149
162void parse_internal(uint8_t fb_channel, uint8_t railcom_channel,
163 const uint8_t *ptr, unsigned size,
164 std::vector<struct RailcomPacket> *output)
165{
166 if (!size)
167 return;
168 for (unsigned ofs = 0; ofs < size; ++ofs)
169 {
170 uint8_t decoded = railcom_decode[ptr[ofs]];
171 uint8_t type = 0xff;
172 uint32_t arg = 0;
173 if (decoded == RailcomDefs::ACK)
174 {
175 type = RailcomPacket::ACK;
176 }
177 else if (decoded == RailcomDefs::NACK)
178 {
179 type = RailcomPacket::NACK;
180 }
181 else if (decoded == RailcomDefs::BUSY)
182 {
183 type = RailcomPacket::BUSY;
184 }
185 else if (decoded >= 64)
186 {
187 output->emplace_back(
188 fb_channel, railcom_channel, RailcomPacket::GARBAGE, 0);
189 break;
190 }
191 if (type != 0xff)
192 {
193 output->emplace_back(fb_channel, railcom_channel, type, 0);
194 continue;
195 }
196 // Now: we have a packet.
197 uint8_t packet_id = decoded >> 2;
198 uint8_t len = 2;
199 arg = decoded & 3;
200 switch (packet_id)
201 {
202 case RMOB_ADRHIGH:
203 type = RailcomPacket::MOB_ADRHIGH;
204 break;
205 case RMOB_ADRLOW:
206 type = RailcomPacket::MOB_ADRLOW;
207 break;
208 case RMOB_EXT:
209 type = RailcomPacket::MOB_EXT;
210 // TODO: according to the standardthis should be a len==3
211 // packet, but the ESU LokPilot 3 is sending it as 2-byte
212 // packet.
213 break;
214 case RMOB_POM:
215 type = RailcomPacket::MOB_POM;
216 if (size == 6 && ofs == 0
217 // The ESU LokPilot V4 decoder fills the CV read (a 2-byte
218 // packet) with four NACK bytes, presumably to report that
219 // it is not actually giving back a 32-bit response but
220 // only an 8-bit response.
221 && railcom_decode[ptr[2]] < 64)
222 {
223 len = 6;
224 }
225 else
226 {
227 len = 2;
228 }
229 break;
230 case RMOB_XPOM0:
231 case RMOB_XPOM1:
232 case RMOB_XPOM2:
233 case RMOB_XPOM3:
234 type = RailcomPacket::MOB_XPOM0 + (packet_id - RMOB_XPOM0);
235 len = 6;
236 break;
237 case RMOB_DYN:
238 type = RailcomPacket::MOB_DYN;
239 len = 3;
240 break;
241 default:
242 // Don't know the size of the fragment. Throws out response.
243 len = 255;
244 break;
245 }
246 if (ofs + len > size)
247 {
248 // The expected message size does not fit.
249 output->emplace_back(
250 fb_channel, railcom_channel, RailcomPacket::GARBAGE, 0);
251 break;
252 }
253 for (int i = 1; i < len; ++i, ++ofs)
254 {
255 arg <<= 6;
256 uint8_t decoded = railcom_decode[ptr[ofs + 1]];
257 if (decoded >= 64)
258 {
259 type = RailcomPacket::GARBAGE;
260 }
261 arg |= decoded;
262 }
263 output->emplace_back(fb_channel, railcom_channel, type, arg);
264 }
265}
266
268 const dcc::Feedback &fb, std::vector<struct RailcomPacket> *output)
269{
270 output->clear();
271 if (fb.channel == 0xff)
272 return; // Occupancy feedback information
273 if (fb.ch1Size == 1 && (railcom_decode[fb.ch1Data[0]] != RailcomDefs::INV) && fb.ch2Size >= 1)
274 {
275 // Railcom channel 1 should have 0 or 2 bytes according to the standard.
276 //
277 // There is probably a mistake in the placement of the second window
278 // (i.e., a timing problem in the decoder). Let's concatenate the two
279 // channels and parse them together.
280 uint8_t data[8];
281 memcpy(data, fb.ch1Data, fb.ch1Size);
282 memcpy(data + fb.ch1Size, fb.ch2Data, fb.ch2Size);
283 parse_internal(fb.channel, 2, data, fb.ch1Size + fb.ch2Size, output);
284 return;
285 }
286 for (bool ch1 : {true, false})
287 {
288 uint8_t size;
289 const uint8_t *ptr;
290 if (ch1)
291 {
292 size = fb.ch1Size;
293 ptr = fb.ch1Data;
294 }
295 else
296 {
297 size = fb.ch2Size;
298 ptr = fb.ch2Data;
299 }
300 parse_internal(fb.channel, ch1 ? 1 : 2, ptr, size, output);
301 }
302}
303
304// static
305void RailcomDefs::add_did_feedback(uint64_t decoder_id, Feedback *fb)
306{
307 fb->ch1Size = 2;
308 fb->ch2Size = 6;
309 append12(
310 RMOB_LOGON_ENABLE_FEEDBACK, (decoder_id >> 36) & 0xff, fb->ch1Data);
311 append36((decoder_id >> 32) & 0xf, (decoder_id & 0xffffffffu), fb->ch2Data);
312}
313
314// static
315void RailcomDefs::add_shortinfo_feedback(uint16_t requested_address,
316 uint8_t max_fn, uint8_t psupp, uint8_t ssupp, Feedback *fb)
317{
319 requested_address &= 0x3FFF;
320 requested_address |= 0x8000;
321 m.update16(requested_address >> 8);
322 m.update16(requested_address & 0xff);
323 m.update16(max_fn);
324 m.update16(psupp);
325 m.update16(ssupp);
326 fb->ch1Size = 2;
327 fb->ch2Size = 6;
328 append12(
329 requested_address >> 12, (requested_address >> 4) & 0xff, fb->ch1Data);
330 uint32_t lp = (uint32_t(max_fn) << 24) | (uint32_t(psupp) << 16) |
331 (uint32_t(ssupp) << 8) | m.get();
332 append36(requested_address & 0xf, lp, fb->ch2Data);
333}
334
335// static
336void RailcomDefs::add_assign_feedback(uint8_t changeflags, uint16_t changecount,
337 uint8_t supp2, uint8_t supp3, Feedback *fb)
338{
339 changecount &= 0xFFF;
340 fb->ch1Size = 2;
341 fb->ch2Size = 6;
342
344 uint8_t h = (RMOB_LOGON_ASSIGN_FEEDBACK << 4) | (changeflags >> 4);
345 m.update16(h);
346 h = ((changeflags & 0xf) << 4) | (changecount >> 8);
347 m.update16(h);
348 append12(RMOB_LOGON_ASSIGN_FEEDBACK, changeflags, fb->ch1Data);
349
350 h = changecount & 0xff;
351 m.update16(h);
352 m.update16(supp2);
353 m.update16(supp3);
354
355 uint32_t lp =
356 ((changecount & 0xff) << 24) | (supp2 << 16) | (supp3 << 8) | m.get();
357 append36(changecount >> 8, lp, fb->ch2Data);
358}
359
360} // namespace dcc
void parse_railcom_data(const dcc::Feedback &fb, std::vector< struct RailcomPacket > *output)
Interprets the data from a railcom feedback.
Definition RailCom.cxx:267
const uint8_t railcom_decode[256]
Table for 8-to-6 decoding of railcom data.
Definition RailCom.cxx:47
const uint8_t railcom_encode[64]
Table for 6-to-8 encoding of railcom data.
Definition RailCom.cxx:83
void parse_internal(uint8_t fb_channel, uint8_t railcom_channel, const uint8_t *ptr, unsigned size, std::vector< struct RailcomPacket > *output)
Helper function to parse a part of a railcom packet.
Definition RailCom.cxx:162
Helper class for computing CRC-8 according to Dallas/Maxim specification for 1-wire protocol.
Definition Crc.hxx:83
void update16(uint8_t message_byte)
Processes one byte of the incoming message.
Definition Crc.hxx:164
uint8_t get()
Definition Crc.hxx:97
uint8_t ch2Size
Number of bytes in channel two.
Definition railcom.h:49
uint8_t ch1Size
Number of bytes in channel one.
Definition railcom.h:45
uint8_t channel
Used by multi-channel railcom receiver drivers.
Definition railcom.h:54
uint8_t ch1Data[2]
Payload of channel 1.
Definition railcom.h:47
uint8_t ch2Data[6]
Payload of channel 2.
Definition railcom.h:51
Structure used for reading (railcom) feedback data from DCC / Railcom device drivers.
Definition RailCom.hxx:50
static void add_shortinfo_feedback(uint16_t requested_address, uint8_t max_fn, uint8_t psupp, uint8_t ssupp, Feedback *fb)
Creates a ShortInfo feedback.
Definition RailCom.cxx:315
static void add_did_feedback(uint64_t decoder_id, Feedback *fb)
Creates a Logon Enable feedback with the decoder unique ID.
Definition RailCom.cxx:305
@ ACK
Railcom ACK; the decoder received the message ok.
Definition RailCom.hxx:103
@ INV
invalid value (not conforming to the 4bit weighting requirement)
Definition RailCom.hxx:100
@ RESVD2
Reserved for future expansion.
Definition RailCom.hxx:114
@ RESVD1
Reserved for future expansion.
Definition RailCom.hxx:112
@ BUSY
The decoder is busy; send the packet again.
Definition RailCom.hxx:109
@ NACK
The decoder rejected the packet.
Definition RailCom.hxx:105
static void append12(uint8_t nibble, uint8_t data, uint8_t *dst)
Encodes 12 bits of useful payload into 16 bits of UART data to transmit.
Definition RailCom.hxx:145
static void append36(uint8_t nibble, uint32_t data, uint8_t *dst)
Encodes a 36-bit railcom datagram into UART bytes.
Definition RailCom.hxx:155
static void add_assign_feedback(uint8_t changeflags, uint16_t changecount, uint8_t supp2, uint8_t supp3, Feedback *fb)
Creates a Logon Assign feedback.
Definition RailCom.cxx:336