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