2026-03-23 11:01:23 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
2026-04-18 01:29:08 +01:00
|
|
|
#include <cstdint>
|
2026-03-26 23:01:20 +00:00
|
|
|
#ifdef PS
|
|
|
|
|
#undef PS
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef F
|
|
|
|
|
#undef F
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <Eigen/Dense>
|
|
|
|
|
|
2026-03-25 18:26:56 +00:00
|
|
|
#include "HardwareSerial.h"
|
2026-03-30 22:09:23 +01:00
|
|
|
|
2026-03-26 23:01:20 +00:00
|
|
|
#include "esp_log.h"
|
2026-04-17 00:24:52 +01:00
|
|
|
#include <Adafruit_GPS.h> #include <cmath>
|
2026-04-18 01:29:08 +01:00
|
|
|
#include <ctime>
|
2026-03-26 23:01:20 +00:00
|
|
|
#include <optional>
|
|
|
|
|
|
|
|
|
|
const float TO_RAD = M_PI / 180.0f;
|
|
|
|
|
const float KNOTS_TO_M_SEC = 0.5144444f;
|
|
|
|
|
const float earth_radius = 6371000.0f;
|
2026-03-23 11:01:23 +00:00
|
|
|
|
2026-04-18 01:29:08 +01:00
|
|
|
#define GPS_TX_PIN 18
|
|
|
|
|
#define GPS_RX_PIN 17
|
2026-04-11 18:18:50 +01:00
|
|
|
|
2026-03-23 11:01:23 +00:00
|
|
|
struct GPS {
|
2026-03-30 22:09:23 +01:00
|
|
|
Adafruit_GPS *gps;
|
2026-03-26 23:01:20 +00:00
|
|
|
float origin_lat;
|
|
|
|
|
float origin_long;
|
|
|
|
|
|
|
|
|
|
GPS() {
|
|
|
|
|
|
|
|
|
|
origin_lat = INFINITY;
|
|
|
|
|
origin_long = INFINITY;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 01:29:08 +01:00
|
|
|
uint64_t unix_timestamp_millis() {
|
|
|
|
|
struct tm t;
|
|
|
|
|
|
|
|
|
|
t.tm_year = (2000 + this->gps->year) - 1900;
|
|
|
|
|
t.tm_mon = this->gps->month - 1;
|
|
|
|
|
t.tm_mday = this->gps->day;
|
|
|
|
|
t.tm_hour = this->gps->hour;
|
|
|
|
|
t.tm_min = this->gps->minute;
|
|
|
|
|
t.tm_sec = this->gps->seconds;
|
|
|
|
|
t.tm_isdst = 0; // GPS is always UTC, so no Daylight Savings
|
|
|
|
|
|
|
|
|
|
// mktime converts struct tm to time_t (Unix timestamp)
|
|
|
|
|
time_t unixTime = mktime(&t);
|
|
|
|
|
|
|
|
|
|
// Calculate how many milliseconds have passed since the GPS updated its
|
|
|
|
|
// data
|
|
|
|
|
unsigned long msSinceUpdate = millis() - this->gps->lastDate;
|
|
|
|
|
|
|
|
|
|
// Return the base timestamp plus the elapsed seconds
|
|
|
|
|
return (uint64_t)unixTime * 1000 + msSinceUpdate + gps->milliseconds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t unix_timestamp_seconds() {
|
|
|
|
|
return this->unix_timestamp_millis() / 1000;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 23:01:20 +00:00
|
|
|
void begin() {
|
2026-04-18 01:29:08 +01:00
|
|
|
Serial2.begin(9600, SERIAL_8N1, GPS_TX_PIN, GPS_RX_PIN);
|
2026-03-30 22:09:23 +01:00
|
|
|
this->gps = new Adafruit_GPS(&Serial2);
|
2026-04-11 18:18:50 +01:00
|
|
|
// this->gps->begin(9600);
|
2026-03-30 22:09:23 +01:00
|
|
|
this->gps->sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
|
2026-03-26 23:01:20 +00:00
|
|
|
|
2026-03-30 22:09:23 +01:00
|
|
|
this->gps->sendCommand(PMTK_API_SET_FIX_CTL_5HZ);
|
|
|
|
|
this->gps->sendCommand(PMTK_SET_NMEA_UPDATE_5HZ);
|
2026-03-26 23:01:20 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 22:09:23 +01:00
|
|
|
bool gps_avaliable() { return this->gps->fix; }
|
2026-03-26 23:01:20 +00:00
|
|
|
|
|
|
|
|
std::optional<Eigen::Vector2f> velocity() {
|
2026-03-30 22:09:23 +01:00
|
|
|
if (!this->gps->fix) {
|
2026-03-26 23:01:20 +00:00
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
2026-03-30 22:09:23 +01:00
|
|
|
float speed = this->gps->speed * KNOTS_TO_M_SEC;
|
|
|
|
|
float angle = this->gps->angle * TO_RAD;
|
2026-03-26 23:01:20 +00:00
|
|
|
|
|
|
|
|
return Eigen::Vector2f(cos(M_PI_2 - angle), sin(M_PI_2 - angle)) * speed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<Eigen::Vector3f>
|
|
|
|
|
waypoint_to_xyz(float latitude, float longitude, float height) {
|
|
|
|
|
if (this->origin_lat == INFINITY || this->origin_long == INFINITY) {
|
2026-04-07 10:48:44 +01:00
|
|
|
if (this->gps_avaliable()) {
|
|
|
|
|
this->origin_lat = this->gps->latitudeDegrees;
|
|
|
|
|
this->origin_long = this->gps->longitudeDegrees;
|
|
|
|
|
} else {
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
2026-03-26 23:01:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float dLat = (latitude - this->origin_lat) * TO_RAD;
|
|
|
|
|
float dLon = (longitude - this->origin_long) * TO_RAD;
|
|
|
|
|
|
|
|
|
|
float meanLat = (this->origin_lat) * TO_RAD;
|
|
|
|
|
|
|
|
|
|
float y = dLat * earth_radius;
|
|
|
|
|
float x = dLon * earth_radius * std::cos(meanLat);
|
2026-03-30 22:09:23 +01:00
|
|
|
float z = this->gps->altitude;
|
2026-03-26 23:01:20 +00:00
|
|
|
|
|
|
|
|
return Eigen::Vector3f(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<Eigen::Vector3f> get_coordinates() {
|
2026-04-07 10:48:44 +01:00
|
|
|
if (this->gps->fix == false) {
|
2026-03-26 23:01:20 +00:00
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
2026-03-30 22:09:23 +01:00
|
|
|
float latitude = this->gps->latitudeDegrees;
|
|
|
|
|
float longitude = this->gps->longitudeDegrees;
|
2026-03-26 23:01:20 +00:00
|
|
|
|
2026-03-30 22:09:23 +01:00
|
|
|
return this->waypoint_to_xyz(latitude, longitude, this->gps->altitude);
|
2026-03-23 11:01:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void poll() {
|
2026-04-17 00:24:52 +01:00
|
|
|
for (int i = 0; i < 50; i++) {
|
|
|
|
|
char _ = this->gps->read();
|
|
|
|
|
}
|
2026-04-07 10:48:44 +01:00
|
|
|
if (this->gps->newNMEAreceived()) {
|
|
|
|
|
char *line = this->gps->lastNMEA();
|
2026-04-18 01:29:08 +01:00
|
|
|
ESP_LOGI("GPS", "NMEA LINE: %s", line);
|
2026-04-07 10:48:44 +01:00
|
|
|
this->gps->parse(line);
|
|
|
|
|
}
|
2026-03-23 11:01:23 +00:00
|
|
|
}
|
|
|
|
|
};
|
2026-03-30 22:09:23 +01:00
|
|
|
|
|
|
|
|
inline SemaphoreHandle_t gps_mutex;
|
|
|
|
|
inline GPS *gps = nullptr;
|
|
|
|
|
|
|
|
|
|
void gps_poll_task(void *_);
|