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