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
Revision 0:432b860b6ff7, committed 2013-04-22
- 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
--- /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