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