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