Skip to content

Commit

Permalink
libnmea 1.0.0 initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jon committed Jun 24, 2011
0 parents commit ab0687a
Show file tree
Hide file tree
Showing 16 changed files with 2,041 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
aclocal.m4
*.in
configure
.bootstrapped
autom4te.cache
1 change: 1 addition & 0 deletions COPYING
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
http://creativecommons.org/licenses/by/3.0/us/
1,395 changes: 1,395 additions & 0 deletions Doxyfile

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions Makefile.am
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

59 changes: 59 additions & 0 deletions README
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.
9 changes: 9 additions & 0 deletions bootstrap.sh
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
1 change: 1 addition & 0 deletions config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*
25 changes: 25 additions & 0 deletions configure.in
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
1 change: 1 addition & 0 deletions doc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*
226 changes: 226 additions & 0 deletions libnmea.c
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");
}
Loading

0 comments on commit ab0687a

Please sign in to comment.