#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
}
}