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