Support antenna status on newer CDTop GPS modules

Co-authored-by: Limor Fried <ladyada@users.noreply.github.com>
This commit is contained in:
ladyadaandLimor Fried committed 2026-09-15 11:56:03 -04:00
1 parent 6d811e98f0
commit 1d07bab68a
11 files changed
+123 -26

No files matched your search

@@ -46,8 +46,9 @@ void setup()
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Default is 1 Hz update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Request antenna status from old and new modules; comment out both to keep quiet
GPS.sendCommand(F(PGCMD_ANTENNA));
GPS.sendCommand(F(CDCMD_ANTENNA));
// Ask for firmware version
GPS.sendCommand(PMTK_Q_RELEASE);
@@ -46,8 +46,9 @@ void setup()
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// Default is 1 Hz update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Request antenna status from old and new modules; comment out both to keep quiet
GPS.sendCommand(F(PGCMD_ANTENNA));
GPS.sendCommand(F(CDCMD_ANTENNA));
// Ask for firmware version
GPS.sendCommand(PMTK_Q_RELEASE);
@@ -51,8 +51,9 @@ void setup()
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Request antenna status from old and new modules; comment out both to keep quiet
GPS.sendCommand(F(PGCMD_ANTENNA));
GPS.sendCommand(F(CDCMD_ANTENNA));
delay(1000);
@@ -72,8 +72,9 @@ void setup() {
// For the parsing code to work nicely and have time to sort thru the data,
// and print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Request antenna status from old and new modules; comment out both to keep quiet
GPS.sendCommand(F(PGCMD_ANTENNA));
GPS.sendCommand(F(CDCMD_ANTENNA));
delay(1000);
+3 -2
View File
@@ -39,8 +39,9 @@ void setup()
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Request antenna status from old and new modules; comment out both to keep quiet
GPS.sendCommand(F(PGCMD_ANTENNA));
GPS.sendCommand(F(CDCMD_ANTENNA));
delay(1000);
@@ -51,8 +51,9 @@ void setup()
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// Request antenna status from old and new modules; comment out both to keep quiet
GPS.sendCommand(F(PGCMD_ANTENNA));
GPS.sendCommand(F(CDCMD_ANTENNA));
delay(1000);
// Ask for firmware version
+2 -1
View File
@@ -127,8 +127,9 @@ void setup() {
// Set the update rate
GPS.sendCommand(F(PMTK_SET_NMEA_UPDATE_1HZ)); // 100 millihertz (once every 10 seconds), 1Hz or 5Hz update rate
// Turn off updates on antenna status, if the firmware permits it
// Disable antenna status on old and new modules, if the firmware permits it.
GPS.sendCommand(F(PGCMD_NOANTENNA));
GPS.sendCommand(F(CDCMD_NOANTENNA));
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
@@ -0,0 +1,74 @@
#include <Adafruit_GPS.h>
Adafruit_GPS GPS;
bool testsPassed = false;
void setup() {
Serial.begin(115200);
// Wait briefly for the Serial Monitor on native USB boards.
while (!Serial && millis() < 5000) {
delay(10);
}
delay(250);
Serial.println(F("Adafruit GPS antenna status regression"));
char valid[][20] = {
"$PGTOP,11,1*6D",
"$PGTOP,11,2*6E",
"$PGTOP,11,3*6F",
"$PCD,11,1*66",
"$PCD,11,2*65",
"$PCD,11,3*64",
};
// PGTOP: problem, internal, external. PCD: internal, external, problem.
const uint8_t expected[] = {1, 2, 3, 2, 3, 1};
for (uint8_t i = 0; i < 6; i++) {
GPS.antenna = 0;
if (!GPS.parse(valid[i]) || GPS.antenna != expected[i]) {
Serial.print(F("FAIL: antenna status for "));
Serial.println(valid[i]);
return;
}
if (i < 3) {
if (strcmp(GPS.lastSource, "PG") || strcmp(GPS.lastSentence, "TOP")) {
Serial.println(F("FAIL: legacy sentence identity"));
return;
}
} else if (strcmp(GPS.lastSource, "P") || strcmp(GPS.lastSentence, "CD")) {
Serial.println(F("FAIL: new sentence identity"));
return;
}
Serial.print(F("PASS: "));
Serial.println(valid[i]);
}
char invalid[][22] = {
"$PCD,11*7B",
"$PCD,11,*57",
"$PCD,11,4*63",
"$PCD,9,2*5C",
"$PCDXYZ,11,1*3D",
"$PGTOP,11*70",
"$PCD,11,10*56",
"$PGTOP,11,10*5D",
};
for (uint8_t i = 0; i < 8; i++) {
GPS.antenna = 2;
if (GPS.parse(invalid[i]) || GPS.antenna != 2) {
Serial.print(F("FAIL: invalid sentence changed antenna status: "));
Serial.println(invalid[i]);
return;
}
}
Serial.println(F("PASS: incomplete and unrelated sentences preserve antenna status"));
testsPassed = true;
}
void loop() {
if (testsPassed) {
Serial.println(F("PASS: antenna status regression"));
} else {
Serial.println(F("FAIL: antenna status regression"));
}
delay(2000);
}
+1 -1
View File
@@ -202,7 +202,7 @@ public:
uint8_t fixquality; ///< Fix quality (0, 1, 2 = Invalid, GPS, DGPS)
uint8_t fixquality_3d; ///< 3D fix quality (1, 3, 3 = Nofix, 2D fix, 3D fix)
uint8_t satellites; ///< Number of satellites in use
uint8_t antenna; ///< Antenna that is used (from PGTOP)
uint8_t antenna; ///< 1=problem, 2=internal, 3=external (PGTOP or PCD)
uint16_t LOCUS_serial; ///< Log serial number
uint16_t LOCUS_records; ///< Log number of data record
+4
View File
@@ -109,6 +109,10 @@
#define PGCMD_ANTENNA \
"$PGCMD,33,1*6C" ///< request for updates on antenna status
#define PGCMD_NOANTENNA "$PGCMD,33,0*6D" ///< don't show antenna status messages
#define CDCMD_ANTENNA \
"$CDCMD,33,1*7C" ///< request antenna status on newer CDTop modules
#define CDCMD_NOANTENNA \
"$CDCMD,33,0*7D" ///< disable antenna status on newer CDTop modules
#define MAXWAITSENTENCE \
10 ///< maximum number of sentences to examine when looking for a response
+24 -12
View File
@@ -26,14 +26,14 @@ const char PROGMEM Adafruit_GPS::sources[][4] = {"II", "WI", "GP", "PG", "GL",
"GA", "GN", "P", "ZZZ"};
#ifdef NMEA_EXTENSIONS
const char PROGMEM Adafruit_GPS::sentences_parsed[][4] = {
"GGA", "GLL", "GSA", "RMC", "DBT", "HDM", "HDT", "MDA", "MTW", "MWV",
"GGA", "GLL", "GSA", "RMC", "CD", "DBT", "HDM", "HDT", "MDA", "MTW", "MWV",
"RMB", "TOP", "TXT", "VHW", "VLW", "VPW", "VWR", "WCV", "XTE", "ZZZ"};
const char PROGMEM Adafruit_GPS::sentences_known[][4] = {
"APB", "DPT", "GSV", "HDG", "MWD", "ROT",
"RPM", "RSA", "VDR", "VTG", "ZDA", "ZZZ"};
#else // make the lists short to save flash on small boards
const char PROGMEM Adafruit_GPS::sentences_parsed[][4] = {"GGA", "GLL", "GSA",
"RMC", "TOP", "ZZZ"};
const char PROGMEM Adafruit_GPS::sentences_parsed[][4] = {
"GGA", "GLL", "GSA", "RMC", "TOP", "CD", "ZZZ"};
const char PROGMEM Adafruit_GPS::sentences_known[][4] = {"DBT", "HDM", "HDT",
"ZZZ"};
#endif
@@ -201,16 +201,27 @@ bool Adafruit_GPS::parse(char *nmea) {
if (!isEmpty(p))
VDOP = atof(p); // last before checksum
} else if (!strcmp(thisSentence, "TOP")) { //*****************************TOP
} else if (!strcmp(thisSentence, "TOP") ||
!strcmp_P(thisSentence, PSTR("CD"))) { // Antenna status
if (fields < 2)
return false;
// See:
// https://learn.adafruit.com/adafruit-ultimate-gps-featherwing/antenna-options
// There is an output sentence that will tell you the status of the
// antenna. $PGTOP,11,x where x is the status number. If x is 3 that means
// it is using the external antenna. If x is 2 it's using the internal
bool cdtop = !strcmp_P(thisSentence, PSTR("CD"));
// Only PCD subtype 11 describes the antenna; other CD messages differ.
if (cdtop && strncmp_P(nmea, PSTR("$PCD,11,"), 8))
return false;
p = strchr(p, ',') + 1;
parseAntenna(p);
if (!parseAntenna(p))
return false;
if (cdtop) {
// PCD uses 1=internal, 2=external, 3=shorted. Preserve the public
// PGTOP convention: 1=problem, 2=internal, 3=external.
if (antenna == 1)
antenna = 2;
else if (antenna == 2)
antenna = 3;
else
antenna = 1;
}
}
#ifdef NMEA_EXTENSIONS // Sentences not required for basic GPS functionality
@@ -895,11 +906,12 @@ bool Adafruit_GPS::parseFix(char *p) {
/*!
@brief Parse a part of an NMEA string for antenna that is used
@param p Pointer to the location of the token in the NMEA string
@return 3=external 2=internal 1=there was an antenna short or problem
@return True if the antenna status was recognized, false otherwise
*/
/**************************************************************************/
bool Adafruit_GPS::parseAntenna(char *p) {
if (!isEmpty(p)) {
if (!isEmpty(p) && p[0] != '\0' &&
(p[1] == ',' || p[1] == '*' || p[1] == '\0')) {
if (p[0] == '3') {
antenna = 3;
} else if (p[0] == '2') {