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

Files at this revision

API Documentation at this revision

Comitter:
bapowell
Date:
Wed Dec 21 22:29:59 2011 +0000
Commit message:

Changed in this revision

EthernetNetIf.lib Show annotated file Show diff for this revision Revisions of this file
LocalConfigFile/ConfigFile.lib Show annotated file Show diff for this revision Revisions of this file
LocalConfigFile/LocalConfigFile.cpp Show annotated file Show diff for this revision Revisions of this file
LocalConfigFile/LocalConfigFile.h Show annotated file Show diff for this revision Revisions of this file
Ranger/Ranger.h Show annotated file Show diff for this revision Revisions of this file
Scaler/Scaler.h Show annotated file Show diff for this revision Revisions of this file
TimeoutPrompt/TimeoutPrompt.cpp Show annotated file Show diff for this revision Revisions of this file
TimeoutPrompt/TimeoutPrompt.h Show annotated file Show diff for this revision Revisions of this file
XPlaneIO/XPlaneAnalogIn.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneIO/XPlaneAnalogIn.h Show annotated file Show diff for this revision Revisions of this file
XPlaneIO/XPlaneIO.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneIO/XPlaneIO.h Show annotated file Show diff for this revision Revisions of this file
XPlaneIO/XPlaneIO_diag.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdp.h Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDATA.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDATA.h Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDATAMap.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDATAMap.h Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDREF.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDREF.h Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDecoder.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpDecoder.h Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpEncoder.cpp Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdpEncoder.h Show annotated file Show diff for this revision Revisions of this file
XPlaneUdp/XPlaneUdp_util.cpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetNetIf.lib	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mamezu/code/EthernetNetIf/#0f6c82fcde82
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LocalConfigFile/ConfigFile.lib	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/shintamainjp/code/ConfigFile/#f6ceafabe9f8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LocalConfigFile/LocalConfigFile.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,114 @@
+#include <ctype.h>
+#include "LocalConfigFile.h"
+
+LocalConfigFile::LocalConfigFile(char *filename) {
+    LocalFileSystem localFileSys("local");  // create the local filesystem under the name "local"
+    sprintf(_buf, "/local/%s", filename);   // set full file path into _buf
+    printf("Reading config file: %s ... ", _buf);
+    if (read(_buf)) {
+        printf("Success \n");
+    }
+    else {
+        printf("Problem \n");
+    }
+}
+
+char LocalConfigFile::getChar(char *key, char defaultVal) {
+    if (getValueIntoBuf(key)) {
+        if (strlen(_buf) > 0) {
+            return _buf[0];
+        }
+        else {
+            printf("getChar(key='%s'): Problem \n", key);
+        }
+    }
+    return defaultVal;
+}
+
+bool LocalConfigFile::getBool(char *key, bool defaultVal) {
+    char c = getChar(key, '.');
+    if (c != '.') {
+        c = toupper(c);
+        return (c == 'T' || c == 'Y' || c == '1');
+    }
+    else {
+        printf("getBool(key='%s'): Problem \n", key);
+    }
+    return defaultVal;
+}
+
+int LocalConfigFile::getInt(char *key, int defaultVal) {
+    if (getValueIntoBuf(key)) {
+        int val;
+        int cnt = sscanf(_buf, "%d", &val);
+        if (cnt == 1) {
+            return val;
+        }
+        else {
+            printf("getInt(key='%s'): Problem \n", key);
+        }
+    }
+    return defaultVal;
+}
+
+PinName LocalConfigFile::getPin(char *key, PinName defaultVal) {
+    const PinName pinName[] = {p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18,
+                               p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30};
+    int p = getInt(key, -1);
+    if (p == -1) {
+        printf("getPin(key='%s'): Problem \n", key);
+    }
+    else if (p < 5 || p > 30) {
+        printf("getPin(key='%s'): Invalid pin#: %d. Pin# must be between 5 and 30, inclusive.\n", key, p);
+    }
+    else {
+        return pinName[p - 5];
+    }
+    return defaultVal;
+}
+
+bool LocalConfigFile::fillIntArray4(char *key, int *intArray) {
+    if (getValueIntoBuf(key)) {
+        int a0, a1, a2, a3;
+        int cnt = sscanf(_buf, "%d %d %d %d", &a0, &a1, &a2, &a3);
+        if (cnt == 4) {
+            intArray[0] = a0;
+            intArray[1] = a1;
+            intArray[2] = a2;
+            intArray[3] = a3;
+            return true;
+        }
+        else {
+            printf("fillIntArray4(key='%s'): Problem \n", key);
+        }
+    }
+    return false;
+}
+
+bool LocalConfigFile::fillFloatArray4(char *key, float *floatArray) {
+    if (getValueIntoBuf(key)) {
+        float a0, a1, a2, a3;
+        int cnt = sscanf(_buf, "%f %f %f %f", &a0, &a1, &a2, &a3);
+        if (cnt == 4) {
+            floatArray[0] = a0;
+            floatArray[1] = a1;
+            floatArray[2] = a2;
+            floatArray[3] = a3;
+            return true;
+        }
+        else {
+            printf("fillFloatArray4(key='%s'): Problem \n", key);
+        }
+    }
+    return false;
+}
+
+bool LocalConfigFile::getValueIntoBuf(char *key) {
+    if (getValue(key, _buf, sizeof(_buf))) {
+        return true;
+    }
+    else {
+        printf("LocalConfigFile.getValueIntoBuf(key='%s'): Problem, or config key not found \n", key);
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LocalConfigFile/LocalConfigFile.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,25 @@
+#ifndef LOCALCONFIGFILE_H_INCLUDED
+#define LOCALCONFIGFILE_H_INCLUDED
+
+#include "mbed.h"
+#include "ConfigFile.h"
+
+class LocalConfigFile : public ConfigFile {
+public:
+
+    LocalConfigFile(char *filename);
+    char getChar(char *key, char defaultVal);
+    bool getBool(char *key, bool defaulVal);
+    int getInt(char *key, int defaultVal);
+    PinName getPin(char *key, PinName defaultVal);
+    bool fillIntArray4(char *key, int *intArray);
+    bool fillFloatArray4(char *key, float *floatArray);
+
+private:
+
+    char _buf[100];
+
+    bool getValueIntoBuf(char *key);
+};
+
+#endif // LOCALCONFIGFILE_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Ranger/Ranger.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,159 @@
+#ifndef RANGER_H_INCLUDED
+#define RANGER_H_INCLUDED
+
+#include <vector>
+#include <stdarg.h>
+
+/**
+ * Perform range-type functions on a list of numbers.
+ * The numbers must be in ascending order.
+ *
+ * Example:
+ * @code
+ *
+ * #include "mbed.h"
+ * #include "Ranger.h"
+ *
+ * AnalogIn hatSwitch(p16);
+ *
+ * int main() {
+ * 
+ *     Ranger<float> hatSwitchInputRanger(4,  0.59, 0.68, 0.78, 0.92);
+ *     int hatSwitchDegreesByRange[5] = {0, -90, 180, 90, 0};
+ *
+ *     while(1) {
+ *         int newRange = hatSwitchInputRanger.range(hatSwitch.read());
+ *         if (hatSwitchInputRanger.rangeChanged()) {
+ *             int degrees = hatSwitchDegreesByRange[newRange];
+ *         }
+ *         wait(0.1);
+ *     }
+ * }
+ * @endcode
+ */
+ 
+template<typename N>
+class Ranger {
+public:
+    
+    /**
+     * Create a Ranger object from the given set of passed-in numbers.
+     * The numbers in the variable argument list must be in ascending order.
+     *
+     * @param argCount number of arguments that follow
+     * @param ... numbers, in ascending order, which define the ranges 
+     */
+    Ranger(int argCount, ... );
+
+    /**
+     * Create a Ranger object from the given vector.
+     * The numbers in the vector must be in ascending order.
+     *
+     * @param vect vector to copy from
+     */
+    Ranger(vector<N> vect);
+
+    /**
+     * Create a Ranger object from the given array.
+     * The numbers in the array must be in ascending order.
+     *
+     * @param count number of elements to copy from the array
+     * @param arr array to copy from
+     */
+    Ranger(int count, N arr[]);
+
+    /**
+     * Return the range that the given number falls in.
+     * The range is a slice index (zero-based), based on the Ranger's vector/array.
+     *
+     * @param input value to determine range from
+     */
+    int range(N input);
+
+    /**
+     * Check if the last call to range() resulted in a change of range from the previous call.
+     */
+    bool rangeChanged() const;
+    
+    /**
+     * Print a representation of this instance to outputStream.
+     */
+    void toString(FILE * outputStream);
+
+private:
+
+    /**
+     * Common initialization, called from each constructor.
+     */
+    void init();
+    
+    vector<N> _nbrVector;
+    N _lastInput;
+    int _lastRange;
+    bool _rangeChanged;
+};
+
+
+template<typename N>
+void Ranger<N>::init() {
+    _lastRange = -1;
+    _rangeChanged = false;
+}
+
+template<typename N>
+Ranger<N>::Ranger(int argCount, ... ) {
+    va_list argList;
+    va_start(argList, argCount);
+    for (int i = 0; i < argCount; i++) {
+        _nbrVector.push_back(va_arg(argList, N));
+    }
+    va_end(argList);
+    
+    init();
+}
+
+template<typename N>
+Ranger<N>::Ranger(vector<N> vect) : _nbrVector(vect) {
+    init();
+}
+
+template<typename N>
+Ranger<N>::Ranger(int count, N arr[]) : _nbrVector(arr, arr + count) {
+    init();
+}
+
+template<typename N>
+int Ranger<N>::range(N input) {
+
+    int newRange = -1;
+    
+    for (int i=0; i < _nbrVector.size(); i++) {
+        if (input < _nbrVector[i]) {
+            newRange = i;
+            break;
+        }
+    }
+    
+    if (newRange == -1) {
+        newRange = _nbrVector.size();
+    }
+    
+    _rangeChanged = (_lastRange != newRange);
+    _lastInput = input;
+    _lastRange = newRange;
+    
+    return newRange;
+}
+
+template<typename N>
+bool Ranger<N>::rangeChanged() const {
+    return _rangeChanged;
+}
+
+template<typename N>
+void Ranger<N>::toString(FILE * outputStream) {
+    fprintf(outputStream, "Ranger: size=%d, lastRange=%d, changed=%d",
+            _nbrVector.size(), _lastRange, _rangeChanged);
+}
+
+#endif  // RANGER_H_INCLUDED
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Scaler/Scaler.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,113 @@
+#ifndef SCALER_H_INCLUDED
+#define SCALER_H_INCLUDED
+
+/**
+ * Use linear projection (interpolation) to map/scale values from one range into another.
+ *
+ * Example:
+ * @code
+ *
+ * #include "mbed.h"
+ * #include "Scaler.h"
+ *
+ * AnalogIn pot(p20);
+ *
+ * Scaler<float> left(0.965, 0.653, -1, 0);
+ * Scaler<float> right(0.653, 0.491, 0, 1);
+ *
+ * int main() {
+ *     while(1) {
+ *         float scaledPotValue;
+ *         float potValue = pot.read();
+ *         printf("Pot value = %f, %d", potValue, pot.read_u16());
+ *         if (potValue > 0.653) {
+ *             scaledPotValue = left.scale(potValue);
+ *         }
+ *         else {
+ *             scaledPotValue = right.scale(potValue);
+ *         }
+ *         printf("... scaled: %f \n", scaledPotValue);
+ *         wait(0.1);
+ *     }
+ * }
+ * @endcode
+ */
+ 
+template<typename N>
+class Scaler {
+public:
+    
+    /**
+     * Create a Scaler object using the specified input and output ranges.
+     *
+     * @param inputFrom input range "from"
+     * @param inputTo input range "to"
+     * @param outputFrom output range "from"
+     * @param outputTo output range "to"
+     */
+    Scaler(N inputFrom, N inputTo, N outputFrom, N outputTo);
+
+    /**
+     * Map the input value, within the input range, to the output range.
+     *
+     * @param input value to project to the output range
+     */
+    N scale(N input) const;
+
+    // Accessors
+    N inputFrom() const;
+    N inputTo() const;
+    N outputFrom() const;
+    N outputTo() const;
+    N slope() const;
+
+private:
+
+    N _inputFrom;
+    N _inputTo;
+    N _outputFrom;
+    N _outputTo;
+    N _slope;
+};
+
+
+template<typename N>
+Scaler<N>::Scaler(N inputFrom, N inputTo, N outputFrom, N outputTo) {
+    _inputFrom = inputFrom;
+    _inputTo = inputTo;
+    _outputFrom = outputFrom;
+    _outputTo = outputTo;
+    _slope = (outputTo - outputFrom) / (inputTo - inputFrom);
+}
+
+template<typename N>
+N Scaler<N>::scale(N input) const {
+    return _slope * (input - _inputFrom) + _outputFrom;
+}
+
+template<typename N>
+N Scaler<N>::inputFrom() const {
+    return _inputFrom;
+}
+
+template<typename N>
+N Scaler<N>::inputTo() const {
+    return _inputTo;
+}
+
+template<typename N>
+N Scaler<N>::outputFrom() const {
+    return _outputFrom;
+}
+
+template<typename N>
+N Scaler<N>::outputTo() const {
+    return _outputTo;
+}
+
+template<typename N>
+N Scaler<N>::slope() const {
+    return _slope;
+}
+
+#endif  // SCALER_H_INCLUDED
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TimeoutPrompt/TimeoutPrompt.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,28 @@
+#include "TimeoutPrompt.h"
+
+TimeoutPrompt::TimeoutPrompt(Serial & serialInOut, int defaultTimeoutSeconds) : 
+    _serialInOut(serialInOut), _defaultTimeoutSeconds(defaultTimeoutSeconds)
+{
+    _timer.reset();
+}
+ 
+char TimeoutPrompt::prompt(int timeoutSeconds, char *promptString, char* validChars) {
+    _serialInOut.printf("%s\n", promptString);
+    _timer.reset();
+    _timer.start();
+    while (_timer.read() < timeoutSeconds) {
+        if (_serialInOut.readable()) {
+            char c = _serialInOut.getc();
+            if (strchr(validChars, c) != NULL) {
+                _timer.stop();
+                return c;
+            }
+        }
+    }
+    _timer.stop();
+    return NULL;
+}
+
+char TimeoutPrompt::prompt(char *promptString, char* validChars) {
+    return prompt(_defaultTimeoutSeconds, promptString, validChars);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TimeoutPrompt/TimeoutPrompt.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,30 @@
+#ifndef TIMEOUTPROMPT_H_INCLUDED
+#define TIMEOUTPROMPT_H_INCLUDED
+
+#include "mbed.h"
+
+class TimeoutPrompt {
+public:
+
+    TimeoutPrompt(Serial & serialInOut, int defaultTimeoutSeconds);
+    
+    /**
+     * Output the given prompt for timeoutSeconds. If a character is input that matches
+     * one of the validChars, then return it. Otherwise, if no valid character is
+     * pressed within the timeout, then return NULL.
+     */
+    char prompt(int timeoutSeconds, char *promptString, char* validChars);
+
+    /**
+     * Overloaded method that uses defaultTimeoutSeconds.
+     */
+    char prompt(char *promptString, char* validChars);
+    
+private:
+
+    Serial & _serialInOut;
+    int _defaultTimeoutSeconds;
+    Timer _timer;
+};
+
+#endif // TIMEOUTPROMPT_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneIO/XPlaneAnalogIn.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,85 @@
+#include "Scaler.h"
+#include "XPlaneAnalogIn.h"
+
+XPlaneAnalogIn::XPlaneAnalogIn(PinName pin, Scaler<float> scale1, Scaler<float> scale2, int msgIdx, int msgFloatIdx) : 
+    _pin(pin), _scale1(scale1), _scale2(scale2), _xplaneDATAMsgIdx(msgIdx), _xplaneDATAMsgFloatIdx(msgFloatIdx),
+    _ain(pin)
+{
+    _lastUnscaledRead = -1.0;
+    _lastScaledRead = -999.0;
+    _lastScaleRange = -1;
+}
+
+float XPlaneAnalogIn::read_unscaled() {
+    _lastUnscaledRead = _ain.read();
+    return _lastUnscaledRead;
+}
+
+float XPlaneAnalogIn::read_scaled(int & range, bool & scaledOutputNoChange) {
+    float scaledOutput;
+    float rawInput = read_unscaled();
+    
+    if (rawInput > _scale1.inputTo()  &&  rawInput < _scale2.inputFrom()) {
+        range = 0;
+        scaledOutput = (_scale1.outputTo() + _scale2.outputFrom()) / 2.0f;
+    }
+    else if (rawInput <= _scale1.inputTo()) {
+        if (rawInput < _scale1.inputFrom()) {
+            range = 3;
+            scaledOutput = _scale1.outputFrom();
+        }
+        else {
+            range = 1;
+            scaledOutput = _scale1.scale(rawInput);
+        }
+    }
+    else {  // rawInput >= _scale2.inputFrom
+        if (rawInput > _scale2.inputTo()) {
+            range = 4;
+            scaledOutput = _scale2.outputTo();
+        }
+        else {
+            range = 2;
+            scaledOutput = _scale2.scale(rawInput);
+        }
+    }
+    
+    scaledOutputNoChange = ((scaledOutput == _lastScaledRead) || 
+                            (range == _lastScaleRange  &&  (range == 0 || range == 3 || range == 4)));
+
+    _lastScaledRead = scaledOutput;                            
+    _lastScaleRange = range;
+    
+    return scaledOutput;
+}
+
+/*
+void XPlaneAnalogIn::scale1(Scaler<float> scale1) {
+    _scale1 = scale1;
+}
+
+void XPlaneAnalogIn::scale2(Scaler<float> scale2) {
+    _scale2 = scale2;
+}
+
+void XPlaneAnalogIn::xplaneDATAMsgIdx(int xplaneDATAMsgIdx) {
+    _xplaneDATAMsgIdx = xplaneDATAMsgIdx;
+}
+
+void XPlaneAnalogIn::xplaneDATAMsgFloatIdx(int xplaneDATAMsgFloatIdx) {
+    _xplaneDATAMsgFloatIdx = xplaneDATAMsgFloatIdx;
+}
+*/
+
+int XPlaneAnalogIn::xplaneDATAMsgIdx() {
+    return _xplaneDATAMsgIdx;
+}
+
+int XPlaneAnalogIn::xplaneDATAMsgFloatIdx() {
+    return _xplaneDATAMsgFloatIdx;
+}
+
+void XPlaneAnalogIn::toString(FILE * outputStream) {
+    fprintf(outputStream, "lastRawRead=%f lastScaledRead=%f lastScaleRange=%d",
+            _lastUnscaledRead, _lastScaledRead, _lastScaleRange);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneIO/XPlaneAnalogIn.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,65 @@
+#ifndef XPLANEANALOGIN_H_INCLUDED
+#define XPLANEANALOGIN_H_INCLUDED
+
+#include "mbed.h"
+#include "Scaler.h"
+#include "XPlaneUdpDATA.h"
+
+class XPlaneAnalogIn {
+public:
+
+    XPlaneAnalogIn(PinName pin, Scaler<float> scale1, Scaler<float> scale2, int msgIdx, int msgFloatIdx);
+    
+    /**
+     * Perform a read() on the AnalogIn pin, and return that value.
+     */
+    float read_unscaled();
+
+    /**
+     * Perform a read() on the AnalogIn pin, and return the value scaled by the scale1 and scale2 ranges.
+     * The range used for the scaling is returned via the reference parameter:
+     *     0 = input fell in between the ranges (i.e. deadband zone), so scaled output is midpoint between scale1 outputTo and scale2 outputFrom 
+     *     1 = input fell in the scale1 range
+     *     2 = input fell in the scale2 range
+     *     3 = input was less than scale1 inputFrom, so scaled output is (bounded by) scale1 outputFrom
+     *     4 = input was greater than scale2 inputTo, so scaled output is (bounded by) scale2 outputTo
+     * The scaledOutputNoChange reference parameter will be set true if the scaled value didn't changed since the last read.
+     */
+    float read_scaled(int & range, bool & scaledOutputNoChange);
+    
+    
+    // Property accessors and mutators
+    
+    //Scaler<float> scale1();
+    //void scale1(Scaler<float> scale1);
+    
+    //Scaler<float> scale2();
+    //void scale2(Scaler<float> scale2);
+    
+    int xplaneDATAMsgIdx();
+    //void xplaneDATAMsgIdx(int xplaneDATAMsgIdx);
+    
+    int xplaneDATAMsgFloatIdx();
+    //void xplaneDATAMsgFloatIdx(int xplaneDATAMsgFloatIdx);
+    
+    /**
+     * Print a representation of this instance to outputStream.
+     */
+    void toString(FILE * outputStream);
+
+private:
+
+    PinName _pin;
+    Scaler<float> _scale1;
+    Scaler<float> _scale2;
+    int _xplaneDATAMsgIdx;
+    int _xplaneDATAMsgFloatIdx;
+    
+    AnalogIn _ain;
+    
+    float _lastUnscaledRead;
+    float _lastScaledRead;
+    int _lastScaleRange;
+};
+
+#endif // XPLANEANALOGIN_H_INCLUDED
--- /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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneIO/XPlaneIO.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,223 @@
+#ifndef XPLANEIO_H_INCLUDED
+#define XPLANEIO_H_INCLUDED
+
+#include <queue>
+#include "mbed.h"
+#include "EthernetNetIf.h"
+#include "UDPSocket.h"
+#include "XPlaneUdpDATA.h"
+#include "XPlaneUdpDecoder.h"
+#include "XPlaneUdpEncoder.h"
+#include "XPlaneAnalogIn.h"
+
+#define UDP_RECV_BUFFER_SIZE 512
+#define UDP_SEND_BUFFER_SIZE 512
+
+/**
+ * XPlaneIO is used to establish networked communication with X-Plane.
+ * Methods in this class allow the the client to send data to X-Plane as well as
+ * read data that has been sent from X-Plane.
+ */
+ 
+class XPlaneIO {
+public:
+
+    //XPlaneIO();
+    ~XPlaneIO();
+    
+    /**
+     * Read configuration file and initialize.
+     */
+    bool setup(char * configFilename, Ethernet * ethernet);
+    
+    /**
+     * Check if user wants to perform interactive XPlaneIO diagnostics. If so, enter diagnostics mode.
+     */
+    void diagnostics(Serial & serialInOut);
+
+    /**
+     * Include a DATA message, of the given index, in the UDP map -- for receiving from X-Plane.
+     * If a message of the given index already exists in the map, then do nothing.
+     * Returns true if a new DATA message was created and added to the map.
+     */
+    bool addDATAToReceive(int msgIndex);
+
+    /**
+     * Include a DATA message, of the given index, in the UDP map -- for sending to X-Plane.
+     * If a message of the given index already exists in the map, then do nothing.
+     * Returns true if a new DATA message was created and added to the map.
+     */
+    bool addDATAToSend(int msgIndex);
+
+    /**
+     * Return a pointer to the DATA message, of the given index, in the UDP receive map (data received from X-Plane).
+     * If a message of the given index does not exist in the map, then return NULL.
+     */
+    XPlaneUdpDATA * getReceiveDATA(int msgIndex) const;
+
+    /**
+     * Return a pointer to the DATA message, of the given index, in the UDP send map (data to send to X-Plane).
+     * If a message of the given index does not exist in the map, then return NULL.
+     */
+    XPlaneUdpDATA * getSendDATA(int msgIndex) const;
+
+    /**
+     * Return the float data value, of the given DATA message index, at the given float index (0-7), in the UDP receive map (data received from X-Plane).
+     * If a message of the given index does not exist in the map, then return -999.999f.
+     */
+    float getReceiveDATAValue(int msgIndex, int floatIndex) const;
+
+    /**
+     * Set the float data value, of the given DATA message index, at the given float index (0-7), in the UDP send map (data to send to X-Plane).
+     * If a message of the given index does not exist in the map, then do nothing.
+     */
+    void setSendDATAValue(int msgIndex, int floatIndex, float f);
+
+    /**
+     * Add an X-Plane DataRef to the queue -- to be sent the next time the 
+     * sendUdp() ticker function is called.
+     */
+    void sendDREF(XPlaneUdpDREF & dref);
+
+    /**
+     * Start sending UDP data to X-Plane.
+     */
+    void startSendingUdp();
+    
+    /**
+     * Stop sending UDP data to X-Plane.
+     */
+    void stopSendingUdp();
+    
+    /**
+     * Utility method to send data via the UDPSocket to the sendHost.
+     */
+    int udpSocketSend(char * buf, int len);
+    
+    // Accessors
+    bool debug() const;
+    bool reverseByteOrder() const;
+    XPlaneUdpDATAMap & recvDATAMap();
+    XPlaneUdpDATAMap & sendDATAMap();
+    
+private:
+
+    bool _debug;
+    bool _debugVerbose;
+    
+    //Serial & _serialInOut;
+    
+    EthernetNetIf * _eth;     // pointer, because don't want instantiated until gather config for it
+
+    // Set up local and remote hosts.
+    Host _recvHost;    // mbed local host
+    Host _sendHost;    // IP address and port to which UDP will be sent
+
+    // UDP socket to use for both binding to local address (to recv data) and to send data to remote host.
+    UDPSocket _udpSocket;
+
+    int _sendInterval;  // milliseconds
+    Ticker _sendTicker;
+        
+    bool _reverseByteOrder;
+    
+    XPlaneUdpDATAMap _recvDATAMap;   // keyed by DATA message index
+    XPlaneUdpDATAMap _sendDATAMap;   // keyed by DATA message index
+
+    queue<XPlaneUdpDREF> _sendDREFQueue;
+
+    char _udpRecvBuffer[UDP_RECV_BUFFER_SIZE];
+    XPlaneUdpDecoder _udpDecoder;
+
+    char _udpSendBuffer[UDP_SEND_BUFFER_SIZE];
+    XPlaneUdpEncoder _udpEncoder;
+
+    XPlaneAnalogIn * _xpAnalogIn[6];
+    int _xpAnalogInCount;
+    
+    /**
+     * Callback handler for when UDP data has been received.
+     * Note that this handler is assigned within the setup() method.
+     */
+    void onUDPSocketEvent(UDPSocketEvent e);
+
+    /**
+     * Ticker function for sending UDP data.
+     */
+    void sendUdp();
+
+    /**
+     * Poll analog inputs, and...
+     */
+    void pollAnalogIn();
+};
+
+#endif // XPLANEIO_H_INCLUDED
+
+
+
+/* Config file:
+
+debug=Y
+debugVerbose=N
+
+# If mbed_ip_address is not specified, then DHCP will be used.
+mbed_ip_address=192 168 10 202
+mbed_ip_netmask=255 255 255 0
+mbed_ip_gateway=192 168 10 99
+mbed_ip_dnssrvr=192 168 10 99
+
+# Ethernet link mode: 0 = AutoNegotiate, 1 = HalfDuplex10, 2 = FullDuplex10, 3 = HalfDuplex100, 4 = FullDuplex100
+# Note that AutoNegotiate does not work for 10 Mbit hub. e.g. Use 2 (FullDuplex10) for 10 Mbit hub that does not auto-detect speed.
+ethernet_link_mode=0
+
+# Local IP port to bind to. X-Plane should be configured to send data to the mbed_ip_address and this port.
+recv_port=49000
+
+# IP address and port to which UDP data will be sent.
+xplane_ip_address=192 168 10 106
+xplane_ip_port=49000
+
+# Interval (milliseconds) for sending UDP data to X-Plane.
+send_interval=250
+
+# Depending on X-Plane host computer (e.g Mac), byte order in UDP packets may need to be reversed.
+reverse_byte_order=N
+
+# Analog Inputs - for sending an analog value to X-Plane. Up to six are supported (# = 1-6).
+# ain_#_pin       : AnalogIn pin number, 15-20 (p15-20)
+# ain_#_scale1    : Scale range 1: input_from  input_to  output_from  output_to
+#                   Note: A gap between range1's input_to and range2's input_from will be seen as deadband; no data will be sent to X-Plane in that case.
+# ain_#_scale2    : Scale range 2: input_from  input_to  output_from  output_to
+# ain_#_msg_idx   : X-Plane DATA message: message index
+# ain_#_float_idx : X-Plane DATA message: float index (0-7) within the message
+
+# Analog Input #1:
+ain_1_pin=20 
+ain_1_scale1=0.525 0.610 1.0 0.0
+ain_1_scale2=0.615 0.910 0.0 -1.0
+ain_1_msg_idx=8
+ain_1_float_idx=1
+
+# Analog Input #2:
+ain_2_pin=19
+ain_2_scale1=0.520 0.605 1.0 0.0
+ain_2_scale2=0.615 0.920 0.0 -1.0
+ain_2_msg_idx=8
+ain_2_float_idx=0
+
+# Analog Input #3:
+ain_3_pin=17
+ain_3_scale1=0.510 0.615 1.0 0.0
+ain_3_scale2=0.620 0.915 0.0 -1.0
+ain_3_msg_idx=8
+ain_3_float_idx=2
+
+
+# X-Plane DATA
+#  MsgIdx  8 : 0=elev,      1=ailrn,      2=ruddr
+#  MsgIdx 13 : 0=elev trim, 1=ailrn trim, 2=ruddr trim, 3=flap handl, 4=flap postn, 5=slat ratio, 6=sbrak handl, 7=sbrak postn
+#  MsgIdx 25 : 0=throttle command
+#  MsgIdx 26 : 0=throttle actual
+
+*/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneIO/XPlaneIO_diag.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,43 @@
+#include "XPlaneIO.h"
+#include "TimeoutPrompt.h"
+
+void XPlaneIO::diagnostics(Serial & serialInOut) {
+    
+    TimeoutPrompt top(serialInOut, 2);
+
+    if (top.prompt("\nPress 'd' within 2 seconds for interactive diagnostics", "Dd") == NULL) {
+        return;
+    }
+    
+    char c;
+
+    while ((c = top.prompt(30, "\n 1. Analog Input \n 2. \n 3. \n q. Quit \n          Select within 30 seconds", "123Qq")) != NULL
+            && c != 'q' && c != 'Q')
+    {
+        serialInOut.printf("%c was typed \n\n", c);
+
+        switch (c) {
+            case '1':
+                for (int i = 0; i < _xpAnalogInCount; i++) {
+                    pollAnalogIn();
+                    serialInOut.printf("Analog Input #%d: ", i + 1);
+                    _xpAnalogIn[i]->toString(serialInOut);
+                    serialInOut.printf("\n");
+                }
+                break;
+            /*
+            case '2':
+                break;
+            case '3':
+                break;
+            case 'a':
+            case 'A':
+                break;
+            */
+            default:
+                serialInOut.printf("%c is not an option \n");
+        }
+    }
+    
+    serialInOut.printf("Done \n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdp.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,14 @@
+#ifndef XPLANEUDP_H_INCLUDED
+#define XPLANEUDP_H_INCLUDED
+
+enum XPlaneUdpMessageType {
+    DATA,
+    UNKNOWN
+};
+
+/**
+ * Utility function to reverse the order of the four bytes at the given location.
+ */
+void reverse4Bytes(char* bytes);
+
+#endif  // XPLANEUDP_H_INCLUDED
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDATA.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,71 @@
+#include "XPlaneUdpDATA.h"
+
+
+XPlaneUdpDATA::XPlaneUdpDATA(int messageIndex, bool reverseByteOrder) {
+    
+    _messageIndex = messageIndex;
+    _reverseByteOrder = reverseByteOrder;
+    for (int i = 0; i < 8; i++) {
+        _data[i] = -999.0f;
+    }
+    
+    _dataChanged = false;
+}
+
+int XPlaneUdpDATA::messageIndex() const {
+    return _messageIndex;
+}
+
+float XPlaneUdpDATA::getData(int i) const {
+    return _data[i];
+}
+    
+void XPlaneUdpDATA::setData(int i, float f) {
+    _data[i] = f;
+    setDataChanged();
+}
+
+float& XPlaneUdpDATA::operator [] (int i) {
+    return _data[i];
+}
+
+void XPlaneUdpDATA::setAllData(char * DATAMessage) {
+    memcpy(_data, DATAMessage + 4, 32);
+    if (_reverseByteOrder) {
+        for (int d = 0; d < 8; d++) {
+            reverse4Bytes((char*) &(_data[d]));
+        }
+    }
+}
+
+void XPlaneUdpDATA::populateDATAMessage(char * bytes) {
+    memcpy(bytes, &_messageIndex, 4);
+    if (_reverseByteOrder) {
+        reverse4Bytes((char*) bytes);
+    }
+    memcpy(bytes + 4, _data, 32);
+    if (_reverseByteOrder) {
+        for (int d = 1; d < 9; d++) {
+            reverse4Bytes((char*) bytes + (d * 4));
+        }
+    }
+}
+
+bool XPlaneUdpDATA::isDataChanged() const {
+    return _dataChanged;
+}
+
+void XPlaneUdpDATA::setDataChanged() {
+    _dataChanged = true;
+}
+
+void XPlaneUdpDATA::resetDataChanged() {
+    _dataChanged = false;
+}
+
+void XPlaneUdpDATA::toString(FILE * outputStream) {
+    fprintf(outputStream, "idx=%d, 0:%f 1:%f 2:%f 3:%f 4:%f 5:%f 6:%f 7:%f, revBytes=%d",
+            _messageIndex,
+            _data[0], _data[1], _data[2], _data[3], _data[4], _data[5], _data[6], _data[7],
+            _reverseByteOrder);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDATA.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,68 @@
+#ifndef XPLANEUDPDATA_H_INCLUDED
+#define XPLANEUDPDATA_H_INCLUDED
+
+#include <map>
+#include "mbed.h"
+#include "XPlaneUdp.h"
+
+#define XPLANE_DATA_MSG_LENGTH 36   // 1 int + 8 floats
+
+class XPlaneUdpDATA {
+public:
+
+    XPlaneUdpDATA(int messageIndex, bool reverseByteOrder);
+
+    /**
+     * Accessor for the message index of this DATA message.
+     */
+    int messageIndex() const;
+    
+    /**
+     * Accessor for the float data at the given float index (0-7).
+     */
+    float getData(int i) const;
+    
+    /**
+     * Mutator for the float data at the given float index (0-7).
+     * The dataChanged flag will also be set.
+     */
+    void setData(int i, float f);
+    
+    /**
+     * Overload the [] operator to return a reference to the float data at the given float index (0-7).
+     * NOTE: The dataChanged flag will not be set if this is used to modify the float data.
+     */
+    float& operator [] (int i);
+
+    /**
+     * Fill data[8] with 32 bytes starting at (DATAMessage + 4).
+     * Byte order will be reversed, according to the reverseByteOrder property.
+     */
+    void setAllData(char * DATAMessage);
+
+    /**
+     * Starting at the given pointer location, populate 36 bytes from values in index and data[8].
+     * Byte order will be reversed, according to the reverseByteOrder property.
+     */
+    void populateDATAMessage(char * bytes);
+
+    // Methods to read/write flag to track if data has been modified.
+    bool isDataChanged() const;
+    void setDataChanged();
+    void resetDataChanged();
+
+    /**
+     * Print a representation of this instance to outputStream.
+     */
+    void toString(FILE * outputStream);
+
+private:
+
+    int _messageIndex;
+    float _data[8];
+    bool _reverseByteOrder;
+    bool _dataChanged;
+};
+
+
+#endif // XPLANEUDPDATA_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDATAMap.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,39 @@
+#include "XPlaneUdpDATAMap.h"
+
+XPlaneUdpDATA * getXPlaneUdpDATA(const XPlaneUdpDATAMap & DATAMap, int index) {
+    XPlaneUdpDATAMap::iterator iter = DATAMap.find(index);
+    if (iter != DATAMap.end()) {
+        return iter->second;
+    }
+    else {
+        return NULL;
+    }
+}
+
+bool addXPlaneUdpDATAToMap(XPlaneUdpDATAMap & DATAMap, int index, bool reverseByteOrder) {
+    XPlaneUdpDATA *DATAmsg = getXPlaneUdpDATA(DATAMap, index);
+    if (DATAmsg != NULL) {
+        return false;
+    }
+    else {
+        DATAmsg = new XPlaneUdpDATA(index, reverseByteOrder);
+        DATAMap[index] = DATAmsg;
+        return true;
+    }
+}
+
+void resetDataChangedInMap(XPlaneUdpDATAMap & DATAMap) {
+    for (XPlaneUdpDATAMap::iterator it = DATAMap.begin(); it != DATAMap.end(); it++) {
+        (it->second)->resetDataChanged();
+    }
+}
+
+void printXPlaneUdpDATAMap(FILE * outputStream, XPlaneUdpDATAMap & DATAMap) {
+    fprintf(outputStream, "Map:\n");
+    for (XPlaneUdpDATAMap::iterator it = DATAMap.begin(); it != DATAMap.end(); it++) {
+        //(it->second)->resetDataChanged();
+        fprintf(outputStream, "    key: %d  value: ", it->first);
+        (it->second)->toString(outputStream);
+        fprintf(outputStream, "\n");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDATAMap.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,35 @@
+#ifndef XPLANEUDPDATAMAP_H_INCLUDED
+#define XPLANEUDPDATAMAP_H_INCLUDED
+
+#include <map>
+#include "XPlaneUdpDATA.h"
+
+/**
+ * Map of XPlaneUdpDATA pointers, keyed by int (e.g. keyed by the DATA message index).
+ */
+typedef std::map<int, XPlaneUdpDATA *> XPlaneUdpDATAMap;
+
+/**
+ * Function to return the XPlaneUdpDATA pointer in the given map, keyed by the given int index.
+ * If no map entry exists for the given key index, then returns NULL.
+ */
+XPlaneUdpDATA * getXPlaneUdpDATA(const XPlaneUdpDATAMap & DATAMap, int index);
+
+/**
+ * Function to include a DATA message, of the given index, in the given map.
+ * If a message of the given index already exists in the map, then do nothing.
+ * Returns true if a new DATA message was created and added to the map.
+ */
+bool addXPlaneUdpDATAToMap(XPlaneUdpDATAMap & DATAMap, int index, bool reverseByteOrder);
+
+/**
+ * Function to reset the dataChanged flag in all DATA messages in the given map.
+ */
+void resetDataChangedInMap(XPlaneUdpDATAMap & DATAMap);
+
+/**
+ * Utility function to print the DATA messages in the given map.
+ */
+void printXPlaneUdpDATAMap(FILE * outputStream, XPlaneUdpDATAMap & DATAMap);
+
+#endif // XPLANEUDPDATAMAP_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDREF.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,75 @@
+#include "XPlaneUdpDREF.h"
+
+
+XPlaneUdpDREF::XPlaneUdpDREF(char * drefName, bool reverseByteOrder) {
+    
+    _drefName = drefName;
+    _reverseByteOrder = reverseByteOrder;
+    
+    _dataChanged = false;
+    _drefFloat = DREF_VALUE_NA;
+    _drefInt = DREF_VALUE_NA;
+}
+
+
+char * XPlaneUdpDREF::drefName() const {
+    return _drefName;
+}
+
+float XPlaneUdpDREF::drefFloat() const {
+    return _drefFloat;
+}
+    
+int XPlaneUdpDREF::drefInt() const {
+    return _drefInt;
+}
+
+void XPlaneUdpDREF::drefFloat(float floatVal) {
+    _drefFloat = floatVal;
+    _drefInt = DREF_VALUE_NA;
+    setDataChanged();
+}
+    
+void XPlaneUdpDREF::drefInt(int intVal) {
+    _drefInt = intVal;
+    _drefFloat = DREF_VALUE_NA;
+    setDataChanged();
+}
+
+    /**
+     * Starting at the given pointer location, populate bytes from the dataRef value and name.
+     * Byte order will be reversed, according to the reverseBytes property.
+     */
+
+void XPlaneUdpDREF::populateDREFMessage(char * bytes) {
+
+    if (_drefInt != DREF_VALUE_NA) {
+        memcpy(bytes, &_drefInt, 4);
+    }
+    else {
+        memcpy(bytes, &_drefFloat, 4);
+    }
+    
+    if (_reverseByteOrder) {
+        reverse4Bytes((char*) bytes);
+    }
+    
+    strcpy(bytes + 4, _drefName);
+}
+
+bool XPlaneUdpDREF::isDataChanged() const {
+    return _dataChanged;
+}
+
+void XPlaneUdpDREF::setDataChanged() {
+    _dataChanged = true;
+}
+
+void XPlaneUdpDREF::resetDataChanged() {
+    _dataChanged = false;
+}
+
+void XPlaneUdpDREF::toString(FILE * outputStream) {
+    fprintf(outputStream, "DataRef: name=%s, intVal=%d, floatVal=%f, revBytes=%d",
+            _drefName, _drefInt, _drefFloat, _reverseByteOrder);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDREF.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,75 @@
+#ifndef XPLANEUDPDREF_H_INCLUDED
+#define XPLANEUDPDREF_H_INCLUDED
+
+#include <map>
+#include "mbed.h"
+#include "XPlaneUdp.h"
+
+#define DREF_VALUE_NA -999
+
+/**
+ * X-Plane DataRef
+ */
+class XPlaneUdpDREF {
+public:
+
+    XPlaneUdpDREF(char * drefName, bool reverseByteOrder);
+
+    /**
+     * Accessor for the DataRef name.
+     */
+    char * drefName() const;
+    
+    /**
+     * Accessor for the DataRef float data value.
+     * Returns -999 if not applicable (this DataRef uses an int value).
+     */
+    float drefFloat() const;
+    
+    /**
+     * Accessor for the DataRef int data value.
+     * Returns -999 if not applicable (this DataRef uses a float value).
+     */
+    int drefInt() const;
+    
+    /**
+     * Mutator for the DataRef float data value.
+     * The DataRef int data will be set to -999.
+     * The dataChanged flag will also be set.
+     */
+    void drefFloat(float floatVal);
+    
+    /**
+     * Mutator for the DataRef int data value.
+     * The DataRef float data will be set to -999.
+     * The dataChanged flag will also be set.
+     */
+    void drefInt(int intVal);
+    
+    /**
+     * Starting at the given pointer location, populate bytes from the DataRef value and name.
+     * Byte order will be reversed, according to the reverseByteOrder property.
+     */
+    void populateDREFMessage(char * bytes);
+
+    // Methods to read/write flag to track if data has been modified.
+    bool isDataChanged() const;
+    void setDataChanged();
+    void resetDataChanged();
+
+    /**
+     * Print a representation of this instance to outputStream.
+     */
+    void toString(FILE * outputStream);
+
+private:
+
+    char * _drefName;
+    float _drefFloat;
+    int _drefInt;
+    bool _reverseByteOrder;
+    bool _dataChanged;
+};
+
+
+#endif // XPLANEUDPDREF_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDecoder.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include <string.h>
+#include "XPlaneUdp.h"
+#include "XPlaneUdpDecoder.h"
+#include "XPlaneUdpDATA.h"
+
+void XPlaneUdpDecoder::setDebug(bool debug) {
+    _debug = debug;
+}
+
+void XPlaneUdpDecoder::setReverseByteOrder(bool reverseByteOrder) {
+    _reverseByteOrder = reverseByteOrder;
+}
+
+XPlaneUdpMessageType XPlaneUdpDecoder::setUdpBuffer(char * buf, int bufLen) {
+    _udpPacketMsgType = UNKNOWN;
+    _udpBuffer = buf;
+    _udpBufferLen = bufLen;
+    //_udpBuffer[bufLen] = 0; // only need to do this if treating buffer like a string, e.g. printing it
+
+    if (_udpBufferLen >= 5) {
+        if (strncmp(_udpBuffer, "DATA", 4) == 0) {
+            if (_debug) printf("Got DATA pkt \n");
+            _udpPacketMsgType = DATA;
+        }
+    }
+    
+    return _udpPacketMsgType;
+}
+
+int XPlaneUdpDecoder::putIntoDATAMap(XPlaneUdpDATAMap & DATAMap, bool filter) {
+    int updateCount = 0;
+    
+    if (_udpPacketMsgType != DATA) {
+        return 0;
+    }
+    
+    if (filter && (DATAMap.size() == 0)) {
+        return 0;
+    }
+    
+    int dataMsgCount = (_udpBufferLen - 5) / XPLANE_DATA_MSG_LENGTH;
+    if (_debug) printf("%d msgs in pkt \n", dataMsgCount);
+    int bufOffset = 5;
+    for (int msgNbr = 0; msgNbr < dataMsgCount; msgNbr++) {
+    
+        int index;
+        memcpy(&index, _udpBuffer + bufOffset, 4);
+        if (_reverseByteOrder) {
+            reverse4Bytes((char*) &index);
+        }
+        
+        XPlaneUdpDATA *DATAmsg = getXPlaneUdpDATA(DATAMap, index);
+        if (DATAmsg != NULL) {
+            DATAmsg->setAllData(_udpBuffer + bufOffset);
+            updateCount++;
+        }
+        else if (! filter) {
+            DATAmsg = new XPlaneUdpDATA(index, _reverseByteOrder);
+            DATAmsg->setAllData(_udpBuffer + bufOffset);
+            DATAMap[index] = DATAmsg;
+            updateCount++;
+        }
+        
+        bufOffset += XPLANE_DATA_MSG_LENGTH;
+    }
+    
+    return updateCount;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpDecoder.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,43 @@
+#ifndef XPLANEUDPDECODER_H_INCLUDED
+#define XPLANEUDPDECODER_H_INCLUDED
+
+#include "XPlaneUdp.h"
+#include "XPlaneUdpDATA.h"
+#include "XPlaneUdpDATAMap.h"
+
+class XPlaneUdpDecoder {
+public:
+
+    //XPlaneUdpDecoder();
+    
+    void setDebug(bool debug);
+    void setReverseByteOrder(bool reverseByteOrder);
+
+    /**
+     * This must be called prior to other methods such as putIntoDATAMap().
+     * Point this decoder to the UDP data buffer, and read the first four bytes
+     * to determine the X-Plane UDP message type (e.g. DATA).
+     */
+    XPlaneUdpMessageType setUdpBuffer(char * buf, int bufLen);
+    
+    /**
+     * Note: The setUdpBuffer() method must be called prior to calling this method.
+     * Decode the UDP packet. If it is a DATA packet from X-Plane, then populate
+     * XPlaneUdpDATA structure(s) in the map.
+     * If filter==true, then only existing structures in the map (keyed by message DATA msg index)
+     * will be populated, i.e. no new structures will be created in the map.
+     * Return the number of XPlaneUdpDATA structures populated/updated in the map.
+     */
+    int putIntoDATAMap(XPlaneUdpDATAMap & DATAMap, bool filter);
+
+private:
+
+    bool _debug;
+    bool _reverseByteOrder;
+    char *_udpBuffer;
+    int _udpBufferLen;
+    XPlaneUdpMessageType _udpPacketMsgType;
+};
+
+
+#endif // XPLANEUDPDECODER_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpEncoder.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <string.h>
+#include "XPlaneUdp.h"
+#include "XPlaneUdpEncoder.h"
+#include "XPlaneUdpDATA.h"
+
+void XPlaneUdpEncoder::setDebug(bool debug) {
+    _debug = debug;
+}
+
+int XPlaneUdpEncoder::encodeFromDATAMap(char * udpBuffer, int maxBufferLen, XPlaneUdpDATAMap & DATAMap, bool changedDataOnly) {
+    if (maxBufferLen < 5  ||  DATAMap.size() == 0) {
+        return 0;
+    }
+
+    strcpy(udpBuffer, "DATA");
+    int bufOffset = 5;
+
+    for (XPlaneUdpDATAMap::iterator it = DATAMap.begin(); it != DATAMap.end(); it++) {
+        if ((bufOffset + XPLANE_DATA_MSG_LENGTH) > maxBufferLen) {
+            break;
+        }
+        else {
+            XPlaneUdpDATA * DATAmsg = it->second;
+            if ((!changedDataOnly) || DATAmsg->isDataChanged()) {
+                DATAmsg->populateDATAMessage(udpBuffer + bufOffset);
+                bufOffset += XPLANE_DATA_MSG_LENGTH;
+            }
+        }
+    }
+
+    if (_debug) printf("encodeFromDATAMap(): Encoded %d bytes into UDP buffer \n", bufOffset);
+
+    return bufOffset;
+}
+
+
+int XPlaneUdpEncoder::encodeDataRef(char * udpBuffer, int maxBufferLen, XPlaneUdpDREF & dref) {
+    if (maxBufferLen < DATAREF_PACKET_SIZE) {
+        return 0;
+    }
+
+    memset(udpBuffer, '\0' , DATAREF_PACKET_SIZE);      // zero out the buffer
+    
+    strcpy(udpBuffer, "DREF");
+    int bufOffset = 5;
+
+    dref.populateDREFMessage(udpBuffer + bufOffset);
+
+    if (_debug) {
+        printf("encodeDataRef(): Encoded DREF packet into UDP buffer: ");
+        dref.toString(stdout);
+        printf("\n");
+    }
+
+    return DATAREF_PACKET_SIZE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdpEncoder.h	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,41 @@
+#ifndef XPLANEUDPENCODER_H_INCLUDED
+#define XPLANEUDPENCODER_H_INCLUDED
+
+#include "XPlaneUdp.h"
+#include "XPlaneUdpDATA.h"
+#include "XPlaneUdpDATAMap.h"
+#include "XPlaneUdpDREF.h"
+
+#define DATAREF_PACKET_SIZE 509    // "DREF" + '\x00' + 4-byte_value + 500-char_name
+
+class XPlaneUdpEncoder {
+public:
+
+    //XPlaneUdpEncoder();
+    
+    void setDebug(bool debug);
+
+    /**
+     * Encode a DATA packet from the messages in the given XPlaneUdpDATAMap.
+     * The encoded data will be placed at the location given by udpBuffer.
+     * If changedDataOnly is true, then only XPlaneUdpDATA messages with dataChanged will be encoded.
+     * Return number of bytes encoded into udpBuffer.
+     * Return -1 if there's more data in the map than what will fit in udpBuffer (> maxBufferLen).
+     */
+    int encodeFromDATAMap(char * udpBuffer, int maxBufferLen, XPlaneUdpDATAMap & DATAMap, bool changedDataOnly);
+
+    /**
+     * Encode a DataRef (DREF) packet, using the given DataRef.
+     * The encoded data will be placed at the location given by udpBuffer.
+     * Return number of bytes encoded into udpBuffer.
+     * Return -1 if there's more data in the map than what will fit in udpBuffer (> maxBufferLen).
+     */
+    int encodeDataRef(char * udpBuffer, int maxBufferLen, XPlaneUdpDREF & dref);
+
+private:
+
+    bool _debug;
+};
+
+
+#endif // XPLANEUDPENCODER_H_INCLUDED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XPlaneUdp/XPlaneUdp_util.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,11 @@
+
+void reverse4Bytes(char* bytes)
+{
+    char temp;
+    temp = bytes[0];
+    bytes[0] = bytes[3];
+    bytes[3] = temp;
+    temp = bytes[1];
+    bytes[1] = bytes[2];
+    bytes[2] = temp;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,74 @@
+#include "mbed.h"
+#include "EthernetNetIf.h"
+#include "Ranger.h"
+#include "XPlaneIO.h"
+
+Serial pc(USBTX, USBRX);
+
+int main() {
+
+    pc.baud(57600);
+    
+    Ethernet ethernet;
+
+    XPlaneIO xpio;
+    if (! xpio.setup("XPlaneIO.cfg", &ethernet)) {
+        printf("Config/setup failed \n");
+        return -1;
+    }
+    
+    // Check if user wants to perform interactive XPlaneIO diagnostics.
+    xpio.diagnostics(pc);
+    
+    AnalogOut throttleActual(p18);
+    xpio.addDATAToReceive(26);  // "throttle actual" in msg 26, data[0]
+
+    AnalogIn hatSwitch(p16);
+    XPlaneUdpDREF pilotHeadHeading("sim/graphics/view/pilots_head_psi", xpio.reverseByteOrder());
+    Ranger<float> hatSwitchInputRanger(4,  0.59, 0.68, 0.78, 0.92);
+    float hatSwitchDegreesByRange[5] = {0, -90, 180, 90, 0};
+
+    Timer timerThrottle;
+    timerThrottle.start();
+
+    Timer timerHatSwitch;
+    timerHatSwitch.start();
+
+    Timer timerDebugPrint;
+    if (xpio.debug()) {
+        timerDebugPrint.start();
+    }
+
+    xpio.startSendingUdp();
+
+    while (1) {
+        Net::poll();
+        
+        if (timerThrottle.read_ms() > 500) {
+            timerThrottle.reset();
+            
+            throttleActual.write(xpio.getReceiveDATAValue(26, 0));     // throttle actual
+        }
+
+        if (timerHatSwitch.read_ms() > 250) {
+            timerHatSwitch.reset();
+            
+            int newRange = hatSwitchInputRanger.range(hatSwitch.read());
+            if (hatSwitchInputRanger.rangeChanged()) {
+                pilotHeadHeading.drefFloat(hatSwitchDegreesByRange[newRange]);
+                xpio.sendDREF(pilotHeadHeading);
+            }
+        }
+
+        if (xpio.debug()) {
+            if (timerDebugPrint.read() > 2) {
+                timerDebugPrint.reset();
+                
+                printf("Receive ");
+                printXPlaneUdpDATAMap(stdout, xpio.recvDATAMap());
+                printf("Send ");
+                printXPlaneUdpDATAMap(stdout, xpio.sendDATAMap());
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Dec 21 22:29:59 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/63bcd7ba4912