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