Расчет CRC-ITU в C#
Я новичок в C#. Мне нужно рассчитать CRC-ITU для пакета, полученного от устройств GPS. В документации представлен код на C, но я не знаю, как перенести его на C#, кто-нибудь может мне помочь?
Вот алгоритм CRC-ITU в C:
static const U16 crctab16[] =
{
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
};
// calculate 16 bits CRC of the given length data.
U16 GetCrc16(const U8* pData, int nLength)
{
U16 fcs = 0xffff; // Initialize
while(nLength>0){
fcs = (fcs >> 8) ^ crctab16[(fcs ^ *pData) & 0xff];
nLength--;
pData++;
}
return ~fcs; // Negate
}
// Check whether the 16 bits CRC of the given length data is right.
BOOL IsCrc16Good(const U8* pData, int nLength)
{
U16 fcs = 0xffff; // Initialize
while(nLength>0){
fcs = (fcs >> 8) ^ crctab16[(fcs ^ *pData) & 0xff];
nLength--;
pData++;
}
return (fcs == 0xf0b8); // 0xf0b8 is CRC-ITU的"Magic Value"
}
Я также нашел код C# по http://ppcode.blogbus.com/logs/1656947.html но я не знаю, как его использовать, вот код
public class CrcITUTable
{
static ushort [] crctab16 =
{
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
};
public CrcITUTable()
{
//
// TODO: 在此处添加构造函数逻辑
//
}
unsafe public ushort GetCrcITU_T(byte * pData,int nLength,out string crcCode)
{
ushort fcs=0xFFFF;
ushort uRlt;
string strTmp;
while(nLength>0)
{
fcs= (ushort) ((ushort)(fcs>>8)^ crctab16[(fcs ^ *pData) &0xFF ]);
nLength--;
pData++;
}
uRlt=(ushort)~fcs;
strTmp=uRlt.ToString("X4") ;
crcCode=strTmp.Substring(2,2) + " " + strTmp.Substring(0,2);
return uRlt;
}
unsafe public bool IsCrcITUGood_T(byte * pData,int nLength)
{
ushort fcs=0xFFFF;
while(nLength>0)
{
fcs= (ushort) ((ushort)(fcs>>8)^ crctab16[(fcs ^ *pData) &0xFF ]);
nLength--;
pData++;
}
return (fcs==0xF0B8);
}
}
Вот несколько примеров в документации:
полный пакет: 0x78 0x78 0x0D 0x01 0x01 0x23 0x45 0x67 0x89 0x01 0x23 0x45 0x00 0x01 0x8C 0xDD 0x0D 0x0A
Начальный бит: 0x78 0x78 Длина: 0x0D Номер протокола: 0x01 Идентификатор устройства: 0x01 0x23 0x45 0x67 0x89 0x01 0x23 0x45 Серийный номер: 0x00 0x01 Проверка CRC: 0x8C 0xDD Стоповый бит: 0x0D 0x0A
цитата из документации:
Устройство или сервер могут судить о точности полученных данных с помощью идентификационного кода. Иногда, из-за электронного шума или других помех, данные будут немного изменены в процессе транзита. В этом случае идентификация кода может гарантировать, что ядро или связанное ядро ничего не сделают с такими неправильными данными, что повысит безопасность и эффективность системы. Этот идентификационный код принимает метод идентификации CRC-ITU. Значение CRC-ITU от протокола "Длина пакета" до "Серийный номер информации" в протоколе (включая "Длина пакета" и "Серийный номер информации"). Если получатель получает CRC неверную расчетную информацию, игнорируйте ее и отбрасывайте эти данные. пакет.
Так у кого-нибудь есть идея, как получить значение crc из приведенного выше примера? Спасибо
3 ответа
Просто следуйте инструкциям документа
Значение CRC-ITU от протокола "Длина пакета" до "Серийный номер информации" в протоколе (включая "Длина пакета" и "Серийный номер информации").
Таким образом, мы берем данные от длины до серийного номера
0x0D 0x01 0x01 0x23 0x45 0x67 0x89 0x01 0x23 0x45 0x00 0x01
Теперь мы используем этот набор чисел и передаем его функции CRC. Код, на который вы ссылаетесь, использует небезопасные указатели, я не знаю, нужны ли они вам или нет, так как вы не публиковали ни одного из тех мест, где вы его используете. Если у вас есть данные в байтовом массиве, вы можете переписать функцию, чтобы просто передать байтовый массив вместо указателя, тогда код больше не нужно будет помечать как небезопасный.
Вот моя очищенная версия, которая использует байты
static void Main(string[] args)
{
byte[] dataPacket = { 0x78, 0x78, 0x0D, 0x01, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x00, 0x01, 0x8C, 0xDD, 0x0D, 0x0A };
var crc16 = GetCrc16(dataPacket, 2, 12);
if (crc16 == ReadBigEndianUInt16(dataPacket, 14)))
{
Debugger.Break();
//use packet.
}
Debugger.Break();
}
public static UInt16 ReadBigEndianUInt16(byte[] data, int offset)
{
if(BitConverter.IsLittleEndian == true)
{
var bytes = new byte[2] {data[offset+1], data[offset]}
return BitConverter.ToUInt16(bytes, 0);
}
else
{
return BitConverter.ToUInt16(data, offset);
}
}
public static ushort GetCrc16(byte[] data, int offset, int length)
{
ushort fcs = 0xFFFF;
for (int i = offset; i < length + offset; i++)
{
fcs = (ushort)((ushort)(fcs >> 8) ^ crctab16[(fcs ^ data[i]) & 0xFF]);
}
return (ushort)(~fcs);
}
public static bool IsCrcITUGood(byte[] data, int offset, int length)
{
ushort fcs = 0xFFFF;
for (int i = offset; i < length + offset; i++)
{
fcs = (ushort)((ushort)(fcs >> 8) ^ crctab16[(fcs ^ data[i]) & 0xFF]);
}
return (fcs == 0xF0B8); //magic value
}
static ushort[] crctab16 = /*(snip)*/;
Похоже, что CRC в пакете хранится с большим порядком байтов, а код C# будет генерировать порядок байтов. Таким образом, вам нужно будет перевернуть два байта CRC, прежде чем сравнивать их.
Как примечание, я думаю, что IsCrcITUGood
Метод не из спецификации GPS. Я думаю, что бы вы ни брали этот пример кода, у него был специальный набор байтов, чтобы CRC всегда был равен 0xF0B8
, В ваших данных он дает вам ожидаемое значение CRC. Я не мог найти способ сделать CRC равным 0xF0B8
Ни один из приведенных выше кодов не работал у меня, но я вдохновил себя на то, чтобы получить мою рабочую версию кода.
private static ushort GetCrc16(byte[] pData, int offset, int length)
{
ushort[] crctab16 = { 0X0000, 0X1189, 0X2312, 0X329B, 0X4624, 0X57AD, 0X6536, 0X74BF, 0X8C48, 0X9DC1, 0XAF5A, 0XBED3, 0XCA6C, 0XDBE5, 0XE97E, 0XF8F7, 0X1081, 0X0108, 0X3393, 0X221A, 0X56A5, 0X472C, 0X75B7, 0X643E, 0X9CC9, 0X8D40, 0XBFDB, 0XAE52, 0XDAED, 0XCB64, 0XF9FF, 0XE876, 0X2102, 0X308B, 0X0210, 0X1399, 0X6726, 0X76AF, 0X4434, 0X55BD, 0XAD4A, 0XBCC3, 0X8E58, 0X9FD1, 0XEB6E, 0XFAE7, 0XC87C, 0XD9F5, 0X3183, 0X200A, 0X1291, 0X0318, 0X77A7, 0X662E, 0X54B5, 0X453C, 0XBDCB, 0XAC42, 0X9ED9, 0X8F50, 0XFBEF, 0XEA66, 0XD8FD, 0XC974, 0X4204, 0X538D, 0X6116, 0X709F, 0X0420, 0X15A9, 0X2732, 0X36BB, 0XCE4C, 0XDFC5, 0XED5E, 0XFCD7, 0X8868, 0X99E1, 0XAB7A, 0XBAF3, 0X5285, 0X430C, 0X7197, 0X601E, 0X14A1, 0X0528, 0X37B3, 0X263A, 0XDECD, 0XCF44, 0XFDDF, 0XEC56, 0X98E9, 0X8960, 0XBBFB, 0XAA72, 0X6306, 0X728F, 0X4014, 0X519D, 0X2522, 0X34AB, 0X0630, 0X17B9, 0XEF4E, 0XFEC7, 0XCC5C, 0XDDD5, 0XA96A, 0XB8E3, 0X8A78, 0X9BF1, 0X7387, 0X620E, 0X5095, 0X411C, 0X35A3, 0X242A, 0X16B1, 0X0738, 0XFFCF, 0XEE46, 0XDCDD, 0XCD54, 0XB9EB, 0XA862, 0X9AF9, 0X8B70, 0X8408, 0X9581, 0XA71A, 0XB693, 0XC22C, 0XD3A5, 0XE13E, 0XF0B7, 0X0840, 0X19C9, 0X2B52, 0X3ADB, 0X4E64, 0X5FED, 0X6D76, 0X7CFF, 0X9489, 0X8500, 0XB79B, 0XA612, 0XD2AD, 0XC324, 0XF1BF, 0XE036, 0X18C1, 0X0948, 0X3BD3, 0X2A5A, 0X5EE5, 0X4F6C, 0X7DF7, 0X6C7E, 0XA50A, 0XB483, 0X8618, 0X9791, 0XE32E, 0XF2A7, 0XC03C, 0XD1B5, 0X2942, 0X38CB, 0X0A50, 0X1BD9, 0X6F66, 0X7EEF, 0X4C74, 0X5DFD, 0XB58B, 0XA402, 0X9699, 0X8710, 0XF3AF, 0XE226, 0XD0BD, 0XC134, 0X39C3, 0X284A, 0X1AD1, 0X0B58, 0X7FE7, 0X6E6E, 0X5CF5, 0X4D7C, 0XC60C, 0XD785, 0XE51E, 0XF497, 0X8028, 0X91A1, 0XA33A, 0XB2B3, 0X4A44, 0X5BCD, 0X6956, 0X78DF, 0X0C60, 0X1DE9, 0X2F72, 0X3EFB, 0XD68D, 0XC704, 0XF59F, 0XE416, 0X90A9, 0X8120, 0XB3BB, 0XA232, 0X5AC5, 0X4B4C, 0X79D7, 0X685E, 0X1CE1, 0X0D68, 0X3FF3, 0X2E7A, 0XE70E, 0XF687, 0XC41C, 0XD595, 0XA12A, 0XB0A3, 0X8238, 0X93B1, 0X6B46, 0X7ACF, 0X4854, 0X59DD, 0X2D62, 0X3CEB, 0X0E70, 0X1FF9, 0XF78F, 0XE606, 0XD49D, 0XC514, 0XB1AB, 0XA022, 0X92B9, 0X8330, 0X7BC7, 0X6A4E, 0X58D5, 0X495C, 0X3DE3, 0X2C6A, 0X1EF1, 0X0F78 };
ushort fcs = 0xFFFF;
ushort fcs2 = 0xFFFF;
for (int i = offset; i < length + offset; i++)
{
fcs = (ushort)((fcs >> 8) ^ crctab16[(fcs ^ pData[i]) & 0xff]);
}
return ((ushort)((fcs & fcs2) ^ fcs2));
}
public static ushort GetCrc16Inverted(byte[] pData, int offset, int length)
{
return ReadBigEndianUInt16(BitConverter.GetBytes(GetCrc16(pData, offset, length)), 0);
}
private static ushort ReadBigEndianUInt16(byte[] data, int offset)
{
if (BitConverter.IsLittleEndian == true)
{
var bytes = new byte[2] { data[offset + 1], data[offset] };
return BitConverter.ToUInt16(bytes, 0);
}
else
{
return BitConverter.ToUInt16(data, offset);
}
}
[Test]
public void Crc_Itu_16_Should_Be_Generated_Correctly()
{
byte[] dataPacket = { 0x78, 0x78, 0x0D, 0x01, 0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x23, 0x45, 0x00, 0x01, 0x8C, 0xDD, 0x0D, 0x0A };
var crc = ConcoxTrackerManager.GetCrc16Inverted(dataPacket, 2, 12);
byte[] crcErrorExpected = { 0x8C, 0xDD };
var crcErrCheckFlipped = BitConverter.ToUInt16(crcErrorExpected, 0);
Assert.True(crc == crcErrCheckFlipped);
}
Нашел очень мало ссылок на вычисление CRC 16, но нашел один из ответов SO в javascript полезным.
В случае, если кто-то ищет тот же расчет CRC 16 с использованием Python, создайте публичный гист вместе с примером рабочего скрипта Python:
https://gist.github.com/mrudang-vora/6a0767be085a275c0df3
Надеюсь, это поможет кому-то.