Ultimate GPS PA1616D - random speeds

General project help for Adafruit customers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Ultimate GPS PA1616D - random speeds

Post by JimRosen »

I am using the Ultimate GPS PA1616D connected up to an Arduino Uno on the hardware serial port. I left all GPS settings at the defaults (9600 baud, etc.). I am using the TinyGPSPlus library. I am logging the Lat, Long, Speed, etc. to an SD card every 800 MS. At random times, I will get some crazy high speeds even when it is just sitting still. These rare random speeds can be 150 mph+. When this occurs, the Lat and Long are stable so it seems like it is only when the parser is getting the speed. It has happened 2 times after running it for a few hours. The next step in this project will involve a feedback control loop for speed/cruise control, so the random speeds could be an issue. Everything works extremely well - except for the crazy random speeds.

Any ideas are much appreciated!

Below is the Arduino sketch. I removed some of the non-relevant functions (CAN BUS stuff, etc.).

Code: Select all

#include <TinyGPSPlus.h>
#include <Canbus.h>
#include <mcp2515.h>
#include <SD.h>
#include <SoftwareSerial.h>

// GPS
TinyGPSPlus gps;

// Bluetooth
SoftwareSerial ble(2, 3);  // RX, TX

int iDataDelay = 10;  // delay in Milliseconds used to separate BLE transfers.
const int chipSelect = 9;
int iVoltage;

unsigned long BLEConnectedTimeMillis = 0;
bool BLEConnectedStatus = false;

unsigned long GPSSendBLEMillis;
unsigned long BANNED;
unsigned long RPMMillis;
unsigned long SpeedMillis;
unsigned long LakeTempMillis;
unsigned long DepthMillis;
unsigned long FuelMillis;
unsigned long VoltMillis;
unsigned long LogMillis;

bool ForceSendDepthBLE = true;
bool ForceSendLakeTempBLE = true;
bool ForceSendFuelBLE = true;
bool ForceSendVoltageBLE = true;
bool ForceSendRPMBLE = true;
bool ForceSendSpeedBLE = true;
bool ForceSendGPSMaxBLE = true;
bool ForceSendSatellitesBLE = true;
bool ForceSendGPSBLE = true;
bool ForceSendTripBLE = true;

// Variables for sensors
unsigned char DepthLowByte;
unsigned char DepthHighByte;
unsigned char FuelByte;
unsigned char LakeTempByte;
unsigned char RPMHighByte;
unsigned char RPMLowByte;
unsigned char SpeedHighByte;
unsigned char SpeedLowByte;
int VoltageData;
unsigned int iSatellites;

bool VehicleActive = false;
char cTripLogPathFileName[26];
bool NewLogFileNameCreated = false;
char cDate[10];
char cTime[10];
uint8_t TimeHour;
uint8_t DateDay;
double dblGPSMaxMPH;
double dblGPSMPH;
double dblLat;
double dblLong;
double dblNewLat;
double dblNewLng;
double dblTripA;
double dblTripB;

bool CanBusMonitoringOn = false;  // Used to log all CAN bus raw data. Mainly used for debugging or finding new functionality.

void setup() {
  // Setup Pin 7 to monitor BLE status
  pinMode(7, INPUT);

  // Open the serial port. Used for debugging when connected to USB. For normal operation it is used by the GPS.
  // GPS TX is connected to RX on UNO board Pin 0. GPS RX is connected to TX on UNO board Pin 1.
  // UNO board RX(Pin 0) must be disconnected when uploading a new program to the UNO.
  // Switch on custom board flipped away from the GPS Dupont connector disconnects TX pin for uploading a new program.
  // Switch on custom board flipped towards the GPS Dupont connector is for normal operation.
  Serial.begin(9600);

  // Bluetooth serial port communication
  ble.begin(9600);

  // CAN BUS Shield
  pinMode(chipSelect, OUTPUT);
  if (!Canbus.init(CANSPEED_250)) {
    Serial.println(F("CANFail"));
  }

  // Init SD Card
  if (!SD.begin(chipSelect)) {
    ble.print(F("Err!-SI"));
    bleNewLnFlushDelay();
    Serial.println(F("SDFail"));
  }

  GPSSendBLEMillis = millis();
  RPMMillis = millis();
  SpeedMillis = millis();
  LakeTempMillis = millis();
  DepthMillis = millis();
  FuelMillis = millis();
  VoltMillis = millis();
  LogMillis = millis();
  BANNED = millis();

  // Read SD card STATS.CSV file to get the last Max GPS speed and the Trip odometer values.
  File StatsFile = SD.open("STATS.CSV", FILE_READ);
  if (StatsFile) {
    dblGPSMaxMPH = StatsFile.readStringUntil(',').toDouble();
    dblTripA = StatsFile.readStringUntil(',').toDouble() * 1609.34;  // Read Trip A from file and Convert to Meters
    dblTripB = StatsFile.readStringUntil(',').toDouble() * 1609.34;  // Read Trip B from file and Convert to Meters
    StatsFile.close();
    SendMaxGPSBLE();
    SendTripOdometersBLE();
  } else {
    ble.print(F("Err!-SS"));
    bleNewLnFlushDelay();
    Serial.print(F("Err Stats"));
    Serial.print(F("\n"));
  }
}

void loop() {
  // Check for commands from Android App
  if (millis() - BANNED > 1000) {
    BANNED();
    BANNED = millis();
  }

  // Force send sensor data if new BLE connection.
  ForceSendBLEOnNewConnection();

  // Fill GPS objects
   while (Serial.available() > 0) { gps.encode(Serial.read()); }

  if (gps.location.isValid() && gps.satellites.value() > 3) {
    dblNewLat = gps.location.lat();
    dblNewLng = gps.location.lng();
    if (dblNewLat <= 0 or dblNewLng >= 0) {  // Check to make sure the location is valid.  If not, set it to the previous value.
      dblNewLat = dblLat;
      dblNewLng = dblLong;
      dblGPSMPH = 0;
    }
  }

  // Send GPS related info to Android via BLE
  if (millis() - GPSSendBLEMillis >= 300) {
    SendGPSInfoBLE();
    GPSSendBLEMillis = millis();
  }

  // Get data from CAN Bus
  GetCanBusData();

  // Set VehicleActive if vehicle is currently moving.
  if (dblGPSMPH >= 1) {
    VehicleActive = true;
  }

  // Get Vehicle Battery Voltage
  if (millis() - VoltMillis >= 500) {
    GetBatteryVoltage();
    VoltMillis = millis();
  }

  // Trip log
  if (millis() - LogMillis >= 800) {
    WriteToTripLog();
    LogMillis = millis();
  }

  // Overwrite StatsFile
  if (VehicleActive == true and dblGPSMPH < 1) {
    WriteToStatsFile();
    VehicleActive = false;
  }
}

void WriteToStatsFile() {
  // Overwrite and store Max GPS Speed and Trip odometers in the STATS.CSV file. This should be written when the vehicle was operating then turned off.
  // There is at least 15 seconds of time to write the file from the time the vehicle is stopped and the power is cut off to the UNO.
  File StatsFile = SD.open("STATS.CSV", FILE_WRITE | O_TRUNC);
  StatsFile.print(dblGPSMaxMPH, 1);
  StatsFile.print(",");
  StatsFile.print(dblTripA * 0.000621371, 2);
  StatsFile.print(",");
  StatsFile.print(dblTripB * 0.000621371, 2);
  StatsFile.print("\n");
  StatsFile.close();
}

void GetBatteryVoltage() {
  // Battery voltage measured by UNO board
  iVoltage = getVoltage();
  if ((iVoltage > VoltageData + 6) or (iVoltage < VoltageData - 6) or ForceSendVoltageBLE == true) {  // If voltage changed or ForceSendBLE is true, send to BLE.
    ble.write("BattVoltage=");
    ble.print(iVoltage);
    VoltageData = iVoltage;
    bleNewLnFlushDelay();
    ForceSendVoltageBLE = false;
  }
}

// Function to read the voltage of the battery using A0 analog port (in millivolts).
// Vref = Vmax * (r1/(r1+r2)))
// Actual measurements: Vref = 3.291, r1 = 9990, r2 = 51030
// Using the above formula Vmax = 20.101784
int getVoltage(void) {
  int value;
  int voltage;
  int vDrop = 36;  // Voltage drop at Vin Pin on UNO board.
  value = analogRead(A0);
  voltage = value * (20.101784 / 1023) * 100 + vDrop;
  return voltage;
}
void WriteToTripLog() {
  // Write a record to the trip log file
  if (GPSDateTimeValid()) {
    if (NewLogFileNameCreated == false) {
      CreateNewLogFileName();
    }
    ConvertDayAndHourFromUTC();
    sprintf(cTime, "%02d:%02d:%02d", TimeHour, gps.time.minute(), gps.time.second());
    File TripLogFile = SD.open(cTripLogPathFileName, FILE_WRITE);
    if (TripLogFile) {
      TripLogFile.print(millis());
      TripLogFile.print(",");
      TripLogFile.print(cDate);
      TripLogFile.print(",");
      TripLogFile.print(cTime);
      TripLogFile.print(",");
      TripLogFile.print(dblNewLat, 6);
      TripLogFile.print(",");
      TripLogFile.print(dblNewLng, 6);
      TripLogFile.print(",");
      TripLogFile.print(dblGPSMPH, 1);
      TripLogFile.print(",");
      TripLogFile.print(dblGPSMaxMPH, 1);
      TripLogFile.print(",");
      TripLogFile.print(dblTripA * 0.000621371, 2);
      TripLogFile.print(",");
      TripLogFile.print(dblTripB * 0.000621371, 2);
      TripLogFile.print(",");
      TripLogFile.print(gps.satellites.value());
      TripLogFile.print(",");
      TripLogFile.print(VoltageData);
      TripLogFile.print(",");
      TripLogFile.print(FuelByte);
      TripLogFile.print(",");
      String strDepth = String(DepthLowByte, HEX);
      if (DepthLowByte < 0x10) { strDepth = "0" + strDepth; }  // Pad with leading 0 if less than 0x10.
      strDepth = String(DepthHighByte, HEX) + strDepth;
      float fDepth = strtol(strDepth.c_str(), NULL, 16) / 30.48;  // Convert HEX to long and calculate to Feet from Centimeters
      if (fDepth > 200) {
        TripLogFile.print("");
      } else {
        TripLogFile.print(fDepth, 1);
      }
      TripLogFile.print(",");
      if (LakeTempByte) {
        String strLakeTemp = String(LakeTempByte, HEX);
        if (LakeTempByte < 0x10) { strLakeTemp = "0" + strLakeTemp; }  // Pad with leading 0 if less than 0x10.
        int iLakeTemp = strtol(strLakeTemp.c_str(), NULL, 16) * 1.8 + 32;
        TripLogFile.print(iLakeTemp);
      }
      TripLogFile.print(",");
      String strRPM = String(RPMLowByte, HEX);
      if (RPMLowByte < 0x10) { strRPM = "0" + strRPM; }  // Pad with leading 0 if less than 0x10.
      strRPM = String(RPMHighByte, HEX) + strRPM;
      int iRPM = strtol(strRPM.c_str(), NULL, 16);
      TripLogFile.print(iRPM);
      TripLogFile.print("\n");
      TripLogFile.close();
    } else {
      ble.print(F("Err!-SD"));
      bleNewLnFlushDelay();
      Serial.print(F("ErrSD\n"));
    }
  }
}

void SendGPSInfoBLE() {
  if (gps.location.isValid() && gps.satellites.value() > 3) {
    if (iSatellites != gps.satellites.value() or ForceSendSatellitesBLE == true) {
      ble.print(F("Sat="));
      ble.print(gps.satellites.value());
      iSatellites = gps.satellites.value();
      bleNewLnFlushDelay();
      ForceSendSatellitesBLE = false;
    }
    if (gps.speed.isValid()) {
      double dblTempGPSMPH = gps.speed.mph();
      if ((dblTempGPSMPH >= 1) or dblGPSMPH != 0.00 or ForceSendGPSBLE == true) {
        dblGPSMPH = dblTempGPSMPH;
        if (dblGPSMPH < 1) {
          dblGPSMPH = 0;
        }
        ble.print(F("SpeedGPS="));
        ble.print(dblGPSMPH, 1);
        bleNewLnFlushDelay();

        //Calculate distace traveled for trip odometer
        if (dblLat != 0.0 && dblLong != 0.0) {
          double dblGPSDistance = gps.distanceBetween(dblLat, dblLong, dblNewLat, dblNewLng);
          dblTripA += dblGPSDistance;
          dblTripB += dblGPSDistance;
          SendTripOdometersBLE();
        }
        dblLat = dblNewLat;
        dblLong = dblNewLng;
        ForceSendGPSBLE = false;
      }
    }
    // Update Max GPS MPH
    if (dblGPSMPH > dblGPSMaxMPH) {
      dblGPSMaxMPH = dblGPSMPH;
      SendMaxGPSBLE();
    }
  } else {
    ble.print(F("Sat=0"));
    bleNewLnFlushDelay();
    ble.print(F("SpeedGPS=0"));
    bleNewLnFlushDelay();
  }
  // Send Max GPS Speed to BLE if ForceSend is set
  if (ForceSendGPSMaxBLE == true) {
    SendMaxGPSBLE();
    ForceSendGPSMaxBLE = false;
  }
  // Send Trip Odometer data to BLE if ForceSend is set
  if (ForceSendTripBLE == true) {
    SendTripOdometersBLE();
    ForceSendTripBLE = false;
  }
}

// Check to see if the time and date returned by the gps function is valid.
// For the year component, the DIY Mall DY-180 GPS module returns 2000 and the
// AdaFruit Ultimate GPS module returns 2080 by default before it sends a valid year.
bool GPSDateTimeValid() {
  if (gps.time.isValid() && gps.date.isValid() && gps.date.year() != 2000 && gps.date.year() != 2080) {
    return true;
  } else {
    return false;
  }
}

void SendTripOdometersBLE() {
  ble.print(F("TripA="));
  ble.print(dblTripA * 0.000621371, 2);
  bleNewLnFlushDelay();
  ble.print(F("TripB="));
  ble.print(dblTripB * 0.000621371, 2);
  bleNewLnFlushDelay();
}

void SendMaxGPSBLE() {
  ble.print(F("MaxGPS="));
  ble.print(dblGPSMaxMPH, 1);
  bleNewLnFlushDelay();
}

void CreateNewLogFileName() {
  char cTimeFileName[10];
  ConvertDayAndHourFromUTC();
  sprintf(cDate, "%04d%02d%02d", gps.date.year(), gps.date.month(), DateDay);
  sprintf(cTimeFileName, "%02d-%02d-%02d", TimeHour, gps.time.minute(), gps.time.second());
  if (!SD.exists(cDate)) {
    SD.mkdir(cDate);
  }
  sprintf(cTripLogPathFileName, "/%s/%s.csv", cDate, cTimeFileName);
  NewLogFileNameCreated = true;
}

// Convert UTC time to EST(DST)
void ConvertDayAndHourFromUTC() {
  TimeHour = gps.time.hour();
  DateDay = gps.date.day();
  if (TimeHour < 4) {
    TimeHour = TimeHour + 20;
    if (DateDay > 1) {
      DateDay = DateDay - 1;
    } else {
      DateDay = GetLastDayOfMonth(gps.date.month() - 1);
    }
  } else {
    TimeHour = TimeHour - 4;
  }
}

void bleNewLnFlushDelay() {
  ble.write("\n");
  ble.flush();
  delay(iDataDelay);
}

uint8_t GetLastDayOfMonth(short month) {
  if (month == 0 || month == 2 || month == 4 || month == 6 || month == 7 || month == 9 || month == 11)
    return 31;
  else if (month == 3 || month == 5 || month == 8 || month == 10)
    return 30;
  else
    return 28;
}
Last edited by adafruit_support_mike on Tue Feb 21, 2023 11:32 pm, edited 1 time in total.
Reason: added CODE tags

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Ultimate GPS PA1616D - random speeds

Post by adafruit_support_mike »

jrosen wrote: Tue Feb 21, 2023 11:24 pm At random times, I will get some crazy high speeds even when it is just sitting still.
That's normal.

The GPS module computes speed based on the past two position estimates and the time between them. The position estimates aren't perfect, so it's normal to expect up to a few meters of random noise from one to estimate to the next. When you divide that distance by the amount of time between readings, the math says the module is zipping from one point to the other at crazy speeds.

The craziness of the speed error is inversely proportional to the actual speed of the module. The larger the actual difference between positions from one reading to the next, the smaller the effect of a few meters of random offset. Obviously, that means the effect is worst when the actual speed of the module is zero.

The standard solution is to ignore the module's computed speed readings until you know it's moving faster than some low-value threshold.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

Hi, thanks for the reply. I am not sure that is the issue. I believe I saw the same thing happen when I was driving. I will test that again tomorrow. Might have to take a long drive to see if it happens. So here are a few records from the SD card. In my code, I am already ignoring < 1 mph. I ran this for some time while not moving and I actually see many data points that are much farther away, 30 ft in some cases. Those variations did not show the issue. You can see some bouncing but look at the Lat and Long when it happened. The distance between those Lat/Long pairs is about 2 ft when looking at them on Google Maps and using the measure tool.

Code: Select all

Millis         Date         Time         Latitude    Longitude        Mph  Max   TripA TripB
4889575	20230221	19:41:14	**.**3531	**.**7801	0	3.8	1.76	27.35
4890430	20230221	19:41:15	**.**3527	**.**7793	0	3.8	1.76	27.35
4891269	20230221	19:41:16	**.**3527	**.**7793	0	3.8	1.76	27.35
4892107	20230221	19:41:17	**.**3527	**.**7793	0	3.8	1.76	27.35
4892946	20230221	19:41:18	**.**3527	**.**7786	0	3.8	1.76	27.35
4893798	20230221	19:41:18	**.**3527	**.**7786	1.4	3.8	1.76	27.35
4894637	20230221	19:41:19	**.**3527	**.**7786	0	3.8	1.76	27.35
4895506	20230221	19:41:20	**.**3524	**.**7786	1.3	3.8	1.76	27.35
4896345	20230221	19:41:21	**.**3524	**.**7778	166.4	166.4	1.76	27.35
4897184	20230221	19:41:22	**.**3524	**.**7778	0	166.4	1.76	27.35
4898025	20230221	19:41:23	**.**3524	**.**7778	0	166.4	1.76	27.35
4898864	20230221	19:41:24	**.**3524	**.**7778	0	166.4	1.76	27.35
4899703	20230221	19:41:24	**.**3524	**.**7778	0	166.4	1.76	27.35
4900548	20230221	19:41:25	**.**3527	**.**7786	0	166.4	1.76	27.35
4901388	20230221	19:41:26	**.**3531	**.**7786	0	166.4	1.76	27.35
4902226	20230221	19:41:27	**.**3531	**.**7793	0	166.4	1.76	27.35
4903065	20230221	19:41:28	**.**3535	**.**7793	0	166.4	1.76	27.35
Last edited by adafruit_support_mike on Wed Feb 22, 2023 6:20 am, edited 1 time in total.

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Ultimate GPS PA1616D - random speeds

Post by adafruit_support_mike »

As a sanity check, try recording the raw NMEA sentences.

Parsed field data goes through a bunch of processing, including an option to skip a sentence if it isn't complete, and possibly a check for whether the sentence is labeled as having a valid fix. Those can hide relevant information.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

Thanks for the reply and suggestion. It happened again while I was connected to the Serial port logging the NMEA sentences.
My log file:
Millis Date Time Lat Long MPH MAX
3375135 20230222 17:40:36 39.143592 -84.277923 1.2 1.9
3375985 20230222 17:40:43 39.143604 -84.277954 1.2 1.9
3376835 20230222 17:40:36 39.143592 -84.277923 329 329
3377697 20230222 17:40:36 39.143592 -84.277923 329 329
3378548 20230222 17:40:36 39.143592 -84.277923 329 329
3379400 20230222 17:40:36 39.143592 -84.277923 329 329
3380289 20230222 17:40:36 39.143592 -84.277923 329 329
3381141 20230222 17:40:48 39.143611 -84.277969 329 329
3381991 20230222 17:40:49 39.143615 -84.277976 329 329
3382885 20230222 17:40:50 39.143615 -84.277969 329 329
3383735 20230222 17:40:51 39.143615 -84.277969 329 329
3384589 20230222 17:40:51 39.143615 -84.277969 0 329

From the Serial Monitor in Arduino IDE during the same time:
16:40:35.500 -> $GNVTG,248.5,,M,0.10,N,0.18,K,A*25
$GNRMC,214029.000,A,39051,N,08416.6738,W,1.30,304.04,220223,,,A*6C
$GNVTG,304.04,T,,M,1.30,N,2.40,K,A*24
6738,W,1.30,304.04,220223,,,
$GLGSA,A,3,74,85,86,75,,,,,,,,,1.08,0.78,0.75*18
$GNRMC,214030.000,A,39151,N,08416.6�f$GPGSA,A,3,24,21,06,03,19,14,30,01,,,,,1.08,0.78,0.75*09
$GLGSA,A,3,74,85,86,75,,,,,,,,,1.08,0.78,0.75*18
$,K,A*2F
$GNGGA,214036.000,3908.6156,N,08416.6753,W,1,13,0.77,225.9,M,-33.3,M,,*48
16:40:36.109 -> $GPGSA,A,3,24,21,06,17,03,19,14,30,01,,,,1.06,0.77,0.72*09
16:40:36.173 -> $GLGSA,A,3,74,85,86,75,,,,,,,,,1.06,0.77,0.72*1E
16:40:36.237 -> $GNRMC,214036.000,A,3908.6156,N,08416.6753,W,0.28,285.88,220223,,,A*6C
16:40:36.301 -> $GNVTG,285.88,T,,M,0.28,N,0.52,K,A*21
16:40:36.333 -> $GPGSA,A,3,24,21,06,17,03,19,14,30,01,,,,1.06,0.77,0.72*09
$GLGSA,A,3,74,85,86,75,,,,,,,,,1.06,0.77,0.72*1E
$GNRMC,214036.000,A,3908.6156,N,08416.6753,W,0.28,285.88,220223,,,A*6C
$GNVTG,285.88,T,,M,0.28,N,0.52,K,A*21
$GNGGA,214037.000,3908.6156,N,08416.6753,W,1,13,0.77,226.2,M,-33.3,M,,*41
16:40:37.119 -> $GPGSA,A,3,24,21,06,17,03,19,14,30,01,,,,1.06,0.77,0.72*09
16:40:37.184 -> $GLGSA,A,3,74,85,86,75,,,,,,,,,1.06,0.77,0.72*1E
16:40:37.216 -> $GNRMC,214037.000,A,3908.6156,N,08416.6753,W,0.84,303.14,220223,,,A*61
16:40:37.313 -> $GNVTG,303.14,T,,M,0.84,N,1.56,K,A*28
16:40:37.344 -> $GPGSA,A,3,24,21,06,17,03,19,14,30,01,,,,1.06,0.77,0.72*09
$GLGSA,A,3,74,85,86,75,,,,,,,,,1.06,0.77,0.72*1E
$GNRMC,214036.000,A,3908.6156,N,08416.6753,W,0.28,285.88,220223,,,A*6C
$GNVTG,285.88,T,,M,0.28,N,0.52,K,A*21
$GNGGA,214037.000,3908.6156,N,08416.675�f$GPGSA,A,3,24,21,06,17,03,19,14,30,01,,,,1.06,0.77,0.72*09
$GLGSA,A,3,74,85,86,75,,,,,,,,,1.06,0.77,0.72*1E
$GNRMC,214037.000,A,3908.6156,N,08416.6753,W,0.84,303.14,220223,,,A*61
$GNVTG,303.14,T,,M,0.84,N,1.56,K,A*28
$GNGGA,214038.000,3908.6157,N,08416.6755,W,1,12,0.78,226.0,M,-33.3,M,,*45

I think outputting the data to the Serial monitor is causing some issues because of the delay to write it. I believe the time is off on my log records due to the delays.

Does the $GNRMC sentence look corrupted causing the issue? If so, any ideas why? Other suggestions?

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Ultimate GPS PA1616D - random speeds

Post by adafruit_support_mike »

I see a couple of places where one NMEA sentence didn't end properly before the next one was seen:

Code: Select all

$GNRMC,214030.000,A,39151,N,08416.6�f$GPGSA,A,3,24,21,06,03,19,14,30,01,,,,,1.08,0.78,0.75*09

. . . 

$GNGGA,214037.000,3908.6156,N,08416.675�f$GPGSA,A,3,24,21,06,17,03,19,14,30,01,,,,1.06,0.77,0.72*09
Errors like that happen, and the GPS library's parser is designed to skip over such errors until it finds valid data again. That can potentially throw off some of your assumptions about timing.
jrosen wrote: Wed Feb 22, 2023 6:19 pm any ideas why?
Noise and interference are inevitable, even for wired signals. The most effective strategy is to find ways to detect such errors, and to have your code skip over damaged/incomplete lines. At the same time, you have to block or invalidate any further operations that rely on assumptions that are no longer true (like an assumption that all calculations come from consecutive NMEA sentences).

It's basically the annoying part of programming: once you get the main-line "everything happens as it should" code out of the way, you have to go back and deal with all the error-checking and recovery issues.

Such code is important, and is pretty much the defining property of anything called a 'mature' system, but it usually isn't much fun to develop or write.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

Thanks again for the reply.
The timing I was referring to is simply the processing time delay of writing to the Serial terminal throwing my timestamps off. That does not occur at all when I am not "debugging" to the Serial terminal. I took out the debugging code and let the Andriod app I created connected via bluetooth to the Arduino run all night last night and the issue did not occur. It ran for around 12 hours straight. It is strange how random it is happening.

That data from yesterday was during the time of the actual issue happening (when it jumped to 329 MPH).
So does it sounds like tinygps++ is not filtering the bad NMEA speed sentence? I wish I could figure out how it saw 329 MPH coming directly from the gps.speed.mph(). What is the most robust NMEA parser in your opinion? I would hate to re-invent that wheel.

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Ultimate GPS PA1616D - random speeds

Post by adafruit_support_mike »

JimRosen wrote: Thu Feb 23, 2023 9:44 am The timing I was referring to is simply the processing time delay of writing to the Serial terminal throwing my timestamps off.
That shouldn't happen unless you're calculating times independent of the timestamps in the NMEA sentences.
JimRosen wrote: Thu Feb 23, 2023 9:44 am What is the most robust NMEA parser in your opinion?
I haven't compared them in detail, and expect that all of them will parse well-formed sentences correctly. The issue here is identifying well-formed and malformed sentences, and deciding what to do with the malformed ones.

Some of that is technical.. a NMEA sentence begins with a dollar sign and ends with an asterisk, a checksum, and a "\r\n" line ending. The malformed lines above don't have the proper ending sequence before the dollar sign at the beginning of the next sentence, for instance. Another kind of error would be sentences with the correct beginning and ending sequences whose checksums don't match the one given by the GPS module.

Some of the decisions you'll need to make involve policy for your own project though. Is it okay to ignore incomplete sentences and skip ahead until you find the next complete one, or do you have to deal with the gap? That's stuff you'll have to decide for yourself, possibly by modifying an existing library or writing your own.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

Hello again, so I believe I found the issue. When running my code, the issue happened again, and my log file showed a speed of 190.2 MPH. The NEMA GNRMC sentence seems to have a comma dropped on a very rare occasion. In this case it is missing between fields 3 and 4 (should be a comma between A and 6149). Almost always the Checksum from the parser is probably filtering it. If you look at my example shown in bold (Line 623) below, you will see that even though this GNRMC sentence has a dropped comma, it still passes the checksum. I pasted the sentence into an online Checksum Calculator, and it calculates it as “*69”. With that column basically dropped, it looks like it is using the “Course Over Ground” field as the “Speed Over Ground”. Converting 190.2 MPH to Knots, you get 165.30. Which should be the Course Over Ground.

Line 592: 20:18:36.666 -> $GNVTG,165.30,T,,M,0.89,N,1.64,K,A*20
Line 602: 20:18:37.328 -> $GNRMC,011837.000,A,3908.6150,N,08416.6774,W,0.31,165.30,280223,,,A*6D
Line 603: 20:18:37.391 -> $GNVTG,165.30,T,,M,0.31,N,0.58,K,A*2D
Line 607: $GNRMC,011836.000,A,3908.6149,N,08416.6775,W,0.89,165.30,280223,,,A*66
Line 608: $GNVTG,165.30,T,,M,0.89,N,1.64,K,A*20
Line 609: 6775,W,0.89,165.30,280223,,,A*66
Line 611: $GNRMC,011837.000,A,3908.6150,N,08416.6774,W,0.31,165.30,280223,,,A*6D
Line 615: 20:18:38.038 -> $GNRMC,011838.000,A,3908.6149,N,08416.6774,W,0.23,165.30,280223,,,A*69
Line 616: 20:18:38.137 -> $GNVTG,165.30,T,,M,0.23,N,0.43,K,A*24
Line 619: $GNRMC,011836.000,A,3908.6149,N,08416.6775,W,0.89,165.30,280223,,,A*66
Line 620: $GNVTG,165.30,T,,M,0.89,N,1.64,K,A*20
Line 621: 6775,W,0.89,165.30,280223,,,A�f$GPGSA,A,3,05,20,17,19,04,12,25,11,06,,,,1.15,0.85,0.78*0A
Line 623: $GNRMC,011838.000,A6149,N,08416.6774,W,0.23,165.30,280223,,,A*69
Line 624: $GNVTG,165.30,T,,M,0.23,N,0.43,K,A*24
Line 627: 20:18:39.057 -> $GNRMC,011839.000,A,3908.6149,N,08416.6775,W,0.17,165.30,280223,,,A*6E
Line 628: 20:18:39.153 -> $GNVTG,165.30,T,,M,0.17,N,0.31,K,A*26
Line 630: $GNRMC,011839.000,A,3908.6149,N,08416.6775,W,0.17,165.30,280223,,,A*6E
Line 631: $GNVTG,165.30,T,,M,0.17,N,0.31,K,A*26
Line 642: 20:18:40.469 -> $GNRMC,011840.000,A,3908.6149,N,08416.6775,W,0.17,165.30,280223,,,A*60
Line 643: 20:18:40.533 -> $GNVTG,165.30,T,,M,0.17,N,0.32,K,A*25
Line 645: $GNRMC,011839.000,A,3908.6149,N,08416.6775,W,0.17,165.30,280223,,,A*6E
Line 646: $GNVTG,165.30,T,,M,0.17,N,0.31,K,A*26
Line 660: 20:18:41.524 -> $GNRMC,011841.000,A,3908.6148,N,08416.6775,W,0.22,165.30,280223,,,A*66
Line 661: 20:18:41.589 -> $GNVTG,165.30,T,,M,0.22,N,0.41,K,A*27
Line 662: 20:18:41.652 -> $GNRMC,011840.000,A,3908.6149,N,08416.6775,W,0.17,165.30,280223,,,A*60

If I am correct about my findings, a few possible solutions come to mind:
• Possibly implement a hardware filter on the serial port? Maybe it is noisy? I am new to working with GPS systems, so I don’t know how many errors are common.
• Implement a “Number Of Fields” check on each of the relevant sentences? Seems like it would be pretty easy to just count the commas. (Should be 13 fields on GNRMC)
• Maybe I should contact the person who wrote TinyGPS++ and see if they will add the checks to their library?
• Is this something Adafruit might want to add to the Adafruit_GPS library?

Does this make sense, or do you have any other ideas? Thanks in advance!

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Ultimate GPS PA1616D - random speeds

Post by adafruit_support_mike »

Good find. That does look like the core problem.
JimRosen wrote: Tue Feb 28, 2023 1:22 am Possibly implement a hardware filter on the serial port? Maybe it is noisy?
Only try this after you've inspected the signal with an oscilloscope. Without that information you don't know if there's even a problem to solve, what kind of filter would solve any problems that do exist, or how to tell if a filter is doing anything useful.

You can easily spend weeks twiddling with options, but the applicable knowledge you gain along the way will be pretty close to zero.
JimRosen wrote: Tue Feb 28, 2023 1:22 am • Implement a “Number Of Fields” check on each of the relevant sentences? Seems like it would be pretty easy to just count the commas. (Should be 13 fields on GNRMC)
That's a partial solution.

The more general solution is to use the use the checksum: run each incoming sentence through the checksum algorithm and compare it to the value at the end of the sentence. That will catch the vast majority of lost-character and altered-character errors. If the checksum you calculate doesn't match the one tacked to the end of the sentence, discard the whole sentence and move on to the next one.

You can build more elaborate options.. comparing each field to a historical average to try and decide where the error occurred, and to recover as much valid information as you can from a malformed sentence. All sorts of things are technically possible, so it comes down to a question of how much effort is worth the investment.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

Hello again, I modified the TinyGPS++ library to also check for the number of fields in the sentence. It is now working perfectly. Sentences seem to have an incorrect number of fields with a correct Checksum maybe once or twice a day when running continuously. I also added a new variable to the statistics for when the number of fields in the sentence is incorrect. I was curious about some of the other statistics so I began monitoring them. I am looking at the failedChecksum() and passedChecksum() functions. After running it for a bit I am seeing about a 7% failure rate for checksums across all of the NEMA sentences sent. Does that seem high for 9600 baud rate? Any idea about what is normal? I will try changing to different baud rates to experiment. I am also going to limit the NEMA sentences in the GPS configuration to just send RMC and GGA sentences.

Unfortunately I don't have an O scope or I would have been looking at the serial signal. I am very picky about hardware so I know my connections are solid. I also have a separate test system so I also separated the GPS and the Bluetooth transceivers a few inches from the boards to help limit any noise for testing purposes.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

So I was looking at the Checksum failure rate incorrectly. It is actually much lower - averaging around .8%. Does that seem normal?

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Ultimate GPS PA1616D - random speeds

Post by adafruit_support_mike »

By "Checksum failure rate", do you mean the number of NMEA sentences that don't match their checksum?

If so, the error rate depends on a lot of parameters related to the connection, but I'd consider 0.8% acceptably low. You can skip the sentences that don't match the checksums without losing too much information.

User avatar
JimRosen
 
Posts: 9
Joined: Tue Feb 21, 2023 10:40 pm

Re: Ultimate GPS PA1616D - random speeds

Post by JimRosen »

Some final notes about my findings with the Ultimate GPS PA1616D. After working with many different settings on the PA1616D I found that limiting the sentence output to just RMC, GGA, and GSA lowers the number of checksum failures to a very low amount (around .2%). I was disappointed when trying to sample at higher rates. Even at just 2x per second (update rate and positional rate), the % of checksum failures were fairly high at around 5%. I also tried adjusting to several different baud rates. So using the Ultimate GPS PA1616D works fairly well when using the default output rates with only the RMC, GGA, and GSA sentence output and modifications to the TinyGPS++ parser as indicated in my previous post.

Locked
Please be positive and constructive with your questions and comments.

Return to “General Project help”