Home > front end >  GPS data processing
GPS data processing

Time:12-24

NMEA-0183 protocol is a set of communication protocols developed by the National Marine Electronics Associa-tion (NMEA) in order to establish a unified BTCM (Marine Radio Technical Committee) standard among different GPS (Global Positioning System) navigation devices.The GPS receiver transmits position, speed and other information to a PC, PDA and other devices through a serial port according to the standard specification of NMEA-0183 protocol. The GPS receiver transmits position, speed and other information to PC, PDA and other devices through the serial port according to the NMEA-0183 protocol specification.

The NMEA-0183 protocol is the standard protocol that GPS receivers should follow, and it is also the most widely used protocol on GPS receivers. Most common GPS receivers, GPS data processing software, and navigation software all follow or at least are compatible with this protocol.

There are many statements defined in NMEA-0183 protocol, but the only commonly used or most widely compatible statements are $GPGGA, $GPGSA, $GPGSV, $GPRMC, $GPVTG, $GPGLL, etc.

One of the $GPRMC statements has the following format.

$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50

Here the whole statement is a text line, the line with a comma "," separated by various fields, the size (length) of each field varies, the example here is only a possibility, and does not assume that the size of the field is the same as the above example sentence.

Field 0: $GPRMC, statement ID, indicates that the statement is Recommended Minimum Specific GPS/TRANSIT Data (RMC) Recommended Minimum Positioning Information

Field 1: UTC time, hhmmss.sss format

Field 2: Status, A=located, V=not located

Field 3: Latitude ddmm.mmmmm, degree minute format (leading digits are not sufficient to make up 0)

Field 4: Latitude N (north) or S (south)

Field 5: Longitude dddmm.mmmm, degree split format (0 for insufficient leading digits)

Field 6: Longitude E (East) or W (West)

Field 7: Speed, knots, Knots

Field 8: Azimuth, degrees

Field 9: UTC date, DDMMYY format

Field 10: Magnetic declination, (000 - 180) degrees (leading digits are not sufficient to make up 0)

Field 11: Direction of magnetic declination, E=East W=West

Field 16: Check value

Here, "" is the checksum identifier, and the two digits following it are the checksum, representing the heteroscedastic value of all characters between "$" and "" (excluding these two characters) Hexadecimal values. The checksum of the above sentence is 50 in hexadecimal, which is 80 in decimal.

Hint: The ^ operator works as an iso-or. The result of doing a ^ operation on all the characters between $ and * (the first character and the second character are iso-or, the result iso-or with the third character, and so on) should be equal to the value of the two hexadecimal digits following * after the value of 65536, otherwise the statement is in error in transmission. Note that this hexadecimal value is the one that will have the capital letters A-F in it.

Now, your program is going to read in a series of GPS outputs that contain $GPRMC and also other statements. At the end of the data, there is a separate line

END

to indicate the end of the data.

Your program has to find the $GPRMC statements from it, calculate the checksum, find the statements in which the checksum is correct and field 2 indicates that it has been located, calculate the time from it, and convert it to Beijing time. There will be more than one $GPRMC statement in the data at a time, and the Beijing time obtained from the last statement will be output as the result.

Your program must read a valid $GPRMC statement.

Input Format:

Multiple GPS statements, each ending with a carriage return line feed. The last line is END in three capital letters.

Output format.

6-digit time, expressed as.

hh:mm:ss

where hh is the two-digit hour, preceded by 0 for less than two digits; mm is the two-digit minute, preceded by 0 for less than two digits; and ss is the two-digit second, preceded by 0 for less than two digits.

Sample input.

$GPRMC,024813.640,A,3158.4608,N,11848.3737,E,10.05,324.27,150706,,,A*50

END

I wrote a code based on this question`

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

char a[100]; // 喂入的数据数组
char *time;  // 储存时间
int h, m, s, flag = 0;
char *check(char *a);
int main()
{
    while (1)
    {
        memset(a, 0, sizeof(a));
        scanf("%s", a);
        if (a[0] == 'E')
            break;
        time = check(a);
        if (time == NULL)
        {
            continue;
        }
        else
        {
            sscanf(time, "ddd.", &h, &m, &s);
            h = h   8; // 换算北京时间
            h = h > 24 ? h - 24 : h;
            flag = 1;
        } // 检查返回值
    }
    if (flag)
        printf("d:d:d\n", h, m, s);
    else
        printf("No vaild data\n");
    return 0;
}
char *check(char *a)
{
    char *c = a;
    char *p[20] = {NULL};
    char j[100];                     // 前检测数组
    int answer;                      // 校验值
    int cal = 0;                     // 计算值
    sscanf(c, "$%[^*]*%x", j, &answer); // 拆封
    //printf("%s %x",j,answer);
    for (int s = 0; j[s] != 0; s  )
    {
        cal ^= j[s];
    }
    if (cal % 65536 == answer)
    {
        for (int i = 0; p[i] != NULL; i  )
            p[i] = i == 0 ? strtok(c, ",") : strtok(NULL, ",");
        if (strcmp(p[0], "$GPRMC") == 0 && *(p[2] 0) == 'A')
        {
            return p[1];
        }
    }
    return NULL;
}

`

I would like to get the following effect

/*
  input sample:
$GPRMC,014600.00,A,2237.496474,N,11356.089515,E,0.0,225.5,310518,2.3,W,A*23
$GPRMC,010101.130,A,3606.6834,N,12021.7778,E,0.0,238.3,010807,,,A*6C
END
  output sample:
  09:01:01
*/

But when i run my code,

Segmentation fault appears in the 'if (strcmp(p[0], "$GPRMC") == 0 && *(p[2] 0) == 'A')' line of code

What should I do? (I am a newbie learning c language, please give me more guidance.)

CodePudding user response:

This may not fix your problem. I'm not brave enough to run that scanf() format specifier on my computer...

However, there's one obvious bug to address:

char *check(char *a)
{
    char *c = a;
    char *p[20] = {NULL};

    /* abridged code */

    if (cal % 65536 == answer)
    {
        for (int i = 0; p[i] != NULL; i  )
            p[i] = i == 0 ? strtok(c, ",") : strtok(NULL, ",");

How could the for() loop ever run? Nothing has changed any of the 20 pointers yet?

Below is cleaner and is probably closer to what you intended.

int i = 0;
for( char *cp = c; (cp = strtok( cp, ",") ) != NULL; cp = NULL )
    p[i  ] = cp;

As said, this may not fix your problem. Just a Christmas offering... :-)

UPDATE:
The following works in a similar fashion, but is a little more cryptic.

int i;
for( p[i = 0] = c; (p[i] = strtok( p[i], "," ) ) != NULL; p[  i] = NULL ) {}

One of the advantages of this version is that the last assigned element in the array of pointers will be set to NULL. This can be used if the array of pointers is persistent (heap?) and a function needs to return both the array (address of, really) and the number of elements. The NULL is as useful as the '\0' that marks the end of a C string.

  • Related