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