Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.
Merged
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -48,3 +48,8 @@ $RECYCLE.BIN/
Network Trash Folder
Temporary Items
.apdisk

# VIM backup files
*~
[._]*.un~
*.swp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
To take advantage of the internal IMU of either the Dead Reckoning GPS
boards (ZED-F9R, NEO-M8U), you must first calibrate them. This includes securing the GPS module
to your vehicle so that it is stable within 2 degrees and that the frame of
reference of the board is consistent with the picture outlined in the
Receiver-Description-Prot-Spec Datasheet under Automotive/Untethered Dead
Reckoning. You may also check either the ZED-F9R or NEO-M8U Hookup Guide for
more information. After the board is secure, you'll need to put the module
through certain conditions for proper calibration: acceleration, turning,
stopping for a few minutes, getting to a speed over 30km/h all under a clear sky
with good GNSS signal. This example simply looks at the
"fusionMode" status which indicates whether the SparkFun Dead Reckoning is
not-calibrated - 0, or calibrated - 1.
*/

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_Ublox_Arduino_Library.h> //http://librarymanager/All#SparkFun_Ublox_GPS
SFE_UBLOX_GPS myGPS;

void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun Ublox Example");

Wire.begin();

if (myGPS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}

myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
}

void loop()
{

if (myGPS.getEsfInfo()){
Serial.print("Fusion Mode: ");
Serial.println(myGPS.imuMeas.fusionMode);
if (myGPS.imuMeas.fusionMode == 1)
Serial.println("Sensor is calibrated!");
}

delay(250);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
After calibrating the module, also known as "Fusion Mode", you can get
data directly from the IMU. This data is integrated directly into the GNSS
output, but is provided by the module as well.
*/

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_Ublox_Arduino_Library.h> //http://librarymanager/All#SparkFun_Ublox_GPS
SFE_UBLOX_GPS myGPS;

void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun Ublox Example");

Wire.begin();

if (myGPS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}

myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)

if (myGPS.getEsfInfo()){

Serial.print("Fusion Mode: ");
Serial.println(myGPS.imuMeas.fusionMode);

if (myGPS.imuMeas.fusionMode == 1){
Serial.println("Fusion Mode is Initialized!");
}
else {
Serial.println("Fusion Mode is either disabled or not initialized - Freezing!");
Serial.println("Please see Example 1 description at top for more information.");
}
}
}

void loop()
{

if (myGPS.getEsfIns())
{
Serial.print("X: ");
Serial.println(myGPS.imuMeas.xAngRate);
Serial.print("Y: ");
Serial.println(myGPS.imuMeas.yAngRate);
Serial.print("Z: ");
Serial.println(myGPS.imuMeas.zAngRate);
Serial.print("X Acceleration: ");
Serial.println(myGPS.imuMeas.xAccel);
Serial.print("Y Acceleration: ");
Serial.println(myGPS.imuMeas.yAccel);
Serial.print("Z Acceleration: ");
Serial.println(myGPS.imuMeas.zAccel);
// These values also have "validity checks" that can be provided by the
// ublox library, add "Vald" to values: e.g. xAngRateVald or xAccelVald.
}

delay(250);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
After calibrating the module, also known as "Fusion Mode", you can get
data directly from the IMU. This example code walks you through trouble
shooting or identifying the different states of any individual
"external" (which include internal) sensors you've hooked up (vehicle speed
sensor) or the internal IMU used by the modules. You can see if the sensor is
being used, if it's calibrated, ready, what data type it returns, the state
of the measurement etc.
*/

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_Ublox_Arduino_Library.h> //http://librarymanager/All#SparkFun_Ublox_GPS
SFE_UBLOX_GPS myGPS;

void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun Ublox Example");

Wire.begin();

if (myGPS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}

myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)

// GetEsfInfo also gets the number of sensors used by the ublox module, this
// includes (in the case of the ZED-F9R) wheel tick input from the vehicle
// speed sensor attached to the module.
if (myGPS.getEsfInfo()){

Serial.print("Fusion Mode: ");
Serial.println(myGPS.imuMeas.fusionMode);

if (myGPS.imuMeas.fusionMode == 1){
Serial.println("Fusion Mode is Initialized!");
}
else {
Serial.println("Fusion Mode is either disabled or not initialized - Freezing!");
Serial.println("Please see Example 1 description at top for more information.");
}
}
}

void loop()
{

for(int i=1; i<=myGPS.ubloxSen.numSens; i++){
myGPS.getSensState(i); // Give the sensor you want to check on.
Serial.print("Sensor Data Type: "); //See ublox receiver description
//or our hookup guide for information on the
//return value.
Serial.println(myGPS.ubloxSen.senType);
Serial.print("Being Used: ");
Serial.println(myGPS.ubloxSen.isUsed);
Serial.print("Is Ready: ");
Serial.println(myGPS.ubloxSen.isReady);
Serial.print("Calibration Status: ");
Serial.println(myGPS.ubloxSen.calibStatus);
Serial.print("Time Status: ");
Serial.println(myGPS.ubloxSen.timeStatus);
Serial.print("Bad Measure: ");
Serial.println(myGPS.ubloxSen.timeStatus);
Serial.print("Bad Time Tag: ");
Serial.println(myGPS.ubloxSen.badTag);
Serial.print("Missed Measure : ");
Serial.println(myGPS.ubloxSen.missMeas);
Serial.print("Noisy Measure: ");
Serial.println(myGPS.ubloxSen.noisyMeas);
}

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
By: Elias Santistevan
SparkFun Electronics
Date: May, 2020
License: MIT. See license file for more information but you can
basically do whatever you want with this code.
Feel like supporting open source hardware?
Buy a board from SparkFun!
NEO-M8U: https://www.sparkfun.com/products/16329
ZED-F9R: https://www.sparkfun.com/products/16344
Hardware Connections:
Plug a Qwiic cable into the GPS and a Redboard Qwiic
If you don't have a platform with a Qwiic connection use the
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
After calibrating the module and securing it to your vehicle such that it's
stable within 2 degrees, and the board is oriented correctly with regards to
the vehicle's frame, you can now read the vehicle's "attitude". The attitude
includes the vehicle's heading, pitch, and roll. You can also check the
accuracy of those readings.
*/

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_Ublox_Arduino_Library.h> //http://librarymanager/All#SparkFun_Ublox_GPS
SFE_UBLOX_GPS myGPS;

void setup()
{
Serial.begin(115200);
while (!Serial); //Wait for user to open terminal
Serial.println("SparkFun Ublox Example");

Wire.begin();

if (myGPS.begin() == false) //Connect to the Ublox module using Wire port
{
Serial.println(F("Ublox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
}

myGPS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)

if (myGPS.getEsfInfo()){

Serial.print("Fusion Mode: ");
Serial.println(myGPS.imuMeas.fusionMode);

if (myGPS.imuMeas.fusionMode == 1){
Serial.println("Fusion Mode is Initialized!");
}
else {
Serial.println("Fusion Mode is either disabled or not initialized - Freezing!");
Serial.println("Please see Example 1 description at top for more information.");
}
}
}

void loop()
{
myGPS.getVehAtt(); // Give the sensor you want to check on.
Serial.print("Roll: ");
Serial.println(myGPS.vehAtt.roll);
Serial.print("Pitch: ");
Serial.println(myGPS.vehAtt.pitch);
Serial.print("Heading: ");
Serial.println(myGPS.vehAtt.heading);
Serial.print("Roll Accuracy: ");
Serial.println(myGPS.vehAtt.accRoll);
Serial.print("Pitch Accuracy: ");
Serial.println(myGPS.vehAtt.accPitch);
Serial.print("Heading Accuracy: ");
Serial.println(myGPS.vehAtt.accHeading);
}


182 changes: 182 additions & 0 deletions src/SparkFun_Ublox_Arduino_Library.cpp
Original file line number Diff line number Diff line change
@@ -3167,3 +3167,185 @@ boolean SFE_UBLOX_GPS::getRELPOSNED(uint16_t maxWait)

return (true);
}
boolean SFE_UBLOX_GPS::getEsfInfo(uint16_t maxWait)
{
// Requesting Data from the receiver
packetCfg.cls = UBX_CLASS_ESF;
packetCfg.id = UBX_ESF_STATUS;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (false); //If command send fails then bail

checkUblox();

// payload should be loaded.
imuMeas.version = extractByte(4);
imuMeas.fusionMode = extractByte(12);
ubloxSen.numSens = extractByte(15);

// Individual Status Sensor in different function
return(true);
}

//
boolean SFE_UBLOX_GPS::getEsfIns(uint16_t maxWait)
{
packetCfg.cls = UBX_CLASS_ESF;
packetCfg.id = UBX_ESF_INS;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (false); //If command send fails then bail

checkUblox();

// Validity of each sensor value below
uint32_t validity = extractLong(0);

imuMeas.xAngRateVald = (validity && 0x0080) >> 8;
imuMeas.yAngRateVald = (validity && 0x0100) >> 9;
imuMeas.zAngRateVald = (validity && 0x0200) >> 10;
imuMeas.xAccelVald = (validity && 0x0400) >> 11;
imuMeas.yAccelVald = (validity && 0x0800) >> 12;
imuMeas.zAccelVald = (validity && 0x1000) >> 13;

imuMeas.xAngRate = extractLong(12); // deg/s
imuMeas.yAngRate = extractLong(16); // deg/s
imuMeas.zAngRate = extractLong(20); // deg/s

imuMeas.xAccel = extractLong(24); // m/s
imuMeas.yAccel = extractLong(28); // m/s
imuMeas.zAccel = extractLong(32); // m/s
}

//
boolean SFE_UBLOX_GPS::getEsfDataInfo(uint16_t maxWait)
{

packetCfg.cls = UBX_CLASS_ESF;
packetCfg.id = UBX_ESF_MEAS;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (false); //If command send fails then bail

checkUblox();

uint32_t timeStamp = extractLong(0);
uint32_t flags = extractInt(4);

uint8_t timeSent = (flags && 0x01) >> 1;
uint8_t timeEdge = (flags && 0x02) >> 2;
uint8_t tagValid = (flags && 0x04) >> 3;
uint8_t numMeas = (flags && 0x1000) >> 15;

if (numMeas > DEF_NUM_SENS)
numMeas = DEF_NUM_SENS;

uint8_t byteOffset = 4;

for(uint8_t i=0; i<numMeas; i++){

uint32_t bitField = extractLong(4 + byteOffset * i);
imuMeas.dataType[i] = (bitField && 0xFF000000) >> 23;
imuMeas.data[i] = (bitField && 0xFFFFFF);
imuMeas.dataTStamp[i] = extractLong(8 + byteOffset * i);

}

}

boolean SFE_UBLOX_GPS::getEsfRawDataInfo(uint16_t maxWait)
{

// Need to know the number of sensor to get the correct data
// Rate selected in UBX-CFG-MSG is not respected
packetCfg.cls = UBX_CLASS_ESF;
packetCfg.id = UBX_ESF_RAW;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (false); //If command send fails then bail

checkUblox();

uint32_t bitField = extractLong(4);
imuMeas.rawDataType = (bitField && 0xFF000000) >> 23;
imuMeas.rawData = (bitField && 0xFFFFFF);
imuMeas.rawTStamp = extractLong(8);

}

sfe_ublox_status_e SFE_UBLOX_GPS::getSensState(uint8_t sensor, uint16_t maxWait)
{

packetCfg.cls = UBX_CLASS_ESF;
packetCfg.id = UBX_ESF_STATUS;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (SFE_UBLOX_STATUS_FAIL); //If command send fails then bail

ubloxSen.numSens = extractByte(15);

if (sensor > ubloxSen.numSens)
return SFE_UBLOX_STATUS_OUT_OF_RANGE;

checkUblox();

uint8_t offset = 4;

// Only the last sensor value checked will remain.
for(uint8_t i=0; i<sensor; i++){

uint8_t sensorFieldOne = extractByte(16 + offset * i);
uint8_t sensorFieldTwo = extractByte(17 + offset * i);
ubloxSen.freq = extractByte(18 + offset * i);
uint8_t sensorFieldThr = extractByte(19 + offset * i);

ubloxSen.senType = (sensorFieldOne && 0x10) >> 5;
ubloxSen.isUsed = (sensorFieldOne && 0x20) >> 6;
ubloxSen.isReady = (sensorFieldOne && 0x30) >> 7;

ubloxSen.calibStatus = sensorFieldTwo && 0x03;
ubloxSen.timeStatus = (sensorFieldTwo && 0xC) >> 2;

ubloxSen.badMeas = (sensorFieldThr && 0x01);
ubloxSen.badTag = (sensorFieldThr && 0x02) >> 1;
ubloxSen.missMeas = (sensorFieldThr && 0x04) >> 2;
ubloxSen.noisyMeas = (sensorFieldThr && 0x08) >> 3;
}

return SFE_UBLOX_STATUS_SUCCESS;

}

boolean SFE_UBLOX_GPS::getVehAtt(uint16_t maxWait){

packetCfg.cls = UBX_CLASS_NAV;
packetCfg.id = UBX_NAV_ATT;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED)
return (SFE_UBLOX_STATUS_FAIL); //If command send fails then bail

checkUblox();

vehAtt.roll = extractLong(8);
vehAtt.pitch = extractLong(12);
vehAtt.heading = extractLong(16);

vehAtt.accRoll = extractLong(20);
vehAtt.accPitch = extractLong(24);
vehAtt.accHeading = extractLong(28);

return true;

}
77 changes: 77 additions & 0 deletions src/SparkFun_Ublox_Arduino_Library.h
Original file line number Diff line number Diff line change
@@ -274,6 +274,7 @@ const uint8_t UBX_MON_TXBUF = 0x08; //Transmitter Buffer Status. Used for query
const uint8_t UBX_MON_VER = 0x04; //Receiver/Software Version. Used for obtaining Protocol Version.

//The following are used to configure the NAV UBX messages (navigation results messages). Descriptions from UBX messages overview (ZED_F9P Interface Description Document page 35-36)
const uint8_t UBX_NAV_ATT = 0x05; //Vehicle "Attitude" Solution
const uint8_t UBX_NAV_CLOCK = 0x22; //Clock Solution
const uint8_t UBX_NAV_DOP = 0x04; //Dilution of precision
const uint8_t UBX_NAV_EOE = 0x61; //End of Epoch
@@ -334,6 +335,14 @@ const uint8_t UBX_ACK_NACK = 0x00;
const uint8_t UBX_ACK_ACK = 0x01;
const uint8_t UBX_ACK_NONE = 0x02; //Not a real value

// The following constants are used to get External Sensor Measurements and Status
// Information.
const uint8_t UBX_ESF_MEAS = 0x02;
const uint8_t UBX_ESF_RAW = 0x03;
const uint8_t UBX_ESF_STATUS = 0x10;
const uint8_t UBX_ESF_INS = 0x15; //36 bytes


const uint8_t SVIN_MODE_DISABLE = 0x00;
const uint8_t SVIN_MODE_ENABLE = 0x01;

@@ -623,6 +632,13 @@ class SFE_UBLOX_GPS
boolean setDynamicModel(dynModel newDynamicModel = DYN_MODEL_PORTABLE, uint16_t maxWait = 1100);
uint8_t getDynamicModel(uint16_t maxWait = 1100); // Get the dynamic model - returns 255 if the sendCommand fails

boolean getEsfInfo(uint16_t maxWait = 1100);
boolean getEsfIns(uint16_t maxWait = 1100);
boolean getEsfDataInfo(uint16_t maxWait = 1100);
boolean getEsfRawDataInfo(uint16_t maxWait = 1100);
sfe_ublox_status_e getSensState(uint8_t sensor, uint16_t maxWait = 1100);
boolean getVehAtt(uint16_t maxWait = 1100);

//Survey-in specific controls
struct svinStructure
{
@@ -700,6 +716,67 @@ class SFE_UBLOX_GPS

uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame

#define DEF_NUM_SENS 7
struct deadReckData
{
uint8_t version;
uint8_t fusionMode;

uint8_t xAngRateVald;
uint8_t yAngRateVald;
uint8_t zAngRateVald;
uint8_t xAccelVald;
uint8_t yAccelVald;
uint8_t zAccelVald;

int32_t xAngRate;
int32_t yAngRate;
int32_t zAngRate;

int32_t xAccel;
int32_t yAccel;
int32_t zAccel;

// The array size is based on testing directly on M8U and F9R
uint32_t rawData;
uint32_t rawDataType;
uint32_t rawTStamp;

uint32_t data[DEF_NUM_SENS];
uint32_t dataType[DEF_NUM_SENS];
uint32_t dataTStamp[DEF_NUM_SENS];
} imuMeas;

struct indivImuData
{

uint8_t numSens;

uint8_t senType;
boolean isUsed;
boolean isReady;
uint8_t calibStatus;
uint8_t timeStatus;

uint8_t freq; // Hz

boolean badMeas;
boolean badTag;
boolean missMeas;
boolean noisyMeas;
} ubloxSen;

struct vehicleAttitude
{
// All values in degrees
int32_t roll;
int32_t pitch;
int32_t heading;
uint32_t accRoll;
uint32_t accPitch;
uint32_t accHeading;
} vehAtt;

private:
//Depending on the sentence type the processor will load characters into different arrays
enum SentenceTypes