PUD: rxBufferPrefixLength is a static const
[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         static const char * rxBufferPrefix = "$GP";
420         static const size_t rxBufferPrefixLength = 3;
421
422         bool retval = false;
423         PositionUpdateEntry * incomingEntry;
424         MovementState currentState = state.state;
425         MovementState newState = MOVING;
426         PositionUpdateEntry * posAvgEntry;
427         PositionUpdateEntry * lastTxEntry;
428         TristateBoolean movingNow;
429
430         /* do not process when the message does not start with $GP */
431         if ((rxCount < rxBufferPrefixLength) || (strncmp((char *) rxBuffer,
432                         rxBufferPrefix, rxBufferPrefixLength) != 0)) {
433                 return true;
434         }
435
436         (void) pthread_mutex_lock(&positionAverageList.mutex);
437
438         /* parse all NMEA strings in the rxBuffer into the incoming entry */
439         incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
440         nmea_zero_INFO(&incomingEntry->nmeaInfo);
441         nmea_parse(&nmeaParser, (char *) rxBuffer, rxCount,
442                         &incomingEntry->nmeaInfo);
443
444 #if defined(PUD_DUMP_AVERAGING)
445         dump_nmeaInfo(&incomingEntry->nmeaInfo,
446                         "receiverUpdateGpsInformation: incoming entry");
447 #endif /* PUD_DUMP_AVERAGING */
448
449         /* ignore when no useful information */
450         if (incomingEntry->nmeaInfo.smask == GPNON) {
451                 retval = true;
452                 goto end;
453         }
454
455         /* we always work with latitude, longitude in degrees and DOPs in meters */
456         nmeaInfoUnitConversion(&incomingEntry->nmeaInfo);
457
458 #if defined(PUD_DUMP_AVERAGING)
459         dump_nmeaInfo(&incomingEntry->nmeaInfo,
460                         "receiverUpdateGpsInformation: incoming entry after unit conversion");
461 #endif /* PUD_DUMP_AVERAGING */
462
463         sanitiseNmeaInfo(&incomingEntry->nmeaInfo);
464
465 #if defined(PUD_DUMP_AVERAGING)
466         dump_nmeaInfo(&incomingEntry->nmeaInfo,
467                         "receiverUpdateGpsInformation: incoming entry after sanitise");
468 #endif /* PUD_DUMP_AVERAGING */
469
470         /* flush on INIT and MOVING */
471         if (currentState != STATIONARY) {
472                 flushPositionAverageList(&positionAverageList);
473         }
474
475         /* add to average */
476         addNewPositionToAverage(&positionAverageList, incomingEntry);
477
478         posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
479         lastTxEntry = getPositionAverageEntry(&positionAverageList, LASTTX);
480
481         movingNow = detemineMoving(posAvgEntry, lastTxEntry);
482
483 #if defined(PUD_DUMP_AVERAGING)
484         olsr_printf(0, "receiverUpdateGpsInformation: currentState = %s\n",
485                         MovementStateToString(currentState));
486         olsr_printf(0, "receiverUpdateGpsInformation: movingNow    = %s\n",
487                         TristateBooleanToString(movingNow));
488 #endif /* PUD_DUMP_AVERAGING */
489
490         /* determine the new state */
491         switch (currentState) {
492                 case STATIONARY:
493                         if (movingNow == SET) {
494                                 /* delay going to moving a bit */
495                                 state.hysteresisCounter++;
496
497                                 if (state.hysteresisCounter >= getHysteresisCountToMoving()) {
498                                         /* outside the hysteresis range, go to moving */
499                                         newState = MOVING;
500                                 } else {
501                                         /* within the hysteresis range, stay in stationary */
502                                         newState = STATIONARY;
503                                 }
504                         } else { /* unset and unknown */
505                                 newState = STATIONARY;
506                         }
507                         break;
508
509                 case MOVING:
510                         if (movingNow == UNSET) {
511                                 /* delay going to stationary a bit */
512                                 state.hysteresisCounter++;
513
514                                 if (state.hysteresisCounter >= getHysteresisCountToStationary()) {
515                                         /* outside the hysteresis range, go to stationary */
516                                         newState = STATIONARY;
517                                 } else {
518                                         /* within the hysteresis range, stay in moving */
519                                         newState = MOVING;
520                                 }
521                         } else { /* set and unknown */
522                                 newState = MOVING;
523                         }
524                         break;
525
526                 default:
527                         /* when unknown do just as if we transition into moving */
528                         newState = MOVING;
529                         break;
530         }
531
532 #if defined(PUD_DUMP_AVERAGING)
533         olsr_printf(0, "receiverUpdateGpsInformation: newState = %s\n",
534                         MovementStateToString(newState));
535 #endif /* PUD_DUMP_AVERAGING */
536
537         /* perform state change actions */
538         if (currentState != newState) {
539                 /* reset the hysteresis counter upon state change */
540                 state.hysteresisCounter = 0;
541
542                 /* set the new state */
543                 state.state = newState;
544
545                 /* restart the timer for the new state */
546                 if (!restartTimer(
547                                 (newState == STATIONARY) ? getUpdateIntervalStationary()
548                                                 : getUpdateIntervalMoving())) {
549                         pudError(0, "Could not restart receiver timer, no position"
550                                 " updates will be sent to the OLSR network");
551                         goto end;
552                 }
553         }
554
555 #if defined(PUD_DUMP_AVERAGING)
556         dump_nmeaInfo(&posAvgEntry->nmeaInfo,
557                         "receiverUpdateGpsInformation: posAvgEntry");
558 #endif /* PUD_DUMP_AVERAGING */
559
560         /*
561          * Update transmitGpsInformation
562          */
563
564         (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
565         transmitGpsInformation.invalid
566                         = (posAvgEntry->nmeaInfo.fix == NMEA_FIX_BAD);
567
568         /* Only when not invalid we update the transmitGpsInformation */
569         if (!transmitGpsInformation.invalid) {
570                 if ((movingNow == SET) || (state.state != STATIONARY)) {
571                         /* Copy posAvgEntry into average list txPosition and
572                          * transmitGpsInformation when we consider ourselves as moving
573                          * (independent of the state) or when we are in the INIT or
574                          * MOVING state */
575                         memcpy(&positionAverageList.txPosition.nmeaInfo,
576                                         &posAvgEntry->nmeaInfo, sizeof(nmeaINFO));
577                         memcpy(&transmitGpsInformation.nmeaInfo, &posAvgEntry->nmeaInfo,
578                                         sizeof(nmeaINFO));
579                         transmitGpsInformation.updated = true;
580                 } else /* (movingNow != SET) && (state.state == STATIONARY) */{
581                         /* no nothing */
582                 }
583         }
584         (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
585
586 #if defined(PUD_DUMP_AVERAGING)
587         dump_nmeaInfo(&transmitGpsInformation.nmeaInfo,
588                         "receiverUpdateGpsInformation: transmitGpsInformation");
589 #endif /* PUD_DUMP_AVERAGING */
590
591         /* on a state change do an immediate transmit */
592         if (currentState != newState) {
593                 txToAllOlsrInterfaces();
594         }
595
596         retval = true;
597
598         end: (void) pthread_mutex_unlock(&positionAverageList.mutex);
599         return retval;
600 }
601
602 /*
603  * Timer
604  */
605
606 /** The timer cookie, used to trace back the originator in debug */
607 struct olsr_cookie_info *pud_receiver_timer_cookie = NULL;
608
609 /** The timer */
610 struct timer_entry * pud_receiver_timer = NULL;
611
612 /**
613  The timer callback
614
615  @param context
616  unused
617  */
618 static void pud_receiver_timer_callback(void *context __attribute__ ((unused))) {
619         txToAllOlsrInterfaces();
620 }
621
622 /**
623  Start the receiver timer. Does nothing when the timer is already running.
624
625  @param interval
626  The interval in seconds
627
628  @return
629  - false on failure
630  - true otherwise
631  */
632 static int startTimer(unsigned long long interval) {
633         if (pud_receiver_timer == NULL) {
634                 pud_receiver_timer = olsr_start_timer(interval * MSEC_PER_SEC, 0,
635                                 OLSR_TIMER_PERIODIC, &pud_receiver_timer_callback, NULL,
636                                 pud_receiver_timer_cookie);
637                 if (pud_receiver_timer == NULL) {
638                         stopReceiver();
639                         return false;
640                 }
641         }
642
643         return true;
644 }
645
646 /**
647  Stop the receiver timer
648  */
649 static void stopTimer(void) {
650         if (pud_receiver_timer != NULL) {
651                 olsr_stop_timer(pud_receiver_timer);
652                 pud_receiver_timer = NULL;
653         }
654 }
655
656 /**
657  Restart the receiver timer
658
659  @param interval
660  The interval in seconds
661
662  @return
663  - false on failure
664  - true otherwise
665  */
666 static int restartTimer(unsigned long long interval) {
667         stopTimer();
668         return startTimer(interval);
669 }
670
671 /*
672  * Receiver start/stop
673  */
674
675 /**
676  Start the receiver
677
678  @return
679  - false on failure
680  - true otherwise
681  */
682 bool startReceiver(void) {
683         pthread_mutexattr_t attr;
684         if (pthread_mutexattr_init(&attr)) {
685                 return false;
686         }
687         if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) {
688                 return false;
689         }
690         if (pthread_mutex_init(&transmitGpsInformation.mutex, &attr)) {
691                 return false;
692         }
693
694         if (!nmea_parser_init(&nmeaParser)) {
695                 pudError(false, "Could not initialise NMEA parser");
696                 return false;
697         }
698
699         nmea_zero_INFO(&transmitGpsInformation.nmeaInfo);
700
701         state.state = INIT;
702         state.hysteresisCounter = 0;
703
704         initPositionAverageList(&positionAverageList, getAverageDepth());
705
706         if (pud_receiver_timer_cookie == NULL) {
707                 pud_receiver_timer_cookie = olsr_alloc_cookie(
708                                 PUD_PLUGIN_ABBR ": receiver", OLSR_COOKIE_TYPE_TIMER);
709                 if (pud_receiver_timer_cookie == NULL) {
710                         stopReceiver();
711                         return false;
712                 }
713         }
714
715         return true;
716 }
717
718 /**
719  Stop the receiver
720  */
721 void stopReceiver(void) {
722         stopTimer();
723
724         if (pud_receiver_timer_cookie != NULL) {
725                 olsr_free_cookie(pud_receiver_timer_cookie);
726                 pud_receiver_timer_cookie = NULL;
727         }
728
729         destroyPositionAverageList(&positionAverageList);
730
731         state.hysteresisCounter = 0;
732         state.state = INIT;
733
734         nmea_zero_INFO(&transmitGpsInformation.nmeaInfo);
735         transmitGpsInformation.updated = false;
736         transmitGpsInformation.invalid = true;
737
738         nmea_parser_destroy(&nmeaParser);
739
740         (void) pthread_mutex_destroy(&transmitGpsInformation.mutex);
741 }