Un calcul de FCS
Retour au menu : Accueil - Index général -


Voir aussi : L'APRS - Le code ASCII - La trame APRS - Transmission de la positionUne trame APRS simple pour un tracker - Un calcul de FCS - Format d'émission des caractères - Le "bourrage de zéros" ou "bit-stuffing" - APRS : codage NRZI -

Préambule

    La trame AX25 se termine par deux octets destinés au contrôle de l'intégrité de la trame après transmission.
Ces deux octets représentent le CRC (Cyclic Redundancy Check) ou FCS (Frame-Check Sequence)
L'abondante documentation disponible sur Internet concernant le calcul du FCS est souvent contradictoire et quand deux documents décrivent la même méthode (la plupart du temps de façon incomplète) il est fort probable que l'un a copié sur l'autre...
Faute d'avoir pu retrouver la méthode qui permet de retrouver le FCS transmis dans les trames entendues sur le réseau, nous avons choisi arbitrairement la méthode décrite ici. Nous remplacerons celle-ci par la bonne quand nous l'aurons trouvée...

Méthode de construction des deux octets du FCS

    La méthode répond à 3 questions :
- sur quelles données appliquer le calcul ?
- quelle est la formule de calcul ?
- comment sont transmis les deux octets ?

Sur quelles données appliquer le calcul

    
Les deux octets du FCS sont transmis en fin de trame mais on pourrait fort bien le calculer à diverses étapes :
- chaine de caractères ASCII en clair avec, par exemple le caractère 5 sous sa forme normale "00110101" (Control = 0x03 et PID = 0xF0)
- caractères dont les bits ont été transposés, le caractère 5 étant sous la forme "01010110" (Control = Ox30 et PID = 0x0F)
- après l'opération de bourrage des zéros

    Cette dernière option semblerait la plus logique pour les raisons suivantes :
- le FCS tient compte de ces bits de bourrage
- les autres opérations de manipulation de bits ne l'affectent pas
- comme il est transmis en dernier, il peut donc être calculé au fur et à mesure de la transmission.

    Mais le FCS lui-même peut prendre une valeur qui inclut une suite de bits égale à "111111" et être pris pour un flag. L'examen des trames captées sur le réseau APRS montre bien que les octets du FCS sont parfois "bourrés", le calcul du FCS sera donc effectué juste avant l'opération de "bit-stuffing".

Formule de calcul

    Le principe est très simple, il s'applique sur tous les octets de la trame sauf les deux octets du FCS, bien entendu.
L'algorithme le plus court est celui qui décrit le calcul bit à bit.
1) déclarer une variable CRC de type entier sur 4 octets (int) et l'initialiser avec 0xFFFF
2) dans une boucle balayant chaque bit de la trame :
- lire le bit de poids faible de la variable CRC et le comparer au bit de la trame
- si les deux bits sont différents, appliquer un OU exclusif sur le contenu de la variable CRC avec la valeur 0x8408
- exécuter un décalage logique de 1 bit vers la droite du contenu de CRC
3) Calculer le complément à 1 de la valeur de CRC en fin de boucle

    Voici un exemple en Java qui ne contient aucune astuce de programmation. Ce n'est qu'un simple exemple que chacun pourra améliorer.
La fonction
crc16b(byte[] data)prend un tableau de bytes comme argument et retourne la valeur du FCS calculé.
Le tableau de bytes contient en fait les quelques centaines de bits de la trame à raison d'un byte par élément 0 ou 1. Le dernier élément contient la valeur "2" placée là après le remplissage du tableau pour faciliter la sortie de la boucle de calcul.

public static int crc16b(byte[] data)      // data est le tableau contenant tous les bits de la trame
{ int valCRC = 0xFFFF; // initialisation de la valeur du CRC avec 0xFFFF
int bitShift=0;
int compteur=0;
do
{ bitShift=valCRC & 0x0001; // valeur du bit à supprimer à droite
valCRC = (valCRC >>> 1);      //décalage à droite de 1 bit, remplissage à gauche avec un 0
if (data[compteur]!=bitShift)
{ valCRC = valCRC ^ 0x8408 ;   //XOR de valCRC avec 0x8048
}
compteur++;
}
while (data[compteur]!=2);              // la valeur "2" a été placée précédemment pour indiquer la fin du tableau
valCRC = ~valCRC; // complément à 1 bit à bit (inversion)
return valCRC & 0xFFFF;                  // retour
}


    A la réception, il suffira de refaire le calcul et de vérifier que les deux valeurs, le FCS reçu et le FCS recalculé sont bien identiques



Transmission des deux octets du FCS

    La documentation est confuse. On peut lire dans une spécification que l'octet de poids faible est transmis en premier et le contraire dans une autre. Et une troisième préconise d'inverser les bits.
Nous suivrons la spécification "AX.25 Link Access Protocol for Amateur Packet Radio" de juillet 1998 qui indique "The FCS field of an AX.25 frame is sent most-significant bit first" et précise plus loin : "FCS is transmitted bit 15 first."
Exemple : si la valeur du FCS est "0x7E02" le premier octet à transmettre contiendra 0x7E.


Documentation

    
Sites en français :
Contrôle d'erreur (CRC) : une bonne initiation sur "Comment ça marche"

    Documents en anglais :
CRC_for_AX25.pdf de Bill Newhall, KB2BRD. Exemple avec Matlab
1200 Baud Packet Radio Details : site de N1VG. Enoncé du principe retenu ici.
A Painless guide to CRC error detection algorithms de Ross Williams
Cyclic Redundancy Check de Enrico Marinoni
Optimized CRC Calculation : un exemple en C++
How to Send AX.25 UI Frames Using Inexpensive PIC Microprocessors par John Hansen, W2FS