The SkyTraq Venus 638FLPx is a GPS sensor which provides a one-pulse-per-second output synchronized to GPS time. This example uses that to capture the ACLK counter and determine the relationship between ulBSP430uptime() and UTC time.
Example output:
15:28.278: Wokeup with 3, lost 0 octets in 0/d 0/a
fix 2 GPS week 1721 sec 402481: 1357228065.040: Thu Jan 3 15:47:45 2013
UTT-to-UTC offset -81 after 837 s; UTT gained 0:00.002 in 13:57.000
15:29.280: Wokeup with 3, lost 0 octets in 0/d 0/a
fix 2 GPS week 1721 sec 402482: 1357228066.040: Thu Jan 3 15:47:46 2013
UTT-to-UTC offset -81 after 838 s; UTT gained 0:00.002 in 13:58.000
- Note
- The implementation for this sensor makes use of Fragpool, a library designed to manage packetized data in embedded communications stacks.
Venus 638FLPx Time Synchronization
GPS data is emitted either in a block of NMEA sentences or a single binary sSkyTraqMsgOut_NAV_DATA packet, once per second, at a fixed interval relative to power-up or reset. Multiple NMEA sentences in a block reference the same time.
On power up, the timestamp starts at 2006-06-28T11:59:45Z and increments in whole seconds. Once GPS transmissions start getting decoded there is a jump to the approximate time at a 1 second resolution. A second adjustment to full precision is made when synchronization is complete. This latter adjustment may be more or less than one second, depending on the error. Sometime after that 1PPS starts showing up.
For NMEA sentences the time is UTC and has a precision of 1ms.
The binary sSkyTraqMsgOut_NAV_DATA message represents time in two components: the GPS week, and the GPS second-of-week as a multiple of 0.01s. The GPS week is not provided modulo 1024, so no week rollover need be considered. However, the packet does not include the GPS-to-UTC offset. As of 2012-07-01T00:00:00 GPS is 16 seconds ahead of UTC, meaning that 1600 must be subtracted from the GPS centisecond to provide the UTC centisecond.
Analysis suggests that timestamps lag the 1PPS signal by between 100ms and 200ms. Thus a timestamp with a fractional part of 0.650 seconds might start transmission 800ms after the 1pps that it describes.
If the Venus is restarted with no-mode-change in sSkyTraqMsgIn_RESTART.mode synchronization resumes within a few seconds and timestamps are synchronized to near the whole second, although the 100ms+ lag remains. This is still worth doing as it reduces the likelihood that a timestamp is associated with the wrong 1PPS event.
main.c
#include <string.h>
#include <ctype.h>
#include <time.h>
#define GPS_STATE_1PPS 0x01
#define GPS_STATE_RX 0x02
#define GPS_STATE_TX_DONE 0x04
#define GPS_STATE_SAW_NMEA 0x08
typedef struct sAppGPSState {
unsigned int flags;
uint8_t keep_fix;
uint8_t last_fix;
unsigned int last_week_be;
unsigned int dropped_fix;
unsigned int app_lost_pkts;
unsigned int driver_lost_pkts;
unsigned int lost_octets;
const uint8_t * msg;
unsigned int msg_len;
unsigned long rx_utt;
unsigned long pps_tt;
int tx_rc;
} sAppGPSState;
static volatile sAppGPSState gps_state_;
static int
gps_serial_cb (const uint8_t * msg,
size_t len,
unsigned long rx_utt)
{
int rc;
unsigned int flags = GPS_STATE_RX;
if (NULL == msg) {
++gps_state_.driver_lost_pkts;
gps_state_.lost_octets += len;
} else if (NULL == gps_state_.msg) {
flags = GPS_STATE_SAW_NMEA;
break;
flags = 0;
rc = 0;
gps_state_.dropped_fix += 1;
break;
}
goto accept;
rc = 0;
flags = 0;
break;
}
default:
accept:
gps_state_.msg = msg;
gps_state_.msg_len = len;
gps_state_.rx_utt = rx_utt;
break;
}
} else {
++gps_state_.app_lost_pkts;
gps_state_.lost_octets += len;
}
if ((NULL != msg) && (gps_state_.msg != msg)) {
}
gps_state_.flags |= flags;
return rc;
}
static int
gps_pps_cb (unsigned long pps_tck)
{
gps_state_.flags |= GPS_STATE_1PPS;
gps_state_.pps_tt = pps_tck;
return 0;
}
static int
gps_tx_complete (const uint8_t * msg,
int rc)
{
gps_state_.flags |= GPS_STATE_TX_DONE;
gps_state_.tx_rc = rc;
}
void main ()
{
int rc;
size_t tx_length;
const uint8_t * msg_to_release;
unsigned int num_sync = 0;
time_t sync_utc = 0;
unsigned long sync_utt = 0;
unsigned long min_lag = 0;
unsigned long max_lag = 0;
unsigned long utt_per_sec;
cprintf(
"\n\nrtc " __DATE__
" " __TIME__
"\n");
cprintf(
"Uptime clock uses %lu Hz\n", utt_per_sec);
{
int rv;
memset(&config, 0, sizeof(config));
config.
pps_port = APP_PPS_PORT_PERIPH_HANDLE;
cprintf(
"Connect NMEA serial data at %lu baud to %s at:\n\t%s\n",
cprintf(
"Connect 1PPS to %s.%u on %s.%u\n",
}
{
memset(mp, 0, sizeof(*mp));
tx_length = sizeof(*mp);
cprintf(
"Queued SW version query\n");
}
memset((void*)&gps_state_, 0, sizeof(gps_state_));
gps_state_.keep_fix = 2;
gps_state_.last_fix = -1;
sync_utc = 0;
msg_to_release = NULL;
while (1) {
sAppGPSState state;
unsigned long now_utt;
do {
if (NULL != msg_to_release) {
msg_to_release = NULL;
}
if (0 < tx_length) {
if (0 == rc) {
tx_length = 0;
}
}
state = gps_state_;
gps_state_.flags = 0;
gps_state_.msg = NULL;
} while (0);
cprintf(
"%s: Wokeup with %x, lost %u octets in %u/d %u/a\n",
state.flags, state.lost_octets, state.driver_lost_pkts, state.app_lost_pkts);
if (NULL != state.msg) {
cprintf(
"\t%s [%u]: %s\n", timestamp, state.msg_len, state.msg);
break;
break;
cprintf(
"\t%s: SW version kernel %08lx odm %08lx rev %08lx\n",
timestamp,
break;
struct tm when_tm;
char cbuf[26];
uint16_t msec = (gps_csow % 100) * 10;
uint32_t gps_sow = gps_csow / 100;
time_t when_utc;
gmtime_r(&when_utc, &when_tm);
cprintf(
"\tfix %u GPS week %d sec %lu: %lu.%03u: %s",
np->
fix_mode, gps_week, gps_sow, when_utc, msec, ctime_r(&when_utc, cbuf));
sync_utc = 0;
}
if (state.flags & GPS_STATE_1PPS) {
unsigned long lag = state.rx_utt - state.pps_tt;
int show_lag = (0 == when_tm.tm_sec);
if ((0 == min_lag) || (lag < min_lag)) {
min_lag = lag;
show_lag = 1;
}
if (lag > max_lag) {
max_lag = lag;
show_lag = 1;
}
if (show_lag) {
}
if (((50 <= msec) && (msec <= 950))
cprintf(
"\tExcessive timestamp offset detected: %u ms\n", msec);
memset(mp, 0, sizeof(*mp));
mp->
mon = when_tm.tm_mon - 1;
mp->
mday = when_tm.tm_mday;
mp->
hour = when_tm.tm_hour;
mp->
min = when_tm.tm_min;
mp->
sec = when_tm.tm_sec;
tx_length = sizeof(*mp);
cprintf(
"** Queued restart command\n");
}
} else if (0 == sync_utc) {
++num_sync;
sync_utc = when_utc;
sync_utt = state.pps_tt;
cprintf(
"** First sync: %lu POSIX == %lu uptime\n", sync_utc, sync_utt);
} else {
unsigned int sync_duration_s = (when_utc - sync_utc);
unsigned long sync_duration_utt = sync_duration_s * utt_per_sec;
unsigned long expected_utt = sync_utt + sync_duration_utt;
long drift_utt = (long)state.pps_tt - (long)expected_utt;
cprintf(
"\tUTT-to-UTC offset %ld after %u s; UTT ", drift_utt, sync_duration_s);
if (0 > drift_utt) {
drift_utt = -drift_utt;
} else {
}
if ((1 == num_sync) && (90 == sync_duration_s)) {
++num_sync;
sync_utc = when_utc;
sync_utt = state.pps_tt;
}
}
}
break;
}
default:
break;
}
msg_to_release = state.msg;
}
if (state.flags & GPS_STATE_1PPS) {
#if 0
cprintf(
"\t1PPS at %lu\n", state.pps_tt);
#endif
}
if (state.flags & GPS_STATE_TX_DONE) {
cprintf(
"\tTX completed %d\n", state.tx_rc);
}
if ((state.flags & GPS_STATE_SAW_NMEA) && (0 == tx_message.
generic.
mid)) {
memset(mp, 0, sizeof(*mp));
tx_length = sizeof(*mp);
cprintf(
"Queued format=binary command\n");
}
}
}
bsp430_config.h
#define BSP430_PLATFORM_BOOT_CONFIGURE_LFXT1 1
#define configBSP430_PLATFORM_SPIN_FOR_JUMPER 1
#define configBSP430_CONSOLE 1
#define BSP430_CONSOLE_TX_BUFFER_SIZE 100
#define BSP430_CONSOLE_RX_BUFFER_SIZE 16
#define BSP430_CONSOLE_BAUD_RATE 115200
#define configBSP430_PLATFORM_PERIPHERAL_HELP 1
#define configBSP430_UPTIME 1
#define configBSP430_UPTIME_DELAY 1
#if ((BSP430_PLATFORM_TRXEB - 0) \
|| (BSP430_PLATFORM_EXP430F5438 - 0))
#define APP_PPS_CCIDX 3
#define APP_PPS_CCIS CCIS_0
#define APP_PPS_PORT_PERIPH_HANDLE BSP430_PERIPH_PORT1
#define APP_PPS_PORT_BIT BIT4
#define configBSP430_HAL_PORT1 1
#endif
#if (BSP430_PLATFORM_TRXEB - 0)
#define configBSP430_HAL_USCI5_A0 1
#define APP_NMEA_UART_PERIPH_HANDLE BSP430_PERIPH_USCI5_A0
#endif
#ifndef APP_NMEA_BAUD_RATE
#define APP_NMEA_BAUD_RATE 9600
#endif
Makefile
PLATFORM ?= trxeb
TEST_PLATFORMS=trxeb
MODULES=$(MODULES_PLATFORM)
MODULES += $(MODULES_UPTIME)
MODULES += $(MODULES_CONSOLE)
MODULES += periph/port
VPATH += $(BSP430_ROOT)/src/sensors
MODULES += sensors/skytraq
CPPFLAGS += -Ifragpool/include
SRC=main.c fragpool/src/fragpool.c
include $(BSP430_ROOT)/make/Makefile.common