build: get rid of HTTPINFO_PUD define
[olsrd.git] / lib / pud / src / receiver.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 #include "receiver.h"
47
48 /* Plugin includes */
49 #include "pud.h"
50 #include "state.h"
51 #include "configuration.h"
52 #include "gpsConversion.h"
53 #include "networkInterfaces.h"
54 #include "timers.h"
55 #include "uplinkGateway.h"
56 #include "posFile.h"
57
58 /* OLSRD includes */
59 #include "net_olsr.h"
60
61 /* System includes */
62 #include <nmea/parser.h>
63 #include <nmea/gmath.h>
64 #include <nmea/sentence.h>
65 #include <nmea/context.h>
66 #include <OlsrdPudWireFormat/wireFormat.h>
67 #include <unistd.h>
68
69 /*
70  * NMEA parser
71  */
72
73 /** The NMEA string parser */
74 static nmeaPARSER nmeaParser;
75
76 /*
77  * State
78  */
79
80 /** Type describing movement calculations */
81 typedef struct _MovementType {
82         TristateBoolean moving; /**< SET: we are moving */
83
84         TristateBoolean overThresholds; /**< SET: at least 1 threshold state is set */
85         TristateBoolean speedOverThreshold; /**< SET: speed is over threshold */
86         TristateBoolean hDistanceOverThreshold; /**< SET: horizontal distance is outside threshold */
87         TristateBoolean vDistanceOverThreshold; /**< SET: vertical distance is outside threshold */
88
89         TristateBoolean outside; /**< SET: at least 1 outside state is SET */
90         TristateBoolean outsideHdop; /**< SET: avg is outside lastTx HDOP */
91         TristateBoolean outsideVdop; /**< SET: avg is outside lastTx VDOP */
92
93         TristateBoolean inside; /**< SET: all inside states are SET */
94         TristateBoolean insideHdop; /**< SET: avg is inside lastTx HDOP */
95         TristateBoolean insideVdop; /**< SET: avg is inside lastTx VDOP */
96 } MovementType;
97
98 /*
99  * Averaging
100  */
101
102 /** The average position with its administration */
103 static PositionAverageList positionAverageList;
104
105 /*
106  * TX to OLSR
107  */
108
109 typedef enum _TimedTxInterface {
110         TX_INTERFACE_OLSR = 1,
111         TX_INTERFACE_UPLINK = 2
112 } TimedTxInterface;
113
114 /** The latest position information that is transmitted */
115 static TransmitGpsInformation transmitGpsInformation;
116
117 /** The size of the buffer in which the OLSR and uplink messages are assembled */
118 #define TX_BUFFER_SIZE_FOR_OLSR 1024
119
120 /** the prefix of all parameters written to the position output file */
121 #define PUD_POSOUT_FILE_PARAM_PREFIX "OLSRD_PUD_"
122
123 /** the position output file */
124 static char * positionOutputFile = NULL;
125
126 /** true when a postion output file error was already reported */
127 static bool positionOutputFileError = false;
128
129 /*
130  * Functions
131  */
132
133 /**
134  Clear the MovementType
135  * @param result a pointer to the MovementType
136  */
137 static void clearMovementType(MovementType * result) {
138         /* clear outputs */
139         result->moving = TRISTATE_BOOLEAN_UNKNOWN;
140         result->overThresholds = TRISTATE_BOOLEAN_UNKNOWN;
141         result->speedOverThreshold = TRISTATE_BOOLEAN_UNKNOWN;
142         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_UNKNOWN;
143         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_UNKNOWN;
144         result->outside = TRISTATE_BOOLEAN_UNKNOWN;
145         result->outsideHdop = TRISTATE_BOOLEAN_UNKNOWN;
146         result->outsideVdop = TRISTATE_BOOLEAN_UNKNOWN;
147         result->inside = TRISTATE_BOOLEAN_UNKNOWN;
148         result->insideHdop = TRISTATE_BOOLEAN_UNKNOWN;
149         result->insideVdop = TRISTATE_BOOLEAN_UNKNOWN;
150 }
151
152 /**
153  Determine whether s position is valid.
154
155  @param position
156  a pointer to a position
157
158  @return
159  - true when valid
160  - false otherwise
161  */
162 static bool positionValid(PositionUpdateEntry * position) {
163         return (nmea_INFO_is_present(position->nmeaInfo.present, FIX)
164                         && (position->nmeaInfo.fix != NMEA_FIX_BAD));
165 }
166
167 /**
168  Send the transmit buffer out over all designated interfaces, called as a
169  timer callback and also immediately on an external state change.
170
171  @param interfaces
172  a bitmap defining which interfaces to send over
173  */
174 static void txToAllOlsrInterfaces(TimedTxInterface interfaces) {
175         /** txBuffer is used to concatenate the position update and cluster leader messages in */
176         unsigned char txBuffer[TX_BUFFER_SIZE_FOR_OLSR];
177         unsigned int txBufferBytesUsed = 0;
178         #define txBufferBytesFree       (sizeof(txBuffer) - txBufferBytesUsed)
179
180         /*
181          * The first message in txBuffer is an OLSR position update.
182          *
183          * The position update is always present.
184          *
185          * The second message is the cluster leader message, but only when uplink
186          * was requested and correctly configured.
187          */
188
189         UplinkMessage * pu_uplink = (UplinkMessage *) &txBuffer[0];
190         union olsr_message * pu = &pu_uplink->msg.olsrMessage;
191         unsigned int pu_size = 0;
192         union olsr_ip_addr gateway;
193         MovementState externalState;
194         nmeaINFO nmeaInfo;
195
196         externalState = getExternalState();
197
198         /* only update the timestamp when the position is invalid AND when the position was not updated */
199         if (!positionValid(&transmitGpsInformation.txPosition) && !transmitGpsInformation.positionUpdated) {
200                 nmea_time_now(&transmitGpsInformation.txPosition.nmeaInfo.utc, &transmitGpsInformation.txPosition.nmeaInfo.present);
201         }
202
203         nmeaInfo = transmitGpsInformation.txPosition.nmeaInfo;
204         transmitGpsInformation.positionUpdated = false;
205         gateway = transmitGpsInformation.txGateway;
206
207         /* convert nmeaINFO to wireformat olsr message */
208         txBufferBytesUsed += sizeof(UplinkHeader); /* keep before txBufferSpaceFree usage */
209         pu_size = gpsToOlsr(&nmeaInfo, pu, txBufferBytesFree,
210                         ((externalState == MOVEMENT_STATE_STATIONARY) ? getUpdateIntervalStationary() : getUpdateIntervalMoving()));
211         txBufferBytesUsed += pu_size;
212
213         /*
214          * push out to all OLSR interfaces
215          */
216         if (((interfaces & TX_INTERFACE_OLSR) != 0) && getOlsrTtl() && (pu_size > 0)) {
217                 int r;
218                 struct interface_olsr *ifn;
219                 for (ifn = ifnet; ifn; ifn = ifn->int_next) {
220                         /* force the pending buffer out if there's not enough space for our message */
221                         if ((int)pu_size > net_outbuffer_bytes_left(ifn)) {
222                           net_output(ifn);
223                         }
224                         r = net_outbuffer_push(ifn, pu, pu_size);
225                         if (r != (int) pu_size) {
226                                 pudError(
227                                                 false,
228                                                 "Could not send to OLSR interface %s: %s (size=%u, r=%d)",
229                                                 ifn->int_name,
230                                                 ((r == -1) ? "no buffer was found" :
231                                                         (r == 0) ? "there was not enough room in the buffer" : "unknown reason"), pu_size, r);
232                         }
233                 }
234
235                 /* loopback to tx interface when so configured */
236                 if (getUseLoopback()) {
237                         (void) packetReceivedFromOlsr(pu, NULL, NULL);
238                 }
239         }
240
241         /* push out over uplink when an uplink is configured */
242         if (((interfaces & TX_INTERFACE_UPLINK) != 0) && isUplinkAddrSet()) {
243                 int fd = getDownlinkSocketFd();
244                 if (fd != -1) {
245                         union olsr_sockaddr * uplink_addr = getUplinkAddr();
246                         void * addr;
247                         socklen_t addrSize;
248
249                         UplinkMessage * cl_uplink = (UplinkMessage *) &txBuffer[txBufferBytesUsed];
250                         UplinkClusterLeader * cl = &cl_uplink->msg.clusterLeader;
251                         union olsr_ip_addr * cl_originator = getClusterLeaderOriginator(olsr_cnf->ip_version, cl);
252                         union olsr_ip_addr * cl_clusterLeader = getClusterLeaderClusterLeader(olsr_cnf->ip_version, cl);
253
254                         unsigned int cl_size =
255                                         sizeof(UplinkClusterLeader) - sizeof(cl->leader)
256                                                         + ((olsr_cnf->ip_version == AF_INET) ? sizeof(cl->leader.v4) :
257                                                                         sizeof(cl->leader.v6));
258
259                         unsigned long long uplinkUpdateInterval =
260                                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() : getUplinkUpdateIntervalMoving();
261
262                         if (uplink_addr->in.sa_family == AF_INET) {
263                                 addr = &uplink_addr->in4;
264                                 addrSize = sizeof(struct sockaddr_in);
265                         } else {
266                                 addr = &uplink_addr->in6;
267                                 addrSize = sizeof(struct sockaddr_in6);
268                         }
269
270                         /*
271                          * position update message (pu)
272                          */
273
274                         /* set header fields in position update uplink message and adjust
275                          * the validity time to the uplink validity time */
276                         if (pu_size > 0) {
277                                 PudOlsrPositionUpdate * pu_gpsMessage = getOlsrMessagePayload(olsr_cnf->ip_version, pu);
278
279                                 setUplinkMessageType(&pu_uplink->header, POSITION);
280                                 setUplinkMessageLength(&pu_uplink->header, pu_size);
281                                 setUplinkMessageIPv6(&pu_uplink->header, (olsr_cnf->ip_version != AF_INET));
282                                 setUplinkMessagePadding(&pu_uplink->header, 0);
283
284                                 /* fixup validity time */
285                                 setValidityTime(&pu_gpsMessage->validityTime, uplinkUpdateInterval);
286                         }
287
288                         /*
289                          * cluster leader message (cl)
290                          */
291
292                         /* set cl_uplink header fields */
293                         setUplinkMessageType(&cl_uplink->header, CLUSTERLEADER);
294                         setUplinkMessageLength(&cl_uplink->header, cl_size);
295                         setUplinkMessageIPv6(&cl_uplink->header, (olsr_cnf->ip_version != AF_INET));
296                         setUplinkMessagePadding(&cl_uplink->header, 0);
297
298                         /* setup cl */
299                         setClusterLeaderVersion(cl, PUD_WIRE_FORMAT_VERSION);
300                         setValidityTime(&cl->validityTime, uplinkUpdateInterval);
301
302                         /* really need 2 memcpy's here because of olsr_cnf->ipsize */
303                         memcpy(cl_originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
304                         memcpy(cl_clusterLeader, &gateway, olsr_cnf->ipsize);
305
306                         txBufferBytesUsed += sizeof(UplinkHeader);
307                         txBufferBytesUsed += cl_size;
308
309                         errno = 0;
310                         if (sendto(fd, &txBuffer, txBufferBytesUsed, 0, addr, addrSize) < 0) {
311                                 /* do not report send errors, they're not really relevant */
312                         }
313                 }
314         }
315 }
316
317 /**
318  * Write the position output file (if so configured)
319  *
320  * @return true on success
321  */
322 static bool writePositionOutputFile(void) {
323   FILE * fp = NULL;
324   nmeaINFO * nmeaInfo;
325   const char * signal;
326   const char * fix;
327
328   if (!positionOutputFile) {
329     return true;
330   }
331
332   fp = fopen(positionOutputFile, "w");
333   if (!fp) {
334     /* could not open the file */
335     if (!positionOutputFileError) {
336       pudError(true, "Could not write to the position output file \"%s\"", positionOutputFile);
337       positionOutputFileError = true;
338     }
339     return false;
340   }
341   positionOutputFileError = false;
342
343   nmeaInfo = &transmitGpsInformation.txPosition.nmeaInfo;
344
345   /* node id */
346   fprintf(fp, "%s%s=\"%s\"\n", PUD_POSOUT_FILE_PARAM_PREFIX, "NODE_ID", transmitGpsInformation.nodeId);
347
348   /* sig */
349   switch (nmeaInfo->sig) {
350     case 0:
351       signal = "INVALID";
352       break;
353
354     case 1:
355       signal = "FIX";
356       break;
357
358     case 2:
359       signal = "DIFFERENTIAL";
360       break;
361
362     case 3:
363       signal = "SENSITIVE";
364       break;
365
366     case 4:
367       signal = "REALTIME";
368       break;
369
370     case 5:
371       signal = "FLOAT";
372       break;
373
374     case 6:
375       signal = "ESTIMATED";
376       break;
377
378     case 7:
379       signal = "MANUAL";
380       break;
381
382     case 8:
383       signal = "SIMULATED";
384       break;
385
386     default:
387       signal = NULL;
388       break;
389   }
390   if (!signal) {
391     fprintf(fp, "%s%s=%d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "SIGNAL", nmeaInfo->sig);
392   } else {
393     fprintf(fp, "%s%s=%s\n", PUD_POSOUT_FILE_PARAM_PREFIX, "SIGNAL", signal);
394   }
395
396   /* fix */
397   switch (nmeaInfo->fix) {
398     case 1:
399       fix = "NONE";
400       break;
401
402     case 2:
403       fix = "2D";
404       break;
405
406     case 3:
407       fix = "3D";
408       break;
409
410     default:
411       fix = NULL;
412       break;
413   }
414   if (!fix) {
415     fprintf(fp, "%s%s=%d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "FIX", nmeaInfo->fix);
416   } else {
417     fprintf(fp, "%s%s=%s\n", PUD_POSOUT_FILE_PARAM_PREFIX, "FIX", fix);
418   }
419
420   /* utc */
421   fprintf(fp, "%s%s=%04d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "YEAR", nmeaInfo->utc.year + 1900);
422   fprintf(fp, "%s%s=%02d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "MONTH", nmeaInfo->utc.mon + 1);
423   fprintf(fp, "%s%s=%02d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "DAY", nmeaInfo->utc.day);
424   fprintf(fp, "%s%s=%02d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "HOUR", nmeaInfo->utc.hour);
425   fprintf(fp, "%s%s=%02d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "MINUTE", nmeaInfo->utc.min);
426   fprintf(fp, "%s%s=%02d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "SECONDS", nmeaInfo->utc.sec);
427   fprintf(fp, "%s%s=%03d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "MILLISECONDS", nmeaInfo->utc.hsec * 10);
428
429   /* DOPs */
430   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "PDOP", nmeaInfo->PDOP);
431   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "HDOP", nmeaInfo->HDOP);
432   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "VDOP", nmeaInfo->VDOP);
433
434   /* lat, lon, ... */
435   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "LATTITUDE", nmeaInfo->lat);
436   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "LONGITUDE", nmeaInfo->lon);
437   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "ELEVATION", nmeaInfo->elv);
438   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "SPEED", nmeaInfo->speed);
439   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "TRACK", nmeaInfo->track);
440   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "MAGNETIC_TRACK", nmeaInfo->mtrack);
441   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "MAGNETIC_VARIATION", nmeaInfo->magvar);
442
443   fclose(fp);
444   return true;
445 }
446
447 /*
448  * Timer Callbacks
449  */
450
451 /**
452  The OLSR tx timer callback
453
454  @param context
455  unused
456  */
457 static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
458         txToAllOlsrInterfaces(TX_INTERFACE_OLSR);
459 }
460
461 /**
462  The uplink timer callback
463
464  @param context
465  unused
466  */
467 static void pud_uplink_timer_callback(void *context __attribute__ ((unused))) {
468         txToAllOlsrInterfaces(TX_INTERFACE_UPLINK);
469 }
470
471 /**
472  Restart the OLSR tx timer
473  */
474 static void restartOlsrTimer(MovementState externalState) {
475         if (!restartOlsrTxTimer(
476                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUpdateIntervalStationary() :
477                                         getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
478                 pudError(0, "Could not restart OLSR tx timer, no periodic"
479                                 " position updates will be sent to the OLSR network");
480         }
481 }
482
483 /**
484  Restart the uplink tx timer
485  */
486 static void restartUplinkTimer(MovementState externalState) {
487         if (!restartUplinkTxTimer(
488                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() :
489                                         getUplinkUpdateIntervalMoving(),
490                         &pud_uplink_timer_callback)) {
491                 pudError(0, "Could not restart uplink timer, no periodic"
492                                 " position updates will be uplinked");
493         }
494 }
495
496 static void doImmediateTransmit(MovementState externalState) {
497         TimedTxInterface interfaces = TX_INTERFACE_OLSR; /* always send over olsr */
498         restartOlsrTimer(externalState);
499
500         if (isUplinkAddrSet()) {
501                 interfaces |= TX_INTERFACE_UPLINK;
502                 restartUplinkTimer(externalState);
503         }
504
505         /* do an immediate transmit */
506         txToAllOlsrInterfaces(interfaces);
507
508         /* write the position output file */
509         writePositionOutputFile();
510 }
511
512 /**
513  The gateway timer callback
514
515  @param context
516  unused
517  */
518 static void pud_gateway_timer_callback(void *context __attribute__ ((unused))) {
519         union olsr_ip_addr bestGateway;
520         bool externalStateChange;
521         MovementState externalState;
522         TristateBoolean movingNow = TRISTATE_BOOLEAN_UNSET;
523
524         getBestUplinkGateway(&bestGateway);
525
526         /*
527          * Movement detection
528          */
529
530         if (!ipequal(&bestGateway, &transmitGpsInformation.txGateway)) {
531                 movingNow = TRISTATE_BOOLEAN_SET;
532         }
533
534         /*
535          * State Determination
536          */
537
538         determineStateWithHysteresis(SUBSTATE_GATEWAY, movingNow, &externalState, &externalStateChange, NULL);
539
540         /*
541          * Update transmitGpsInformation
542          */
543
544         if (movingNow == TRISTATE_BOOLEAN_SET) {
545                 transmitGpsInformation.txGateway = bestGateway;
546         }
547
548         if (externalStateChange) {
549                 doImmediateTransmit(externalState);
550         }
551 }
552
553 /**
554  Detemine whether we are moving from the position, by comparing fields from the
555  average position against those of the last transmitted position.
556
557  MUST be called which the position average list locked.
558
559  @param avg
560  the average position
561  @param lastTx
562  the last transmitted position
563  @param result
564  the results of all movement criteria
565  */
566 static void detemineMovingFromPosition(PositionUpdateEntry * avg, PositionUpdateEntry * lastTx, MovementType * result) {
567         /* avg field presence booleans */
568         bool avgHasSpeed;
569         bool avgHasPos;
570         bool avgHasHdop;
571         bool avgHasElv;
572         bool avgHasVdop;
573
574         /* lastTx field presence booleans */bool lastTxHasPos;
575         bool lastTxHasHdop;
576         bool lastTxHasElv;
577         bool lastTxHasVdop;
578
579         /* these have defaults */
580         double dopMultiplier;
581         double avgHdop;
582         double lastTxHdop;
583         double avgVdop;
584         double lastTxVdop;
585
586         /* calculated values and their validity booleans */
587         double hDistance;
588         double vDistance;
589         double hdopDistanceForOutside;
590         double hdopDistanceForInside;
591         double vdopDistanceForOutside;
592         double vdopDistanceForInside;
593         bool hDistanceValid;
594         bool hdopDistanceValid;
595         bool vDistanceValid;
596         bool vdopDistanceValid;
597
598         /*
599          * Validity
600          *
601          * avg  last  movingNow
602          *  0     0   UNKNOWN : can't determine whether we're moving
603          *  0     1   UNKNOWN : can't determine whether we're moving
604          *  1     0   UNKNOWN : can't determine whether we're moving
605          *  1     1   determine via other parameters
606          */
607
608         if (!positionValid(avg)) {
609                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
610                 return;
611         }
612
613         /* avg is valid here */
614
615         if (!positionValid(lastTx)) {
616                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
617                 return;
618         }
619
620         /* both avg and lastTx are valid here */
621
622         /* avg field presence booleans */
623         avgHasSpeed = nmea_INFO_is_present(avg->nmeaInfo.present, SPEED);
624         avgHasPos = nmea_INFO_is_present(avg->nmeaInfo.present, LAT)
625                         && nmea_INFO_is_present(avg->nmeaInfo.present, LON);
626         avgHasHdop = nmea_INFO_is_present(avg->nmeaInfo.present, HDOP);
627         avgHasElv = nmea_INFO_is_present(avg->nmeaInfo.present, ELV);
628         avgHasVdop = nmea_INFO_is_present(avg->nmeaInfo.present, VDOP);
629
630         /* lastTx field presence booleans */
631         lastTxHasPos = nmea_INFO_is_present(lastTx->nmeaInfo.present, LAT)
632                         && nmea_INFO_is_present(lastTx->nmeaInfo.present, LON);
633         lastTxHasHdop = nmea_INFO_is_present(lastTx->nmeaInfo.present, HDOP);
634         lastTxHasElv = nmea_INFO_is_present(lastTx->nmeaInfo.present, ELV);
635         lastTxHasVdop = nmea_INFO_is_present(lastTx->nmeaInfo.present, VDOP);
636
637         /* fill in some values _or_ defaults */
638         dopMultiplier = getDopMultiplier();
639         avgHdop = avgHasHdop ? avg->nmeaInfo.HDOP : getDefaultHdop();
640         lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.HDOP : getDefaultHdop();
641         avgVdop = avgHasVdop ? avg->nmeaInfo.VDOP : getDefaultVdop();
642         lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.VDOP : getDefaultVdop();
643
644         /*
645          * Calculations
646          */
647
648         /* hDistance */
649         if (avgHasPos && lastTxHasPos) {
650                 nmeaPOS avgPos;
651                 nmeaPOS lastTxPos;
652
653                 avgPos.lat = nmea_degree2radian(avg->nmeaInfo.lat);
654                 avgPos.lon = nmea_degree2radian(avg->nmeaInfo.lon);
655
656                 lastTxPos.lat = nmea_degree2radian(lastTx->nmeaInfo.lat);
657                 lastTxPos.lon = nmea_degree2radian(lastTx->nmeaInfo.lon);
658
659                 hDistance = fabs(nmea_distance_ellipsoid(&avgPos, &lastTxPos, NULL, NULL));
660                 hDistanceValid = true;
661         } else {
662                 hDistanceValid = false;
663         }
664
665         /* hdopDistance */
666         if (avgHasHdop || lastTxHasHdop) {
667                 hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
668                 hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
669                 hdopDistanceValid = true;
670         } else {
671                 hdopDistanceValid = false;
672         }
673
674         /* vDistance */
675         if (avgHasElv && lastTxHasElv) {
676                 vDistance = fabs(lastTx->nmeaInfo.elv - avg->nmeaInfo.elv);
677                 vDistanceValid = true;
678         } else {
679                 vDistanceValid = false;
680         }
681
682         /* vdopDistance */
683         if (avgHasVdop || lastTxHasVdop) {
684                 vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
685                 vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
686                 vdopDistanceValid = true;
687         } else {
688                 vdopDistanceValid = false;
689         }
690
691         /*
692          * Moving Criteria Evaluation Start
693          * We compare the average position against the last transmitted position.
694          */
695
696         /* Speed */
697         if (avgHasSpeed) {
698                 if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
699                         result->speedOverThreshold = TRISTATE_BOOLEAN_SET;
700                 } else {
701                         result->speedOverThreshold = TRISTATE_BOOLEAN_UNSET;
702                 }
703         }
704
705         /*
706          * Position
707          *
708          * avg  last  hDistanceMoving
709          *  0     0   determine via other parameters
710          *  0     1   determine via other parameters
711          *  1     0   MOVING
712          *  1     1   determine via distance threshold and HDOP
713          */
714         if (avgHasPos && !lastTxHasPos) {
715                 result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
716         } else if (hDistanceValid) {
717                 if (hDistance >= getMovingDistanceThreshold()) {
718                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
719                 } else {
720                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
721                 }
722
723                 /*
724                  * Position with HDOP
725                  *
726                  * avg  last  movingNow
727                  *  0     0   determine via other parameters
728                  *  0     1   determine via position with HDOP (avg has default HDOP)
729                  *  1     0   determine via position with HDOP (lastTx has default HDOP)
730                  *  1     1   determine via position with HDOP
731                  */
732                 if (hdopDistanceValid) {
733                         /* we are outside the HDOP when the HDOPs no longer overlap */
734                         if (hDistance > hdopDistanceForOutside) {
735                                 result->outsideHdop = TRISTATE_BOOLEAN_SET;
736                         } else {
737                                 result->outsideHdop = TRISTATE_BOOLEAN_UNSET;
738                         }
739
740                         /* we are inside the HDOP when the HDOPs fully overlap */
741                         if (hDistance <= hdopDistanceForInside) {
742                                 result->insideHdop = TRISTATE_BOOLEAN_SET;
743                         } else {
744                                 result->insideHdop = TRISTATE_BOOLEAN_UNSET;
745                         }
746                 }
747         }
748
749         /*
750          * Elevation
751          *
752          * avg  last  movingNow
753          *  0     0   determine via other parameters
754          *  0     1   determine via other parameters
755          *  1     0   MOVING
756          *  1     1   determine via distance threshold and VDOP
757          */
758         if (avgHasElv && !lastTxHasElv) {
759                 result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
760         } else if (vDistanceValid) {
761                 if (vDistance >= getMovingDistanceThreshold()) {
762                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
763                 } else {
764                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
765                 }
766
767                 /*
768                  * Elevation with VDOP
769                  *
770                  * avg  last  movingNow
771                  *  0     0   determine via other parameters
772                  *  0     1   determine via elevation with VDOP (avg has default VDOP)
773                  *  1     0   determine via elevation with VDOP (lastTx has default VDOP)
774                  *  1     1   determine via elevation with VDOP
775                  */
776                 if (vdopDistanceValid) {
777                         /* we are outside the VDOP when the VDOPs no longer overlap */
778                         if (vDistance > vdopDistanceForOutside) {
779                                 result->outsideVdop = TRISTATE_BOOLEAN_SET;
780                         } else {
781                                 result->outsideVdop = TRISTATE_BOOLEAN_UNSET;
782                         }
783
784                         /* we are inside the VDOP when the VDOPs fully overlap */
785                         if (vDistance <= vdopDistanceForInside) {
786                                 result->insideVdop = TRISTATE_BOOLEAN_SET;
787                         } else {
788                                 result->insideVdop = TRISTATE_BOOLEAN_UNSET;
789                         }
790                 }
791         }
792
793         /*
794          * Moving Criteria Evaluation End
795          */
796
797         /* accumulate inside criteria */
798         if ((result->insideHdop == TRISTATE_BOOLEAN_SET) && (result->insideVdop == TRISTATE_BOOLEAN_SET)) {
799                 result->inside = TRISTATE_BOOLEAN_SET;
800         } else if ((result->insideHdop == TRISTATE_BOOLEAN_UNSET) || (result->insideVdop == TRISTATE_BOOLEAN_UNSET)) {
801                 result->inside = TRISTATE_BOOLEAN_UNSET;
802         }
803
804         /* accumulate outside criteria */
805         if ((result->outsideHdop == TRISTATE_BOOLEAN_SET) || (result->outsideVdop == TRISTATE_BOOLEAN_SET)) {
806                 result->outside = TRISTATE_BOOLEAN_SET;
807         } else if ((result->outsideHdop == TRISTATE_BOOLEAN_UNSET)
808                         || (result->outsideVdop == TRISTATE_BOOLEAN_UNSET)) {
809                 result->outside = TRISTATE_BOOLEAN_UNSET;
810         }
811
812         /* accumulate threshold criteria */
813         if ((result->speedOverThreshold == TRISTATE_BOOLEAN_SET)
814                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_SET)
815                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_SET)) {
816                 result->overThresholds = TRISTATE_BOOLEAN_SET;
817         } else if ((result->speedOverThreshold == TRISTATE_BOOLEAN_UNSET)
818                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)
819                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)) {
820                 result->overThresholds = TRISTATE_BOOLEAN_UNSET;
821         }
822
823         /* accumulate moving criteria */
824         if ((result->overThresholds == TRISTATE_BOOLEAN_SET) || (result->outside == TRISTATE_BOOLEAN_SET)) {
825                 result->moving = TRISTATE_BOOLEAN_SET;
826         } else if ((result->overThresholds == TRISTATE_BOOLEAN_UNSET)
827                         && (result->outside == TRISTATE_BOOLEAN_UNSET)) {
828                 result->moving = TRISTATE_BOOLEAN_UNSET;
829         }
830
831         return;
832 }
833
834 /**
835  Update the latest GPS information. This function is called when a packet is
836  received from a rxNonOlsr interface, containing one or more NMEA strings with
837  GPS information.
838
839  @param rxBuffer
840  the receive buffer with the received NMEA string(s)
841  @param rxCount
842  the number of bytes in the receive buffer
843
844  @return
845  - false on failure
846  - true otherwise
847  */
848 bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount) {
849         static const char * rxBufferPrefix = "$GP";
850         static const size_t rxBufferPrefixLength = 3;
851
852         bool retval = false;
853         PositionUpdateEntry * incomingEntry;
854         PositionUpdateEntry * posAvgEntry;
855         MovementType movementResult;
856         bool subStateExternalStateChange;
857         bool externalStateChange;
858         bool updateTransmitGpsInformation = false;
859         MovementState externalState;
860
861         /* do not process when the message does not start with $GP */
862         if ((rxCount < rxBufferPrefixLength) || (strncmp((char *) rxBuffer,
863                         rxBufferPrefix, rxBufferPrefixLength) != 0)) {
864                 return true;
865         }
866
867         /* parse all NMEA strings in the rxBuffer into the incoming entry */
868         incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
869         nmea_zero_INFO(&incomingEntry->nmeaInfo);
870         nmea_parse(&nmeaParser, (char *) rxBuffer, rxCount,
871                         &incomingEntry->nmeaInfo);
872
873         /* ignore when no useful information */
874         if (incomingEntry->nmeaInfo.smask == GPNON) {
875                 retval = true;
876                 goto end;
877         }
878
879         nmea_INFO_sanitise(&incomingEntry->nmeaInfo);
880
881         /* we always work with latitude, longitude in degrees and DOPs in meters */
882         nmea_INFO_unit_conversion(&incomingEntry->nmeaInfo);
883
884         /*
885          * Averaging
886          */
887
888         if (getInternalState(SUBSTATE_POSITION) == MOVEMENT_STATE_MOVING) {
889                 /* flush average: keep only the incoming entry */
890                 flushPositionAverageList(&positionAverageList);
891         }
892         addNewPositionToAverage(&positionAverageList, incomingEntry);
893         posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
894
895         /*
896          * Movement detection
897          */
898
899         clearMovementType(&movementResult);
900
901         detemineMovingFromPosition(posAvgEntry, &transmitGpsInformation.txPosition, &movementResult);
902
903         /*
904          * State Determination
905          */
906
907         determineStateWithHysteresis(SUBSTATE_POSITION, movementResult.moving, &externalState, &externalStateChange,
908                         &subStateExternalStateChange);
909
910         /*
911          * Update transmitGpsInformation
912          */
913
914         updateTransmitGpsInformation = subStateExternalStateChange
915                         || (positionValid(posAvgEntry) && !positionValid(&transmitGpsInformation.txPosition))
916                         || (movementResult.inside == TRISTATE_BOOLEAN_SET);
917
918         if ((externalState == MOVEMENT_STATE_MOVING) || updateTransmitGpsInformation) {
919                 transmitGpsInformation.txPosition = *posAvgEntry;
920                 transmitGpsInformation.positionUpdated = true;
921
922                 /*
923                  * When we're stationary:
924                  * - the track is not reliable or even invalid, so we must clear it.
925                  * - to avoid confusion in consumers of the data, we must clear the speed
926                  *   because it is possible to have a very low speed while moving.
927                  */
928                 if (externalState == MOVEMENT_STATE_STATIONARY) {
929                         transmitGpsInformation.txPosition.nmeaInfo.speed = (double)0.0;
930                         transmitGpsInformation.txPosition.nmeaInfo.track = (double)0.0;
931                 }
932         }
933
934         if (externalStateChange) {
935                 doImmediateTransmit(externalState);
936         }
937
938         retval = true;
939
940         end:
941         return retval;
942 }
943
944 /**
945  * Log nmea library errors as plugin errors
946  * @param str
947  * @param str_size
948  */
949 static void nmea_errors(const char *str, int str_size __attribute__((unused))) {
950         pudError(false, "NMEA library error: %s", str);
951 }
952
953 /**
954  * Helper function to read the position file into the transmit position
955  */
956 void updatePositionFromFile(void) {
957   char * positionFile = getPositionFile();
958   if (positionFile) {
959     readPositionFile(positionFile, &transmitGpsInformation.txPosition.nmeaInfo);
960   }
961 }
962
963 /**
964  Start the receiver
965
966  @return
967  - false on failure
968  - true otherwise
969  */
970 bool startReceiver(void) {
971         MovementState externalState;
972
973         if (!nmea_parser_init(&nmeaParser)) {
974                 pudError(false, "Could not initialise NMEA parser");
975                 return false;
976         }
977
978         /* hook up the NMEA library error callback */
979         nmea_context_set_error_func(&nmea_errors);
980
981         nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
982         updatePositionFromFile();
983
984         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
985         transmitGpsInformation.positionUpdated = false;
986         transmitGpsInformation.nodeId = getNodeId(NULL);
987
988         olsr_cnf->pud_position = &transmitGpsInformation;
989
990         initPositionAverageList(&positionAverageList, getAverageDepth());
991
992         if (!initOlsrTxTimer()) {
993                 stopReceiver();
994                 return false;
995         }
996
997         if (!initUplinkTxTimer()) {
998                 stopReceiver();
999                 return false;
1000         }
1001
1002         if (!initGatewayTimer()) {
1003                 stopReceiver();
1004                 return false;
1005         }
1006
1007         positionOutputFile = getPositionOutputFile();
1008         positionOutputFileError = false;
1009
1010         /* write the position output file */
1011         if (!writePositionOutputFile()) {
1012           return false;
1013         }
1014
1015         externalState = getExternalState();
1016         restartOlsrTimer(externalState);
1017         restartUplinkTimer(externalState);
1018         if (!restartGatewayTimer(getGatewayDeterminationInterval(), &pud_gateway_timer_callback)) {
1019                 pudError(0, "Could not start gateway timer");
1020                 stopReceiver();
1021                 return false;
1022         }
1023
1024         return true;
1025 }
1026
1027 /**
1028  Stop the receiver
1029  */
1030 void stopReceiver(void) {
1031         if (positionOutputFile) {
1032           unlink(positionOutputFile);
1033         }
1034
1035         destroyGatewayTimer();
1036         destroyUplinkTxTimer();
1037         destroyOlsrTxTimer();
1038
1039         destroyPositionAverageList(&positionAverageList);
1040
1041         nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
1042         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
1043         transmitGpsInformation.positionUpdated = false;
1044 }