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