Мне нужно рассчитать значение 16-битной контрольной суммы CCITT для данных, переданных в качестве параметра, вместе с длиной. Если я заполню свой массив TempStr тестовыми данными «123456789», использую полином 0x8408 с длиной, исключающей нулевой символ завершения, я получу строку результата 6E90 (Hex). Вместе с нулевым символом завершения я получаю 907A. Когда я заменяю полином на 0x1201, я получаю результаты 29E2 (Hex) и EFE8 (Hex) с и без символа завершения.
Мои вопросы:
Нужно ли рассчитывать CRC с нулевым символом завершения или без него, чтобы получить правильное значение?
Использовать ли в алгоритме полином 0x1201 или обратный полином 0x8408?
Правильный ли CRC приведенных данных 0x29B1? Мне нужно правильное значение, чтобы определить, работает ли функция правильно.
Правильно ли рассчитан этот конкретный тип CRC?
wData = (без знака int) 0xff & * PDATA ++ ??
Если кто-то может объяснить мне, что не так и как исправить мою проблему, я был бы очень признателен.
Спасибо
Это код, который использует и отображает функцию Calculate_CRC16:
CHAR_t TestStr[] = {"123456789"};
unsigned short CrcTest = calculate_CRC16(TestStr,sizeof(TestStr)-1);
QString CrcDisplay = QString("CrcTest : %1").arg(CrcTest);
ui->txtDebug->setText(CrcDisplay);
Это функция Calculate_CRC16:
UINT16_t MainWindow::calculate_CRC16(CHAR_t* pData, UINT16_t wLength)
{
UCHAR_t i;
UINT16_t wData;
UINT16_t wCrc = 0xffff;
if (wLength == 0)
return (~wCrc);
do
{
for (i=0, wData=(unsigned int)0xff & *pData++; i < 8; i++, wData >>= 1)
{
if ((wCrc & 0x0001) ^ (wData & 0x0001))
wCrc = (wCrc >> 1) ^ CRC_POLY;
else wCrc >>= 1;
}
} while (--wLength);
wCrc = ~wCrc;
wData = wCrc;
wCrc = (wCrc << 8) | (wData >> 8 & 0xff);
return (wCrc);
}
Результат 0x29b1
для «ложный» CCITT CRC-16 (ссылка на каталог CRC). Который, видимо, тот, который вам нужен. Из каталога:
width=16 poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1 name="CRC-16/CCITT-FALSE"
Так что нет битов разворота (refin
, refout
ложный). CRC инициализируется с 0xffff
и не подвергается постобработке.
Чтобы исправить ваш код с наименьшими изменениями:
if (wLength == 0)
return wCrc;
do
{
for (i=0, wData=((unsigned int)0xff & *pData++) << 8; i < 8; i++, wData <<= 1)
{
if ((wCrc & 0x8000) ^ (wData & 0x8000))
wCrc = (wCrc << 1) ^ 0x1021;
else wCrc <<= 1;
}
} while (--wLength);
return wCrc & 0xffff;
или сделать это более разумно:
while (wLength--) {
wCrc ^= *(unsigned char *)pData++ << 8;
for (i=0; i < 8; i++)
wCrc = wCrc & 0x8000 ? (wCrc << 1) ^ 0x1021 : wCrc << 1;
}
return wCrc & 0xffff;
Если вы посмотрите, он рассчитает CRC для разных строк (или шестнадцатеричных последовательностей, для проверки с или без NUL)
http://www.lammertbies.nl/comm/info/crc-calculation.html
В соответствии с этим, вы НЕ должны рассчитывать, включая завершающий ноль, чтобы получить значение 0x29B1 для вашего расчета.
Поскольку вы начинаете с младшего бита, вы должны использовать «не обратный» полином.
Я думаю, что проблема в том, что вы перемещаетесь в неправильном направлении, когда вы перемещаете «wCrc» в своих вычислениях.
Другими словами:
wCrc = (wCrc >> 1) ^ CRC_POLY;
должно быть:
wCrc = (wCrc << 1) ^ CRC_POLY;
и аналогично:
wCrc >>= 1;
должно быть:
wCrc <<= 1;
Однако я не уверен на 100%.
Существует ряд различных вариантов алгоритмов CRC.
Этот последний момент вызывает путаницу. Возвращаясь к теории CRC, CRC можно рассматривать как длинное деление в GF (2), где результатом является остаток от длинного деления. Чтобы сделать правильный расчет в соответствии с базовой теорией, N нулевые биты должны быть добавлены в конец сообщения, чтобы получить правильный ответ. Есть алгоритмы CRC, которые делают вычисления таким образом.
Однако чаще всего алгоритмы CRC выполняются другим способом, так что сообщение не нуждается в нулевых битах, добавляемых в конец сообщения. Этот расчет часто называют «прямым алгоритмом». Это более удобно в использовании и функционально эквивалентно, Кроме что любое «начальное значение» алгоритма необходимо изменить, чтобы учесть этот вариант алгоритма.
В случае CRC-16 / CCITT это приводит к путанице относительно правильного начального значения: должно ли оно быть 0xFFFF
или же 0x1D0F
? Можно утверждать, что 0xFFFF
является правильным начальным значением для алгоритма, который добавляет расширенные биты к сообщению. Если используется «прямой алгоритм», начальное значение должно быть установлено на 0x1D0F
чтобы получить тот же результат.
Таким образом, вы должны знать об этой разнице и использовать ту, которая необходима для взаимодействия с программой / системой, с которой вы взаимодействуете.
Дальнейшее чтение: