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