30e492aad09147f34f33d7c7931da8b526f83cf8
[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                         r = net_outbuffer_push(ifn, pu, pu_size);
166                         if (r != (int) pu_size) {
167                                 pudError(
168                                                 false,
169                                                 "Could not send to OLSR interface %s: %s (size=%u, r=%d)",
170                                                 ifn->int_name,
171                                                 ((r == -1) ? "no buffer was found" :
172                                                         (r == 0) ? "there was not enough room in the buffer" : "unknown reason"), pu_size, r);
173                         }
174                 }
175
176                 /* loopback to tx interface when so configured */
177                 if (getUseLoopback()) {
178                         (void) packetReceivedFromOlsr(pu, NULL, NULL);
179                 }
180         }
181
182         /* push out over uplink when an uplink is configured */
183         if (((interfaces & TX_INTERFACE_UPLINK) != 0) && isUplinkAddrSet()) {
184                 int fd = getDownlinkSocketFd();
185                 if (fd != -1) {
186                         union olsr_sockaddr * uplink_addr = getUplinkAddr();
187                         void * addr;
188                         socklen_t addrSize;
189
190                         UplinkMessage * cl_uplink = (UplinkMessage *) &txBuffer[txBufferBytesUsed];
191                         UplinkClusterLeader * cl = &cl_uplink->msg.clusterLeader;
192                         union olsr_ip_addr * cl_originator = getClusterLeaderOriginator(olsr_cnf->ip_version, cl);
193                         union olsr_ip_addr * cl_clusterLeader = getClusterLeaderClusterLeader(olsr_cnf->ip_version, cl);
194
195                         unsigned int cl_size =
196                                         sizeof(UplinkClusterLeader) - sizeof(cl->leader)
197                                                         + ((olsr_cnf->ip_version == AF_INET) ? sizeof(cl->leader.v4) :
198                                                                         sizeof(cl->leader.v6));
199
200                         unsigned long long uplinkUpdateInterval =
201                                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() : getUplinkUpdateIntervalMoving();
202
203                         if (uplink_addr->in.sa_family == AF_INET) {
204                                 addr = &uplink_addr->in4;
205                                 addrSize = sizeof(struct sockaddr_in);
206                         } else {
207                                 addr = &uplink_addr->in6;
208                                 addrSize = sizeof(struct sockaddr_in6);
209                         }
210
211                         /*
212                          * position update message (pu)
213                          */
214
215                         /* set header fields in position update uplink message and adjust
216                          * the validity time to the uplink validity time */
217                         if (pu_size > 0) {
218                                 PudOlsrPositionUpdate * pu_gpsMessage = getOlsrMessagePayload(olsr_cnf->ip_version, pu);
219
220                                 setUplinkMessageType(&pu_uplink->header, POSITION);
221                                 setUplinkMessageLength(&pu_uplink->header, pu_size);
222                                 setUplinkMessageIPv6(&pu_uplink->header, (olsr_cnf->ip_version != AF_INET));
223                                 setUplinkMessagePadding(&pu_uplink->header, 0);
224
225                                 /* fixup validity time */
226                                 setValidityTime(&pu_gpsMessage->validityTime, uplinkUpdateInterval);
227                         }
228
229                         /*
230                          * cluster leader message (cl)
231                          */
232
233                         /* set cl_uplink header fields */
234                         setUplinkMessageType(&cl_uplink->header, CLUSTERLEADER);
235                         setUplinkMessageLength(&cl_uplink->header, cl_size);
236                         setUplinkMessageIPv6(&cl_uplink->header, (olsr_cnf->ip_version != AF_INET));
237                         setUplinkMessagePadding(&cl_uplink->header, 0);
238
239                         /* setup cl */
240                         setClusterLeaderVersion(cl, PUD_WIRE_FORMAT_VERSION);
241                         setValidityTime(&cl->validityTime, uplinkUpdateInterval);
242
243                         /* really need 2 memcpy's here because of olsr_cnf->ipsize */
244                         memcpy(cl_originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
245                         memcpy(cl_clusterLeader, &gateway, olsr_cnf->ipsize);
246
247                         txBufferBytesUsed += sizeof(UplinkHeader);
248                         txBufferBytesUsed += cl_size;
249
250                         errno = 0;
251                         if (sendto(fd, &txBuffer, txBufferBytesUsed, 0, addr, addrSize) < 0) {
252                                 pudError(true, "Could not send to uplink (size=%u)", txBufferBytesUsed);
253                         }
254                 }
255         }
256 }
257
258 /*
259  * Timer Callbacks
260  */
261
262 /**
263  The OLSR tx timer callback
264
265  @param context
266  unused
267  */
268 static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
269         txToAllOlsrInterfaces(TX_INTERFACE_OLSR);
270 }
271
272 /**
273  The uplink timer callback
274
275  @param context
276  unused
277  */
278 static void pud_uplink_timer_callback(void *context __attribute__ ((unused))) {
279         txToAllOlsrInterfaces(TX_INTERFACE_UPLINK);
280 }
281
282 /**
283  Restart the OLSR tx timer
284  */
285 static void restartOlsrTimer(MovementState externalState) {
286         if (!restartOlsrTxTimer(
287                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUpdateIntervalStationary() :
288                                         getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
289                 pudError(0, "Could not restart OLSR tx timer, no periodic"
290                                 " position updates will be sent to the OLSR network");
291         }
292 }
293
294 /**
295  Restart the uplink tx timer
296  */
297 static void restartUplinkTimer(MovementState externalState) {
298         if (!restartUplinkTxTimer(
299                         (externalState == MOVEMENT_STATE_STATIONARY) ? getUplinkUpdateIntervalStationary() :
300                                         getUplinkUpdateIntervalMoving(),
301                         &pud_uplink_timer_callback)) {
302                 pudError(0, "Could not restart uplink timer, no periodic"
303                                 " position updates will be uplinked");
304         }
305 }
306
307 static void doImmediateTransmit(MovementState externalState) {
308         TimedTxInterface interfaces = TX_INTERFACE_OLSR; /* always send over olsr */
309         restartOlsrTimer(externalState);
310
311         if (isUplinkAddrSet()) {
312                 interfaces |= TX_INTERFACE_UPLINK;
313                 restartUplinkTimer(externalState);
314         }
315
316         /* do an immediate transmit */
317         txToAllOlsrInterfaces(interfaces);
318 }
319
320 /**
321  The gateway timer callback
322
323  @param context
324  unused
325  */
326 static void pud_gateway_timer_callback(void *context __attribute__ ((unused))) {
327         union olsr_ip_addr bestGateway;
328         bool externalStateChange;
329         MovementState externalState;
330         TristateBoolean movingNow = TRISTATE_BOOLEAN_UNSET;
331
332         getBestUplinkGateway(&bestGateway);
333
334         /*
335          * Movement detection
336          */
337
338         if (!ipequal(&bestGateway, &transmitGpsInformation.txGateway)) {
339                 movingNow = TRISTATE_BOOLEAN_SET;
340         }
341
342         /*
343          * State Determination
344          */
345
346         determineStateWithHysteresis(SUBSTATE_GATEWAY, movingNow, &externalState, &externalStateChange, NULL);
347
348         /*
349          * Update transmitGpsInformation
350          */
351
352         if (movingNow == TRISTATE_BOOLEAN_SET) {
353                 transmitGpsInformation.txGateway = bestGateway;
354         }
355
356         if (externalStateChange) {
357                 doImmediateTransmit(externalState);
358         }
359 }
360
361 /**
362  Detemine whether we are moving from the position, by comparing fields from the
363  average position against those of the last transmitted position.
364
365  MUST be called which the position average list locked.
366
367  @param avg
368  the average position
369  @param lastTx
370  the last transmitted position
371  @param result
372  the results of all movement criteria
373  */
374 static void detemineMovingFromPosition(PositionUpdateEntry * avg, PositionUpdateEntry * lastTx, MovementType * result) {
375         /* avg field presence booleans */
376         bool avgHasSpeed;
377         bool avgHasPos;
378         bool avgHasHdop;
379         bool avgHasElv;
380         bool avgHasVdop;
381
382         /* lastTx field presence booleans */bool lastTxHasPos;
383         bool lastTxHasHdop;
384         bool lastTxHasElv;
385         bool lastTxHasVdop;
386
387         /* these have defaults */
388         double dopMultiplier;
389         double avgHdop;
390         double lastTxHdop;
391         double avgVdop;
392         double lastTxVdop;
393
394         /* calculated values and their validity booleans */
395         double hDistance;
396         double vDistance;
397         double hdopDistanceForOutside;
398         double hdopDistanceForInside;
399         double vdopDistanceForOutside;
400         double vdopDistanceForInside;
401         bool hDistanceValid;
402         bool hdopDistanceValid;
403         bool vDistanceValid;
404         bool vdopDistanceValid;
405
406         /*
407          * Validity
408          *
409          * avg  last  movingNow
410          *  0     0   UNKNOWN : can't determine whether we're moving
411          *  0     1   UNKNOWN : can't determine whether we're moving
412          *  1     0   UNKNOWN : can't determine whether we're moving
413          *  1     1   determine via other parameters
414          */
415
416         if (!positionValid(avg)) {
417                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
418                 return;
419         }
420
421         /* avg is valid here */
422
423         if (!positionValid(lastTx)) {
424                 result->moving = TRISTATE_BOOLEAN_UNKNOWN;
425                 return;
426         }
427
428         /* both avg and lastTx are valid here */
429
430         /* avg field presence booleans */
431         avgHasSpeed = nmea_INFO_is_present(avg->nmeaInfo.present, SPEED);
432         avgHasPos = nmea_INFO_is_present(avg->nmeaInfo.present, LAT)
433                         && nmea_INFO_is_present(avg->nmeaInfo.present, LON);
434         avgHasHdop = nmea_INFO_is_present(avg->nmeaInfo.present, HDOP);
435         avgHasElv = nmea_INFO_is_present(avg->nmeaInfo.present, ELV);
436         avgHasVdop = nmea_INFO_is_present(avg->nmeaInfo.present, VDOP);
437
438         /* lastTx field presence booleans */
439         lastTxHasPos = nmea_INFO_is_present(lastTx->nmeaInfo.present, LAT)
440                         && nmea_INFO_is_present(lastTx->nmeaInfo.present, LON);
441         lastTxHasHdop = nmea_INFO_is_present(lastTx->nmeaInfo.present, HDOP);
442         lastTxHasElv = nmea_INFO_is_present(lastTx->nmeaInfo.present, ELV);
443         lastTxHasVdop = nmea_INFO_is_present(lastTx->nmeaInfo.present, VDOP);
444
445         /* fill in some values _or_ defaults */
446         dopMultiplier = getDopMultiplier();
447         avgHdop = avgHasHdop ? avg->nmeaInfo.HDOP : getDefaultHdop();
448         lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.HDOP : getDefaultHdop();
449         avgVdop = avgHasVdop ? avg->nmeaInfo.VDOP : getDefaultVdop();
450         lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.VDOP : getDefaultVdop();
451
452         /*
453          * Calculations
454          */
455
456         /* hDistance */
457         if (avgHasPos && lastTxHasPos) {
458                 nmeaPOS avgPos;
459                 nmeaPOS lastTxPos;
460
461                 avgPos.lat = nmea_degree2radian(avg->nmeaInfo.lat);
462                 avgPos.lon = nmea_degree2radian(avg->nmeaInfo.lon);
463
464                 lastTxPos.lat = nmea_degree2radian(lastTx->nmeaInfo.lat);
465                 lastTxPos.lon = nmea_degree2radian(lastTx->nmeaInfo.lon);
466
467                 hDistance = fabs(nmea_distance_ellipsoid(&avgPos, &lastTxPos, NULL, NULL));
468                 hDistanceValid = true;
469         } else {
470                 hDistanceValid = false;
471         }
472
473         /* hdopDistance */
474         if (avgHasHdop || lastTxHasHdop) {
475                 hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
476                 hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
477                 hdopDistanceValid = true;
478         } else {
479                 hdopDistanceValid = false;
480         }
481
482         /* vDistance */
483         if (avgHasElv && lastTxHasElv) {
484                 vDistance = fabs(lastTx->nmeaInfo.elv - avg->nmeaInfo.elv);
485                 vDistanceValid = true;
486         } else {
487                 vDistanceValid = false;
488         }
489
490         /* vdopDistance */
491         if (avgHasVdop || lastTxHasVdop) {
492                 vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
493                 vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
494                 vdopDistanceValid = true;
495         } else {
496                 vdopDistanceValid = false;
497         }
498
499         /*
500          * Moving Criteria Evaluation Start
501          * We compare the average position against the last transmitted position.
502          */
503
504         /* Speed */
505         if (avgHasSpeed) {
506                 if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
507                         result->speedOverThreshold = TRISTATE_BOOLEAN_SET;
508                 } else {
509                         result->speedOverThreshold = TRISTATE_BOOLEAN_UNSET;
510                 }
511         }
512
513         /*
514          * Position
515          *
516          * avg  last  hDistanceMoving
517          *  0     0   determine via other parameters
518          *  0     1   determine via other parameters
519          *  1     0   MOVING
520          *  1     1   determine via distance threshold and HDOP
521          */
522         if (avgHasPos && !lastTxHasPos) {
523                 result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
524         } else if (hDistanceValid) {
525                 if (hDistance >= getMovingDistanceThreshold()) {
526                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
527                 } else {
528                         result->hDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
529                 }
530
531                 /*
532                  * Position with HDOP
533                  *
534                  * avg  last  movingNow
535                  *  0     0   determine via other parameters
536                  *  0     1   determine via position with HDOP (avg has default HDOP)
537                  *  1     0   determine via position with HDOP (lastTx has default HDOP)
538                  *  1     1   determine via position with HDOP
539                  */
540                 if (hdopDistanceValid) {
541                         /* we are outside the HDOP when the HDOPs no longer overlap */
542                         if (hDistance > hdopDistanceForOutside) {
543                                 result->outsideHdop = TRISTATE_BOOLEAN_SET;
544                         } else {
545                                 result->outsideHdop = TRISTATE_BOOLEAN_UNSET;
546                         }
547
548                         /* we are inside the HDOP when the HDOPs fully overlap */
549                         if (hDistance <= hdopDistanceForInside) {
550                                 result->insideHdop = TRISTATE_BOOLEAN_SET;
551                         } else {
552                                 result->insideHdop = TRISTATE_BOOLEAN_UNSET;
553                         }
554                 }
555         }
556
557         /*
558          * Elevation
559          *
560          * avg  last  movingNow
561          *  0     0   determine via other parameters
562          *  0     1   determine via other parameters
563          *  1     0   MOVING
564          *  1     1   determine via distance threshold and VDOP
565          */
566         if (avgHasElv && !lastTxHasElv) {
567                 result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
568         } else if (vDistanceValid) {
569                 if (vDistance >= getMovingDistanceThreshold()) {
570                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_SET;
571                 } else {
572                         result->vDistanceOverThreshold = TRISTATE_BOOLEAN_UNSET;
573                 }
574
575                 /*
576                  * Elevation with VDOP
577                  *
578                  * avg  last  movingNow
579                  *  0     0   determine via other parameters
580                  *  0     1   determine via elevation with VDOP (avg has default VDOP)
581                  *  1     0   determine via elevation with VDOP (lastTx has default VDOP)
582                  *  1     1   determine via elevation with VDOP
583                  */
584                 if (vdopDistanceValid) {
585                         /* we are outside the VDOP when the VDOPs no longer overlap */
586                         if (vDistance > vdopDistanceForOutside) {
587                                 result->outsideVdop = TRISTATE_BOOLEAN_SET;
588                         } else {
589                                 result->outsideVdop = TRISTATE_BOOLEAN_UNSET;
590                         }
591
592                         /* we are inside the VDOP when the VDOPs fully overlap */
593                         if (vDistance <= vdopDistanceForInside) {
594                                 result->insideVdop = TRISTATE_BOOLEAN_SET;
595                         } else {
596                                 result->insideVdop = TRISTATE_BOOLEAN_UNSET;
597                         }
598                 }
599         }
600
601         /*
602          * Moving Criteria Evaluation End
603          */
604
605         /* accumulate inside criteria */
606         if ((result->insideHdop == TRISTATE_BOOLEAN_SET) && (result->insideVdop == TRISTATE_BOOLEAN_SET)) {
607                 result->inside = TRISTATE_BOOLEAN_SET;
608         } else if ((result->insideHdop == TRISTATE_BOOLEAN_UNSET) || (result->insideVdop == TRISTATE_BOOLEAN_UNSET)) {
609                 result->inside = TRISTATE_BOOLEAN_UNSET;
610         }
611
612         /* accumulate outside criteria */
613         if ((result->outsideHdop == TRISTATE_BOOLEAN_SET) || (result->outsideVdop == TRISTATE_BOOLEAN_SET)) {
614                 result->outside = TRISTATE_BOOLEAN_SET;
615         } else if ((result->outsideHdop == TRISTATE_BOOLEAN_UNSET)
616                         || (result->outsideVdop == TRISTATE_BOOLEAN_UNSET)) {
617                 result->outside = TRISTATE_BOOLEAN_UNSET;
618         }
619
620         /* accumulate threshold criteria */
621         if ((result->speedOverThreshold == TRISTATE_BOOLEAN_SET)
622                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_SET)
623                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_SET)) {
624                 result->overThresholds = TRISTATE_BOOLEAN_SET;
625         } else if ((result->speedOverThreshold == TRISTATE_BOOLEAN_UNSET)
626                         || (result->hDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)
627                         || (result->vDistanceOverThreshold == TRISTATE_BOOLEAN_UNSET)) {
628                 result->overThresholds = TRISTATE_BOOLEAN_UNSET;
629         }
630
631         /* accumulate moving criteria */
632         if ((result->overThresholds == TRISTATE_BOOLEAN_SET) || (result->outside == TRISTATE_BOOLEAN_SET)) {
633                 result->moving = TRISTATE_BOOLEAN_SET;
634         } else if ((result->overThresholds == TRISTATE_BOOLEAN_UNSET)
635                         && (result->outside == TRISTATE_BOOLEAN_UNSET)) {
636                 result->moving = TRISTATE_BOOLEAN_UNSET;
637         }
638
639         return;
640 }
641
642 /**
643  Update the latest GPS information. This function is called when a packet is
644  received from a rxNonOlsr interface, containing one or more NMEA strings with
645  GPS information.
646
647  @param rxBuffer
648  the receive buffer with the received NMEA string(s)
649  @param rxCount
650  the number of bytes in the receive buffer
651
652  @return
653  - false on failure
654  - true otherwise
655  */
656 bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount) {
657         static const char * rxBufferPrefix = "$GP";
658         static const size_t rxBufferPrefixLength = 3;
659
660         bool retval = false;
661         PositionUpdateEntry * incomingEntry;
662         PositionUpdateEntry * posAvgEntry;
663         MovementType movementResult;
664         bool subStateExternalStateChange;
665         bool externalStateChange;
666         bool updateTransmitGpsInformation = false;
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         detemineMovingFromPosition(posAvgEntry, &transmitGpsInformation.txPosition, &movementResult);
710
711         /*
712          * State Determination
713          */
714
715         determineStateWithHysteresis(SUBSTATE_POSITION, movementResult.moving, &externalState, &externalStateChange,
716                         &subStateExternalStateChange);
717
718         /*
719          * Update transmitGpsInformation
720          */
721
722         updateTransmitGpsInformation = subStateExternalStateChange
723                         || (positionValid(posAvgEntry) && !positionValid(&transmitGpsInformation.txPosition))
724                         || (movementResult.inside == TRISTATE_BOOLEAN_SET);
725
726         if ((externalState == MOVEMENT_STATE_MOVING) || updateTransmitGpsInformation) {
727                 transmitGpsInformation.txPosition = *posAvgEntry;
728                 transmitGpsInformation.positionUpdated = true;
729
730                 /*
731                  * When we're stationary:
732                  * - the track is not reliable or even invalid, so we must clear it.
733                  * - to avoid confusion in consumers of the data, we must clear the speed
734                  *   because it is possible to have a very low speed while moving.
735                  */
736                 if (externalState == MOVEMENT_STATE_STATIONARY) {
737                         transmitGpsInformation.txPosition.nmeaInfo.speed = (double)0.0;
738                         transmitGpsInformation.txPosition.nmeaInfo.track = (double)0.0;
739                 }
740         }
741
742         if (externalStateChange) {
743                 doImmediateTransmit(externalState);
744         }
745
746         retval = true;
747
748         end:
749         return retval;
750 }
751
752 /**
753  * Log nmea library errors as plugin errors
754  * @param str
755  * @param str_size
756  */
757 static void nmea_errors(const char *str, int str_size __attribute__((unused))) {
758         pudError(false, "NMEA library error: %s", str);
759 }
760
761 /**
762  Start the receiver
763
764  @return
765  - false on failure
766  - true otherwise
767  */
768 bool startReceiver(void) {
769         MovementState externalState;
770         char * positionFile = getPositionFile();
771
772         if (!nmea_parser_init(&nmeaParser)) {
773                 pudError(false, "Could not initialise NMEA parser");
774                 return false;
775         }
776
777         /* hook up the NMEA library error callback */
778         nmea_context_set_error_func(&nmea_errors);
779
780         if (positionFile) {
781                 readPositionFile(positionFile, &transmitGpsInformation.txPosition.nmeaInfo);
782         } else {
783                 nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
784         }
785         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
786         transmitGpsInformation.positionUpdated = false;
787         transmitGpsInformation.nodeId = getNodeId();
788
789 #ifdef HTTPINFO_PUD
790         olsr_cnf->pud_position = &transmitGpsInformation;
791 #endif
792         initPositionAverageList(&positionAverageList, getAverageDepth());
793
794         if (!initOlsrTxTimer()) {
795                 stopReceiver();
796                 return false;
797         }
798
799         if (!initUplinkTxTimer()) {
800                 stopReceiver();
801                 return false;
802         }
803
804         if (!initGatewayTimer()) {
805                 stopReceiver();
806                 return false;
807         }
808
809         externalState = getExternalState();
810         restartOlsrTimer(externalState);
811         restartUplinkTimer(externalState);
812         if (!restartGatewayTimer(getGatewayDeterminationInterval(), &pud_gateway_timer_callback)) {
813                 pudError(0, "Could not start gateway timer");
814                 stopReceiver();
815                 return false;
816         }
817
818         return true;
819 }
820
821 /**
822  Stop the receiver
823  */
824 void stopReceiver(void) {
825         destroyGatewayTimer();
826         destroyUplinkTxTimer();
827         destroyOlsrTxTimer();
828
829         destroyPositionAverageList(&positionAverageList);
830
831         nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
832         transmitGpsInformation.txGateway = olsr_cnf->main_addr;
833         transmitGpsInformation.positionUpdated = false;
834
835         nmea_parser_destroy(&nmeaParser);
836 }