From 29e9b8cef5b79082e48fde6abef1ac8b22b07851 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 30 Sep 2024 11:00:37 +0100 Subject: [PATCH] Railcom Decoder --- IO_I2CRailcom.h | 5 ++ Railcom.cpp | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ Railcom.h | 35 +++++++++++ 3 files changed, 204 insertions(+) create mode 100644 Railcom.cpp create mode 100644 Railcom.h diff --git a/IO_I2CRailcom.h b/IO_I2CRailcom.h index 609795d..a8256f7 100644 --- a/IO_I2CRailcom.h +++ b/IO_I2CRailcom.h @@ -50,6 +50,7 @@ #include "I2CManager.h" #include "DIAG.h" #include "DCCWaveform.h" +#include "Railcom.h" // Debug and diagnostic defines, enable too many will result in slowing the driver #define DIAG_I2CRailcom @@ -61,6 +62,7 @@ private: uint8_t _UART_CH=0x00; byte _inbuf[65]; byte _outbuf[2]; + Railcom _channelMonitors[2]; public: // Constructor I2CRailcom(VPIN firstVpin, int nPins, I2CAddress i2cAddress){ @@ -120,6 +122,9 @@ public: for (int i = 0; i < inlength; i++){ DIAG(F("[0x%x]: 0x%x"), i, _inbuf[i]); } + auto locoid=_channelMonitors[_UART_CH].getChannel1Loco(_inbuf); + DIAG(F("Railcom Channel1=%d"), locoid); + #endif } diff --git a/Railcom.cpp b/Railcom.cpp new file mode 100644 index 0000000..31acee0 --- /dev/null +++ b/Railcom.cpp @@ -0,0 +1,164 @@ +/* + * SEE ADDITIONAL COPYRIGHT ATTRIBUTION BELOW + * © 2024 Chris Harlow + * All rights reserved. + * + * This file is part of DCC-EX + * + * This 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 3 of the License, or + * (at your option) any later version. + * + * It 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 CommandStation. If not, see . + */ + +/** Sections of this code (the decode table constants) + * are taken from openmrn under the following copyright. + * + * Copyright (c) 2014, Balazs Racz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + **/ + +#include "Railcom.h" +#include + + /** Table for 8-to-6 decoding of railcom data. This table can be indexed by the + * 8-bit value read from the railcom channel, and the return value will be + * either a 6-bit number, or one of the defined Railcom constrantrs. If the + * value is invalid, the INV constant is returned. */ + + // These values appear in the railcom_decode table to mean special symbols. + static constexpr uint8_t + // highest valid 6-bit value + MAX_VALID = 0x3F, + /// invalid value (not conforming to the 4bit weighting requirement) + INV = 0xff, + /// Railcom ACK; the decoder received the message ok. NOTE: There are + /// two codepoints that map to this. + ACK = 0xfe, + /// The decoder rejected the packet. + NACK = 0xfd, + /// The decoder is busy; send the packet again. This is typically + /// returned when a POM CV write is still pending; the caller must + /// re-try sending the packet later. + BUSY = 0xfc, + + /// Reserved for future expansion. + RESVD1 = 0xfb, + /// Reserved for future expansion. + RESVD2 = 0xfa; + +const uint8_t HIGHFLASH decode[256] = + // 0|8 1|9 2|a 3|b 4|c 5|d 6|e 7|f +{ INV, INV, INV, INV, INV, INV, INV, INV, // 0 + INV, INV, INV, INV, INV, INV, INV, ACK, // 0 + INV, INV, INV, INV, INV, INV, INV, 0x33, // 1 + INV, INV, INV, 0x34, INV, 0x35, 0x36, INV, // 1 + INV, INV, INV, INV, INV, INV, INV, 0x3A, // 2 + INV, INV, INV, 0x3B, INV, 0x3C, 0x37, INV, // 2 + INV, INV, INV, 0x3F, INV, 0x3D, 0x38, INV, // 3 + INV, 0x3E, 0x39, INV, NACK, INV, INV, INV, // 3 + INV, INV, INV, INV, INV, INV, INV, 0x24, // 4 + INV, INV, INV, 0x23, INV, 0x22, 0x21, INV, // 4 + INV, INV, INV, 0x1F, INV, 0x1E, 0x20, INV, // 5 + INV, 0x1D, 0x1C, INV, 0x1B, INV, INV, INV, // 5 + INV, INV, INV, 0x19, INV, 0x18, 0x1A, INV, // 6 + INV, 0x17, 0x16, INV, 0x15, INV, INV, INV, // 6 + INV, 0x25, 0x14, INV, 0x13, INV, INV, INV, // 7 + 0x32, INV, INV, INV, INV, INV, INV, INV, // 7 + INV, INV, INV, INV, INV, INV, INV, RESVD2, // 8 + INV, INV, INV, 0x0E, INV, 0x0D, 0x0C, INV, // 8 + INV, INV, INV, 0x0A, INV, 0x09, 0x0B, INV, // 9 + INV, 0x08, 0x07, INV, 0x06, INV, INV, INV, // 9 + INV, INV, INV, 0x04, INV, 0x03, 0x05, INV, // a + INV, 0x02, 0x01, INV, 0x00, INV, INV, INV, // a + INV, 0x0F, 0x10, INV, 0x11, INV, INV, INV, // b + 0x12, INV, INV, INV, INV, INV, INV, INV, // b + INV, INV, INV, RESVD1, INV, 0x2B, 0x30, INV, // c + INV, 0x2A, 0x2F, INV, 0x31, INV, INV, INV, // c + INV, 0x29, 0x2E, INV, 0x2D, INV, INV, INV, // d + 0x2C, INV, INV, INV, INV, INV, INV, INV, // d + INV, BUSY, 0x28, INV, 0x27, INV, INV, INV, // e + 0x26, INV, INV, INV, INV, INV, INV, INV, // e + ACK, INV, INV, INV, INV, INV, INV, INV, // f + INV, INV, INV, INV, INV, INV, INV, INV, // f +}; + /// Packet identifiers from Mobile Decoders. + enum RailcomMobilePacketId + { + RMOB_POM = 0, + RMOB_ADRHIGH = 1, + RMOB_ADRLOW = 2, + RMOB_EXT = 3, + RMOB_DYN = 7, + RMOB_XPOM0 = 8, + RMOB_XPOM1 = 9, + RMOB_XPOM2 = 10, + RMOB_XPOM3 = 11, + RMOB_SUBID = 12, + RMOB_LOGON_ASSIGN_FEEDBACK = 13, + RMOB_LOGON_ENABLE_FEEDBACK = 15, +}; + + +Railcom::Railcom() { + haveHigh=false; + haveLow=false; +} + +int16_t Railcom::getChannel1Loco(uint8_t * inbound) { + auto v1=GETHIGHFLASH(decode,inbound[0]); + if (v1>MAX_VALID) return -1; + auto v2=GETHIGHFLASH(decode,inbound[1]); + if (v2>MAX_VALID) return -1; + auto packet=(v1<<6) | v2; + // packet is 12 bits TTTTDDDDDDDD + auto type=packet>>8; + auto data= packet & 0xFF; + if (type==RMOB_ADRHIGH) { + holdoverHigh=data; + haveHigh=true; + } + else if (type==RMOB_ADRLOW) { + holdoverLow=data; + haveLow=true; + } + else if (type==RMOB_EXT) { + /* ignore*/ + } + else { + haveHigh=false; + haveLow=false; + } + if (haveHigh && haveLow) return ((holdoverHigh<<8)| holdoverLow); + return -1; // no loco info available +} diff --git a/Railcom.h b/Railcom.h new file mode 100644 index 0000000..0873c8f --- /dev/null +++ b/Railcom.h @@ -0,0 +1,35 @@ +/* + * © 2024 Chris Harlow + * All rights reserved. + * + * This file is part of DCC-EX + * + * This 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 3 of the License, or + * (at your option) any later version. + * + * It 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 CommandStation. If not, see . + */ + +#ifndef Railcom_h +#define Railcom_h +#include "Arduino.h" + +class Railcom { + public: + Railcom(); + int16_t getChannel1Loco(uint8_t * inbound); + + private: + uint8_t holdoverHigh,holdoverLow; +bool haveHigh,haveLow; +}; + +#endif \ No newline at end of file