Hi Folks
I'm working on a mesh project with some nodes which are nomadic. I need to know the location of each node, so i can do map overlays. So these nomadic nodes have GPS and i'm distributing the location information over Alfred, with a new server/client binary. I also have stationary nodes, and a simulation system, without GPS, so i have support for passing location via the command line.
The code needs some more testing, a man page, but release early, release often, so here it is.
Comment welcome.
Andrew
Andrew Lunn (1): alfred: Add support for passing location information over alfred.
CHANGELOG | 3 + Makefile | 24 ++- README | 52 +++++- gpsd/Makefile | 81 ++++++++ gpsd/alfred-gpsd.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gpsd/alfred-gpsd.h | 102 +++++++++++ 6 files changed, 784 insertions(+), 7 deletions(-) create mode 100644 gpsd/Makefile create mode 100644 gpsd/alfred-gpsd.c create mode 100644 gpsd/alfred-gpsd.h
--- CHANGELOG | 3 + Makefile | 24 ++- README | 52 +++++- gpsd/Makefile | 81 ++++++++ gpsd/alfred-gpsd.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gpsd/alfred-gpsd.h | 102 +++++++++++ 6 files changed, 784 insertions(+), 7 deletions(-) create mode 100644 gpsd/Makefile create mode 100644 gpsd/alfred-gpsd.c create mode 100644 gpsd/alfred-gpsd.h
diff --git a/CHANGELOG b/CHANGELOG index aa0371f..404f3f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,3 +11,6 @@ alfred 2013.3.0: * exchanges data via IPv6 unicast/multicast
-- Sun, 28 Jul 2013 18:56:52 +0200 + + * Add support for distributing geographic location information, + either fixed, or read from gpsd. \ No newline at end of file diff --git a/Makefile b/Makefile index 7b13d7b..5bc24d8 100644 --- a/Makefile +++ b/Makefile @@ -64,9 +64,15 @@ ifneq ($(CONFIG_ALFRED_VIS),n) VIS_INSTALL=vis-install endif
+ifneq ($(CONFIG_ALFRED_GPSD),n) + GPSD_ALL=gpsd-all + GPSD_CLEAN=gpsd-clean + GPSD_INSTALL=gpsd-install +endif +
# default target -all: $(BINARY_NAME) $(VIS_ALL) +all: $(BINARY_NAME) $(VIS_ALL) $(GPSD_ALL)
# standard build rules .SUFFIXES: .o .c @@ -76,10 +82,10 @@ all: $(BINARY_NAME) $(VIS_ALL) $(BINARY_NAME): $(OBJ) $(LINK.o) $^ $(LDLIBS) -o $@
-clean: $(VIS_CLEAN) +clean: $(VIS_CLEAN) $(GPSD_CLEAN) $(RM) $(BINARY_NAME) $(OBJ) $(DEP)
-install: $(BINARY_NAME) $(VIS_INSTALL) +install: $(BINARY_NAME) $(VIS_INSTALL) $(GPSD_INSTALL) $(MKDIR) $(DESTDIR)$(SBINDIR) $(MKDIR) $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 0755 $(BINARY_NAME) $(DESTDIR)$(SBINDIR) @@ -94,8 +100,18 @@ vis-all: vis-clean: $(MAKE) -C vis clean
+gpsd-install: + $(MAKE) -C gpsd install + +gpsd-all: + $(MAKE) -C gpsd all + +gpsd-clean: + $(MAKE) -C gpsd clean + # load dependencies DEP = $(OBJ:.o=.d) -include $(DEP)
-.PHONY: all clean install vis-install vis-all vis-clean +.PHONY: all clean install vis-install vis-all vis-clean \ + gpsd-install gpsd-all gpsd-clean diff --git a/README b/README index 50c36ad..3e1001f 100644 --- a/README +++ b/README @@ -29,11 +29,14 @@ alfred depends on: * librt (usually part of libc) * IPv6 support in the kernel/host system
+and if distributing GPS information: + * libgps + To compile alfred, simply type:
$ make
-This will compile both alfred and batadv-vis. To install, use +This will compile alfred, batadv-vis & alfred-gpsd. To install, use
$ make install
@@ -44,6 +47,13 @@ If you don't want to compile batadv-vis, add the directive CONFIG_ALFRED_VIS=n: $ make CONFIG_ALFRED_VIS=n $ make CONFIG_ALFRED_VIS=n install
+If you don't want to compile alfred-gpsd, add the directive +CONFIG_ALFRED_GPSD=n: + + $ make CONFIG_ALFRED_GPSD=n + $ make CONFIG_ALFRED_GPSD=n install + + Usage -----
@@ -169,11 +179,47 @@ For a json formatted output, use: { "primary" : "fe:f0:00:00:08:01" } [...]
+Alfred-gpsd +----------- + +Alfred-gpsd can be used to distibute GPS location information about +your batman-adv mesh network. This information could be, for example, +combined with Vis to visualize your mesh topology with true geographic +layout. For mobile or nomadic nodes, Alfred-gpsd, can get location +information from gpsd. Alternatively, a static location can be passed +on the command line, which is useful for static nodes without a GPS. + +Alfred-gpsd, similar to to alfred, combines server (daemon) and client +functionality in the 'alfred-gpsd' binary. The alfred-gpsd server must +be started to distribute location information. When retrieving +location information from gpsd, it should be started with: + + $ alfred-gpsd -s + +For a static location, use: + + $ alfred-gpsd -s -l 48.858222,2.2945,358 + +This server will set the location in alfred via unix +socket. Obviously, the alfred server must run too to get this +information set. When using gpsd, it updates alfred every 2 +seconds. With a static location, the update it made every 5 minutes. + +To get JSON formatted output, use: + + $ alfred-gpsd +[ + { "source" : "f6:00:48:13:d3:1e", "tpv" : {"class":"TPV","tag":"RMC","device":"/dev/ttyACM0","mode":3,"time":"2013-10-01T10:43:20.000Z","ept":0.005,"lat":52.575485000,"lon":-1.339716667,"alt":122.500,"epx":10.199,"epy":15.720,"epv":31.050,"track":0.0000,"speed":0.010,"climb":0.000,"eps":31.44} }, + { "source" : "8e:4c:77:b3:65:b4", "tpv" : {"class":"TPV","device":"command line","time":"2013-10-01T10:43:05.129Z","lat":48.858222,"lon":2.2945,"alt":358.000000,"mode":3} } +] + +See gpsd_json(5) for documentation of the tpv object. + License -------
-alfred and batadv-vis are licensed under the terms of version 2 of the GNU -General Public License (GPL). Please see the LICENSE file. +alfred, batadv-vis and alfred-gpsd are licensed under the terms of version 2 +of the GNU General Public License (GPL). Please see the LICENSE file.
The file "packet.h" is an exception and not licensed with the GPL. Instead, it is licensed using ISC license (see the head of this file). This allows diff --git a/gpsd/Makefile b/gpsd/Makefile new file mode 100644 index 0000000..69add9c --- /dev/null +++ b/gpsd/Makefile @@ -0,0 +1,81 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# +# Copyright (C) 2013 B.A.T.M.A.N. contributors +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA +# + +# alfred build +BINARY_NAME = alfred-gpsd +OBJ = alfred-gpsd.o + +# alfred flags and options +CFLAGS += -pedantic -Wall -W -std=gnu99 -fno-strict-aliasing -MD -MP +LDLIBS += -lgps + +# disable verbose output +ifneq ($(findstring $(MAKEFLAGS),s),s) +ifndef V + Q_CC = @echo ' ' CC $@; + Q_LD = @echo ' ' LD $@; + export Q_CC + export Q_LD +endif +endif + +# standard build tools +CC ?= gcc +RM ?= rm -f +INSTALL ?= install +MKDIR ?= mkdir -p +COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +LINK.o = $(Q_LD)$(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) + +# standard install paths +PREFIX = /usr/local +SBINDIR = $(PREFIX)/sbin + +# try to generate revision +REVISION= $(shell if [ -d ../.git ]; then \ + echo $$(git describe --always --dirty --match "v*" |sed 's/^v//' 2> /dev/null || echo "[unknown]"); \ + fi) +ifneq ($(REVISION),) +CPPFLAGS += -DSOURCE_VERSION="$(REVISION)" +endif + +# default target +all: $(BINARY_NAME) + +# standard build rules +.SUFFIXES: .o .c +.c.o: + $(COMPILE.c) -o $@ $< + +$(BINARY_NAME): $(OBJ) + $(LINK.o) $^ $(LDLIBS) -o $@ + +clean: + $(RM) $(BINARY_NAME) $(OBJ) $(DEP) + +install: $(BINARY_NAME) + $(MKDIR) $(DESTDIR)$(SBINDIR) + $(INSTALL) -m 0755 $(BINARY_NAME) $(DESTDIR)$(SBINDIR) + +# load dependencies +DEP = $(OBJ:.o=.d) +-include $(DEP) + +.PHONY: all clean install diff --git a/gpsd/alfred-gpsd.c b/gpsd/alfred-gpsd.c new file mode 100644 index 0000000..e8f1125 --- /dev/null +++ b/gpsd/alfred-gpsd.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Andrew Lunn, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "alfred-gpsd.h" + +static int alfred_open_sock(struct globals *globals) +{ + struct sockaddr_un addr; + + globals->unix_sock = socket(AF_LOCAL, SOCK_STREAM, 0); + if (globals->unix_sock < 0) { + fprintf(stderr, "can't create unix socket: %s\n", + strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + strcpy(addr.sun_path, ALFRED_SOCK_PATH); + + if (connect(globals->unix_sock, (struct sockaddr *)&addr, + sizeof(addr)) < 0) { + fprintf(stderr, "can't connect to unix socket: %s\n", + strerror(errno)); + return -1; + } + + return 0; +} + +static int gpsd_publish_data(struct globals *globals) +{ + int len, ret; + + /* to push data we have to add a push header, the header for the data + * and our own data type. + */ + globals->push->tx.id = htons(ntohs(globals->push->tx.id) + 1); + + len = GPSD_DATA_SIZE(globals->gpsd_data); + globals->push->data->header.length = htons(len); + len += sizeof(*globals->push) - sizeof(globals->push->header); + len += sizeof(*globals->push->data); + globals->push->header.length = htons(len); + len += sizeof(globals->push->header); + + alfred_open_sock(globals); + if (globals->unix_sock < 0) + return globals->unix_sock; + + ret = write(globals->unix_sock, globals->buf, len); + close(globals->unix_sock); + if (ret < len) + return -1; + + return 0; +} + +static void gpsd_get_location(struct globals *globals) +{ + if (globals->source == SOURCE_CMDLINE) { + char tbuf[JSON_DATE_MAX+1]; + timestamp_t now = timestamp(); + + sprintf(globals->gpsd_data->tpv, + "{"class":"TPV","device":"command line"," + ""time":"%s"," + ""lat":%f,"lon":%f,"alt":%f," + ""mode":3}", + unix_to_iso8601(now, tbuf, sizeof(tbuf)), + globals->lat, globals->lon, globals->alt); + globals->gpsd_data->tpv_len = + htonl(strlen(globals->gpsd_data->tpv) + 1); + } +} + +static int gpsd_update_data(struct globals *globals) +{ + gpsd_get_location(globals); + gpsd_publish_data(globals); + + return 0; +} + +static int gpsd_request_data(struct globals *globals) +{ + int ret; + + globals->request = (struct alfred_request_v0 *) globals->buf; + + globals->request->header.type = ALFRED_REQUEST; + globals->request->header.version = ALFRED_VERSION; + globals->request->header.length = + htons(sizeof(*globals->request) - + sizeof(globals->request->header)); + globals->request->requested_type = GPSD_PACKETTYPE; + globals->request->tx_id = htons(random()); + + alfred_open_sock(globals); + if (globals->unix_sock < 0) + return globals->unix_sock; + + ret = write(globals->unix_sock, globals->request, + sizeof(*globals->request)); + if (ret < (int)sizeof(*globals->request)) { + close(globals->unix_sock); + return -1; + } + + return globals->unix_sock; +} + +static struct gpsd_v1 *gpsd_receive_answer_packet(int sock, uint16_t *len, + uint8_t *source) +{ + static uint8_t buf[65536]; + struct alfred_tlv *tlv; + struct alfred_push_data_v0 *push; + struct alfred_data *data; + int l, ret; + + ret = read(sock, buf, sizeof(*tlv)); + if (ret < 0) + return NULL; + + if (ret < (int)sizeof(*tlv)) + return NULL; + + tlv = (struct alfred_tlv *)buf; + /* TODO: might return an ALFRED_STATUS_ERROR too, handle it */ + if (tlv->type != ALFRED_PUSH_DATA) + return NULL; + + l = ntohs(tlv->length); + /* exceed the buffer? don't read */ + if (l > (int)(sizeof(buf) - sizeof(push->header))) + return NULL; + + /* not enough for even the push packet and header? don't bother. */ + if (l < (int)(sizeof(*push) - sizeof(push->header) + sizeof(*data))) + return NULL; + + /* read the rest of the packet */ + ret = read(sock, buf + sizeof(*tlv), l); + if (ret < l) + return NULL; + + push = (struct alfred_push_data_v0 *)buf; + data = push->data; + *len = ntohs(data->header.length); + + if (data->header.type != GPSD_PACKETTYPE) + return NULL; + + if (data->header.version != GPSD_PACKETVERSION) + return NULL; + + memcpy(source, data->source, ETH_ALEN); + return (struct gpsd_v1 *) data->data; +} + +static int gpsd_read_answer(struct globals *globals) +{ + struct gpsd_v1 *gpsd_data; + uint16_t len; + uint8_t source[ETH_ALEN]; + bool first_line = true; + + printf("[\n"); + + while ((gpsd_data = gpsd_receive_answer_packet(globals->unix_sock, + &len, + source)) != NULL) { + if (len < sizeof(*gpsd_data)) + break; + + /* check size and skip bogus packets */ + if (len != GPSD_DATA_SIZE(gpsd_data)) + continue; + + if (first_line) + first_line = false; + else + printf(",\n"); + + printf(" { "source" : "%02x:%02x:%02x:%02x:%02x:%02x", " + ""tpv" : %s }", + source[0], source[1], source[2], + source[3], source[4], source[5], + gpsd_data->tpv); + } + printf("\n]\n"); + + + return 0; +} + +/* Standard parsing of a GPS data source spec. Taken from gpsdclient.c */ +static void gpsd_source_spec(const char *arg, struct fixsource_t *source) +{ + /* the casts attempt to head off a -Wwrite-strings warning */ + source->server = (char *)"localhost"; + source->port = (char *)DEFAULT_GPSD_PORT; + source->device = NULL; + + if (arg != NULL) { + char *colon1, *skipto, *rbrk; + source->spec = strdup(arg); + assert(source->spec != NULL); + + skipto = source->spec; + if (*skipto == '[' && (rbrk = strchr(skipto, ']')) != NULL) { + skipto = rbrk; + } + colon1 = strchr(skipto, ':'); + + if (colon1 != NULL) { + char *colon2; + *colon1 = '\0'; + if (colon1 != source->spec) { + source->server = source->spec; + } + source->port = colon1 + 1; + colon2 = strchr(source->port, ':'); + if (colon2 != NULL) { + *colon2 = '\0'; + source->device = colon2 + 1; + } + } else if (strchr(source->spec, '/') != NULL) { + source->device = source->spec; + } else { + source->server = source->spec; + } + } + + if (*source->server == '[') { + char *rbrk = strchr(source->server, ']'); + ++source->server; + if (rbrk != NULL) + *rbrk = '\0'; + } +} + +static int gpsd_get_data(struct globals *globals) +{ + globals->unix_sock = gpsd_request_data(globals); + if (globals->unix_sock < 0) + return -1; + + gpsd_read_answer(globals); + close(globals->unix_sock); + + return 0; +} + +static void gpsd_connect_gpsd(struct globals *globals) +{ + unsigned int flags = WATCH_ENABLE | WATCH_JSON; + int ret; + + ret = gps_open(globals->gpsdsource.server, globals->gpsdsource.port, + &globals->gpsdata); + + if (ret) { + /* Could not connect to gpsd. Set the fd so we don't + try to perform select(2) on it. */ + globals->gpsdata.gps_fd = -1; + return; + } + + if (globals->gpsdsource.device != NULL) + flags |= WATCH_DEVICE; + + gps_stream(&globals->gpsdata, flags, globals->gpsdsource.device); +} + +static void gpsd_read_gpsd(struct globals *globals) +{ + ssize_t ret; + size_t cnt; + bool eol = false; + char buf[4096]; + + cnt = 0; + do { + ret = read(globals->gpsdata.gps_fd, &buf[cnt], 1); + if (ret != 1) { + gps_close(&globals->gpsdata); + globals->gpsdata.gps_fd = -1; + return; + } + + if (buf[cnt] == '\r') + cnt--; + + if (buf[cnt] == '\n') { + eol = true; + buf[cnt] = '\0'; + break; + } + } while (cnt++ < sizeof(buf) - 1); + + if (!eol) { + gps_close(&globals->gpsdata); + globals->gpsdata.gps_fd = -1; + return; + } + +#define STARTSWITH(str, prefix) strncmp(str, prefix, sizeof(prefix)-1)==0 + if (STARTSWITH(buf, "{"class":"TPV"")) { + strcpy(globals->gpsd_data->tpv, buf); + globals->gpsd_data->tpv_len = + htonl(strlen(globals->gpsd_data->tpv) + 1); + } +} + +static void gpsd_usage(void) +{ + printf("Usage: alfred-gpsd [options]\n"); + printf(" -s, --server start up in server mode, which regularly updates gpsd data from batman-adv\n"); + printf(" -l <lat>,<lon>,<alt> Static location\n"); + printf(" -g server[:port[:device]] GPSD source\n"); + printf(" -v, --version print the version\n"); + printf(" -h, --help this help\n"); + printf("\n"); +} + +static void gpsd_parse_location(struct globals *globals, + const char * optarg) +{ + int n; + float lat, lon, alt; + + n = sscanf(optarg, "%f,%f,%f", &lat, &lon, &alt); + if (n != 3) { + printf("Unable to parse location\n"); + gpsd_usage(); + exit(EXIT_FAILURE); + } + + if ((lat < -90) || (lat > 90)) { + printf("Invalid latitude\n"); + gpsd_usage(); + exit(EXIT_FAILURE); + } + + if ((lon < -180) || (lon > 180)) { + printf("Invalid longitude\n"); + gpsd_usage(); + exit(EXIT_FAILURE); + } + + if ((alt < -1000) || (lon > 9000)) { + /* No support for aircraft or submarines! */ + printf("Invalid altitude\n"); + gpsd_usage(); + exit(EXIT_FAILURE); + } + + globals->lat = lat; + globals->lon = lon; + globals->alt = alt; +} + +static struct globals *gpsd_init(int argc, char *argv[]) +{ + bool have_source = false; + int opt, opt_ind; + struct globals *globals; + struct option long_options[] = { + {"server", no_argument, NULL, 's'}, + {"location", required_argument, NULL, 'l'}, + {"gpsd", required_argument, NULL, 'g'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0}, + }; + + globals = malloc(sizeof(*globals)); + if (!globals) + return NULL; + + memset(globals, 0, sizeof(*globals)); + + globals->opmode = OPMODE_CLIENT; + globals->source = SOURCE_GPSD; + globals->gpsd_format = FORMAT_JSON; + + while ((opt = getopt_long(argc, argv, "shl:g:v", long_options, + &opt_ind)) != -1) { + switch (opt) { + case 's': + globals->opmode = OPMODE_SERVER; + break; + case 'l': + globals->source = SOURCE_CMDLINE; + gpsd_parse_location(globals, optarg); + break; + case 'g': + gpsd_source_spec(optarg, &globals->gpsdsource); + have_source = true; + break; + case 'v': + printf("%s %s\n", argv[0], SOURCE_VERSION); + printf("GPSD alfred client\n"); + return NULL; + case 'h': + default: + gpsd_usage(); + return NULL; + } + } + + if (globals->source == SOURCE_GPSD && !have_source) + gpsd_source_spec(NULL, &globals->gpsdsource); + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + fprintf(stderr, "could not register SIGPIPE handler\n"); + return globals; +} + +static int gpsd_server(struct globals *globals) +{ + struct timeval tv; + fd_set fds; + int max_fd, ret; + const size_t overhead = sizeof(*globals->push) + + sizeof(struct alfred_data); + long interval; + + globals->push = (struct alfred_push_data_v0 *) globals->buf; + globals->gpsd_data = (struct gpsd_v1 *) + (globals->buf + overhead); + + globals->push->header.type = ALFRED_PUSH_DATA; + globals->push->header.version = ALFRED_VERSION; + globals->push->tx.id = 0; + globals->push->tx.seqno = 0; + globals->push->data->header.type = GPSD_PACKETTYPE; + globals->push->data->header.version = GPSD_PACKETVERSION; + + strcpy(globals->gpsd_data->tpv, GPSD_INIT_TPV); + globals->gpsd_data->tpv_len = + htonl(strlen(globals->gpsd_data->tpv) + 1); + + /* If we have a static location, we don't need to update very + often. */ + if (globals->source == SOURCE_GPSD) { + globals->gpsdata.gps_fd = -1; + interval = 2; + } else + interval = 60 * 5; + + while (1) { + gpsd_update_data(globals); + + /* If we are not connected to gpsd, try to connect. */ + if (globals->source == SOURCE_GPSD && + globals->gpsdata.gps_fd == -1) { + gpsd_connect_gpsd(globals); + } + + /* Use linux's select(2) behaviour of setting + tv to the remaining time when it exists */ + tv.tv_sec = interval; + tv.tv_usec = 0; + + do { + FD_ZERO(&fds); + + if (globals->source == SOURCE_GPSD && + globals->gpsdata.gps_fd != -1) { + FD_SET(globals->gpsdata.gps_fd, &fds); + max_fd = globals->gpsdata.gps_fd + 1; + } else { + max_fd = 0; + } + + errno = 0; + ret = select(max_fd, &fds, NULL, NULL, &tv); + if (ret == -1 && errno != EINTR) + printf("select error %s(%d)\n", + strerror(errno), errno); + + if (ret == 1) + gpsd_read_gpsd(globals); + } while (ret != 0); + } + return EXIT_FAILURE; +} + +int main(int argc, char *argv[]) +{ + struct globals *globals; + + globals = gpsd_init(argc, argv); + + if (!globals) + return EXIT_FAILURE; + + switch (globals->opmode) { + case OPMODE_SERVER: + return gpsd_server(globals); + break; + case OPMODE_CLIENT: + return gpsd_get_data(globals); + break; + } + + return EXIT_FAILURE; +} diff --git a/gpsd/alfred-gpsd.h b/gpsd/alfred-gpsd.h new file mode 100644 index 0000000..206561e --- /dev/null +++ b/gpsd/alfred-gpsd.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Andrew Lunn, Simon Wunderlich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/ether.h> +#include <netinet/in.h> +#include <gps.h> +#include "../packet.h" +#include "../list.h" + +#ifndef SOURCE_VERSION +#define SOURCE_VERSION "2013.4.0" +#endif + +#define ALFRED_SOCK_PATH "/var/run/alfred.sock" +#define PATH_BUFF_LEN 200 +#define GPSD_PACKETTYPE 2 +#define GPSD_PACKETVERSION 1 +#define UPDATE_INTERVAL 10 + +enum opmode { + OPMODE_SERVER, + OPMODE_CLIENT +}; + +enum source { + SOURCE_CMDLINE, + SOURCE_GPSD +}; + +enum gpsd_format { + FORMAT_JSON +}; + +#define JSON_DATE_MAX 24 /* ISO8601 timestamp with 2 decimal places */ + +struct gpsd_v1 { + uint32_t tpv_len; + __extension__ char tpv[0]; +} __packed; + +#define GPSD_INIT_TPV "{"class":"TPV","mode":0}" + +#define GPSD_DATA_SIZE(gpsd_data) \ + (sizeof(*gpsd_data) + (ntohl(gpsd_data->tpv_len))) + +/* struct taken from gpsdclient.h */ +struct fixsource_t +{ + char *spec; /* pointer to actual storage */ + char *server; + char *port; + char *device; +}; + +struct globals { + enum opmode opmode; + enum source source; + enum gpsd_format gpsd_format; + uint8_t buf[65536]; + + /* internal pointers into buf */ + struct alfred_request_v0 *request; + struct alfred_push_data_v0 *push; + struct gpsd_v1 *gpsd_data; + + float lat, lon, alt; + int unix_sock; + + struct fixsource_t gpsdsource; + struct gps_data_t gpsdata; + char * tpv; +};
Hello Andrew (and the rest of the list),
On Tue, Oct 1, 2013, at 22:23, Andrew Lunn wrote:
+Alfred-gpsd +-----------
+Alfred-gpsd can be used to distibute GPS location information about +your batman-adv mesh network. This information could be, for example, +combined with Vis to visualize your mesh topology with true geographic +layout. For mobile or nomadic nodes, Alfred-gpsd, can get location +information from gpsd. Alternatively, a static location can be passed +on the command line, which is useful for static nodes without a GPS.
+Alfred-gpsd, similar to to alfred, combines server (daemon) and client +functionality in the 'alfred-gpsd' binary. The alfred-gpsd server must +be started to distribute location information. When retrieving +location information from gpsd, it should be started with:
- $ alfred-gpsd -s
+For a static location, use:
- $ alfred-gpsd -s -l 48.858222,2.2945,358
+This server will set the location in alfred via unix +socket. Obviously, the alfred server must run too to get this +information set. When using gpsd, it updates alfred every 2 +seconds. With a static location, the update it made every 5 minutes.
+To get JSON formatted output, use:
- $ alfred-gpsd
+[
- { "source" : "f6:00:48:13:d3:1e", "tpv" :
{"class":"TPV","tag":"RMC","device":"/dev/ttyACM0","mode":3,"time":"2013-10-01T10:43:20.000Z","ept":0.005,"lat":52.575485000,"lon":-1.339716667,"alt":122.500,"epx":10.199,"epy":15.720,"epv":31.050,"track":0.0000,"speed":0.010,"climb":0.000,"eps":31.44} },
- { "source" : "8e:4c:77:b3:65:b4", "tpv" :
{"class":"TPV","device":"command line","time":"2013-10-01T10:43:05.129Z","lat":48.858222,"lon":2.2945,"alt":358.000000,"mode":3} } +]
+See gpsd_json(5) for documentation of the tpv object.
As there are many different ways to display GPS coordinates, I suggest adding the format in which the data will be reported to the documentation. In my time playing with gps positioning (just a couple of weeks), I've run into at least 4 different latitude/longitude formats, most of which look very much alike, but all of which are incompatible with each other, so it's important to know which format is used.
Specifically, gpsd parses (standard) NMEA messages, which give position as hybrid of integer degrees and decimal arc minutes (ddmm.mmmm) followed by a direction (NSEW). When displayed in raw mode, gpsd will return these NMEA formatted values; the JSON output however (which is used) returns pure decimal degrees (dd.dddddd), with the sign specifying direction (NW are positive, SE are negative if I'm not mistaking).
I know the format is specified in the gpsd_json man page (which is referenced), but it seems more user-friendly to me to have the output format described here as well. Seeing as there is a member gpsd_format, I'm assuming this is meant to allow for different formatting in the future, which makes it even more important to explicitly state how the values are formatted.
Other than that, this looks very promising.
On Wed, Oct 02, 2013 at 09:25:31AM +0200, Thijs van Veen wrote:
Hello Andrew (and the rest of the list),
On Tue, Oct 1, 2013, at 22:23, Andrew Lunn wrote:
+Alfred-gpsd +-----------
+Alfred-gpsd can be used to distibute GPS location information about +your batman-adv mesh network. This information could be, for example, +combined with Vis to visualize your mesh topology with true geographic +layout. For mobile or nomadic nodes, Alfred-gpsd, can get location +information from gpsd. Alternatively, a static location can be passed +on the command line, which is useful for static nodes without a GPS.
+Alfred-gpsd, similar to to alfred, combines server (daemon) and client +functionality in the 'alfred-gpsd' binary. The alfred-gpsd server must +be started to distribute location information. When retrieving +location information from gpsd, it should be started with:
- $ alfred-gpsd -s
+For a static location, use:
- $ alfred-gpsd -s -l 48.858222,2.2945,358
+This server will set the location in alfred via unix +socket. Obviously, the alfred server must run too to get this +information set. When using gpsd, it updates alfred every 2 +seconds. With a static location, the update it made every 5 minutes.
+To get JSON formatted output, use:
- $ alfred-gpsd
+[
- { "source" : "f6:00:48:13:d3:1e", "tpv" :
{"class":"TPV","tag":"RMC","device":"/dev/ttyACM0","mode":3,"time":"2013-10-01T10:43:20.000Z","ept":0.005,"lat":52.575485000,"lon":-1.339716667,"alt":122.500,"epx":10.199,"epy":15.720,"epv":31.050,"track":0.0000,"speed":0.010,"climb":0.000,"eps":31.44} },
- { "source" : "8e:4c:77:b3:65:b4", "tpv" :
{"class":"TPV","device":"command line","time":"2013-10-01T10:43:05.129Z","lat":48.858222,"lon":2.2945,"alt":358.000000,"mode":3} } +]
+See gpsd_json(5) for documentation of the tpv object.
As there are many different ways to display GPS coordinates, I suggest adding the format in which the data will be reported to the documentation.
This would be quite a bit of information, so i would probably put in into an alfred-gpsd specific README, not the Alfred README.
In my time playing with gps positioning (just a couple of weeks), I've run into at least 4 different latitude/longitude formats, most of which look very much alike, but all of which are incompatible with each other, so it's important to know which format is used.
Specifically, gpsd parses (standard) NMEA messages, which give position as hybrid of integer degrees and decimal arc minutes (ddmm.mmmm) followed by a direction (NSEW). When displayed in raw mode, gpsd will return these NMEA formatted values; the JSON output however (which is used) returns pure decimal degrees (dd.dddddd), with the sign specifying direction (NW are positive, SE are negative if I'm not mistaking).
I know the format is specified in the gpsd_json man page (which is referenced), but it seems more user-friendly to me to have the output format described here as well.
The gpsd_json documentation is not very specific:
├───────┼─────────┼─────────┼────────────────────────┤ │lat │ No │ numeric │ Latitude in degrees: │ │ │ │ │ +/- signifies │ │ │ │ │ West/East. Present │ │ │ │ │ when mode is 2 │ │ │ │ │ or 3. │ ├───────┼─────────┼─────────┼────────────────────────┤ │lon │ No │ numeric │ Longitude in degrees: │ │ │ │ │ +/- signifies │ │ │ │ │ North/South. Present │ │ │ │ │ when mode is 2 │ │ │ │ │ or 3. │ ├───────┼─────────┼─────────┼────────────────────────┤
It just says degrees, not "pure" degrees, "not hybrid" degrees, etc. So importing the gpsd documentation does not really clarify it more. I'm also hesitant to add additional documentation, above what gpsd_json says. I've just implemented a "dumb pipe". I don't interpret the data in any way, i don't guarantee that the format will never change etc.
Seeing as there is a member gpsd_format, I'm assuming this is meant to allow for different formatting in the future, which makes it even more important to explicitly state how the values are formatted.
No, that is not why i added the tpv gpsd_format. I will probably need the ATT, the vehicle-attitude report, to report what direction my directional antennae are pointing. I've no intentional of adding other location formats, eg raw NMEA. That just added complexity i don't want to handle.
Other than that, this looks very promising.
Thanks Andrew
On Tue, Oct 01, 2013 at 10:23:52PM +0200, Andrew Lunn wrote:
CHANGELOG | 3 + Makefile | 24 ++- README | 52 +++++- gpsd/Makefile | 81 ++++++++ gpsd/alfred-gpsd.c | 529 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gpsd/alfred-gpsd.h | 102 +++++++++++ 6 files changed, 784 insertions(+), 7 deletions(-) create mode 100644 gpsd/Makefile create mode 100644 gpsd/alfred-gpsd.c create mode 100644 gpsd/alfred-gpsd.h
diff --git a/CHANGELOG b/CHANGELOG index aa0371f..404f3f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,3 +11,6 @@ alfred 2013.3.0:
- exchanges data via IPv6 unicast/multicast
-- Sun, 28 Jul 2013 18:56:52 +0200
- Add support for distributing geographic location information,
- either fixed, or read from gpsd.
I'd like to do the CHANGELOG the other way around, e.g. newest entries come first - and your changes will not be released in 2013.3.0 or before. :)
Please just drop this change as I'll compile the CHANGELOG when releasing myself anyway. No need to bother. :)
\ No newline at end of file [...] --- /dev/null +++ b/gpsd/Makefile @@ -0,0 +1,81 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# +# Copyright (C) 2013 B.A.T.M.A.N. contributors +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA +#
+# alfred build +BINARY_NAME = alfred-gpsd +OBJ = alfred-gpsd.o
+# alfred flags and options +CFLAGS += -pedantic -Wall -W -std=gnu99 -fno-strict-aliasing -MD -MP +LDLIBS += -lgps
I have no idea if we need some special handling for this to let it work for crosscompiling, OpenWRT etc?
+# disable verbose output +ifneq ($(findstring $(MAKEFLAGS),s),s) +ifndef V
- Q_CC = @echo ' ' CC $@;
- Q_LD = @echo ' ' LD $@;
- export Q_CC
- export Q_LD
+endif +endif [...]
- if ((alt < -1000) || (lon > 9000)) {
/* No support for aircraft or submarines! */
Too bad :D
I haven't tested this, but I like the idea to transport the gpsd information. We will probably seperate the package to let OpenWRT users choose if they want to have this feature (because it comes with gpsd dependency), just as we have done with vis. TBH I didn't expect to fill the alfred repository with feature daemons, but as long as it is maintained, why not. :)
Thanks, Simon
On Wednesday 02 October 2013 06:19:15 Simon Wunderlich wrote:
+# alfred build +BINARY_NAME = alfred-gpsd +OBJ = alfred-gpsd.o
+# alfred flags and options +CFLAGS += -pedantic -Wall -W -std=gnu99 -fno-strict-aliasing -MD -MP +LDLIBS += -lgps
I have no idea if we need some special handling for this to let it work for crosscompiling, OpenWRT etc?
Please use pkg-config for things like this. An example Makefile is attached.
@Simon: Please fix your e-mail adress inside the README file.
Kind regards, Sven
On Wed, Oct 02, 2013 at 06:34:15PM +0200, Sven Eckelmann wrote:
On Wednesday 02 October 2013 06:19:15 Simon Wunderlich wrote:
+# alfred build +BINARY_NAME = alfred-gpsd +OBJ = alfred-gpsd.o
+# alfred flags and options +CFLAGS += -pedantic -Wall -W -std=gnu99 -fno-strict-aliasing -MD -MP +LDLIBS += -lgps
I have no idea if we need some special handling for this to let it work for crosscompiling, OpenWRT etc?
Please use pkg-config for things like this. An example Makefile is attached.
Hi Sven
Thanks for the Makefile. Works on my Debian system.
Andrew
On Wed, Oct 02, 2013 at 06:34:15PM +0200, Sven Eckelmann wrote:
On Wednesday 02 October 2013 06:19:15 Simon Wunderlich wrote:
+# alfred build +BINARY_NAME = alfred-gpsd +OBJ = alfred-gpsd.o
+# alfred flags and options +CFLAGS += -pedantic -Wall -W -std=gnu99 -fno-strict-aliasing -MD -MP +LDLIBS += -lgps
I have no idea if we need some special handling for this to let it work for crosscompiling, OpenWRT etc?
Please use pkg-config for things like this. An example Makefile is attached.
@Simon: Please fix your e-mail adress inside the README file.
Done, applied in 9021511.
Thanks for pointing that out. Simon
b.a.t.m.a.n@lists.open-mesh.org