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