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