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