info: java: update workspace
[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 <nmealib/context.h>
63 #include <nmealib/nmath.h>
64 #include <nmealib/sentence.h>
65 #include <OlsrdPudWireFormat/wireFormat.h>
66 #include <unistd.h>
67
68 static void receiverProcessIncomingEntry(PositionUpdateEntry * incomingEntry);
69
70 /*
71  * State
72  */
73
74 /** Type describing movement calculations */
75 typedef struct _MovementType {
76         TristateBoolean moving; /**< SET: we are moving */
77
78         TristateBoolean overThresholds; /**< SET: at least 1 threshold state is set */
79         TristateBoolean speedOverThreshold; /**< SET: speed is over threshold */
80         TristateBoolean hDistanceOverThreshold; /**< SET: horizontal distance is outside threshold */
81         TristateBoolean vDistanceOverThreshold; /**< SET: vertical distance is outside threshold */
82
83         TristateBoolean outside; /**< SET: at least 1 outside state is SET */
84         TristateBoolean outsideHdop; /**< SET: avg is outside lastTx HDOP */
85         TristateBoolean outsideVdop; /**< SET: avg is outside lastTx VDOP */
86
87         TristateBoolean inside; /**< SET: all inside states are SET */
88         TristateBoolean insideHdop; /**< SET: avg is inside lastTx HDOP */
89         TristateBoolean insideVdop; /**< SET: avg is inside lastTx VDOP */
90 } MovementType;
91
92 /*
93  * Averaging
94  */
95
96 /** The average position with its administration */
97 static PositionAverageList positionAverageList;
98
99 /*
100  * TX to OLSR
101  */
102
103 typedef enum _TimedTxInterface {
104         TX_INTERFACE_OLSR = 1,
105         TX_INTERFACE_UPLINK = 2
106 } TimedTxInterface;
107
108 /** The latest position information that is transmitted */
109 static TransmitGpsInformation transmitGpsInformation;
110
111 /** The size of the buffer in which the OLSR and uplink messages are assembled */
112 #define TX_BUFFER_SIZE_FOR_OLSR 1024
113
114 /** the prefix of all parameters written to the position output file */
115 #define PUD_POSOUT_FILE_PARAM_PREFIX "OLSRD_PUD_"
116
117 /** the position output file */
118 static char * positionOutputFile = NULL;
119
120 /** true when a postion output file error was already reported */
121 static bool positionOutputFileError = false;
122
123 /** gpsd daemon data */
124 struct gps_data_t gpsdata;
125
126 /** gpsd daemon connection tracking */
127 static struct GpsdConnectionState connectionTracking;
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 (nmeaInfoIsPresentAll(position->nmeaInfo.present, NMEALIB_PRESENT_FIX)
164                         && (position->nmeaInfo.fix != NMEALIB_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           nmeaTimeSet(&transmitGpsInformation.txPosition.nmeaInfo.utc, &transmitGpsInformation.txPosition.nmeaInfo.present, NULL);
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);
422   fprintf(fp, "%s%s=%02d\n", PUD_POSOUT_FILE_PARAM_PREFIX, "MONTH", nmeaInfo->utc.mon);
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->latitude);
436   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "LONGITUDE", nmeaInfo->longitude);
437   fprintf(fp, "%s%s=%f\n", PUD_POSOUT_FILE_PARAM_PREFIX, "ELEVATION", nmeaInfo->elevation);
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 gpsd fetch timer callback
453
454  @param context
455  unused
456  */
457 static void pud_gpsd_fetch_timer_callback(void *context __attribute__ ((unused))) {
458   static char cnt = 0;
459
460   PositionUpdateEntry * incomingEntry;
461   GpsDaemon *gpsd = getGpsd();
462   if (!gpsd) {
463     return;
464   }
465
466   /* fetch info from gpsd daemon into the incoming entry */
467   incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
468   if (!cnt) {
469     nmeaInfoClear(&incomingEntry->nmeaInfo);
470     nmeaTimeSet(&incomingEntry->nmeaInfo.utc, &incomingEntry->nmeaInfo.present, NULL);
471   }
472   readFromGpsd(gpsd, &gpsdata, &connectionTracking, &incomingEntry->nmeaInfo);
473
474   if (cnt >= (TIMER_GPSD_READS_PER_SEC - 1)) {
475     receiverProcessIncomingEntry(incomingEntry);
476     cnt = 0;
477   } else {
478     cnt++;
479   }
480 }
481
482 /**
483  The OLSR tx timer callback
484
485  @param context
486  unused
487  */
488 static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
489         txToAllOlsrInterfaces(TX_INTERFACE_OLSR);
490 }
491
492 /**
493  The uplink timer callback
494
495  @param context
496  unused
497  */
498 static void pud_uplink_timer_callback(void *context __attribute__ ((unused))) {
499         txToAllOlsrInterfaces(TX_INTERFACE_UPLINK);
500 }
501
502 /**
503  Restart the gpsd fetch timer
504  */
505 static void restartGpsdTimer(void) {
506   if (!getGpsd()) {
507     return;
508   }
509
510   if (!restartGpsdFetchTimer(&pud_gpsd_fetch_timer_callback)) {
511     pudError(0, "Could not restart gpsd fetch timer, no position information will be available");
512   }
513 }
514
515 /**
516  Restart the OLSR tx timer
517  */
518 static void restartOlsrTimer(MovementState externalState) {
519         if (!restartOlsrTxTimer(
520                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUpdateIntervalStationary() :
521                                         getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
522                 pudError(0, "Could not restart OLSR tx timer, no periodic"
523                                 " position updates will be sent to the OLSR network");
524         }
525 }
526
527 /**
528  Restart the uplink tx timer
529  */
530 static void restartUplinkTimer(MovementState externalState) {
531         if (!restartUplinkTxTimer(
532                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() :
533                                         getUplinkUpdateIntervalMoving(),
534                         &pud_uplink_timer_callback)) {
535                 pudError(0, "Could not restart uplink timer, no periodic"
536                                 " position updates will be uplinked");
537         }
538 }
539
540 static void doImmediateTransmit(MovementState externalState) {
541         TimedTxInterface interfaces = TX_INTERFACE_OLSR; /* always send over olsr */
542         restartOlsrTimer(externalState);
543
544         if (isUplinkAddrSet()) {
545                 interfaces |= TX_INTERFACE_UPLINK;
546                 restartUplinkTimer(externalState);
547         }
548
549         /* do an immediate transmit */
550         txToAllOlsrInterfaces(interfaces);
551
552         /* write the position output file */
553         writePositionOutputFile();
554 }
555
556 /**
557  The gateway timer callback
558
559  @param context
560  unused
561  */
562 static void pud_gateway_timer_callback(void *context __attribute__ ((unused))) {
563         union olsr_ip_addr bestGateway;
564         bool externalStateChange;
565         MovementState externalState;
566         TristateBoolean movingNow = TRISTATE_BOOLEAN_UNSET;
567
568         getBestUplinkGateway(&bestGateway);
569
570         /*
571          * Movement detection
572          */
573
574         if (!ipequal(&bestGateway, &transmitGpsInformation.txGateway)) {
575                 movingNow = TRISTATE_BOOLEAN_SET;
576         }
577
578         /*
579          * State Determination
580          */
581
582         determineStateWithHysteresis(SUBSTATE_GATEWAY, movingNow, &externalState, &externalStateChange, NULL, false);
583
584         /*
585          * Update transmitGpsInformation
586          */
587
588         if (movingNow == TRISTATE_BOOLEAN_SET) {
589                 transmitGpsInformation.txGateway = bestGateway;
590         }
591
592         if (externalStateChange) {
593                 doImmediateTransmit(externalState);
594         }
595 }
596
597 /**
598  Detemine whether we are moving from the position, by comparing fields from the
599  average position against those of the last transmitted position.
600
601  MUST be called which the position average list locked.
602
603  @param avg
604  the average position
605  @param lastTx
606  the last transmitted position
607  @param result
608  the results of all movement criteria
609  */
610 static void detemineMovingFromPosition(PositionUpdateEntry * avg, PositionUpdateEntry * lastTx, MovementType * result) {
611         /* avg field presence booleans */
612         bool avgHasSpeed;
613         bool avgHasPos;
614         bool avgHasHdop;
615         bool avgHasElv;
616         bool avgHasVdop;
617
618         /* lastTx field presence booleans */
619         bool lastTxHasPos;
620         bool lastTxHasHdop;
621         bool lastTxHasElv;
622         bool lastTxHasVdop;
623
624         /* these have defaults */
625         double dopMultiplier;
626         double avgHdop;
627         double lastTxHdop;
628         double avgVdop;
629         double lastTxVdop;
630
631         /* calculated values and their validity booleans */
632         double hDistance;
633         double vDistance;
634         double hdopDistanceForOutside;
635         double hdopDistanceForInside;
636         double vdopDistanceForOutside;
637         double vdopDistanceForInside;
638         bool hDistanceValid;
639         bool hdopDistanceValid;
640         bool vDistanceValid;
641         bool vdopDistanceValid;
642
643         /*
644          * Validity
645          *
646          * avg  last  movingNow
647          *  0     0   UNKNOWN : can't determine whether we're moving
648          *  0     1   UNKNOWN : can't determine whether we're moving
649          *  1     0   UNKNOWN : can't determine whether we're moving
650          *  1     1   determine via other parameters
651          */
652
653         if (!positionValid(avg)) {
654                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
655                 return;
656         }
657
658         /* avg is valid here */
659
660         if (!positionValid(lastTx)) {
661                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
662                 return;
663         }
664
665         /* both avg and lastTx are valid here */
666
667         /* avg field presence booleans */
668         avgHasSpeed = nmeaInfoIsPresentAll(avg->nmeaInfo.present, NMEALIB_PRESENT_SPEED);
669         avgHasPos = nmeaInfoIsPresentAll(avg->nmeaInfo.present, NMEALIB_PRESENT_LAT)
670                         && nmeaInfoIsPresentAll(avg->nmeaInfo.present, NMEALIB_PRESENT_LON);
671         avgHasHdop = nmeaInfoIsPresentAll(avg->nmeaInfo.present, NMEALIB_PRESENT_HDOP);
672         avgHasElv = nmeaInfoIsPresentAll(avg->nmeaInfo.present, NMEALIB_PRESENT_ELV);
673         avgHasVdop = nmeaInfoIsPresentAll(avg->nmeaInfo.present, NMEALIB_PRESENT_VDOP);
674
675         /* lastTx field presence booleans */
676         lastTxHasPos = nmeaInfoIsPresentAll(lastTx->nmeaInfo.present, NMEALIB_PRESENT_LAT)
677                         && nmeaInfoIsPresentAll(lastTx->nmeaInfo.present, NMEALIB_PRESENT_LON);
678         lastTxHasHdop = nmeaInfoIsPresentAll(lastTx->nmeaInfo.present, NMEALIB_PRESENT_HDOP);
679         lastTxHasElv = nmeaInfoIsPresentAll(lastTx->nmeaInfo.present, NMEALIB_PRESENT_ELV);
680         lastTxHasVdop = nmeaInfoIsPresentAll(lastTx->nmeaInfo.present, NMEALIB_PRESENT_VDOP);
681
682         /* fill in some values _or_ defaults */
683         dopMultiplier = getDopMultiplier();
684         avgHdop = avgHasHdop ? avg->nmeaInfo.hdop : getDefaultHdop();
685         lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.hdop : getDefaultHdop();
686         avgVdop = avgHasVdop ? avg->nmeaInfo.vdop : getDefaultVdop();
687         lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.vdop: getDefaultVdop();
688
689         /*
690          * Calculations
691          */
692
693         /* hDistance */
694         if (avgHasPos && lastTxHasPos) {
695                 NmeaPosition avgPos;
696                 NmeaPosition lastTxPos;
697
698                 avgPos.lat = nmeaMathDegreeToRadian(avg->nmeaInfo.latitude);
699                 avgPos.lon = nmeaMathDegreeToRadian(avg->nmeaInfo.longitude);
700
701                 lastTxPos.lat = nmeaMathDegreeToRadian(lastTx->nmeaInfo.latitude);
702                 lastTxPos.lon = nmeaMathDegreeToRadian(lastTx->nmeaInfo.longitude);
703
704                 hDistance = fabs(nmeaMathDistanceEllipsoid(&avgPos, &lastTxPos, NULL, NULL));
705                 hDistanceValid = true;
706         } else {
707                 hDistanceValid = false;
708         }
709
710         /* hdopDistance */
711         if (avgHasHdop || lastTxHasHdop) {
712                 hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
713                 hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
714                 hdopDistanceValid = true;
715         } else {
716                 hdopDistanceValid = false;
717         }
718
719         /* vDistance */
720         if (avgHasElv && lastTxHasElv) {
721                 vDistance = fabs(lastTx->nmeaInfo.elevation - avg->nmeaInfo.elevation);
722                 vDistanceValid = true;
723         } else {
724                 vDistanceValid = false;
725         }
726
727         /* vdopDistance */
728         if (avgHasVdop || lastTxHasVdop) {
729                 vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
730                 vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
731                 vdopDistanceValid = true;
732         } else {
733                 vdopDistanceValid = false;
734         }
735
736         /*
737          * Moving Criteria Evaluation Start
738          * We compare the average position against the last transmitted position.
739          */
740
741         /* Speed */
742         if (avgHasSpeed) {
743                 if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
744                         result->speedOverThreshold = TRISTATE_BOOLEAN_SET;
745                 } else {
746                         result->speedOverThreshold = TRISTATE_BOOLEAN_UNSET;
747                 }
748         }
749
750         /*
751          * Position
752          *
753          * avg  last  hDistanceMoving
754          *  0     0   determine via other parameters
755          *  0     1   determine via other parameters
756          *  1     0   MOVING
757          *  1     1   determine via distance threshold and HDOP
758          */
759         if (avgHasPos && !lastTxHasPos) {
760                 result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
761         } else if (hDistanceValid) {
762                 if (hDistance >= getMovingDistanceThreshold()) {
763                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
764                 } else {
765                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
766                 }
767
768                 /*
769                  * Position with HDOP
770                  *
771                  * avg  last  movingNow
772                  *  0     0   determine via other parameters
773                  *  0     1   determine via position with HDOP (avg has default HDOP)
774                  *  1     0   determine via position with HDOP (lastTx has default HDOP)
775                  *  1     1   determine via position with HDOP
776                  */
777                 if (hdopDistanceValid) {
778                         /* we are outside the HDOP when the HDOPs no longer overlap */
779                         if (hDistance > hdopDistanceForOutside) {
780                                 result->outsideHdop = TRISTATE_BOOLEAN_SET;
781                         } else {
782                                 result->outsideHdop = TRISTATE_BOOLEAN_UNSET;
783                         }
784
785                         /* we are inside the HDOP when the HDOPs fully overlap */
786                         if (hDistance <= hdopDistanceForInside) {
787                                 result->insideHdop = TRISTATE_BOOLEAN_SET;
788                         } else {
789                                 result->insideHdop = TRISTATE_BOOLEAN_UNSET;
790                         }
791                 }
792         }
793
794         /*
795          * Elevation
796          *
797          * avg  last  movingNow
798          *  0     0   determine via other parameters
799          *  0     1   determine via other parameters
800          *  1     0   MOVING
801          *  1     1   determine via distance threshold and VDOP
802          */
803         if (avgHasElv && !lastTxHasElv) {
804                 result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
805         } else if (vDistanceValid) {
806                 if (vDistance >= getMovingDistanceThreshold()) {
807                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
808                 } else {
809                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
810                 }
811
812                 /*
813                  * Elevation with VDOP
814                  *
815                  * avg  last  movingNow
816                  *  0     0   determine via other parameters
817                  *  0     1   determine via elevation with VDOP (avg has default VDOP)
818                  *  1     0   determine via elevation with VDOP (lastTx has default VDOP)
819                  *  1     1   determine via elevation with VDOP
820                  */
821                 if (vdopDistanceValid) {
822                         /* we are outside the VDOP when the VDOPs no longer overlap */
823                         if (vDistance > vdopDistanceForOutside) {
824                                 result->outsideVdop = TRISTATE_BOOLEAN_SET;
825                         } else {
826                                 result->outsideVdop = TRISTATE_BOOLEAN_UNSET;
827                         }
828
829                         /* we are inside the VDOP when the VDOPs fully overlap */
830                         if (vDistance <= vdopDistanceForInside) {
831                                 result->insideVdop = TRISTATE_BOOLEAN_SET;
832                         } else {
833                                 result->insideVdop = TRISTATE_BOOLEAN_UNSET;
834                         }
835                 }
836         }
837
838         /*
839          * Moving Criteria Evaluation End
840          */
841
842         /* accumulate inside criteria */
843         if ((result->insideHdop == TRISTATE_BOOLEAN_SET) && (result->insideVdop == TRISTATE_BOOLEAN_SET)) {
844                 result->inside = TRISTATE_BOOLEAN_SET;
845         } else if ((result->insideHdop == TRISTATE_BOOLEAN_UNSET) || (result->insideVdop == TRISTATE_BOOLEAN_UNSET)) {
846                 result->inside = TRISTATE_BOOLEAN_UNSET;
847         }
848
849         /* accumulate outside criteria */
850         if ((result->outsideHdop == TRISTATE_BOOLEAN_SET) || (result->outsideVdop == TRISTATE_BOOLEAN_SET)) {
851                 result->outside = TRISTATE_BOOLEAN_SET;
852         } else if ((result->outsideHdop == TRISTATE_BOOLEAN_UNSET)
853                         || (result->outsideVdop == TRISTATE_BOOLEAN_UNSET)) {
854                 result->outside = TRISTATE_BOOLEAN_UNSET;
855         }
856
857         /* accumulate threshold criteria */
858         if ((result->speedOverThreshold == TRISTATE_BOOLEAN_SET)
859                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_SET)
860                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_SET)) {
861                 result->overThresholds = TRISTATE_BOOLEAN_SET;
862         } else if ((result->speedOverThreshold == TRISTATE_BOOLEAN_UNSET)
863                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)
864                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)) {
865                 result->overThresholds = TRISTATE_BOOLEAN_UNSET;
866         }
867
868         /* accumulate moving criteria */
869         if ((result->overThresholds == TRISTATE_BOOLEAN_SET) || (result->outside == TRISTATE_BOOLEAN_SET)) {
870                 result->moving = TRISTATE_BOOLEAN_SET;
871         } else if ((result->overThresholds == TRISTATE_BOOLEAN_UNSET)
872                         && (result->outside == TRISTATE_BOOLEAN_UNSET)) {
873                 result->moving = TRISTATE_BOOLEAN_UNSET;
874         }
875
876         return;
877 }
878
879 static void receiverProcessIncomingEntry(PositionUpdateEntry * incomingEntry) {
880         static bool gpnonPrev = false;
881
882         PositionUpdateEntry * posAvgEntry;
883         MovementType movementResult;
884         bool subStateExternalStateChange;
885         bool externalStateChange;
886         bool updateTransmitGpsInformation = false;
887         MovementState externalState;
888         bool gpnon;
889         bool gpnonChanged;
890
891         /* we always work with latitude, longitude in degrees and DOPs in meters */
892         nmeaInfoUnitConversion(&incomingEntry->nmeaInfo, true);
893
894         gpnon = !nmeaInfoIsPresentAll(incomingEntry->nmeaInfo.present, NMEALIB_PRESENT_SMASK) || (incomingEntry->nmeaInfo.smask == NMEALIB_SENTENCE_GPNON);
895         gpnonChanged = gpnon != gpnonPrev;
896         gpnonPrev = gpnon;
897
898         /*
899          * Averaging
900          */
901
902         if ((getInternalState(SUBSTATE_POSITION) == MOVEMENT_STATE_MOVING) || gpnonChanged) {
903                 /* flush average: keep only the incoming entry */
904                 flushPositionAverageList(&positionAverageList);
905         }
906         addNewPositionToAverage(&positionAverageList, incomingEntry);
907         posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
908
909         /*
910          * Movement detection
911          */
912
913         clearMovementType(&movementResult);
914
915         detemineMovingFromPosition(posAvgEntry, &transmitGpsInformation.txPosition, &movementResult);
916
917         /*
918          * State Determination
919          */
920
921         determineStateWithHysteresis(SUBSTATE_POSITION, movementResult.moving, &externalState, &externalStateChange,
922                         &subStateExternalStateChange, gpnonChanged);
923
924         /*
925          * Update transmitGpsInformation
926          */
927
928         updateTransmitGpsInformation = subStateExternalStateChange
929                         || (positionValid(posAvgEntry) && !positionValid(&transmitGpsInformation.txPosition))
930                         || (movementResult.inside == TRISTATE_BOOLEAN_SET);
931
932         if ((externalState == MOVEMENT_STATE_MOVING) || updateTransmitGpsInformation) {
933                 transmitGpsInformation.txPosition = *posAvgEntry;
934                 transmitGpsInformation.positionUpdated = true;
935
936                 /*
937                  * When we're stationary:
938                  * - the track is not reliable or even invalid, so we must clear it.
939                  * - to avoid confusion in consumers of the data, we must clear the speed
940                  *   because it is possible to have a very low speed while moving.
941                  */
942                 if (externalState == MOVEMENT_STATE_STATIONARY) {
943                         transmitGpsInformation.txPosition.nmeaInfo.speed = (double)0.0;
944                         transmitGpsInformation.txPosition.nmeaInfo.track = (double)0.0;
945                 }
946         }
947
948         if (externalStateChange) {
949                 doImmediateTransmit(externalState);
950         }
951 }
952
953 /**
954  * Log nmea library errors as plugin errors
955  * @param str
956  * @param sz
957  */
958 static void nmea_errors(const char *s, size_t sz __attribute__((unused))) {
959         pudError(false, "NMEA library error: %s", s);
960 }
961
962 /**
963  * Helper function to read the position file into the transmit position
964  */
965 void updatePositionFromFile(void) {
966   char * positionFile = getPositionFile();
967   if (positionFile) {
968     readPositionFile(positionFile, &transmitGpsInformation.txPosition.nmeaInfo);
969   }
970 }
971
972 /**
973  Start the receiver
974
975  @return
976  - false on failure
977  - true otherwise
978  */
979 bool startReceiver(void) {
980         MovementState externalState;
981
982         /* hook up the NMEA library error callback */
983         nmeaContextSetErrorFunction(&nmea_errors);
984
985         nmeaInfoClear(&transmitGpsInformation.txPosition.nmeaInfo);
986         nmeaTimeSet(&transmitGpsInformation.txPosition.nmeaInfo.utc, &transmitGpsInformation.txPosition.nmeaInfo.present, NULL);
987         updatePositionFromFile();
988
989         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
990         transmitGpsInformation.positionUpdated = false;
991         transmitGpsInformation.nodeId = getNodeId(NULL);
992
993         olsr_cnf->pud_position = &transmitGpsInformation;
994
995         initPositionAverageList(&positionAverageList, getAverageDepth());
996
997         memset(&gpsdata, 0, sizeof(gpsdata));
998         gpsdata.gps_fd = -1;
999         memset(&connectionTracking, 0, sizeof(connectionTracking));
1000
1001         if (!initGpsdFetchTimer()) {
1002           stopReceiver();
1003           return false;
1004         }
1005
1006         if (!initOlsrTxTimer()) {
1007                 stopReceiver();
1008                 return false;
1009         }
1010
1011         if (!initUplinkTxTimer()) {
1012                 stopReceiver();
1013                 return false;
1014         }
1015
1016         if (!initGatewayTimer()) {
1017                 stopReceiver();
1018                 return false;
1019         }
1020
1021         positionOutputFile = getPositionOutputFile();
1022         positionOutputFileError = false;
1023
1024         /* write the position output file */
1025         if (!writePositionOutputFile()) {
1026           return false;
1027         }
1028
1029         externalState = getExternalState();
1030         restartGpsdTimer();
1031         restartOlsrTimer(externalState);
1032         restartUplinkTimer(externalState);
1033         if (!restartGatewayTimer(getGatewayDeterminationInterval(), &pud_gateway_timer_callback)) {
1034                 pudError(0, "Could not start gateway timer");
1035                 stopReceiver();
1036                 return false;
1037         }
1038
1039         return true;
1040 }
1041
1042 /**
1043  Stop the receiver
1044  */
1045 void stopReceiver(void) {
1046         if (positionOutputFile) {
1047           unlink(positionOutputFile);
1048         }
1049
1050         destroyGatewayTimer();
1051         destroyUplinkTxTimer();
1052         destroyOlsrTxTimer();
1053         destroyGpsdFetchTimer();
1054
1055         destroyPositionAverageList(&positionAverageList);
1056
1057         gpsdDisconnect(&gpsdata, &connectionTracking);
1058         memset(&connectionTracking, 0, sizeof(connectionTracking));
1059         memset(&gpsdata, 0, sizeof(gpsdata));
1060         gpsdata.gps_fd = -1;
1061
1062         nmeaInfoClear(&transmitGpsInformation.txPosition.nmeaInfo);
1063         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
1064         transmitGpsInformation.positionUpdated = false;
1065 }