5 #include "gpsConversion.h"
6 #include "configuration.h"
10 #include "networkInterfaces.h"
12 #include "uplinkGateway.h"
19 #include <nmea/parser.h>
20 #include <nmea/info.h>
22 #include <nmea/info.h>
24 #include <nmea/gmath.h>
25 #include <nmea/sentence.h>
31 #if defined(PUD_DUMP_GPS_PACKETS_TX_OLSR) || \
32 defined(PUD_DUMP_GPS_PACKETS_TX_UPLINK) || \
33 defined(PUD_DUMP_AVERAGING)
41 /** The NMEA string parser */
42 static nmeaPARSER nmeaParser;
48 /** Type describing a tri-state boolean */
49 typedef enum _TristateBoolean {
55 #define TristateBooleanToString(s) ((s == SET) ? "set" : \
56 (s == UNSET) ? "unset" : \
59 /** Type describing movement state */
60 typedef enum _MovementState {
65 #define MovementStateToString(s) ((s == MOVING) ? "moving" : \
68 /** Type describing state */
69 typedef struct _StateType {
70 MovementState internalState; /**< the internal movement state */
71 MovementState externalState; /**< the externally visible movement state */
72 unsigned long long hysteresisCounter; /**< the hysteresis counter external state changes */
76 static StateType state = {
77 .internalState = MOVING,
78 .externalState = MOVING,
79 .hysteresisCounter = 0
82 /** Type describing movement calculations */
83 typedef struct _MovementType {
84 TristateBoolean moving; /**< SET: we are moving */
86 TristateBoolean overThresholds; /**< SET: at least 1 threshold state is set */
87 TristateBoolean speedOverThreshold; /**< SET: speed is over threshold */
88 TristateBoolean hDistanceOverThreshold; /**< SET: horizontal distance is outside threshold */
89 TristateBoolean vDistanceOverThreshold; /**< SET: vertical distance is outside threshold */
91 TristateBoolean outside; /**< SET: at least 1 outside state is SET */
92 TristateBoolean outsideHdop; /**< SET: avg is outside lastTx HDOP */
93 TristateBoolean outsideVdop; /**< SET: avg is outside lastTx VDOP */
95 TristateBoolean inside; /**< SET: all inside states are SET */
96 TristateBoolean insideHdop; /**< SET: avg is inside lastTx HDOP */
97 TristateBoolean insideVdop; /**< SET: avg is inside lastTx VDOP */
104 /** The average position with its administration */
105 static PositionAverageList positionAverageList;
111 typedef enum _TimedTxInterface {
116 /** Structure of the latest GPS information that is transmitted */
117 typedef struct _TransmitGpsInformation {
118 pthread_mutex_t mutex; /**< access mutex */
119 bool updated; /**< true when the information was updated */
120 PositionUpdateEntry txPosition; /**< The last transmitted position */
121 } TransmitGpsInformation;
123 /** The latest position information that is transmitted */
124 static TransmitGpsInformation transmitGpsInformation;
126 /** The last transmitted position.
127 * The same as transmitGpsInformation.txPosition.
128 * We keep this because then we can access the information without locking
130 static PositionUpdateEntry txPosition;
132 /** The size of the buffer in which the OLSR and uplink messages are assembled */
133 #define TX_BUFFER_SIZE_FOR_OLSR 1024
140 Determine whether s position is valid.
143 a pointer to a position
149 static bool positionValid(PositionUpdateEntry * position) {
150 return (nmea_INFO_has_field(position->nmeaInfo.smask, FIX)
151 && (position->nmeaInfo.fix != NMEA_FIX_BAD));
155 Send the transmit buffer out over all designated interfaces, called as a
156 timer callback and also immediately on an external state change.
159 a bitmap defining which interfaces to send over
161 static void txToAllOlsrInterfaces(TimedTxInterface interfaces) {
162 /** txBuffer is used to concatenate the position update and cluster leader messages in */
163 unsigned char txBuffer[TX_BUFFER_SIZE_FOR_OLSR];
164 unsigned int txBufferBytesUsed = 0;
165 #define txBufferBytesFree (sizeof(txBuffer) - txBufferBytesUsed)
168 * The first message in txBuffer is an OLSR position update.
170 * The position update is not present when the position is not valid.
171 * Otherwise it is always present: when we transmit onto the OLSR network
172 * and/or when we transmit onto the uplink.
174 * The second message is the cluster leader message, but only when uplink
175 * was requested and correctly configured.
178 UplinkMessage * pu_uplink = (UplinkMessage *) &txBuffer[0];
179 union olsr_message * pu = &pu_uplink->msg.olsrMessage;
180 unsigned int pu_size = 0;
183 * convert nmeaINFO to wireformat olsr message (always, since we always try
184 * to send the position)
186 (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
188 /* only get the position when it is valid (no tx on invalid position) */
189 if (positionValid(&transmitGpsInformation.txPosition)) {
190 /* fixup timestamp when the position was not updated */
191 if (!transmitGpsInformation.updated) {
192 nmea_time_now(&transmitGpsInformation.txPosition.nmeaInfo.utc);
195 txBufferBytesUsed += sizeof(UplinkHeader); /* keep before txBufferSpaceFree usage */
196 pu_size = gpsToOlsr(&transmitGpsInformation.txPosition.nmeaInfo, pu, txBufferBytesFree,
197 ((state.externalState == MOVING) ? getUpdateIntervalMoving() : getUpdateIntervalStationary()));
198 txBufferBytesUsed += pu_size;
201 transmitGpsInformation.updated = false;
202 (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
205 * push out to all OLSR interfaces
207 if (((interfaces & OLSR) != 0) && (pu_size > 0)) {
209 struct interface *ifn;
210 for (ifn = ifnet; ifn; ifn = ifn->int_next) {
211 r = net_outbuffer_push(ifn, pu, pu_size);
212 if (r != (int) pu_size) {
215 "Could not send to OLSR interface %s: %s (size=%u, r=%d)",
217 ((r == -1) ? "no buffer was found" :
218 (r == 0) ? "there was not enough room in the buffer" : "unknown reason"), pu_size, r);
220 #ifdef PUD_DUMP_GPS_PACKETS_TX_OLSR
222 olsr_printf(0, "%s: packet sent to OLSR interface %s (%d bytes)\n",
223 PUD_PLUGIN_ABBR, ifn->int_name, pu_size);
224 dump_packet((unsigned char *)pu, pu_size);
229 /* loopback to tx interface when so configured */
230 if (getUseLoopback()) {
231 (void) packetReceivedFromOlsr(pu, NULL, NULL);
235 /* push out over uplink when an uplink is configured */
236 if (((interfaces & UPLINK) != 0) && isUplinkAddrSet()) {
237 int fd = getDownlinkSocketFd();
239 union olsr_sockaddr * uplink_addr = getUplinkAddr();
240 union olsr_ip_addr * gw_addr = getBestUplinkGateway();
242 UplinkMessage * cl_uplink = (UplinkMessage *) &txBuffer[txBufferBytesUsed];
243 UplinkClusterLeader * cl = &cl_uplink->msg.clusterLeader;
244 union olsr_ip_addr * cl_originator = getClusterLeaderOriginator(olsr_cnf->ip_version, cl);
245 union olsr_ip_addr * cl_clusterLeader = getClusterLeaderClusterLeader(olsr_cnf->ip_version, cl);
246 unsigned int cl_size =
247 sizeof(UplinkClusterLeader) - sizeof(cl->leader)
248 + ((olsr_cnf->ip_version == AF_INET) ? sizeof(cl->leader.v4) :
249 sizeof(cl->leader.v6));
252 * position update message (pu)
255 /* set header fields in position update uplink message and adjust
256 * the validity time to the uplink validity time */
258 PudOlsrPositionUpdate * pu_gpsMessage = getOlsrMessagePayload(olsr_cnf->ip_version, pu);
260 setUplinkMessageType(&pu_uplink->header, POSITION);
261 setUplinkMessageLength(&pu_uplink->header, pu_size);
262 setUplinkMessageIPv6(&pu_uplink->header, (olsr_cnf->ip_version != AF_INET));
263 setUplinkMessagePadding(&pu_uplink->header, 0);
265 /* fixup validity time */
267 &pu_gpsMessage->validityTime,
268 (state.externalState == MOVING) ?
269 getUplinkUpdateIntervalMoving() : getUplinkUpdateIntervalStationary());
273 * cluster leader message (cl)
276 /* set cl_uplink header fields */
277 setUplinkMessageType(&cl_uplink->header, CLUSTERLEADER);
278 setUplinkMessageLength(&cl_uplink->header, cl_size);
279 setUplinkMessageIPv6(&cl_uplink->header, (olsr_cnf->ip_version != AF_INET));
280 setUplinkMessagePadding(&cl_uplink->header, 0);
283 setClusterLeaderVersion(cl, PUD_WIRE_FORMAT_VERSION);
286 (state.externalState == MOVING) ?
287 getUplinkUpdateIntervalMoving() : getUplinkUpdateIntervalStationary());
289 memcpy(cl_originator, &olsr_cnf->main_addr, olsr_cnf->ipsize);
290 memcpy(cl_clusterLeader, gw_addr, olsr_cnf->ipsize);
292 txBufferBytesUsed += sizeof(UplinkHeader);
293 txBufferBytesUsed += cl_size;
296 if (sendto(fd, &txBuffer, txBufferBytesUsed, 0, (struct sockaddr *) &uplink_addr->in,
297 sizeof(uplink_addr->in)) < 0) {
298 pudError(true, "Could not send to uplink (size=%u)", txBufferBytesUsed);
300 #ifdef PUD_DUMP_GPS_PACKETS_TX_UPLINK
302 olsr_printf(0, "%s: packet sent to uplink (%d bytes)\n",
303 PUD_PLUGIN_ABBR, pu_size);
304 dump_packet((unsigned char *)&txBuffer, txBufferBytesUsed);
316 The OLSR tx timer callback
321 static void pud_olsr_tx_timer_callback(void *context __attribute__ ((unused))) {
322 txToAllOlsrInterfaces(OLSR);
326 The uplink timer callback
331 static void pud_uplink_timer_callback(void *context __attribute__ ((unused))) {
332 txToAllOlsrInterfaces(UPLINK);
336 Detemine whether we are moving by comparing fields from the average
337 position against those of the last transmitted position.
339 MUST be called which the position average list locked.
344 the last transmitted position
346 the results of all movement criteria
348 static void detemineMoving(PositionUpdateEntry * avg,
349 PositionUpdateEntry * lastTx, MovementType * result) {
350 /* avg field presence booleans */
357 /* lastTx field presence booleans */bool lastTxHasPos;
362 /* these have defaults */
363 double dopMultiplier;
369 /* calculated values and their validity booleans */
372 double hdopDistanceForOutside;
373 double hdopDistanceForInside;
374 double vdopDistanceForOutside;
375 double vdopDistanceForInside;
377 bool hdopDistanceValid;
379 bool vdopDistanceValid;
382 result->moving = UNKNOWN;
383 result->overThresholds = UNKNOWN;
384 result->speedOverThreshold = UNKNOWN;
385 result->hDistanceOverThreshold = UNKNOWN;
386 result->vDistanceOverThreshold = UNKNOWN;
387 result->outside = UNKNOWN;
388 result->outsideHdop = UNKNOWN;
389 result->outsideVdop = UNKNOWN;
390 result->inside = UNKNOWN;
391 result->insideHdop = UNKNOWN;
392 result->insideVdop = UNKNOWN;
398 * 0 0 UNKNOWN : can't determine whether we're moving
399 * 0 1 UNKNOWN : can't determine whether we're moving
400 * 1 0 MOVING : always seen as movement
401 * 1 1 determine via other parameters
404 if (!positionValid(avg)) {
405 /* force stationary when the position is invalid */
406 result->moving = UNSET;
410 /* avg is valid here */
412 if (!positionValid(lastTx)) {
413 /* the position just became valid: force moving */
414 result->moving = SET;
415 /* the rest is unknown */
419 /* both avg and lastTx are valid here */
421 /* avg field presence booleans */
422 avgHasSpeed = nmea_INFO_has_field(avg->nmeaInfo.smask, SPEED);
423 avgHasPos = nmea_INFO_has_field(avg->nmeaInfo.smask, LAT)
424 && nmea_INFO_has_field(avg->nmeaInfo.smask, LON);
425 avgHasHdop = nmea_INFO_has_field(avg->nmeaInfo.smask, HDOP);
426 avgHasElv = nmea_INFO_has_field(avg->nmeaInfo.smask, ELV);
427 avgHasVdop = nmea_INFO_has_field(avg->nmeaInfo.smask, VDOP);
429 /* lastTx field presence booleans */
430 lastTxHasPos = nmea_INFO_has_field(lastTx->nmeaInfo.smask, LAT)
431 && nmea_INFO_has_field(lastTx->nmeaInfo.smask, LON);
432 lastTxHasHdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, HDOP);
433 lastTxHasElv = nmea_INFO_has_field(lastTx->nmeaInfo.smask, ELV);
434 lastTxHasVdop = nmea_INFO_has_field(lastTx->nmeaInfo.smask, VDOP);
436 /* fill in some values _or_ defaults */
437 dopMultiplier = getDopMultiplier();
438 avgHdop = avgHasHdop ? avg->nmeaInfo.HDOP : getDefaultHdop();
439 lastTxHdop = lastTxHasHdop ? lastTx->nmeaInfo.HDOP : getDefaultHdop();
440 avgVdop = avgHasVdop ? avg->nmeaInfo.VDOP : getDefaultVdop();
441 lastTxVdop = lastTxHasVdop ? lastTx->nmeaInfo.VDOP : getDefaultVdop();
448 if (avgHasPos && lastTxHasPos) {
452 avgPos.lat = nmea_degree2radian(avg->nmeaInfo.lat);
453 avgPos.lon = nmea_degree2radian(avg->nmeaInfo.lon);
455 lastTxPos.lat = nmea_degree2radian(lastTx->nmeaInfo.lat);
456 lastTxPos.lon = nmea_degree2radian(lastTx->nmeaInfo.lon);
458 hDistance = nmea_distance_ellipsoid(&avgPos, &lastTxPos, NULL, NULL);
459 hDistanceValid = true;
461 hDistanceValid = false;
465 if (avgHasHdop || lastTxHasHdop) {
466 hdopDistanceForOutside = dopMultiplier * (lastTxHdop + avgHdop);
467 hdopDistanceForInside = dopMultiplier * (lastTxHdop - avgHdop);
468 hdopDistanceValid = true;
470 hdopDistanceValid = false;
474 if (avgHasElv && lastTxHasElv) {
475 vDistance = fabs(lastTx->nmeaInfo.elv - avg->nmeaInfo.elv);
476 vDistanceValid = true;
478 vDistanceValid = false;
482 if (avgHasVdop || lastTxHasVdop) {
483 vdopDistanceForOutside = dopMultiplier * (lastTxVdop + avgVdop);
484 vdopDistanceForInside = dopMultiplier * (lastTxVdop - avgVdop);
485 vdopDistanceValid = true;
487 vdopDistanceValid = false;
491 * Moving Criteria Evaluation Start
492 * We compare the average position against the last transmitted position.
497 if (avg->nmeaInfo.speed >= getMovingSpeedThreshold()) {
498 result->speedOverThreshold = SET;
500 result->speedOverThreshold = UNSET;
507 * avg last hDistanceMoving
508 * 0 0 determine via other parameters
509 * 0 1 determine via other parameters
511 * 1 1 determine via distance threshold and HDOP
513 if (avgHasPos && !lastTxHasPos) {
514 result->hDistanceOverThreshold = SET;
515 } else if (hDistanceValid) {
516 if (hDistance >= getMovingDistanceThreshold()) {
517 result->hDistanceOverThreshold = SET;
519 result->hDistanceOverThreshold = UNSET;
526 * 0 0 determine via other parameters
527 * 0 1 determine via position with HDOP (avg has default HDOP)
528 * 1 0 determine via position with HDOP (lastTx has default HDOP)
529 * 1 1 determine via position with HDOP
531 if (hdopDistanceValid) {
532 /* we are outside the HDOP when the HDOPs no longer overlap */
533 if (hDistance > hdopDistanceForOutside) {
534 result->outsideHdop = SET;
536 result->outsideHdop = UNSET;
539 /* we are inside the HDOP when the HDOPs fully overlap */
540 if (hDistance <= hdopDistanceForInside) {
541 result->insideHdop = SET;
543 result->insideHdop = UNSET;
552 * 0 0 determine via other parameters
553 * 0 1 determine via other parameters
555 * 1 1 determine via distance threshold and VDOP
557 if (avgHasElv && !lastTxHasElv) {
558 result->vDistanceOverThreshold = SET;
559 } else if (vDistanceValid) {
560 if (vDistance >= getMovingDistanceThreshold()) {
561 result->vDistanceOverThreshold = SET;
563 result->vDistanceOverThreshold = UNSET;
567 * Elevation with VDOP
570 * 0 0 determine via other parameters
571 * 0 1 determine via elevation with VDOP (avg has default VDOP)
572 * 1 0 determine via elevation with VDOP (lastTx has default VDOP)
573 * 1 1 determine via elevation with VDOP
575 if (vdopDistanceValid) {
576 /* we are outside the VDOP when the VDOPs no longer overlap */
577 if (vDistance > vdopDistanceForOutside) {
578 result->outsideVdop = SET;
580 result->outsideVdop = UNSET;
583 /* we are inside the VDOP when the VDOPs fully overlap */
584 if (vDistance <= vdopDistanceForInside) {
585 result->insideVdop = SET;
587 result->insideVdop = UNSET;
593 * Moving Criteria Evaluation End
596 /* accumulate inside criteria */
597 if ((result->insideHdop == SET) && (result->insideVdop == SET)) {
598 result->inside = SET;
599 } else if ((result->insideHdop == UNSET) || (result->insideVdop == UNSET)) {
600 result->inside = UNSET;
603 /* accumulate outside criteria */
604 if ((result->outsideHdop == SET) || (result->outsideVdop == SET)) {
605 result->outside = SET;
606 } else if ((result->outsideHdop == UNSET)
607 || (result->outsideVdop == UNSET)) {
608 result->outside = UNSET;
611 /* accumulate threshold criteria */
612 if ((result->speedOverThreshold == SET)
613 || (result->hDistanceOverThreshold == SET)
614 || (result->vDistanceOverThreshold == SET)) {
615 result->overThresholds = SET;
616 } else if ((result->speedOverThreshold == UNSET)
617 || (result->hDistanceOverThreshold == UNSET)
618 || (result->vDistanceOverThreshold == UNSET)) {
619 result->overThresholds = UNSET;
622 /* accumulate moving criteria */
623 if ((result->overThresholds == SET) || (result->outside == SET)) {
624 result->moving = SET;
625 } else if ((result->overThresholds == UNSET)
626 && (result->outside == UNSET)) {
627 result->moving = UNSET;
634 Restart the OLSR tx timer
636 static void restartOlsrTimer(void) {
637 if (!restartOlsrTxTimer(
638 (state.externalState == STATIONARY) ? getUpdateIntervalStationary() :
639 getUpdateIntervalMoving(), &pud_olsr_tx_timer_callback)) {
640 pudError(0, "Could not restart OLSR tx timer, no periodic"
641 " position updates will be sent to the OLSR network");
646 Restart the uplink tx timer
648 static void restartUplinkTimer(void) {
649 if (!restartUplinkTxTimer(
650 (state.externalState == STATIONARY) ? getUplinkUpdateIntervalStationary() :
651 getUplinkUpdateIntervalMoving(),
652 &pud_uplink_timer_callback)) {
653 pudError(0, "Could not restart uplink timer, no periodic"
654 " position updates will be uplinked");
659 Update the latest GPS information. This function is called when a packet is
660 received from a rxNonOlsr interface, containing one or more NMEA strings with
664 the receive buffer with the received NMEA string(s)
666 the number of bytes in the receive buffer
672 bool receiverUpdateGpsInformation(unsigned char * rxBuffer, size_t rxCount) {
673 static const char * rxBufferPrefix = "$GP";
674 static const size_t rxBufferPrefixLength = 3;
677 PositionUpdateEntry * incomingEntry;
678 MovementState newState = STATIONARY;
679 PositionUpdateEntry * posAvgEntry;
680 MovementType movementResult;
681 TristateBoolean movingNow;
682 bool internalStateChange = false;
683 bool externalStateChange = false;
684 bool updateTransmitGpsInformation = false;
686 /* do not process when the message does not start with $GP */
687 if ((rxCount < rxBufferPrefixLength) || (strncmp((char *) rxBuffer,
688 rxBufferPrefix, rxBufferPrefixLength) != 0)) {
692 (void) pthread_mutex_lock(&positionAverageList.mutex);
694 /* parse all NMEA strings in the rxBuffer into the incoming entry */
695 incomingEntry = getPositionAverageEntry(&positionAverageList, INCOMING);
696 nmea_zero_INFO(&incomingEntry->nmeaInfo);
697 nmea_parse(&nmeaParser, (char *) rxBuffer, rxCount,
698 &incomingEntry->nmeaInfo);
700 #if defined(PUD_DUMP_AVERAGING)
701 dump_nmeaInfo(&incomingEntry->nmeaInfo,
702 "receiverUpdateGpsInformation: incoming entry");
703 #endif /* PUD_DUMP_AVERAGING */
705 /* ignore when no useful information */
706 if (incomingEntry->nmeaInfo.smask == GPNON) {
711 nmea_INFO_sanitise(&incomingEntry->nmeaInfo);
713 #if defined(PUD_DUMP_AVERAGING)
714 dump_nmeaInfo(&incomingEntry->nmeaInfo,
715 "receiverUpdateGpsInformation: incoming entry after sanitise");
716 #endif /* PUD_DUMP_AVERAGING */
718 /* we always work with latitude, longitude in degrees and DOPs in meters */
719 nmea_INFO_unit_conversion(&incomingEntry->nmeaInfo);
721 #if defined(PUD_DUMP_AVERAGING)
722 dump_nmeaInfo(&incomingEntry->nmeaInfo,
723 "receiverUpdateGpsInformation: incoming entry after unit conversion");
724 #endif /* PUD_DUMP_AVERAGING */
730 if (state.internalState == MOVING) {
731 /* flush average: keep only the incoming entry */
732 flushPositionAverageList(&positionAverageList);
734 addNewPositionToAverage(&positionAverageList, incomingEntry);
735 posAvgEntry = getPositionAverageEntry(&positionAverageList, AVERAGE);
741 detemineMoving(posAvgEntry, &txPosition, &movementResult);
742 movingNow = movementResult.moving;
744 #if defined(PUD_DUMP_AVERAGING)
745 olsr_printf(0, "receiverUpdateGpsInformation: internalState = %s\n",
746 MovementStateToString(state.internalState));
747 olsr_printf(0, "receiverUpdateGpsInformation: movingNow = %s\n",
748 TristateBooleanToString(movingNow));
749 #endif /* PUD_DUMP_AVERAGING */
755 if (movingNow == SET) {
757 } else if (movingNow == UNSET) {
758 newState = STATIONARY;
760 internalStateChange = (state.internalState != newState);
761 state.internalState = newState;
764 * External State (+ hysteresis)
767 if (internalStateChange) {
768 /* restart hysteresis for external state change when we have an internal
770 state.hysteresisCounter = 0;
773 /* when internal state and external state are not the same we need to
774 * perform hysteresis before we can propagate the internal state to the
776 newState = state.externalState;
777 if (state.internalState != state.externalState) {
778 switch (state.internalState) {
780 /* external state is MOVING */
782 /* delay going to stationary a bit */
783 state.hysteresisCounter++;
785 if (state.hysteresisCounter
786 >= getHysteresisCountToStationary()) {
787 /* outside the hysteresis range, go to stationary */
788 newState = STATIONARY;
793 /* external state is STATIONARY */
795 /* delay going to moving a bit */
796 state.hysteresisCounter++;
798 if (state.hysteresisCounter >= getHysteresisCountToMoving()) {
799 /* outside the hysteresis range, go to moving */
805 /* when unknown do just as if we transition into stationary */
806 newState = STATIONARY;
810 externalStateChange = (state.externalState != newState);
811 state.externalState = newState;
813 #if defined(PUD_DUMP_AVERAGING)
814 olsr_printf(0, "receiverUpdateGpsInformation: newState = %s\n",
815 MovementStateToString(newState));
816 dump_nmeaInfo(&posAvgEntry->nmeaInfo,
817 "receiverUpdateGpsInformation: posAvgEntry");
818 #endif /* PUD_DUMP_AVERAGING */
821 * Update transmitGpsInformation
824 updateTransmitGpsInformation = externalStateChange
825 || (positionValid(posAvgEntry) && !positionValid(&txPosition))
826 || (movementResult.inside == SET);
828 if ((state.externalState == MOVING) || updateTransmitGpsInformation) {
829 memcpy(&txPosition.nmeaInfo, &posAvgEntry->nmeaInfo, sizeof(nmeaINFO));
830 (void) pthread_mutex_lock(&transmitGpsInformation.mutex);
831 memcpy(&transmitGpsInformation.txPosition.nmeaInfo,
832 &posAvgEntry->nmeaInfo, sizeof(nmeaINFO));
833 transmitGpsInformation.updated = true;
835 #if defined(PUD_DUMP_AVERAGING)
836 dump_nmeaInfo(&transmitGpsInformation.txPosition.nmeaInfo,
837 "receiverUpdateGpsInformation: transmitGpsInformation");
838 #endif /* PUD_DUMP_AVERAGING */
840 (void) pthread_mutex_unlock(&transmitGpsInformation.mutex);
843 if (externalStateChange) {
844 TimedTxInterface interfaces = OLSR; /* always send over olsr */
847 if (isUplinkAddrSet()) {
848 interfaces |= UPLINK;
849 restartUplinkTimer();
852 /* do an immediate transmit */
853 txToAllOlsrInterfaces(interfaces);
858 end: (void) pthread_mutex_unlock(&positionAverageList.mutex);
863 * Receiver start/stop
873 bool startReceiver(void) {
874 pthread_mutexattr_t attr;
875 if (pthread_mutexattr_init(&attr)) {
878 if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)) {
881 if (pthread_mutex_init(&transmitGpsInformation.mutex, &attr)) {
885 if (!nmea_parser_init(&nmeaParser)) {
886 pudError(false, "Could not initialise NMEA parser");
890 nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
891 transmitGpsInformation.updated = false;
893 nmea_zero_INFO(&txPosition.nmeaInfo);
895 state.internalState = STATIONARY;
896 state.externalState = STATIONARY;
897 state.hysteresisCounter = 0;
899 initPositionAverageList(&positionAverageList, getAverageDepth());
901 if (!initOlsrTxTimer()) {
906 if (!initUplinkTxTimer()) {
912 restartUplinkTimer();
920 void stopReceiver(void) {
921 destroyUplinkTxTimer();
922 destroyOlsrTxTimer();
924 destroyPositionAverageList(&positionAverageList);
926 state.hysteresisCounter = 0;
927 state.externalState = STATIONARY;
928 state.internalState = STATIONARY;
930 nmea_zero_INFO(&txPosition.nmeaInfo);
932 transmitGpsInformation.updated = false;
933 nmea_zero_INFO(&transmitGpsInformation.txPosition.nmeaInfo);
935 nmea_parser_destroy(&nmeaParser);
937 (void) pthread_mutex_destroy(&transmitGpsInformation.mutex);