Как сгенерировать CRC-16 из C#

Я пытаюсь сгенерировать CRC-16 с помощью С#. Аппаратное обеспечение, которое я использую для RS232, требует, чтобы входная строка была HEX. На приведенном ниже снимке экрана показано правильное преобразование. Для теста мне нужно, чтобы 8000 было равно 0xC061, однако метод C#, который генерирует CRC-16, должен иметь возможность преобразовывать любую данную HEX-строку.

Скриншот требуемого вывода.

Я пытался использовать Nito.KitchenSink.CRC

Я также пробовал ниже, который генерирует 8009 при вводе 8000 -

public string CalcCRC16(string strInput)
    {
        ushort crc = 0x0000;
        byte[] data = GetBytesFromHexString(strInput);
        for (int i = 0; i < data.Length; i++)
        {
            crc ^= (ushort)(data[i] << 8);
            for (int j = 0; j < 8; j++)
            {
                if ((crc & 0x8000) > 0)
                    crc = (ushort)((crc << 1) ^ 0x8005);
                else
                    crc <<= 1;
            }
        }
        return crc.ToString("X4");
    }

    public Byte[] GetBytesFromHexString(string strInput)
    {
        Byte[] bytArOutput = new Byte[] { };
        if (!string.IsNullOrEmpty(strInput) && strInput.Length % 2 == 0)
        {
            SoapHexBinary hexBinary = null;
            try
            {
                hexBinary = SoapHexBinary.Parse(strInput);
                if (hexBinary != null)
                {
                    bytArOutput = hexBinary.Value;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        return bytArOutput;
    }

person dynamicuser    schedule 04.04.2014    source источник
comment
Итак, есть некоторый код для генерации CRC (я предполагаю). Какая конкретная проблема/вопрос?   -  person Chris Sinclair    schedule 04.04.2014
comment
Проблема в том, что сгенерированный CRC неверен при использовании приведенного выше кода. Я обновлю вопрос с его текущим выводом.   -  person dynamicuser    schedule 04.04.2014
comment
Ввод 8000 - это десятичное число 8000, шестнадцатеричное число 8000 или строка 8000?   -  person Marc Gravell    schedule 04.04.2014
comment
Устройство требует, чтобы он был HEX   -  person dynamicuser    schedule 04.04.2014
comment
@MattBaughan нет, вопрос не в этом; компьютеры на самом деле не говорят в шестнадцатеричном или десятичном виде или что-то еще. У них просто цифры. Что мне нужно знать, так это то, что вы (не машина) имеете в виду под 8000? CRC работает с байтами. Мне нужно знать, какую последовательность байтов мы хешируем. Я могу интерпретировать 8000 как 15-байтовую последовательность, даже не пытаясь. Мне нужно знать, какой.   -  person Marc Gravell    schedule 04.04.2014
comment
Извините, если вы посмотрите на скриншот, переключатель HEX отмечен флажком. Затем я ввожу 8000, и сгенерированный CRC, который мне нужен, равен 0xC061. 8000 — это строка для начала.   -  person dynamicuser    schedule 04.04.2014
comment
Я всегда буду хешировать строку   -  person dynamicuser    schedule 04.04.2014
comment
@MattBaughan нет, вы всегда будете хешировать байты. Хэши работают с байтами, а не со строками. Чтобы добраться до байтов, вам нужно знать правила кодирования. Это ASCII? то есть 8000 => {56, 48, 48, 48} ?   -  person Marc Gravell    schedule 04.04.2014
comment
Для информации, алгоритм CRC-16, который я разместил (теперь удален), является алгоритмом CRC-CCITT (XModem), также известным как CRC16 ISO 13239. Я посмотрю, смогу ли я найти, какой алгоритм дает другой результат.   -  person Marc Gravell    schedule 04.04.2014
comment
Все, что я знаю, это то, что устройство отправляет значения HEX в виде строки через RS-232, и я их получаю. У меня уже есть метод, который преобразует строку в byte[], и я отправляю сообщения обратно на устройство, используя метод, который возвращает строку HEX. Устройство понимает это, и все работает нормально, мне просто нужно реализовать проверку CRC для строк, отправляемых с устройства на ПК. Ваше здоровье   -  person dynamicuser    schedule 04.04.2014


Ответы (2)


Вот так; обратите внимание, что это особый вариант CRC-16 - просто "CRC-16" сбивает с толку. Это заимствует некоторые особенности реализации с http://www.sanity-free.com/134/standard_crc_16_in_csharp.html - обратите внимание, что у меня есть сделал это static, а не на основе экземпляра.

using System;

static class Program
{
    static void Main()
    {
        string input = "8000";
        var bytes = HexToBytes(input);
        string hex = Crc16.ComputeChecksum(bytes).ToString("x2");
        Console.WriteLine(hex); //c061
    }
    static byte[] HexToBytes(string input)
    {
        byte[] result = new byte[input.Length / 2];
        for(int i = 0; i < result.Length; i++)
        {
            result[i] = Convert.ToByte(input.Substring(2 * i, 2), 16);
        }
        return result;
    }

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

        public static 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;
        }

        static 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;
            }
        }
    }
}
person Marc Gravell    schedule 04.04.2014
comment
Привет, у меня это не работает, в этой строке result[i] = Convert.ToByte(input.Substring(2 * i, 2), 16); второй параметр ожидает значение System.IFormatProvider, из которых 16, по-видимому, не является одним из них. - person Shaun Bebbers; 27.03.2017
comment
Метод @Shaun определенно существует в полном объеме: msdn. microsoft.com/en-us/library/c7xhf79k(v=vs.110).aspx — возможно, вы используете ядро ​​.net? Или еще один уменьшенный фреймворк? - person Marc Gravell; 27.03.2017
comment
Кажется, вычисляет неправильные значения. По крайней мере, они не совпадают с теми, которые я получаю от функции CRC16 на Atmel AVR. Сейчас ищу другой источник. - person ygoe; 30.12.2019
comment
@ygoe Я намеренно начал пост, подчеркнув, что существует несколько несовместимых алгоритмов, известных как CRC16; знать, что вы имеете в виду, очень важно. Скорее всего, мы имеем в виду разные. - person Marc Gravell; 30.12.2019

Кроме того, если вы хотите CRC16-CCITT.

private ushort Crc16Ccitt(byte[] bytes)
{
    const ushort poly = 4129;
    ushort[] table = new ushort[256];
    ushort initialValue = 0xffff;
    ushort temp, a;
    ushort crc = initialValue;
    for (int i = 0; i < table.Length; ++i)
    {
        temp = 0;
        a = (ushort)(i << 8);
        for (int j = 0; j < 8; ++j)
        {
            if (((temp ^ a) & 0x8000) != 0)
                temp = (ushort)((temp << 1) ^ poly);
            else
                temp <<= 1;
            a <<= 1;
        }
        table[i] = temp;
    }
    for (int i = 0; i < bytes.Length; ++i)
    {
        crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
    }
    return crc;
}
person TheDebugger    schedule 22.01.2016
comment
Crc16Ccitt(Encoding.ASCII.GetBytes("123456789")).ToString("X") возвращает 29B1. Судя по этой веб-странице, реализация неверна. - person Denxorz; 22.10.2019
comment
Изменение initialValue на 0x0x1D0F похоже работает. - person Denxorz; 22.10.2019