4

Solved by this code -> https://gist.github.com/Sbreitzke/b26107798eee74e39ff85800abf71fb1


I searched the web for a CRC 4 implementation in C# because I have to calculate a checksum by

Changing the numbers of the barcode into Hex representation, then to bytes and then to bits and then calculate a CRC4 checksum on the bit stream.

I already found this question from 8 years ago without an answer CRC-4 implementation in C#.

I tried changing the CRC 8 and 16 implementations to CRC 4 but they don't quite get the result I require.

0130E0928270FFFFFFF should evaluate to 7.

I found two C implementation but was unable to convert them to C#. For example this one:

 short[] crc4_tab = {
 0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
 0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
};

/**
* crc4 - calculate the 4-bit crc of a value.
* @crc:  starting crc4
* @x:    value to checksum
* @bits: number of bits in @x to checksum
*
* Returns the crc4 value of @x, using polynomial 0b10111.
*
* The @x value is treated as left-aligned, and bits above @bits are ignored
* in the crc calculations.
*/
short crc4(uint8_t c, uint64_t x, int bits)
{
    int i;

    /* mask off anything above the top bit */
    x &= (1ull << bits) -1;

    /* Align to 4-bits */
    bits = (bits + 3) & ~0x3;

    /* Calculate crc4 over four-bit nibbles, starting at the MSbit */
    for (i = bits - 4; i >= 0; i -= 4)
    c = crc4_tab[c ^ ((x >> i) & 0xf)];

    return c;
}

My current generation code (unit test) looks like this:

[TestMethod]
public void x()
{
    var ordnungskennzeichen = 01;
    var kundennummer = 51251496;
    var einlieferungsbel = 9999;
    var sendungsnr = 16777215;

    var hex_ordnungskennzeichen = ordnungskennzeichen.ToString("x2");
    var hex_kundennummer = kundennummer.ToString("x2");
    var hex_einlieferungsbel = einlieferungsbel.ToString("x2");
    var hex_sendungsnr = sendungsnr.ToString("x2");

    var complete = hex_ordnungskennzeichen + hex_kundennummer + hex_einlieferungsbel + hex_sendungsnr;

    var bytes = Encoding.ASCII.GetBytes(complete);

    //var computeChecksum = crc4(???);
    //   Console.WriteLine(computeChecksum);

}

short[] crc4_tab = {
    0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
    0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
};

/**
* crc4 - calculate the 4-bit crc of a value.
* @crc:  starting crc4
* @x:    value to checksum
* @bits: number of bits in @x to checksum
*
* Returns the crc4 value of @x, using polynomial 0b10111.
*
* The @x value is treated as left-aligned, and bits above @bits are ignored
* in the crc calculations.
*/

short crc4(byte c, ulong x, int bits)
{
    int i;

    /* mask off anything above the top bit */
    x &= ((ulong)1 << bits) -1;

    /* Align to 4-bits */
    bits = (bits + 3) & ~0x3;

    /* Calculate crc4 over four-bit nibbles, starting at the MSbit */
    for (i = bits - 4; i >= 0; i -= 4)
        c = (byte) crc4_tab[c ^ ((x >> i) & 0xf)];

    return c;
}
Zebi
  • 8,682
  • 1
  • 36
  • 42
  • 1
    Really did you tried to convert the code? That code works exactly as-is with three minor changes... – Gusman Oct 27 '17 at 09:48
  • in this case I was not sure how to convert ` x &= (1ull << bits) -1;` to c# and how to pass my argument (currently a byte array) into it. I changed the signature to `short crc4(short c, long x, int bits)` - is this correct? – Zebi Oct 27 '17 at 09:53
  • No, `1ull` is `(ulong)1` in c#, `uint8_t` is `byte` and `uint64_t` is `ulong` – Gusman Oct 27 '17 at 10:11
  • I am sorry I don't get it. If I change the types as you say the short coming out of `crc4_tag` is not aligning with `byte c`. I can cast it to make the compiler happy but I don't have any clue how to call the method then. The `c` parameter can be used as an initializer? so I can pass 0? why is it a parameter at all not a variable? Is `x` the digit I want to hash? it is too long for a long. `bits` makes no sense to me as long as I don`t understand `x`. – Zebi Oct 27 '17 at 10:31
  • So how do you know 0130E0928270FFFFFFF should evaluate to 7 (by the way it's not even number of bytes there - is that number correct)? – Evk Oct 27 '17 at 10:32
  • You must cast `crc_tab` to `byte`: `c = (byte)crc4_tab[c ^ ((x >> i) & 0xf)];` – Gusman Oct 27 '17 at 10:37
  • @Evk: The document with the requirement came from the german post (Deutsche Post) and contains an example. I did the cast as described but how do I call it? The only input parameter I have is a byte[] or a string containing "0130e0928270fffffff". I added the first part of the code to the question. – Zebi Oct 27 '17 at 10:48

3 Answers3

3

Converting it to C# is not very hard. c is initial or previous nibble (4-bit number), x is 64bit number you want to calculate crc4 of, bits is number of bits in that 64bit number to actually use (the rest are ignored). Since you have array of bytes - you don't need to use 64bit number as x - use can just use byte. Then the first two lines are irrelevant for you, because all they do is throwing away irrelevant bits from 64bit number and ensuring bits is divisable by 4. So after removing irrelevant lines your implementation becomes:

static readonly byte[] crc4_tab = {
    0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
    0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
};

static byte crc4(byte c, byte x) {
    var low4Bits = x & 0x0F;
    var high4Bits = x >> 4;
    c = crc4_tab[c ^ high4Bits];
    c = crc4_tab[c ^ low4Bits];

    return c;
}

static byte crc4(byte[] array) {
    byte start = 0;
    foreach (var item in array) {
        start = crc4(start, item);
    }
    return start;
}
Evk
  • 98,527
  • 8
  • 141
  • 191
  • woho the result is 7. Thank you very much, also for the explanation! :) – Zebi Oct 27 '17 at 12:11
  • @Zebi I'd suggest to test this on more strings, because you took some random code from internet, then some another random guy converted it to C# (and maybe even incorrectly), so who knows what can go wrong here :) – Evk Oct 27 '17 at 12:15
  • Of course I will check it further, the problem is that we have only one example but they will make a further approval so we will see ;) They send two examples and in one of them they even got the conversion to hex wrong so this can get interesting. Thank you very much! – Zebi Oct 27 '17 at 12:35
  • @Zebi I dont get it... `crc4(Encoding.Default.GetBytes("0130E0928270FFFFFFF")` will return `E` and not `7`?! – David Nov 09 '17 at 14:44
  • @david after further testing we modified it and this should work correctly: https://gist.github.com/Sbreitzke/b26107798eee74e39ff85800abf71fb1 – Zebi Nov 13 '17 at 10:47
3

After further testing and communication with the Deutsche Post AG we made a correct implementation (for the purpose of Deutsche Post at least):

https://gist.github.com/Sbreitzke/b26107798eee74e39ff85800abf71fb1

Zebi
  • 8,682
  • 1
  • 36
  • 42
2

For the purpose of Deutsche Post as well, I'd like to contribute a rather less complex algorithm wich may more easily be translated into other languages as well:

       private string crc4(string sText) {
        int iCRC;
        int iPoly;
        int iByte;
        int iBit;
        byte[] bText;
        sText = sText.Replace(" ", "");
        iPoly = 0x13 << 3;
        iCRC = 0;
        bText = Encoding.Default.GetBytes(sText);
        for (iByte=0; iByte < bText.Length; iByte++){
            iCRC = iCRC ^ bText[iByte];
            for (iBit = 0; iBit < 8; iBit++){
                if ((iCRC & 0x80) != 0){
                    iCRC = iCRC ^ iPoly;
                }
                iCRC = iCRC << 1;
            }
        }
        iCRC = iCRC >> 4;

        return String.Format("{0:X}", iCRC);
    }

Fed with i.e. "A0 0101 002B 00 000C D10" the above code will calculate "F" as the correct check digit. (and tested with numerous other input values)

ub_coding
  • 139
  • 5