this version has all of Jim's fixes for reading the GPS and IMU data synchronously

Dependencies:   MODSERIAL SDFileSystem mbed SDShell CRC CommHandler FP LinkedList LogUtil

Files at this revision

API Documentation at this revision

Comitter:
jekain314
Date:
Mon Apr 22 21:26:04 2013 +0000
Child:
1:8e24e633f8d8
Commit message:
does not work yet with mbed_test. Correctly collects GPS and IMU data.

Changed in this revision

ADIS16488.h Show annotated file Show diff for this revision Revisions of this file
MODSERIAL.lib Show annotated file Show diff for this revision Revisions of this file
OEM615.h Show annotated file Show diff for this revision Revisions of this file
PCMessaging.h Show annotated file Show diff for this revision Revisions of this file
SDFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ADIS16488.h	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,122 @@
+
+
+
+//set up the SPI on pins 5, 6, 7 to read from the ADIS16488
+SPI spi(p5, p6, p7); // mosi (DIN), miso (DOUT), sclk (CLK)
+DigitalOut ADIS_CS(p8);     //Chip Select for the ADIS SPI
+InterruptIn ADIS_DR(p28);   //DataReady interrupt connected to DIO2 for ADIS
+DigitalOut ADIS_RST(p20);   //ADIS reset pin
+
+bool IMUDataReady = false;
+int IMURecordCounter = 0;
+//see Table 9 from page 11 of the ADIS16488 spec 
+//see fig 15 of spec -- note the low byte of the regsiter word is always zero
+// X_DELTANG_LOW, Y_DELTANG_LOW, X_DETANG_LOW, X_DELTVEL_LOW, Y_DELTVEL_LOW, Z_DELTVEL_LOW
+unsigned short LOW_REGISTER[]  = {0x4000, 0x4400, 0x4800, 0x4C00, 0x5000, 0x5400};   
+// X_DELTANG_HIGH, Y_DELTANG_HIGH, X_DETANG_HIGH, X_DELTVEL_HIGH, Y_DELTVEL_HIGH, Z_DELTVEL_HIGH
+unsigned short HIGH_REGISTER[] = {0x4200, 0x4600, 0x4A00, 0x4E00, 0x5200, 0x5600}; 
+
+volatile unsigned long IMUtimeFrom1PPS = 0;
+volatile int IMUClockCounter = 0;            //counter for IMU samples per sec
+
+union WD { long dataWord; unsigned short pt[2];} wd;
+
+//IMU records are buffered in the IMUDataReady ISR
+const unsigned char IMUrecArraySize = 15;
+
+#pragma pack(1)
+struct IMUREC
+{
+    unsigned long synch;
+    unsigned short msgID;
+    unsigned long GPSTime;
+    long dataWord[6];
+    //  4 + 2 + 4 + 24 = 34
+};
+
+IMUREC imuPing[IMUrecArraySize];
+IMUREC imuPong[IMUrecArraySize];
+IMUREC tempRec;
+volatile bool fillingPingWritingPong = true;
+
+
+unsigned long maxDelIMUmsecs = 0;
+unsigned long delIMUmsecs = 0;      
+unsigned long lastIMUmsecs = 0; 
+
+void IMUDataReadyISR(void)
+{   
+    IMUtimeFrom1PPS = timeFromPPS.read_us();        
+    tempRec.GPSTime = GPSTimemsecs + PPSTimeOffset*1000 + IMUtimeFrom1PPS/1000.0;
+    
+    if (IMUClockCounter == IMUrecArraySize ) 
+    {
+        IMUDataReady = true;
+        fillingPingWritingPong = !fillingPingWritingPong;
+        IMUClockCounter = 0;
+    }
+  
+    spi.write((int) HIGH_REGISTER[0]); //next read will return results from HIGH_REGITER[0]
+    for (int i=0; i<6; i++)  //read the 6 rate and accel variables
+    {
+        wd.pt[1] = (unsigned short)spi.write((int) LOW_REGISTER[i]); 
+        if (i<5)  // dont this on the last because this was pre-called
+        {   wd.pt[0] = (unsigned short)spi.write((int) HIGH_REGISTER[i+1]); }
+        
+        if ( fillingPingWritingPong) tempRec.dataWord[i] = wd.dataWord; //data word is a signed long
+        else                         tempRec.dataWord[i] = wd.dataWord; //data word is a signed long
+    }           
+            
+    if (fillingPingWritingPong)  imuPing[IMUClockCounter] = tempRec;
+    else                         imuPong[IMUClockCounter] = tempRec;
+    
+    IMUClockCounter++;
+    
+    return;
+} 
+
+void setupADIS(void)
+{
+    ADIS_DR.mode(PullDown);
+    ADIS_RST = 0;
+    
+    //  set the IMU dataReady ISR
+    ADIS_DR.rise(&IMUDataReadyISR);
+    
+    // Setup the mbed SPI for 16 bit data, high steady state clock,
+    // second edge capture, with a 1MHz clock rate
+    spi.format(16,3);
+    spi.frequency(5000000);   
+    
+    ADIS_CS = 1;  //CS must be set high before it goes low cause the enable is the transition
+    ADIS_RST = 1;
+    wait(0.5);
+    ADIS_CS = 0; //set the Chip select low to enable the IMU SPI access
+    
+    spi.write((int)0x8003);  //change to page 3
+    
+    //change the DECRATE to 98.4 Hz (this is also in page 3)
+    //the 8 sets the high bit to 1 indicating a write to a register
+    // The C abd D designate the registers for the DECRATE of Page 3
+    // The 0x17 sets the rate to:  2460/(23+1) = 102.5Hz
+    // The 0x18 sets the rate to:  2460/(24+1) =  98.4Hz
+    spi.write((int)0x8C17);    //write high byte  (only page number can be written in a single byte)
+    spi.write((int)0x8D00);    //write the low byte of DECRATE 
+    
+    //to set the GPS VARF clock as the input synch clock for the IMU
+    //the high byte is CD indicating the synch is enabled on the rising edge of the input clock  
+    //spi.write((int)0x86CD);    //write high byte to register 0x06
+    //spi.write((int)0x8700);    //write the low byte of 00 to registed 0x07
+    
+    //change the page to 0 to get the data
+    spi.write((int)0x8000);  //change to page 0
+    
+    toPC.printf(" setting the default values\n");
+    
+    //set the IMU synch and message ID
+    tempRec.synch = 0x1C1244AA;  //same as the GPS synch words
+    tempRec.msgID = 111;  //IMU record ID
+    
+    toPC.printf(" finished setting the default values\n");
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL.lib	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/AjK/code/MODSERIAL/#ae0408ebdd68
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OEM615.h	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,278 @@
+    
+#pragma pack(1)  //this forces the structure to be packed on byte boundaries (with no byte filler)
+//set up the GPS message header in a structure to enable easy reading -- see the OEM615 manual (Table 4, page 24)
+struct MESSAGEHEADER   
+{
+    char synchAA;                   //1st synch word
+    char synch44;                   //2nd synch word
+    char synch12;                   //3rd synch word
+    unsigned char headerLength;     //always 28
+    unsigned short messageID;       //42  (0x2A  BESTPOS) , 43 (2B  RANGE) , or 99 (x63 BESTVEL), 
+    char messageType;               //always = 0 for binary
+    unsigned char portAddress;      //0x20 for COM1
+    // from spec:  The length in bytes of the body of the message, not including the header nor the CRC
+    unsigned short messageLength;   //not including header or CRC
+    unsigned short sequence;        //typically 0
+    unsigned char idleTime;         //Time the processor is idle, in the last second
+    unsigned char timeStatus;       //Enum Indicating quality of the GPS reference time
+    unsigned short GPSweek;         //GPS reference week
+    unsigned long GPSTime_msecs;    //from beginning of week
+    unsigned long receiverStatus;   //32-bits representing the status of hardware and software
+    unsigned short reserved;
+    unsigned short receiverSWversion;  //receiver software build number
+    //total length in bytes of this header is 28
+};
+MESSAGEHEADER *msgHeader[6];
+
+#pragma pack(1)
+//structure for OEM615 BESTVEL log message  (page 314)
+struct OEM615BESTVEL
+{
+    MESSAGEHEADER msgHeader;
+    int solStatus;                      //solution status
+    //solutionStatusOEMStar solStatus;  //solution status
+    int velType;                        //position or velocity type
+    //velTypeOEMStar posType;           //position or velocity type   
+    float latency;
+    float age;
+    double horizontalSpeed;         //horizontal velocity vector magnitude (m/s)
+    double heading;                 //deg from North called TRK GND in specification
+    double verticalSpeed;           //vertical velocity magnitude (m/s)
+    float reserved;
+    unsigned long CRC;
+};
+
+/*  Solution Status descritpion from OEMV manual
+0   SOL_COMPUTED        Solution computed
+1   INSUFFICIENT_OBS    Insufficient observations
+2   NO_CONVERGENCE      No convergence
+3   SINGULARITY         Singularity at parameters matrix
+4   COV_TRACE           Covariance trace exceeds maximum (trace > 1000 m)
+5   TEST_DIST           Test distance exceeded (maximum of 3 rejections if distance >10 km)
+6   COLD_START          Not yet converged from cold start
+7   V_H_LIMIT           Height or velocity limits exceeded
+8   VARIANCE            Variance exceeds limits
+9   RESIDUALS           Residuals are too large
+10  DELTA_POS           Delta position is too large
+11  NEGATIVE_VAR        Negative variance
+12  Reserved
+13  INTEGRITY_WARNING   Large residuals make position unreliable
+18  PENDING             
+19  INVALID_FIX         The fixed position, entered using the FIX POSITION command, is not valid
+20  UNAUTHORIZED        Position type is unauthorized - HP or XP 
+*/
+
+#pragma pack(1)
+//structure for BESTPOS message 
+struct OEM615BESTPOS
+{
+    MESSAGEHEADER msgHeader;
+    int solStatus;                      //solution status
+    //solutionStatusOEMStar solStatus;  //solution status
+    int posType;                        //position or velocity type
+    //posTypeOEMStar posType;           //position or velocity type
+    double latitude;                    //latitude
+    double longitude;                   //longitude
+    double height;                      //height above mean sea level
+    float undulation;                   //the realtionship between the geoid and the
+                                        //ellipsoid of the chosen datum (m)
+    char datumID[4];                    //datum ID is actually an enum that is not implemented
+    float latitudeSTD;                  //latitude standard deviation
+    float longitudeSTD;                 //longitude standard deviation
+    float heightSTD;                    //height standard deviation
+    char baseStationID[4];              //base station ID
+    float diffAge;                      //differential age (s)
+    float solutionAge;                  //solution age (s)
+    unsigned char numSV;                //number of satellite vehicles tracked
+    unsigned char numSolSV;             //number of satellite vehicles used in solution
+    unsigned char numGGL1;              //number of GPS plus Glonass L1
+    unsigned char res1;
+    unsigned char res2;
+    unsigned char extSolStatus;         //extended solution status
+    unsigned char res3;
+    unsigned char sigMask;              //signals used mask
+    unsigned long CRC;
+};
+
+//GPS-specific pins
+DigitalOut GPS_Reset(p18);      //GPS RESET line
+InterruptIn PPSInt(p15);        // GPS 1PPS (timemark) from the OEM615
+InterruptIn IMUClock(p17);
+
+Timer timeFromPPS;
+unsigned long GPSTimemsecs = 0;
+double GPSTime = 0;
+
+//mbed tx/rx interface to the GPS COM1 port
+MODSERIAL GPS_COM1(p9,p10);  //this serial port communicates with the GPS receiver serial port (COM1)
+
+int test = 0;
+unsigned short messageCounter = 0;
+unsigned short savedMessageCounter = 0;
+const unsigned short maxGPSbytesPerSec = 1536;
+unsigned char msgBuffer[maxGPSbytesPerSec];  //array to contain one full second of GPS bytes
+const unsigned char maxGPSMessagesPerSec = 12;
+unsigned short messageLocation[maxGPSMessagesPerSec] = {0};  //stores the message location start within the message buffer
+
+unsigned short bytesFromMessageHdrDetect = 0;
+union SWAPBYTES { unsigned char b[2]; unsigned short w; };
+volatile SWAPBYTES messageLength;  //used to swap the bytes
+unsigned long computedCRC = 0;  //final resulting computed CRC for this message
+unsigned long incrementalCRC = 0;  //incrementally-formed CRC over message sequence
+unsigned short endByteForCRCcomputation[maxGPSMessagesPerSec];
+bool completeMessageAvailable = false;
+
+//this code was taken from the Novatel Firmware document page 35
+//#define CRC32_POLYNOMIAL 0xEDB88320L
+/* --------------------------------------------------------------------------
+Calculate a CRC value to be used by CRC calculation functions.
+-------------------------------------------------------------------------- */
+/*
+////////////////////////////////////////////
+//original code from the OEM615 manual
+////////////////////////////////////////////
+unsigned long CRC32Value(int i)
+{
+    int j;
+    unsigned long ulCRC;
+    ulCRC = i;
+    for ( j = 8 ; j > 0; j-- )
+    {
+        if ( ulCRC & 1 )
+        ulCRC = ( ulCRC >> 1 ) ^ CRC32_POLYNOMIAL;
+        else
+        ulCRC >>= 1;
+    }
+    return ulCRC;
+} 
+*/
+
+#define CRC32_POLYNOMIAL 0xEDB88320L
+void CRC32Value(unsigned long &CRC, unsigned char c)
+{
+    /////////////////////////////////////////////////////////////////////////////////////
+    //CRC must be initialized as zero 
+    //c is a character from the sequence that is used to form the CRC
+    //this code is a modification of the code from the Novatel OEM615 specification
+    /////////////////////////////////////////////////////////////////////////////////////
+    unsigned long ulTemp1 = ( CRC >> 8 ) & 0x00FFFFFFL;
+    unsigned long ulCRC = ((int) CRC ^ c ) & 0xff ;
+    for (int  j = 8 ; j > 0; j-- )
+    {
+        if ( ulCRC & 1 )
+            ulCRC = ( ulCRC >> 1 ) ^ CRC32_POLYNOMIAL;
+        else
+            ulCRC >>= 1;
+    }
+    CRC = ulTemp1 ^ ulCRC;
+} 
+
+/* --------------------------------------------------------------------------
+Calculates the CRC-32 of a block of data all at once
+//the CRC is from the complete message (header plus data) 
+//but excluding (of course) the CRC at the end
+-------------------------------------------------------------------------- */
+unsigned long CalculateBlockCRC32(
+        unsigned long ulCount,    /* Number of bytes in the data block */
+        unsigned char *ucBuffer ) /* Data block */
+{
+    //////////////////////////////////////////////////////////////////////
+    //the below code tests the CRC32Value procedure used in a markov form
+    //////////////////////////////////////////////////////////////////////
+    unsigned long CRC = 0;
+    for (int i = 0; i<ulCount; i++)  CRC32Value( CRC, *ucBuffer++ );
+    return  CRC;
+}
+
+/*
+unsigned long CalculateBlockCRC32(
+        unsigned long ulCount, 
+        unsigned char *ucBuffer )
+{
+////////////////////////////////////////////
+//original code from the OEM615 manual
+////////////////////////////////////////////
+    unsigned long ulTemp1;
+    unsigned long ulTemp2;
+    unsigned long ulCRC = 0;
+    while ( ulCount-- != 0 )
+    {
+        ulTemp1 = ( ulCRC >> 8 ) & 0x00FFFFFFL;
+        ulTemp2 = CRC32Value( ((int) ulCRC ^ *ucBuffer++ ) & 0xff );
+        ulCRC = ulTemp1 ^ ulTemp2;
+    }
+    return( ulCRC );
+}
+*/
+void sendASCII(char* ASCI_message, int numChars)
+{
+    /////////////////////////////////////////////////
+    //send an ASCII command to the GPS receiver
+    /////////////////////////////////////////////////
+
+    //char ASCI_message[] = "unlogall COM1";
+    int as = numChars - 1;
+    unsigned char CR = 0x0d;  //ASCII Carriage Return
+    unsigned char LF = 0x0a;  //ASCII Line Feed
+    
+    //printf("%s", ch);
+    //printf("\n");
+
+    for (int i=0; i<as; i++) GPS_COM1.putc(ASCI_message[i]); 
+    GPS_COM1.putc(CR);   //carriage return at end
+    GPS_COM1.putc(LF);   //line feed at end
+};
+
+
+//see the mbed COOKBOOK for MODSERIAL
+//MODSERIAL is an easy to use library that extends Serial to add fully buffered input and output.
+void readSerialByte(MODSERIAL_IRQ_INFO *q)
+{ 
+    MODSERIAL *serial = q->serial;      //see example of MODSERIAL usage in cookbook
+    unsigned char synch0 = serial->getc();  //get the next byte
+    
+    //byteCounter is zeroed only at a 1PPA event in the 1PPS ISR
+    //all message bytes stored for a single GPS second 
+    msgBuffer[byteCounter % maxGPSbytesPerSec] = synch0;
+    
+     //accumulate the CRC for this message
+     //incrementalCRC re-initialized after message header is is detected
+     CRC32Value( incrementalCRC, synch0); 
+
+    //Trap the GPS message header byte-string per Novatel OEM615 spec: 0xAA44121C
+    //generate a 4-byte sliding-window sequence from the input bytes
+    //shift last 4-byte value left 8 bits & push current-read byte (synch0) into low-order byte
+    test = (test<<8) | synch0; 
+   
+    if (test == 0xAA44121C) //test for the Receiver message header signature
+    {
+        messageLocation[perSecMessageCounter % maxGPSMessagesPerSec] = byteCounter-3; //store the location of this message (1st of 4 synch word)
+        perSecMessageCounter++;  //counts messages this second
+        bytesFromMessageHdrDetect = 0;  //start byte counter for this message
+        incrementalCRC = 0x39b0f0e1;    //initializes the recursive CRC after the AA44121C header byte sequence 
+     }
+     else if (bytesFromMessageHdrDetect == 5) messageLength.b[0] = synch0;  //first byte of msg length
+     else if (bytesFromMessageHdrDetect == 6)   //second byte of message length
+     {
+        messageLength.b[1] = synch0; 
+        endByteForCRCcomputation[perSecMessageCounter] = 24 + messageLength.w;  //use union to perform byte-swap from stored bytes
+     }
+     else if (bytesFromMessageHdrDetect == endByteForCRCcomputation[perSecMessageCounter])  //stop the CRC recursive computation
+        computedCRC = incrementalCRC;  //store the computed CRC for this message for use in main  
+     else if (bytesFromMessageHdrDetect == (endByteForCRCcomputation[perSecMessageCounter] + 4) )  //detect the end of the message (end of its CRC)
+     {
+        savedMessageCounter = perSecMessageCounter;  //message counter can be corrupted before use in main
+        completeMessageAvailable = true;  //set flg for the main message processing
+     }
+         
+     //byteCounter reset to zero in main after the 1PPS is detected -- its NOT reset in the 1PPS ISR
+     byteCounter++;     //total per-sec byte counter (reset to zero in main when 1PPS detected) 
+     bytesFromMessageHdrDetect++;  //counts the byes received after a message header
+};
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PCMessaging.h	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,305 @@
+//these are defines for the messages that are sent from the PC across the USB
+//these messages produce reactions on the mbed
+const unsigned char  STATUS_MSG         =0;
+const unsigned char  POSVEL_MSG         =1;
+const unsigned char  STARTDATA_MSG      =2;
+const unsigned char  STOPDATA_MSG       =3;
+const unsigned char  STARTSTREAM_MSG    =4;
+const unsigned char  STOPSTREAM_MSG     =5;
+const unsigned char  STARTLOGINFO_MSG   =6;
+const unsigned char  STOPLOGINFO_MSG    =7;
+
+const double  DEGREES_TO_RADIANS = acos(-1.0)/180.0;
+const double eccen          = 0.0818191908426;  //WGS84 earth eccentricity
+const double earthRadius    = 6378137;          //WGS84 earthRadius in meters
+
+char serBuf[128];
+int serBufChars=0;
+
+//flags to control the PC command actions
+bool sendPosVel     =false;
+bool sendStatus     =false;
+bool sendRecData    =false;
+bool streamPos      =false;
+bool sendStreamPos  =false;
+bool logMsgInfo     =false;
+bool sendLogMsgInfo =false;
+bool recordData     =false;
+
+
+const unsigned char numMessages = 8;    //number of potential messages
+char msgList[numMessages][32];          //text array storing the command messages from the PC
+char minMessageSize = 11;               //minimum size of a text message
+unsigned char CR = 0x0d;                //ASCII Carriage Return
+unsigned char LF = 0x0a;                //ASCII Line Feed
+
+void setUpMessages(void)
+{ 
+    //set up the ASCII text records that are candidates to be passed from the PC
+    sprintf(msgList[STATUS_MSG],        "WMsg STATUS");
+    sprintf(msgList[POSVEL_MSG],        "WMsg POSVEL");
+    sprintf(msgList[STARTDATA_MSG],     "WMsg RECORDDATA Y");
+    sprintf(msgList[STOPDATA_MSG],      "WMsg RECORDDATA N");
+    sprintf(msgList[STARTSTREAM_MSG],   "WMsg POSSTREAM Y");
+    sprintf(msgList[STOPSTREAM_MSG],    "WMsg POSSTREAM N");
+    sprintf(msgList[STARTLOGINFO_MSG],  "WMsg LOGINFO Y");
+    sprintf(msgList[STOPLOGINFO_MSG],   "WMsg LOGINFO N");
+    //message length is from 10 to 16 chars
+    
+    toPC.printf(" finished setting up messages \n");
+}
+
+void readFromPC()
+{
+     //The received commands only occur at the initialization stage -- time is not that critical there.
+    //during the real-time action, we will never pass the followong if test ( no characters received)
+    if (toPC.readable()) //read a PC serial byte and test it for a command
+    {
+        
+        // Read in next character
+        char inChar = toPC.getc();  //read char from the USB serial link to the PC
+        //toPC.printf("%02x ",inChar);
+        
+        //incoming messages will end witb a CR / LF -- disregard these chars
+        if (inChar == CR || inChar == LF)  return; //CR is a 0x0a
+        
+        serBuf[serBufChars++] = inChar; //set this char in a char array
+        
+        //no need to continue if numChars are less than the shortest candidate message
+        //if (serBufChars < minMessageSize) return;
+                
+        // Append end of string
+        //We always assume we have a complete message string and test for this below
+        serBuf[serBufChars] = '\0';
+        
+        bool validMessage = false;
+        
+        
+        // Check for valid message -- there are numMessages possible messages
+        for (int m = 0; m < numMessages && !validMessage; m++) //check for all messages ... 
+        {
+            //toPC.printf(" \n\n found chars to test %3d %3d %s \n\n\n ", serBufChars, strlen(msgList[m]), serBuf );
+
+            //check the current partial message against ALL possible messages
+            //messages must match in both strength length and text
+            if (serBufChars == strlen(msgList[m]) && strncmp(serBuf, msgList[m], serBufChars) == 0 )
+            {
+            
+                //toPC.printf( "\n       found valid message %s  \n\n", serBuf);
+
+                validMessage = true;
+                serBufChars = 0; //reset the character count to reset for next message
+    
+                //set programmatic action flags based on the message
+                switch(m)
+                {
+                    case STATUS_MSG:
+                        sendStatus = true;  //send a status message back to PC
+                    break;
+                    case POSVEL_MSG:
+                        sendPosVel = true;  //send a posvel message back to PC
+                    break;
+                    case STARTDATA_MSG:  //start the data recording to the SD card
+                        recordData = true;
+                        sendRecData = true;
+                        break;
+                    case STOPDATA_MSG:   //stop the data recording to the SD card
+                        recordData = false;
+                        sendRecData = true;
+                        break;
+                    case STARTSTREAM_MSG:
+                    case STOPSTREAM_MSG:
+                        streamPos = (m == STARTSTREAM_MSG);
+                        sendStreamPos = true;
+                    break;
+                    case STARTLOGINFO_MSG:
+                    case STOPLOGINFO_MSG:
+                        logMsgInfo = (m == STARTLOGINFO_MSG);
+                        sendLogMsgInfo = true;
+                    break;  
+                }  //end Switch statement
+                break;
+            } //end test for a valid message
+        }  //end message text loop
+    }  //end pc.readable
+};
+
+void earthCoefficients(double latitudeRad, double longitudeRad, double height, double &latRateFac, double &lonRateFac)
+{
+    //compute the lat and lon factors for use in the interpolation of the lat and lon between 1 sec epochs
+    //latRateFac & lonRateFac multiplied by Vnorth or VEast to get latRate and lonRate
+    //see this document (page 32)    www.fas.org/spp/military/program/nav/basicnav.pdf
+
+    double eccenSinLat = eccen * sin(latitudeRad); 
+    double temp1 = 1.0 - eccenSinLat*eccenSinLat;
+    double temp2 = sqrt(temp1);
+    double r_meridian = earthRadius * ( 1.0 - eccen*eccen)/ (temp1 * temp2);
+    double r_normal   = earthRadius / temp2;
+        
+    //divide Vnorth by latRateFac to get the latitude rate in deg per sec
+    latRateFac = (r_meridian + height)* DEGREES_TO_RADIANS;
+    
+    //divide VEast by lonRateFac to get the longitude rate in deg per sec
+    lonRateFac =  (r_normal + height) * cos(latitudeRad)* DEGREES_TO_RADIANS;
+}
+
+void sendPosVelMessageToPC(OEM615BESTPOS posMsg, OEM615BESTVEL velMsg)
+{
+            //north and east velocity from the horizontal speed and heading
+            //velMsg may not be the "current" message --- but is the one also associated with a position message
+            double nVel = velMsg.horizontalSpeed*cos(velMsg.heading*DEGREES_TO_RADIANS);
+            double eVel = velMsg.horizontalSpeed*sin(velMsg.heading*DEGREES_TO_RADIANS);
+            
+            double latRateFac;
+            double lonRateFac;
+                        
+            earthCoefficients(  posMsg.latitude*DEGREES_TO_RADIANS, 
+                                posMsg.longitude*DEGREES_TO_RADIANS, 
+                                posMsg.height, 
+                                latRateFac, lonRateFac);
+            
+            //commented calculations are for a spherical earth (Chris's original computation)
+            // For the 1 second deltas with which we are dealing
+            // This calculation should be close enough for now
+            // Approximately 1 nautical mile / minute latitude, 60 minutes/degree, 1852 meters/nautical mile
+            //double latMetersPerDeg = 60.0*1852.0;
+            // longitude separation is approximately equal to latitude separation * cosine of latitude
+            //double lonMetersPerDeg = latMetersPerDeg*cos(posMsg.latitude*DEGREES_TO_RADIANS);
+
+            // Elapsed time since last known GPS position
+            //PPSTimeOffset is a result of possibly missing a prior GPS position message
+            // timeFromPPS.read() is always the time from the moset recent 1PPS
+            double elTime = (double)PPSTimeOffset + timeFromPPS.read();
+            
+            // Position time -- GPSTime is the time of the last valid GPS position message
+            double posTime = GPSTime + elTime;
+            
+            //toPC.printf(" elTime = %6.3f PPSimeOffset = %6.3f \n", elTime, PPSTimeOffset);
+            //toPC.printf(" latRateFac = %10.3f  lonRateFac = %10.3f \n", latRateFac, lonRateFac);
+            //toPC.printf(" latRateFac = %10.3f  lonRateFac = %10.3f \n", latMetersPerDeg, lonMetersPerDeg);
+
+            // Estimated position based on previous position and velocity
+            // posMsg is the last time when the BESTVEL and BESTPOS messages had identical times
+            //double latPos = posMsg.latitude  + (nVel/latMetersPerDeg)*elTime;
+            //double lonPos = posMsg.longitude + (eVel/lonMetersPerDeg)*elTime;
+            
+            double latPos = posMsg.latitude  + (nVel/latRateFac)*elTime;
+            double lonPos = posMsg.longitude + (eVel/lonRateFac)*elTime;            
+            double htPos  = posMsg.height    + velMsg.verticalSpeed/(60*1852)*elTime;
+            
+            char solReady = 'N';
+            //solStatus 
+            if (posMsg.solStatus == 0) //see description of solution status in OEMV615.h
+            {
+                solReady = 'Y';
+            }
+            
+            toPC.printf("WMsg POSVEL %5.3lf %1d %c %8.5lf %9.5lf %4.3lf %4.3lf %4.3lf %4.3lf\n", 
+                         posTime, 
+                         posMsg.numSolSV,
+                         solReady,
+                         latPos,
+                         lonPos,
+                         htPos,
+                         nVel,
+                         eVel,
+                         velMsg.verticalSpeed
+                         );
+}
+
+void processPCmessages(FILE* &fpNav, OEM615BESTPOS posMsg, OEM615BESTVEL velMsg)
+{
+
+        //we should put the below stuff into the readPC() procedure.
+        //only do these actions in response to a command so no need for the tests w/o an inoput byte from the PC
+        //perform the activities as a response to the commands
+        if (sendPosVel)  //true if we want to return a position solution
+        {
+            sendPosVel=false; //set to true if a POSVEL is requested from the PC
+            sendPosVelMessageToPC(posMsg, velMsg);
+        }
+        
+        //all this does is assess the GPS convergence -- really available in the above
+        if (sendStatus)  //send the status message to the PC
+        {
+            sendStatus=false;
+            char solReady = 'N';
+            //solStatus 
+            if (posMsg.solStatus == 0) //see description of solution status in OEMV615.h
+            {
+                solReady = 'Y';
+            }
+            toPC.printf("WMsg STATUS %5.3lf %c\n", 
+                         GPSTime, 
+                         solReady
+                         );
+        }
+        
+        //should just record ALL the data -- can pick over it in the post-processing
+        if (sendRecData)  //begin to (or stop) record the serial data
+        {
+            sendRecData=false;
+            char recChar = 'N';
+            if (recordData)
+            {
+                if ((fpNav == NULL))
+                {
+                    fpNav = fopen("/sd/Data/NAV.bin", "wb");
+                    setvbuf(fpNav, NULL, _IONBF, 512); 
+                    //toPC.printf("\n opened the SD card file recordData=%10d \n\n", fpNav);
+                }
+                if (fpNav != NULL)
+                {
+                    recChar = 'Y';
+                }
+                else
+                {
+                    toPC.printf(" Could not open the SD card \n\n");
+                }
+            }
+            else
+            {
+                if (fpNav != NULL)
+                {
+                    toPC.printf(" closing the SD card file \n\n");
+                    fclose(fpNav);
+                    wait(1.0);
+                    //toPC.printf("\n after closing the SD card file \n\n");
+                    recordData = false;
+                    fpNav = NULL;
+                }
+            }
+            toPC.printf("WMsg RECORDDATA %c\n", 
+                         recChar
+                         );
+        }
+        
+        if (sendStreamPos)  //stream the position data to the PC
+        {
+            sendStreamPos=false;
+            char streamChar = 'N';
+            if (streamPos)
+            {
+                streamChar = 'Y';
+            }
+            toPC.printf("WMsg POSSTREAM %c\n", 
+                         streamChar
+                         );
+        }
+        
+        //not sure this is ever used ..
+        if (sendLogMsgInfo)  //send log info to the PC
+        {
+            sendLogMsgInfo=false;
+            char logChar = 'N';
+            if (logMsgInfo)
+            {
+                logChar = 'Y';
+            }
+            toPC.printf("WMsg LOGINFO %c\n", 
+                         logChar
+                         );
+        }
+        
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.lib	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/SDFileSystem/#c8f66dc765d4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,371 @@
+#include "mbed.h" 
+#include <string>
+
+//set up the message buffer to be filled by the GPS read process
+#define MODSERIAL_DEFAULT_RX_BUFFER_SIZE 256 
+
+#include "MODSERIAL.h"
+#include "SDFileSystem.h"      //imported using the import utility    
+
+//general digital I/O specifications for this application
+//SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name);
+SDFileSystem sd(p11,p12,p13,p14,"sd");
+DigitalIn sd_detect(p27);
+DigitalOut ppsled(LED1);        //blink an LED at the 1PPS
+DigitalOut trig1led(LED2);      //blink an LED at the camera trigger detection
+DigitalOut recordDataled(LED4); //set the led when the record is on
+InterruptIn camera1Int(p30);    // camera interrupt in
+DigitalOut camera2Pin(p29);     // We dont use the second camera interrupt
+//USB serial data stream back to the PC
+Serial toPC(USBTX, USBRX);      //connect the GPS TX, RX to p9 and p10
+
+Timer timeFromStart;
+
+bool detectedGPS1PPS = false;       //flag set in the ISR and reset after processing the 1PPS event
+int PPSCounter = 0;                 //counts the 1PPS occurrences
+int byteCounter = 0;                //byte counter -- zeroed at 1PPS
+unsigned short perSecMessageCounter=0; //counts the number of messages in a sec based on the header detection
+bool messageDetected = false;       //have detected a message header
+unsigned long IMUbytesWritten = 0;  //counts the IMU bytes written by the fwrite() to the SD card 
+int savedByteCounter = 0;           //save ByteCounter at the 1PPS for display in main
+int savedPerSecMessageCounter=0;    //saved PerSecMsgCounter for display in main
+int savedIMUClockCounter=0;         //saved at the 1PPS for later diaplay from main
+bool camera1EventDetected = false;  //flag from ISR indicating a clock event occurred
+double camera1Time;                 //GPS time of the camera event 
+int TotalBadCRCmatches = 0;         //counter for the bad CRC matches for all GPS messages
+
+volatile int PPSTimeOffset = 0;
+
+//////////////////////////////////////////////////////////////////////
+// the below should become classes
+//////////////////////////////////////////////////////////////////////
+#include "OEM615.h"         //OEM615 GPS activities
+#include "ADIS16488.h"      //ADIS16488 activities
+#include "PCMessaging.h"    //PC messaging activities
+
+//ISR for detection of the GPS 1PPS
+void detect1PPSISR(void)
+{
+    timeFromPPS.reset();                    //reset the 1PPS timer upon 1PPS detection
+    //note -- the below accounts for time information becoming available AFTER the 1PPS event
+    PPSTimeOffset++;                //counts 1PPS events between matching POS and VEL messages
+    
+    //covers the case where the PPS ISR interrupts the IMU data ready ISR
+    if(IMUDataReady) IMUtimeFrom1PPS = 0;
+            
+    savedByteCounter = byteCounter;         //save byteCounter for display in main
+    savedPerSecMessageCounter = perSecMessageCounter;   //save for display un main
+    byteCounter = 0;                        //countes bytes between 1PPS events 
+    perSecMessageCounter = 0;               //counts GPS messages between 1PPS events
+    
+    GPS_COM1.rxBufferFlush();               //flush the GPS serial buffer
+    
+    detectedGPS1PPS = true;         //set false in the main when 1PPS actions are complete
+    PPSCounter++;                   //count number of 1PPS epoch
+    
+    ppsled = !ppsled;               //blink an LED at the 1PPS
+};
+
+//ISR for detection of the hotshoe trigger 1
+void camera1ISR(void)
+{
+    //GPSTime is from POS message header
+    //PPSTimeOffset is an even sec to account for Time becoming known AFTER the 1PPS
+    //PPSTimeOffset + timeFromPPS.read() can be as large as 1.02 secs
+    camera1Time = GPSTime + PPSTimeOffset + timeFromPPS.read();    
+    camera1EventDetected = true;  //reset to false in main after processing the image detection
+    trig1led = !trig1led;  //blink an LEWD at the camera event detection
+};
+
+///////////////////////////////////////////////////////
+//set up the USB port and the GPS COM port
+/////////////////////////////////////////////////////// 
+FILE *fpNav = NULL;  //file pointer to the nav file on the SD card
+void setupCOM(void)
+{
+    //system starts with GPS in reset active
+    //dis-engage the reset to get the GPS started
+    GPS_Reset=1; wait_ms(1000); 
+    
+    //establish 1PPS ISR 
+    PPSInt.rise(&detect1PPSISR);
+    
+    //set the USB serial data rate -- rate must be matched at the PC end
+    //This the serial communication back to the the PC host
+    //Launch the C++ serial port read program there to catch the ASCII characters
+    //toPC.baud(9600); wait_ms(100);    
+    toPC.baud(8*115200); wait_ms(100);
+    //toPC.baud(1*115200); wait_ms(100);
+    toPC.printf("\n\n released GPS from RESET and set to high baud rate \n\n");
+    
+    //just wait to launch the GPS receiver
+    for (int i=0; i<5; i++) { toPC.printf(" to start: %3d \n", 4-i); wait(1); }
+
+    sd_detect.mode(PullUp);
+    
+    if (sd_detect == 0)
+    {
+        mkdir("/sd/Data", 0777);
+    }
+    else
+    {
+        toPC.printf(" SD card not present \n");
+    }
+    
+    //NOTE:  we do not assume that the GPS receiver has been pre-set up for the WALDO_FCS functionality
+    //we alwsys start with a reset and reprogram the receiver with our data out products
+    // this prevents failure because of a blown NVRAM as occurred for the older camera systems
+    
+    //this is the COM1 port from th GPS receiuver to the mbed
+    //it should be always started at 9600 baud because thats the default for the GPS receiver 
+    GPS_COM1.baud(9600); wait_ms(100);
+   
+    // this ASCII command sets up the serial data from the GPS receiver on its COM1
+    char ch7[] = "serialconfig COM1 9600 n 8 1 n off";
+    // this is a software reset and has the same effect as a hardware reset (why do it?) 
+    //char ch0[] = "RESET"; 
+    //this command stops all communication from the GPS receiver on COM1
+    //logs should still be presented on USB port so the Novatel CDU application can be used on the PC in parallel
+    char ch1[] = "unlogall COM1";
+    //set the final baud rate that we will use from here  
+    //allowable baud rate values: 9600 115200 230400 460800 921600
+    //char ch2[] = "serialconfig COM1 921600 n 8 1 n off";
+    char ch2[] = "serialconfig COM1 115200 n 8 1 n off";
+    
+    //the below commands request the POS, VEL, RANGE, and TIME messages
+    char ch3[] = "log COM1 BESTPOSB ONTIME 1";   //messageID = 42 
+    char ch4[] = "log COM1 BESTVelB ONTIME 1";   //messageID = 99
+    char ch5[] = "log COM1 RANGEB ONTIME 1";     //messageID = 43
+    //char ch6[] = "log COM1 TIMEB ONTIME 1";      //messageID = 101
+    
+    //set up VARF to be 100Hz with 1X10^4 * 10^-8 = 10^-4 sec (10usec) pulse width
+    //in fact, we do not use this output but it is available.
+    //originally planned to use this to command the IMU data
+    //char ch8[] = "FREQUENCYOUT enable 10000 1000000";
+    
+    toPC.printf("set serial config \n");
+    sendASCII(ch7, sizeof(ch7)); wait_ms(500);
+    //sendASCII(ch0, sizeof(ch0));  
+    toPC.printf("unlog all messages \n");
+    sendASCII(ch1, sizeof(ch1)); wait_ms(500);
+    toPC.printf("log BESTPOSB on COM1 \n");
+    sendASCII(ch3, sizeof(ch3)); wait_ms(500);
+    toPC.printf("log BESTVELB on COM1\n");
+    sendASCII(ch4, sizeof(ch4)); wait_ms(500);
+    toPC.printf("log RANGEB on COM1\n");
+    sendASCII(ch5, sizeof(ch5)); wait_ms(500);
+    
+    //toPC.printf("log TIMEB om COM1 \n");
+    //sendASCII(ch6, sizeof(ch6)); wait_ms(100);
+    
+    //toPC.printf("Set up th VARF signal \n"); 
+    //sendASCII(ch8, sizeof(ch8)); wait_ms(500);
+       
+    //set GPS output COM1 to the final high rate
+    toPC.printf("set the COM ports to high rate\n");
+    sendASCII(ch2, sizeof(ch2)); wait_ms(500);
+    
+    //set the mbed COM port to match the GPS transmit rate
+    //the below baud rate must match the COM1 rate coming from the GPS receiver 
+    GPS_COM1.baud(115200); wait_ms(500);  //without this wait -- the baud rate is not detected when using MODSERIAL     
+    //GPS_COM1.baud(921600); wait_ms(500);  //without this wait -- the baud rate is not detected when using MODSERIAL     
+};
+
+void setupTriggers()
+{
+    camera1Int.mode(PullUp);
+    camera2Pin = 1;
+    //establish Trigger ISR 
+    camera1Int.rise(&camera1ISR);
+    
+};
+
+/////////////////////////////////////////////////////////////////////
+//  mbed main to support the Waldo_FCS
+/////////////////////////////////////////////////////////////////////
+int main() {
+    
+    //these are structures for the to GPS messages that must be parsed
+    MESSAGEHEADER msgHdr;
+    OEM615BESTPOS posMsg;   //BESTPOS structure in OEMV615.h that has matching time to a BESTVEL message
+    OEM615BESTPOS curPos;   //BESTPOS structure in OEMV615.h
+    OEM615BESTVEL velMsg;   //BESTVEL structure in OEMV615.h that has matching time to a BESTPOS message
+    OEM615BESTVEL curVel;   //BESTVEL structure in OEMV615.h
+
+    //set up the GPS and mbed COM ports
+    setupCOM(); 
+    
+    //set up the ADIS16488 
+    setupADIS();
+    
+    //setup Hotshoe
+    setupTriggers();
+
+    setUpMessages();  //set up the expected text message commands frm the PC 
+    
+    //set up the interrupt to catch the GPS receiver serial bytes as they are presented
+    GPS_COM1.attach(&readSerialByte, MODSERIAL::RxIrq);
+    
+    timeFromPPS.start();  //start the time for measuring time from 1PPS events
+    timeFromStart.start();
+    
+    toPC.printf("\n\n top of the main loop \n\n");
+    
+    int totalBytesWritten = 0;
+    
+    /*establish the initial value for the CRC recursion atter the header
+    unsigned long CRC = 0;
+    CRC32Value(CRC, 0xAA);
+    CRC32Value(CRC, 0x44);
+    CRC32Value(CRC, 0x12);
+    CRC32Value(CRC, 0x1C);
+    //this results in a value of:   0x39b0f0e1
+    toPC.printf(" CRC after AA44121C header: %08x \n", CRC);
+    wait(20);
+    */
+    
+    int CRCerrors = 0;
+    
+    recordData = true;
+    sendRecData = true;     
+    
+    unsigned long cyclesPerSec = 0;
+    unsigned long delTimeOfWrite = 0;
+    unsigned long maxWriteTime = 0;
+    bool GPSdataWritten = false;
+    
+    while(PPSCounter < 1000)
+    ///////////////////////////////////////////////////////////////////////////
+    // top of the while loop
+    ///////////////////////////////////////////////////////////////////////////
+    //while(1)
+    {        
+        //read the USB serial data from the PC to check for commands
+        //in the primary real-time portion, there are no bytes from the PC so this has no impact
+        readFromPC();
+                
+        processPCmessages(fpNav, posMsg, velMsg);
+        
+        cyclesPerSec++;
+
+//
+        ////////////////////////////////////////////////////////////////////////////
+        //below is where we process the complete stored GPS message for the second
+        //The !IMUDataReady test prevents the IMU and GPS data from being written 
+        //to disk on the same pass through this loop  
+        /////////////////////////////////////////////////////////////////////////////
+        
+        
+        if (completeMessageAvailable && !IMUDataReady)
+        {
+            
+            msgHdr = *((MESSAGEHEADER*)&msgBuffer[messageLocation[savedMessageCounter-1]]);
+            
+            //these times are used to tag the IMU sample time. PPSTimeOffset increments by 1 exactly at the 1PPS event (in the 1PPS ISR)
+            //GPSTimemsecs increments by 1 for each new GPS measurement -- note that the below computations are actually
+            //done at the receipt of each GPS message. This is OK because each message has the same time in its header.
+            //Thus GPSTimemsecs increments by 1 here while GPSTimemsecs effectively decrements by 1.
+            //This handles IMU time tagging between the 1PPS event and the first receipt of a new GPS time.
+
+            GPSTimemsecs = msgHdr.GPSTime_msecs;  //time in GPS message header
+            PPSTimeOffset = 0;                    //incremented by 1 in the PPS ISR
+            
+            unsigned long msgCRC = *((unsigned long*)&msgBuffer[messageLocation[savedMessageCounter-1] + 28 + msgHdr.messageLength]);
+            
+            //toPC.printf("tmeFrom1PPS= %5d  ID= %3d Ln = %3d computedCRC= %08x msgCRC= %08x msgCntr = %3d CRCerr=%4d\n", 
+            //    timeFromPPS.read_us(), msgHdr.messageID, msgHdr.messageLength, computedCRC, msgCRC, savedMessageCounter, TotalBadCRCmatches);
+                
+            if ( msgCRC != computedCRC)
+            {
+                toPC.printf(" bad CRC match for messageID %3d total CRC errors = %4d \n",  
+                    msgHdr.messageLength, TotalBadCRCmatches++);
+            }
+                     
+            if      (msgHdr.messageID == 42)
+            {
+                curPos = *((OEM615BESTPOS*)&msgBuffer[messageLocation[savedMessageCounter-1]]);
+                posMsg = curPos;
+                
+                //if (streamPos)
+                {
+                        toPC.printf("BESTPOS %5d %1d %8.5lf %9.5lf %5.3lf %d %d %d\n",
+                                          curPos.msgHeader.GPSTime_msecs,  curPos.solStatus,
+                                          curPos.latitude, curPos.longitude, curPos.height,
+                                          curPos.numSV, curPos.numSolSV, curPos.numGGL1);
+                }
+                
+            } 
+            else if (msgHdr.messageID == 99)  
+            {
+                curVel = *((OEM615BESTVEL*)&msgBuffer[ messageLocation[savedMessageCounter-1] ]);
+                toPC.printf("BESTVEL  vel: horizontalSpeed= %5.3f heading=%5.1f verticalSpeed=%4.2f \n", 
+                    curVel.horizontalSpeed,  curVel.heading,  curVel.verticalSpeed );
+                velMsg = curVel;
+            }
+            /*
+            if (recordData && (fpNav != NULL) && (byteCounter > 0))
+            {
+                //wait_us(10);
+                int totalMessageLength = 28 + msgHdr.messageLength + 4;  //header length + message Length + CRC word size
+                totalBytesWritten += fwrite(&msgBuffer[messageLocation[savedMessageCounter-1]], 1, totalMessageLength, fpNav);  // this writes out a complete set of messages for this sec
+                //wait_us(10);
+            }
+                
+            
+            */
+            completeMessageAvailable = false;
+        }
+        
+        if (!IMUDataReady && !GPSdataWritten && timeFromPPS.read_us() > 500000 && recordData && (fpNav != NULL))
+        {
+                totalBytesWritten += fwrite(&msgBuffer, 1, byteCounter, fpNav);
+                GPSdataWritten = true;
+        }
+        
+        //the IMU data record is read from the SPI in the ISR and the IMUDataReady is set true
+        //we write the IMU data here
+        if (IMUDataReady)  //IMUDataReady is true if we have a recent IMU data record
+        {
+            //write the IMU data
+            if ( recordData && (fpNav != NULL) )
+            {   
+                delTimeOfWrite = timeFromStart.read_us();
+                
+                if (fillingPingWritingPong) fwrite(&imuPong, 1, IMUrecArraySize*sizeof(IMUREC), fpNav);
+                else                        fwrite(&imuPing, 1, IMUrecArraySize*sizeof(IMUREC), fpNav);
+                    
+                delTimeOfWrite = (unsigned long)((unsigned long)timeFromStart.read_us() - delTimeOfWrite);
+                if (delTimeOfWrite > maxWriteTime) maxWriteTime = delTimeOfWrite;                
+            }
+            IMURecordCounter+=IMUrecArraySize;
+            IMUDataReady = false;
+        }
+        
+        if (camera1EventDetected)  //we have detected a camera trigger event
+        {
+            toPC.printf("WMsg TRIGGERTIME %5.3lf\n", camera1Time);
+            camera1EventDetected = false;
+        }
+        
+        if (detectedGPS1PPS)  //true if we are exactly at a 1PPS event detection
+        {   
+            toPC.printf("PPS=%4d stat=%1d bytes=%3d GPSMsgs=%2d  #write=%8d cycles=%6d  MR=%5u\n", 
+                            PPSCounter, posMsg.solStatus, savedByteCounter, savedPerSecMessageCounter, 
+                            totalBytesWritten, cyclesPerSec, maxWriteTime );
+                            
+            //if ( (savedIMUClockCounter - IMURecordCounter) > 2) toPC.printf(" IMU diffs = %2d \n", savedIMUClockCounter - IMURecordCounter);
+            //completeMessageAvailable = false;   
+            cyclesPerSec = 0;
+            maxDelIMUmsecs = 0;
+            maxWriteTime = 0;
+            totalBytesWritten = 0;
+            GPSdataWritten = false;
+            
+            IMURecordCounter = 0;
+            detectedGPS1PPS = false;
+        }
+    }
+      
+    fclose(fpNav);
+    toPC.printf(" normal termination \n");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Mon Apr 22 21:26:04 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/7e6c9f46b3bd
\ No newline at end of file