gps test works

This commit is contained in:
shinya 2026-05-21 12:30:02 +02:00
parent b26a6ea7dd
commit ea7f7ec704
2 changed files with 197 additions and 0 deletions

158
chip_test_example/gps.hpp Normal file
View File

@ -0,0 +1,158 @@
// gps.hpp — u-blox NMEA GPS reader over UART
// No external libs; uses POSIX termios + read().
//
// Hardware pins (THE TRUTH):
// GPIO14/TXD0 → UBLOX_TX line (/dev/serial0)
// GPIO15/RXD0 → UBLOX_RX line
// Default baud: 9600
//
// Enable UART: sudo raspi-config → Interfaces → Serial Port
// "login shell over serial" = No, "serial port hardware" = Yes
#pragma once
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
namespace gps {
struct Fix {
bool valid = false;
double lat = 0.0; // degrees, positive=N
double lon = 0.0; // degrees, positive=E
float speed_knots = 0.0f;
char time[12]{}; // HHMMSS.ss (raw NMEA)
char date[7]{}; // DDMMYY
};
class Reader {
public:
bool begin(const char *dev = "/dev/serial0", int baud = 9600);
void end();
// Read one NMEA sentence into buf (null-terminated). Returns false on error.
bool readLine(char *buf, size_t cap);
// Parse $GPRMC or $GNRMC from a sentence. Returns false if sentence is
// not RMC or position is invalid.
static bool parseRmc(const char *sentence, Fix &fix);
private:
int fd_ = -1;
};
inline bool Reader::begin(const char *dev, int baud)
{
fd_ = ::open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd_ < 0) return false;
::fcntl(fd_, F_SETFL, 0); // blocking
termios t{};
if (tcgetattr(fd_, &t) < 0) return false;
speed_t spd;
switch (baud) {
case 4800: spd = B4800; break;
case 9600: spd = B9600; break;
case 19200: spd = B19200; break;
case 38400: spd = B38400; break;
case 57600: spd = B57600; break;
default: spd = B9600;
}
cfsetispeed(&t, spd);
cfsetospeed(&t, spd);
cfmakeraw(&t);
t.c_cc[VMIN] = 1;
t.c_cc[VTIME] = 0;
tcsetattr(fd_, TCSANOW, &t);
return true;
}
inline void Reader::end()
{
if (fd_ >= 0) { ::close(fd_); fd_ = -1; }
}
inline bool Reader::readLine(char *buf, size_t cap)
{
size_t n = 0;
char c;
// Wait for '$' (start of NMEA sentence)
do {
if (::read(fd_, &c, 1) != 1) return false;
} while (c != '$');
buf[n++] = '$';
while (n < cap - 1) {
if (::read(fd_, &c, 1) != 1) return false;
buf[n++] = c;
if (c == '\n') break;
}
buf[n] = '\0';
return true;
}
// Parse $GPRMC or $GNRMC
// Format: $GNRMC,HHMMSS.ss,A,DDMM.MMMM,N,DDDMM.MMMM,E,spd,crs,DDMMYY,...
inline bool Reader::parseRmc(const char *sentence, Fix &fix)
{
if (!sentence) return false;
// Check sentence type
if (std::strncmp(sentence, "$GPRMC", 6) != 0 &&
std::strncmp(sentence, "$GNRMC", 6) != 0)
return false;
// Make a mutable copy
char buf[128];
std::strncpy(buf, sentence, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
// Tokenize on commas
const char *fields[13]{};
int nf = 0;
char *p = buf;
while (*p && nf < 13) {
fields[nf++] = p;
char *next = std::strchr(p, ',');
if (!next) break;
*next = '\0';
p = next + 1;
}
if (nf < 10) return false;
// field[2] = status ('A'=valid, 'V'=void)
if (fields[2][0] != 'A') { fix.valid = false; return false; }
// field[1] = time
std::strncpy(fix.time, fields[1], sizeof(fix.time) - 1);
// field[3] = lat DDMM.MMMM, field[4] = N/S
double raw_lat = std::atof(fields[3]);
int lat_deg = (int)(raw_lat / 100.0);
double lat_min = raw_lat - lat_deg * 100.0;
fix.lat = lat_deg + lat_min / 60.0;
if (fields[4][0] == 'S') fix.lat = -fix.lat;
// field[5] = lon DDDMM.MMMM, field[6] = E/W
double raw_lon = std::atof(fields[5]);
int lon_deg = (int)(raw_lon / 100.0);
double lon_min = raw_lon - lon_deg * 100.0;
fix.lon = lon_deg + lon_min / 60.0;
if (fields[6][0] == 'W') fix.lon = -fix.lon;
fix.speed_knots = (float)std::atof(fields[7]);
// field[9] = date DDMMYY
std::strncpy(fix.date, fields[9], sizeof(fix.date) - 1);
fix.valid = true;
return true;
}
} // namespace gps

View File

@ -0,0 +1,39 @@
#include <cstdio>
#include "gps.hpp"
int main()
{
gps::Reader reader;
if (!reader.begin()) {
std::fprintf(stderr, "gps open failed\n"
" check /dev/serial0 (UART)\n"
" enable serial: sudo raspi-config -> Interfaces -> Serial Port\n"
" login shell = No, hardware = Yes\n");
return 1;
}
std::fprintf(stderr, "gps reading /dev/serial0 at 9600...\n");
char line[128];
gps::Fix fix;
for (;;) {
if (!reader.readLine(line, sizeof(line))) {
std::fprintf(stderr, "uart read error\n");
break;
}
if (!gps::Reader::parseRmc(line, fix)) {
// Not an RMC sentence or no fix — print raw line for debug
// std::fprintf(stderr, "raw: %s", line);
continue;
}
if (fix.valid) {
std::fprintf(stderr, "fix lat=%+.6f lon=%+.6f spd=%.1f kn "
"time=%s date=%s\n",
fix.lat, fix.lon, fix.speed_knots,
fix.time, fix.date);
} else {
std::fprintf(stderr, "no fix\n");
}
}
}