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