UBX protocol help: configuring a NEO 6M Arduino GPS Module... Fletcher checksum?

So I’ve been banging my head against my keyboard for a few days because I can’t figure out how to get my new GPS shield to play nicely with my Teensy 3.1 (Arduino compatible) microcontroller.

The GPS shield in question is iteaduino based, and can be seen here: http://imall.iteadstudio.com/im120417017.html

I have no trouble using the TinyGPS Arduino Library to parse incoming data from the NEO 6 gps module on the Teensy’s UART pins, and output latitude and longitude to my serial monitor in the Arduino IDE.

The problem arises when I try to issue a NMEA command or a UBX command to the NEO 6. This is the only way to actually control the module, instead of just letting it drone out the same 6 NMEA messages every second. (for example, you can’t set the module into power save mode without issuing a UBX RXM-PMREQ command).

I started by basing my code on the example provided by ukhas (http://ukhas.org.uk/guides:ublox6), but couldn’t get that to work. So, I made a simple small program that basically does the following:

  • Establishes serial communication to NEO 6 module at 9600 baud
  • Sends the GPS module an 11 byte data packet that follows UBX protocol, telling it to stop sending out NMEA lat/lon messages
  • Parses incoming data packets from the GPS module in search of an ACK (acknowledgement) message

No acknowledgement message ever comes! What am I doing wrong?! I’m starting to suspect that my fletcher checksum logic is flawed…

Here is my code:

#include <HardwareSerial.h>
#include <string.h>
#include <TinyGPS.h>

void gpsdump(TinyGPS &gps);
void printFloat(double f, int digits = 2);

HardwareSerial2 GPS= HardwareSerial2();  //Initialize harware serial object for the GPS unit
TinyGPS gps;
byte gps_set_sucess = 0 ;

//Pin Definitions
int GPS_RxPin= 9;
int GPS_TxPin=10;

//I/O variables
int GPSbaud = 9600;
int Serialbaud=19200;

int byteCount;


//----------------------------------GPS unit functions------------------------------------------------

// Send a byte array of UBX protocol to the GPS
void sendUBX(uint8_t *MSG, uint32_t len, long timeout=3000) {

  uint32_t CK_A = 0, CK_B = 0;
  uint8_t sum1=0x00, sum2=0x00;
  uint8_t fullPacket[len+4];

  for(int i=0; i<len; i++) {
    fullPacket[i+2]=MSG[i];
  }

  Serial.println();
  fullPacket[0]=0xB5;
  fullPacket[1]= 0x62;

  //Calculate checksum
  for(int i=0; i<len; i++){
    CK_A = CK_A + MSG[i];
    CK_B = CK_B + CK_A;
    Serial.println("CK_A= " + String(CK_A));
    Serial.println("CK_B= " + String(CK_B));
  }

  sum1 = CK_A &0xff;//Mask the checksums to be one byte
  sum2= CK_B &0xff;

  fullPacket[len+2]=sum1; //Add the checksums to the end of the UBX packet
  fullPacket[len+3]=sum2;

  Serial.print("Checksum 1 premask= ");
  Serial.println(CK_A,HEX);
  Serial.print("Checksum 1 postmask= ");
  Serial.println(sum1, HEX);

  Serial.print("Checksum 2 premask= ");
  Serial.println(CK_B,HEX);
  Serial.print("Checksum 2 postmask= ");
  Serial.println(sum2, HEX);

  Serial.println("fullPacket is:");

  for(int i=0; i<(len+4); i++) { 
    Serial.print(fullPacket[i],HEX);//Print out a byt of the UBX data packet to the serial monitor
    Serial.print(", ");
    GPS.write(fullPacket[i]);//Send a byte of the UBX data packet to the GPS unit
  }
  GPS.clear(); 
  Serial.println();
}//end function


// Calculate expected UBX ACK packet and parse UBX response from GPS--------------------------
boolean getUBX_ACK(uint8_t *MSG, uint32_t len) {
  uint8_t b;
  uint8_t ackByteID = 0;
  uint8_t ackPacket[10];
  unsigned long startTime = millis();
  uint32_t CK_A=0, CK_B=0;
  boolean notAcknowledged=false;

 Serial.print(" * Reading ACK response: ");
  // Construct the expected ACK packet    
  ackPacket[0] = 0xB5;  // header
  ackPacket[1] = 0x62;  // header
  ackPacket[2] = 0x05;  // class
  ackPacket[3] = 0x01;  // id
  ackPacket[4] = 0x02;  // length
  ackPacket[5] = 0x00;
  ackPacket[6] = MSG[0];    // MGS class
  ackPacket[7] = MSG[1];    // MSG id
  ackPacket[8] = 0;     // CK_A
  ackPacket[9] = 0;     // CK_B

  // Calculate the checksums
  for (uint8_t i=2; i<8; i++) {
    CK_A = CK_A + ackPacket[i];
    CK_B= CK_B + CK_A;
  }

  ackPacket[8]= CK_A &0xff;//Mask the checksums to be one byte
  ackPacket[9]= CK_B &0xff;

  Serial.println("Searching for UBX ACK response:");
  Serial.print("Target data packet: ");

  for(int i =0; i<10; i++) {
    Serial.print(ackPacket[i], HEX);
    Serial.print(", ");
    }

  Serial.println();
  Serial.print("Candidate   packet: ");

  while (1) {

    // Test for success
    if (ackByteID > 9) {
      // All packets in order!
      Serial.println(" (Response received from GPS unit:)");
      if(notAcknowledged){
        Serial.println("ACK-NAK!");
      }
      else{
        Serial.println("ACK-ACK!");
        return true;
      }
    }

    // Timeout if no valid response in 5 seconds
    if (millis() - startTime > 5000) { 
      Serial.println("<<<Response timed out!>>>");
      return false;
    }

    // Make sure data is available to read
    if (GPS.available()) {
      b = GPS.read();

      // Check that bytes arrive in sequence as per expected ACK packet
      if (b == ackPacket[ackByteID]) { 
        ackByteID++;
        Serial.print(b, HEX);
        Serial.print(", ");
        // Check if message was not acknowledged
        if (ackByteID==3){
          b=GPS.read();
          if (b==0x00){
            notAcknowledged=true;
            ackByteID++;
          }
        }
      } 
      else if(ackByteID>0){
        ackByteID = 0;  // Reset and look again, invalid order
        Serial.print(b,HEX);
        Serial.println(" -->NOPE!");
        Serial.print("Candidate   packet: ");    
      }

    }
  }//end while
}//end function

//--------SETUP------------------

void setup()
{
  boolean gps_get_success=false;

  delay(5000);//Give yourself time to open up the serial monitor  

  pinMode(GPS_TxPin,OUTPUT); //Define the UART transmission pin for ommunication with the GPS unit
  pinMode(GPS_RxPin,INPUT); // Define the UART read pin for communication with the GPS unit

 Serial.begin(Serialbaud);  //Begin serial ommunication with Serial Monitor
 Serial.println("Serial monitor operational");
 GPS.begin(GPSbaud);  //Begin serial communication with GPS unit

 //Compile a UBX data packet to send to GPS - turn off GLL reporting
 uint8_t disableGLL[] = {0x06, 0x01, 0x03, 0x00, 0xF0, 0x01, 0x00};
 uint32_t len= sizeof(disableGLL)/sizeof(uint8_t); 

 Serial.println("Attempting to send UBX command to turn of GLL reporting");
 Serial.println("Original message is " + String(len) + " bytes:");

  for(int i=0; i<len; i++) {
    Serial.print(disableGLL[i]);
    Serial.print(", ");
  }
  Serial.println();

   //Clear the communication buffer
   while ( GPS.available())
    {
      char c = GPS.read();
    }

 sendUBX(disableGLL, len);
 getUBX_ACK(disableGLL, len);


}

//--------MAIN LOOP-------MAIN LOOP-------MAIN LOOP-------MAIN LOOP-------MAIN LOOP-------MAIN LOOP--
void loop()
{
  while ( GPS.available())
    {
      char c = GPS.read();
      if(c==0xb5){Serial.println();}
      Serial.print(c, HEX); // uncomment this line if you want to see the GPS data flowing
      Serial.print(", ");
    }

}//END LOOP-------------------

I’m not really sure why the GPS module isn’t responding to my command. It’s starting to seem insolent. Here is the serial monitor output:

Serial monitor operational
Attempting to send UBX command to turn of GLL reporting
Original message is 7 bytes:
6, 1, 3, 0, 240, 1, 0, 

CK_A= 6
CK_B= 6
CK_A= 7
CK_B= 13
CK_A= 10
CK_B= 23
CK_A= 10
CK_B= 33
CK_A= 250
CK_B= 283
CK_A= 251
CK_B= 534
CK_A= 251
CK_B= 785
Checksum 1 premask= FB
Checksum 1 postmask= FB
Checksum 2 premask= 311
Checksum 2 postmask= 11
fullPacket is:
B5, 62, 6, 1, 3, 0, F0, 1, 0, FB, 11, 
 * Reading ACK response: Searching for UBX ACK response:
Target data packet: B5, 62, 5, 1, 2, 0, 6, 1, F, 38, 
Candidate   packet: B5, 38 -->NOPE!
Candidate   packet: B5, CC -->NOPE!
Candidate   packet: B5, 38 -->NOPE!
Candidate   packet: <<<Response timed out!>>>

And here is an example of the raw Bytes coming in over the UART (these were sent to the arduino serial monitor)

B5, 38, 35, FC, 10, 40, A1, 59, 3C, 10, 1D, 3C, 30, 11, BD, 19, 90, 18, 10, 48, BD, 51, 39, 1C, 3C, 10, 39, 5D, BC, 91, 91, 59, 3D, B9, B1, B1, 10, D5, 3C, B0, 59, 3D, 3C, 10, 91, 3D, B8, BC, 90, 19, 38, BC, 10, 48, BD, 11, 1D, 1C, 38, 50, 39, 11, 1D, 18, 3C, 11, B9, 1D, 3D, 1, 17, 11, 59, BC, 3C, 10, 5D, 18, B8, 50, 9D, 31, AC, 42, 1D, 5C, 71, 98, B1, 3C, B, 99, 59, 8A, 39, 1, CD, 19, 59, A, BC, 18, 31, 9D, 9D, BC, 31, A5, 86, 94, 32, B1, 0, 85, 25, B1, A5, 1C, 8A, 30, 1, 10, 19, 59, 99, 1D, 38, 31, 63, 84, B, B8, 19, BD,

This might be over my head but I though NMEA (National Maritime Electronics Association) protocol used RS-485.
That's what I read in the standards document. So how are you using it ? (unless you're using the USB ports on the NMEA devices.)

The NMEA protocol that my GPS module uses outputs strings that look like this over the serial monitor:

The UBX protocol that I am sending and receiving in the code that I posted, on the other hand, has the following structure:

You mean the phoenix connector has TTL Tx and Rx pins ?

The GPS shield I'm using doesn't have any phoenix connector, rather it has female standard pin connectors (as on an arduino).

The Rx and Tx pins of the NEO 6 module on the shield are connected via circuit board traces to the female connectors:

So it's not actually Marine equipment you would find on a boat (the kind I was talking about) ?
Is it like an arduino shield that mimics true NMEA marine equipment but isn't marine equipment ?
(and has the TTL serial that true marine equipment on a boat would not have ?)

I honestly didn’t read anything about the NEO 6M, but if you’re talking UBX, perhaps the same stuff I’ve been using for the ublox MAX6 would work?

For example, how about

/*
  GPS Level Convertor Board Test Script
  03/05/2012 2E0UPU
 
  Initialise the GPS Module in Flight Mode and then echo's out the NMEA Data to the Software Serial.
 
  This example code is in the public domain.
  Additional Code by J Coxon (http://ukhas.org.uk/guides:falcom_fsa03)
 */
 
#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(4, 5);
 
byte gps_set_sucess = 0 ;
 
void setup()
{
  mySerial.begin(9600);
  Serial.begin(9600);
  mySerial.println("GPS Level Convertor Board Test Script");
  mySerial.println("03/06/2012 2E0UPU");
  mySerial.println("Initialising....");
 
 // THIS COMMAND SETS FLIGHT MODE AND CONFIRMS IT 
  mySerial.println("Setting uBlox nav mode: ");
  uint8_t setNav[] = {
    0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0xDC                      };
  while(!gps_set_sucess)
  {
    sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t));
    gps_set_sucess=getUBX_ACK(setNav);
  }
  gps_set_sucess=0;
 
  // THE FOLLOWING COMMANDS DO WHAT THE $PUBX ONES DO BUT WITH CONFIRMATION
  /* 
   debug.println("Switching off NMEA GLL: ");
   uint8_t setGLL[] = { 
   0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x2B                   };
   while(!gps_set_sucess)
   {		
   sendUBX(setGLL, sizeof(setGLL)/sizeof(uint8_t));
   gps_set_sucess=getUBX_ACK(setGLL);
   }
   gps_set_sucess=0;
 
   debug.println("Switching off NMEA GSA: ");
   uint8_t setGSA[] = { 
   0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x32                   };
   while(!gps_set_sucess)
   {	
   sendUBX(setGSA, sizeof(setGSA)/sizeof(uint8_t));
   gps_set_sucess=getUBX_ACK(setGSA);
   }
   gps_set_sucess=0;
   debug.println("Switching off NMEA GSV: ");
   uint8_t setGSV[] = { 
   0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x39                   };
   while(!gps_set_sucess)
   {
   sendUBX(setGSV, sizeof(setGSV)/sizeof(uint8_t));
   gps_set_sucess=getUBX_ACK(setGSV);
   }
   gps_set_sucess=0;
   debug.print("Switching off NMEA RMC: ");
   uint8_t setRMC[] = { 
   0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x40                   };
   while(!gps_set_sucess)
   {
   sendUBX(setRMC, sizeof(setRMC)/sizeof(uint8_t));
   gps_set_sucess=getUBX_ACK(setRMC);
   }
   gps_set_sucess=0;
   debug.print("Switching off NMEA VTG: ");
   uint8_t setVTG[] = { 
   0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x46                   };
   while(!gps_set_sucess)
   {
   sendUBX(setVTG, sizeof(setRMC)/sizeof(uint8_t));
   gps_set_sucess=getUBX_ACK(setVTG);
 
   }
   */
 
}
 
void loop()
{
  while(1)
  {
    if(Serial.available() > 0) 
    {
      char inByte = Serial.read();
      mySerial.write(inByte);
 
    }
 
  }
}     
// Send a byte array of UBX protocol to the GPS
void sendUBX(uint8_t *MSG, uint8_t len) {
  for(int i=0; i<len; i++) {
    Serial.write(MSG[i]);
    mySerial.print(MSG[i], HEX);
  }
  Serial.println();
}
 
 
// Calculate expected UBX ACK packet and parse UBX response from GPS
boolean getUBX_ACK(uint8_t *MSG) {
  uint8_t b;
  uint8_t ackByteID = 0;
  uint8_t ackPacket[10];
  unsigned long startTime = millis();
  mySerial.print(" * Reading ACK response: ");
 
  // Construct the expected ACK packet    
  ackPacket[0] = 0xB5;	// header
  ackPacket[1] = 0x62;	// header
  ackPacket[2] = 0x05;	// class
  ackPacket[3] = 0x01;	// id
  ackPacket[4] = 0x02;	// length
  ackPacket[5] = 0x00;
  ackPacket[6] = MSG[2];	// ACK class
  ackPacket[7] = MSG[3];	// ACK id
  ackPacket[8] = 0;		// CK_A
  ackPacket[9] = 0;		// CK_B
 
  // Calculate the checksums
  for (uint8_t i=2; i<8; i++) {
    ackPacket[8] = ackPacket[8] + ackPacket[i];
    ackPacket[9] = ackPacket[9] + ackPacket[8];
  }
 
  while (1) {
 
    // Test for success
    if (ackByteID > 9) {
      // All packets in order!
      mySerial.println(" (SUCCESS!)");
      return true;
    }
 
    // Timeout if no valid response in 3 seconds
    if (millis() - startTime > 3000) { 
      mySerial.println(" (FAILED!)");
      return false;
    }
 
    // Make sure data is available to read
    if (Serial.available()) {
      b = Serial.read();
 
      // Check that bytes arrive in sequence as per expected ACK packet
      if (b == ackPacket[ackByteID]) { 
        ackByteID++;
        mySerial.print(b, HEX);
      } 
      else {
        ackByteID = 0;	// Reset and look again, invalid order
      }
 
    }
  }
}

I used that example code to add support (and talk UBX) for the MAX6/7 to the Trackuino source code.

I hope this helps,

Brad
KF7FER

Hey guys, is this document any help ?

u-blox5_Protocol_Specifications(GPS.G5-X-07036).pdf (1.2 MB)

Hey guys, is this document any help ?

Not as much as this document is of help:
http://www.u-blox.com/images/downloads/Product_Docs/u-blox6_ReceiverDescriptionProtocolSpec_(GPS.G6-SW-10018).pdf

(the NEO 6m uses a newer version of protocols... version 6)

I honestly didn't read anything about the NEO 6M, but if you're talking UBX, perhaps the same stuff I've been using for the ublox MAX6 would work?

Yep, if you look at the code I posted, you'll see that it is based on that exact same example "GPS Level Convertor Board Test Script"

The full code is here: guides:ublox6 [UKHAS Wiki]

It didn't work for me though! Not really sure why.

@Brad Burleson: When you run the code you posted, do you receive an ACK-ACK message in response from the module? (a.k.a. your serial monitor would spit out "Success!"

macdonaldtomw:
Yep, if you look at the code I posted, you'll see that it is based on that exact same example "GPS Level Convertor Board Test Script"

Sorry I didn't do that. My bad.

@Brad Burleson: When you run the code you posted, do you receive an ACK-ACK message in response from the module? (a.k.a. your serial monitor would spit out "Success!"

I'm not sure. I never used the code I posted, only what I derived from it. I doubt it will help but here is the code I'm using

    // Set MAX-6 to flight mode
    sendUBX(setNav, sizeof(setNav)/sizeof(uint8_t));
    gps_status = getUBX_ACK(setNav);
    if (!gps_status) 
      while (1)
        blinker(100, 1, 2000);

    // Switch baud rate
    sendUBX(setBaudRate, sizeof(setBaudRate)/sizeof(prog_char));

    // Now don't forget to switch our serial baud rate to match
    Serial.flush();                              // Empty the buffer
    delay(100);                                  // give it a moment
    Serial.end();                                // Close serial port
    Serial.begin(38400);                         // Re-open w/ at new rate

    // Finally turn off any NMEA sentences we don't need (in this case it's 
    // everything except GGA and RMC)
    sendUBX(setGLL, sizeof(setGLL)/sizeof(uint8_t));   // Disable GGL
    sendUBX(setGSA, sizeof(setGSA)/sizeof(uint8_t));   // Disable GSA
    sendUBX(setGSV, sizeof(setGSV)/sizeof(uint8_t));   // Disable GSV
    sendUBX(setVTG, sizeof(setVTG)/sizeof(uint8_t));   // Disable VTG

    // Finally save the configuration in case we have power issues
    // (or try to sleep)	
    sendUBX(saveConfig, sizeof(saveConfig)/sizeof(prog_char));
    gps_status = getUBX_ACK(saveConfig);
    if (!gps_status)
      while (1)
        blinker(100, 2, 2000);

I believe that sendUBX and getUBX_ACK is the same as you (and I) have already posted.

I doubt it helped but...

Regards,

Brad
KF7FER

PS Going on IRC to #highaltitude would probably be your best bet; the authors of code posted usually lurk there

I know this is way past the useful date for this thread, but this thread was the first that came up when I was googling for similar problems, so hopefully this is useful for someone else.

Took me a while to figure this out, but there is indeed an error in the fletcher checksum implementation in OP (none of the other code posted by other answer actually try to compute the checksum and is thus irrelevant).

Instead of

//Calculate checksum
  for(int i=0; i<len; i++){
    CK_A = CK_A + MSG[i];
    CK_B = CK_B + CK_A;
    Serial.println("CK_A= " + String(CK_A));
    Serial.println("CK_B= " + String(CK_B));
  }

  sum1 = CK_A &0xff;//Mask the checksums to be one byte
  sum2= CK_B &0xff;

the mask needed to be applied at each stage of the computation, so the correct code should be

//Calculate checksum
  for(int i=0; i<len; i++){
    CK_A = CK_A + MSG[i];
    CK_B = CK_B + CK_A;
    CK_A  = CK_A & 0xff;
    CK_B = CK_B & 0xff; 
    Serial.println("CK_A= " + String(CK_A));
    Serial.println("CK_B= " + String(CK_B));
  }

  sum1 = CK_A;
  sum2= CK_B;

Oh, fer cryin’ out loud! I answered this over here! As I said,

Using 32-bit CS variables is perhaps wasteful, but it does not generate incorrect checksums. He masks them at the end of the computation, in both cases (sending and receiving).

Did you even try it? Here are the calculations with 32-bit ints:

Byte CK A CK B
------ ------ ------
6 6 6
1 7 13
3 10 23
0 10 33
240 250 283
1 251 534
0 251 785

If you mask those last two bytes, you get 251 and 17 (0xFB and 0x11). Those are the correct bytes, Justin. They are the same bytes you would get even if you masked after each addition:

Byte CK A CK B
------ ------ ------
6 6 6
1 7 13
3 10 23
0 10 33
240 250 27
1 251 22
0 251 17

Actually, he could have used 64-bit long long or 16-bit int integers and computed the same CS. More wastefully, but it would have computed the same result. He should have used 8-bit unsigned char, like Brad Burleson’s code above, or like NeoGPS does here. Then, no masking would have been required anywhere.

The reason it doesn’t matter is that the lower 8 bits are added the same way, regardless of sums that carry into the higher bits. At the end, you only look at the lower 8 bits and mask off the upper bits. They just don’t matter.

I do think the ublox spec is misleading, as it suggests exactly what you said:

ublox Neo-6/7/8 Protocol:
26 UBX Checksum
The checksum is calculated over the packet, starting and including the CLASS field, up until, but excluding, the Checksum Field.

The checksum algorithm used is the 8-Bit Fletcher Algorithm, which is used in the TCP standard (RFC 1145 1). This algorithm works as follows:

   Buffer[N] contains the data over which the checksum is to be calculated.

The two CK_ values are 8-Bit unsigned integers, only! If implementing with
  larger-sized integer values, make sure to mask both CK_A and CK_B with 0xFF
  after both operations in the loop.

CK_A = 0, CK_B = 0
      For(I=0;I<N;I++)
      {
          CK_A = CK_A + Buffer[I]
          CK_B = CK_B + CK_A
      }

After the loop, the two U1 values contain the checksum, transmitted at the end of the packet.

An implementer would generate a correct checksum by following this to the letter. However, that does not mean that a CS calculation with larger integers, followed by masking, would generate an incorrect CS. It actually works, as you can see in the above tables.

The real problem is that the OP is trying to print too much while receiving from the GPS. This is a common problem, and I have documented the symptoms and solutions here. Go read it. Then upvote my answer.

With Irrelevant Civility,
/dev


1 The relevant excerpt from RFC 1145:

APPENDIX I: The 8-bit Fletcher Checksum Algorithm:

   The 8-bit Fletcher Checksum Algorithm is calculated over a sequence

of data octets (call them D[1] through D[N]) by maintaining 2
  unsigned 1’s-complement 8-bit accumulators A and B whose contents are
  initially zero, and performing the following loop where i ranges from
  1 to N:

A := A + D[i]
          B := B + A

It can be shown that at the end of the loop A will contain the 8-bit
  1’s complement sum of all octets in the datagram, and that B will
  contain (N)D[1] + (N-1)D[2] + … + D[N].

The octets covered by this algorithm should be the same as those over
  which the standard TCP checksum calculation is performed, with the
  pseudoheader being D[1] through D[12] and the TCP header beginning at
  D[13].  Note that, for purposes of the checksum computation, the
  checksum field itself must be equal to zero.

At the end of the loop, the A goes in the first byte of the TCP
  checksum and B goes in the second byte.