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