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