vshymanskyy / TinyGSM

A small Arduino library for GSM modules, that just works
GNU Lesser General Public License v3.0
1.98k stars 726 forks source link

.getGPS Inverts Latitude (SIM7600) #566

Open wjcloudy opened 3 years ago

wjcloudy commented 3 years ago

I am in northern hemisphere and westerly longitude, (51.8,-0.72) however using .getGPS gives both a negative latitude (incorrect) and negative longitude (correct) - The included example does the same.

The values are correct when using .getGPSraw (xxxxx,N,xxxxxW) (SIM7600 on TinyGSM v0.11.3)

gorghino commented 3 years ago

I was exactly checking this issue on my SIM7600. There's a problem in getGPSImpl() :

Apparently north = stream.read() fails most of the times returning '' instead of N or E even if other parameters are correctly parsed.

17:16:01.955 -> AT+CGNSSINFO
17:16:01.955 -> AT+CGNSSINFO

17:16:01.955 -> +CGNSSINFO: 2,08,00,00,4430.812577,N,01119.128956,E,210721,151601.0,50.9,0.0,34.1,1.5,1.2,0.9
17:16:01.955 -> 
17:16:01.955 -> OK
17:16:04.975 -> ⸮ <-----------------  Serial.println(north);
17:16:04.975 -> 0 <-----------------  Serial.println(north == 'N');
17:16:05.972 -> [4808978] Latitude: -44.51354218    Longitude: -11.31881523
17:16:05.972 -> [4808978] Speed: 0.00   Altitude: 50.90
17:16:05.972 -> [4808978] Visible Satellites: 0     Used Satellites: 0
17:16:05.972 -> [4808978] Accuracy: 1.20
17:16:05.972 -> [4808978] Year: 2021    Month: 7    Day: 21
17:16:05.972 -> [4808979] Hour: 15  Minute: 16  Second: 1
17:16:05.972 -> [4808979] Retrieving GPS/GNSS/GLONASS location again as a string
17:16:05.972 -> AT+CGNSSINFO
17:16:05.972 -> AT+CGNSSINFO

17:16:05.972 -> +CGNSSINFO: 2,08,00,00,4430.812575,N,01119.128959,E,210721,151605.0,50.9,0.0,34.1,1.5,1.2,0.9
17:16:05.972 -> 
17:16:05.972 -> OK
17:16:05.972 -> [4808992] GPS/GNSS Based Location String: 2,08,00,00,4430.812575,N,01119.128959,E,210721,151605.0,50.9,0.0,34.1,1.5,1.2,0.9
wjcloudy commented 3 years ago

I see your longitude is also inverted when it shouldn't. What does your print statements give for longitude? (Guess that's doing the same)

gorghino commented 3 years ago

Yes it's the same. The formula to get the lat/lon is correct:

*lat = (floor(ilat / 100) + fmod(ilat, 100.) / 60) * (north == 'N' ? 1 : -1);
*lon = (floor(ilon / 100) + fmod(ilon, 100.) / 60) * (east == 'E' ? 1 : -1);

but apparently north and east often have read by stream instead of 'N' or 'E' @SRGDamia1 any idea why this happens?

gorghino commented 3 years ago

Ok I think i fixed it. It seems that stream.read() is not ready yet to be read while the CGNSSINFO stream is arriving.

In getGPSImpl(), add a small delay(10) after if (waitResponse(GF(GSM_NL "+CGNSSINFO:")) != 1) { return false; }. This helps.

wjcloudy commented 3 years ago

That does appear to work for me too. Only issue now is that command seems to block for several seconds, whereas get raw returns instantly...

simkard69 commented 3 years ago

I can confirm, the same goes for me on my side with a Lilygo SIM7600G-H. When running :

Any update on this problem ?

simkard69 commented 3 years ago

Hello Guys,

I modified a little bit file "TinyGsmClientSIM7600.h" to change the way "getGPSImpl()" is working. As "getGPSrawImpl()" was returning results quite instantly by querying "+CGNSSINFO", which "getGPSImpl()" was doing the same but was timing out in most cases, here is my code for file "TinyGsmClientSIM7600.h".

Frustrating things with the SIM7600 module regarding GPS output using "+CGNSSINFO" are 2 things :

Please report if it is working on your side. NB : I modified v0.11.3 library sources for that and my module is SIM7600G-H R2 NB2 : I removed "stream" and used a string parsing way instead ... probably using more CPU sycles at some point, anyway what is better between // up to you to decide :

Cheers !

TinyGsmClientSIM7600.zip

star297 commented 3 years ago

Couldn't get TinyGSM v0.10.9 SIM7600E GPS part working properly so did this. Not sure if later updates are any better, I haven't tried. Get the GPS raw data and process it outside the library. You have date and time also and ESP32 set time process is at the bottom. Works for me.

void getGPS(){
  char    GPSraw[128];                         //  raw gps data
  char    GPSdata[32][20];                     //  allow space for upto 32 GPS values, 20 bytes wide
  char    GPSdatedata[16], GPStimedata[16];
  char    GPSnorth, GPSeast;
  float   RAWlat = 0, RAWlon = 0;
  int     GPSfixmode = 0;
  int     GPSyear, GPSmonth, GPSdate;             //  GPS date data
  int     GPShours, GPSminutes, GPSseconds;       //  GPS time data
  int     GPSs = 0, GLONASSs = 0, BEIDOUs = 0 ;
  float   accuracy;
  float   GPSaltitude = 0, GPSspeed = 0; 
  int     GPSready = 0;
  int     vsat, usat;                             //  visable, using satelites
  int     t = 0, n = 0, i = 0;

  strcpy (GPSraw, (modem.getGPSraw().c_str()));   // get GPS raw data

  int len = strlen(GPSraw);
  for (i = 0; i <= len; i++) {
    if (GPSraw[i] != ',') {
      if (n <= 16) {
        GPSdata[n][t] = GPSraw[i];
        t++;
      }
    }
    else {
      GPSdata[n][t] = '\0';
      n++; t = 0;
    }
  }
  Serial.println(GPSraw);
  GPSfixmode = (int)GPSdata[0][0] - 48;
  if (GPSfixmode > 1) {
    GPSs        = atoi(GPSdata[1]);
    GLONASSs    = atoi(GPSdata[2]);
    BEIDOUs     = atoi(GPSdata[3]);
    RAWlat      = atof(GPSdata[4]);
    GPSnorth    = GPSdata[5][0];
    RAWlon      = atof(GPSdata[6]);
    GPSeast     = GPSdata[7][0];
    strcpy(GPSdatedata,  GPSdata[8]);
    strcpy(GPStimedata,  GPSdata[9]);
    GPSaltitude = atof(GPSdata[10]);
    GPSspeed    = atof(GPSdata[11]);  // Knots
    accuracy    = atof(GPSdata[13]);  // PDOP

    if (RAWlat && RAWlon)
    {
      GPSlat = (floor(RAWlat / 100) + fmod(RAWlat, 100.) / 60) *
               (GPSnorth == 'N' ? 1 : -1);
      GPSlong = (floor(RAWlon / 100) + fmod(RAWlon, 100.) / 60) *
                (GPSeast == 'E' ? 1 : -1);
      GPSspeed = GPSspeed * 1.852;  // convert to kph from knots
      sscanf(GPSdatedata, "%2d%2d%4d", &GPSdate, &GPSmonth, &GPSyear);
      sscanf(GPStimedata, "%2d%2d%2d", &GPShours, &GPSminutes, &GPSseconds);
      usat = GPSs + GLONASSs + BEIDOUs;
      vsat = usat; // no usat data available
      GPSready = 1;
    }
  }

 if(GPSready){
     Serial.printf("\n\nLat: %3.8f, Long: %3.8f\n", GPSlatitude, GPSlongitude);
     Serial.printf("Speed: %3.2fKph  Alti: %4.2fM\n", GPSspeed, GPSaltitude);
     Serial.printf("Visible Satellites: %d  Used Satellites: %d\n", vsat, usat);
     Serial.printf("Accuracy: %2.1f\n", accuracy);
     Serial.printf("Date: %d-%d-%d\n", GPSyear, GPSmonth, GPSdate);
     Serial.printf("Time: %d:%d:%d\n\n", GPShours, GPSminutes, GPSseconds);

  if (GPSyear < 2000) {
      GPSyear += 2000;
    }
    time_t  GPSUTCtime = 0;
    struct tm s;
    s.tm_sec    = (GPSseconds);
    s.tm_min    = (GPSminutes);
    s.tm_hour   = (GPShours);
    s.tm_mday   = (GPSdate);
    s.tm_mon    = (GPSmonth - 1);
    s.tm_year   = (GPSyear - 1900);
    GPSUTCtime  = (mktime(&s));

    Serial.printf("GPS Time:    %s",  ctime(&GPSUTCtime));

    if (GPSUTCtime > 1615155060) {        //  make sure time is not in the past
      setenv("TZ", "CET-1CET-2,M3.5.0/02:00:00,M10.5.0/03:00:00", 1);   //  set local DST settings
      tzset();
      struct timeval tv;
      memset(&tv, 0, sizeof(struct timeval));
      tv.tv_sec = GPSUTCtime;
      settimeofday(&tv, NULL);
    }
   }
 }