Zusammenfassung: CRC (Cyclic Redundancy Check) wird häufig zur Fehlererkennung in der Datenkommunikation eingesetzt und zeichnet sich durch hohe Leistungsfähigkeit aus. Dieser Artikel erläutert die Grundprinzipien von CRC und stellt verschiedene Implementierungsmethoden vor, nachdem der Ursprung des gängigen Lookup-Tabellen-Algorithmus erklärt wurde. 1. Fehlererkennung : In der Datenkommunikation muss der Empfänger erkennen, ob während der Übertragung Fehler aufgetreten sind. Gängige Verfahren sind Paritätsprüfung, Prüfsummenprüfung und CRC (Cyclic Redundancy Check). Bei all diesen Verfahren berechnet der Sender mithilfe eines bestimmten Algorithmus eine Prüfsumme für die Nachricht und sendet diese zusammen mit der Nachricht an den Empfänger. Der Empfänger berechnet mithilfe desselben Algorithmus eine Prüfsumme für die empfangene Nachricht und vergleicht sie mit der empfangenen Prüfsumme, um die Korrektheit der Nachricht zu überprüfen. Die Paritätsprüfung benötigt nur eine Prüfsumme und ist sehr einfach zu berechnen. Am Beispiel der Odd-Check-Prüfung muss der Sender lediglich eine XOR-Operation auf alle Nachrichtenbits durchführen; ist das Ergebnis 0, ist die Prüfsumme 1. Andernfalls ist der Wert 0. Der Empfänger kann dieselbe Berechnung für die Nachricht durchführen und anschließend die Prüfsummen vergleichen. Die Nachricht kann auch zusammen mit der Prüfsumme berechnet werden. Ist der Wert 0, liegt ein Fehler vor; andernfalls ist die Prüfung erfolgreich. Es heißt üblicherweise, die Paritätsprüfung könne einen einzelnen Bitfehler erkennen, tatsächlich kann sie jedoch jede ungerade Anzahl von Bitfehlern erkennen. Die Idee der Prüfsumme ist ebenfalls sehr einfach: Die übertragene Nachricht wird als Folge von 8-Bit- (oder 16/32-Bit-)Ganzzahlen betrachtet. Diese Ganzzahlen werden addiert, um die Prüfsumme zu erhalten. Die Prüfsumme wird im IP-Protokoll verwendet und wird anhand von 16-Bit-Ganzzahlen berechnet. Der Übertrag des höchstwertigen Bits (MSB) wird zum Ergebnis addiert. Sowohl die Paritätsprüfung als auch die Prüfsumme weisen offensichtliche Schwächen auf. Die Paritätsprüfung kann keine gerade Anzahl von Bitfehlern erkennen. Bei der Prüfsumme wird, falls zwei ganze Zahlen in der Folge fehlerhaft sind, eine um einen bestimmten Wert erhöht und die andere um denselben Wert verringert. Dieser Fehler kann nicht erkannt werden. 2. Grundprinzip des CRC-Algorithmus: Der CRC-Algorithmus basiert auf der Polynomarithmetik des GF(2)-Polynoms (Galois-Körper mit zwei Elementen). Das klingt kompliziert, aber seine Haupteigenschaften und Rechenregeln sind leicht verständlich. Ein GF(2)-Polynom enthält nur eine Variable x mit Koeffizienten von 0 und 1, z. B.: 1x⁷ + 0x⁶ + 1x⁵ + 0x⁴ + 0x³ + 1x² + 1x¹ + 1x⁰, was x⁷ + x⁵ + x² + x + 1 entspricht (xⁿ steht für x hoch n). In einem GF(2)-Polynom werden Addition und Subtraktion modulo-2-arithmetisch durchgeführt, indem die Koeffizienten der entsprechenden Terme addiert bzw. subtrahiert werden. Modulo-2 bedeutet, dass Übertrag und Entleihung bei Addition und Subtraktion unberücksichtigt bleiben, d. h.: 0 + 0 = 0, 0 - 0 = 0, 0 + 1 = 1, 0 - 1 = 1, 1 + 0 = 1, 1 - 0 = 1, 1 + 1 = 0, 1 - 1 = 0. Addition und Subtraktion haben offensichtlich denselben Effekt (daher kommt das Minuszeichen im GF(2)-Polynom üblicherweise nicht vor) und entsprechen der XOR-Operation. Zum Beispiel: P1 = x︿3 + x︿2 + 1, P2 = x︿3 + x︿1 + 1, P1 + P2 ist: x︿3 + x︿2 + 1 + x︿3 + x + 1 —————————————————— x︿2 + x GF(2) Die Polynommultiplikation ist im Grunde die gleiche wie die allgemeine Polynommultiplikation, nur dass beim Addieren von Termen die Modulo-2-Arithmetik verwendet wird. Zum Beispiel ist P1 * P2: (x︿3 + x︿2 + 1)(x︿3 + x︿1 + 1) = (x︿6 + x︿4 + x︿3 + x︿5 + x︿3 + x︿2 + x︿3 + x + 1) = x︿6 + x︿5 + x︿4 + x︿3 + x︿2 + x + 1 Die GF(2)-Polynomdivision ist im Grunde die gleiche wie die allgemeine Polynomdivision, nur dass beim Subtrahieren von Termen Modulo-2-Arithmetik verwendet wird. Zum Beispiel: P3 = x︿7 + x︿6 + x︿5 + x︿2 + x, P3 / P2 ist: x︿4 + x︿3 + 1 ————————————————————————————————————————— x︿3 + x + 1 )x︿7 + x︿6 + x︿5 + x︿2 + x x︿7 + x︿5 + x︿4 ————————————————————- x︿6 + x︿4 x︿6 + x︿4 + x︿3 ————————————————————- x︿3 + x︿2 + x x︿3 + x + 1 Der CRC-Algorithmus bildet eine Nachricht der Länge m Bit auf ein GF(2)-Polynom M ab. Beispielsweise ergibt sich für eine 8-Bit-Nachricht 11100110, wenn das MSB zuerst übertragen wird, das Polynom x⁷ + x⁶ + x⁵ + x + x. Sender und Empfänger einigen sich auf ein GF(2)-Polynom G vom Grad r, das sogenannte Generatorpolynom, beispielsweise x³ + x + 1, r = 3. Das Anhängen von r Nullen an das Ende der Nachricht entspricht dem Polynom M', welches offensichtlich M' = Mxᵣ ist. Die Division von M' durch G liefert ein Restpolynom R vom Grad r - 1 oder kleiner, dessen r Bits die Prüfziffer bilden. Wie unten dargestellt: 11001100 ————————————- 1011 )11100110000 1011....... ————....... 1010...... 1011...... ————...... 1110... 1011... ————... 1010.. 1011.. ———— 100 <——- Der Sender sendet die m-Bit-Nachricht zusammen mit der r-Bit-Prüfsumme (M' + R). Der Empfänger berechnet die Prüfsumme der empfangenen m-Bit-Nachricht auf dieselbe Weise und vergleicht sie mit der empfangenen Prüfsumme. Alternativ kann der Empfänger alle empfangenen m + r Bits durch das Generatorpolynom dividieren und anschließend prüfen, ob der Rest 0 ist. Dies liegt daran, dass M' + R = (QG + R) + R = QG gilt, wobei Q der Quotient ist. Offensichtlich kann der Empfänger, wie der Sender, nach allen m + r Bits r Nullen anhängen und anschließend durch das Generatorpolynom dividieren. Tritt kein Fehler auf, bleibt der Rest 0. 3. Auswahl des Generatorpolynoms: Unterschiedliche Generatorpolynome weisen offensichtlich unterschiedliche Fehlererkennungsfähigkeiten auf. Die Wahl eines geeigneten Generatorpolynoms erfordert mathematische Kenntnisse; hier werden wir es nur unter einigen Aspekten betrachten. Um eine r-Bit-Prüfsumme zu verwenden, muss der Grad des Generatorpolynoms r sein. Das Generatorpolynom muss Einsen enthalten, da sonst das LSB (Least Significant Bit) der Prüfsumme immer 0 ist. Tritt bei der Übertragung der Nachricht (einschließlich der Prüfsumme) T ein Fehler auf, kann die vom Empfänger empfangene Nachricht als T + E dargestellt werden. Ist E nicht durch das Generatorpolynom G teilbar, kann der Fehler erkannt werden. Betrachten wir die folgenden Fälle: 1) Ein-Bit-Fehler, d. h. E = x ∑n = 100…00, n ≥ 0. E ist nicht durch G teilbar, solange G mindestens zwei Einsen enthält. Dies liegt daran, dass Gx ∑k einer Linksverschiebung von G um k Bits entspricht. Für jedes Polynom Q ist QG äquivalent zur Addition mehrerer verschiedener linksverschobener Gs. Enthält G mindestens zwei Einsen, so enthält auch die Summe seiner verschiedenen linksverschobenen Gs mindestens zwei Einsen. 2) Fehler durch ungerade Ziffernzahl: Enthält G den Faktor F = x + 1, so ist E nicht durch G teilbar. Dies liegt daran, dass QG = Q'F gilt, und gemäß der Analyse in 1) muss die Anzahl der Einsen in der Summe mehrerer verschiedener linksverschobener F gerade sein. 3) Explosiver Fehler: E = (x︿n + ... + 1)x︿m = 1…100…00, n ≥ 1, m ≥ 0. Offensichtlich ist E nicht teilbar, wenn G den Term „1“ enthält und sein Exponent größer als n ist. 4) Zweistelliger Fehler: E = (x︿n + 1)x︿m = 100…00100…00, n ≥ 0. Sei x︿n + 1 = QG + R, dann ist E = QGx︿m + Rx︿m. Aus 3) wissen wir, dass E genau dann durch G teilbar ist, wenn R gleich 0 ist. Daher genügt es, x<sub>n+1</sub> zu analysieren. Laut [3] existiert für jeden Grad r ein Generatorpolynom G, sodass x<sub>n+1</sub> für n ≥ 2<sup>r</sup> - 1 teilbar ist. Dieses Generatorpolynom wird als primitiv bezeichnet und bietet die höchste Fähigkeit zur Erkennung von 2-Bit-Fehlern dieses Grades, da x<sub>n+1</sub> für n = 2<sup>r</sup> - 1 durch jedes Polynom vom Grad r teilbar ist. [3] Es wird auch darauf hingewiesen, dass das primitive Generatorpolynom irreduzibel ist, ein irreduzibles Polynom jedoch nicht notwendigerweise primitiv ist. Daher kann das primitive Generatorpolynom einige ungeradzahlige Bitfehler nicht erkennen. Hier sind die Generatorpolynome für einige Standard-CRC-Algorithmen: Standardpolynom Hexadezimaldarstellung: CRC12 x︿12 + x︿11 + x︿3 + x︿2 + x + 1 80F CRC16 x︿16 + x︿15 + x︿2 + 1 8005 CRC16-CCITT x︿16 + x︿12 + x︿5 + 1 1021 CRC32 x︿32 + x︿26 + x︿23 + x︿22 + x︿16 + x︿12 + x︿11 04C11DB7 + x︿10 + x︿8 + x︿7 + x︿5 + x︿4 + x︿2 + x + 1 Die Hexadezimaldarstellung entfernt die Der höchste Term. CCITT wurde 1993 in ITU-T umbenannt. CRC12 wird für 6-Bit-Bytes verwendet, andere Standards für 8-Bit-Bytes. CRC16 kommt im BISYNCH-Kommunikationsstandard von IBM zum Einsatz. CRC16-CCITT ist in Kommunikationsprotokollen wie XMODEM, X.25 und SDLC weit verbreitet. Ethernet und FDDI verwenden CRC32, das auch in Dateikomprimierungsprogrammen wie ZIP und RAR verwendet wird. Unter diesen Generatorpolynomen ist CRC32 primitiv, während die anderen drei einen Faktor x + 1 enthalten. 4. Implementierung des CRC-Algorithmus – Um den CRC-Algorithmus programmatisch zu implementieren, betrachten Sie die Transformation der Polynomdivision aus Abschnitt 2. Es bleibt M = 11100110, G = 1011 und der Koeffizient r ist 3. 11001100 11100110000 ————————————- 1011 1011 )11100110000 ——————————- 1011....... 1010110000 ————....... 1010110000 1010...... 1011 1011...... ===> ——————————- ————...... 001110000 1110... 1110000 1011... 1011 ————... ——————————- 1010.. 101000 1011.. 101000 ———— 1011 100 <——-Prüfcode——————————- 00100 100 <——-Das Prüfcode-Programm kann wie folgt implementiert werden: 1) Die ersten r Bits von Mx︿r werden in ein Register der Länge r geschrieben; 2) Wenn das erste Bit des Registers 1 ist, wird das Register um 1 Bit nach links verschoben (das verbleibende MSB von Mx︿r wird in das LSB des Registers verschoben) und anschließend mit den letzten r Bits von G per XOR verknüpft; andernfalls wird das Register einfach um 1 Bit nach links verschoben (das verbleibende MSB von Mx︿r wird in das LSB des Registers verschoben); 3) Wiederholen Sie Schritt 2, bis alle Mx︿r von M in das Register geschoben wurden; 4) Der Wert im Register ist die Prüfsumme. Mit dem Generatorpolynom 0x1021 für CRC16-CCITT lautet der zugehörige C-Code (der gesamte Code in diesem Artikel setzt ein 32-Bit-System voraus und lässt sich unter VC6 erfolgreich kompilieren) wie folgt: unsigned short do_crc(unsigned char *message, unsigned int len) { int i, j; unsigned short crc_reg; crc_reg = (message[0] << 8) + message[1]; for (i = 0; i < len; i++) { if (i < len - 2) for (j = 0; j <= 7; j++) { if ((short)crc_reg < 0) crc_reg = ((crc_reg << 1) + (message[i + 2] >> (7 - i))) ︿ 0x1021; else crc_reg = (crc_reg << 1) + (message[i + 2] >> (7 - i))) ︿ 0x1021; else crc_reg = (crc_reg << 1) 1) + (message[i + 2] >> (7 - i)); } else for (j = 0; j <= 7; j++) { if ((short)crc_reg < 0) crc_reg = (crc_reg << 1) ︿ 0x1021; else crc_reg <<= 1; } } return crc_reg; } Offensichtlich hängt das Verhalten jeder inneren Schleife vom ersten Bit des Registers ab. Da die XOR-Operation das Kommutativ- und Assoziativgesetz erfüllt und die XOR-Verknüpfung mit 0 keine Auswirkung hat, muss die Nachricht nicht in das Register verschoben werden. Stattdessen wird während jeder inneren Schleife das erste Bit des Registers mit dem entsprechenden Nachrichtenbit per XOR verknüpft. Der verbesserte Code lautet wie folgt: `unsigned short do_crc(unsigned char *message, unsigned int len) { int i, j; unsigned short crc_reg = 0; unsigned short current; for (i = 0; i < len; i++) { current = message[i] << 8; for (j = 0; j < 8; j++) { if ((short)(crc_reg ︿ current) < 0) crc_reg = (crc_reg << 1) ︿ 0x1021; else crc_reg <<= 1; current <<= 1; } } return crc_reg; }` In der obigen Diskussion wird jedes Byte der Nachricht zuerst (MSB) übertragen. Der CRC16-CCITT-Standard berechnet die Nachricht jedoch, indem zuerst das LSB übertragen und die Nachricht dann nach rechts in das Register verschoben wird. Ändern Sie einfach den Code, um das LSB des Registers zu überprüfen und das bitweise invertierte 0x1021 (0x8408) mit dem Register per XOR zu verknüpfen, wie unten gezeigt: `unsigned short do_crc(unsigned char *message, unsigned int len) { int i, j; unsigned short crc_reg = 0; unsigned short current; for (i = 0; i < len; i++) { current = message[i]; for (j = 0; j < 8; j++) { if ((crc_reg ︿ current) & 0x0001) crc_reg = (crc_reg >> 1) ︿ 0x8408; else crc_reg >>= 1; current >>= 1; } } return crc_reg; }` Dieser Algorithmus verwendet zwei verschachtelte Schleifen, um die Nachricht bitweise zu verarbeiten, was sehr ineffizient ist. Um die Zeiteffizienz zu verbessern, ist der übliche Ansatz, Speicherplatz gegen Zeit einzutauschen. Da die innere Schleife nur das aktuelle Nachrichtenbyte und das niederwertige Byte von crc_reg verarbeitet, lässt sich der Algorithmus äquivalent wie folgt transformieren: `unsigned short do_crc(unsigned char *message, unsigned int len) { int i, j; unsigned short crc_reg = 0; unsigned char index; unsigned short to_xor; for (i = 0; i < len; i++) { index = (crc_reg ︿ message[i]) & 0xff; to_xor = index; for (j = 0; j < 8; j++) { if (to_xor & 0x0001) to_xor = (to_xor >> 1) ︿ 0x8408; else to_xor >>= 1; } crc_reg = (crc_reg >> 8) ︿ to_xor;` } return crc_reg; } Die innere Schleife bezieht sich nun nur noch auf den Index. Wir können die Tabelle crc16_ccitt_table im Voraus als Array generieren, sodass to_xor = crc16_ccitt_table[index]. Somit lässt es sich vereinfachen zu: unsigned short do_crc(unsigned char *message, unsigned int len) { unsigned short crc_reg = 0; while (len——) crc_reg = (crc_reg >> 8) ︿ crc16_ccitt_table[(crc_reg ︿ *message++) & 0xff]; return crc_reg; } crc16_ccitt_table wird durch folgenden Code generiert: int main() { unsigned char index = 0; unsigned short to_xor; int i; printf("unsigned short crc16_ccitt_table[256] =\n{"); while (1) {if (! (index % 8)) printf ("\n"); to_xor = index; for (i = 0; i < 8; i++) {if (to_xor & 0x0001) to_xor = (to_xor >> 1) 0x8408; else to_xor >>= 1; printf("0x%04x", to_xor); if (index == 255) printf("\n"); break; else printf(", "); Die generierte Tabelle sieht wie folgt aus: unsigned short crc16_ccitt_table[256] = {0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 }; Die Prüfsumme für die Nachricht `unsigned char message[len]` lautet also: `unsigned short code = do_crc(message, len);` und wird wie folgt gesendet: `message[len] = code & 0x00ff; message[len + 1] = (code >> 8) & 0x00ff;` Der Empfänger führt `do_crc` auf den empfangenen Bytes len + 2 aus. Tritt kein Fehler auf, ist das Ergebnis 0. In manchen Übertragungsprotokollen gibt der Sender die Nachrichtenlänge nicht an, sondern verwendet ein Endzeichen. Folgende Fehler können auftreten: 1) Voranstellen eines oder mehrerer Nullbytes; 2) Die Nachricht beginnt mit einem oder mehreren aufeinanderfolgenden Nullbytes, gefolgt vom Weglassen eines oder mehrerer Nullbytes. 3) Hinzufügen eines oder mehrerer 0-Bytes nach der Nachricht (einschließlich der Prüfsumme); 4) Die Nachricht (einschließlich der Prüfsumme) endet mit einem oder mehreren aufeinanderfolgenden 0-Bytes, gefolgt vom Weglassen eines oder mehrerer 0-Bytes. Diese Fehler sind offensichtlich nicht erkennbar, da die Verarbeitung von 0-Nachrichtenbytes (oder -Bits) den Registerwert nicht ändert, wenn dieser 0 ist. Um die ersten beiden Probleme zu lösen, genügt es, das Register einfach mit einem Wert ungleich null zu initialisieren. Die Funktion `do_crc` wird wie folgt verbessert: `unsigned short do_crc(unsigned short reg_init, unsigned char *message, unsigned int len) { unsigned short crc_reg = reg_init; while (len-) crc_reg = (crc_reg >> 8) crc16_ccitt_table[(crc_reg *message++) & 0xff]; return crc_reg; Im CRC16-CCITT-Standard ist `reg_init = 0xffff`. Um die beiden letztgenannten Probleme zu lösen, führt der CRC16-CCITT-Standard eine XOR-Verknüpfung der berechneten Prüfsumme mit 0xffff durch, d. h.: `unsigned short code = do_crc(0xffff, message, len); code = 0xffff; message[len] = code &` 0x00ff; message[len + 1] = (code >> 8) & 0x00ff; Offensichtlich führt der Empfänger nun `do_crc` auf allen empfangenen Bytes aus. Wenn keine Fehler auftreten, sollte das Ergebnis ein konstanter Wert GOOD_CRC sein. Dieser erfüllt die folgende Beziehung: unsigned char p[] = {0xff, 0xff}; GOOD_CRC = do_crc(0, p, 2); Das Ergebnis ist GOOD_CRC = 0xf0b8. Referenzen ———————— [1] Ross N. Williams, „A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS“, Version 3, http://www.ross.net/crc/crcpaper.html, August 1993 [2] Simpson, W., Hrsg., „PPP in HDLC Framing“, RFC 1549, Dezember 1993 [3] PE Boudreau, WC Bergman und DR Irvin, „Performance of a cyclic redundancy check and its interaction with a data scrambler“, IBM J. RES. DEVELOP., Band 38, Nr. 6, November 1994