-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
jon
committed
Jun 24, 2011
0 parents
commit ab0687a
Showing
16 changed files
with
2,041 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
aclocal.m4 | ||
*.in | ||
configure | ||
.bootstrapped | ||
autom4te.cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
http://creativecommons.org/licenses/by/3.0/us/ |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
SUBDIRS = . tests | ||
|
||
lib_LTLIBRARIES = libnmea.la | ||
include_HEADERS = libnmea.h | ||
libnmea_la_SOURCES = libnmea2.c | ||
|
||
ACLOCAL_AMFLAGS=-I m4 | ||
|
||
AM_CFLAGS = -ansi -pedantic | ||
|
||
libnmea_la_LDFLAGS = -version-info 1:0:0 | ||
|
||
doc:: | ||
doxygen Doxyfile | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
libnmea | ||
nmea 0183 stream tools | ||
jon@wroth.org | ||
|
||
|
||
FOR MAINTAINERS | ||
|
||
run ./bootstrap.sh after checking out the project. | ||
|
||
or, bootstrap it yourself. | ||
|
||
|
||
IMPLEMENTATION | ||
|
||
you can use this library to manage NMEA streams from a device, however you | ||
happen to get them. use nmea_concat to add any new input to the buffer | ||
manager, partial or not. then use nmea_scan and nmea_parse to trigger your | ||
event, foo, on the presence of a message boundary. | ||
|
||
here's an example of an event driver that never terminates. | ||
|
||
{ | ||
nmeamsg_t msg; /* libnmea message context */ | ||
nmeabuf_t nbuf; /* libnmea read buffer state */ | ||
char src[SRCSZ]; /* source stream buffer */ | ||
char rxbuf[BUFSZ]; /* read/recv chunk */ | ||
size_t rxlen; | ||
char evbuf[MSGSZ]; /* complete message "$...*xx\r\n" */ | ||
size_t evlen; | ||
|
||
nmea_ctor(&nbuf, src, SRCSZ); | ||
|
||
for (;;) | ||
if (poll(fd, ...)) | ||
if (rxlen = read(fd, rxbuf, BUFSZ)) | ||
if (nmea_concat(&nbuf, rxbuf, (size_t)rxlen)) | ||
while (nmea_scan(&nbuf, &msg)) | ||
{ | ||
evlen = nmea_parse(evbuf, MSGSZ, &msg); | ||
foo(txbuf, txlen); | ||
} | ||
} | ||
|
||
the buffer sizes will vary depending on the device you're dealing with and | ||
your intentions for it, but, MSGSZ should always be at least as big as the | ||
longest possible sentence used to communicate with the device. | ||
|
||
SRCSZ should be big enough to hold several messages if they cannot be serviced | ||
rapidly enough. | ||
|
||
depending on whether you are performing a blocking read/recv or not, BUFSZ | ||
should be tuned somewhere near the size of the most common messages, or the | ||
average of all possible message sizes, or some such heuristic. | ||
|
||
one way to tune it in realtime would be to measure the number of read/recv | ||
calls, R, and foo() events raised, E, over some period of time. if the real | ||
value E/R approaches one, then BUFSZ is doing fine. as it heads towards | ||
zero, however, it takes too many read() calls to raise one foo(), which we | ||
can assume means that BUFSZ is too small. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/sh | ||
# | ||
if [ -f .bootstrapped ]; then | ||
autoreconf | ||
else | ||
libtoolize | ||
autoreconf -i | ||
touch .bootstrapped | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
AC_PREREQ(2.59) | ||
AC_INIT([libnmea], [1.0.0], [jon@wroth.org]) | ||
AC_CONFIG_MACRO_DIR([m4]) | ||
AC_CONFIG_AUX_DIR([config]) | ||
AC_CONFIG_HEADER([config.h]) | ||
AC_CONFIG_SRCDIR([libnmea.c]) | ||
AC_DISABLE_STATIC | ||
LT_INIT | ||
AM_INIT_AUTOMAKE([foreign]) | ||
|
||
AC_PROG_CC | ||
AC_PROG_LIBTOOL | ||
|
||
AC_HEADER_STDC | ||
AC_CHECK_HEADERS([stdlib.h string.h stdio.h]) | ||
|
||
AC_C_CONST | ||
AC_TYPE_SIZE_T | ||
|
||
AC_CHECK_FUNCS([memmove memset memcpy strchr strtol]) | ||
AC_FUNC_MEMCMP | ||
|
||
AC_CONFIG_FILES([Makefile tests/Makefile]) | ||
|
||
AC_OUTPUT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
/*! | ||
* \file libnmea.c | ||
* \author jon <jon@wroth.org> | ||
* \date september 2009 | ||
* \brief library for manipulating streams of NMEA strings. | ||
*/ | ||
|
||
/*! | ||
* \mainpage libnmea: NMEA stream library | ||
* | ||
* you can use this library to manage NMEA streams from a device, however you | ||
* happen to get them. use nmea_concat to add any new input to the buffer | ||
* manager, partial or not. then use nmea_scan and nmea_parse to trigger your | ||
* event, foo, on the presence of a message boundary. here's an example. | ||
* | ||
* \code | ||
nmeamsg_t msg; | ||
nmeabuf_t nbuf; | ||
char src[SRCSZ]; | ||
nmea_ctor(&nbuf, src, SRCSZ); | ||
while (1) | ||
if (poll(fd, ...)) | ||
if (rxlen = read(fd, rxbuf, ...)) | ||
if (nmea_concat(&nbuf, rxbuf, (size_t)rxlen)) | ||
while (nmea_scan(&nbuf, &msg)) | ||
{ | ||
txlen = nmea_parse(txbuf, BUFSZ, &msg); | ||
foo(txbuf, txlen); | ||
} | ||
* \endcode | ||
*/ | ||
|
||
#include "config.h" | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <stdio.h> | ||
#include "libnmea.h" | ||
|
||
|
||
/*! | ||
*/ | ||
static void nmeabuf_shift(nmeabuf_t *buffer) | ||
{ | ||
size_t off; | ||
switch(buffer->state) | ||
{ | ||
case NMEA_S_CLEAR: off = buffer->index; break; | ||
case NMEA_S_MARK: off = buffer->start; break; | ||
default: return; | ||
} | ||
buffer->start = 0; | ||
buffer->index -= off; | ||
buffer->length -= off; | ||
if (buffer->length) memmove(buffer->buffer, buffer->buffer + off, sizeof(char) * buffer->length); | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
void nmea_ctor(nmeabuf_t *buffer, const char *source, const size_t maxlen) | ||
{ | ||
buffer->buffer = (char *)source; | ||
buffer->alloc = maxlen; | ||
nmea_reset(buffer); | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
void nmea_reset(nmeabuf_t *buffer) | ||
{ | ||
memset(buffer->buffer, '\0', sizeof(char) * buffer->alloc); | ||
buffer->length = 0; | ||
buffer->index = 0; | ||
buffer->start = 0; | ||
buffer->state = NMEA_S_CLEAR; | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
int nmea_concat(nmeabuf_t *buffer, const char *rbuf, const size_t rlen) | ||
{ | ||
nmeabuf_shift(buffer); | ||
|
||
if (!rlen) return 0; | ||
|
||
if (rlen > (buffer->alloc - buffer->length)) return 0; | ||
|
||
memcpy(buffer->buffer + buffer->length, rbuf, sizeof(char) * rlen); | ||
|
||
buffer->length += rlen; | ||
|
||
return 1; | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
int nmea_scan(nmeabuf_t *buffer, nmeamsg_t *message) | ||
{ | ||
size_t i = buffer->index; | ||
int ret = 0; | ||
|
||
while ((i < buffer->length) && (0 == ret)) | ||
{ | ||
switch(buffer->state) | ||
{ | ||
case NMEA_S_CLEAR: | ||
if ('$' == buffer->buffer[i]) | ||
{ | ||
buffer->start = i; | ||
buffer->state = NMEA_S_MARK; | ||
} | ||
break; | ||
|
||
case NMEA_S_MARK: | ||
if ('\n' == buffer->buffer[i]) | ||
{ | ||
if (message) | ||
{ | ||
message->nmeabuf = buffer; | ||
message->start = buffer->start; | ||
message->length = i - buffer->start + 1; | ||
} | ||
buffer->state = NMEA_S_CLEAR; | ||
ret = 1; | ||
} | ||
break; | ||
} | ||
i++; | ||
} | ||
buffer->index = i; | ||
return ret; | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
size_t nmea_peek(char *wbuf, const size_t maxlen, nmeamsg_t *message) | ||
{ | ||
if (message->length > maxlen) return 0; | ||
memcpy(wbuf, message->nmeabuf->buffer + message->start, sizeof(char) * message->length); | ||
return message->length; | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
size_t nmea_parse(char *wbuf, const size_t maxlen, nmeamsg_t *message) | ||
{ | ||
size_t len = nmea_peek(wbuf, maxlen, message); | ||
nmeabuf_shift(message->nmeabuf); | ||
return len; | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
int nmea_cksum(const char *nmeasz, long *cksum) | ||
{ | ||
char * end = NULL, | ||
* start = NULL; | ||
long calcsum = 0, | ||
checksum = 0; | ||
|
||
/* get start/end of sentence | ||
* verify they're in order | ||
*/ | ||
start = strchr(nmeasz, '$'); | ||
if (NULL == start) return -1; | ||
end = strchr(start, '*'); | ||
if (NULL == end) return -1; | ||
|
||
checksum = strtol(end+1, (char **)NULL, 16); | ||
if (0 > checksum) | ||
{ | ||
perror("strtol"); | ||
return -1; | ||
} | ||
|
||
start++; /* begin after '$' */ | ||
while (start < end) calcsum ^= *(start++); /* calculate */ | ||
if (NULL != cksum) *cksum = calcsum; /* store calculated checksum */ | ||
return (calcsum == checksum) ? 0 : -1; /* test, return */ | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
int nmea_cksum_msg(nmeamsg_t *message, long *cksum) | ||
{ | ||
char buf[message->length+1]; | ||
memset(buf, '\0', sizeof(char) * message->length + 1); | ||
nmea_parse(buf, message->length, message); | ||
return nmea_cksum(buf, cksum); | ||
} | ||
|
||
|
||
/*! | ||
*/ | ||
void nmea_debug(FILE *stream, const nmeabuf_t *buffer) | ||
{ | ||
size_t i; | ||
|
||
for (i = 0; i < buffer->length; i++) | ||
fprintf(stream, "%c", isprint(buffer->buffer[i]) ? buffer->buffer[i] : '.'); | ||
fprintf(stream, "\n"); | ||
|
||
for (i = 0; i <= buffer->length; i++) | ||
if (i == buffer->start) | ||
{ | ||
if (i == buffer->index) | ||
fprintf(stream, "X"); | ||
else | ||
fprintf(stream, "S"); | ||
} | ||
else | ||
{ | ||
if (i == buffer->index) | ||
fprintf(stream, "I"); | ||
else | ||
fprintf(stream, " "); | ||
} | ||
fprintf(stream, "\n"); | ||
} |
Oops, something went wrong.