P1 Meter CRC16校验和

时间:2017-01-27 19:42:59

标签: c# crc crc16 smartmeter

我正在尝试为P1仪表消息编写CRC16校验和的单元测试(并且很难将其正确...)。

我拥有的是:

  • 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";

规范

可以找到来源here

  

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代码

可以找到来源here

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。

有人能指出我正确的方向吗?

更新

@Mark Adler,我修复了你发现的问题,但我仍然计算错误的校验和。我剥夺了下面所有不相关的部分,你能再好看了吗?如果愿意,您只需复制粘贴下面的代码即可。或者,回发计算正确校验和的代码。

提前多多感谢!

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 个答案:

答案 0 :(得分:1)

CRC代码看起来很好。问题是它期待回车换行,而不仅仅是换行。如果您在每个\n前面加\r,那么从0x29ed/的字节CRC得到!