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



File content as of revision 30:96d133f3008e:

#include "mbed.h" 
#include <string>

//set up the message buffer to be filled by the GPS read process
#define CRC32_POLYNOMIAL 0xEDB88320

#include "MODSERIAL.h"
#include "SDFileSystem.h"      //imported using the import utility    

//general digital I/O specifications for this application

//if we dont write to the card we dont need this ... 
//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 rxMsg(LED3);
DigitalOut txMsg(LED4);
//DigitalOut recordDataled(LED4); //set the led when the record is on

//hardware trigger mechanization for bulb shutter commands
DigitalInOut fire(p29);     //connected to the tip of 2.5mm connector (T2i)
DigitalInOut pre_fire(p30); //connected to the mid-connection for 2.5mm connector (T2i)

//USB serial data stream back to the PC
Serial toPC(USBTX, USBRX);      //connect the GPS TX, RX to p9 and p10

Timer timeFromStart;
Timer timeInMessageRead;

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 
volatile int PPSTimeOffset = 0;
int totalGPSBytes = 0;

bool writeIMUDataToPC = false;      //used in main loop to cause IMU data write to the PC
bool writeGPSDataToPC = false;      //used in main loop to cause GPS data write to the PC

#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 GPS receiver time information becoming available AFTER the 1PPS event
    PPSTimeOffset++;   //counts 1PPS events -- reset to zero in main after we determine a GPS time
    //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

//set up the USB port and the GPS COM port
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 
    //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 some time to launch the GPS receiver
    for (int i=0; i<5; i++) { toPC.printf("WMessage start countdown: %3d \n", 4-i); wait(1); }

    //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 receiver 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"; 
    //toPC.printf("WMsg set RESET command \n");
    //sendASCII(ch0, sizeof(ch0)); wait_ms(3000);    
    //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
    //Binary commands are shown below
    //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

    //ASCII commands are shown below
    char ch3[] = "log COM1 BESTPOSA ONTIME 1";   //messageID = 42 
    char ch4[] = "log COM1 BESTVelA ONTIME 1";   //messageID = 99
    char ch5[] = "log COM1 RANGEA ONTIME 1";     //messageID = 43
    //char ch6[] = "log COM1 TIMEB ONTIME 1";      //messageID = 101
    //toPC.printf("WMsg set serial config \n");
    sendASCII(ch7, sizeof(ch7)); wait_ms(500);
    //sendASCII(ch0, sizeof(ch0));  
    //toPC.printf("WMsg unlog all messages \n");
    sendASCII(ch1, sizeof(ch1)); wait_ms(500);
    //toPC.printf("WMsg log BESTPOSB on COM1 \n");
    sendASCII(ch3, sizeof(ch3)); wait_ms(500);
    //toPC.printf("WMsg log BESTVELB on COM1\n");
    sendASCII(ch4, sizeof(ch4)); wait_ms(500);
    //toPC.printf("WMsg 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("WMsg 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 CRC32Value_2(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;
            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_2(
        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_2( CRC, *ucBuffer++ );
    return  CRC;

bool checkMessageCRC(char* message, int byteCounter)
    //calculate the Novatel OEM615 CRC
    //length of CRC bytes is string length minus:  8 bytes for long word, 2 bytes for "#" & "*" and 2 bytes for CR, LF 
    // start at byte 1 (not zero) to skip the "#" at the start                            
    unsigned long computedCRC = CalculateBlockCRC32_2((unsigned long)(byteCounter-12), (unsigned char*)(&message[1]));
    //get the computed CRC into a zero-padded string so we can compare to the string CRC in the message
    char CRCCharBuffer [50];
    int n=sprintf (CRCCharBuffer, "%08x", computedCRC);  //note the zero-padding
    //toPC.printf("n = %d  charCRC = %s \n", n, CRCCharBuffer);
    //get a string reresentation of just the CRC from the message
    char CRCString[10];
    strncpy(CRCString, &message[byteCounter-10], 8);
    //toPC.printf(" %s  %s  \n", CRCCharBuffer, CRCString);
    if (strncmp(CRCCharBuffer, CRCString, 8) != 0)
            toPC.printf("mbed bad CRC %d  %s  %d  %s  \n", strlen(CRCCharBuffer), CRCCharBuffer, strlen(CRCString), CRCString );
            return false;
    return true;

//  mbed main to support the Waldo_FCS
int main() 
    fire.output();      //set the fire pin as output
    pre_fire.output();  //set the pre-fire pin as output
    //set up for the first trigger
    fire = 1;
    pre_fire = 1;
    //set up the GPS and mbed COM ports
    //prints to PC above here wont work cause the PC serial link baud rate has not been set
    toPC.printf("initiating the mbed app\n");

    toPC.printf("setting up the ADIS\n");
    //set up the ADIS16488 IMU

    //initiate 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
    timeInMessageRead.start();  //used to prevent hanging in the PC message read loop
    unsigned long cyclesPerSec = 0;  //main while() loop cycles per GPS sec

    bool finishTrigger = false;
    Timer triggerInterval;
    bool newMission = true;
    int workingTime = 0;
    bool triggeringInProcess = false;
    int badRangeMessages = 0;
    int badBESPOSMessages = 0;
    int badBESVELMessages = 0;
    int VERSION = 1;
    double timeAtMessage0 = 0;
    // top of the mission while loop
        readFromPC();   //#1
        //process GPS data messages (messages 0, 1, 2) 
        if (toPC.writeable() && message0Complete && !IMUDataReady )
            int thisWorkTime = timeFromStart.read_us();
            //get the GPS time at the prior 1PPS from the first header
            char msgName [15], port [15], clockStatus [15];
            int a1=0, GPSWeek=0;
            double b1 = 0.0, GPSTimeFromHeader = 0.0;  
            //note the use of the "C" scanset [^,] to read the comma-delimited ASCII message provided by te Novatel OEM615 receiver
            //these are used only for the character variables in order to avoid detecting the "," as another character
            //only spaces can be used as delimiters if scanset methodology is not used         
            int N = sscanf(msgBuffer0,"%15[^,],%15[^,],%d,%f,%15[^,],%d,%lf", msgName, port, &a1, &b1, clockStatus, &GPSWeek, &GPSTimeFromHeader);
            if (N != 7)  toPC.printf("scanf failed for message: %s ", msgBuffer0);

            GPSTimemsecs = GPSTimeFromHeader * 1000;
            //this is used to account for the GPS time from the receiver becoming available a few msecs after the actual 1PPS event
            //A precision timer is set at the 1PPS event that is used to tag the IMU data
            timeAtMessage0 = timeFromPPS.read_us()/1000.0;
            PPSTimeOffset = 0.0;
            if (!checkMessageCRC(msgBuffer0, GPSbyteCounter0)) badRangeMessages++;
            //write the GPS message bytes to the PC
            for (int i=0; i<GPSbyteCounter0; i++)   N += toPC.printf("%c", msgBuffer0[i]);
            if (N != GPSbyteCounter0) toPC.printf(" error writing msgBuffer0: %d of %d bytes written \n", N, GPSbyteCounter0);
            message0Complete = false; //set up for the next message
            workingTime += timeFromStart.read_us() - thisWorkTime;
            readFromPC();   //#2
        else if (toPC.writeable() && message1Complete && !IMUDataReady)
            if (!checkMessageCRC(msgBuffer1, GPSbyteCounter1)) badBESPOSMessages++;
            int thisWorkTime = timeFromStart.read_us();
            int N=0;
            for (int i=0; i<GPSbyteCounter1; i++)   N += toPC.printf("%c", msgBuffer1[i]);
            if (N != GPSbyteCounter1) toPC.printf(" error writing msgBuffer1: %d of %d bytes written \n", N, GPSbyteCounter1);
            message1Complete = false;
            workingTime += timeFromStart.read_us() - thisWorkTime;
            readFromPC();   //#3
        else if (toPC.writeable() && message2Complete && !IMUDataReady)
            if( !checkMessageCRC(msgBuffer2, GPSbyteCounter2)) badBESVELMessages++;
            int thisWorkTime = timeFromStart.read_us();
            int N = 0;
            for (int i=0; i<GPSbyteCounter2; i++)   N += toPC.printf("%c", msgBuffer2[i]);
            if (N != GPSbyteCounter2) toPC.printf(" error writing msgBuffer2: %d of %d bytes written \n", N, GPSbyteCounter2);
            message2Complete = false;
            workingTime += timeFromStart.read_us() - thisWorkTime;
            readFromPC();   //#4
        if (toPC.writeable() && IMUDataReady)
            int thisWorkTime = timeFromStart.read_us();
            //write the IMU data to the PC
            for (int i=0; i<IMUrecArraySize; i++)
                if (fillingPingWritingPong)
                    toPC.printf("IMU %d13,%d13,%d13,%d13,%d13,%d13,%d13 " , //70  bytes
                    imuPong[i].dataWord[5]  );
                    toPC.printf("IMU %d13,%d13,%d13,%d13,%d13,%d13,%d13 " , //70  bytes
                    imuPing[i].dataWord[5]  );                
                readFromPC();   //#5
            toPC.printf("\n");  //total of 10 * 70 + 9 = 709 bytes
            IMUDataReady = false;
            workingTime += timeFromStart.read_us() - thisWorkTime;
        //read the USB serial data from the PC to check for commands
        //only message we expect will be the trigger fire command

        if(toPC.writeable() && fireTrigger)  //comes from a PC request message
            triggeringInProcess = true;
            unsigned long triggerTime = GPSTimemsecs + PPSTimeOffset*1000.0 + timeFromPPS.read_us()/1000.0;
            toPC.printf("mbedmessage trig1 %d \n", triggerTime);
            //pre-fire the trigger using the mid-body 2.5mm connection (T2i)
            pre_fire = 0;  //pin30 (midbody of connector) set to zero
            fire = 0; //fire the trigger using the tip connection
            fireTrigger = false;  //finished the setup -- but wait to do the actual fire 
            finishTrigger = true; //set to false after firing the trigger
        //the trigger requires a pulse -- the above portion lowers the signal and the below raises it
        //this has been tested at 50 msecs and it will not fire at that pulse duration
        if(toPC.writeable() && finishTrigger && triggerInterval.read_ms() > 100)
            unsigned long triggerTime = GPSTimemsecs + PPSTimeOffset*1000.0 + timeFromPPS.read_us()/1000.0;
            toPC.printf("mbedmessage trig2 %d \n", triggerTime);
            //pre-fire the trigger using 2the mid-body 2.5mm connec
            fire = 1;
            pre_fire = 1;
            finishTrigger = false;  //completes the trigger firing pulse definition
            triggeringInProcess = false;
        if (detectedGPS1PPS)  //true if we are exactly at a 1PPS event detection
            if (toPC.writeable() )
                toPC.printf("STATUS %03d %04d %06d %02d %06d %5.3f %03d %03d %03d\n", 
                VERSION, PPSCounter, totalGPSBytes, IMURecordCounter, cyclesPerSec, timeAtMessage0,  /*workingTime/1000000.0,*/ 
                badRangeMessages, badBESVELMessages, badBESVELMessages);
            cyclesPerSec = 0;
            workingTime= 0.0;
            messagePerSecCounter = 0;   //GPS message per second counter

            IMURecordCounter = 0;
            detectedGPS1PPS = false;
            rxMsg = !rxMsg;  //flash the lights to make sure the mbed loop is operative
            txMsg = !txMsg;
            readFromPC();   //#6
    }  //end of the major while() loop