CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Converting two characters to an integer.....

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

Converting two characters to an integer.....
PostPosted: Wed Jun 06, 2018 10:32 am     Reply with quote

Hi All,

I'm in the process of adding checksum validation to a GPS enabled project based on an NMEA parsing routine that's available in the Code Library. The NMEA parsing routines did not parse & make available the NMEA checksum, so that was job #1. I have some new code that extracts the two character checksum from the NMEA string, and then converts it to an integer value. It's working as intended, except in the case where a character is the checksum is an 'A' - 'F', then it fails. For example, if the checksum is '40' it works, but if it's '4C' is doesn't. So far, I don't see a nice clean way to convert the two (hex) checksum characters to an integer in all cases.

Here are the NMEA parsing routines with my added code to parse and convert the checksum characters:
Code:

///////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <stdlib.h>
///////////////////////////////////////////////////////////////////////////////
typedef struct _DateTimeInfo
{
   int8 Day;
   int8 Month;
   int8 Year;
   int8 Hour;
   int8 Minute;
   int8 Second;
} DateTimeInfo;
////////////////////////////////////////
typedef struct _GPRMCInfo
{
   char Valid;
   DateTimeInfo DT;
   float Latitude;
   char N_S;
   float Longitude;
   char E_W;
   float Speed;
   int8 Checksum;
} GPRMCInfo;
///////////////////////////////////////////////////////////////////////////////
//copy string (pos n to pos m) from s2 to s1
char* StrnmCpy(char *s1, char *s2, size_t n, size_t m)
{
   int8 i;
   char *s;
   
   for (s=s1, i=n, s2+=n; i<=m; i++)
      *s++ = *s2++;
   *s = '\0';
   
   return s1;
}
///////////////////////////////////////////////////////////////////////////////
// find c in s starting from pos st
int8 StrFnd(char *s, char c, size_t st)
{
   int8 l;
   
   for (l=st, s+=st ; *s != '\0' ; l++, s++)
      if (*s == c)
         return l;
   return -1;
}
///////////////////////////////////////////////////////////////////////////////
void GPRMC_decode(char *GPRMCStr, GPRMCInfo *RMCInfo)
{
   int8 p1, p2;
   char TempStr[16];
   
   p1 = StrFnd(GPRMCStr, ',', 0);      //find first comma
   if (p1 == 6)
   {
      //check for valid packet:
      if ( (StrFnd(GPRMCStr, 'A', p1+1) == 18) && (GPRMCStr[0]=='$')) //valid?
      {
         RMCInfo->Valid = 'A';
         
         //Get time
         p1 = StrFnd(GPRMCStr, ',', 0);      //find first comma
         p2 = StrFnd(GPRMCStr, ',', p1+1);   //find next comma
         RMCInfo->DT.Hour = atoi(StrnmCpy(TempStr, GPRMCStr, p1+1, p1+2));   //hour
         RMCInfo->DT.Minute = atoi(StrnmCpy(TempStr, GPRMCStr, p1+3, p1+4)); //min
         RMCInfo->DT.Second = atoi(StrnmCpy(TempStr, GPRMCStr, p1+5, p1+6)); //sec
         
         //Get latitude & direction
         p1 = StrFnd(GPRMCStr, ',', p2+1);   //find next comma
         p2 = StrFnd(GPRMCStr, ',', p1+1);   //find next comma
         RMCInfo->Latitude = atof(StrnmCpy(TempStr, GPRMCStr, p1+1, p2-1));
         RMCInfo->N_S = GPRMCStr[p2+1];
         
         //Get longitude & direction
         p1 = StrFnd(GPRMCStr, ',', p2+1);   //find next comma
         p2 = StrFnd(GPRMCStr, ',', p1+1);   //find next comma
         RMCInfo->Longitude = atof(StrnmCpy(TempStr, GPRMCStr, p1+1, p2-1));
         RMCInfo->E_W = GPRMCStr[p2+1];
         
         //Get speed
         p1 = StrFnd(GPRMCStr, ',', p2+1);   //find next comma
         p2 = StrFnd(GPRMCStr, ',', p1+1);   //find next comma
         RMCInfo->Speed = atof(StrnmCpy(TempStr, GPRMCStr, p1+1, p2-1));
         
         //Get date
         p1 = StrFnd(GPRMCStr, ',', p2+1);   //find next comma
         p2 = StrFnd(GPRMCStr, ',', p1+1);   //find next comma
         RMCInfo->DT.Day = atoi(StrnmCpy(TempStr, GPRMCStr, p1+1, p1+2));  //dd
         RMCInfo->DT.Month = atoi(StrnmCpy(TempStr, GPRMCStr, p1+3, p1+4));//mm
         RMCInfo->DT.year = atoi(StrnmCpy(TempStr, GPRMCStr, p1+5, p1+6)); //yy

       //Get checksum
       p1 = StrFnd(GPRMCStr, '*', 0);   //find first asterisk
       RMCInfo->Checksum = atoi(StrnmCpy(TempStr, GPRMCStr, p1+1, p1+2)); //nn <--- Added Code
      }
      else                                //not valid
      {
         RMCInfo->Valid = 'V';
      }
   }
}
///////////////////////////////////////////////////////////////////////////////


Can anyone suggest a simple method to convert the two NMEA checksum characters to an integer in all cases?

Jack
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Jun 06, 2018 10:44 am     Reply with quote

The axtoi() function will convert 1 or 2 ascii hex digits to an 8-bit integer:
http://www.ccsinfo.com/forum/viewtopic.php?t=35175&start=2
The input does not have to be a string (no 0x00 terminator is required).
Ttelmah



Joined: 11 Mar 2010
Posts: 19545

View user's profile Send private message

PostPosted: Wed Jun 06, 2018 10:53 am     Reply with quote

OK. Your problem is atoi, does not know you are giving it a hex value.

atoi, needs the text 0x in front of the value to treat is as hex.
Option #1.
Copy the two characters to a 5 character char buffer array, and put 0x in front of these (and a null terminator).

Code:

   TempStr[0]='0';
   TempStr[1]='x';
   StrnmCpy(TempStr+2, GPRMCStr, p1+1, p1+2));
   RMCInfo->Checksum = atoi(TempStr);


Or
Option #2
Simply convert the two characters yourself. You can convert a single hex digit to a value with something like:
Code:

int hex2int(char ch)
{
    if (ch >= '0' && ch <= '9')
        return ch - '0';
    if (ch >= 'A' && ch <= 'F')
        return ch - 'A' + 10;
    if (ch >= 'a' && ch <= 'f')
        return ch - 'a' + 10;
    return -1;
}


I see PCM_programmer has posted a third great option while I was typing. Smile
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

PostPosted: Thu Jun 07, 2018 1:40 pm     Reply with quote

Hi All,

Thanks for the help so far! I'm still trying to put this issue to bed, but without total success!

I used this implementation to convert the two ascii NMEA CRC characters to a binary value:

Code:

//Get checksum
p1 = StrFnd(GPRMCStr, '*', 0);   //find first asterisk
TempStr[0]='0';
TempStr[1]='x';
RMCInfo->Checksum = atoi(StrnmCpy(TempStr+2, GPRMCStr, p1+1, p1+2));
fprintf(Diag, "TempStr: %s\n\r", TempStr);


Here are the results:
Quote:

$GPRMC,192829.000,A,4030.1334,N,07304.9255,W,0.22,96.91,070618,,,A*43
TempStr: 0x43
Calc. CRC: 67 Rcvd. CRC: 67 <--- Correct!!

$GPRMC,192830.000,A,4030.1333,N,07304.9256,W,0.10,96.91,070618,,,A*4E
TempStr: 0x4E
Calc. CRC: 78 Rcvd. CRC: 04 <--- Incorrect!!


So, the problem remains the same. As long as the CRC is fully numeric, the atoi function it works correctly, but if a character is 'A' thru 'F' is included, it does not!

Jack
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

PostPosted: Thu Jun 07, 2018 2:28 pm     Reply with quote

Hi All,

Something else must be going on here, because a test program seems to validate the functionality of the atoi function...

Code:

#include <18F46K22.h>
#fuses INTRC_IO, NOWDT, BROWNOUT, PUT, NOPBADEN
#use delay(clock=4M)
#use rs232(baud=9600, xmit=PIN_b4, stream = Diag)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void main () {
   int8 iIndex = 0;
   int val;
   char str[20];

   strcpy(str, "0x41");
   val = atoi(str);
   fprintf(Diag, "String value = %s, Int value = %d\n\r", str, val);

   strcpy(str, "0x4A");
   val = atoi(str);
   fprintf(Diag, "String value = %s, Int value = %d\n\r", str, val);

   while(1){}

}


And the results are:
Quote:

String value = 0x41, Int value = 65
String value = 0x4A, Int value = 74


Jack
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Jun 08, 2018 3:41 am     Reply with quote

Quote:

p1 = StrFnd(GPRMCStr, '*', 0); //find first asterisk
TempStr[0]='0';
TempStr[1]='x';
RMCInfo->Checksum = atoi(StrnmCpy(TempStr+2, GPRMCStr, p1+1, p1+2));
fprintf(Diag, "TempStr: %s\n\r", TempStr);

The problem is that you're trying to do too much. You're trying to pack
everything together in one line, and it's a mistake.

1. First you run your StrnmCpy() to copy "4E" after the "0x" in TempStr[].
2. Then you run atoi() on the returned pointer from StrnmCpy().
3 But, the returned pointer is TempStr+2. When you run atoi() you
are skipping over the "0x". Hence, running atoi() on "4E" is going to
return 4, or "04".

Split it up into two lines and then it will work properly. Note atoi() is run
on the full TempStr, starting from the beginning of the string:
Code:
p1 = StrFnd(GPRMCStr, '*', 0);   //find first asterisk
TempStr[0]='0';
TempStr[1]='x';
StrnmCpy(TempStr+2, GPRMCStr, p1+1, p1+2);
checksum = atoi(TempStr);
JAM2014



Joined: 24 Apr 2014
Posts: 138

View user's profile Send private message

PostPosted: Fri Jun 08, 2018 9:33 am     Reply with quote

Hi All,

Ah, yes, I see that now! I split the lines as shown, and it's working like a champ!

I'll post the updates to the NMEA parsing routines to the code library to include the return of the checksum!

Thanks,

Jack
Ttelmah



Joined: 11 Mar 2010
Posts: 19545

View user's profile Send private message

PostPosted: Fri Jun 08, 2018 2:38 pm     Reply with quote

As a comment though it'd be a lot smaller and much faster to use the direct hex read and not atoi. Atoi, has to parse the string, work out that you want to convert hex, and then call the routines to do this. Bulky and slow.

If you have two characters in 'tempstr', containing hex characters, with the little routine I already posted:

Checksum=hex2int(*TempStr)*16+hex2int(*(TempStr+1));

You will find the result will be smaller code, and this will be a lot faster as well....
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group