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