Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GPS Drivers #4

Open
wants to merge 52 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
f1f7d0c
add WIP gps drivers
EricPedley Feb 13, 2024
946f8d2
rename folder to be more specific
EricPedley Feb 13, 2024
8d2b2d0
switch to pvt
EricPedley Feb 13, 2024
6150e71
remove unnecessary files
EricPedley Feb 13, 2024
81692b4
fix errors with incomplete removal of old code
EricPedley Feb 13, 2024
9a58e82
move enums into class and add more specific poll results
EricPedley Feb 14, 2024
228d388
add verbose comments and more specific enum types
EricPedley Feb 14, 2024
5832e52
add line to not mess up enum formatting
EricPedley Feb 14, 2024
6a962f8
ran clang-format
EricPedley Feb 14, 2024
8ec2a07
refactor conditional logic in ubxPacket
EricPedley Feb 18, 2024
c10d3fb
rename files to .cc
EricPedley Feb 18, 2024
d32e5c0
change functions to PascalCase
EricPedley Feb 18, 2024
050647a
put enum definition before reference
EricPedley Feb 18, 2024
7e7e005
replace i2c header include
EricPedley Feb 18, 2024
df9ed0e
rename gps class to include full info and pull reset pin in constructor
EricPedley Feb 18, 2024
5a561fe
move ubxHelpers to private methods of gps class
EricPedley Feb 18, 2024
cb0b67f
add formatting to comments
EricPedley Feb 18, 2024
130b5b9
refactor i2c timeout to a macro
EricPedley Feb 18, 2024
e178026
refactor: move private helper to end of file
EricPedley Feb 21, 2024
5739f12
move pin reset to Init method
EricPedley Feb 21, 2024
6388ca8
fix comments
EricPedley Feb 21, 2024
106088d
add newline
EricPedley Feb 21, 2024
3a5fe53
decrease column limit
EricPedley Feb 21, 2024
1bd56a7
make private variables prefixed with underscore
EricPedley Feb 21, 2024
d0c32bd
make reset pin and port private members
EricPedley Feb 21, 2024
a747683
replace hard-coded i2c header with macros
EricPedley Feb 21, 2024
4015ee3
succumb to function squashing
EricPedley Feb 21, 2024
38d1f2a
add ecef payload back to messages
EricPedley Feb 22, 2024
04d50ac
un-hardcode what message the GPS is sending.
EricPedley Feb 22, 2024
4e2f6f0
fix syntax errors from not building earlier
EricPedley Feb 23, 2024
e91c121
add extra parens
EricPedley Feb 23, 2024
df910cc
add ecef conversion helper
EricPedley Feb 23, 2024
03be05a
un-const messages
EricPedley Feb 23, 2024
143f797
start working on coordinate conversion
EricPedley Feb 23, 2024
080f3cb
add datesheet links to message structs
EricPedley Feb 23, 2024
0df9418
convert coords to ECEF
EricPedley Feb 25, 2024
459a1cc
fix whitespace problem
EricPedley Feb 25, 2024
35dbd3f
fix math includes
EricPedley Mar 7, 2024
27f57f0
add include guard on coorhelpers header
EricPedley Mar 25, 2024
9e486a0
pascal case function names
EricPedley Mar 25, 2024
5f5f7f3
fix macros
EricPedley Mar 25, 2024
6b27138
rename and dont ifndef packet reader size
EricPedley Mar 25, 2024
e049ea2
shorten datasheet link
EricPedley Mar 25, 2024
4a8d227
add example of ecef conversion function
EricPedley Mar 25, 2024
345ab6e
use only 1 accuracy and fix duplicate structs
EricPedley Mar 25, 2024
f44baaf
fix header order
EricPedley Mar 25, 2024
a3d3b0d
update example
EricPedley Apr 12, 2024
b9ef6ea
check ubx send status and actually return payload
EricPedley Apr 12, 2024
12e6126
swap packet reader conditionals (fixed dumbass bug)
EricPedley Apr 12, 2024
398572b
rename files and add missing namespace to payload conversion
EricPedley Apr 12, 2024
25b6347
make payload conversion pass-by-reference
EricPedley Apr 12, 2024
093a5a5
make conversion function private and just return simpler struct
EricPedley Apr 12, 2024
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
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ BasedOnStyle: Google
AccessModifierOffset: -2
ColumnLimit: 200
IndentWidth: 4
AllowShortEnumsOnASingleLine: false
DanielHeEGG marked this conversation as resolved.
Show resolved Hide resolved
117 changes: 117 additions & 0 deletions gps_ubxm8_i2c/gps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include "gps.h"
EricPedley marked this conversation as resolved.
Show resolved Hide resolved

#include "ubxHelpers.h"
#include "ubxMessages.h"
#include "ubxPacket.h"

#define I2C_BUFFER_SIZE 1024

GPS::GPS() {
packetReader = UBXPacketReader();
state = GPS::State::REQUEST_NOT_SENT;
}

const GPS::State GPS::getState() { return state; }
EricPedley marked this conversation as resolved.
Show resolved Hide resolved

/**
* Sends a request for position data if none are currently pending.
* Then, checks if there is data available from the GPS. If there is none,
* it just returns a NO_DATA response. If there is data, it reads up to 1024 bytes (configurable at the top of gps.cpp)
* After reading, it will return one of these:
* - RECEIVE_IN_PROGRESS: it found a valid header for the data we're looking for (UBX packet) but hasn't read all of it yet.
* - NO_UBX_DATA: it successfully read data but there was no header for the data we're looking for (UBX packet)
* - POLL_JUST_FINISHED: it got data. Now you can read it with GPS::getSolution.
*
* There are also special return types for different types of error, that are hopefully verbose enough to explain themselves.
*/
const GPS::PollResult GPS::pollUpdate(I2C_HandleTypeDef* i2c) {
if (state == GPS::State::REQUEST_NOT_SENT) {
uint8_t message[4] = {0x01, 0x07, 0x00, 0x00};
sendUBX(message, 4, &hi2c3);
state = GPS::State::POLLING_RESPONSE;
}

if (state == GPS::State::POLLING_RESPONSE) {
DanielHeEGG marked this conversation as resolved.
Show resolved Hide resolved
uint8_t lenBytes[2];
uint16_t dataLen = 0;
uint8_t buffer[I2C_BUFFER_SIZE];

HAL_StatusTypeDef dataLenReadStatus = HAL_I2C_Mem_Read(i2c, 0x42 << 1, 0xFD, 1, lenBytes, 2, 100);
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
if (dataLenReadStatus != HAL_OK) {
return GPS::PollResult::DATA_LEN_POLL_FAILED;
}

dataLen = lenBytes[0] << 8 | lenBytes[1];
if (dataLen == 0) {
return GPS::PollResult::NO_DATA;
}

if (dataLen > I2C_BUFFER_SIZE) {
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
dataLen = I2C_BUFFER_SIZE;
}
HAL_StatusTypeDef rcvStatus = HAL_I2C_Master_Receive(i2c, 0x42 << 1, buffer, dataLen, 100);
if (rcvStatus != HAL_OK) {
return GPS::PollResult::DATA_RECEIVE_I2C_FAILED;
}

if (packetReader.isInProgress()) {
DanielHeEGG marked this conversation as resolved.
Show resolved Hide resolved
for (uint16_t i = 0; i < dataLen; i++) {
UBXPacketUpdateResult res = packetReader.update(buffer[i]);
DanielHeEGG marked this conversation as resolved.
Show resolved Hide resolved
if (res == UBXPacketUpdateResult::CHECKSUM_FAILED) {
packetReader.reset();
return GPS::PollResult::DATA_RECEIVE_CHECKSUM_FAILED;
}
if (packetReader.isComplete()) {
state = GPS::State::RESPONSE_READY;
return GPS::PollResult::POLL_JUST_FINISHED;
}
}
return GPS::PollResult::RECEIVE_IN_PROGRESS;
} else {
for (uint16_t i = 0; i < dataLen; i++) {
if (buffer[i] == 0xB5 && buffer[i + 1] == 0x62) {
i += 2; // skip the header
while (i < dataLen && !packetReader.isComplete()) {
UBXPacketUpdateResult res = packetReader.update(buffer[i]);
if (res != UBXPacketUpdateResult::UPDATE_OK) {
packetReader.reset();
return GPS::PollResult::DATA_RECEIVE_CHECKSUM_FAILED;
}
i++;
}
}
}

if (!packetReader.isInProgress()) {
return GPS::PollResult::NO_UBX_DATA;
}

if (packetReader.isComplete()) {
state = GPS::State::RESPONSE_READY;
return GPS::PollResult::POLL_JUST_FINISHED;
}
return GPS::PollResult::RECEIVE_IN_PROGRESS;
}
}
return GPS::PollResult::POLL_ALREADY_FINISHED;
}

/**
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
* Returns GPS position solution info. This will only give valid data
* if it's called after `pollUpdate` returns a POLL_JUST_FINISHED response,
* which will put the GPS's state in RESPONSE_READY mode. If you call this before that,
* the data in the struct is undefined.
*
* After calling this and using the info returned, getting the next
* packet requires you to call `reset` and `pollUpdate` again.
*/
const UBX_NAV_PVT_PAYLOAD GPS::getSolution() { return *(UBX_NAV_PVT_PAYLOAD*)packetReader.getPayload(); }

/**
* Puts the GPS state back to its initial value so that `pollUpdate` knows
* it needs to send a new data request.
*/
void GPS::reset() {
state = GPS::State::REQUEST_NOT_SENT;
packetReader.reset();
}
57 changes: 57 additions & 0 deletions gps_ubxm8_i2c/gps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

#include <i2c.h>
EricPedley marked this conversation as resolved.
Show resolved Hide resolved

#include "ubxMessages.h"
#include "ubxPacket.h"

/**
* Class for interfacing over i2c with any M8 GPS.
* Before this class is used, the GPS reset pin needs to be pulled high.
*
* This class keeps its own finite state machine for abstracting away polling data availability and retrying requests.
* An example usage is as follows:
* ```
* GPS myGPS;
* while(1) {
* if(myGPS.pollUpdate(&i2c1) == GPS::PollResult::POLL_FINISHED) {
* UBX_NAV_PVT_PAYLOAD pvtData = myGPS.getSolution();
* myGPS.reset();
* }
* }
* ```
*
* After a packet is received, you _need_ to call the reset method.
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
* If you don't, `pollUpdate` will just do nothing and return a `POLL_ALREADY_FINISHED` response.
*
* The datasheet is here: https://content.u-blox.com/sites/default/files/products/documents/u-blox8-M8_ReceiverDescrProtSpec_UBX-13003221.pdf
* The most relevant parts are "UBX Protocol" (section 32) and "DDC Port" (section 15).
*
*/
class GPS {
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
public:
enum class State {
REQUEST_NOT_SENT,
POLLING_RESPONSE,
RESPONSE_READY
};
enum class PollResult {
POLL_JUST_FINISHED,
POLL_ALREADY_FINISHED,
RECEIVE_IN_PROGRESS,
NO_DATA,
NO_UBX_DATA,
DATA_LEN_POLL_FAILED,
DATA_RECEIVE_I2C_FAILED,
DATA_RECEIVE_CHECKSUM_FAILED
};
GPS();
const State getState();
const PollResult pollUpdate(I2C_HandleTypeDef* i2c);
const UBX_NAV_PVT_PAYLOAD getSolution();
void reset();

private:
UBXPacketReader packetReader;
State state;
};
34 changes: 34 additions & 0 deletions gps_ubxm8_i2c/ubxHelpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "ubxHelpers.h"

#include "ubxMessages.h"
#include "ubxPacket.h"

#define I2C_BUFFER_SIZE 1024

static uint8_t magicBytes[2] = {0xB5, 0x62};

bool sendUBX(uint8_t* message, uint16_t len, I2C_HandleTypeDef* i2c) {
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
uint8_t CK_A{0}, CK_B{0};

HAL_StatusTypeDef status;
status = HAL_I2C_Master_Transmit(i2c, 0x42 << 1, magicBytes, 2, 100);
if (status != HAL_OK) {
return false;
}

for (uint16_t i = 0; i < len; i++) {
CK_A = CK_A + message[i];
CK_B = CK_B + CK_A;
}

uint8_t CK[2] = {CK_A, CK_B};
status = HAL_I2C_Master_Transmit(i2c, 0x42 << 1, message, len, 100);
if (status != HAL_OK) {
return false;
}
status = HAL_I2C_Master_Transmit(i2c, 0x42 << 1, CK, 2, 100);
if (status != HAL_OK) {
return false;
}
return true;
}
5 changes: 5 additions & 0 deletions gps_ubxm8_i2c/ubxHelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once
#include "i2c.h"
#include "ubxMessages.h"

bool sendUBX(uint8_t* message, uint16_t len, I2C_HandleTypeDef* i2c);
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
48 changes: 48 additions & 0 deletions gps_ubxm8_i2c/ubxMessages.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>

#pragma pack(push, 1)
struct UBX_NAV_PVT_PAYLOAD {
uint32_t iTOW;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
uint8_t valid;
uint32_t tAcc;
int32_t nano;
GPSFixType fixType;
uint8_t flags;
uint8_t flags2;
uint8_t numSV;
int32_t lon;
int32_t lat;
int32_t height;
int32_t hMSL;
uint32_t hAcc;
uint32_t vAcc;
int32_t velN;
int32_t velE;
int32_t velD;
int32_t gSpeed;
int32_t headMot;
uint32_t sAcc;
uint32_t headAcc;
uint16_t pDOP;
uint8_t reserved1[6];
int32_t headVeh;
int16_t magDec;
uint16_t magAcc;
};
#pragma pack(pop)

enum class GPSFixType : uint8_t {
NO_FIX,
DEAD_RECKONING,
FIX_2D,
FIX_3D,
GNSS_AND_DEAD_RECKONING,
TIME_ONLY_FIX
};
69 changes: 69 additions & 0 deletions gps_ubxm8_i2c/ubxPacket.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "ubxPacket.h"

// make a ubxPacket class that has a method to update one byte at a time
UBXPacketReader::UBXPacketReader() {
payloadLength = 0;
packetIndex = 0;
ckA = 0;
ckB = 0;
complete = false;
inProgress = false;
}

UBXPacketUpdateResult UBXPacketReader::update(uint8_t newByte) {
if (complete) {
return UBXPacketUpdateResult::PACKET_ALREADY_COMPLETE;
}
inProgress = true;
int currentPacketIndex = packetIndex;
packetIndex++;
if (currentPacketIndex == 0) {
messageClass = newByte;
} else if (currentPacketIndex == 1) {
messageId = newByte;
} else if (currentPacketIndex == 2) {
payloadLength = newByte;
} else if (currentPacketIndex == 3) {
payloadLength |= (uint16_t)newByte << 8;
} else if (currentPacketIndex >= 4 + payloadLength && currentPacketIndex < 6 + payloadLength) {
EricPedley marked this conversation as resolved.
Show resolved Hide resolved
if (currentPacketIndex == 4 + payloadLength) {
if (ckA != newByte) {
return UBXPacketUpdateResult::CHECKSUM_FAILED;
}
} else {
if (ckB != newByte) {
return UBXPacketUpdateResult::CHECKSUM_FAILED;
} else {
complete = true;
inProgress = false;
}
}
return UBXPacketUpdateResult::UPDATE_OK; // return early to avoid updating checksums
} else {
payload[currentPacketIndex - 4] = newByte;
}
ckA = ckA + newByte;
ckB = ckB + ckA;
return UBXPacketUpdateResult::UPDATE_OK;
}

void* UBXPacketReader::getPayload() { return (void*)payload; }

uint8_t UBXPacketReader::getPayloadLength() { return payloadLength; }

uint8_t UBXPacketReader::getMessageClass() { return messageClass; }

uint8_t UBXPacketReader::getMessageId() { return messageId; }

bool UBXPacketReader::isComplete() { return complete; }

bool UBXPacketReader::isInProgress() { return inProgress; }

void UBXPacketReader::reset() {
payloadLength = 0;
packetIndex = 0;
ckA = 0;
ckB = 0;
complete = false;
inProgress = false;
}
33 changes: 33 additions & 0 deletions gps_ubxm8_i2c/ubxPacket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <stdint.h>

enum class UBXPacketUpdateResult {
UPDATE_OK,
CHECKSUM_FAILED,
PACKET_ALREADY_COMPLETE
};

class UBXPacketReader {
public:
UBXPacketReader();
UBXPacketUpdateResult update(uint8_t byte);
bool isComplete();
bool isInProgress();
void* getPayload();
uint8_t getPayloadLength();
uint8_t getMessageClass();
uint8_t getMessageId();
void reset();

private:
uint8_t payload[512];
uint8_t payloadLength;
uint8_t packetIndex;
uint8_t messageClass;
uint8_t messageId;
uint8_t ckA;
uint8_t ckB;
bool complete;
bool inProgress;
};
Loading