PC RTS sünkroniseerimine
#7
Martinil on oigus, mikrokontrolleri peal on noutava täpsusega kellamajandus kindlasti palju lihtsam. PC peal tuleb täpsusprobleemide lahendamist alustada kas voi näiteks sellest, et VB-s kirjutatud programmis on praktiliselt voimatu garanteerida, et kellaaja väljalugemise ja ADC andmete lugemise vahele jääv aeg on konstantne - vahepeal voib VB programm ise teha prahikoristust (garbage collection), windows voib swappida, jne. jne.

ADC lugemite seostamine täpse kellaajaga on üsna levinud, toenäoliselt leiad google otsingust nii mondagi. Ilma PPS signaali arvestamata olen seda isegi teinud. Kui huvi on, leian äkki C koodi kusagilt üles, tosi küll, ta on vale mikrokontrolleri (AVR) jaoks tehtud.

Mis kellaaeg tegelikult noutav? Nimelt GPS kellaaeg erineb meie tavapärasest liigsekundite vorra. Aga kui küsitakse TAI aega, siis tuleb vaid GPS-i kellaajale 19 sekundit juurde liita ja ongi olemas.

Lisan GPS-i kellaaja väljalugemise C koodi, loodetavasti ei tee see lisainfo asja segasemaks Smile

gps.h:
Kood:
// vim: ts=2 shiftwidth=2
#ifndef gps_h_
#define gps_h_


#include <stdint.h>    // int32_t, etc.
#include <stdbool.h>    // true, false.


typedef enum {
    SENTENCE_NONE = 0,
    SENTENCE_GGA = 1,
    SENTENCE_VTG = 2,
    SENTENCE_ZDA = 3,
} SENTENCE;

/** Handle gps input. */
extern SENTENCE
handle_gps_input(    const uint8_t        c,
            int32_t*        gps_time,
            uint16_t*        course_x100);


#endif /* gps_h_ */


gps.c:
Kood:
// vim: ts=2 shiftwidth=2
#include <avr/pgmspace.h>
#include <stdint.h>    // int32_t, etc.
#include <stdbool.h>    // true, false.
#include <string.h>    // strchr
#include <stdlib.h>
#include "main.h"
#include "setup.h"
#include "gps.h"


#ifndef GPS_DEBUG
#define    GPS_DEBUG    0
#endif

#ifndef GPS_IGNORE_FIX
#define    GPS_IGNORE_FIX    0
#endif

#ifndef GPS_USE_GPZDA
#define    GPS_USE_GPZDA    1
#endif

#define    GPS_MAX_FIELDS    20

/*****************************************************************************/
typedef struct {
    uint8_t    hour;            ///< 0 .. 23
    uint8_t    minute;        ///< 0 .. 59
    uint8_t    second;        ///< 0 .. 59
    uint16_t    tick;            ///< Internal, set to zero only. 0 .. 7199
} TIME;

/*****************************************************************************/
static uint8_t            gps_field_index = 0;    ///< 0 - not receiving, 1=type (GPGGA), 2=time (HHMMSS.s/ss/sss), 3=lat, 4='N'/'S', 5=lon, 6='E'/'W', 7=quality (0=no fix, 1=fix, 2=diff. fix), 8=number of satellites per view, 9=PDOP, 10=altitude, 11=alt. unit, ..., *CHECKSUM
static uint8_t            gps_buffer[64];    // this
static uint8_t            gps_buffer_index = 0;
static SENTENCE            gps_sentence = SENTENCE_NONE;
static bool            gps_is_checksum = false;
static bool            gps_has_fix = false;
static bool            gps_has_time = false;
static TIME            gps_time;
static bool            gps_has_course = false;
static uint16_t            gps_course_x100 = 0;
static uint8_t            gps_checksum = 0;

/*****************************************************************************/
static uint16_t
parse_n_decimals(    const uint8_t*    s,
            const uint8_t    n)
{
    uint8_t    i;
    uint16_t    r = 0;
    for (i=0; i<n; ++i) {
        r = r*10 + (s[i]-'0');
    }
    return r;
}

/*****************************************************************************/
static uint8_t
hexchar_of_int(        const uint8_t    ii)
{
    return ii<10 ? ii + '0' : ii + 'A' - 10;
}

/*****************************************************************************/
static int32_t ticks_of_time(        TIME*                t)
{
    // hour
    int32_t    r = t->hour;

    // minute
    r *= 60;
    r += t->minute;

    // second
    r *= 60;
    r += t->second;

    // ticks
    r *= PRECISION_TICKS_PER_SECOND;
    r += t->tick;
    return r;
}

/*****************************************************************************/
static bool
gps_parse_time(
    TIME*        t,
    const uint8_t*    s,
    const uint8_t    size)
{
    uint16_t        ticks = 0;

    if (size<6) {
        return false;
    }

    if (size>=8 && s[6]=='.') {
        const uint8_t    dotpos = 6;
        const uint8_t    n = size - 6 - 1;
        switch (n) {
        case 1:
            ticks = parse_n_decimals(s + dotpos + 1, n);
            ticks = ticks * (PRECISION_TICKS_PER_SECOND / 10);
            break;
        case 2:
            ticks = parse_n_decimals(s + dotpos + 1, n);
            ticks = ticks * (PRECISION_TICKS_PER_SECOND / 100);
            break;
        case 3:
            ticks = parse_n_decimals(s + dotpos + 1, n);
            ticks = (ticks / 5) * (PRECISION_TICKS_PER_SECOND / 200);
            break;
        default:
            return false;
        }
    }

    t->hour   = parse_n_decimals(s  , 2);
    t->minute = parse_n_decimals(s+2, 2);
    t->second = parse_n_decimals(s+4, 2);
    t->tick        = ticks;

    return true;
}

/*****************************************************************************/
static bool
gps_parse_course(
    uint16_t*    course_x100,
    const char*    s,
    const uint8_t    size)
{
    const char*    dotptr = strchr(s, '.');
    const int8_t    dotpos = dotptr==0
                ? -1
                : (dotptr - s);
    uint16_t    before_comma = atoi(s);
    
    if (*s) {
        if (dotpos > 0) {
            const int8_t    n = size - dotpos - 1;
            const uint16_t    after_comma = parse_n_decimals((const uint8_t*)(s + dotpos + 1), n);
            switch (n) {
            case 1:
                *course_x100 = before_comma*100 + after_comma*10;
                return true;
            case 2:
                *course_x100 = before_comma*100 + after_comma;
                return true;
            case 3:
                *course_x100 = before_comma*100 + after_comma/10;
                return true;
            }
        } else {
            *course_x100 = before_comma * 100;
            return true;
        }
    }
    // nothing to do.
    return false;
}

/*****************************************************************************/
SENTENCE
handle_gps_input(    const uint8_t        c,
            int32_t*        out_time,
            uint16_t*        course_x100)
{
    SENTENCE    r = SENTENCE_NONE;

    switch (c) {
    case '$':
        // Start again.
        gps_field_index = 1;
        gps_buffer_index = 0;
        gps_sentence = SENTENCE_NONE;
        gps_is_checksum = false;
        gps_has_fix = false;
        gps_has_time = false;
        gps_has_course = false;
        gps_checksum = 0;
        gps_buffer[0] = 0;
        break;
    case '*':
        // Switch to checksum.
        gps_is_checksum = true;
        gps_buffer_index = 0;
        gps_buffer[0] = 0;
        break;
    case ',':
        gps_checksum = gps_checksum ^ c;
        // Field over.
        if (gps_field_index>=1 && gps_field_index + 1 < GPS_MAX_FIELDS) {
            if (gps_field_index==1) {
                if (gps_buffer_index==5) {
                    if (memcmp_P(gps_buffer, PSTR("GPGGA"), 5)==0) {
                        gps_sentence = SENTENCE_GGA;
                    } else if (memcmp_P(gps_buffer, PSTR("GPZDA"), 5)==0) {
                        gps_sentence = SENTENCE_ZDA;
                    } else if (memcmp_P(gps_buffer, PSTR("GPVTG"), 5)==0) {
                        gps_sentence = SENTENCE_VTG;
                    }
                }
            } else if (gps_sentence == SENTENCE_GGA) {
#if (!GPS_USE_GPZDA)
                switch (gps_field_index) {
                    case 2:
                        // time
                        gps_has_time = gps_parse_time(&gps_time, gps_buffer, gps_buffer_index);
                        break;
                    case 7:
                        // fix
                        gps_has_fix = gps_buffer_index>0 && gps_buffer[0]!='0';
#if (GPS_DEBUG)
                        if (gps_has_fix) {
                            setup_send_P(PSTR("GOT FIX\r\n"));
                        } else {
                            setup_send_char('$');
                            setup_send_hex(gps_buffer_index);
                            setup_send_char('_');
                            setup_send_hex(gps_buffer[0]);
                            setup_send_P(PSTR("NO FIX\r\n"));
                        }
                        break;
#endif
#if (GPS_IGNORE_FIX)
                        gps_has_fix = true;
#endif
                }
#endif
            } else if (gps_sentence == SENTENCE_ZDA) {
#if (GPS_USE_GPZDA)
                if (gps_field_index == 2) {
                    gps_has_time = gps_parse_time(&gps_time, gps_buffer, gps_buffer_index);
                    gps_has_fix = gps_has_time;
                }
#endif
            } else if (gps_sentence == SENTENCE_VTG) {
                if (gps_field_index == 2) {
                    gps_has_course = gps_parse_course(&gps_course_x100, (const char*)gps_buffer, gps_buffer_index);
#if (0)
                    setup_send_P(PSTR("\r\nbuffer="));
                    setup_send(gps_buffer);
                    setup_send_P(PSTR("\r\n"));
                    setup_send_integer(PSTR("gps_course_x100"), gps_course_x100, PSTR(""));
#endif
                }
            }
        }
        gps_buffer_index = 0;
        gps_buffer[0] = 0;
        ++gps_field_index;
        break;
    case 0x0D:
        // Check checksum.
        if (gps_buffer_index>=2 && hexchar_of_int(gps_checksum >> 4)==gps_buffer[0] && hexchar_of_int(gps_checksum & 0x0F)==gps_buffer[1]) {
            switch (gps_sentence) {
                case SENTENCE_GGA: /* fallthrough */
                case SENTENCE_ZDA:
                    if (gps_has_time && gps_has_fix) {
                        // YES!
                        *out_time = ticks_of_time(&gps_time);
                        r = gps_sentence;
#if (GPS_DEBUG)
                        setup_send_P(PSTR("TIME OK\r\n"));
#endif
                    } else {
#if (GPS_DEBUG)
                        if (gps_has_time) {
                            setup_send_P(PSTR("FIX MISSING\r\n"));
                        } else {
                            setup_send_P(PSTR("CK OK\r\n"));
                        }
#endif
                    }
                    break;
                case SENTENCE_VTG:
                    if (gps_has_course) {
                        *course_x100 = gps_course_x100;
                        r = gps_sentence;
                    }
                    break;
                default:
                    break; // pass
            }
        } else {
#if (GPS_DEBUG)
            setup_send_char(gps_has_time ? '!' : '#');
            setup_send_hex(gps_sentence);
            setup_send_char(':');
            setup_send_hex(gps_checksum);
            setup_send_char('=');
            setup_send_char(gps_buffer[0]);
            setup_send_char(gps_buffer[1]);
            setup_send_P(PSTR("\r\n"));
#endif
        }
        // Clear.
        gps_field_index = 0;
        gps_has_time = false;
        gps_has_fix = false;
        gps_has_course = false;
        gps_sentence = SENTENCE_NONE;
        break;
    case 0x0A:
        // pass.
        break;
    default:
        // Receiving?
        if (gps_field_index>0) {
            // !Overflow?
            if (gps_buffer_index+1 < sizeof(gps_buffer)/sizeof(gps_buffer[0])) {
                gps_buffer[gps_buffer_index] = c;
                ++gps_buffer_index;
                gps_buffer[gps_buffer_index] = 0;    // prepare with zeros :)
                if (!gps_is_checksum) {
                    gps_checksum ^= c;
                }
            } else {
                gps_field_index = 0; // restart on overflow.
            }
        }
    }

    return r;
}

Ja veel selline pisiasi, et kui 8-bitise mikrokontrolleri peal votavad 32-bitised rehkendused päris palju aega ja ajakriitilistes rakendustes voib see päris märgatav olla. Soovitan kohe votta 16- voi veel parem, 32-bitine mikrokontroller, maksab sama palju, aga üks mure vähem.
Vasta


Sõnumeid selles teemas
PC RTS sünkroniseerimine - Autor: felch - 12-05-2015, 09:16 PM
RE: PC RTS sünkroniseerimine - Autor: madis - 13-05-2015, 10:19 PM
RE: PC RTS sünkroniseerimine - Autor: felch - 13-05-2015, 10:46 PM
RE: PC RTS sünkroniseerimine - Autor: martinr112 - 13-05-2015, 11:34 PM
RE: PC RTS sünkroniseerimine - Autor: felch - 14-05-2015, 07:58 AM
RE: PC RTS sünkroniseerimine - Autor: A.R. - 14-05-2015, 09:55 AM
RE: PC RTS sünkroniseerimine - Autor: andrei - 14-05-2015, 11:04 AM
RE: PC RTS sünkroniseerimine - Autor: madis - 14-05-2015, 03:13 PM
RE: PC RTS sünkroniseerimine - Autor: felch - 14-05-2015, 09:32 PM
RE: PC RTS sünkroniseerimine - Autor: A.R. - 15-05-2015, 08:21 AM
RE: PC RTS sünkroniseerimine - Autor: andrei - 15-05-2015, 09:05 AM
RE: PC RTS sünkroniseerimine - Autor: madis - 15-05-2015, 02:09 PM

Alamfoorumi hüpe:


Kasutaja, kes vaatavad seda teemat: 1 külali(st)ne