The xplane_io (X-Plane I/O) program is used to establish network communications, via UDP, with the X-Plane flight simulator running on a computer. The code consists of class libraries that abstract the lower-level UDP packet encoding and decoding details, according to the UDP protocol specifications in X-Plane version 9. Any X-Plane DATA packets can be sent and received, and any X-Plane DataRefs can be set by sending DREF packets to X-Plane.

Dependencies:   EthernetNetIf mbed ConfigFile

Revision:
0:a5d13af495af
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneIO/XPlaneIO.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,300 @@
+#include "EthernetNetIf.h"
+#include "UDPSocket.h"
+#include "LocalConfigFile.h"
+#include "Scaler.h"
+#include "XPlaneUdp.h"
+#include "XPlaneUdpDATA.h"
+#include "XPlaneUdpDecoder.h"
+#include "XPlaneUdpEncoder.h"
+#include "XPlaneIO.h"
+
+XPlaneIO::~XPlaneIO() {
+    if (_eth != NULL) {
+        free(_eth);
+        _eth = NULL;
+    }
+
+    for (int i = 0; i < _xpAnalogInCount; i++) {
+        if (_xpAnalogIn[i] != NULL) {
+            free(_xpAnalogIn[i]);
+        }
+    }
+    
+    for (XPlaneUdpDATAMap::iterator it = _recvDATAMap.begin(); it != _recvDATAMap.end(); it++) {
+        if ((it->second) != NULL) {
+            free(it->second);
+        }
+    }
+    _recvDATAMap.clear();
+
+}
+
+bool XPlaneIO::setup(char * configFilename, Ethernet * ethernet) {
+    
+    printf("X-Plane I/O ... initializing \n");
+    
+    bool returnStatus = true;
+    
+    LocalConfigFile cfg(configFilename);
+    
+    _debug = cfg.getBool("debug", false);
+    printf("debug: %d \n", _debug);
+
+    _debugVerbose = cfg.getBool("debugVerbose", false);
+    printf("debugVerbose: %d \n", _debugVerbose);
+
+    IpAddr ipAddr;
+    int intIp[4];
+
+    bool dhcp = true;
+    if (cfg.fillIntArray4("mbed_ip_address", intIp)) {
+        ipAddr = IpAddr(intIp[0], intIp[1], intIp[2], intIp[3]);
+        // Continue gathering IP configuration.
+        if (cfg.fillIntArray4("mbed_ip_netmask", intIp)) {
+            IpAddr ipMask = IpAddr(intIp[0], intIp[1], intIp[2], intIp[3]);
+            if (cfg.fillIntArray4("mbed_ip_gateway", intIp)) {
+                IpAddr ipGtwy = IpAddr(intIp[0], intIp[1], intIp[2], intIp[3]);
+                if (cfg.fillIntArray4("mbed_ip_dnssrvr", intIp)) {
+                    IpAddr ipDns = IpAddr(intIp[0], intIp[1], intIp[2], intIp[3]);
+                    
+                    _eth = new EthernetNetIf(ipAddr, ipMask, ipGtwy, ipDns);
+                    dhcp = false;
+                }
+            }
+        }
+    }
+    if (dhcp) {
+        if (_debug) printf("mbed_ip_* address config not found or invalid, so defaulting to DHCP \n");
+        _eth = new EthernetNetIf;
+    }
+    
+    // Set up the mbed ethernet interface.
+    EthernetErr ethErr = _eth->setup();
+    if (ethErr != ETH_OK) {
+        printf("Ethernet setup failed (error = %d) \n", ethErr);
+        returnStatus = false;
+    }
+
+    if (_debug) {
+        ipAddr = _eth->getIp();
+        printf("mbed IP address is %d.%d.%d.%d \n", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
+    }
+
+    const Ethernet::Mode linkModes[] = {Ethernet::AutoNegotiate, Ethernet::HalfDuplex10, Ethernet::FullDuplex10, Ethernet::HalfDuplex100, Ethernet::FullDuplex100};
+    int linkMode = cfg.getInt("ethernet_link_mode", 0);
+    ethernet->set_link(linkModes[linkMode]);
+    if (_debug) printf("ethernet_link_mode set to %d \n", linkMode);
+
+    if (_debug) printf("Setting recvHost and sendHost \n");
+    _recvHost.setIp(ipAddr);
+    _recvHost.setPort(cfg.getInt("recv_port", 49000));
+    if (cfg.fillIntArray4("xplane_ip_address", intIp)) {
+        _sendHost.setIp(IpAddr(intIp[0], intIp[1], intIp[2], intIp[3]));
+    }
+    _sendHost.setPort(cfg.getInt("xplane_ip_port", 49000));
+    
+    if (_debug) printf("Setting event callback handler \n");
+    _udpSocket.setOnEvent(this, &XPlaneIO::onUDPSocketEvent);
+
+    if (_debug) printf("Binding to recvHost \n");
+    UDPSocketErr udpErr = _udpSocket.bind(_recvHost);
+    if (udpErr != UDPSOCKET_OK) {
+        printf("Failure in binding to recvHost (error = %d) \n", udpErr);
+        returnStatus = false;
+    }
+
+    _sendInterval = cfg.getInt("send_interval", 200);
+    printf("send_interval: %d ms \n", _sendInterval);
+
+    _reverseByteOrder = cfg.getBool("reverse_byte_order", false);
+    printf("reverse_byte_order: %d \n", _reverseByteOrder);
+
+    char key[33];
+    float f4[4];
+
+    // Scan for Analog Inputs (up to 6).
+    _xpAnalogInCount = 0;
+    for (int a = 1; a <= 6; a++) {
+        sprintf(key, "ain_%d_pin", a);
+        PinName pin = cfg.getPin(key, NC);
+        if (pin != NC) {
+            sprintf(key, "ain_%d_scale1", a);
+            if (cfg.fillFloatArray4(key, f4)) {
+                Scaler<float> scale1(f4[0], f4[1], f4[2], f4[3]);
+                sprintf(key, "ain_%d_scale2", a);
+                if (cfg.fillFloatArray4(key, f4)) {
+                    Scaler<float> scale2(f4[0], f4[1], f4[2], f4[3]);
+                    sprintf(key, "ain_%d_msg_idx", a);
+                    int msgIdx = cfg.getInt(key, -1);
+                    if (msgIdx >= 0) {
+                        sprintf(key, "ain_%d_float_idx", a);
+                        int floatIdx = cfg.getInt(key, -1);
+                        if (floatIdx >= 0) {
+                            // found all valid config items
+                            printf("Setting Analog Input # %d \n", a);
+                            _xpAnalogIn[_xpAnalogInCount] = new XPlaneAnalogIn(pin, scale1, scale2, msgIdx, floatIdx);
+                            _xpAnalogInCount++;
+                            
+                            addDATAToSend(msgIdx);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    for (int i = _xpAnalogInCount; i < 6; i++) {
+        _xpAnalogIn[i] = NULL;
+    }
+
+    _udpDecoder.setDebug(_debugVerbose);
+    _udpDecoder.setReverseByteOrder(_reverseByteOrder);
+    
+    _udpEncoder.setDebug(_debugVerbose);
+    
+    return returnStatus;
+}
+
+
+bool XPlaneIO::addDATAToReceive(int msgIndex) {
+    return addXPlaneUdpDATAToMap(_recvDATAMap, msgIndex, _reverseByteOrder);
+}
+
+bool XPlaneIO::addDATAToSend(int msgIndex) {
+    return addXPlaneUdpDATAToMap(_sendDATAMap, msgIndex, _reverseByteOrder);
+}
+
+XPlaneUdpDATA * XPlaneIO::getReceiveDATA(int msgIndex) const {
+    return getXPlaneUdpDATA(_recvDATAMap, msgIndex);
+}
+
+XPlaneUdpDATA * XPlaneIO::getSendDATA(int msgIndex) const {
+    return getXPlaneUdpDATA(_sendDATAMap, msgIndex);
+}
+
+float XPlaneIO::getReceiveDATAValue(int msgIndex, int floatIndex) const {
+    XPlaneUdpDATA *DATAmsg = getReceiveDATA(msgIndex);
+    if (DATAmsg != NULL) {
+        return DATAmsg->getData(floatIndex);
+    }
+    else {
+        return -999.999f;
+    }
+}
+
+void XPlaneIO::setSendDATAValue(int msgIndex, int floatIndex, float f) {
+    XPlaneUdpDATA *DATAmsg = getSendDATA(msgIndex);
+    if (DATAmsg != NULL) {
+        DATAmsg->setData(floatIndex, f);
+    }
+}
+
+
+void XPlaneIO::pollAnalogIn() {
+    int scaleRange;
+    bool noChange;
+    for (int i = 0; i < _xpAnalogInCount; i++) {
+        XPlaneAnalogIn * xpAIn = _xpAnalogIn[i];
+        float aout = xpAIn->read_scaled(scaleRange, noChange);
+        if (! noChange) {
+            setSendDATAValue(xpAIn->xplaneDATAMsgIdx(), xpAIn->xplaneDATAMsgFloatIdx(), aout);
+        }
+    }
+}
+
+
+void XPlaneIO::sendDREF(XPlaneUdpDREF & dref) {
+    _sendDREFQueue.push(dref);
+}
+
+
+void XPlaneIO::startSendingUdp() {
+    _sendTicker.attach_us(this, &XPlaneIO::sendUdp, _sendInterval * 1000);
+}
+
+void XPlaneIO::stopSendingUdp() {
+    _sendTicker.detach();
+}
+
+void XPlaneIO::sendUdp() {
+    if (_debugVerbose) printf("sendUdp() called \n");
+    
+    pollAnalogIn();
+    
+    int len = _udpEncoder.encodeFromDATAMap(_udpSendBuffer, UDP_SEND_BUFFER_SIZE, _sendDATAMap, true);
+    if (len > 5) {
+        udpSocketSend(_udpSendBuffer, len);
+        resetDataChangedInMap(_sendDATAMap);
+    }
+    
+    while (_sendDREFQueue.size() > 0) {
+        len = _udpEncoder.encodeDataRef(_udpSendBuffer, UDP_SEND_BUFFER_SIZE, _sendDREFQueue.front());
+        if (len > 0) {
+            udpSocketSend(_udpSendBuffer, len);
+        }
+        _sendDREFQueue.pop();
+    }
+}
+
+
+/**
+ * Callback handler for received UDP data.
+ */
+void XPlaneIO::onUDPSocketEvent(UDPSocketEvent e) {
+    if (_debugVerbose) printf("onUDPSocketEvent() called \n");
+    
+    if (e == UDPSOCKET_READABLE) {
+        if (_debugVerbose) printf("onUDPSocketEvent() : event=UDPSOCKET_READABLE \n");
+        
+        Host host;
+        while (int len = _udpSocket.recvfrom(_udpRecvBuffer, UDP_RECV_BUFFER_SIZE, &host)) {
+            if (len <= 0) {
+                break;
+            }
+            
+            if (_debugVerbose) {
+                IpAddr hostIp = host.getIp();
+                //printf("From %d.%d.%d.%d: len=%d: %s\n", hostIp[0], hostIp[1], hostIp[2], hostIp[3], len, buf);
+                printf("From %d.%d.%d.%d: len=%d \n", hostIp[0], hostIp[1], hostIp[2], hostIp[3], len);
+            }
+            
+            XPlaneUdpMessageType pktMsgType = _udpDecoder.setUdpBuffer(_udpRecvBuffer, len);
+            if (pktMsgType == DATA) {
+                _udpDecoder.putIntoDATAMap(_recvDATAMap, true);
+            }
+        }
+    }
+}
+
+
+int XPlaneIO::udpSocketSend(char * buf, int len) {
+    if (_debugVerbose) printf("udpSocketSend(): sending %d bytes \n", len);
+    
+    int nSent = _udpSocket.sendto(buf, len, &_sendHost);
+
+    if ( nSent < 0 ) {
+        if (_debug) printf("PROBLEM SENDING UDP DATA (error = %d) \n", (UDPSocketErr)nSent);
+    }
+    else if ( nSent != len ) {
+        if (_debug) printf("WARNING: %d bytes were sent \n", nSent);
+    }
+    
+    return nSent;
+}
+
+
+
+bool XPlaneIO::debug() const {
+    return _debug;
+}
+
+bool XPlaneIO::reverseByteOrder() const {
+    return _reverseByteOrder;
+}
+
+XPlaneUdpDATAMap & XPlaneIO::recvDATAMap() {
+    return _recvDATAMap;
+}
+
+XPlaneUdpDATAMap & XPlaneIO::sendDATAMap() {
+    return _sendDATAMap;
+}