#region File Header // // CRC16.cs - Computes a 16-bit CRC // // Copyright (C) Javier Valcarce. BSD License #endregion #region Using Statements using System; using System.Diagnostics; using System.IO; using System.Collections.Generic; using System.Text; #endregion namespace Arq { /// /// Cyclic code redundancy calculator for any 16-bit generator polynomial. The conventions /// followed to calculate it are those used commonly in hardware (which are diferent from /// the conventions followed in software) /// /// Convention: Remaindet initialized with InitValue = 0xFFFF /// Convention: The first serial data bit entering in the LFSR is the byte's MSB /// Convention: Polynomial x^16 + x^15 + x^2 + 1 is represented as 0x8005 (not 0xA001) /// public class Crc16 { ushort polinomial; ushort crc; int count; static ushort[] table; public const ushort InitValue = 0xFFFF; public const ushort CRC_16 = 0x8005; /// /// Constructor /// /// Generator polynomial in hexadecimal notation. For example: the /// polinomial g(x) = 1 + x2 + x15 + x16 has a hexadecimal representation 0x8005 public Crc16(ushort polinomial) { Debug.Assert((polinomial & 0x0001) == 0x0001, "Bad polynomial, zero order coeficcient must be 1"); this.polinomial = polinomial; // Initialized with InitValue crc = InitValue; count = 0; // table table = new ushort[0x10000]; // 0xFFFF + 1 ushort t; ushort c; for (int r = 0; r < 0x10000; r++) { c = (ushort) r; for (int j = 0; j < 8; j++) { t = (ushort)(c & 0x8000); c = (ushort)(c << 1); if (t == 0x8000) { c = (ushort)(c ^ polinomial); } } table[r] = c; } //for (int r = 0; r < 0x10000; r++) //{ // Console.WriteLine("{0,5} -> 0x{1:x4}", r, crctable[r]); //} } #region Public Methods /// /// Feed the LFSR (Linear Feedback Shift Register) with bytes buf[offset] to /// buf[offset + count - 1] and update the CRC /// /// The array /// Offset in the array /// Number of bytes computed public void Compute(byte[] buf, int offset, int count) { ushort s; for (int i = 0; i < count; i++) { s = (ushort)(buf[offset + i] << 8); crc = (ushort)(crc ^ s); crc = table[crc]; } this.count += count; } /// /// Feed the LFSR (Linear Feedback Shift Register) with the byte b and update the CRC /// /// Byte feeded into the LFSR public void Compute(byte b) { ushort s; s = (ushort)(b << 8); crc = (ushort)(crc ^ s); crc = table[crc]; this.count++; } /// /// Computes a 16-bit CRC from frame[offset + 0] to frame[offset + count - 3] and store it /// at the last 2 bytes, frame[offset + count - 2] and frame[offset + count - 1] in /// little-endian or big-endian depending on bigEndian parameter. /// /// /// /// /// True to use big-endian, false for little-endian public void AppendCode(byte[] frame, int offset, int count, bool bigEndian) { ushort localcrc = InitValue; ushort s; for (int i = 0; i < count - 2; i++) { s = (ushort)(frame[offset + i] << 8); localcrc = (ushort)(localcrc ^ s); localcrc = table[localcrc]; } byte lsb = (byte)((localcrc & 0x00FF) >> 0); byte msb = (byte)((localcrc & 0xFF00) >> 8); if (bigEndian) { frame[offset + count - 2] = msb; frame[offset + count - 1] = lsb; } else { frame[offset + count - 2] = lsb; frame[offset + count - 1] = msb; } } /// /// Check a frame. Computes CRC from frame[0] to frame[frame.Length-3]. It is supposed that /// then redundancy is in the last 2 bytes of the array frame[frame.Length - 2] and /// frame[frame.Length - 1]. /// /// Frame to be checked /// /// /// True to use big-endian, false for little-endian /// True if valid frame, false if not public bool ValidFrame(byte[] frame, int offset, int count, bool bigEndian) { ushort localcrc = InitValue; ushort s; for (int i = 0; i < count - 2; i++) { s = (ushort)(frame[offset + i] << 8); localcrc = (ushort)(localcrc ^ s); localcrc = table[localcrc]; } byte lsb = (byte)((localcrc & 0x00FF) >> 0); byte msb = (byte)((localcrc & 0xFF00) >> 8); if (bigEndian) { if (frame[offset + count - 2] != msb) return false; if (frame[offset + count - 1] != lsb) return false; } else { if (frame[offset + count - 2] != lsb) return false; if (frame[offset + count - 1] != msb) return false; } return true; } /// /// Resets current CRC value /// public void Reset() { crc = InitValue; count = 0; } #endregion #region Properties /// /// Number of bytes computed /// public int Count { get { return count; } } /// /// Current CRC (16-bits) computed value /// public ushort Code { get { return crc; } } #endregion } }