Technical topic: ASN.1 and ACN - How to add a CRC value to an encoded packet

From TASTE
Jump to: navigation, search

The need

If you are using ASN.1 to encode the messages you send over a network (as you should!) you may face the situation where ASN.1 cannot know the value of a field before the whole packet is encoded. In space-ground links there are two fields of the standard telecommand packets that enter this category:

  • a Length field in the packet header (used to know the number of bytes to wait for on the network interface)
  • a CRC/Checksum field at the end of the packet

In the ASN.1 model you may add a placeholder for these fields but the encoder is of course not able to set the value, as it may require to compute it with a user-defined algorithm.

Adding manually the fields to the buffer after the encoding phase is possible but not trivial, as you need to take care of endianness and possibly shift bits here and there. In other words, you loose some benefits of ASN.1 by doing things manually again.

Luckily ASN1SCC provides help using a simple API to fill a value in the packet after it is encoded. You only need to know where to put the value.

Solution using ASN.1 and ACN

We need two things:

  • to specify the placeholder for the values
  • to fill in the value after the packet is encoded

The placeholder can be present in the ASN.1 type itself, but since it is not a field that is of any relevance to the end user (not application semantics) we will rather use ACN to add it.

Assuming a top-level structure in ASN.1 that mimicks a space-to-ground telemetry packet (TM):


   	TM-PACKET ::= SEQUENCE {
   		header 	TM-HEADER,
   		data	TM-DATA OPTIONAL
   	}
   with
   	TM-HEADER ::= SEQUENCE {
   		applicationProcessID	INTEGER(0..2047),
   		grouping-flags		TC-HEADER-SEQUENCE-FLAGS,
   		sequence-count		INTEGER(0..16383)
       }

One of the fields we want to add is at the level of the packet itself: we want to add a packet error control (CRC). Additionally we want to add at the end of the TM-HEADER structure a field that contains the total size of the TM-PACKET record.


We can create a corresponding ACN encoding structure that "adds" the missing fields (in bold):

       TM-PACKET [] {
               header                 [],
               data                   [present-when header.data-field-header-flag],
               packet-error-control    NULL    [pattern '0000000000000000'B, align-to-next byte]
       }
       
       TM-HEADER [] {
               data-field-header-flag  BOOLEAN         [],
               applicationProcessID                    [],
               grouping-flags                          [],
               sequence-count                          [],
               packet-length           NULL    [pattern '0000000000000000'B]
       }

In your code, you may create the functions that compute the CRC and size after the packet is encoded.

Here is the code you can use:

      // A buffer to encode your TM
      static byte encBuff[TM_PACKET_REQUIRED_BYTES_FOR_ACN_ENCODING];
      BitStream bitStrm;
      BitStream bitStrm_aux;
      flag ret;
      int errCode;
      int i;
      //telemetry packet to be encoded
      static TM_PACKET tm_packet = ....   
          
      // initialize bit stream
      BitStream_Init(&bitStrm, encBuff, TM_PACKET_REQUIRED_BYTES_FOR_ACN_ENCODING);
          
      // Encode value using ACN
      ret = TM_PACKET_ACN_Encode(&tm_packet, &bitStrm, &errCode, TRUE);
   
      // First field to add: get the length of the full packet
      asn1SccSint encoded_data_length = BitStream_GetLength(&bitStrm);
      
      // Encode length field 
      bitStrm_aux.buf = bitStrm.buf;
      bitStrm_aux.count = bitStrm.count;
      bitStrm_aux.currentByte = 4;
      bitStrm_aux.currentBit = 0; 
      BitStream_EncodeConstraintWholeNumber(&bitStrm_aux, encoded_data_length - 6, 0, 0xFFFF);
      
      //encode crc field;
      bitStrm_aux.buf = bitStrm.buf;
      bitStrm_aux.count = bitStrm.count;
      bitStrm_aux.currentByte = bitStrm.currentByte - 2;
      bitStrm_aux.currentBit = 0; 
      uint16_t crc = gen_crc16(encBuff, bitStrm.currentByte);  // User-defined CRC computation
      BitStream_EncodeConstraintWholeNumber(&bitStrm_aux, crc, 0, 0xFFFF);
      

That's it!