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