aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Ranostay <matt.ranostay@konsulko.com>2017-08-25 09:54:17 -0700
committerMatt Ranostay <matt.ranostay@konsulko.com>2017-08-28 10:50:57 -0700
commit344f115511abc18c0156c2784c2fcc50e0d8306d (patch)
treedd34b988e31bb3d2d53e2199b022163e60dcfda9
parent94af7aa13b923fb96c152c4cb4251fe415271a91 (diff)
binding: gps: rewrite binding to use libgps
Rewrite gps binding to use libgps native functionality to communicate with gps over SOCK_STREAM socket. Bug-AGL: SPEC-844 Change-Id: I78d2d14900d69b542e8bd456df577ee9ecdcb103 Signed-off-by: Matt Ranostay <matt.ranostay@konsulko.com>
-rw-r--r--LICENSE54
-rw-r--r--binding/afm-gps-binding.c1066
-rw-r--r--conf.d/cmake/config.cmake1
3 files changed, 150 insertions, 971 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..31c692a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,54 @@
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+ You must give any other recipients of the Work or Derivative Works a copy of this License; and
+ You must cause any modified files to carry prominent notices stating that You changed the files; and
+ You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+ If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/binding/afm-gps-binding.c b/binding/afm-gps-binding.c
index 080f1e5..e7cbf8f 100644
--- a/binding/afm-gps-binding.c
+++ b/binding/afm-gps-binding.c
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2016 "IoT.bzh"
- * Author José Bollo <jose.bollo@iot.bzh>
+ * Copyright (C) 2017 Konsulko Group
+ * Author: Matt Ranostay <matt.ranostay@konsulko.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,1036 +16,160 @@
*/
#define _GNU_SOURCE
+#include <errno.h>
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
-#include <fcntl.h>
-#include <math.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
+#include <gps.h>
+#include <pthread.h>
#include <json-c/json.h>
-#include <systemd/sd-event.h>
-
+#define AFB_BINDING_VERSION 2
#include <afb/afb-binding.h>
-#include <afb/afb-service-itf.h>
-
-#define NAUTICAL_MILE_IN_METER 1852
-#define MILE_IN_METER 1609.344
-#define KNOT_TO_METER_PER_SECOND 0.5144444444 /* 1852 / 3600 */
-#define METER_PER_SECOND_TO_KNOT 1.943844492 /* 3600 / 1852 */
-#define METER_PER_SECOND_TO_KILOMETER_PER_HOUR 3.6 /* 3600 / 1000 */
-#define METER_PER_SECOND_TO_MILE_PER_HOUR 2.236936292 /* 3600 / 1609.344 */
-
-#define DEFAULT_PERIOD 2000 /* 2 seconds */
-
-/*
- * references:
- *
- * https://www.w3.org/TR/geolocation-API/
- * http://www.gpsinformation.org/dale/nmea.htm
- */
-/* flags for recording what field is set */
-struct flags {
- unsigned time: 1;
- unsigned latitude: 1;
- unsigned longitude: 1;
- unsigned altitude: 1;
- unsigned speed: 1;
- unsigned track: 1;
-};
+static struct gps_data_t data;
+static struct afb_event location_event;
-/* the gps data converted */
-struct gps {
- struct flags set;
+static pthread_t thread;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- uint32_t time;
- double latitude;
- double longitude;
- double altitude;
- double speed;
- double track;
-};
+#define MSECS_TO_USECS(x) (x * 1000)
-/*
- * the type of position expected
- *
- * here, this type is mainly the selection of units
- */
-enum type {
- type_wgs84, /* longitude, latitude, track: degre, altitude: m, speed: m/s */
- type_dms_kmh, /* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: km/h */
- type_dms_mph, /* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: mph */
- type_dms_kn, /* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: kn */
- type_COUNT,
- type_DEFAULT = type_wgs84,
- type_INVALID = -1
-};
-
-struct event;
-
-/*
- * for each expected period
- */
-struct period {
- struct period *next; /* link to the next other period */
- struct event *events; /* events for the period */
- uint32_t period; /* value of the period in ms */
- uint32_t last; /* last update of the period */
-};
-
-/*
- * each generated event
- */
-struct event {
- struct event *next; /* link for the same period */
- const char *name; /* name of the event */
- struct afb_event event; /* the event for the binder */
- enum type type; /* the type of data expected */
- int id; /* id of the event for unsubscribe */
-};
-
-/*
- * names of the types
- */
-static const char * const type_NAMES[type_COUNT] = {
- "WGS84",
- "DMS.km/h",
- "DMS.mph",
- "DMS.kn"
-};
-
-/*
- * the interface to afb-daemon
- */
-const struct afb_binding_interface *afbitf;
-
-/*
- * records the raw frames
- */
-static struct gps frames[10]; /* a short memory for further computation if needed */
-static int frameidx; /* index of the last frame (frames are in the reverse order) */
-static int newframes; /* boolean indication of wether new frames are availables */
-
-/*
- * records the JSON object for sending positions
- */
-static struct json_object *time_ms; /* time as double in millisecond */
-static struct json_object *latitude_wgs; /* latitude as double in degree */
-static struct json_object *longitude_wgs; /* longitude as double in degree */
-static struct json_object *latitude_dms; /* latitude as string in d°m's.s"X */
-static struct json_object *longitude_dms; /* longitude as string in d°m's.s"X */
-static struct json_object *altitude_m; /* altitude as double in meter */
-static struct json_object *speed_ms; /* speed as double in m/s */
-static struct json_object *speed_kmh; /* speed as double in km/h */
-static struct json_object *speed_mph; /* speed as double in mph */
-static struct json_object *speed_kn; /* speed as double in kn */
-static struct json_object *track_d; /* heading track as double in degree */
-
-static struct json_object *positions[type_COUNT]; /* computed positions by type */
-
-/* head of the list of periods */
-static struct period *list_of_periods;
-
-/***************************************************************************************/
-/***************************************************************************************/
-/** **/
-/** **/
-/** SECTION: FORMATING JSON POSITIONS **/
-/** **/
-/** **/
-/***************************************************************************************/
-/***************************************************************************************/
-/*
- * Creates the JSON representation for Degree Minute Second representation of coordinates
- */
-static struct json_object *new_dms(double a, int islat)
+static json_object *populate_json_data(json_object *jresp)
{
- char buffer[50], pos;
- double D, M;
+ json_object *value = NULL;
- if (islat) {
- if (a >= 0)
- pos = 'N';
- else {
- a = -a;
- pos = 'S';
- }
- } else {
- if (a <= 180)
- pos = 'E';
- else {
- a = 360 - a;
- pos = 'W';
- }
+ if (data.fix.mode != MODE_3D) {
+ json_object_put(jresp);
+ return NULL;
}
- D = floor(a);
- a = (a - D) * 60;
- M = floor(a);
- a = (a - M) * 60;
- sprintf(buffer, "%d°%d'%.3f\"%c", (int)D, (int)M, a, pos);
- return json_object_new_string(buffer);
-}
-
-/*
- * adds the value (with reference count increment) if not null
- */
-static void addif(struct json_object *obj, const char *name, struct json_object *val)
-{
- if (val != NULL)
- json_object_object_add(obj, name, json_object_get(val));
-}
-
-/*
- * release the object (put) and reset the pointer to null
- */
-static void clear(struct json_object **obj)
-{
- json_object_put(*obj);
- *obj = NULL;
-}
-
-/*
- * get the last/current position of type
- */
-static struct json_object *position(enum type type)
-{
- struct json_object *result;
- struct gps *g0;
- /* clean on new frame */
- if (newframes) {
- clear(&time_ms);
- clear(&latitude_wgs);
- clear(&longitude_wgs);
- clear(&latitude_dms);
- clear(&longitude_dms);
- clear(&altitude_m);
- clear(&speed_ms);
- clear(&speed_kmh);
- clear(&speed_mph);
- clear(&speed_kn);
- clear(&track_d);
- clear(&positions[type_wgs84]);
- clear(&positions[type_dms_kmh]);
- clear(&positions[type_dms_mph]);
- clear(&positions[type_dms_kn]);
- newframes = 0;
+ if (data.set & ALTITUDE_SET) {
+ value = json_object_new_double(data.fix.altitude);
+ json_object_object_add(jresp, "altitude", value);
}
- /* get the result */
- result = positions[type];
- if (result == NULL) {
- DEBUG(afbitf, "building position for type %s", type_NAMES[type]);
+ if (data.set & LATLON_SET) {
+ value = json_object_new_double(data.fix.latitude);
+ json_object_object_add(jresp, "latitude", value);
- /* should build the result */
- g0 = &frames[frameidx];
- result = json_object_new_object();
- if (result == NULL)
- return NULL;
- positions[type] = result;
-
- /* set the result type */
- json_object_object_add(result, "type", json_object_new_string(type_NAMES[type]));
-
- /* build time, altitude and track */
- if (time_ms == NULL && g0->set.time)
- time_ms = json_object_new_double (g0->time);
- addif(result, "time", time_ms);
- if (altitude_m == NULL && g0->set.altitude)
- altitude_m = json_object_new_double (g0->altitude);
- addif(result, "altitude", altitude_m);
- if (track_d == NULL && g0->set.track)
- track_d = json_object_new_double (g0->track);
- addif(result, "track", track_d);
-
- /* build position */
- switch (type) {
- default:
- case type_wgs84:
- if (latitude_wgs == NULL && g0->set.latitude)
- latitude_wgs = json_object_new_double (g0->latitude);
- addif(result, "latitude", latitude_wgs);
- if (longitude_wgs == NULL && g0->set.longitude)
- longitude_wgs = json_object_new_double (g0->longitude);
- addif(result, "longitude", longitude_wgs);
- break;
- case type_dms_kmh:
- case type_dms_mph:
- case type_dms_kn:
- if (latitude_dms == NULL && g0->set.latitude)
- latitude_dms = new_dms (g0->latitude, 1);
- addif(result, "latitude", latitude_dms);
- if (longitude_dms == NULL && g0->set.longitude)
- longitude_dms = new_dms (g0->longitude, 0);
- addif(result, "longitude", longitude_dms);
- break;
- }
-
- /* build speed */
- switch (type) {
- default:
- case type_wgs84:
- if (speed_ms == NULL && g0->set.speed)
- speed_ms = json_object_new_double (g0->speed);
- addif(result, "speed", speed_ms);
- break;
- case type_dms_kmh:
- if (speed_kmh == NULL && g0->set.speed)
- speed_kmh = json_object_new_double (g0->speed * METER_PER_SECOND_TO_KILOMETER_PER_HOUR);
- addif(result, "speed", speed_kmh);
- break;
- case type_dms_mph:
- if (speed_mph == NULL && g0->set.speed)
- speed_mph = json_object_new_double (g0->speed * METER_PER_SECOND_TO_MILE_PER_HOUR);
- addif(result, "speed", speed_mph);
- break;
- case type_dms_kn:
- if (speed_kn == NULL && g0->set.speed)
- speed_kn = json_object_new_double (g0->speed * METER_PER_SECOND_TO_KNOT);
- addif(result, "speed", speed_kn);
- break;
- }
- }
-
- return json_object_get(result);
-}
-
-/***************************************************************************************/
-/***************************************************************************************/
-/** **/
-/** **/
-/** SECTION: MANAGING EVENTS **/
-/** **/
-/** **/
-/***************************************************************************************/
-/***************************************************************************************/
-/*
- * get the event handler of given id
- */
-static struct event *event_of_id(int id)
-{
- struct period *p;
- struct event *e;
-
- p = list_of_periods;
- while(p != NULL) {
- e = p->events;
- p = p->next;
- while(e != NULL) {
- if (e->id == id)
- return e;
- e = e->next;
- }
- }
- return NULL;
-}
-
-/*
- * get the event handler for the type and the period
- */
-static struct event *event_get(enum type type, int period)
-{
- static int id;
- int shift;
- uint32_t perio;
- struct period *p, **pp, *np;
- struct event *e;
-
- /* normalize the period */
- period = period <= 100 ? 1 : period > 60000 ? 600 : (period / 100);
- shift = 0;
- while((period >> shift) > 31)
- shift++;
- perio = (uint32_t)(100 * (((period >> shift) & 31) << shift));
-
- /* search for the period */
- pp = &list_of_periods;
- p = *pp;
- while(p != NULL && p->period < perio) {
- pp = &p->next;
- p = *pp;
+ value = json_object_new_double(data.fix.longitude);
+ json_object_object_add(jresp, "longitude", value);
}
- /* create the period if it misses */
- if (p == NULL || p->period != perio) {
- np = calloc(1, sizeof *p);
- if (np == NULL)
- return NULL;
- np->next = p;
- np->period = perio;
- *pp = np;
- p = np;
+ if (data.set & SPEED_SET) {
+ value = json_object_new_double(data.fix.speed);
+ json_object_object_add(jresp, "speed", value);
}
- /* search the type */
- e = p->events;
- while(e != NULL && e->type != type)
- e = e->next;
-
- /* creates the type if needed */
- if (e == NULL) {
- e = calloc(1, sizeof *e);
- if (e == NULL)
- return NULL;
-
- e->name = "GPS"; /* TODO */
- e->event = afb_daemon_make_event(afbitf->daemon, e->name);
- if (e->event.itf == NULL) {
- free(e);
- return NULL;
- }
-
- e->next = p->events;
- e->type = type;
- do {
- id++;
- if (id < 0)
- id = 1;
- } while(event_of_id(id) != NULL);
- e->id = id;
- p->events = e;
- }
-
- return e;
-}
-
-/*
- * Sends the events if needed
- */
-static void event_send()
-{
- struct period *p, **pp;
- struct event *e, **pe;
- struct timeval tv;
- uint32_t now;
-
- /* skip if nothing is new */
- if (!newframes)
- return;
-
- /* computes now */
- gettimeofday(&tv, NULL);
- now = (uint32_t)(tv.tv_sec * 1000) + (uint32_t)(tv.tv_usec / 1000);
-
- /* iterates over the periods */
- pp = &list_of_periods;
- p = *pp;
- while (p != NULL) {
- if (p->events == NULL) {
- /* no event for the period, frees it */
- *pp = p->next;
- free(p);
- } else {
- if (p->period <= now - p->last) {
- /* its time to refresh */
- p->last = now;
- pe = &p->events;
- e = *pe;
- while (e != NULL) {
- /* sends the event */
- if (afb_event_push(e->event, position(e->type)) != 0)
- pe = &e->next;
- else {
- /* no more listeners, free the event */
- *pe = e->next;
- afb_event_drop(e->event);
- free(e);
- }
- e = *pe;
- }
- }
- pp = &p->next;
- }
- p = *pp;
- }
+ return jresp;
}
-/***************************************************************************************/
-/***************************************************************************************/
-/** **/
-/** **/
-/** SECTION: HANDLING NMEA **/
-/** **/
-/** **/
-/***************************************************************************************/
-/***************************************************************************************/
-/*
- * interprets a nmea time
- */
-static int nmea_time(const char *text, uint32_t *result)
+static void get_data(struct afb_req request)
{
- uint32_t x;
+ json_object *jresp = NULL;
+ pthread_mutex_lock(&mutex);
- if (text[0] < '0' || text[0] > '2'
- || text[1] < '0' || text[1] > (text[0] == '2' ? '3' : '9')
- || text[2] < '0' || text[2] > '5'
- || text[3] < '0' || text[3] > '9'
- || text[4] < '0' || text[4] > '5'
- || text[5] < '0' || text[5] > '9'
- || (text[6] != 0 && text[6] != '.'))
- return 0;
-
- x = (uint32_t)(text[0] - '0');
- x = x * 10 + (uint32_t)(text[1]-'0');
- x = x * 6 + (uint32_t)(text[2]-'0');
- x = x * 10 + (uint32_t)(text[3]-'0');
- x = x * 6 + (uint32_t)(text[4]-'0');
- x = x * 10 + (uint32_t)(text[5]-'0');
- x = x * 1000;
- if (text[6] == '.') {
- if (text[7] != 0) {
- if (text[7] < '0' || text[7] > '9') return 0;
- x += (uint32_t)(text[7]-'0') * 100;
- if (text[8] != 0) {
- if (text[8] < '0' || text[8] > '9') return 0;
- x += (uint32_t)(text[8]-'0') * 10;
- if (text[9] != 0) {
- if (text[9] < '0' || text[9] > '9') return 0;
- x += (uint32_t)(text[9]-'0');
- if (text[10] != 0) {
- if (text[10] < '0' || text[10] > '9') return 0;
- x += text[10] > '5';
- }
- }
- }
- }
+ jresp = populate_json_data(json_object_new_object());
+ if (jresp != NULL) {
+ afb_req_success(request, jresp, "GNSS location data");
+ } else {
+ afb_req_fail(request, "failed", "No 3D GNSS fix");
}
- *result = x;
- return 1;
+ pthread_mutex_unlock(&mutex);
}
-/*
- * interprets a nmea angle having minutes
- */
-static int nmea_angle(const char *text, double *result)
+static void subscribe(struct afb_req request)
{
- uint32_t x = 0;
- double v;
- int dotidx = (int)(strchrnul(text, '.') - text);
-
- switch(dotidx) {
- case 5:
- if (text[dotidx - 5] < '0' || text[dotidx - 5] > '9')
- return 0;
- x = x * 10 + (uint32_t)(text[dotidx - 5] - '0');
- case 4:
- if (text[dotidx - 4] < '0' || text[dotidx - 4] > '9')
- return 0;
- x = x * 10 + (uint32_t)(text[dotidx - 4] - '0');
- case 3:
- if (text[dotidx - 3] < '0' || text[dotidx - 3] > '9')
- return 0;
- x = x * 10 + (uint32_t)(text[dotidx - 3] - '0');
- case 2:
- v = atof(&text[dotidx - 2]);
- break;
- case 1:
- if (text[dotidx - 1] < '0' || text[dotidx - 1] > '9')
- return 0;
- case 0:
- v = atof(text);
- break;
- default:
- return 0;
- }
-
- *result = (double)x + v * 0.01666666666666666666666; /* 1 / 60 */
+ const char *value = afb_req_value(request, "value");
- return 1;
-}
-
-/*
- * creates a new position for the given optionnal fields
- * returns 1 if correct or 0 if a format error exists
- */
-static int nmea_set(
- const char *tim,
- const char *lat, const char *latu,
- const char *lon, const char *lonu,
- const char *alt, const char *altu,
- const char *spe,
- const char *tra,
- const char *dat
-)
-{
- struct gps gps;
-
- DEBUG(afbitf, "time=%s latitude=%s%s longitude=%s%s altitude=%s%s speed=%s track=%s date=%s",
- tim, lat, latu, lon, lonu, alt, altu, spe, tra, dat);
-
- /* get the time in milliseconds */
- if (tim == NULL)
- gps.set.time = 0;
- else {
- if (!nmea_time(tim, &gps.time))
- return 0;
- gps.set.time = 1;
- }
-
- /* get the latitude */
- if (lat == NULL || latu == NULL)
- gps.set.latitude = 0;
- else {
- if ((latu[0] != 'N' && latu[0] != 'S') || latu[1] != 0)
- return 0;
- if (!nmea_angle(lat, &gps.latitude))
- return 0;
- if (latu[0] == 'S')
- gps.latitude = -gps.latitude;
- gps.set.latitude = 1;
- }
-
- /* get the longitude */
- if (lon == NULL || lonu == NULL)
- gps.set.longitude = 0;
- else {
- if ((lonu[0] != 'E' && lonu[0] != 'W') || lonu[1] != 0)
- return 0;
- if (!nmea_angle(lon, &gps.longitude))
- return 0;
- if (lonu[0] == 'W')
- gps.longitude = 360.0 - gps.longitude;
- gps.set.longitude = 1;
- }
-
- /* get the altitude */
- if (alt == NULL || altu == NULL)
- gps.set.altitude = 0;
- else {
- if (altu[0] != 'M' || altu[1] != 0)
- return 0;
- gps.altitude = atof(alt);
- gps.set.altitude = 1;
- }
-
- /* get the speed */
- if (spe == NULL)
- gps.set.speed = 0;
- else {
- gps.speed = atof(spe) * KNOT_TO_METER_PER_SECOND;
- gps.set.speed = 1;
- }
-
- /* get the track */
- if (tra != NULL)
- gps.set.track = 0;
- else {
- gps.track = atof(tra);
- gps.set.track = 1;
- }
-
- /* push the frame */
- frameidx = (frameidx ? : (int)(sizeof frames / sizeof *frames)) - 1;
- frames[frameidx] = gps;
- newframes++;
-
- DEBUG(afbitf, "time:%d=%d latitude:%d=%g longitude:%d=%g altitude:%d=%g speed:%d=%g track:%d=%g",
- (int)gps.set.time, gps.set.time ? (int)gps.time : 0,
- (int)gps.set.latitude, gps.set.latitude ? gps.latitude : 0,
- (int)gps.set.longitude, gps.set.longitude ? gps.longitude : 0,
- (int)gps.set.altitude, gps.set.altitude ? gps.altitude : 0,
- (int)gps.set.speed, gps.set.speed ? gps.speed : 0,
- (int)gps.set.track, gps.set.track ? gps.track : 0
- );
-
- return 1;
-}
-
-/*
- * Splits the nmea sentences in its fields
- */
-static int nmea_split(char *s, char *fields[], int count)
-{
- int index = 0;
- while (*s && index < count) {
- fields[index++] = s;
- while (*s && *s != ',')
- s++;
- if (*s == ',')
- *s++ = 0;
+ if (value && !strcasecmp(value, "location")) {
+ afb_req_subscribe(request, location_event);
+ afb_req_success(request, NULL, NULL);
+ return;
}
- return !*s && index == count;
-}
-
-/*
- * interprete one sentence GGA - Fix information
- */
-static int nmea_gga(char *s)
-{
- char *f[14];
-
- return nmea_split(s, f, (int)(sizeof f / sizeof *f))
- && *f[5] != '0'
- && nmea_set(f[0], f[1], f[2], f[3], f[4], f[6], f[7], NULL, NULL, NULL);
-}
-
-/*
- * interprete one sentence RMC - Recommended Minimum
- */
-static int nmea_rmc(char *s)
-{
- char *f[12];
-
- return nmea_split(s, f, (int)(sizeof f / sizeof *f))
- && *f[1] == 'A'
- && nmea_set(f[0], f[2], f[3], f[4], f[5], NULL, NULL, f[6], f[7], f[8]);
-}
-
-
-/*
- * interprete one NMEA sentence
- */
-static int nmea_sentence(char *s)
-{
- if (!s[0] || !s[1])
- return 0;
-
- if (s[2] == 'G' && s[3] == 'G' && s[4] == 'A' && s[5] == ',')
- return nmea_gga(&s[6]);
-
- if (s[2] == 'R' && s[3] == 'M' && s[4] == 'C' && s[5] == ',')
- return nmea_rmc(&s[6]);
-
- return 0;
-}
-
-/*
- * reads the NMEA stream
- */
-static int nmea_read(int fd)
-{
- static char buffer[160];
- static int pos = 0;
- static int overflow = 0;
-
- int rc;
- for(;;) {
- rc = (int)read(fd, &buffer[pos], sizeof buffer - (size_t)pos);
- if (rc < 0) {
- /* its an error if not interrupted */
- if (errno != EINTR)
- return rc;
- } else if (rc == 0) {
- /* nothing more to be read */
- return 0;
- } else {
- /* scan the buffer */
- while (pos != rc) {
- if (buffer[pos] != '\n') {
- pos++;
- if (pos == rc) {
- if (pos == (int)(sizeof buffer)) {
- overflow = 1;
- pos = 0;
- rc = 0;
- }
- }
- } else {
- if (buffer[0] == '$' && pos > 0 && buffer[pos-1] == '\r' && !overflow) {
- if (pos > 3 && buffer[pos-4] == '*') {
- /* TODO: check the cheksum */
- buffer[pos-4] = 0;
- } else {
- buffer[pos-1] = 0;
- }
- nmea_sentence(&buffer[1]);
- }
- pos++;
- rc -= pos;
- if (rc > 0)
- memmove(buffer, buffer+pos, (size_t)rc);
- pos = 0;
- overflow = 0;
- }
- }
- }
- }
+ afb_req_fail(request, "failed", "Invalid event");
}
-/***************************************************************************************/
-/***************************************************************************************/
-/** **/
-/** **/
-/** SECTION: HANDLING OF CONNECTION **/
-/** **/
-/** **/
-/***************************************************************************************/
-/***************************************************************************************/
-/* declare the connection routine */
-static int connection();
-
-/*
- * called on an event on the NMEA stream
- */
-static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata)
+static void unsubscribe(struct afb_req request)
{
- /* read available data */
- if ((revents & EPOLLIN) != 0) {
- nmea_read(fd);
- event_send();
- }
+ const char *value = afb_req_value(request, "value");
- /* check if error or hangup */
- if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0) {
- sd_event_source_unref(s);
- close(fd);
- connection(fd);
+ if (value && !strcasecmp(value, "location")) {
+ afb_req_unsubscribe(request, location_event);
+ afb_req_success(request, NULL, NULL);
+ return;
}
- return 0;
+ afb_req_fail(request, "failed", "Invalid event");
}
-/*
- * opens a socket to a host and a service (or port)
- */
-static int open_socket_to(const char *host, const char *service)
+static void *data_poll(void *ptr)
{
- int rc, fd;
- struct addrinfo hint, *rai, *iai;
+ /*
+ * keep reading till an error condition happens
+ */
+ while (gps_waiting(&data, MSECS_TO_USECS(2000)) && !errno)
+ {
+ json_object *jresp = NULL;
- /* get addr */
- memset(&hint, 0, sizeof hint);
- hint.ai_family = AF_INET;
- hint.ai_socktype = SOCK_STREAM;
- rc = getaddrinfo(host, service, &hint, &rai);
- if (rc != 0)
- return -1;
-
- /* get the socket */
- iai = rai;
- while (iai != NULL) {
- fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol);
- if (fd >= 0) {
- rc = connect(fd, iai->ai_addr, iai->ai_addrlen);
- if (rc == 0) {
- fcntl(fd, F_SETFL, O_NONBLOCK);
- freeaddrinfo(rai);
- return fd;
- }
- close(fd);
+ pthread_mutex_lock(&mutex);
+ if (gps_read(&data) == -1) {
+ AFB_ERROR("Cannot read from GPS daemon.\n");
+ pthread_mutex_unlock(&mutex);
+ break;
}
- iai = iai->ai_next;
- }
- freeaddrinfo(rai);
- return -1;
-}
-
-/*
- * connection to nmea stream for the host and the port
- */
-static int connect_to(const char *host, const char *service, int isgpsd)
-{
- sd_event_source *source;
- int rc, fd;
- /* TODO connect to somewhere else */
- fd = open_socket_to(host, service);
- if (fd < 0) {
- ERROR(afbitf, "can't connect to host %s, service %s", host, service);
- return fd;
- }
- if (isgpsd) {
- static const char gpsdsetup[] = "?WATCH={\"enable\":true,\"nmea\":true};\r\n";
- write(fd, gpsdsetup, sizeof gpsdsetup - 1);
+ jresp = populate_json_data(json_object_new_object());
+ if (jresp != NULL)
+ afb_event_push(location_event, jresp);
+ pthread_mutex_unlock(&mutex);
}
- /* adds to the event loop */
- rc = sd_event_add_io(afb_daemon_get_event_loop(afbitf->daemon), &source, fd, EPOLLIN, on_event, NULL);
- if (rc < 0) {
- close(fd);
- ERROR(afbitf, "can't coonect host %s, service %s to the event loop", host, service);
- } else {
- NOTICE(afbitf, "Connected to host %s, service %s", host, service);
- }
- return rc;
-}
+ AFB_INFO("Closing GPS daemon connection.\n");
+ gps_stream(&data, WATCH_DISABLE, NULL);
+ gps_close(&data);
-/*
- * connection to nmea stream
- */
-static int connection()
-{
- const char *host;
- const char *service;
- int isgpsd;
+ abort();
- /* TODO connect to somewhere else */
- host = getenv("AFBGPS_HOST") ? : "sinagot.net";
- service = getenv("AFBGPS_SERVICE") ? : "5001";
- isgpsd = getenv("AFBGPS_ISNMEA") ? 0 : 1;
- return connect_to(host, service, isgpsd);
+ return NULL;
}
-/***************************************************************************************/
-/***************************************************************************************/
-/** **/
-/** **/
-/** SECTION: BINDING VERBS IMPLEMENTATION **/
-/** **/
-/** **/
-/***************************************************************************************/
-/***************************************************************************************/
-/*
- * Returns the type corresponding to the given name
- */
-static enum type type_of_name(const char *name)
+static int init()
{
- enum type result;
- if (name == NULL)
- return type_DEFAULT;
- for (result = 0 ; result != type_COUNT ; result++)
- if (strcmp(type_NAMES[result], name) == 0)
- return result;
- return type_INVALID;
-}
+ const char *host, *port;
+ int ret;
-/*
- * extract a valid type from the request
- */
-static int get_type_for_req(struct afb_req req, enum type *type)
-{
- if ((*type = type_of_name(afb_req_value(req, "type"))) != type_INVALID)
- return 1;
- afb_req_fail(req, "unknown-type", NULL);
- return 0;
-}
-
-/*
- * Get the last known position
- *
- * parameter of the get are:
- *
- * type: string: the type of position expected (defaults to "WGS84" if not present)
- *
- * returns the position
- *
- * The valid types are:
- *
- * +==========+=======================+=======+==========+=======+
- * | type | longitude & latitude | speed | altitude | track |
- * +==========+=======================+=======+==========+=======+
- * | WGS84 | degre | m/s | | |
- * +----------+-----------------------+-------+ | |
- * | DMS.km/h | | km/h | | |
- * +----------+ +-------+ meter | degre |
- * | DMS.mph | deg°min'sec"X | mph | | |
- * +----------+ +-------+ | |
- * | DMS.kn | | kn | | |
- * +==========+=======================+=======+==========+=======+
- */
-static void get(struct afb_req req)
-{
- enum type type;
- if (get_type_for_req(req, &type))
- afb_req_success(req, position(type), NULL);
-}
+ location_event = afb_daemon_make_event("location");
-/*
- * subscribe to notification of position
- *
- * parameters of the subscription are:
- *
- * type: string: the type of position expected (defaults to WCS84 if not present)
- * see the list above (get)
- * period: integer: the expected period in milliseconds (defaults to 2000 if not present)
- *
- * returns an object with 2 fields:
- *
- * name: string: the name of the event without its prefix
- * id: integer: a numeric identifier of the event to be used for unsubscribing
- */
-static void subscribe(struct afb_req req)
-{
- enum type type;
- const char *period;
- struct event *event;
- struct json_object *json;
+ host = getenv("AFBGPS_HOST") ? : "localhost";
+ port = getenv("AFBGPS_SERVICE") ? : "2947";
- if (get_type_for_req(req, &type)) {
- period = afb_req_value(req, "period");
- event = event_get(type, period == NULL ? DEFAULT_PERIOD : atoi(period));
- if (event == NULL)
- afb_req_fail(req, "out-of-memory", NULL);
- else if (afb_req_subscribe(req, event->event) != 0)
- afb_req_fail_f(req, "failed", "afb_req_subscribe returned an error: %m");
- else {
- json = json_object_new_object();
- json_object_object_add(json, "name", json_object_new_string(event->name));
- json_object_object_add(json, "id", json_object_new_int(event->id));
- afb_req_success(req, json, NULL);
- }
- }
-}
+ ret = gps_open(host, port, &data);
+ if (ret < 0)
+ return ret;
-/*
- * unsubscribe a previous subscription
- *
- * parameters of the unsubscription are:
- *
- * id: integer: the numeric identifier of the event as returned when subscribing
- */
-static void unsubscribe(struct afb_req req)
-{
- const char *id;
- struct event *event;
+ gps_stream(&data, WATCH_ENABLE | WATCH_JSON, NULL);
- id = afb_req_value(req, "id");
- if (id == NULL)
- afb_req_fail(req, "missing-id", NULL);
- else {
- event = event_of_id(atoi(id));
- if (event == NULL)
- afb_req_fail(req, "bad-id", NULL);
- else {
- afb_req_unsubscribe(req, event->event);
- afb_req_success(req, NULL, NULL);
- }
- }
+ return pthread_create(&thread, NULL, &data_poll, NULL);
}
-/*
- * array of the verbs exported to afb-daemon
- */
-static const struct afb_verb_desc_v1 binding_verbs[] = {
- /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */
- { .name= "get", .session= AFB_SESSION_NONE, .callback= get, .info= "get the last known data" },
- { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= subscribe, .info= "subscribe to notification of position" },
- { .name= "unsubscribe", .session= AFB_SESSION_NONE, .callback= unsubscribe, .info= "unsubscribe a previous subscription" },
- { .name= NULL } /* marker for end of the array */
+static const struct afb_verb_v2 binding_verbs[] = {
+ { .verb = "location", .callback = get_data, .info = "Get GNSS data" },
+ { .verb = "subscribe", .callback = subscribe, .info = "Subscribe to GNSS events" },
+ { .verb = "unsubscribe", .callback = unsubscribe, .info = "Unsubscribe to GNSS events" },
+ { }
};
/*
- * description of the binding for afb-daemon
+ * binder API description
*/
-static const struct afb_binding binding_description =
-{
- /* description conforms to VERSION 1 */
- .type= AFB_BINDING_VERSION_1,
- .v1= { /* fills the v1 field of the union when AFB_BINDING_VERSION_1 */
- .prefix= "gps", /* the API name (or binding name or prefix) */
- .info= "Access to the GPS data", /* short description of of the binding */
- .verbs = binding_verbs /* the array describing the verbs of the API */
- }
+const struct afb_binding_v2 afbBindingV2 = {
+ .api = "gps",
+ .specification = "GNSS/GPS API",
+ .verbs = binding_verbs,
+ .init = init,
};
-
-/*
- * activation function for registering the binding called by afb-daemon
- */
-const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf)
-{
- afbitf = itf; /* records the interface for accessing afb-daemon */
- return &binding_description; /* returns the description of the binding */
-}
-
-int afbBindingV1ServiceInit(struct afb_service service)
-{
- return connection();
-}
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index 861cb5b..2bfd53c 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -66,6 +66,7 @@ set (gcc_minimal_version 4.9)
# -----------------------------
set (PKG_REQUIRED_LIST
json-c
+ libgps
libsystemd>=222
afb-daemon
)