5

I've tried the example from wikipedia: http://en.wikipedia.org/wiki/Longitudinal_redundancy_check

This is the code for lrc (C#):

/// <summary>
/// Longitudinal Redundancy Check (LRC) calculator for a byte array. 
/// ex) DATA (hex 6 bytes): 02 30 30 31 23 03
///     LRC  (hex 1 byte ): EC    
/// </summary> 
public static byte calculateLRC(byte[] bytes)
{
    byte LRC = 0x00;
    for (int i = 0; i < bytes.Length; i++)
    {
        LRC = (LRC + bytes[i]) & 0xFF; 
    }
    return ((LRC ^ 0xFF) + 1) & 0xFF;
}   

It said the result is "EC" but I get "71", what I'm doing wrong?

Thanks.

Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
Alexx Perez
  • 215
  • 2
  • 10
  • 19
  • 3
    That shouldn't even compile. Math on a byte turns it into an int. – harold Oct 09 '12 at 11:41
  • 3
    What if Wikipedia is wrong? The C# appears to have been changed many times, changing the algorithm used as well (it used to be a simple xor of all bytes, which, by the way, doesn't give EC as the answer either). – harold Oct 09 '12 at 11:51
  • Also, you can only get 71 from this code with this input by writing the result in decimal, in which case you'd *never* see EC even if you get that as the answer (you'd see 236) – harold Oct 09 '12 at 11:57
  • 1
    It might be worth noting that the term LRC itself is ambiguous, being used by different people to describe two different algorithms: https://en.wikipedia.org/wiki/Talk%3ALongitudinal_redundancy_check. If you're looking to check for corrupt bytes then the XOR algorithm is the simplest and should do the trick. Further, clearly Wikipedia is incorrect in either the algorithm or the expected result. – Mike Chamberlain Oct 09 '12 at 12:07

6 Answers6

15

Here's a cleaned up version that doesn't do all those useless operations (instead of discarding the high bits every time, they're discarded all at once in the end), and it gives the result you observed. This is the version that uses addition, but that has a negation at the end - might as well subtract and skip the negation. That's a valid transformation even in the case of overflow.

public static byte calculateLRC(byte[] bytes)
{
    int LRC = 0;
    for (int i = 0; i < bytes.Length; i++)
    {
        LRC -= bytes[i];
    }
    return (byte)LRC;
}

Here's the alternative LRC (a simple xor of bytes)

public static byte calculateLRC(byte[] bytes)
{
    byte LRC = 0;
    for (int i = 0; i < bytes.Length; i++)
    {
        LRC ^= bytes[i];
    }
    return LRC;
}

And Wikipedia is simply wrong in this case, both in the code (doesn't compile) and in the expected result.

harold
  • 61,398
  • 6
  • 86
  • 164
5

Guess this one looks cooler ;)

public static byte calculateLRC(byte[] bytes)
{
    return bytes.Aggregate<byte, byte>(0, (x, y) => (byte) (x^ y));
}
Omidoo
  • 493
  • 1
  • 6
  • 14
4

If someone wants to get the LRC char from a string:

    public static char CalculateLRC(string toEncode)
    {
        byte[] bytes = Encoding.ASCII.GetBytes(toEncode);
        byte LRC = 0;
        for (int i = 0; i < bytes.Length; i++)
        {
            LRC ^= bytes[i];
        }
        return Convert.ToChar(LRC);
    }
amp
  • 11,754
  • 18
  • 77
  • 133
0

The corrected Wikipedia version is as follows:

private byte calculateLRC(byte[] b)
    {
        byte lrc = 0x00;
        for (int i = 0; i < b.Length; i++)
        {
            lrc = (byte)((lrc + b[i]) & 0xFF);
        }
        lrc = (byte)(((lrc ^ 0xff) + 2) & 0xFF);
        return lrc;
    }
BarToK
  • 1
  • I know this is a bit of a necro-comment, but some of those operations are not necessary. For example, in addition, the lower bits of the result do not depend on the higher ones, so you don't need to do the `& 0xff` for every byte, you can do it once in the end without changing the result. Casting to `byte` even makes the last `& 0xff` redundant. – harold Oct 09 '13 at 11:31
0

I created this for Arduino to understand the algorithm (of course it's not written in the most efficient way)

String calculateModbusAsciiLRC(String input)
{
  //Refer this document http://www.simplymodbus.ca/ASCII.htm

  if((input.length()%2)!=0) { return "ERROR COMMAND SHOULD HAVE EVEN NUMBER OF CHARACTERS"; } 
 // Make sure to omit the semicolon in input string and input String has even number of characters

   byte byteArray[input.length()+1];
   input.getBytes(byteArray, sizeof(byteArray));
    byte LRC = 0;
    for (int i = 0; i <sizeof(byteArray)/2; i++)
    {
      // Gettting the sum of all registers
     uint x=0;
     if(47<byteArray[i*2] && byteArray[i*2] <58) {x=byteArray[i*2] -48;}
     else { x=byteArray[i*2] -55;     }
       uint y=0;
     if(47<byteArray[i*2+1] && byteArray[i*2+1] <58) {y=byteArray[i*2+1] -48;}
     else { y=byteArray[i*2+1] -55;     }
     LRC  += x*16 + y;
    }   
    LRC = ~LRC + 1;   // Getting twos Complement
    String checkSum = String(LRC, HEX);
    checkSum.toUpperCase(); // Converting to upper case eg: bc to BC - Optional somedevices are case insensitve 
    return checkSum;
} 
-1

I realize that this question pretty old, but I had trouble figuring out how to do this. It's working now, so I figured I should paste the code. In my case, the checksum needs to return as an ASCII string.

public function getLrc($string)
{
    $LRC = 0;
    // Get hex checksum.
    foreach (str_split($string, 1) as $char) {
        $LRC ^= ord($char);
    }
    $hex = dechex($LRC);
    // convert hex to string
    $str = '';
    for($i=0;$i<strlen($hex);$i+=2) $str .= chr(hexdec(substr($hex,$i,2)));
    return $str;
}
Charlie
  • 498
  • 2
  • 10
  • 24