Raspberry Pi C++ Чтение предложений NMEA из окончательного GPS-модуля Adafruit

Я пытаюсь прочитать предложения GPS NMEA из GPS-модуля Ultimate от Adafruit. Я использую С++ на Raspberry Pi, чтобы прочитать соединение последовательного порта с модулем.

Вот моя функция чтения:

int Linuxutils::readFromSerialPort(int fd, int bufferSize) {

    /*
    Reading data from a port is a little trickier. When you operate the port in raw data mode,
    each read(2) system call will return however many characters are actually available in the
    serial input buffers. If no characters are available, the call will block (wait) until
    characters come in, an interval timer expires, or an error occurs. The read function can be
    made to return immediately by doing the following:
    fcntl(fd, F_SETFL, FNDELAY);
    The NDELAY option causes the read function to return 0 if no characters are available on the port.
    */

    // Check the file descriptor
    if ( !checkFileDecriptorIsValid(fd) ) {
        fprintf(stderr, "Could not read from serial port - it is not a valid file descriptor!\n");
        return -1;
    }

    // Now, let's wait for an input from the serial port.
    fcntl(fd, F_SETFL, 0); // block until data comes in

    // Now read the data
    int absoluteMax = bufferSize*2;
    char *buffer = (char*) malloc(sizeof(char) * bufferSize); // allocate buffer.
    int rcount = 0;
    int length = 0;

    // Read in each newline
    FILE* fdF = fdopen(fd, "r");
    int ch = getc(fdF);
    while ( (ch != '\n') ) { // Check for end of file or newline

        // Reached end of file
        if ( ch == EOF ) {
            printf("ERROR: EOF!");
            continue;
        }

        // Expand by reallocating if necessary
        if( rcount == absoluteMax ) { // time to expand ?
          absoluteMax *= 2; // expand to double the current size of anything similar.
          rcount = 0; // Re-init count
          buffer = (char*)realloc(buffer, absoluteMax); // Re-allocate memory.
        }

        // Read from stream
        ch = getc(fdF);

        // Stuff in buffer
        buffer[length] = ch;

        // Increment counters
        length++;
        rcount++;

    }

    // Don't care if we return 0 chars read
    if ( rcount == 0 ) {
        return 0;
    }

    // Stick
    buffer[rcount] = '\0';

    // Print results
    printf("Received ( %d bytes ): %s\n", rcount,buffer);

    // Return bytes read
    return rcount;

}

Таким образом, я получаю предложения, как вы можете видеть ниже, проблема в том, что я получаю эти «повторяющиеся» части полного предложения, например:

Received ( 15 bytes ): M,-31.4,M,,*61

Вот полная вещь:

Received ( 72 bytes ): GPGGA,182452.000,4456.2019,N,09337.0243,W,1,8,1.19,292.6,M,-31.4,M,,*61

Received ( 56 bytes ): GPGSA,A,3,17,07,28,26,08,11,01,09,,,,,1.49,1.19,0.91*00

Received ( 15 bytes ): M,-31.4,M,,*61

Received ( 72 bytes ): GPGGA,182453.000,4456.2019,N,09337.0242,W,1,8,1.19,292.6,M,-31.4,M,,*61

Received ( 56 bytes ): GPGSA,A,3,17,07,28,26,08,11,01,09,,,,,1.49,1.19,0.91*00

Received ( 15 bytes ): M,-31.4,M,,*61

Received ( 72 bytes ): GPGGA,182456.000,4456.2022,N,09337.0241,W,1,8,1.21,292.6,M,-31.4,M,,*64

Received ( 56 bytes ): GPGSA,A,3,17,07,28,26,08,11,01,09,,,,,2.45,1.21,2.13*0C

Received ( 70 bytes ): GPRMC,182456.000,A,4456.2022,N,09337.0241,W,0.40,183.74,110813,,,A*7F

Received ( 37 bytes ): GPVTG,183.74,T,,M,0.40,N,0.73,K,A*34

Received ( 70 bytes ): GPRMC,182453.000,A,4456.2019,N,09337.0242,W,0.29,183.74,110813,,,A*7E

Received ( 37 bytes ): GPVTG,183.74,T,,M,0.29,N,0.55,K,A*3F

Received ( 32 bytes ): 242,W,0.29,183.74,110813,,,A*7E

Received ( 70 bytes ): GPRMC,182452.000,A,4456.2019,N,09337.0243,W,0.33,183.74,110813,,,A*75

Почему я получаю повторяющиеся предложения и как это исправить? Я попытался сбросить буферы последовательного порта, но потом все стало ужасно! Спасибо.


person PhilBot    schedule 11.08.2013    source источник


Ответы (2)


Я не уверен, что понимаю вашу точную проблему. Однако есть несколько проблем с функцией, которые могут объяснить множество ошибок.

Линии

int absoluteMax = bufferSize*2;
char *buffer = (char*) malloc(sizeof(char) * bufferSize); // allocate buffer.

показаться неправильным. Вы решите, когда увеличить буфер, сравнив количество прочитанных символов с absoluteMax, поэтому оно должно соответствовать размеру выделенного буфера. В настоящее время вы пишете за пределы выделенной памяти, прежде чем перераспределить. Это приводит к неопределенному поведению. Если вам повезет, ваше приложение выйдет из строя, если вам не повезет, все будет работать, но вы потеряете вторую половину данных, которые вы прочитали, поскольку только данные, записанные в вашу память, будут перемещены realloc (если он перемещает вашу ячейку кучи).

Кроме того, вы не должны приводить возврат от malloc (или realloc) и можете полагаться на то, что sizeof(char) равно 1.

Вы теряете первый прочитанный символ (тот, который читается непосредственно перед циклом while). Это преднамеренно?

Когда вы перераспределяете buffer, вы не должны сбрасывать rcount. Это вызывает ту же ошибку, что и выше, когда вы пишете после конца buffer перед повторным перераспределением. Опять же, последствия этого не определены, но могут включать потерю части вывода.

Не связанный с ошибкой, которая вас сейчас беспокоит, но также стоит отметить тот факт, что вы сливаете buffer и fdF. Вы должны free и fclose соответственно перед выходом из функции.

Следующая (непроверенная) версия должна исправить эти проблемы.

int Linuxutils::readFromSerialPort(int fd, int bufferSize)
{
    if ( !checkFileDecriptorIsValid(fd) ) {
        fprintf(stderr, "Could not read from serial port - it is not a valid file descriptor!\n");
        return -1;
    }

    fcntl(fd, F_SETFL, 0); // block until data comes in
    int absoluteMax = bufferSize;
    char *buffer = malloc(bufferSize);
    int rcount = 0;
    int length = 0;

    // Read in each newline
    FILE* fdF = fdopen(fd, "r");
    int ch = getc(fdF);
    for (;;) {
        int ch = getc(fdF);
        if (ch == '\n') {
            break;
        }
        if (ch == EOF) { // Reached end of file
            printf("ERROR: EOF!\n");
            break;
        }
        if (length+1 >= absoluteMax) {
            absoluteMax *= 2;
            char* tmp = realloc(buffer, absoluteMax);
            if (tmp == NULL) {
                printf("ERROR: OOM\n");
                goto cleanup;
            }
            buffer = tmp;
        }
        buffer[length++] = ch;
    }

    if (length == 0) {
        return 0;
    }
    buffer[length] = '\0';

    // Print results
    printf("Received ( %d bytes ): %s\n", rcount,buffer);

cleanup:
    free(buffer);
    fclose(fdH);
    return length;
}
person simonc    schedule 11.08.2013

Возможно, вы могли бы попытаться очистить буферы последовательного порта перед чтением из него, как показано в этой ссылке ?

Я бы также подумал о том, чтобы не открывать последовательный порт каждый раз, когда вы вызываете Linuxutils::readFromSerialPort - вы можете оставить дескриптор файла открытым для дальнейшего чтения (в любом случае вызов блокируется, поэтому с точки зрения вызывающего ничего не меняется).

person cyberz    schedule 11.08.2013