5 #include "gpsConversion.h"
6 #include "configuration.h"
10 #include "networkInterfaces.h"
12 #include "uplinkGateway.h"
20 #include <nmea/parser.h>
21 #include <nmea/info.h>
23 #include <nmea/info.h>
25 #include <nmea/gmath.h>
26 #include <nmea/sentence.h>
32 #if defined(PUD_DUMP_GPS_PACKETS_TX_OLSR) || \
33 defined(PUD_DUMP_GPS_PACKETS_TX_UPLINK) || \
34 defined(PUD_DUMP_AVERAGING)
42 /** The NMEA string parser */
43 static nmeaPARSER nmeaParser;
49 /** Type describing a tri-state boolean */
50 typedef enum _TristateBoolean {
56 #define TristateBooleanToString(s) ((s == SET) ? "set" : \
57 (s == UNSET) ? "unset" : \
60 /** Type describing movement state */
61 typedef enum _MovementState {
66 #define MovementStateToString(s) ((s == MOVING) ? "moving" : \
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 hysteresisCounter; /**< the hysteresis counter external state changes */
77 static StateType state = {
78 .internalState = MOVING,
79 .externalState = MOVING,
80 .hysteresisCounter = 0
83 /** Type describing movement calculations */
84 typedef struct _MovementType {
85 TristateBoolean moving; /**< SET: we are moving */
87 TristateBoolean differentGateway; /**< SET: the gateway is different */
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 */
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 */
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 */
107 /** The average position with its administration */
108 static PositionAverageList positionAverageList;
114 typedef enum _TimedTxInterface {
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;
128 /** The latest position information that is transmitted */
129 static TransmitGpsInformation transmitGpsInformation;
131 /** The size of the buffer in which the OLSR and uplink messages are assembled */
132 #define TX_BUFFER_SIZE_FOR_OLSR 1024
139 Clear the MovementType
140 * @param result a pointer to the MovementType
142 static void clearMovementType(MovementType * result) {
144 result->moving = UNKNOWN;
145 result->differentGateway = UNSET;
146 result->overThresholds = UNKNOWN;
147 result->speedOverThreshold = UNKNOWN;
148 result->hDistanceOverThreshold = UNKNOWN;
149 result->vDistanceOverThreshold = UNKNOWN;
150 result->outside = UNKNOWN;
151 result->outsideHdop = UNKNOWN;
152 result->outsideVdop = UNKNOWN;
153 result->inside = UNKNOWN;
154 result->insideHdop = UNKNOWN;
155 result->insideVdop = UNKNOWN;
159 Determine whether s position is valid.
162 a pointer to a position
168 static bool positionValid(PositionUpdateEntry * position) {
169 return (nmea_INFO_has_field(position->nmeaInfo.smask, FIX)
170 && (position->nmeaInfo.fix != NMEA_FIX_BAD));
174 Send the transmit buffer out over all designated interfaces, called as a
175 timer callback and also immediately on an external state change.
178 a bitmap defining which interfaces to send over
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)
187 * The first message in txBuffer is an OLSR position update.
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.
193 * The second message is the cluster leader message, but only when uplink
194 * was requested and correctly configured.
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;
202 (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
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);
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;
216 transmitGpsInformation.updated = false;
217 (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
220 * push out to all OLSR interfaces
222 if (((interfaces & OLSR) != 0) && (pu_size > 0)) {
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) {
230 "Could not send to OLSR interface %s: %s (size=%u, r=%d)",
232 ((r == -1) ? "no buffer was found" :
233 (r == 0) ? "there was not enough room in the buffer" : "unknown reason"), pu_size, r);
235 #ifdef PUD_DUMP_GPS_PACKETS_TX_OLSR
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);
244 /* loopback to tx interface when so configured */
245 if (getUseLoopback()) {
246 (void) packetReceivedFromOlsr(pu, NULL, NULL);
250 /* push out over uplink when an uplink is configured */
251 if (((interfaces & UPLINK) != 0) && isUplinkAddrSet()) {
252 int fd = getDownlinkSocketFd();
254 union olsr_sockaddr * uplink_addr = getUplinkAddr();
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));
266 * position update message (pu)
269 /* set header fields in position update uplink message and adjust
270 * the validity time to the uplink validity time */
272 PudOlsrPositionUpdate * pu_gpsMessage = getOlsrMessagePayload(olsr_cnf->ip_version, pu);
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);
279 /* fixup validity time */
281 &pu_gpsMessage->validityTime,
282 (state.externalState == MOVING) ?
283 getUplinkUpdateIntervalMoving() : getUplinkUpdateIntervalStationary());
287 * cluster leader message (cl)
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);
297 setClusterLeaderVersion(cl, PUD_WIRE_FORMAT_VERSION);
300 (state.externalState == MOVING) ?
301 getUplinkUpdateIntervalMoving() : getUplinkUpdateIntervalStationary());
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);
307 txBufferBytesUsed += sizeof(UplinkHeader);
308 txBufferBytesUsed += cl_size;
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);
315 #ifdef PUD_DUMP_GPS_PACKETS_TX_UPLINK
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);
331 The OLSR tx timer callback
336 static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
337 txToAllOlsrInterfaces(OLSR);
341 The uplink timer callback
346 static void pud_uplink_timer_callback(void *context __attribute__ ((unused))) {
347 txToAllOlsrInterfaces(UPLINK);
351 Detemine whether we are moving from the gateway.
353 MUST be called which the position average list locked.
356 the current best gateway
358 the last best gateway
360 the results of all movement criteria
362 static void detemineMovingFromGateway(union olsr_ip_addr * gateway, union olsr_ip_addr * lastGateway,
363 MovementType * result) {
365 * When the gateway is different from the gateway during last transmit, then
368 if (!ipequal(gateway, lastGateway)) {
369 result->moving = SET;
370 result->differentGateway = SET;
374 result->differentGateway = UNSET;
378 Detemine whether we are moving from the position, by comparing fields from the
379 average position against those of the last transmitted position.
381 MUST be called which the position average list locked.
386 the last transmitted position
388 the results of all movement criteria
390 static void detemineMovingFromPosition(PositionUpdateEntry * avg, PositionUpdateEntry * lastTx, MovementType * result) {
391 /* avg field presence booleans */
398 /* lastTx field presence booleans */bool lastTxHasPos;
403 /* these have defaults */
404 double dopMultiplier;
410 /* calculated values and their validity booleans */
413 double hdopDistanceForOutside;
414 double hdopDistanceForInside;
415 double vdopDistanceForOutside;
416 double vdopDistanceForInside;
418 bool hdopDistanceValid;
420 bool vdopDistanceValid;
426 * 0 0 UNKNOWN : can't determine whether we're moving
427 * 0 1 UNKNOWN : can't determine whether we're moving
428 * 1 0 UNKNOWN : can't determine whether we're moving
429 * 1 1 determine via other parameters
432 if (!positionValid(avg)) {
433 result->moving = UNKNOWN;
437 /* avg is valid here */
439 if (!positionValid(lastTx)) {
440 result->moving = UNKNOWN;
444 /* both avg and lastTx are valid here */
446 /* avg field presence booleans */
447 avgHasSpeed = nmea_INFO_has_field(avg->nmeaInfo.smask, SPEED);
448 avgHasPos = nmea_INFO_has_field(avg->nmeaInfo.smask, LAT)
449 && nmea_INFO_has_field(avg->nmeaInfo.smask, LON);
450 avgHasHdop = nmea_INFO_has_field(avg->nmeaInfo.smask, HDOP);
451 avgHasElv = nmea_INFO_has_field(avg->nmeaInfo.smask, ELV);
452 avgHasVdop = nmea_INFO_has_field(avg->nmeaInfo.smask, VDOP);
454 /* lastTx field presence booleans */
455 lastTxHasPos = nmea_INFO_has_field(lastTx->nmeaInfo.smask, LAT)
456 && nmea_INFO_has_field(lastTx->nmeaInfo.smask, LON);
457 lastTxHasHdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, HDOP);
458 lastTxHasElv = nmea_INFO_has_field(lastTx->nmeaInfo.smask, ELV);
459 lastTxHasVdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, VDOP);
461 /* fill in some values _or_ defaults */
462 dopMultiplier = getDopMultiplier();
463 avgHdop = avgHasHdop ? avg->nmeaInfo.HDOP : getDefaultHdop();
464 lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.HDOP : getDefaultHdop();
465 avgVdop = avgHasVdop ? avg->nmeaInfo.VDOP : getDefaultVdop();
466 lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.VDOP : getDefaultVdop();
473 if (avgHasPos && lastTxHasPos) {
477 avgPos.lat = nmea_degree2radian(avg->nmeaInfo.lat);
478 avgPos.lon = nmea_degree2radian(avg->nmeaInfo.lon);
480 lastTxPos.lat = nmea_degree2radian(lastTx->nmeaInfo.lat);
481 lastTxPos.lon = nmea_degree2radian(lastTx->nmeaInfo.lon);
483 hDistance = nmea_distance_ellipsoid(&avgPos, &lastTxPos, NULL, NULL);
484 hDistanceValid = true;
486 hDistanceValid = false;
490 if (avgHasHdop || lastTxHasHdop) {
491 hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
492 hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
493 hdopDistanceValid = true;
495 hdopDistanceValid = false;
499 if (avgHasElv && lastTxHasElv) {
500 vDistance = fabs(lastTx->nmeaInfo.elv - avg->nmeaInfo.elv);
501 vDistanceValid = true;
503 vDistanceValid = false;
507 if (avgHasVdop || lastTxHasVdop) {
508 vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
509 vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
510 vdopDistanceValid = true;
512 vdopDistanceValid = false;
516 * Moving Criteria Evaluation Start
517 * We compare the average position against the last transmitted position.
522 if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
523 result->speedOverThreshold = SET;
525 result->speedOverThreshold = UNSET;
532 * avg last hDistanceMoving
533 * 0 0 determine via other parameters
534 * 0 1 determine via other parameters
536 * 1 1 determine via distance threshold and HDOP
538 if (avgHasPos && !lastTxHasPos) {
539 result->hDistanceOverThreshold = SET;
540 } else if (hDistanceValid) {
541 if (hDistance >= getMovingDistanceThreshold()) {
542 result->hDistanceOverThreshold = SET;
544 result->hDistanceOverThreshold = UNSET;
551 * 0 0 determine via other parameters
552 * 0 1 determine via position with HDOP (avg has default HDOP)
553 * 1 0 determine via position with HDOP (lastTx has default HDOP)
554 * 1 1 determine via position with HDOP
556 if (hdopDistanceValid) {
557 /* we are outside the HDOP when the HDOPs no longer overlap */
558 if (hDistance > hdopDistanceForOutside) {
559 result->outsideHdop = SET;
561 result->outsideHdop = UNSET;
564 /* we are inside the HDOP when the HDOPs fully overlap */
565 if (hDistance <= hdopDistanceForInside) {
566 result->insideHdop = SET;
568 result->insideHdop = UNSET;
577 * 0 0 determine via other parameters
578 * 0 1 determine via other parameters
580 * 1 1 determine via distance threshold and VDOP
582 if (avgHasElv && !lastTxHasElv) {
583 result->vDistanceOverThreshold = SET;
584 } else if (vDistanceValid) {
585 if (vDistance >= getMovingDistanceThreshold()) {
586 result->vDistanceOverThreshold = SET;
588 result->vDistanceOverThreshold = UNSET;
592 * Elevation with VDOP
595 * 0 0 determine via other parameters
596 * 0 1 determine via elevation with VDOP (avg has default VDOP)
597 * 1 0 determine via elevation with VDOP (lastTx has default VDOP)
598 * 1 1 determine via elevation with VDOP
600 if (vdopDistanceValid) {
601 /* we are outside the VDOP when the VDOPs no longer overlap */
602 if (vDistance > vdopDistanceForOutside) {
603 result->outsideVdop = SET;
605 result->outsideVdop = UNSET;
608 /* we are inside the VDOP when the VDOPs fully overlap */
609 if (vDistance <= vdopDistanceForInside) {
610 result->insideVdop = SET;
612 result->insideVdop = UNSET;
618 * Moving Criteria Evaluation End
621 /* accumulate inside criteria */
622 if ((result->insideHdop == SET) && (result->insideVdop == SET)) {
623 result->inside = SET;
624 } else if ((result->insideHdop == UNSET) || (result->insideVdop == UNSET)) {
625 result->inside = UNSET;
628 /* accumulate outside criteria */
629 if ((result->outsideHdop == SET) || (result->outsideVdop == SET)) {
630 result->outside = SET;
631 } else if ((result->outsideHdop == UNSET)
632 || (result->outsideVdop == UNSET)) {
633 result->outside = UNSET;
636 /* accumulate threshold criteria */
637 if ((result->speedOverThreshold == SET)
638 || (result->hDistanceOverThreshold == SET)
639 || (result->vDistanceOverThreshold == SET)) {
640 result->overThresholds = SET;
641 } else if ((result->speedOverThreshold == UNSET)
642 || (result->hDistanceOverThreshold == UNSET)
643 || (result->vDistanceOverThreshold == UNSET)) {
644 result->overThresholds = UNSET;
647 /* accumulate moving criteria */
648 if ((result->overThresholds == SET) || (result->outside == SET)) {
649 result->moving = SET;
650 } else if ((result->overThresholds == UNSET)
651 && (result->outside == UNSET)) {
652 result->moving = UNSET;
659 Restart the OLSR tx timer
661 static void restartOlsrTimer(void) {
662 if (!restartOlsrTxTimer(
663 (state.externalState == STATIONARY) ? getUpdateIntervalStationary() :
664 getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
665 pudError(0, "Could not restart OLSR tx timer, no periodic"
666 " position updates will be sent to the OLSR network");
671 Restart the uplink tx timer
673 static void restartUplinkTimer(void) {
674 if (!restartUplinkTxTimer(
675 (state.externalState == STATIONARY) ? getUplinkUpdateIntervalStationary() :
676 getUplinkUpdateIntervalMoving(),
677 &pud_uplink_timer_callback)) {
678 pudError(0, "Could not restart uplink timer, no periodic"
679 " position updates will be uplinked");
684 Update the latest GPS information. This function is called when a packet is
685 received from a rxNonOlsr interface, containing one or more NMEA strings with
689 the receive buffer with the received NMEA string(s)
691 the number of bytes in the receive buffer
697 bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount) {
698 static const char * rxBufferPrefix = "$GP";
699 static const size_t rxBufferPrefixLength = 3;
702 PositionUpdateEntry * incomingEntry;
703 MovementState newState;
704 PositionUpdateEntry * posAvgEntry;
705 MovementType movementResult;
706 TristateBoolean movingNow;
707 bool internalStateChange = false;
708 bool externalStateChange = false;
709 bool updateTransmitGpsInformation = false;
710 union olsr_ip_addr bestGateway;
711 PositionUpdateEntry txPosition;
712 union olsr_ip_addr txGateway;
714 /* do not process when the message does not start with $GP */
715 if ((rxCount < rxBufferPrefixLength) || (strncmp((char *) rxBuffer,
716 rxBufferPrefix, rxBufferPrefixLength) != 0)) {
720 (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
721 getBestUplinkGateway(&transmitGpsInformation.bestGateway);
723 txPosition = transmitGpsInformation.txPosition;
724 txGateway = transmitGpsInformation.txGateway;
725 bestGateway = transmitGpsInformation.bestGateway;
726 (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
728 /* parse all NMEA strings in the rxBuffer into the incoming entry */
729 incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
730 nmea_zero_INFO(&incomingEntry->nmeaInfo);
731 nmea_parse(&nmeaParser, (char *) rxBuffer, rxCount,
732 &incomingEntry->nmeaInfo);
734 #if defined(PUD_DUMP_AVERAGING)
735 dump_nmeaInfo(&incomingEntry->nmeaInfo,
736 "receiverUpdateGpsInformation: incoming entry");
737 #endif /* PUD_DUMP_AVERAGING */
739 /* ignore when no useful information */
740 if (incomingEntry->nmeaInfo.smask == GPNON) {
745 nmea_INFO_sanitise(&incomingEntry->nmeaInfo);
747 #if defined(PUD_DUMP_AVERAGING)
748 dump_nmeaInfo(&incomingEntry->nmeaInfo,
749 "receiverUpdateGpsInformation: incoming entry after sanitise");
750 #endif /* PUD_DUMP_AVERAGING */
752 /* we always work with latitude, longitude in degrees and DOPs in meters */
753 nmea_INFO_unit_conversion(&incomingEntry->nmeaInfo);
755 #if defined(PUD_DUMP_AVERAGING)
756 dump_nmeaInfo(&incomingEntry->nmeaInfo,
757 "receiverUpdateGpsInformation: incoming entry after unit conversion");
758 #endif /* PUD_DUMP_AVERAGING */
764 if (state.internalState == MOVING) {
765 /* flush average: keep only the incoming entry */
766 flushPositionAverageList(&positionAverageList);
768 addNewPositionToAverage(&positionAverageList, incomingEntry);
769 posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
775 clearMovementType(&movementResult);
776 detemineMovingFromGateway(&bestGateway, &txGateway, &movementResult);
777 if (movementResult.moving != SET) {
778 detemineMovingFromPosition(posAvgEntry, &txPosition, &movementResult);
780 movingNow = movementResult.moving;
782 #if defined(PUD_DUMP_AVERAGING)
783 olsr_printf(0, "receiverUpdateGpsInformation: internalState = %s\n",
784 MovementStateToString(state.internalState));
785 olsr_printf(0, "receiverUpdateGpsInformation: movingNow = %s\n",
786 TristateBooleanToString(movingNow));
787 #endif /* PUD_DUMP_AVERAGING */
793 if (movingNow == SET) {
795 } else if (movingNow == UNSET) {
796 newState = STATIONARY;
798 /* force back to stationary for unknown movement */
799 newState = STATIONARY;
801 internalStateChange = (state.internalState != newState);
802 state.internalState = newState;
805 * External State (+ hysteresis)
808 if (internalStateChange) {
809 /* restart hysteresis for external state change when we have an internal
811 state.hysteresisCounter = 0;
814 /* when internal state and external state are not the same we need to
815 * perform hysteresis before we can propagate the internal state to the
817 newState = state.externalState;
818 if (state.internalState != state.externalState) {
819 switch (state.internalState) {
821 /* external state is MOVING */
823 /* delay going to stationary a bit */
824 state.hysteresisCounter++;
826 if (state.hysteresisCounter
827 >= getHysteresisCountToStationary()) {
828 /* outside the hysteresis range, go to stationary */
829 newState = STATIONARY;
834 /* external state is STATIONARY */
836 /* delay going to moving a bit */
837 state.hysteresisCounter++;
839 if (state.hysteresisCounter >= getHysteresisCountToMoving()) {
840 /* outside the hysteresis range, go to moving */
846 /* when unknown do just as if we transition into stationary */
847 newState = STATIONARY;
851 externalStateChange = (state.externalState != newState);
852 state.externalState = newState;
854 #if defined(PUD_DUMP_AVERAGING)
855 olsr_printf(0, "receiverUpdateGpsInformation: newState = %s\n",
856 MovementStateToString(newState));
857 dump_nmeaInfo(&posAvgEntry->nmeaInfo,
858 "receiverUpdateGpsInformation: posAvgEntry");
859 #endif /* PUD_DUMP_AVERAGING */
862 * Update transmitGpsInformation
865 updateTransmitGpsInformation = externalStateChange
866 || (positionValid(posAvgEntry) && !positionValid(&txPosition))
867 || (movementResult.inside == SET);
869 if ((state.externalState == MOVING) || updateTransmitGpsInformation) {
870 (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
871 transmitGpsInformation.txPosition.nmeaInfo = posAvgEntry->nmeaInfo;
872 transmitGpsInformation.txGateway = bestGateway;
873 transmitGpsInformation.updated = true;
874 (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
876 #if defined(PUD_DUMP_AVERAGING)
877 dump_nmeaInfo(&posAvgEntry->nmeaInfo,
878 "receiverUpdateGpsInformation: transmitGpsInformation");
879 #endif /* PUD_DUMP_AVERAGING */
882 if (externalStateChange) {
883 TimedTxInterface interfaces = OLSR; /* always send over olsr */
886 if (isUplinkAddrSet()) {
887 interfaces |= UPLINK;
888 restartUplinkTimer();
891 /* do an immediate transmit */
892 txToAllOlsrInterfaces(interfaces);
902 * Receiver start/stop
912 bool startReceiver(void) {
913 pthread_mutexattr_t attr;
914 if (pthread_mutexattr_init(&attr)) {
917 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) {
920 if (pthread_mutex_init(&transmitGpsInformation.mutex, &attr)) {
924 if (!nmea_parser_init(&nmeaParser)) {
925 pudError(false, "Could not initialise NMEA parser");
929 nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
930 transmitGpsInformation.txGateway = olsr_cnf->main_addr;
931 transmitGpsInformation.updated = false;
933 state.internalState = MOVING;
934 state.externalState = MOVING;
935 state.hysteresisCounter = 0;
937 initPositionAverageList(&positionAverageList, getAverageDepth());
939 if (!initOlsrTxTimer()) {
944 if (!initUplinkTxTimer()) {
950 restartUplinkTimer();
958 void stopReceiver(void) {
959 destroyUplinkTxTimer();
960 destroyOlsrTxTimer();
962 destroyPositionAverageList(&positionAverageList);
964 state.hysteresisCounter = 0;
965 state.externalState = MOVING;
966 state.internalState = MOVING;
968 (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
969 transmitGpsInformation.updated = false;
970 nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
971 transmitGpsInformation.txGateway = olsr_cnf->main_addr;
972 (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
974 nmea_parser_destroy(&nmeaParser);
976 (void) pthread_mutex_destroy(&transmitGpsInformation.mutex);