diff --git a/C/monitorExample/main.c b/C/monitorExample/main.c new file mode 100644 index 00000000..b9f7c439 --- /dev/null +++ b/C/monitorExample/main.c @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +// National Aeronautics and Space Administration. All Rights Reserved. +// +// DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "../src/xplaneConnect.h" +#include +#include "stdio.h" + +#ifdef WIN32 +HANDLE hStdIn = NULL; +INPUT_RECORD buffer; +int waitForInput() +{ + if (hStdIn == NULL) + { + hStdIn = GetStdHandle(STD_INPUT_HANDLE); + } + FlushConsoleInputBuffer(hStdIn); + DWORD result = WaitForSingleObject(hStdIn, 100); + if (result == WAIT_OBJECT_0) + { + DWORD eventsRead; + PeekConsoleInput(hStdIn, &buffer, 1, &eventsRead); + if (eventsRead > 0) + { + return buffer.EventType == KEY_EVENT; + } + } + return FALSE; +} +#else +int fdstdin = 0; +fd_set fds; +struct timeval tv; + +int waitForInput() +{ + + FD_ZERO(&fds); + FD_SET(fdstdin, &fds); + select(1, &fds, NULL, NULL, &tv); + return FD_ISSET(fdstdin, &fds); +} +#endif + +int main(void) +{ + XPCSocket client = openUDP("127.0.0.1"); + const int aircraftNum = 0; + tv.tv_usec = 100 * 1000; + while (1) + { + float posi[7]; + int result = getPOSI(client, posi, aircraftNum); + if (result < 0) // Error in getPOSI + { + break; + } + + float ctrl[7]; + result = getCTRL(client, ctrl, aircraftNum); + if (result < 0) // Error in getCTRL + { + break; + } + + printf("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n", + posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]); + + // Check if any key has been pressed and break + if (waitForInput()) + { + break; + } + } + + + printf("\n\nPress Any Key to exit..."); + getchar(); + return 0; +} \ No newline at end of file diff --git a/C/monitorExample/monitorExample.sln b/C/monitorExample/monitorExample.sln new file mode 100644 index 00000000..11435f79 --- /dev/null +++ b/C/monitorExample/monitorExample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitorExample", "monitorExample.vcxproj", "{0F45204B-6910-46C7-BECD-273985390F75}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0F45204B-6910-46C7-BECD-273985390F75}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F45204B-6910-46C7-BECD-273985390F75}.Debug|Win32.Build.0 = Debug|Win32 + {0F45204B-6910-46C7-BECD-273985390F75}.Release|Win32.ActiveCfg = Release|Win32 + {0F45204B-6910-46C7-BECD-273985390F75}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/C/monitorExample/monitorExample.vcxproj b/C/monitorExample/monitorExample.vcxproj new file mode 100644 index 00000000..3be8e308 --- /dev/null +++ b/C/monitorExample/monitorExample.vcxproj @@ -0,0 +1,79 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {0F45204B-6910-46C7-BECD-273985390F75} + monitorExample + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + ../src;$(IncludePath) + + + ../src;$(IncludePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + + + true + true + true + + + + + + + + + + \ No newline at end of file diff --git a/C/monitorExample/monitorExample.vcxproj.filters b/C/monitorExample/monitorExample.vcxproj.filters new file mode 100644 index 00000000..50a53c3e --- /dev/null +++ b/C/monitorExample/monitorExample.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/C/playbackExample/src/chrome.c b/C/playbackExample/src/chrome.c new file mode 100644 index 00000000..652de969 --- /dev/null +++ b/C/playbackExample/src/chrome.c @@ -0,0 +1,71 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "chrome.h" + +#include + +void displayStart(char* version) +{ + printf("X-Plane Connect Playback Example [Version %s]\n", version); + printf("(c) 2013-2015 United States Government as represented by the Administrator\n"); + printf("of the National Aeronautics and Space Administration. All Rights Reserved.\n"); +} + +int displayMenu(char* title, char* opts[], size_t count) +{ + printf("\n"); + printf("+---------------------------------------------- +\n"); + printf("| %-42s |\n", title); + printf("+---------------------------------------------- +\n"); + for (size_t i = 0; i < count; i++) + { + printf("| %2u. %-40s |\n", i + 1, opts[i]); + } + printf("+---------------------------------------------- +\n"); + printf("Please select an option: "); + + int opt; + scanf("%d", &opt); + return opt; +} + +void displayMsg(char* msg) +{ + printf("%s\n", msg); +} + +void getString(char* prompt, char buffer[255]) +{ + printf("%s: ", prompt); + scanf("%255s", buffer); +} + +int getInt(char* prompt) +{ + printf("%s: ", prompt); + int result; + scanf("%d", &result); + return result; +} \ No newline at end of file diff --git a/C/playbackExample/src/chrome.h b/C/playbackExample/src/chrome.h new file mode 100644 index 00000000..d0da842c --- /dev/null +++ b/C/playbackExample/src/chrome.h @@ -0,0 +1,40 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#ifndef XPC_PLAYBACKEX_CHROME_H_ +#define XPC_PLAYBACKEX_CHROME_H_ + +#include + +void displayStart(char* version); + +int displayMenu(char* title, char* opts[], size_t count); + +void displayMsg(char* msg); + +void getString(char* prompt, char buffer[255]); + +int getInt(char* prompt); + +#endif \ No newline at end of file diff --git a/C/playbackExample/src/main.c b/C/playbackExample/src/main.c new file mode 100644 index 00000000..c796eddd --- /dev/null +++ b/C/playbackExample/src/main.c @@ -0,0 +1,70 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "chrome.h" +#include "playback.h" + +int main(void) +{ + char* mainOpts[3] = + { + "Record X-Plane", + "Playback File", + "Exit" + }; + char path[256] = { 0 }; + + displayStart("1.2.0.0"); + while (1) + { + switch (displayMenu("What would you like to do?", mainOpts, 3)) + { + case 1: + { + getString("Enter save file path", path); + int interval = getInt("Enter interval between frames (milliseconds)"); + int duration = getInt("Enter duration to record for (seconds)"); + + record(path, interval, duration); + break; + } + case 2: + { + getString("Enter path to saved playback file", path); + int interval = getInt("Enter interval between frames (milliseconds)"); + + playback(path, interval); + break; + } + case 3: + displayMsg("Exiting."); + return 0; + default: + displayMsg("Unrecognized option."); + break; + } + } + + return 0; +} \ No newline at end of file diff --git a/C/playbackExample/src/playback.c b/C/playbackExample/src/playback.c new file mode 100644 index 00000000..75ad5235 --- /dev/null +++ b/C/playbackExample/src/playback.c @@ -0,0 +1,106 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#include "playback.h" + +#include "chrome.h" + +#include "xplaneConnect.h" + +#include +#include + +#ifdef WIN32 +#include +#else +#include +#endif + +void playbackSleep(int ms) +{ +#ifdef WIN32 + Sleep(ms); +#else + usleep(ms * 1000); +#endif +} + +void record(char* path, int interval, int duration) +{ + FILE* fd = fopen(path, "w"); + int count = duration * 1000 / interval; + if (!fd) + { + displayMsg("Unable to open output file."); + return; + } + if (count < 1) + { + displayMsg("Duration is less than one iteration."); + return; + } + displayMsg("Recording..."); + + XPCSocket sock = openUDP("127.0.0.1"); + for (int i = 0; i < count; ++i) + { + float posi[7]; + int result = getPOSI(sock, posi, 0); + playbackSleep(interval); + if (result < 0) + { + continue; + } + fprintf(fd, "%f, %f, %f, %f, %f, %f, %f\n", posi[0], posi[1], posi[2], posi[3], posi[4], posi[5], posi[6]); + } + closeUDP(sock); + displayMsg("Recording Complete"); +} + +void playback(char* path, int interval) +{ + FILE* fd = fopen(path, "r"); + if (!fd) + { + displayMsg("Unable to open output file."); + return; + } + displayMsg("Starting Playback..."); + + XPCSocket sock = openUDP("127.0.0.1"); + float posi[7]; + while (!feof(fd) && !ferror(fd)) + { + int result = fscanf(fd, "%f, %f, %f, %f, %f, %f, %f\n", + &posi[0], &posi[1], &posi[2], &posi[3], &posi[4], &posi[5], &posi[6]); + playbackSleep(interval); + if (result != 7) + { + continue; + } + sendPOSI(sock, posi, 7, 0); + } + closeUDP(sock); + displayMsg("Playback Complete"); +} \ No newline at end of file diff --git a/C/playbackExample/src/playback.h b/C/playbackExample/src/playback.h new file mode 100644 index 00000000..e54a3b11 --- /dev/null +++ b/C/playbackExample/src/playback.h @@ -0,0 +1,32 @@ +//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the +//National Aeronautics and Space Administration. All Rights Reserved. +// +//DISCLAIMERS +// No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND, +// EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY THAT +// THE SUBJECT SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY +// THAT THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT DOCUMENTATION, IF PROVIDED, +// WILL CONFORM TO THE SUBJECT SOFTWARE. THIS AGREEMENT DOES NOT, IN ANY MANNER, CONSTITUTE AN +// ENDORSEMENT BY GOVERNMENT AGENCY OR ANY PRIOR RECIPIENT OF ANY RESULTS, RESULTING DESIGNS, +// HARDWARE, SOFTWARE PRODUCTS OR ANY OTHER APPLICATIONS RESULTING FROM USE OF THE SUBJECT +// SOFTWARE. FURTHER, GOVERNMENT AGENCY DISCLAIMS ALL WARRANTIES AND LIABILITIES REGARDING +// THIRD-PARTY SOFTWARE, IF PRESENT IN THE ORIGINAL SOFTWARE, AND DISTRIBUTES IT "AS IS." +// +// Waiver and Indemnity: RECIPIENT AGREES TO WAIVE ANY AND ALL CLAIMS AGAINST THE UNITED STATES +// GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT. IF +// RECIPIENT'S USE OF THE SUBJECT SOFTWARE RESULTS IN ANY LIABILITIES, DEMANDS, DAMAGES, EXPENSES +// OR LOSSES ARISING FROM SUCH USE, INCLUDING ANY DAMAGES FROM PRODUCTS BASED ON, OR RESULTING +// FROM, RECIPIENT'S USE OF THE SUBJECT SOFTWARE, RECIPIENT SHALL INDEMNIFY AND HOLD HARMLESS THE +// UNITED STATES GOVERNMENT, ITS CONTRACTORS AND SUBCONTRACTORS, AS WELL AS ANY PRIOR RECIPIENT, +// TO THE EXTENT PERMITTED BY LAW. RECIPIENT'S SOLE REMEDY FOR ANY SUCH MATTER SHALL BE THE +// IMMEDIATE, UNILATERAL TERMINATION OF THIS AGREEMENT. + +#ifndef XPC_PLAYBACKEX_PLAYBACK_H_ +#define XPC_PLAYBACKEX_PLAYBACK_H_ + +void record(char* path, int interval, int duration); + +void playback(char* path, int interval); + +#endif \ No newline at end of file diff --git a/C/playbackExample/win/playbackExample.sln b/C/playbackExample/win/playbackExample.sln new file mode 100644 index 00000000..18a4c814 --- /dev/null +++ b/C/playbackExample/win/playbackExample.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "playbackExample", "playbackExample.vcxproj", "{40781C32-3EE6-4B3C-A94F-E128BD41E21C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Debug|Win32.ActiveCfg = Debug|Win32 + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Debug|Win32.Build.0 = Debug|Win32 + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Release|Win32.ActiveCfg = Release|Win32 + {40781C32-3EE6-4B3C-A94F-E128BD41E21C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/C/playbackExample/win/playbackExample.vcxproj b/C/playbackExample/win/playbackExample.vcxproj new file mode 100644 index 00000000..a8adf8ab --- /dev/null +++ b/C/playbackExample/win/playbackExample.vcxproj @@ -0,0 +1,89 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {40781C32-3EE6-4B3C-A94F-E128BD41E21C} + playbackExample + + + + Application + true + v120 + MultiByte + + + Application + false + v120 + true + MultiByte + + + + + + + + + + + + + ../../src;../src;$(IncludePath) + ../../src;../src;$(SourcePath) + + + ../../src;../src;$(IncludePath) + ../../src;../src;$(SourcePath) + + + + Level3 + Disabled + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + + + + + Level3 + MaxSpeed + true + true + true + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/C/playbackExample/win/playbackExample.vcxproj.filters b/C/playbackExample/win/playbackExample.vcxproj.filters new file mode 100644 index 00000000..6a83f74f --- /dev/null +++ b/C/playbackExample/win/playbackExample.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index e4e633ef..24fb56e5 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -116,7 +116,7 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port) #else struct timeval timeout; timeout.tv_sec = 0; - timeout.tv_usec = 100000; + timeout.tv_usec = 250000; #endif if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0) { @@ -521,6 +521,38 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char /*****************************************************************************/ /**** POSI functions ****/ /*****************************************************************************/ +int getPOSI(XPCSocket sock, float values[7], char ac) +{ + // Setup send command + unsigned char buffer[6] = "GETP"; + buffer[5] = ac; + + // Send command + if (sendUDP(sock, buffer, 6) < 0) + { + printError("getPOSI", "Failed to send command."); + return -1; + } + + // Get response + unsigned char readBuffer[34]; + int readResult = readUDP(sock, readBuffer, 34); + if (readResult < 0) + { + printError("getPOSI", "Failed to read response."); + return -2; + } + if (readResult != 34) + { + printError("getPOSI", "Unexpected response length."); + return -3; + } + + // Copy response into values + memcpy(values, readBuffer + 6, 7 * sizeof(float)); + return 0; +} + int sendPOSI(XPCSocket sock, float values[], int size, char ac) { // Validate input @@ -565,6 +597,41 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac) /*****************************************************************************/ /**** CTRL functions ****/ /*****************************************************************************/ +int getCTRL(XPCSocket sock, float values[7], char ac) +{ + // Setup send command + unsigned char buffer[6] = "GETC"; + buffer[5] = ac; + + // Send command + if (sendUDP(sock, buffer, 6) < 0) + { + printError("getCTRL", "Failed to send command."); + return -1; + } + + // Get response + unsigned char readBuffer[31]; + int readResult = readUDP(sock, readBuffer, 31); + if (readResult < 0) + { + printError("getCTRL", "Failed to read response."); + return -2; + } + if (readResult != 31) + { + printError("getCTRL", "Unexpected response length."); + return -3; + } + + // Copy response into values + memcpy(values, readBuffer + 5, 4 * sizeof(float)); + values[4] = readBuffer[21]; + values[5] = *((float*)(readBuffer + 22)); + values[6] = *((float*)(readBuffer + 27)); + return 0; +} + int sendCTRL(XPCSocket sock, float values[], int size, char ac) { // Validate input diff --git a/C/src/xplaneConnect.h b/C/src/xplaneConnect.h index 3827a685..161178ad 100644 --- a/C/src/xplaneConnect.h +++ b/C/src/xplaneConnect.h @@ -196,6 +196,14 @@ int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char // Position +/// Gets the position and orientation of the specified aircraft. +/// +/// \param sock The socket used to send the command and receive the response. +/// \param values An array to store the position information returned by the +/// plugin. The format of values is [Lat, Lon, Alt, Pitch, Roll, Yaw, Gear] +/// \returns 0 if successful, otherwise a negative value. +int getPOSI(XPCSocket sock, float values[7], char ac); + /// Sets the position and orientation of the specified aircraft. /// /// \param sock The socket to use to send the command. @@ -209,6 +217,16 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac); // Controls +/// Gets the control surface information for the specified aircraft. +/// +/// \param sock The socket used to send the command and receive the response. +/// \param values An array to store the position information returned by the +/// plugin. The format of values is [Elevator, Aileron, Rudder, +/// Throttle, Gear, Flaps, Speed Brakes] +/// \param ac The aircraft to set the control surfaces of. 0 is the main/player aircraft. +/// \returns 0 if successful, otherwise a negative value. +int getCTRL(XPCSocket sock, float values[7], char ac); + /// Sets the control surfaces of the specified aircraft. /// /// \param sock The socket to use to send the command. diff --git a/Java/Examples/ContinuousOperation/.idea/.name b/Java/Examples/ContinuousOperation/.idea/.name new file mode 100644 index 00000000..9ac7aa61 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/.name @@ -0,0 +1 @@ +ContinuousOperation \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/compiler.xml b/Java/Examples/ContinuousOperation/.idea/compiler.xml new file mode 100644 index 00000000..96cc43ef --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/copyright/profiles_settings.xml b/Java/Examples/ContinuousOperation/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000..e7bedf33 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/libraries/XPlaneConnect.xml b/Java/Examples/ContinuousOperation/.idea/libraries/XPlaneConnect.xml new file mode 100644 index 00000000..da48fd95 --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/libraries/XPlaneConnect.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/misc.xml b/Java/Examples/ContinuousOperation/.idea/misc.xml new file mode 100644 index 00000000..a61f7bca --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/misc.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/modules.xml b/Java/Examples/ContinuousOperation/.idea/modules.xml new file mode 100644 index 00000000..0a1c2fec --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/.idea/vcs.xml b/Java/Examples/ContinuousOperation/.idea/vcs.xml new file mode 100644 index 00000000..6564d52d --- /dev/null +++ b/Java/Examples/ContinuousOperation/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/ContinuousOperation.iml b/Java/Examples/ContinuousOperation/ContinuousOperation.iml new file mode 100644 index 00000000..6c4f0abc --- /dev/null +++ b/Java/Examples/ContinuousOperation/ContinuousOperation.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/ContinuousOperation/libs/XPlaneConnect.jar b/Java/Examples/ContinuousOperation/libs/XPlaneConnect.jar new file mode 100644 index 00000000..eba5283b Binary files /dev/null and b/Java/Examples/ContinuousOperation/libs/XPlaneConnect.jar differ diff --git a/Java/Examples/ContinuousOperation/src/Main.java b/Java/Examples/ContinuousOperation/src/Main.java new file mode 100644 index 00000000..61a14eb5 --- /dev/null +++ b/Java/Examples/ContinuousOperation/src/Main.java @@ -0,0 +1,47 @@ +package gov.nasa.xpc.ex; + +import gov.nasa.xpc.XPlaneConnect; + +import java.io.IOException; + +/** + * An example program demonstrating the use of X-PlaneConnect in a continuous loop. + * + * @author Jason Watkins + * @version 1.0 + * @since 2015-06-19 + */ +public class Main +{ + public static void main(String[] args) + { + try (XPlaneConnect xpc = new XPlaneConnect()) + { + int aircraft = 0; + while(true) + { + float[] posi = xpc.getPOSI(aircraft); + float[] ctrl = xpc.getCTRL(aircraft); + + System.out.format("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n", + posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]); + + try + { + Thread.sleep(100); + } + catch (InterruptedException ex) {} + + if(System.in.available() > 0) + { + break; + } + } + } + catch(IOException ex) + { + System.out.println("Error:"); + System.out.println(ex.getMessage()); + } + } +} diff --git a/Java/Examples/Playback/.idea/compiler.xml b/Java/Examples/Playback/.idea/compiler.xml new file mode 100644 index 00000000..96cc43ef --- /dev/null +++ b/Java/Examples/Playback/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/copyright/profiles_settings.xml b/Java/Examples/Playback/.idea/copyright/profiles_settings.xml new file mode 100644 index 00000000..e7bedf33 --- /dev/null +++ b/Java/Examples/Playback/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/description.html b/Java/Examples/Playback/.idea/description.html new file mode 100644 index 00000000..db5f1295 --- /dev/null +++ b/Java/Examples/Playback/.idea/description.html @@ -0,0 +1 @@ +Simple Java application that includes a class with main() method \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/libraries/XPlaneConnect.xml b/Java/Examples/Playback/.idea/libraries/XPlaneConnect.xml new file mode 100644 index 00000000..f0cda694 --- /dev/null +++ b/Java/Examples/Playback/.idea/libraries/XPlaneConnect.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/misc.xml b/Java/Examples/Playback/.idea/misc.xml new file mode 100644 index 00000000..5a65e9a6 --- /dev/null +++ b/Java/Examples/Playback/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/modules.xml b/Java/Examples/Playback/.idea/modules.xml new file mode 100644 index 00000000..7ba8a90f --- /dev/null +++ b/Java/Examples/Playback/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/project-template.xml b/Java/Examples/Playback/.idea/project-template.xml new file mode 100644 index 00000000..1f08b887 --- /dev/null +++ b/Java/Examples/Playback/.idea/project-template.xml @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/Java/Examples/Playback/.idea/vcs.xml b/Java/Examples/Playback/.idea/vcs.xml new file mode 100644 index 00000000..6564d52d --- /dev/null +++ b/Java/Examples/Playback/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/Playback.iml b/Java/Examples/Playback/Playback.iml new file mode 100644 index 00000000..6c4f0abc --- /dev/null +++ b/Java/Examples/Playback/Playback.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Java/Examples/Playback/src/gov/nasa/xpc/Main.java b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java new file mode 100644 index 00000000..0e0b3761 --- /dev/null +++ b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java @@ -0,0 +1,147 @@ +package gov.nasa.xpc; + +import com.sun.javafx.scene.EnteredExitedHandler; + +import java.io.*; +import java.util.Scanner; + +public class Main +{ + + public static void main(String[] args) throws IOException + { + String[] mainOpts = new String[] { "Record X-Plane", "Playback File", "Exit" }; + + System.out.println("X-Plane Connect Playback Example [Version 1.2.0.0]"); + System.out.println("(c) 2013-2015 United States Government as represented by the Administrator"); + System.out.println("of the National Aeronautics and Space Administration. All Rights Reserved."); + while(true) + { + int result = displayMenu("What would you like to do?", mainOpts); + switch(result) + { + case 1: + { + String path = getString("Enter a save file path: "); + int interval = getInt("Enter interval between frames (milliseconds): "); + int duration = getInt("Enter duration to record for (seconds): "); + + record(path, interval, duration); + break; + } + case 2: + { + String path = getString("Enter path to saved playback file: "); + int interval = getInt("Enter interval between frames (milliseconds): "); + + playback(path, interval); + break; + } + case 3: + { + System.out.println("Exiting."); + return; + } + default: + { + System.out.println("Unrecognized menu option."); + break; + } + } + } + } + + private static int displayMenu(String title, String[] opts) throws IOException + { + System.out.println(); + System.out.println("+---------------------------------------------- +"); + System.out.println(String.format("| %1$-42s |", title)); + System.out.println("+---------------------------------------------- +"); + for(int i = 0; i < opts.length; ++i) + { + System.out.println(String.format("| %1$2d. %2$-40s |", i + 1, opts[i])); + + } + System.out.println("+---------------------------------------------- +"); + return getInt("Please select an option: "); + } + + private static String getString(String prompt) throws IOException + { + System.out.print(prompt); + + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + return reader.readLine(); + } + + private static int getInt(String prompt) throws IOException + { + System.out.print(prompt); + + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + String result = reader.readLine(); + + return Integer.parseInt(result); + } + + public static void record(String path, int interval, int duration) throws IOException + { + int count = duration * 1000 / interval; + if (count < 1) + { + throw new IllegalArgumentException("duration is less than one interval."); + } + + try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) + { + System.out.println("Recording..."); + try (XPlaneConnect xpc = new XPlaneConnect()) + { + for (int i = 0; i < count; ++i) + { + float[] posi = xpc.getPOSI(0); + writer.write(String.format("%1$f, %2$f, %3$f, %4$f, %5$f, %6$f, %7$f\n", + posi[0], posi[1], posi[2], posi[3], posi[4], posi[5], posi[6])); + try + { + Thread.sleep(interval); + } + catch (InterruptedException ex) + { + } + } + } + System.out.println("Recording Complete"); + } + } + + public static void playback(String path, int interval) throws IOException + { + try(Scanner reader = new Scanner(new FileReader(path))) + { + reader.useDelimiter("[\\s\\r\\n,]+"); + System.out.println("Starting playback..."); + try (XPlaneConnect xpc = new XPlaneConnect()) + { + while(reader.hasNextLine()) + { + float[] posi = new float[7]; + for (int i = 0; i < 7; ++i) + { + String s = reader.next(); + posi[i] = Float.parseFloat(s); + } + reader.nextLine(); + xpc.sendPOSI(posi); + try + { + Thread.sleep(interval); + } + catch (InterruptedException ex) + { + } + } + } + } + } +} diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java index b40b048a..f98816ee 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -398,6 +398,47 @@ public void sendDREFs(String[] drefs, float[][] values) throws IOException sendUDP(os.toByteArray()); } + /** + * Gets the control surface information for the specified airplane. + * + * @param ac The aircraft to get control surface information for. + * @return An array containing control surface data in the same format as {@code sendCTRL}. + * @throws IOException If the command cannot be sent or a response cannot be read. + */ + public float[] getCTRL(int ac) throws IOException + { + // Send request + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("GETC".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + os.write(ac); + sendUDP(os.toByteArray()); + + // Read response + byte[] data = readUDP(); + if(data.length == 0) + { + throw new IOException("No response received."); + } + if(data.length < 31) + { + throw new IOException("Response too short"); + } + + // Parse response + float[] result = new float[7]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.order(ByteOrder.LITTLE_ENDIAN); + result[0] = bb.getFloat(5); + result[1] = bb.getFloat(9); + result[2] = bb.getFloat(13); + result[3] = bb.getFloat(17); + result[4] = bb.get(21); + result[5] = bb.getFloat(22); + result[6] = bb.getFloat(27); + return result; + } + /** * Sends command to X-Plane setting control surfaces on the player ac. * @@ -409,6 +450,7 @@ public void sendDREFs(String[] drefs, float[][] values) throws IOException *
  • Throttle [-1, 1]
  • *
  • Gear (0=up, 1=down)
  • *
  • Flaps [0, 1]
  • + *
  • Speedbrakes [-0.5, 1.5]
  • * *

    * If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To @@ -433,6 +475,7 @@ public void sendCTRL(float[] values) throws IOException *

  • Throttle [-1, 1]
  • *
  • Gear (0=up, 1=down)
  • *
  • Flaps [0, 1]
  • + *
  • Speedbrakes [-0.5, 1.5]
  • * *

    * If @{code ctrl} is less than 6 elements long, The missing elements will not be changed. To @@ -499,6 +542,44 @@ else if (i >= values.length) sendUDP(os.toByteArray()); } + /** + * Gets position information for the specified airplane. + * + * @param ac The aircraft to get position information for. + * @return An array containing control surface data in the same format as {@code sendPOSI}. + * @throws IOException If the command cannot be sent or a response cannot be read. + */ + public float[] getPOSI(int ac) throws IOException + { + // Send request + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write("GETP".getBytes(StandardCharsets.UTF_8)); + os.write(0xFF); //Placeholder for message length + os.write(ac); + sendUDP(os.toByteArray()); + + // Read response + byte[] data = readUDP(); + if(data.length == 0) + { + throw new IOException("No response received."); + } + if(data.length < 34) + { + throw new IOException("Response too short"); + } + + // Parse response + float[] result = new float[7]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.order(ByteOrder.LITTLE_ENDIAN); + for(int i = 0; i < 7; ++i) + { + result[i] = bb.getFloat(6 + 4 * i); + } + return result; + } + /** * Sets the position of the player ac. * diff --git a/MATLAB/+XPlaneConnect/getCTRL.m b/MATLAB/+XPlaneConnect/getCTRL.m new file mode 100644 index 00000000..820afcac --- /dev/null +++ b/MATLAB/+XPlaneConnect/getCTRL.m @@ -0,0 +1,29 @@ +function ctrl = getCTRL(ac, socket) +% getCTRL Gets control surface information for the specified aircraft +% +% Inputs +% ac: The aircraft number to get control surface data for. +% Outputs +% posi: An array of values matching the the format used by sendCTRL + + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[getCTRL] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input +ac = int32(ac); + +%% Send command +ctrl = socket.getCTRL(ac); + +end \ No newline at end of file diff --git a/MATLAB/+XPlaneConnect/getPOSI.m b/MATLAB/+XPlaneConnect/getPOSI.m new file mode 100644 index 00000000..35b8d5a5 --- /dev/null +++ b/MATLAB/+XPlaneConnect/getPOSI.m @@ -0,0 +1,29 @@ +function posi = getPOSI(ac, socket) +% getPOSI Gets position information for the specified aircraft +% +% Inputs +% ac: The aircraft number to get position data for. +% Outputs +% posi: An array of values matching the the format used by sendPOSI + + +import XPlaneConnect.* + +%% Get client +global clients; +if ~exist('socket', 'var') + assert(isequal(length(clients) < 2, 1), '[getPOSI] ERROR: Multiple clients open. You must specify which client to use.'); + if isempty(clients) + socket = openUDP(); + else + socket = clients(1); + end +end + +%% Validate input +ac = int32(ac); + +%% Send command +posi = socket.getPOSI(ac); + +end \ No newline at end of file diff --git a/MATLAB/MonitorExample/MonitorExample.m b/MATLAB/MonitorExample/MonitorExample.m new file mode 100644 index 00000000..ffbdc145 --- /dev/null +++ b/MATLAB/MonitorExample/MonitorExample.m @@ -0,0 +1,19 @@ +%% X-Plane Connect MATLAB Example Script +% This script demonstrates how to read and write data to the XPC plugin. +% Before running this script, ensure that the XPC plugin is installed and +% X-Plane is running. +%% Import XPC +clear all +addpath('../') +import XPlaneConnect.* + +Socket = openUDP(); +while 1 + posi = getPOSI(0, Socket); + ctrl = getCTRL(0, Socket); + + fprintf('Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n', ... + posi(1), posi(2), posi(3), ctrl(2), ctrl(1), ctrl(3)); + pause(0.1); +end +closeUDP(Socket); \ No newline at end of file diff --git a/MATLAB/PlaybackExample/MyRecording.txt b/MATLAB/PlaybackExample/MyRecording.txt new file mode 100644 index 00000000..60c546aa --- /dev/null +++ b/MATLAB/PlaybackExample/MyRecording.txt @@ -0,0 +1,100 @@ +37.184181, -120.793640, 3904.040771, 1.856917, 0.001644, 116.058609, 0.000000 +37.184143, -120.793541, 3903.825928, 2.172139, -0.034512, 115.875587, 0.000000 +37.184090, -120.793404, 3903.511719, 2.425995, -0.111958, 115.577911, 0.000000 +37.184040, -120.793282, 3903.230225, 2.497653, -0.205506, 115.289268, 0.000000 +37.183994, -120.793167, 3902.961914, 2.478850, -0.315117, 115.006279, 0.000000 +37.183949, -120.793053, 3902.683838, 2.412536, -0.447890, 114.716370, 0.000000 +37.183910, -120.792953, 3902.452881, 2.344441, -0.570747, 114.485756, 0.000000 +37.183857, -120.792824, 3902.137939, 2.253826, -0.752984, 114.194984, 0.000000 +37.183807, -120.792702, 3901.830566, 2.178489, -0.942979, 113.943779, 0.000000 +37.183765, -120.792595, 3901.561279, 2.125190, -1.116157, 113.754135, 0.000000 +37.183735, -120.792511, 3901.342773, 2.089507, -1.260052, 113.621834, 0.000000 +37.183693, -120.792404, 3901.058350, 2.051378, -1.449701, 113.479713, 0.000000 +37.183628, -120.792244, 3900.620361, 2.004393, -1.742637, 113.320374, 0.000000 +37.183571, -120.792107, 3900.232178, 1.969860, -1.999084, 113.234406, 0.000000 +37.183529, -120.791992, 3899.922852, 1.945357, -2.199157, 113.199463, 0.000000 +37.183487, -120.791885, 3899.607666, 1.922479, -2.397843, 113.190887, 0.000000 +37.183441, -120.791771, 3899.279297, 1.738008, -2.604320, 113.214371, 0.000000 +37.183395, -120.791649, 3898.942383, 1.172052, -2.819112, 113.280800, 0.000000 +37.183342, -120.791512, 3898.525879, 0.437769, -3.059577, 113.392952, 0.000000 +37.183292, -120.791389, 3898.116943, -0.110597, -3.260861, 113.510010, 0.000000 +37.183247, -120.791275, 3897.675293, -0.458939, -3.399007, 113.635101, 0.000000 +37.183220, -120.791206, 3897.409668, -0.495934, -3.425584, 113.706985, 0.000000 +37.183182, -120.791107, 3896.976318, -0.338542, -3.312644, 113.832870, 0.000000 +37.183128, -120.790962, 3896.313965, 0.088305, -2.812056, 114.069130, 0.000000 +37.183083, -120.790848, 3895.734863, 0.355302, -2.143086, 114.327766, 0.000000 +37.183033, -120.790718, 3895.112061, 0.345563, -1.171917, 114.668144, 0.000000 +37.182987, -120.790604, 3894.519043, 0.034761, -0.025642, 115.037415, 0.000000 +37.182941, -120.790489, 3893.926270, -0.549108, 1.265883, 115.421654, 0.000000 +37.182888, -120.790344, 3893.151611, -1.627703, 3.039703, 115.896240, 0.000000 +37.182831, -120.790199, 3892.313477, -2.879907, 4.868656, 116.325722, 0.000000 +37.182785, -120.790077, 3891.557129, -3.883288, 6.350969, 116.633751, 0.000000 +37.182743, -120.789970, 3890.766602, -4.752331, 7.729563, 116.888329, 0.000000 +37.182705, -120.789871, 3889.997314, -5.439554, 8.871016, 117.077042, 0.000000 +37.182659, -120.789749, 3888.965820, -6.164141, 10.014505, 117.239494, 0.000000 +37.182610, -120.789627, 3887.832031, -6.734039, 10.811956, 117.319382, 0.000000 +37.182560, -120.789505, 3886.558594, -7.134449, 11.245357, 117.313339, 0.000000 +37.182514, -120.789383, 3885.197510, -7.320403, 11.326477, 117.228592, 0.000000 +37.182465, -120.789261, 3883.700439, -7.264509, 11.086659, 117.071564, 0.000000 +37.182415, -120.789131, 3882.124023, -7.020517, 10.579235, 116.843575, 0.000000 +37.182365, -120.789009, 3880.477295, -6.698184, 9.864036, 116.538239, 0.000000 +37.182316, -120.788887, 3878.771973, -6.407937, 8.986709, 116.154274, 0.000000 +37.182266, -120.788765, 3877.028076, -6.217397, 7.992366, 115.705994, 0.000000 +37.182217, -120.788643, 3875.249023, -6.173934, 6.988136, 115.220467, 0.000000 +37.182167, -120.788521, 3873.432617, -6.285208, 6.050294, 114.729034, 0.000000 +37.182117, -120.788399, 3871.583496, -6.453388, 5.213163, 114.269440, 0.000000 +37.182068, -120.788269, 3869.689209, -6.484870, 4.480176, 113.873062, 0.000000 +37.182014, -120.788147, 3867.766357, -6.310386, 3.856612, 113.555290, 0.000000 +37.181965, -120.788025, 3865.820801, -6.021461, 3.327790, 113.311180, 0.000000 +37.181915, -120.787903, 3863.870361, -5.728759, 2.874094, 113.135651, 0.000000 +37.181866, -120.787781, 3861.910156, -5.480271, 2.475115, 113.025513, 0.000000 +37.181816, -120.787651, 3859.973145, -5.302026, 2.122235, 112.979225, 0.000000 +37.181767, -120.787529, 3858.042969, -5.182843, 1.808723, 112.993736, 0.000000 +37.181717, -120.787407, 3856.119629, -5.038956, 1.552066, 113.070557, 0.000000 +37.181667, -120.787285, 3854.215088, -4.828547, 1.368155, 113.206085, 0.000000 +37.181614, -120.787148, 3852.185059, -4.515563, 1.254129, 113.410904, 0.000000 +37.181557, -120.787010, 3850.030762, -4.132499, 1.210060, 113.681358, 0.000000 +37.181507, -120.786888, 3848.263916, -3.831450, 1.225745, 113.935280, 0.000000 +37.181458, -120.786766, 3846.475342, -3.528734, 1.284648, 114.214294, 0.000000 +37.181408, -120.786644, 3844.712158, -3.243377, 1.382594, 114.502975, 0.000000 +37.181358, -120.786522, 3842.987549, -2.982575, 1.515415, 114.790489, 0.000000 +37.181309, -120.786392, 3841.302246, -2.688687, 1.679037, 115.070442, 0.000000 +37.181259, -120.786270, 3839.659424, -2.305070, 1.869125, 115.338135, 0.000000 +37.181210, -120.786148, 3838.070068, -1.827937, 2.080883, 115.588036, 0.000000 +37.181156, -120.786018, 3836.515137, -1.326015, 2.315055, 115.817902, 0.000000 +37.181107, -120.785889, 3835.020508, -0.858468, 2.566000, 116.019691, 0.000000 +37.181053, -120.785767, 3833.605713, -0.446646, 2.827466, 116.188957, 0.000000 +37.181004, -120.785637, 3832.263672, -0.082390, 3.097450, 116.326546, 0.000000 +37.180950, -120.785515, 3830.996094, 0.248427, 3.372406, 116.433670, 0.000000 +37.180901, -120.785385, 3829.802734, 0.561189, 3.648955, 116.513596, 0.000000 +37.180847, -120.785263, 3828.686279, 0.901348, 3.909596, 116.570610, 0.000000 +37.180798, -120.785133, 3827.642090, 1.422296, 4.101938, 116.610832, 0.000000 +37.180744, -120.785011, 3826.675537, 2.124010, 4.216800, 116.636208, 0.000000 +37.180691, -120.784889, 3825.800049, 2.895841, 4.268193, 116.644135, 0.000000 +37.180641, -120.784767, 3825.024658, 3.647615, 4.264566, 116.632553, 0.000000 +37.180588, -120.784637, 3824.355469, 4.389325, 4.198403, 116.604164, 0.000000 +37.180538, -120.784523, 3823.797119, 5.116096, 4.063403, 116.559959, 0.000000 +37.180489, -120.784401, 3823.351562, 5.802851, 3.858843, 116.500069, 0.000000 +37.180435, -120.784279, 3823.021240, 6.434232, 3.583184, 116.425621, 0.000000 +37.180386, -120.784164, 3822.803223, 7.040646, 3.237821, 116.340439, 0.000000 +37.180336, -120.784042, 3822.697021, 7.629384, 2.839028, 116.249237, 0.000000 +37.180286, -120.783928, 3822.700195, 8.181519, 2.404881, 116.155312, 0.000000 +37.180244, -120.783821, 3822.798828, 8.596326, 1.981372, 116.066658, 0.000000 +37.180183, -120.783684, 3823.074219, 9.020256, 1.372200, 115.947289, 0.000000 +37.180126, -120.783554, 3823.453369, 9.331639, 0.798377, 115.847244, 0.000000 +37.180073, -120.783424, 3823.932129, 9.642418, 0.224538, 115.759720, 0.000000 +37.180019, -120.783302, 3824.521729, 9.962214, -0.365530, 115.680557, 0.000000 +37.179970, -120.783188, 3825.146240, 10.216949, -0.901261, 115.617035, 0.000000 +37.179928, -120.783081, 3825.777100, 10.420901, -1.378152, 115.566948, 0.000000 +37.179871, -120.782951, 3826.665283, 10.683296, -1.973540, 115.510689, 0.000000 +37.179817, -120.782829, 3827.599854, 10.932059, -2.526971, 115.463028, 0.000000 +37.179764, -120.782700, 3828.676758, 11.182608, -3.091355, 115.418022, 0.000000 +37.179710, -120.782570, 3829.791748, 11.405788, -3.608584, 115.379250, 0.000000 +37.179657, -120.782448, 3830.954102, 11.541166, -4.084742, 115.349564, 0.000000 +37.179604, -120.782326, 3832.230469, 11.431418, -4.526593, 115.343117, 0.000000 +37.179554, -120.782204, 3833.525391, 11.053594, -4.891589, 115.367775, 0.000000 +37.179501, -120.782082, 3834.855957, 10.596283, -5.199400, 115.407677, 0.000000 +37.179451, -120.781960, 3836.201660, 10.188977, -5.458726, 115.448441, 0.000000 +37.179401, -120.781837, 3837.573486, 9.879080, -5.681745, 115.481895, 0.000000 +37.179348, -120.781715, 3838.972656, 9.667299, -5.869191, 115.505310, 0.000000 +37.179298, -120.781593, 3840.342529, 9.507944, -5.992330, 115.524872, 0.000000 diff --git a/MATLAB/PlaybackExample/Playback.m b/MATLAB/PlaybackExample/Playback.m new file mode 100644 index 00000000..f5ea2a5d --- /dev/null +++ b/MATLAB/PlaybackExample/Playback.m @@ -0,0 +1,36 @@ +%% X-Plane Connect MATLAB Playback Example Script +% This script demonstrates how to playback recorded data from X-Plane. +% (See Record.m) +% Before running this script, ensure that the XPC plugin is installed and +% X-Plane is running. +%% Import XPC +addpath('../') +import XPlaneConnect.* + +%% Setup +% Create variables and open connection to X-Plane +path = 'MyRecording.txt'; % File containing stored data +interval = 0.1; % Time between snapshots in seconds +duration = 10; +Socket = openUDP(); % Open connection to X-Plane +fd = fopen(path, 'r'); % Open file + +disp('X-Plane Connect Playback Example Script'); +fprintf('Playing back ''%s'' in %fs increments.\n', path, interval); + +%% Start Playback +count = floor(duration / interval); +pauseSim(1); +for i = 1:count + line = fgetl(fd); + posi = sscanf(line, '%f, %f, %f, %f, %f, %f, %f\n'); + sendPOSI(posi); + pause(interval); +end + +%% Close connection and file +pauseSim(0); +closeUDP(Socket); +fclose(fd); + +disp('Playback complete.'); \ No newline at end of file diff --git a/MATLAB/PlaybackExample/Record.m b/MATLAB/PlaybackExample/Record.m new file mode 100644 index 00000000..8f0d0b01 --- /dev/null +++ b/MATLAB/PlaybackExample/Record.m @@ -0,0 +1,34 @@ +%% X-Plane Connect MATLAB Recording Example Script +% This script demonstrates how to record data from X-Plane that can later +% be played back. (See Playback.m) +% Before running this script, ensure that the XPC plugin is installed and +% X-Plane is running. +%% Import XPC +addpath('../') +import XPlaneConnect.* + +%% Setup +% Create variables and open connection to X-Plane +path = 'MyRecording.txt'; % File to save the data in +interval = 0.1; % Time between snapshots in seconds +duration = 10; % Time to record for in seconds +Socket = openUDP(); % Open connection to X-Plane +fd = fopen(path, 'w'); % Open file + +disp('X-Plane Connect Recording Example Script'); +fprintf('Recording to ''%s'' for %f seconds in %fs increments.\n', path, duration, interval); + +%% Start Recording +count = floor(duration / interval); +for i = 1:count + posi = getPOSI(0, Socket); + fprintf(fd, '%f, %f, %f, %f, %f, %f, %f\n', ... + posi(1), posi(2), posi(3), posi(4), posi(5), posi(6), posi(7)); + pause(interval); +end + +%% Close connection and file +closeUDP(Socket); +fclose(fd); + +disp('Recording complete.'); \ No newline at end of file diff --git a/Python/src/example.py b/Python/src/basicExample.py similarity index 100% rename from Python/src/example.py rename to Python/src/basicExample.py diff --git a/Python/src/monitorExample.py b/Python/src/monitorExample.py new file mode 100644 index 00000000..c4cdb480 --- /dev/null +++ b/Python/src/monitorExample.py @@ -0,0 +1,16 @@ +import sys + +import xpc + +def monitor(): + with xpc.XPlaneConnect() as client: + while True: + posi = client.getPOSI(); + ctrl = client.getCTRL(); + + print "Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n"\ + % (posi[0], posi[1], posi[2], ctrl[1], ctrl[0], ctrl[2]) + + +if __name__ == "__main__": + monitor() \ No newline at end of file diff --git a/Python/src/playbackExample.py b/Python/src/playbackExample.py new file mode 100644 index 00000000..dfa4f522 --- /dev/null +++ b/Python/src/playbackExample.py @@ -0,0 +1,82 @@ +from time import sleep +import xpc + +def record(path, interval = 0.1, duration = 60): + try: + fd = open(path, "w") + except: + print "Unable to open file." + return + + count = int(duration / interval) + if count < 1: + print "duration is less than a single frame." + return + + with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client: + print "Recording..." + for i in range(0, count): + try: + posi = client.getPOSI() + fd.write("{0}, {1}, {2}, {3}, {4}, {5}, {6}\n".format(*posi)) + except: + print "Error reading position" + continue + sleep(interval); + print "Recording Complete" + fd.close() + +def playback(path, interval): + try: + fd = open(path, "r") + except: + print "Unable to open file." + return + + with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client: + print "Starting Playback..." + for line in fd: + try: + posi = [ float(x) for x in line.split(',') ] + posi = client.sendPOSI(posi) + except: + print "Error sending position" + continue + sleep(interval); + print "Playback Complete" + fd.close() + +def printMenu(title, opts): + print "\n+---------------------------------------------- +" + print "| {0:42} |\n".format(title) + print "+---------------------------------------------- +" + for i in range(0,len(opts)): + print "| {0:2}. {1:40} |".format(i + 1, opts[i]) + print "+---------------------------------------------- +" + return int(raw_input("Please select and option: ")) + +def ex(): + print "X-Plane Connect Playback Example [Version 1.2.0]" + print "(c) 2013-2015 United States Government as represented by the Administrator" + print "of the National Aeronautics and Space Administration. All Rights Reserved." + + mainOpts = [ "Record X-Plane", "Playback File", "Exit" ] + + while True: + opt = printMenu("What would you like to do?", mainOpts) + if opt == 1: + path = raw_input("Enter save file path: ") + interval = float(raw_input("Enter interval between frames (seconds): ")) + duration = float(raw_input("Enter duration to record for (seconds): ")) + record(path, interval, duration) + elif opt == 2: + path = raw_input("Enter save file path: ") + interval = float(raw_input("Enter interval between frames (seconds): ")) + playback(path, interval) + elif opt == 3: + return; + else: + print "Unrecognized option." + +if __name__ == "__main__": + ex() \ No newline at end of file diff --git a/Python/src/xpc.py b/Python/src/xpc.py index c19bbdb5..e2b5c5ea 100644 --- a/Python/src/xpc.py +++ b/Python/src/xpc.py @@ -143,7 +143,29 @@ def sendDATA(self, data): buffer += struct.pack("2.0 3c7a940d-17c8-4e91-882f-9bc8b1d2f54b . - src\example.py + src\basicExample.py . @@ -24,8 +24,14 @@ false + + Code + + + Code + - + Code diff --git a/TestScripts/C Tests/CtrlTests.h b/TestScripts/C Tests/CtrlTests.h index ccf05692..7b9e4459 100644 --- a/TestScripts/C Tests/CtrlTests.h +++ b/TestScripts/C Tests/CtrlTests.h @@ -38,6 +38,33 @@ int doCTRLTest(char* drefs[7], float values[], int size, int ac, float expected[ return compareArray(expected, actual, 7); } +int doGETCTest(float values[7], int ac, float expected[7]) +{ + // Execute Test + float actual[7]; + XPCSocket sock = openUDP(IP); + int result = sendCTRL(sock, values, 7, ac); + if (result >= 0) + { + result = getCTRL(sock, actual, ac); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test values + for (int i = 0; i < 7; ++i) + { + if (fabs(expected[i] - actual[i]) > 1e-4) + { + return -10 - i; + } + } + return 0; +} + int basicCTRLTest(char** drefs, int ac) { // Set control surfaces to known state. @@ -146,4 +173,16 @@ int testCTRL_Speedbrakes() } return 0; } + +int testGETC() +{ + float CTRL[7] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F }; + return doGETCTest(CTRL, 0, CTRL); +} + +int testGETC_NonPlayer() +{ + float CTRL[7] = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F }; + return doGETCTest(CTRL, 2, CTRL); +} #endif \ No newline at end of file diff --git a/TestScripts/C Tests/PosiTests.h b/TestScripts/C Tests/PosiTests.h index d8b56730..104e517a 100644 --- a/TestScripts/C Tests/PosiTests.h +++ b/TestScripts/C Tests/PosiTests.h @@ -40,6 +40,33 @@ int doPOSITest(char* drefs[7], float values[], int size, int ac, float expected[ return compareArray(expected, actual, 7); } +int doGETPTest(float values[7], int ac, float expected[7]) +{ + // Execute Test + float actual[7]; + XPCSocket sock = openUDP(IP); + int result = sendPOSI(sock, values, 7, ac); + if (result >= 0) + { + result = getPOSI(sock, actual, ac); + } + closeUDP(sock); + if (result < 0) + { + return -1; + } + + // Test values + for (int i = 0; i < 7; ++i) + { + if (fabs(expected[i] - actual[i]) > 1e-4) + { + return -10 - i; + } + } + return 0; +} + int basicPOSITest(char** drefs, int ac) { // Set psoition and initial orientation @@ -116,6 +143,16 @@ int testPOSI_NonPlayer() return basicPOSITest(drefs, 1); } +int testGetPOSI_Player() +{ + float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 }; + return doGETPTest(POSI, 0, POSI); +} +int testGetPOSI_NonPlayer() +{ + float POSI[7] = { 37.624F, -122.06899F, 1500, 0, 0, 0, 1 }; + return doGETPTest(POSI, 3, POSI); +} #endif \ No newline at end of file diff --git a/TestScripts/C Tests/main.c b/TestScripts/C Tests/main.c index 0f5cdd90..26428eb2 100644 --- a/TestScripts/C Tests/main.c +++ b/TestScripts/C Tests/main.c @@ -40,9 +40,13 @@ int main(int argc, const char * argv[]) runTest(testCTRL_Player, "CTRL (player)"); runTest(testCTRL_NonPlayer, "CTRL (non-player)"); runTest(testCTRL_Speedbrakes, "CTRL (speedbrakes)"); + runTest(testGETC, "GETC (player)"); + runTest(testGETC_NonPlayer, "GETC (Non-player)"); // POSI runTest(testPOSI_Player, "POSI (player)"); runTest(testPOSI_NonPlayer, "POSI (non-player)"); + runTest(testGetPOSI_Player, "GETP (player)"); + runTest(testGetPOSI_NonPlayer, "GETP (non-player)"); // Data runTest(testDATA, "DATA"); // Text diff --git a/TestScripts/Java Tests/XPlaneConnectTest.java b/TestScripts/Java Tests/XPlaneConnectTest.java index fc7c76e7..2e94bb5c 100644 --- a/TestScripts/Java Tests/XPlaneConnectTest.java +++ b/TestScripts/Java Tests/XPlaneConnectTest.java @@ -698,4 +698,31 @@ public void testSendView() throws IOException } } + + @Test + public void testGetPOSI() throws IOException + { + float[] values = { 37.524F, -122.06899F, 2500.0F, 45.0F, -45.0F, 15.0F, 1.0F }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + xpc.pauseSim(true); + xpc.sendPOSI(values); + float[] actual = xpc.getPOSI(0); + + assertArrayEquals(values, actual, 1e-4F); + } + } + + @Test + public void testGetCTRL() throws IOException + { + float[] values = { 0.0F, 0.0F, 0.0F, 0.8F, 1.0F, 0.5F, -1.5F }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + xpc.sendCTRL(values); + float[] actual = xpc.getCTRL(0); + + assertArrayEquals(values, actual, 1e-4F); + } + } } \ No newline at end of file diff --git a/TestScripts/MATLAB Tests/getCTRLTest.m b/TestScripts/MATLAB Tests/getCTRLTest.m new file mode 100644 index 00000000..3dc74f8b --- /dev/null +++ b/TestScripts/MATLAB Tests/getCTRLTest.m @@ -0,0 +1,15 @@ +function getCTRLTest() +%GETCTRLTEST Summary of this function goes here +% Detailed explanation goes here +addpath('../../MATLAB') +import XPlaneConnect.* + +values = [10.0, 5.0, -5.0, 0.8, 1.0, 0.5, -1.5]; +sendCTRL(values, 0); +actual = getCTRL(0); + +assert(isequal(length(actual), length(values))); +for i = 1:length(actual) + assert(abs(actual(i) - values(i)) <1e-4) +end +end diff --git a/TestScripts/MATLAB Tests/getPOSITest.m b/TestScripts/MATLAB Tests/getPOSITest.m new file mode 100644 index 00000000..c80b79ad --- /dev/null +++ b/TestScripts/MATLAB Tests/getPOSITest.m @@ -0,0 +1,17 @@ +function getPOSITest() +%GETCTRLTEST Summary of this function goes here +% Detailed explanation goes here +addpath('../../MATLAB') +import XPlaneConnect.* + +values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ]; +pauseSim(1); +sendPOSI(values, 0); +actual = getPOSI(0); +pauseSim(0); + +assert(isequal(length(actual), length(values))); +for i = 1:length(actual) + assert(abs(actual(i) - values(i)) <1e-4) +end +end diff --git a/TestScripts/MATLAB Tests/CTRLTest.m b/TestScripts/MATLAB Tests/sendCTRLTest.m similarity index 98% rename from TestScripts/MATLAB Tests/CTRLTest.m rename to TestScripts/MATLAB Tests/sendCTRLTest.m index 7b8a9bc6..05699175 100644 --- a/TestScripts/MATLAB Tests/CTRLTest.m +++ b/TestScripts/MATLAB Tests/sendCTRLTest.m @@ -1,4 +1,4 @@ -function CTRLTest( ) +function sendCTRLTest( ) %CTRLTest Summary of this function goes here % Detailed explanation goes here %% Test player aircraft diff --git a/TestScripts/MATLAB Tests/POSITest.m b/TestScripts/MATLAB Tests/sendPOSITest.m similarity index 96% rename from TestScripts/MATLAB Tests/POSITest.m rename to TestScripts/MATLAB Tests/sendPOSITest.m index 16b6c8df..42a6e15a 100644 --- a/TestScripts/MATLAB Tests/POSITest.m +++ b/TestScripts/MATLAB Tests/sendPOSITest.m @@ -1,4 +1,4 @@ -function POSITest( ) +function sendPOSITest( ) %POSITest Summary of this function goes here % Detailed explanation goes here addpath('../../MATLAB') diff --git a/TestScripts/MATLAB Tests/tests.m b/TestScripts/MATLAB Tests/tests.m index 13e0be4c..2ef6d5f5 100644 --- a/TestScripts/MATLAB Tests/tests.m +++ b/TestScripts/MATLAB Tests/tests.m @@ -18,8 +18,10 @@ {@getDREFsTest,'Request DREF Test', 0},... {@sendDREFTest,'Send DREF Test', 0},... {@DATATest,'DATA Test', 0},... - {@CTRLTest,'CTRL Test', 0},... - {@POSITest,'POSI Test', 0},... + {@sendCTRLTest,'sendCTRL Test', 0},... + {@getCTRLTest,'getCTRL Test', 0},... + {@sendPOSITest,'sendPOSI Test', 0},... + {@getPOSITest,'getPOSI Test', 0},... {@sendWYPTTest,'WYPT Test', 0},... {@sendVIEWTest,'VIEW Test', 0},... {@pauseTest,'Pause Test', 0},... diff --git a/TestScripts/Python Tests/Tests.py b/TestScripts/Python Tests/Tests.py index 2cdc8254..486803d9 100644 --- a/TestScripts/Python Tests/Tests.py +++ b/TestScripts/Python Tests/Tests.py @@ -3,7 +3,7 @@ import imp import time -import xpc +xpc = imp.load_source('xpc', '../../Python/src/xpc.py') class XPCTests(unittest.TestCase): """Tests the functionality of the XPlaneConnect class.""" @@ -202,6 +202,29 @@ def do_test(): expected = 0.0 do_test() + def test_getCTRL(self): + values = None + ac = 0 + expected = None + def do_test(): + with xpc.XPlaneConnect() as client: + # Execute + client.sendCTRL(values, ac) + result = client.getCTRL(ac) + + # Test + self.assertEqual(len(result), len(expected)) + for a, e in zip(result, expected): + self.assertAlmostEqual(a, e, 4) + + values = [0.0, 0.0, 0.0, 0.8, 1.0, 0.5, -1.5] + expected = values + ac = 0 + do_test() + + ac = 3 + do_test() + def test_sendCTRL(self): # Setup @@ -270,6 +293,31 @@ def do_test(): ctrl[6] = 0.0 do_test() + def test_getPOSI(self): + values = None + ac = 0 + expected = None + def do_test(): + with xpc.XPlaneConnect() as client: + # Execute + client.pauseSim(True) + client.sendPOSI(values, ac) + result = client.getPOSI(ac) + client.pauseSim(False) + + # Test + self.assertEqual(len(result), len(expected)) + for a, e in zip(result, expected): + self.assertAlmostEqual(a, e, 4) + + values = [ 37.524, -122.06899, 2500, 45, -45, 15, 1 ] + expected = values + ac = 0 + do_test() + + ac = 3 + do_test() + def test_sendPOSI(self): # Setup drefs = ["sim/flightmodel/position/latitude",\ diff --git a/xpcPlugin/CMakeLists.txt b/xpcPlugin/CMakeLists.txt index ca964b5b..d07ae272 100644 --- a/xpcPlugin/CMakeLists.txt +++ b/xpcPlugin/CMakeLists.txt @@ -13,7 +13,6 @@ SET(CMAKE_CXX_COMPILER g++) add_library(xpc64 SHARED XPCPlugin.cpp DataManager.cpp - DataMaps.cpp Drawing.cpp Log.cpp Message.cpp @@ -24,7 +23,6 @@ set_target_properties(xpc64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-shared add_library(xpc32 SHARED XPCPlugin.cpp DataManager.cpp - DataMaps.cpp Drawing.cpp Log.cpp Message.cpp diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp index 42048e5b..45710060 100644 --- a/xpcPlugin/DataManager.cpp +++ b/xpcPlugin/DataManager.cpp @@ -169,8 +169,8 @@ namespace XPC sprintf(multi, "sim/multiplayer/position/plane%i_gear_deploy", i); mdrefs[i][DREF_GearDeploy] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio", i); - mdrefs[i][DREF_FlapSetting] = XPLMFindDataRef(multi); // Can't set the actual flap setting on npc aircraft mdrefs[i][DREF_FlapActual] = XPLMFindDataRef(multi); + mdrefs[i][DREF_FlapSetting] = mdrefs[i][DREF_FlapActual]; // Can't set the actual flap setting on npc aircraft sprintf(multi, "sim/multiplayer/position/plane%i_flap_ratio2", i); mdrefs[i][DREF_FlapActual2] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i); @@ -183,6 +183,7 @@ namespace XPC mdrefs[i][DREF_Sweep] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_throttle", i); mdrefs[i][DREF_ThrottleActual] = XPLMFindDataRef(multi); + mdrefs[i][DREF_ThrottleSet] = mdrefs[i][DREF_ThrottleActual]; // No throttle set for multiplayer planes. sprintf(multi, "sim/multiplayer/position/plane%i_yolk_pitch", i); mdrefs[i][DREF_YokePitch] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_yolk_roll", i); @@ -528,7 +529,7 @@ namespace XPC } XPLMDataTypeID dataType = XPLMGetDataRefTypes(xdref); - Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %s (x:%X) Type: %i", xdref, dataType); + Log::FormatLine(LOG_INFO, "DMAN", "Setting DREF %s (x:%X) Type: %i", dref.c_str(), xdref, dataType); if ((dataType & 2) == 2) // Float { XPLMSetDataf(xdref, values[0]); @@ -729,13 +730,13 @@ namespace XPC // http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf float q[4]; float halfRad = 0.00872664625997F; - orient[2] = halfRad * orient[2]; - orient[0] = halfRad * orient[0]; - orient[1] = halfRad * orient[1]; - q[0] = cos(orient[2]) * cos(orient[0]) * cos(orient[1]) + sin(orient[2]) * sin(orient[0]) * sin(orient[1]); - q[1] = cos(orient[2]) * cos(orient[0]) * sin(orient[1]) - sin(orient[2]) * sin(orient[0]) * cos(orient[1]); - q[2] = cos(orient[2]) * sin(orient[0]) * cos(orient[1]) + sin(orient[2]) * cos(orient[0]) * sin(orient[1]); - q[3] = sin(orient[2]) * cos(orient[0]) * cos(orient[1]) - cos(orient[2]) * sin(orient[0]) * sin(orient[1]); + float theta = halfRad * orient[0]; + float phi = halfRad * orient[1]; + float psi = halfRad * orient[2]; + q[0] = cos(phi) * cos(theta) * cos(psi) + sin(phi) * sin(theta) * sin(psi); + q[1] = sin(phi) * cos(theta) * cos(psi) - cos(phi) * sin(theta) * sin(psi); + q[2] = cos(phi) * sin(theta) * cos(psi) + sin(phi) * cos(theta) * sin(psi); + q[3] = cos(phi) * cos(theta) * sin(psi) - sin(phi) * sin(theta) * cos(psi); // If the sim is un-paused, this will overwrite the pitch/roll/yaw // values set above. diff --git a/xpcPlugin/Log.cpp b/xpcPlugin/Log.cpp index 411cdb43..7b849dd1 100644 --- a/xpcPlugin/Log.cpp +++ b/xpcPlugin/Log.cpp @@ -57,7 +57,7 @@ namespace XPC static void WriteLevel(FILE* fd, int level) { - char* str; + const char* str; switch (level) { case LOG_OFF: diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp index 26dca40e..3563a09d 100644 --- a/xpcPlugin/Message.cpp +++ b/xpcPlugin/Message.cpp @@ -25,12 +25,6 @@ namespace XPC return m; } - unsigned long Message::GetMagicNumber() const - { - unsigned long val = size < 4 ? 0 : *((unsigned long*)buffer); - return val; - } - std::string Message::GetHead() const { std::string val = size < 4 ? "" : std::string((char*)buffer, 4); @@ -59,26 +53,22 @@ namespace XPC stringstream ss; // Dump raw bytes to string - ss << hex << setfill('0'); + ss << std::hex << setfill('0'); for (int i = 0; i < size; ++i) { ss << ' ' << setw(2) << static_cast(buffer[i]); } - Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + Log::WriteLine(LOG_TRACE, "DBUG", ss.str()); + std::string head = GetHead(); ss.str(""); - ss << "Head: " << GetHead() << "(0x" << setw(8) << GetMagicNumber() << ")" << dec << " Size: " << GetSize(); - switch (GetMagicNumber()) // Binary version of head + ss << "Head: " << head << std::dec << " Size: " << GetSize(); + if (head == "CONN" || head == "WYPT" || head == "TEXT") { - case 0x4E4EF443: // CONN - case 0x54505957: // WYPT - case 0x54584554: // TEXT - { Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - break; - } - case 0x4C525443: // CTRL - { + } + else if (head == "CTRL") + { // Parse message data float pitch = *((float*)(buffer + 5)); float roll = *((float*)(buffer + 9)); @@ -91,12 +81,11 @@ namespace XPC { aircraft = buffer[26]; } - ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; - ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; - Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - break; - } - case 0x41544144: // DATA + ss << " Attitude:(" << pitch << " " << roll << " " << yaw << ")"; + ss << " Thr:" << thr << " Gear:" << (int)gear << " Flaps:" << flaps; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else if (head == "DATA") { size_t numCols = (size - 5) / 36; float values[32][9]; @@ -107,19 +96,18 @@ namespace XPC } ss << " (" << numCols << " lines)"; Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - for (int i = 0; i < numCols; ++i) - { - ss.str(""); + for (int i = 0; i < numCols; ++i) + { + ss.str(""); ss << " #" << values[i][0]; - for (int j = 1; j < 9; ++j) - { + for (int j = 1; j < 9; ++j) + { ss << " " << values[i][j]; } Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - } - break; - } - case 0x46455244: // DREF + } + } + else if (head == "DREF") { Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); string dref((char*)buffer + 6, buffer[5]); @@ -132,9 +120,13 @@ namespace XPC ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i)); } Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - break; } - case 0x44544547: // GETD + else if (head == "GETC" || head == "GETP") + { + ss << " Aircraft:" << (int)buffer[5]; + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else if (head == "GETD") { Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); int cur = 6; @@ -145,9 +137,8 @@ namespace XPC i + 1, buffer[5], dref.length(), dref.c_str()); cur += 1 + buffer[cur]; } - break; } - case 0x49534F50: // POSI + else if (head == "POSI") { char aircraft = buffer[5]; float gear = *((float*)(buffer + 30)); @@ -160,20 +151,21 @@ namespace XPC ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:"; ss << gear; Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - break; } - case 0x554D4953: // SIMU + else if (head == "SIMU") { ss << ' ' << (int)buffer[5]; Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - break; - } - default: + } + else if (head == "VIEW") + { + ss << "Type:" << *((unsigned long*)(buffer + 5)); + Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); + } + else { ss << " UNKNOWN HEADER "; Log::WriteLine(LOG_DEBUG, "DBUG", ss.str()); - break; } - } } } diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index d72b8a84..d94be092 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -55,6 +55,8 @@ namespace XPC handlers.insert(std::make_pair("TEXT", MessageHandlers::HandleText)); handlers.insert(std::make_pair("WYPT", MessageHandlers::HandleWypt)); handlers.insert(std::make_pair("VIEW", MessageHandlers::HandleView)); + handlers.insert(std::make_pair("GETC", MessageHandlers::HandleGetC)); + handlers.insert(std::make_pair("GETP", MessageHandlers::HandleGetP)); // X-Plane data messages handlers.insert(std::make_pair("DSEL", MessageHandlers::HandleXPlaneData)); handlers.insert(std::make_pair("USEL", MessageHandlers::HandleXPlaneData)); @@ -425,6 +427,43 @@ namespace XPC } } + void MessageHandlers::HandleGetC(const Message& msg) + { + const unsigned char* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + if (size != 6) + { + Log::FormatLine(LOG_ERROR, "GCTL", "Unexpected message length: %u", size); + return; + } + unsigned char aircraft = buffer[5]; + // TODO(jason-watkins): Get proper printf specifier for unsigned char + Log::FormatLine(LOG_TRACE, "GCTL", "Getting control information for aircraft %u", aircraft); + + float throttle[8]; + unsigned char response[31] = "CTRL"; + *((float*)(response + 5)) = DataManager::GetFloat(DREF_Elevator, aircraft); + *((float*)(response + 9)) = DataManager::GetFloat(DREF_Aileron, aircraft); + *((float*)(response + 13)) = DataManager::GetFloat(DREF_Rudder, aircraft); + DataManager::GetFloatArray(DREF_ThrottleSet, throttle, 8, aircraft); + *((float*)(response + 17)) = throttle[0]; + if (aircraft == 0) + { + response[21] = (char)DataManager::GetInt(DREF_GearHandle, aircraft); + } + else + { + float mpGear[10]; + DataManager::GetFloatArray(DREF_GearDeploy, mpGear, 10, aircraft); + response[21] = mpGear[0] > 0.5 ? 1 : 0; + } + *((float*)(response + 22)) = DataManager::GetFloat(DREF_FlapSetting, aircraft); + response[26] = aircraft; + *((float*)(response + 27)) = DataManager::GetFloat(DREF_SpeedBrakeSet, aircraft); + + sock->SendTo(response, 31, &connection.addr); + } + void MessageHandlers::HandleGetD(const Message& msg) { const unsigned char* buffer = msg.GetBuffer(); @@ -471,6 +510,34 @@ namespace XPC sock->SendTo(response, cur, &connection.addr); } + void MessageHandlers::HandleGetP(const Message& msg) + { + const unsigned char* buffer = msg.GetBuffer(); + std::size_t size = msg.GetSize(); + if (size != 6) + { + Log::FormatLine(LOG_ERROR, "GPOS", "Unexpected message length: %u", size); + return; + } + unsigned char aircraft = buffer[5]; + Log::FormatLine(LOG_TRACE, "GPOS", "Getting position information for aircraft %u", aircraft); + + unsigned char response[34] = "POSI"; + response[5] = (char)DataManager::GetInt(DREF_GearHandle, aircraft); + *((float*)(response + 6)) = (float)DataManager::GetDouble(DREF_Latitude, aircraft); + *((float*)(response + 10)) = (float)DataManager::GetDouble(DREF_Longitude, aircraft); + *((float*)(response + 14)) = (float)DataManager::GetDouble(DREF_Elevation, aircraft); + *((float*)(response + 18)) = DataManager::GetFloat(DREF_Pitch, aircraft); + *((float*)(response + 22)) = DataManager::GetFloat(DREF_Roll, aircraft); + *((float*)(response + 26)) = DataManager::GetFloat(DREF_HeadingTrue, aircraft); + + float gear[10]; + DataManager::GetFloatArray(DREF_GearDeploy, gear, 10, aircraft); + *((float*)(response + 30)) = gear[0]; + + sock->SendTo(response, 34, &connection.addr); + } + void MessageHandlers::HandlePosi(const Message& msg) { // Update log diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h index 13742eeb..d7f317d9 100644 --- a/xpcPlugin/MessageHandlers.h +++ b/xpcPlugin/MessageHandlers.h @@ -43,7 +43,9 @@ namespace XPC static void HandleCtrl(const Message& msg); static void HandleData(const Message& msg); static void HandleDref(const Message& msg); + static void HandleGetC(const Message& msg); static void HandleGetD(const Message& msg); + static void HandleGetP(const Message& msg); static void HandlePosi(const Message& msg); static void HandleSimu(const Message& msg); static void HandleText(const Message& msg); diff --git a/xpcPlugin/UDPSocket.cpp b/xpcPlugin/UDPSocket.cpp index 349f127d..210cc565 100644 --- a/xpcPlugin/UDPSocket.cpp +++ b/xpcPlugin/UDPSocket.cpp @@ -135,7 +135,7 @@ namespace XPC } else { - Log::FormatLine(LOG_INFO, tag, "Send failed. (remote: %s)", GetHost(remote).c_str()); + Log::FormatLine(LOG_INFO, tag, "Send succeeded. (remote: %s)", GetHost(remote).c_str()); } } diff --git a/xpcPlugin/XPCPlugin.cpp b/xpcPlugin/XPCPlugin.cpp index c8b4aef0..f94b98ad 100755 --- a/xpcPlugin/XPCPlugin.cpp +++ b/xpcPlugin/XPCPlugin.cpp @@ -91,7 +91,7 @@ static float XPCFlightLoopCallback(float inElapsedSinceLastCall, float inElapsed PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) { - strcpy(outName, "X-Plane Connect [Version 1.0.1]"); + strcpy(outName, "X-Plane Connect [Version 1.2.0]"); strcpy(outSig, "NASA.XPlaneConnect"); strcpy(outDesc, "X Plane Communications Toolbox\nCopyright (c) 2013-2015 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved."); @@ -105,7 +105,7 @@ PLUGIN_API int XPluginStart(char* outName, char* outSig, char* outDesc) 1000000000.0; } #endif - XPC::Log::Initialize("1.1.0"); + XPC::Log::Initialize("1.1.1"); XPC::Log::WriteLine(LOG_INFO, "EXEC", "Plugin Start"); XPC::DataManager::Initialize(); diff --git a/xpcPlugin/XPlaneConnect/64/lin.xpl b/xpcPlugin/XPlaneConnect/64/lin.xpl index 9fe51276..476986c7 100755 Binary files a/xpcPlugin/XPlaneConnect/64/lin.xpl and b/xpcPlugin/XPlaneConnect/64/lin.xpl differ diff --git a/xpcPlugin/XPlaneConnect/64/win.xpl b/xpcPlugin/XPlaneConnect/64/win.xpl index a27a63b7..1328419d 100644 Binary files a/xpcPlugin/XPlaneConnect/64/win.xpl and b/xpcPlugin/XPlaneConnect/64/win.xpl differ diff --git a/xpcPlugin/XPlaneConnect/lin.xpl b/xpcPlugin/XPlaneConnect/lin.xpl index 09f31589..a1780c79 100755 Binary files a/xpcPlugin/XPlaneConnect/lin.xpl and b/xpcPlugin/XPlaneConnect/lin.xpl differ diff --git a/xpcPlugin/XPlaneConnect/mac.xpl b/xpcPlugin/XPlaneConnect/mac.xpl index da391d26..aa4d8191 100755 Binary files a/xpcPlugin/XPlaneConnect/mac.xpl and b/xpcPlugin/XPlaneConnect/mac.xpl differ diff --git a/xpcPlugin/XPlaneConnect/win.xpl b/xpcPlugin/XPlaneConnect/win.xpl index ce965032..08131986 100644 Binary files a/xpcPlugin/XPlaneConnect/win.xpl and b/xpcPlugin/XPlaneConnect/win.xpl differ