Контрольная сумма P1 Meter CRC16

Я пытаюсь написать модульный тест для контрольной суммы CRC16 для сообщения счетчика P1 (и мне трудно это сделать правильно...).

Что у меня есть:

  • Сообщение счетчика P1
  • Раздели его, чтобы у меня была правильная часть согласно спецификации (см. Ниже)
  • Превратил его в байтовый массив (кодировка ASCII)
  • Рассчитать контрольную сумму CRC16
  • Сравните это с контрольной суммой

Учитывая тот факт, что я разместил вопрос на SO, результат не в порядке...

Пожалуйста, терпите меня, я поделюсь тем, что у меня есть ниже:

Сообщение P1

    private static readonly string telegramText =
        "/KFM5KAIFA-METER\n" +
        "\n" +
        "1-3:0.2.8(42)\n" +
        "0-0:1.0.0(170124213128W)\n" +
        "0-0:96.1.1(4530303236303030303234343934333135)\n" +
        "1-0:1.8.1(000306.946*kWh)\n" +
        "1-0:1.8.2(000210.088*kWh)\n" +
        "1-0:2.8.1(000000.000*kWh)\n" +
        "1-0:2.8.2(000000.000*kWh)\n" +
        "0-0:96.14.0(0001)\n" +
        "1-0:1.7.0(02.793*kW)\n" +
        "1-0:2.7.0(00.000*kW)\n" +
        "0-0:96.7.21(00001)\n" +
        "0-0:96.7.9(00001)\n" +
        "1-0:99.97.0(1)(0-0:96.7.19)(000101000006W)(2147483647*s)\n" +
        "1-0:32.32.0(00000)\n" +
        "1-0:52.32.0(00000)\n" +
        "1-0:72.32.0(00000)\n" +
        "1-0:32.36.0(00000)\n" +
        "1-0:52.36.0(00000)\n" +
        "1-0:72.36.0(00000)\n" +
        "0-0:96.13.1()\n" +
        "0-0:96.13.0()\n" +
        "1-0:31.7.0(003*A)\n" +
        "1-0:51.7.0(005*A)\n" +
        "1-0:71.7.0(005*A)\n" +
        "1-0:21.7.0(00.503*kW)\n" +
        "1-0:41.7.0(01.100*kW)\n" +
        "1-0:61.7.0(01.190*kW)\n" +
        "1-0:22.7.0(00.000*kW)\n" +
        "1-0:42.7.0(00.000*kW)\n" +
        "1-0:62.7.0(00.000*kW)\n" +
        "0-1:24.1.0(003)\n" +
        "0-1:96.1.0(4730303331303033333738373931363136)\n" +
        "0-1:24.2.1(170124210000W)(00671.790*m3)\n" +
        "!29ED\n";

Спецификация

Источник можно найти здесь

CRC - это значение CRC16, вычисленное по предыдущим символам в сообщении данных (от "/" до "!" С использованием полинома: x16 + x15 + x2 + 1).

CRC16 не использует XOR-вход, XOR-выход и сначала вычисляется с младшим значащим битом. Значение представляется в виде 4 шестнадцатеричных символов (сначала MSB).

Получить байты и сделать CRC

Прочитанное сообщение - это сообщение P1 выше, но затем от '/' до '!' (включая '/' и включая '!'), как продиктовано приведенной выше спецификацией.

var bytes = Encoding.ASCII.GetBytes(_readMessage);
var computeChecksum = new Crc16().ComputeChecksum(bytes);

Наконец, код CRC

Источник можно найти здесь

public class Crc16
{
    const ushort polynomial = 0xA001;
    ushort[] table = new ushort[256];

    public ushort ComputeChecksum(byte[] bytes)
    {
        ushort crc = 0;
        for (int i = 0; i < bytes.Length; ++i)
        {
            byte index = (byte)(crc ^ bytes[i]);
            crc = (ushort)((crc >> 8) ^ table[index]);
        }
        return crc;
    }

    public byte[] ComputeChecksumBytes(byte[] bytes)
    {
        ushort crc = ComputeChecksum(bytes);
        return BitConverter.GetBytes(crc);
    }

    public Crc16()
    {
        ushort value;
        ushort temp;
        for (ushort i = 0; i < table.Length; ++i)
        {
            value = 0;
            temp = i;
            for (byte j = 0; j < 8; ++j)
            {
                if (((value ^ temp) & 0x0001) != 0)
                {
                    value = (ushort)((value >> 1) ^ polynomial);
                }
                else
                {
                    value >>= 1;
                }
                temp >>= 1;
            }
            table[i] = value;
        }
    }
}

Неудачный результат

Контрольная сумма телеграммы P1 должна быть: 0x29ED, но, к сожалению, я вычисляю 0x6500.

Может ли кто-нибудь указать мне правильное направление?

Обновить

@ Марк Адлер, я исправил найденную проблему, но все еще вычисляю неверную контрольную сумму. Я снял все не относящиеся к делу части ниже, не могли бы вы быть так любезны, чтобы посмотреть еще раз? Вы можете просто скопировать вставить код ниже, если хотите. Или отправьте обратно код, который вычисляет правильную контрольную сумму.

Заранее большое спасибо!

class Program
{
    private static readonly string telegramText =
        "/KFM5KAIFA-METER\r\n" +
        "\r\n" +
        "1-3:0.2.8(42)\r\n" +
        "0-0:1.0.0(170124213128W)\r\n" +
        "0-0:96.1.1(4530303236303030303234343934333135)\r\n" +
        "1-0:1.8.1(000306.946*kWh)\r\n" +
        "1-0:1.8.2(000210.088*kWh)\r\n" +
        "1-0:2.8.1(000000.000*kWh)\r\n" +
        "1-0:2.8.2(000000.000*kWh)\r\n" +
        "0-0:96.14.0(0001)\r\n" +
        "1-0:1.7.0(02.793*kW)\r\n" +
        "1-0:2.7.0(00.000*kW)\r\n" +
        "0-0:96.7.21(00001)\r\n" +
        "0-0:96.7.9(00001)\r\n" +
        "1-0:99.97.0(1)(0-0:96.7.19)(000101000006W)(2147483647*s)\r\n" +
        "1-0:32.32.0(00000)\r\n" +
        "1-0:52.32.0(00000)\r\n" +
        "1-0:72.32.0(00000)\r\n" +
        "1-0:32.36.0(00000)\r\n" +
        "1-0:52.36.0(00000)\r\n" +
        "1-0:72.36.0(00000)\r\n" +
        "0-0:96.13.1()\r\n" +
        "0-0:96.13.0()\r\n" +
        "1-0:31.7.0(003*A)\r\n" +
        "1-0:51.7.0(005*A)\r\n" +
        "1-0:71.7.0(005*A)\r\n" +
        "1-0:21.7.0(00.503*kW)\r\n" +
        "1-0:41.7.0(01.100*kW)\r\n" +
        "1-0:61.7.0(01.190*kW)\r\n" +
        "1-0:22.7.0(00.000*kW)\r\n" +
        "1-0:42.7.0(00.000*kW)\r\n" +
        "1-0:62.7.0(00.000*kW)\r\n" +
        "0-1:24.1.0(003)\r\n" +
        "0-1:96.1.0(4730303331303033333738373931363136)\r\n" +
        "0-1:24.2.1(170124210000W)(00671.790*m3)\r\n" +
        "!";

    static void Main(string[] args)
    {
        var bytes = Encoding.ASCII.GetBytes(telegramText);
        var computeChecksum = new Crc16().ComputeChecksum(bytes);
    }
}

public class Crc16
{
    const ushort polynomial = 0x8005;
    ushort[] table = new ushort[256];

    public ushort ComputeChecksum(byte[] bytes)
    {
        ushort crc = 0;
        for (int i = 0; i < bytes.Length; ++i)
        {
            byte index = (byte)(crc ^ bytes[i]);
            crc = (ushort)((crc >> 8) ^ table[index]);
        }
        return crc;
    }

    public byte[] ComputeChecksumBytes(byte[] bytes)
    {
        ushort crc = ComputeChecksum(bytes);
        return BitConverter.GetBytes(crc);
    }

    public Crc16()
    {
        ushort value;
        ushort temp;
        for (ushort i = 0; i < table.Length; ++i)
        {
            value = 0;
            temp = i;
            for (byte j = 0; j < 8; ++j)
            {
                if (((value ^ temp) & 0x0001) != 0)
                {
                    value = (ushort)((value >> 1) ^ polynomial);
                }
                else
                {
                    value >>= 1;
                }
                temp >>= 1;
            }
            table[i] = value;
        }
    }
}

1 ответ

Решение

Код CRC выглядит нормально. Проблема состоит в том, что он ожидает возврат каретки, а не только перевод строки. Если вы предшествуете каждому \n с \rтогда вы получите 0x29ed для CRC байтов из / к !включительно.

Другие вопросы по тегам