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