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