PUD: fix validity time on uplink and reduce lock contention window
[olsrd.git] / lib / pud / src / receiver.c
1 #include "receiver.h"
2
3 /* Plugin includes */
4 #include "pud.h"
5 #include "state.h"
6 #include "posAvg.h"
7 #include "configuration.h"
8 #include "gpsConversion.h"
9 #include "networkInterfaces.h"
10 #include "timers.h"
11 #include "uplinkGateway.h"
12 #include "dump.h"
13
14 /* OLSRD includes */
15 #include "olsr_types.h"
16 #include "net_olsr.h"
17
18 /* System includes */
19 #include <pthread.h>
20 #include <nmea/parser.h>
21 #include <nmea/gmath.h>
22 #include <nmea/sentence.h>
23 #include <OlsrdPudWireFormat/wireFormat.h>
24
25 /* Debug includes */
26 #if defined(PUD_DUMP_GPS_PACKETS_TX_OLSR) || \
27         defined(PUD_DUMP_GPS_PACKETS_TX_UPLINK) || \
28         defined(PUD_DUMP_AVERAGING)
29 #include "olsr.h"
30 #endif
31
32 /*
33  * NMEA parser
34  */
35
36 /** The NMEA string parser */
37 static nmeaPARSER nmeaParser;
38
39 /*
40  * State
41  */
42
43 /** Type describing movement calculations */
44 typedef struct _MovementType {
45         TristateBoolean moving; /**< SET: we are moving */
46
47         TristateBoolean overThresholds; /**< SET: at least 1 threshold state is set */
48         TristateBoolean speedOverThreshold; /**< SET: speed is over threshold */
49         TristateBoolean hDistanceOverThreshold; /**< SET: horizontal distance is outside threshold */
50         TristateBoolean vDistanceOverThreshold; /**< SET: vertical distance is outside threshold */
51
52         TristateBoolean outside; /**< SET: at least 1 outside state is SET */
53         TristateBoolean outsideHdop; /**< SET: avg is outside lastTx HDOP */
54         TristateBoolean outsideVdop; /**< SET: avg is outside lastTx VDOP */
55
56         TristateBoolean inside; /**< SET: all inside states are SET */
57         TristateBoolean insideHdop; /**< SET: avg is inside lastTx HDOP */
58         TristateBoolean insideVdop; /**< SET: avg is inside lastTx VDOP */
59 } MovementType;
60
61 /*
62  * Averaging
63  */
64
65 /** The average position with its administration */
66 static PositionAverageList positionAverageList;
67
68 /*
69  * TX to OLSR
70  */
71
72 typedef enum _TimedTxInterface {
73         TX_INTERFACE_OLSR = 1,
74         TX_INTERFACE_UPLINK = 2
75 } TimedTxInterface;
76
77 /** Structure of the latest GPS information that is transmitted */
78 typedef struct _TransmitGpsInformation {
79         pthread_mutex_t mutex; /**< access mutex */
80         bool positionUpdated; /**< true when the position information was updated */
81         PositionUpdateEntry txPosition; /**< The last transmitted position */
82         union olsr_ip_addr txGateway; /**< the best gateway */
83 } TransmitGpsInformation;
84
85 /** The latest position information that is transmitted */
86 static TransmitGpsInformation transmitGpsInformation;
87
88 /** The size of the buffer in which the OLSR and uplink messages are assembled */
89 #define TX_BUFFER_SIZE_FOR_OLSR 1024
90
91 /*
92  * Functions
93  */
94
95 /**
96  Clear the MovementType
97  * @param result a pointer to the MovementType
98  */
99 static void clearMovementType(MovementType * result) {
100         /* clear outputs */
101         result->moving = TRISTATE_BOOLEAN_UNKNOWN;
102         result->overThresholds = TRISTATE_BOOLEAN_UNKNOWN;
103         result->speedOverThreshold = TRISTATE_BOOLEAN_UNKNOWN;
104         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_UNKNOWN;
105         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_UNKNOWN;
106         result->outside = TRISTATE_BOOLEAN_UNKNOWN;
107         result->outsideHdop = TRISTATE_BOOLEAN_UNKNOWN;
108         result->outsideVdop = TRISTATE_BOOLEAN_UNKNOWN;
109         result->inside = TRISTATE_BOOLEAN_UNKNOWN;
110         result->insideHdop = TRISTATE_BOOLEAN_UNKNOWN;
111         result->insideVdop = TRISTATE_BOOLEAN_UNKNOWN;
112 }
113
114 /**
115  Determine whether s position is valid.
116
117  @param position
118  a pointer to a position
119
120  @return
121  - true when valid
122  - false otherwise
123  */
124 static bool positionValid(PositionUpdateEntry * position) {
125         return (nmea_INFO_has_field(position->nmeaInfo.smask, FIX)
126                         && (position->nmeaInfo.fix != NMEA_FIX_BAD));
127 }
128
129 /**
130  Send the transmit buffer out over all designated interfaces, called as a
131  timer callback and also immediately on an external state change.
132
133  @param interfaces
134  a bitmap defining which interfaces to send over
135  */
136 static void txToAllOlsrInterfaces(TimedTxInterface interfaces) {
137         /** txBuffer is used to concatenate the position update and cluster leader messages in */
138         unsigned char txBuffer[TX_BUFFER_SIZE_FOR_OLSR];
139         unsigned int txBufferBytesUsed = 0;
140         #define txBufferBytesFree       (sizeof(txBuffer) - txBufferBytesUsed)
141
142         /*
143          * The first message in txBuffer is an OLSR position update.
144          *
145          * The position update is always present.
146          *
147          * The second message is the cluster leader message, but only when uplink
148          * was requested and correctly configured.
149          */
150
151         UplinkMessage * pu_uplink = (UplinkMessage *) &txBuffer[0];
152         union olsr_message * pu = &pu_uplink->msg.olsrMessage;
153         unsigned int pu_size = 0;
154         union olsr_ip_addr gateway;
155         MovementState externalState;
156         nmeaINFO nmeaInfo;
157
158         externalState = getExternalState();
159
160         (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
161
162         /* only fixup timestamp when the position is valid _and_ when the position was not updated */
163         if (positionValid(&transmitGpsInformation.txPosition) && !transmitGpsInformation.positionUpdated) {
164                 nmea_time_now(&transmitGpsInformation.txPosition.nmeaInfo.utc);
165         }
166
167         nmeaInfo = transmitGpsInformation.txPosition.nmeaInfo;
168         transmitGpsInformation.positionUpdated = false;
169         gateway = transmitGpsInformation.txGateway;
170
171         (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
172
173         /* convert nmeaINFO to wireformat olsr message */
174         txBufferBytesUsed += sizeof(UplinkHeader); /* keep before txBufferSpaceFree usage */
175         pu_size = gpsToOlsr(&nmeaInfo, pu, txBufferBytesFree,
176                         ((externalState == MOVEMENT_STATE_STATIONARY) ? getUpdateIntervalStationary() : getUpdateIntervalMoving()));
177         txBufferBytesUsed += pu_size;
178
179         /*
180          * push out to all OLSR interfaces
181          */
182         if (((interfaces & TX_INTERFACE_OLSR) != 0) && (pu_size > 0)) {
183                 int r;
184                 struct interface *ifn;
185                 for (ifn = ifnet; ifn; ifn = ifn->int_next) {
186                         r = net_outbuffer_push(ifn, pu, pu_size);
187                         if (r != (int) pu_size) {
188                                 pudError(
189                                                 false,
190                                                 "Could not send to OLSR interface %s: %s (size=%u, r=%d)",
191                                                 ifn->int_name,
192                                                 ((r == -1) ? "no buffer was found" :
193                                                         (r == 0) ? "there was not enough room in the buffer" : "unknown reason"), pu_size, r);
194                         }
195 #ifdef PUD_DUMP_GPS_PACKETS_TX_OLSR
196                         else {
197                                 olsr_printf(0, "%s: packet sent to OLSR interface %s (%d bytes)\n",
198                                                 PUD_PLUGIN_ABBR, ifn->int_name, pu_size);
199                                 dump_packet((unsigned char *)pu, pu_size);
200                         }
201 #endif
202                 }
203
204                 /* loopback to tx interface when so configured */
205                 if (getUseLoopback()) {
206                         (void) packetReceivedFromOlsr(pu, NULL, NULL);
207                 }
208         }
209
210         /* push out over uplink when an uplink is configured */
211         if (((interfaces & TX_INTERFACE_UPLINK) != 0) && isUplinkAddrSet()) {
212                 int fd = getDownlinkSocketFd();
213                 if (fd != -1) {
214                         union olsr_sockaddr * uplink_addr = getUplinkAddr();
215
216                         UplinkMessage * cl_uplink = (UplinkMessage *) &txBuffer[txBufferBytesUsed];
217                         UplinkClusterLeader * cl = &cl_uplink->msg.clusterLeader;
218                         union olsr_ip_addr * cl_originator = getClusterLeaderOriginator(olsr_cnf->ip_version, cl);
219                         union olsr_ip_addr * cl_clusterLeader = getClusterLeaderClusterLeader(olsr_cnf->ip_version, cl);
220                         unsigned int cl_size =
221                                         sizeof(UplinkClusterLeader) - sizeof(cl->leader)
222                                                         + ((olsr_cnf->ip_version == AF_INET) ? sizeof(cl->leader.v4) :
223                                                                         sizeof(cl->leader.v6));
224
225                         unsigned long long uplinkUpdateInterval =
226                                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() : getUplinkUpdateIntervalMoving();
227
228                         /*
229                          * position update message (pu)
230                          */
231
232                         /* set header fields in position update uplink message and adjust
233                          * the validity time to the uplink validity time */
234                         if (pu_size > 0) {
235                                 PudOlsrPositionUpdate * pu_gpsMessage = getOlsrMessagePayload(olsr_cnf->ip_version, pu);
236
237                                 setUplinkMessageType(&pu_uplink->header, POSITION);
238                                 setUplinkMessageLength(&pu_uplink->header, pu_size);
239                                 setUplinkMessageIPv6(&pu_uplink->header, (olsr_cnf->ip_version != AF_INET));
240                                 setUplinkMessagePadding(&pu_uplink->header, 0);
241
242                                 /* fixup validity time */
243                                 setValidityTime(&pu_gpsMessage->validityTime, uplinkUpdateInterval);
244                         }
245
246                         /*
247                          * cluster leader message (cl)
248                          */
249
250                         /* set cl_uplink header fields */
251                         setUplinkMessageType(&cl_uplink->header, CLUSTERLEADER);
252                         setUplinkMessageLength(&cl_uplink->header, cl_size);
253                         setUplinkMessageIPv6(&cl_uplink->header, (olsr_cnf->ip_version != AF_INET));
254                         setUplinkMessagePadding(&cl_uplink->header, 0);
255
256                         /* setup cl */
257                         setClusterLeaderVersion(cl, PUD_WIRE_FORMAT_VERSION);
258                         setValidityTime(&cl->validityTime, uplinkUpdateInterval);
259
260                         /* really need 2 memcpy's here because of olsr_cnf->ipsize */
261                         memcpy(cl_originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
262                         memcpy(cl_clusterLeader, &gateway, olsr_cnf->ipsize);
263
264                         txBufferBytesUsed += sizeof(UplinkHeader);
265                         txBufferBytesUsed += cl_size;
266
267                         errno = 0;
268                         if (sendto(fd, &txBuffer, txBufferBytesUsed, 0, (struct sockaddr *) &uplink_addr->in,
269                                         sizeof(uplink_addr->in)) < 0) {
270                                 pudError(true, "Could not send to uplink (size=%u)", txBufferBytesUsed);
271                         }
272 #ifdef PUD_DUMP_GPS_PACKETS_TX_UPLINK
273                         else {
274                                 olsr_printf(0, "%s: packet sent to uplink (%d bytes)\n",
275                                                 PUD_PLUGIN_ABBR, pu_size);
276                                 dump_packet((unsigned char *)&txBuffer, txBufferBytesUsed);
277                         }
278 #endif
279                 }
280         }
281 }
282
283 /*
284  * Timer Callbacks
285  */
286
287 /**
288  The OLSR tx timer callback
289
290  @param context
291  unused
292  */
293 static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
294         txToAllOlsrInterfaces(TX_INTERFACE_OLSR);
295 }
296
297 /**
298  The uplink timer callback
299
300  @param context
301  unused
302  */
303 static void pud_uplink_timer_callback(void *context __attribute__ ((unused))) {
304         txToAllOlsrInterfaces(TX_INTERFACE_UPLINK);
305 }
306
307 /**
308  Restart the OLSR tx timer
309  */
310 static void restartOlsrTimer(MovementState externalState) {
311         if (!restartOlsrTxTimer(
312                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUpdateIntervalStationary() :
313                                         getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
314                 pudError(0, "Could not restart OLSR tx timer, no periodic"
315                                 " position updates will be sent to the OLSR network");
316         }
317 }
318
319 /**
320  Restart the uplink tx timer
321  */
322 static void restartUplinkTimer(MovementState externalState) {
323         if (!restartUplinkTxTimer(
324                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() :
325                                         getUplinkUpdateIntervalMoving(),
326                         &pud_uplink_timer_callback)) {
327                 pudError(0, "Could not restart uplink timer, no periodic"
328                                 " position updates will be uplinked");
329         }
330 }
331
332 static void doImmediateTransmit(MovementState externalState) {
333         TimedTxInterface interfaces = TX_INTERFACE_OLSR; /* always send over olsr */
334         restartOlsrTimer(externalState);
335
336         if (isUplinkAddrSet()) {
337                 interfaces |= TX_INTERFACE_UPLINK;
338                 restartUplinkTimer(externalState);
339         }
340
341         /* do an immediate transmit */
342         txToAllOlsrInterfaces(interfaces);
343 }
344
345 /**
346  The gateway timer callback
347
348  @param context
349  unused
350  */
351 static void pud_gateway_timer_callback(void *context __attribute__ ((unused))) {
352         union olsr_ip_addr bestGateway;
353         bool subStateExternalStateChange;
354         bool externalStateChange;
355         MovementState externalState;
356         TristateBoolean movingNow = TRISTATE_BOOLEAN_UNSET;
357
358         getBestUplinkGateway(&bestGateway);
359
360         (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
361
362         /*
363          * Movement detection
364          */
365
366         if (!ipequal(&bestGateway, &transmitGpsInformation.txGateway)) {
367                 movingNow = TRISTATE_BOOLEAN_SET;
368         }
369
370         /*
371          * State Determination
372          */
373
374         determineStateWithHysteresis(SUBSTATE_GATEWAY, movingNow, &externalState, &externalStateChange,
375                         &subStateExternalStateChange);
376
377         /*
378          * Update transmitGpsInformation
379          */
380
381         if ((externalState == MOVEMENT_STATE_MOVING) || subStateExternalStateChange) {
382                 transmitGpsInformation.txGateway = bestGateway;
383         }
384
385         (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
386
387         if (externalStateChange) {
388                 doImmediateTransmit(externalState);
389         }
390 }
391
392 /**
393  Detemine whether we are moving from the position, by comparing fields from the
394  average position against those of the last transmitted position.
395
396  MUST be called which the position average list locked.
397
398  @param avg
399  the average position
400  @param lastTx
401  the last transmitted position
402  @param result
403  the results of all movement criteria
404  */
405 static void detemineMovingFromPosition(PositionUpdateEntry * avg, PositionUpdateEntry * lastTx, MovementType * result) {
406         /* avg field presence booleans */
407         bool avgHasSpeed;
408         bool avgHasPos;
409         bool avgHasHdop;
410         bool avgHasElv;
411         bool avgHasVdop;
412
413         /* lastTx field presence booleans */bool lastTxHasPos;
414         bool lastTxHasHdop;
415         bool lastTxHasElv;
416         bool lastTxHasVdop;
417
418         /* these have defaults */
419         double dopMultiplier;
420         double avgHdop;
421         double lastTxHdop;
422         double avgVdop;
423         double lastTxVdop;
424
425         /* calculated values and their validity booleans */
426         double hDistance;
427         double vDistance;
428         double hdopDistanceForOutside;
429         double hdopDistanceForInside;
430         double vdopDistanceForOutside;
431         double vdopDistanceForInside;
432         bool hDistanceValid;
433         bool hdopDistanceValid;
434         bool vDistanceValid;
435         bool vdopDistanceValid;
436
437         /*
438          * Validity
439          *
440          * avg  last  movingNow
441          *  0     0   UNKNOWN : can't determine whether we're moving
442          *  0     1   UNKNOWN : can't determine whether we're moving
443          *  1     0   UNKNOWN : can't determine whether we're moving
444          *  1     1   determine via other parameters
445          */
446
447         if (!positionValid(avg)) {
448                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
449                 return;
450         }
451
452         /* avg is valid here */
453
454         if (!positionValid(lastTx)) {
455                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
456                 return;
457         }
458
459         /* both avg and lastTx are valid here */
460
461         /* avg field presence booleans */
462         avgHasSpeed = nmea_INFO_has_field(avg->nmeaInfo.smask, SPEED);
463         avgHasPos = nmea_INFO_has_field(avg->nmeaInfo.smask, LAT)
464                         && nmea_INFO_has_field(avg->nmeaInfo.smask, LON);
465         avgHasHdop = nmea_INFO_has_field(avg->nmeaInfo.smask, HDOP);
466         avgHasElv = nmea_INFO_has_field(avg->nmeaInfo.smask, ELV);
467         avgHasVdop = nmea_INFO_has_field(avg->nmeaInfo.smask, VDOP);
468
469         /* lastTx field presence booleans */
470         lastTxHasPos = nmea_INFO_has_field(lastTx->nmeaInfo.smask, LAT)
471                         && nmea_INFO_has_field(lastTx->nmeaInfo.smask, LON);
472         lastTxHasHdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, HDOP);
473         lastTxHasElv = nmea_INFO_has_field(lastTx->nmeaInfo.smask, ELV);
474         lastTxHasVdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, VDOP);
475
476         /* fill in some values _or_ defaults */
477         dopMultiplier = getDopMultiplier();
478         avgHdop = avgHasHdop ? avg->nmeaInfo.HDOP : getDefaultHdop();
479         lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.HDOP : getDefaultHdop();
480         avgVdop = avgHasVdop ? avg->nmeaInfo.VDOP : getDefaultVdop();
481         lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.VDOP : getDefaultVdop();
482
483         /*
484          * Calculations
485          */
486
487         /* hDistance */
488         if (avgHasPos && lastTxHasPos) {
489                 nmeaPOS avgPos;
490                 nmeaPOS lastTxPos;
491
492                 avgPos.lat = nmea_degree2radian(avg->nmeaInfo.lat);
493                 avgPos.lon = nmea_degree2radian(avg->nmeaInfo.lon);
494
495                 lastTxPos.lat = nmea_degree2radian(lastTx->nmeaInfo.lat);
496                 lastTxPos.lon = nmea_degree2radian(lastTx->nmeaInfo.lon);
497
498                 hDistance = nmea_distance_ellipsoid(&avgPos, &lastTxPos, NULL, NULL);
499                 hDistanceValid = true;
500         } else {
501                 hDistanceValid = false;
502         }
503
504         /* hdopDistance */
505         if (avgHasHdop || lastTxHasHdop) {
506                 hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
507                 hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
508                 hdopDistanceValid = true;
509         } else {
510                 hdopDistanceValid = false;
511         }
512
513         /* vDistance */
514         if (avgHasElv && lastTxHasElv) {
515                 vDistance = fabs(lastTx->nmeaInfo.elv - avg->nmeaInfo.elv);
516                 vDistanceValid = true;
517         } else {
518                 vDistanceValid = false;
519         }
520
521         /* vdopDistance */
522         if (avgHasVdop || lastTxHasVdop) {
523                 vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
524                 vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
525                 vdopDistanceValid = true;
526         } else {
527                 vdopDistanceValid = false;
528         }
529
530         /*
531          * Moving Criteria Evaluation Start
532          * We compare the average position against the last transmitted position.
533          */
534
535         /* Speed */
536         if (avgHasSpeed) {
537                 if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
538                         result->speedOverThreshold = TRISTATE_BOOLEAN_SET;
539                 } else {
540                         result->speedOverThreshold = TRISTATE_BOOLEAN_UNSET;
541                 }
542         }
543
544         /*
545          * Position
546          *
547          * avg  last  hDistanceMoving
548          *  0     0   determine via other parameters
549          *  0     1   determine via other parameters
550          *  1     0   MOVING
551          *  1     1   determine via distance threshold and HDOP
552          */
553         if (avgHasPos && !lastTxHasPos) {
554                 result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
555         } else if (hDistanceValid) {
556                 if (hDistance >= getMovingDistanceThreshold()) {
557                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
558                 } else {
559                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
560                 }
561
562                 /*
563                  * Position with HDOP
564                  *
565                  * avg  last  movingNow
566                  *  0     0   determine via other parameters
567                  *  0     1   determine via position with HDOP (avg has default HDOP)
568                  *  1     0   determine via position with HDOP (lastTx has default HDOP)
569                  *  1     1   determine via position with HDOP
570                  */
571                 if (hdopDistanceValid) {
572                         /* we are outside the HDOP when the HDOPs no longer overlap */
573                         if (hDistance > hdopDistanceForOutside) {
574                                 result->outsideHdop = TRISTATE_BOOLEAN_SET;
575                         } else {
576                                 result->outsideHdop = TRISTATE_BOOLEAN_UNSET;
577                         }
578
579                         /* we are inside the HDOP when the HDOPs fully overlap */
580                         if (hDistance <= hdopDistanceForInside) {
581                                 result->insideHdop = TRISTATE_BOOLEAN_SET;
582                         } else {
583                                 result->insideHdop = TRISTATE_BOOLEAN_UNSET;
584                         }
585                 }
586         }
587
588         /*
589          * Elevation
590          *
591          * avg  last  movingNow
592          *  0     0   determine via other parameters
593          *  0     1   determine via other parameters
594          *  1     0   MOVING
595          *  1     1   determine via distance threshold and VDOP
596          */
597         if (avgHasElv && !lastTxHasElv) {
598                 result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
599         } else if (vDistanceValid) {
600                 if (vDistance >= getMovingDistanceThreshold()) {
601                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
602                 } else {
603                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
604                 }
605
606                 /*
607                  * Elevation with VDOP
608                  *
609                  * avg  last  movingNow
610                  *  0     0   determine via other parameters
611                  *  0     1   determine via elevation with VDOP (avg has default VDOP)
612                  *  1     0   determine via elevation with VDOP (lastTx has default VDOP)
613                  *  1     1   determine via elevation with VDOP
614                  */
615                 if (vdopDistanceValid) {
616                         /* we are outside the VDOP when the VDOPs no longer overlap */
617                         if (vDistance > vdopDistanceForOutside) {
618                                 result->outsideVdop = TRISTATE_BOOLEAN_SET;
619                         } else {
620                                 result->outsideVdop = TRISTATE_BOOLEAN_UNSET;
621                         }
622
623                         /* we are inside the VDOP when the VDOPs fully overlap */
624                         if (vDistance <= vdopDistanceForInside) {
625                                 result->insideVdop = TRISTATE_BOOLEAN_SET;
626                         } else {
627                                 result->insideVdop = TRISTATE_BOOLEAN_UNSET;
628                         }
629                 }
630         }
631
632         /*
633          * Moving Criteria Evaluation End
634          */
635
636         /* accumulate inside criteria */
637         if ((result->insideHdop == TRISTATE_BOOLEAN_SET) && (result->insideVdop == TRISTATE_BOOLEAN_SET)) {
638                 result->inside = TRISTATE_BOOLEAN_SET;
639         } else if ((result->insideHdop == TRISTATE_BOOLEAN_UNSET) || (result->insideVdop == TRISTATE_BOOLEAN_UNSET)) {
640                 result->inside = TRISTATE_BOOLEAN_UNSET;
641         }
642
643         /* accumulate outside criteria */
644         if ((result->outsideHdop == TRISTATE_BOOLEAN_SET) || (result->outsideVdop == TRISTATE_BOOLEAN_SET)) {
645                 result->outside = TRISTATE_BOOLEAN_SET;
646         } else if ((result->outsideHdop == TRISTATE_BOOLEAN_UNSET)
647                         || (result->outsideVdop == TRISTATE_BOOLEAN_UNSET)) {
648                 result->outside = TRISTATE_BOOLEAN_UNSET;
649         }
650
651         /* accumulate threshold criteria */
652         if ((result->speedOverThreshold == TRISTATE_BOOLEAN_SET)
653                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_SET)
654                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_SET)) {
655                 result->overThresholds = TRISTATE_BOOLEAN_SET;
656         } else if ((result->speedOverThreshold == TRISTATE_BOOLEAN_UNSET)
657                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)
658                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)) {
659                 result->overThresholds = TRISTATE_BOOLEAN_UNSET;
660         }
661
662         /* accumulate moving criteria */
663         if ((result->overThresholds == TRISTATE_BOOLEAN_SET) || (result->outside == TRISTATE_BOOLEAN_SET)) {
664                 result->moving = TRISTATE_BOOLEAN_SET;
665         } else if ((result->overThresholds == TRISTATE_BOOLEAN_UNSET)
666                         && (result->outside == TRISTATE_BOOLEAN_UNSET)) {
667                 result->moving = TRISTATE_BOOLEAN_UNSET;
668         }
669
670         return;
671 }
672
673 /**
674  Update the latest GPS information. This function is called when a packet is
675  received from a rxNonOlsr interface, containing one or more NMEA strings with
676  GPS information.
677
678  @param rxBuffer
679  the receive buffer with the received NMEA string(s)
680  @param rxCount
681  the number of bytes in the receive buffer
682
683  @return
684  - false on failure
685  - true otherwise
686  */
687 bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount) {
688         static const char * rxBufferPrefix = "$GP";
689         static const size_t rxBufferPrefixLength = 3;
690
691         bool retval = false;
692         PositionUpdateEntry * incomingEntry;
693         PositionUpdateEntry * posAvgEntry;
694         MovementType movementResult;
695         bool subStateExternalStateChange;
696         bool externalStateChange;
697         bool updateTransmitGpsInformation = false;
698         PositionUpdateEntry txPosition;
699         MovementState externalState;
700
701         /* do not process when the message does not start with $GP */
702         if ((rxCount < rxBufferPrefixLength) || (strncmp((char *) rxBuffer,
703                         rxBufferPrefix, rxBufferPrefixLength) != 0)) {
704                 return true;
705         }
706
707         /* parse all NMEA strings in the rxBuffer into the incoming entry */
708         incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
709         nmea_zero_INFO(&incomingEntry->nmeaInfo);
710         nmea_parse(&nmeaParser, (char *) rxBuffer, rxCount,
711                         &incomingEntry->nmeaInfo);
712
713 #if defined(PUD_DUMP_AVERAGING)
714         dump_nmeaInfo(&incomingEntry->nmeaInfo,
715                         "receiverUpdateGpsInformation: incoming entry");
716 #endif /* PUD_DUMP_AVERAGING */
717
718         /* ignore when no useful information */
719         if (incomingEntry->nmeaInfo.smask == GPNON) {
720                 retval = true;
721                 goto end;
722         }
723
724         nmea_INFO_sanitise(&incomingEntry->nmeaInfo);
725
726 #if defined(PUD_DUMP_AVERAGING)
727         dump_nmeaInfo(&incomingEntry->nmeaInfo,
728                         "receiverUpdateGpsInformation: incoming entry after sanitise");
729 #endif /* PUD_DUMP_AVERAGING */
730
731         /* we always work with latitude, longitude in degrees and DOPs in meters */
732         nmea_INFO_unit_conversion(&incomingEntry->nmeaInfo);
733
734 #if defined(PUD_DUMP_AVERAGING)
735         dump_nmeaInfo(&incomingEntry->nmeaInfo,
736                         "receiverUpdateGpsInformation: incoming entry after unit conversion");
737 #endif /* PUD_DUMP_AVERAGING */
738
739         /*
740          * Averaging
741          */
742
743         if (getInternalState(SUBSTATE_POSITION) == MOVEMENT_STATE_MOVING) {
744                 /* flush average: keep only the incoming entry */
745                 flushPositionAverageList(&positionAverageList);
746         }
747         addNewPositionToAverage(&positionAverageList, incomingEntry);
748         posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
749
750 #if defined(PUD_DUMP_AVERAGING)
751         dump_nmeaInfo(&posAvgEntry->nmeaInfo,
752                         "receiverUpdateGpsInformation: posAvgEntry");
753 #endif /* PUD_DUMP_AVERAGING */
754
755         /*
756          * Movement detection
757          */
758
759         clearMovementType(&movementResult);
760
761         (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
762         txPosition = transmitGpsInformation.txPosition;
763
764         detemineMovingFromPosition(posAvgEntry, &txPosition, &movementResult);
765
766         /*
767          * State Determination
768          */
769
770         determineStateWithHysteresis(SUBSTATE_POSITION, movementResult.moving, &externalState, &externalStateChange,
771                         &subStateExternalStateChange);
772
773         /*
774          * Update transmitGpsInformation
775          */
776
777         updateTransmitGpsInformation = subStateExternalStateChange
778                         || (positionValid(posAvgEntry) && !positionValid(&txPosition))
779                         || (movementResult.inside == TRISTATE_BOOLEAN_SET);
780
781         if ((externalState == MOVEMENT_STATE_MOVING) || updateTransmitGpsInformation) {
782                 transmitGpsInformation.txPosition.nmeaInfo = posAvgEntry->nmeaInfo;
783                 transmitGpsInformation.positionUpdated = true;
784
785 #if defined(PUD_DUMP_AVERAGING)
786                 dump_nmeaInfo(&posAvgEntry->nmeaInfo,
787                         "receiverUpdateGpsInformation: transmitGpsInformation");
788 #endif /* PUD_DUMP_AVERAGING */
789         }
790
791         (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
792
793         if (externalStateChange) {
794                 doImmediateTransmit(externalState);
795         }
796
797         retval = true;
798
799         end:
800         return retval;
801 }
802
803 /**
804  Start the receiver
805
806  @return
807  - false on failure
808  - true otherwise
809  */
810 bool startReceiver(void) {
811         MovementState externalState;
812
813         pthread_mutexattr_t attr;
814         if (pthread_mutexattr_init(&attr)) {
815                 return false;
816         }
817         if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) {
818                 return false;
819         }
820         if (pthread_mutex_init(&transmitGpsInformation.mutex, &attr)) {
821                 return false;
822         }
823
824         if (!nmea_parser_init(&nmeaParser)) {
825                 pudError(false, "Could not initialise NMEA parser");
826                 return false;
827         }
828
829         nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
830         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
831         transmitGpsInformation.positionUpdated = false;
832
833         initPositionAverageList(&positionAverageList, getAverageDepth());
834
835         if (!initOlsrTxTimer()) {
836                 stopReceiver();
837                 return false;
838         }
839
840         if (!initUplinkTxTimer()) {
841                 stopReceiver();
842                 return false;
843         }
844
845         if (!initGatewayTimer()) {
846                 stopReceiver();
847                 return false;
848         }
849
850         externalState = getExternalState();
851         restartOlsrTimer(externalState);
852         restartUplinkTimer(externalState);
853         if (!restartGatewayTimer(getGatewayDeterminationInterval(), &pud_gateway_timer_callback)) {
854                 pudError(0, "Could not start gateway timer");
855                 stopReceiver();
856                 return false;
857         }
858
859         return true;
860 }
861
862 /**
863  Stop the receiver
864  */
865 void stopReceiver(void) {
866         destroyGatewayTimer();
867         destroyUplinkTxTimer();
868         destroyOlsrTxTimer();
869
870         destroyPositionAverageList(&positionAverageList);
871
872         (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
873         nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
874         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
875         transmitGpsInformation.positionUpdated = false;
876         (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
877
878         nmea_parser_destroy(&nmeaParser);
879
880         (void) pthread_mutex_destroy(&transmitGpsInformation.mutex);
881 }