Sscanf instead of strtok to parse CGNSINF

I'm totally lost with this. Why are the results different from the original? Can I use char instead of float for the dt_time as I would like to parse it into a more readable form?
I get:
date is : 20230526992384.000000
lat: 54.568832
lng: -5.942575
count is : 3

int count;
float dt_time,lat,lng;
char* buffer="+CGNSINF: 1,1,20230526110935.000,54.568834,-5.942575,30.300,0.00,209.5,1,,0.9,1.2,0.8,,13,8,,,33,,";


void setup() {

 Serial.begin(115200);
   count= sscanf(buffer, "+CGNSINF: 1,1,%f,%f,%f,,0.0", &dt_time,&lat,&lng); 
     Serial.println();
     printf("date is : %f\n",dt_time);
     printf("lat: %f\n",lat);
      printf("lng: %f\n",lng);
       printf("count is : %d\n",count); 
}

void loop() {}

on a UNO or MEGA or similar, sscanf does not support the %f float format

Which board are you compiling for? Float has a limited number of significant digits, 6 to 7 on an UNO which only supports 32-bit floats, double should have about 15 significant digits. I would use either integers or char arrays if all you need is the text.

Hi
I'm actually using a LilyGo 7000G module which has an onboard ESP32.
I had no success using a char array to extract the date bit.
thanks

I've tried using int and double and the elements still are modified from the original

The date/time field should be easy to work with as a char array, all the data is in fixed positions within the field. The only complication is if you want to suppress leading zeros, but even then, it would be easy to convert the individual subfields to numbers by starting with the seconds and working backwords.

Thanks David
I am happy to parse the date if I could only get it extracted accurately from the original data.

Why are you insisting on not using strtok()?

I did have some success with strtok() but had similar issues with extracting the date accurately as a char array. Anyway I'm curious as to why sscanf will not work for me.

Is it your date?

For double precision, the total number of digits should be 15/16?

While converting, I get overflow (ovf) message in my ESP32?

The strtok() function extracts the string correctly and then the strtod() function fails to convert it into double dt_time.

Are you missing a comma?
20230526110935.000
==> 20230526, 110935.000

There is no comma. The date part is UTC.
Since the buffer is simply a char array is there no way to do this with sscanf?

why not

 20230526110935.000 -- yr 2023, mon  5, day 26, sec 110935
const char *date = "20230526110935.000";
char s [80];

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);

    unsigned yr;
    unsigned mon;
    unsigned day;
    long     sec;

    sscanf (date, "%4d%2d%2d%lu", &yr, &mon, &day, &sec);
    sprintf (s, " %s -- yr %d, mon %2d, day %2d, sec %6ld\n",
        date, yr, mon, day, sec);
    Serial.println (s);
}

void
loop (void)
{
}

I don't think it's 110935 seconds, that would be more than one day. Pretty sure it's:
Hour = 11
Minutes = 09
Seconds = 35

Since you are on an ESP32, why not doubles instead of floats:

int count;
double dt_time,lat,lng;
char* buffer="+CGNSINF: 1,1,20230526110935.000,54.568834,-5.942575,30.300,0.00,209.5,1,,0.9,1.2,0.8,,13,8,,,33,,";


void setup() {

 Serial.begin(115200);
   count= sscanf(buffer, "+CGNSINF: 1,1,%lf,%lf,%lf,,0.0", &dt_time,&lat,&lng); 
     Serial.println();
     printf("date is : %lf\n",dt_time);
     printf("lat: %lf\n",lat);
      printf("lng: %lf\n",lng);
       printf("count is : %d\n",count); 
}

void loop() {
  // put your main code here, to run repeatedly:
}
date is : 20230526110935.000000
lat: 54.568834
lng: -5.942575
count is : 3

The reason it works for doubles and not floats is that floats have 6-7 digits of precision while doubles have up to 15

Taking into account of the sketch of post #11 @gcjr and the comment of post #12 @gfvalvo, the OP may use the following sketch to extract his date, time, Lat, and Lng fields.

//const char *date = "20230526110935.000";
char buffer[] =
  "+CGNSINF: 1,1,20230526110935.000,54.568834,-5.942575,30.300,0.00,209.5,1,,0.9,1.2,0.8,,13,8,,,33,,";
char s [80];
char *token;

// -----------------------------------------------------------------------------
void setup (void)
{
  Serial.begin (9600);
  Serial.println();
  unsigned yr;
  unsigned mon;
  unsigned day;
  long     time;

  float lat, lng;

  token  = strtok(buffer, ",");
  token = strtok(NULL, ",");
  token = strtok(NULL, ",");   //token = 20230526110935.000
  //Serial. println(token);
  //--------------------
  sscanf (token, "%4d%2d%2d%lu", &yr, &mon, &day, &time);
  sprintf (s, " %s -- yr %d, mon %2d, day %2d, time %6ld\n",
           token, yr, mon, day, time);
  Serial.println (s);
  //------------------
  token = strtok(NULL, ",");
  lat = atof(token);
  Serial.print("Lat: "); Serial.println(lat, 6);
  //----------------------------------------
  token = strtok(NULL, ",");
  lng = atof(token);
  Serial.print("Lng: "); Serial.println(lng, 6);
}

void loop (void)
{
}

Output:

20230526110935.000 -- yr 2023, mon  5, day 26, time 110935

Lat: 54.568832
Lng: -5.942575

With the fixed format of the date/time field, I would use atof() for seconds, place a null where seconds begin and use atoi() for minutes, place null where minutes begin, repeat all the way back to year. That would put everything into numeric variables.

Thank you everybody for the help but for fun lets pretend that sscanf is the only tool we have and try to extract accurately UTC time(20230526110935.000), lat(54.568834) and long(-5.942575) from this char array. I've not been able to do it. (Ignore the date formatting for now)

char* buffer="+CGNSINF: 1,1,20230526110935.000,54.568834,-5.942575,30.300,0.00,209.5,1,,0.9,1.2,0.8,,13,8,,,33,,";

On the ESP32, use sscanf, but with the '%lf' codes to parse the floating point number into DOUBLEs instead of floats:

Try sscanf(buffer."%lf",&doubleval);. -- It just works on devices where doubles are higher precision than the wimpy 4-byte floats.

(I think of %lf as long float)

With the code in #13:

DaveX.... thank you so much. I'd never heard of lf as a variable. Apologies also in that I had tried #13 unsuccessfully because I had a rogue "&" in my code.
Thanks again.
John

"lf" is a format specifier for sscanf, the "l" specifies double in this case.

The full details are in the reference page: https://cplusplus.com/reference/cstdio/scanf/

try this

char buffer[] = "+CGNSINF: 1,1,20230526110935.000,54.568834,-5.942575,30.300,0.00,209.5,1,,0.9,1.2,0.8,,13,8,,,33,,";
void setup() {
  Serial.begin(115200);

  char sentence[20];
  int v1;
  int v2;
  int Y, M, D, h, m, s, ms;
  double lat, lon;
  char theRest[100];

  int n = sscanf(buffer, "%s %d,%d,%4d%2d%2d%2d%2d%2d.%3d,%lf,%lf,%s",
                 sentence, &v1, &v2, &Y, &M, &D, &h, &m, &s, &ms, &lat, &lon, theRest);

  Serial.print("parsed : "); Serial.println(n);
  Serial.print("sentence : "); Serial.println(sentence);
  Serial.print("v1 : "); Serial.println(v1);
  Serial.print("v2 : "); Serial.println(v2);
  Serial.print("Y : "); Serial.println(Y);
  Serial.print("M : "); Serial.println(M);
  Serial.print("D : "); Serial.println(D);
  Serial.print("h : "); Serial.println(h);
  Serial.print("m : "); Serial.println(m);
  Serial.print("s : "); Serial.println(s);
  Serial.print("ms : "); Serial.println(ms);
  Serial.print("latitude : "); Serial.println(lat, 10);
  Serial.print("longitude : "); Serial.println(lon, 10);
  Serial.print("the rest we did not read : "); Serial.println(theRest);

}

void loop() {}

you should get

parsed : 13
sentence : +CGNSINF:
v1 : 1
v2 : 1
Y : 2023
M : 5
D : 26
h : 11
m : 9
s : 35
ms : 0
latitude : 54.5688340000
longitude : -5.9425750000
the rest we did not read : 30.300,0.00,209.5,1,,0.9,1.2,0.8,,13,8,,,33,,