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