diff --git a/.gitignore b/.gitignore
index 2575f4c..d77d831 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@
# OS generated files #
######################
.DS_Store?
+.DS_Store
ehthumbs.db
Icon?
Thumbs.db
@@ -69,6 +70,7 @@ build/
xcuserdata
*.moved-aside
*.xccheckout
+xcshareddata
# IntelliJ Files #
##################
diff --git a/C/monitorExample/main.c b/C/monitorExample/main.c
index e78f0cf..e6d2e62 100644
--- a/C/monitorExample/main.c
+++ b/C/monitorExample/main.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// DISCLAIMERS
@@ -70,7 +70,7 @@ int main(void)
tv.tv_usec = 100 * 1000;
while (1)
{
- float posi[7];
+ float posi[7]; // FIXME: change this to the 64-bit lat/lon/h
int result = getPOSI(client, posi, aircraftNum);
if (result < 0) // Error in getPOSI
{
@@ -98,4 +98,4 @@ int main(void)
printf("\n\nPress Any Key to exit...");
getchar();
return 0;
-}
\ No newline at end of file
+}
diff --git a/C/playbackExample/src/chrome.c b/C/playbackExample/src/chrome.c
index f1850dd..b4a95b9 100644
--- a/C/playbackExample/src/chrome.c
+++ b/C/playbackExample/src/chrome.c
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -68,4 +68,4 @@ int getInt(char* 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
index fd240e6..33bc0c6 100644
--- a/C/playbackExample/src/chrome.h
+++ b/C/playbackExample/src/chrome.h
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -37,4 +37,4 @@ void getString(char* prompt, char buffer[255]);
int getInt(char* prompt);
-#endif
\ No newline at end of file
+#endif
diff --git a/C/playbackExample/src/main.c b/C/playbackExample/src/main.c
index abdeb33..785582d 100644
--- a/C/playbackExample/src/main.c
+++ b/C/playbackExample/src/main.c
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -35,7 +35,7 @@ int main(void)
};
char path[256] = { 0 };
- displayStart("1.2.0.0");
+ displayStart("1.3-rc.1");
while (1)
{
switch (displayMenu("What would you like to do?", mainOpts, 3))
@@ -67,4 +67,4 @@ int main(void)
}
return 0;
-}
\ No newline at end of file
+}
diff --git a/C/playbackExample/src/playback.c b/C/playbackExample/src/playback.c
index 3226c5a..4d2e594 100644
--- a/C/playbackExample/src/playback.c
+++ b/C/playbackExample/src/playback.c
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -89,18 +89,18 @@ void playback(char* path, int interval)
displayMsg("Starting Playback...");
XPCSocket sock = openUDP("127.0.0.1");
- float posi[7];
+ double posi[7];
while (!feof(fd) && !ferror(fd))
{
- int result = fscanf(fd, "%f, %f, %f, %f, %f, %f, %f\n",
+ int result = fscanf(fd, "%lf, %lf, %lf, %lf, %lf, %lf, %lf\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
index 98fa54b..d76623d 100644
--- a/C/playbackExample/src/playback.h
+++ b/C/playbackExample/src/playback.h
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -29,4 +29,4 @@ void record(char* path, int interval, int duration);
void playback(char* path, int interval);
-#endif
\ No newline at end of file
+#endif
diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c
index e201a75..ed984fb 100755
--- a/C/src/xplaneConnect.c
+++ b/C/src/xplaneConnect.c
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -82,13 +82,13 @@ XPCSocket openUDP(const char *xpIP)
XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port)
{
XPCSocket sock;
-
+
// Setup Port
struct sockaddr_in recvaddr;
recvaddr.sin_family = AF_INET;
recvaddr.sin_addr.s_addr = INADDR_ANY;
recvaddr.sin_port = htons(port);
-
+
// Set X-Plane Port and IP
if (strcmp(xpIP, "localhost") == 0)
{
@@ -96,7 +96,7 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port)
}
strncpy(sock.xpIP, xpIP, 16);
sock.xpPort = xpPort == 0 ? 49009 : xpPort;
-
+
#ifdef _WIN32
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
@@ -105,7 +105,7 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port)
exit(EXIT_FAILURE);
}
#endif
-
+
if ((sock.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
printError("OpenUDP", "Socket creation failed");
@@ -128,7 +128,7 @@ XPCSocket aopenUDP(const char *xpIP, unsigned short xpPort, unsigned short port)
if (setsockopt(sock.sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0)
{
printError("OpenUDP", "Failed to set timeout");
- }
+ }
return sock;
}
@@ -160,7 +160,7 @@ int sendUDP(XPCSocket sock, char buffer[], int len)
printError("sendUDP", "Message length must be positive.");
return -1;
}
-
+
// Set up destination address
struct sockaddr_in dst;
dst.sin_family = AF_INET;
@@ -273,7 +273,7 @@ int setCONN(XPCSocket* sock, unsigned short port)
int pauseSim(XPCSocket sock, char pause)
{
// Validte input
- if (pause < 0 || pause > 2)
+ if (pause < 0 || (pause > 2 && pause < 100) || (pause > 119 && pause < 200) || pause > 219)
{
printError("pauseSim", "Invalid argument: %i", pause);
return -2;
@@ -311,7 +311,7 @@ int sendDATA(XPCSocket sock, float data[][9], int rows)
// Setup command
// 5 byte header + 134 rows * 9 values * 4 bytes per value => 4829 byte max length.
- char buffer[4829] = "DATA";
+ char buffer[4829] = "DATA";
int len = 5 + rows * 9 * sizeof(float);
unsigned short step = 9 * sizeof(float);
int i; // iterator
@@ -462,7 +462,7 @@ int getDREFResponse(XPCSocket sock, float* values[], unsigned char count, int si
{
unsigned char buffer[65536];
int result = readUDP(sock, buffer, 65536);
-
+
if (result < 0)
{
#ifdef _WIN32
@@ -563,13 +563,14 @@ int getPOSI(XPCSocket sock, float values[7], char ac)
printError("getPOSI", "Unexpected response length.");
return -3;
}
+ // TODO: change this to the 64-bit lat/lon/h
// Copy response into values
memcpy(values, readBuffer + 6, 7 * sizeof(float));
return 0;
}
-int sendPOSI(XPCSocket sock, float values[], int size, char ac)
+int sendPOSI(XPCSocket sock, double values[], int size, char ac)
{
// Validate input
if (ac < 0 || ac > 20)
@@ -584,23 +585,32 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac)
}
// Setup command
- // 5 byte header + up to 7 values * 5 bytes each
- unsigned char buffer[40] = "POSI";
+ unsigned char buffer[46] = "POSI";
+ buffer[4] = 0xff; //Placeholder for message length
buffer[5] = ac;
int i; // iterator
- for (i = 0; i < 7; i++)
+
+ for (i = 0; i < 7; i++) // double for lat/lon/h
{
- float val = -998;
+ double val = -998;
if (i < size)
{
val = values[i];
}
- *((float*)(buffer + 6 + i * 4)) = val;
+ if (i < 3) /* lat/lon/h */
+ {
+ memcpy(&buffer[6 + i*8], &val, sizeof(double));
+ }
+ else /* attitude and gear */
+ {
+ float f = (float)val;
+ memcpy(&buffer[18 + i*4], &f, sizeof(float));
+ }
}
// Send Command
- if (sendUDP(sock, buffer, 40) < 0)
+ if (sendUDP(sock, buffer, 46) < 0)
{
printError("sendPOSI", "Failed to send command");
return -3;
@@ -735,9 +745,9 @@ int sendTEXT(XPCSocket sock, char* msg, int x, int y)
size_t len = 14 + msgLen;
memcpy(buffer + 5, &x, sizeof(int));
memcpy(buffer + 9, &y, sizeof(int));
- buffer[13] = msgLen;
+ buffer[13] = (unsigned char)msgLen;
strncpy(buffer + 14, msg, msgLen);
-
+
// Send Command
if (sendUDP(sock, buffer, len) < 0)
{
@@ -807,4 +817,4 @@ int sendVIEW(XPCSocket sock, VIEW_TYPE view)
}
/*****************************************************************************/
/**** End View functions ****/
-/*****************************************************************************/
\ No newline at end of file
+/*****************************************************************************/
diff --git a/C/src/xplaneConnect.h b/C/src/xplaneConnect.h
index b701c88..8cfb06f 100644
--- a/C/src/xplaneConnect.h
+++ b/C/src/xplaneConnect.h
@@ -1,8 +1,8 @@
-//Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 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,
+// 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
@@ -78,7 +78,7 @@ typedef enum
XPC_VIEW_FULLSCREENWITHHUD,
XPC_VIEW_FULLSCREENNOHUD,
} VIEW_TYPE;
-
+
// Low Level UDP Functions
/// Opens a new connection to XPC on an OS chosen port.
@@ -112,10 +112,10 @@ int setCONN(XPCSocket* sock, unsigned short port);
/// Pause or unpause the simulation.
///
/// \param sock The socket to use to send the command.
-/// \param pause 0 to unpause the sim; any other value to pause.
+/// \param pause 0 to unpause the sim; 1 to pause, 100:119 to pause a/c 0:19, 200:219 to unpause a/c 0:19.
/// \returns 0 if successful, otherwise a negative value.
int pauseSim(XPCSocket sock, char pause);
-
+
// X-Plane UDP DATA
/// Reads X-Plane data from the specified socket.
@@ -193,7 +193,7 @@ int getDREF(XPCSocket sock, const char* dref, float values[], int* size);
/// to the actual number of elements copied in for that row.
/// \returns 0 if successful, otherwise a negative value.
int getDREFs(XPCSocket sock, const char* drefs[], float* values[], unsigned char count, int sizes[]);
-
+
// Position
/// Gets the position and orientation of the specified aircraft.
@@ -213,7 +213,7 @@ int getPOSI(XPCSocket sock, float values[7], char ac);
/// \param size The number of elements in values.
/// \param ac The aircraft number to set the position of. 0 for the player aircraft.
/// \returns 0 if successful, otherwise a negative value.
-int sendPOSI(XPCSocket sock, float values[], int size, char ac);
+int sendPOSI(XPCSocket sock, double values[], int size, char ac);
// Controls
diff --git a/C/xpcExample/xpcExample/src/main.cpp b/C/xpcExample/xpcExample/src/main.cpp
index e9e645a..ba06f01 100644
--- a/C/xpcExample/xpcExample/src/main.cpp
+++ b/C/xpcExample/xpcExample/src/main.cpp
@@ -33,7 +33,7 @@ int main()
// Set Location/Orientation (sendPOSI)
// Set Up Position Array
- float POSI[9] = { 0.0 };
+ double POSI[9] = { 0.0 };
POSI[0] = 37.524; // Lat
POSI[1] = -122.06899; // Lon
POSI[2] = 2500; // Alt
diff --git a/Java/.idea/misc.xml b/Java/.idea/misc.xml
index a61f7bc..b7148e8 100644
--- a/Java/.idea/misc.xml
+++ b/Java/.idea/misc.xml
@@ -1,19 +1,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Java/.idea/modules.xml b/Java/.idea/modules.xml
index 3132258..bc73bac 100644
--- a/Java/.idea/modules.xml
+++ b/Java/.idea/modules.xml
@@ -2,6 +2,7 @@
+
diff --git a/Java/Examples/BasicOperation/src/Main.java b/Java/Examples/BasicOperation/src/Main.java
index dc82c9f..99c28c9 100644
--- a/Java/Examples/BasicOperation/src/Main.java
+++ b/Java/Examples/BasicOperation/src/Main.java
@@ -1,7 +1,6 @@
package gov.nasa.xpc.ex;
import gov.nasa.xpc.XPlaneConnect;
-
import java.io.IOException;
import java.net.SocketException;
import java.util.Arrays;
@@ -25,11 +24,11 @@ public class Main
xpc.getDREF("sim/test/test_float");
System.out.println("Setting player aircraft position");
- float[] posi = new float[] {37.524F, -122.06899F, 2500, 0, 0, 0, 1};
+ double[] posi = new double[] {37.524, -122.06899, 2500, 0, 0, 0, 1};
xpc.sendPOSI(posi);
System.out.println("Setting another aircraft position");
- posi[0] = 37.52465F;
+ posi[0] = 37.52465;
posi[4] = 20;
xpc.sendPOSI(posi, 1);
diff --git a/Java/Examples/ContinuousOperation/src/Main.java b/Java/Examples/ContinuousOperation/src/Main.java
index 61a14eb..801a369 100644
--- a/Java/Examples/ContinuousOperation/src/Main.java
+++ b/Java/Examples/ContinuousOperation/src/Main.java
@@ -20,7 +20,7 @@ public class Main
int aircraft = 0;
while(true)
{
- float[] posi = xpc.getPOSI(aircraft);
+ float[] posi = xpc.getPOSI(aircraft); // FIXME: change this to 64-bit double
float[] ctrl = xpc.getCTRL(aircraft);
System.out.format("Loc: (%4f, %4f, %4f) Aileron:%2f Elevator:%2f Rudder:%2f\n",
diff --git a/Java/Examples/DiscoveryExample/DiscoveryExample.iml b/Java/Examples/DiscoveryExample/DiscoveryExample.iml
new file mode 100644
index 0000000..eeb2a2f
--- /dev/null
+++ b/Java/Examples/DiscoveryExample/DiscoveryExample.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Java/Examples/DiscoveryExample/src/DiscoveryConnectionExample.java b/Java/Examples/DiscoveryExample/src/DiscoveryConnectionExample.java
new file mode 100644
index 0000000..454dd6d
--- /dev/null
+++ b/Java/Examples/DiscoveryExample/src/DiscoveryConnectionExample.java
@@ -0,0 +1,35 @@
+package gov.nasa.xpc.ex;
+
+import gov.nasa.xpc.XPlaneConnect;
+import gov.nasa.xpc.discovery.XPlaneConnectDiscovery;
+
+import java.io.IOException;
+import java.net.SocketException;
+
+
+
+public class DiscoveryConnectionExample {
+
+ public static void main(String[] args) throws IOException {
+
+ XPlaneConnectDiscovery discovery = new XPlaneConnectDiscovery();
+ discovery.start(xpc -> {
+
+ sendDref(xpc);
+ });
+ System.out.println("Example done");
+ }
+
+ static void sendDref(XPlaneConnect xpc) {
+ System.out.println("Sending DREF");
+ try {
+ xpc.getDREF("sim/test/test_float");
+
+ } catch (SocketException e) {
+ System.out.println("Unable to set up the connection. (Error message was '" + e.getMessage() + "'.)");
+ } catch (IOException e) {
+ System.out.println("Something went wrong with one of the commands. (Error message was '" + e.getMessage() + "'.)");
+ }
+ System.out.println("Sending DREF done");
+ }
+}
diff --git a/Java/Examples/DiscoveryExample/src/SimpleDiscoveryExample.java b/Java/Examples/DiscoveryExample/src/SimpleDiscoveryExample.java
new file mode 100644
index 0000000..cfab704
--- /dev/null
+++ b/Java/Examples/DiscoveryExample/src/SimpleDiscoveryExample.java
@@ -0,0 +1,23 @@
+package gov.nasa.xpc.ex;
+
+import gov.nasa.xpc.discovery.XPlaneConnectDiscovery;
+
+import java.io.IOException;
+
+
+/**
+ * XPlaneConnect discovery example that prints continuously each BECN packet
+ */
+public class SimpleDiscoveryExample {
+
+ public static void main(String[] args) throws IOException {
+XPlaneConnectDiscovery discovery = new XPlaneConnectDiscovery();
+discovery.onBeaconReceived(beacon -> {
+ System.out.println("Discovered XPlaneConnect plugin:");
+ System.out.println("Plugin version: " + beacon.getPluginVersion());
+ System.out.println("X-Plane version: " + beacon.getXplaneVersion());
+ System.out.println("Address: " + beacon.getXplaneAddress().getHostAddress() + ":" + beacon.getPluginPort());
+});
+discovery.start();
+ }
+}
diff --git a/Java/Examples/Playback/src/gov/nasa/xpc/Main.java b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java
index 0e0b376..3b5925b 100644
--- a/Java/Examples/Playback/src/gov/nasa/xpc/Main.java
+++ b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java
@@ -12,7 +12,7 @@ public class Main
{
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("X-Plane Connect Playback Example [Version 1.3-rc.1]");
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)
@@ -99,7 +99,7 @@ public class Main
{
for (int i = 0; i < count; ++i)
{
- float[] posi = xpc.getPOSI(0);
+ float[] posi = xpc.getPOSI(0); // FIXME: change this to 64-bit double
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
@@ -125,11 +125,11 @@ public class Main
{
while(reader.hasNextLine())
{
- float[] posi = new float[7];
+ double[] posi = new double[7];
for (int i = 0; i < 7; ++i)
{
String s = reader.next();
- posi[i] = Float.parseFloat(s);
+ posi[i] = Double.parseDouble(s);
}
reader.nextLine();
xpc.sendPOSI(posi);
diff --git a/Java/XPlaneConnect.iml b/Java/XPlaneConnect.iml
index 70873b5..8acc69f 100644
--- a/Java/XPlaneConnect.iml
+++ b/Java/XPlaneConnect.iml
@@ -1,6 +1,6 @@
-
+
@@ -10,6 +10,15 @@
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Java/lib/hamcrest-core-1.3.jar b/Java/lib/hamcrest-core-1.3.jar
new file mode 100644
index 0000000..9d5fe16
Binary files /dev/null and b/Java/lib/hamcrest-core-1.3.jar differ
diff --git a/Java/lib/junit-4.12.jar b/Java/lib/junit-4.12.jar
new file mode 100644
index 0000000..3a7fc26
Binary files /dev/null and b/Java/lib/junit-4.12.jar differ
diff --git a/Java/src/ViewType.java b/Java/src/ViewType.java
index 0457050..3cff8c4 100644
--- a/Java/src/ViewType.java
+++ b/Java/src/ViewType.java
@@ -1,5 +1,5 @@
//NOTICES:
-// Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// DISCLAIMERS
diff --git a/Java/src/WaypointOp.java b/Java/src/WaypointOp.java
index 116d848..30cf4fc 100644
--- a/Java/src/WaypointOp.java
+++ b/Java/src/WaypointOp.java
@@ -1,5 +1,5 @@
//NOTICES:
-// Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// DISCLAIMERS
diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java
index 8b1f228..675da9b 100644
--- a/Java/src/XPlaneConnect.java
+++ b/Java/src/XPlaneConnect.java
@@ -1,5 +1,5 @@
//NOTICES:
-// Copyright (c) 2013-2016 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// DISCLAIMERS
@@ -25,6 +25,8 @@
package gov.nasa.xpc;
+import gov.nasa.xpc.discovery.Beacon;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.AutoCloseable;
@@ -136,6 +138,19 @@ public class XPlaneConnect implements AutoCloseable
this(xpHost, xpPort, port, 100);
}
+
+ /**
+ * Initializes a new instance of the {@code XPlaneConnect} class from a received discovery Beacon
+ * @param beacon The beacon received from {@code XPlaneConnectDiscovery}
+ * @throws SocketException If this instance is unable to bind to the specified port.
+ */
+ public XPlaneConnect(Beacon beacon) throws SocketException {
+ this.socket = new DatagramSocket(0);
+ this.xplaneAddr = beacon.getXplaneAddress();
+ this.xplanePort = beacon.getPluginPort();
+ this.socket.setSoTimeout(100);
+ }
+
/**
* Initializes a new instance of the {@code XPlaneConnect} class using the specified ports, hostname, and timeout.
*
@@ -223,9 +238,9 @@ public class XPlaneConnect implements AutoCloseable
*/
public void pauseSim(int pause) throws IOException
{
- if(pause < 0 || pause > 2)
+ if(pause < 0 || (pause > 2 && pause < 100) || (pause > 119 && pause < 200) || pause > 219)
{
- throw new IllegalArgumentException("pause must be a value in the range [0, 2].");
+ throw new IllegalArgumentException("pause must be a value in the range [0, 2], [100, 119], or [200, 219].");
}
// S I M U LEN VAL
@@ -549,7 +564,7 @@ public class XPlaneConnect implements AutoCloseable
* @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
+ public double[] getPOSI(int ac) throws IOException
{
// Send request
ByteArrayOutputStream os = new ByteArrayOutputStream();
@@ -570,7 +585,7 @@ public class XPlaneConnect implements AutoCloseable
}
// Parse response
- float[] result = new float[7];
+ double[] result = new double[7];
ByteBuffer bb = ByteBuffer.wrap(data);
bb.order(ByteOrder.LITTLE_ENDIAN);
for(int i = 0; i < 7; ++i)
@@ -600,13 +615,13 @@ public class XPlaneConnect implements AutoCloseable
*
* @throws IOException If the command can not be sent.
*/
- public void sendPOSI(float[] values) throws IOException
+ public void sendPOSI(double[] values) throws IOException
{
sendPOSI(values, 0);
}
/**
- * Sets the position of the specified ac.
+ * Sets the position of the specified ac with double precision coordinates.
*
* @param values An array containing position elements as follows:
*
@@ -626,7 +641,7 @@ public class XPlaneConnect implements AutoCloseable
* @param ac The ac to set. 0 for the player ac.
* @throws IOException If the command can not be sent.
*/
- public void sendPOSI(float[] values, int ac) throws IOException
+ public void sendPOSI(double[] values, int ac) throws IOException
{
//Preconditions
if(values == null)
@@ -644,15 +659,22 @@ public class XPlaneConnect implements AutoCloseable
//Pad command values and convert to bytes
int i;
- ByteBuffer bb = ByteBuffer.allocate(28);
+ ByteBuffer bb = ByteBuffer.allocate(40);
bb.order(ByteOrder.LITTLE_ENDIAN);
for(i = 0; i < values.length; ++i)
{
- bb.putFloat(i * 4, values[i]);
+ if(i<3) /* lat/lon/height as double */
+ {
+ bb.putDouble(values[i]);
+ }
+ else
+ {
+ bb.putFloat((float)values[i]);
+ }
}
for(; i < 7; ++i)
{
- bb.putFloat(i * 4, -998);
+ bb.putFloat(-998);
}
//Build and send message
diff --git a/Java/src/discovery/Beacon.java b/Java/src/discovery/Beacon.java
new file mode 100644
index 0000000..c5a33c1
--- /dev/null
+++ b/Java/src/discovery/Beacon.java
@@ -0,0 +1,45 @@
+package gov.nasa.xpc.discovery;
+
+import java.net.InetAddress;
+
+
+public class Beacon {
+
+ private InetAddress xplaneAddress;
+ private int pluginPort;
+ private String pluginVersion;
+ private int xPlaneVersion;
+
+
+ public Beacon(InetAddress xplaneAddress, int pluginPort, String pluginVersion, int xPlaneVersion) {
+ this.xplaneAddress = xplaneAddress;
+ this.pluginPort = pluginPort;
+ this.pluginVersion = pluginVersion;
+ this.xPlaneVersion = xPlaneVersion;
+ }
+
+ public String getHost() {
+ return xplaneAddress.getHostAddress();
+ }
+
+ public String getPluginVersion() {
+ return pluginVersion;
+ }
+
+ public String getXplaneVersion() {
+ return String.format("%.2f", xPlaneVersion / 100.0);
+ }
+
+ public int getPluginPort() {
+ return pluginPort;
+ }
+
+ public InetAddress getXplaneAddress() {
+ return xplaneAddress;
+ }
+
+ @Override
+ public String toString() {
+ return "host: " + getHost() + ":" + getPluginPort() +" version: " + getPluginVersion() + " X-Plane Version: " + getXplaneVersion();
+ }
+}
diff --git a/Java/src/discovery/BeaconParser.java b/Java/src/discovery/BeaconParser.java
new file mode 100644
index 0000000..1f7805f
--- /dev/null
+++ b/Java/src/discovery/BeaconParser.java
@@ -0,0 +1,54 @@
+package gov.nasa.xpc.discovery;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Parser class that parses the UDP discovery BECN packet. The format of the packet is
+ * - 4 bytes: BECN
+ * - 1 byte: 0
+ * - 2 bytes: XPlaneConnect server port e.g. '49009'
+ * - 4 bytes: X-Plane version e.g. '11260'
+ * - null terminated string: XPlaneConnect plugin version e.g. '1.3-rc.1'
+ */
+public class BeaconParser {
+
+ private static String BECN = "BECN";
+
+ private static int XPC_PORT_OFFSET = BECN.length() + 1;
+ private static int XPC_PORT_LEN = 2;
+ private static int XPC_VERSION_OFFSET = XPC_PORT_OFFSET + XPC_PORT_LEN;
+ private static int XPC_VERSION_LEN = 4;
+ private static int XPC_PLUGIN_VERSION_OFFSET = XPC_VERSION_OFFSET + XPC_VERSION_LEN;
+
+
+ public Beacon readBCN(DatagramPacket packet) throws IOException {
+ if (packet.getLength() < BECN.length()) {
+ throw new IOException("BECN response too short");
+ }
+
+ byte[] data = packet.getData();
+
+ String command = new String(data, 0, BECN.length());
+ if (!command.equals(BECN)) {
+ throw new IOException("Expected " + BECN + " got '" + command + "'");
+ }
+
+ InetAddress address = packet.getAddress();
+ ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ // 2 bytes: port as short + converted to an int
+ int port = bb.getShort(XPC_PORT_OFFSET) & 0xffff;
+
+ // 4 bytes: x plane version as int
+ int version = bb.getInt(XPC_VERSION_OFFSET);
+
+ // plugin version
+ String pluginVersion = new String(data, XPC_PLUGIN_VERSION_OFFSET, packet.getLength() - XPC_PLUGIN_VERSION_OFFSET);
+
+ return new Beacon(address, port, pluginVersion.trim(), version);
+ }
+}
\ No newline at end of file
diff --git a/Java/src/discovery/BeaconReceivedListener.java b/Java/src/discovery/BeaconReceivedListener.java
new file mode 100644
index 0000000..cd2e0d8
--- /dev/null
+++ b/Java/src/discovery/BeaconReceivedListener.java
@@ -0,0 +1,10 @@
+package gov.nasa.xpc.discovery;
+
+public interface BeaconReceivedListener {
+
+ /**
+ * Called every time a BECN packet is received
+ * @param beacon The parsed beacon
+ */
+ void onBeaconReceived(Beacon beacon);
+}
diff --git a/Java/src/discovery/DiscoveryConnectionCallback.java b/Java/src/discovery/DiscoveryConnectionCallback.java
new file mode 100644
index 0000000..1292826
--- /dev/null
+++ b/Java/src/discovery/DiscoveryConnectionCallback.java
@@ -0,0 +1,14 @@
+package gov.nasa.xpc.discovery;
+
+import gov.nasa.xpc.XPlaneConnect;
+
+public interface DiscoveryConnectionCallback {
+
+ /**
+ * Helper callback called when a 1st XPlanePlugin is discovered. When the 1st packet is received, an XPlaneConnect
+ * instance is created and the discovery is stopped.
+ *
+ * @param xpc The XPlaneConnect instance configured with the discovered
+ */
+ void onConnectionEstablished(XPlaneConnect xpc);
+}
diff --git a/Java/src/discovery/XPlaneConnectDiscovery.java b/Java/src/discovery/XPlaneConnectDiscovery.java
new file mode 100644
index 0000000..0a92dce
--- /dev/null
+++ b/Java/src/discovery/XPlaneConnectDiscovery.java
@@ -0,0 +1,94 @@
+package gov.nasa.xpc.discovery;
+
+import gov.nasa.xpc.XPlaneConnect;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.net.SocketException;
+
+
+/**
+ * The XPlaneConnectDiscovery joins a UDP multi cast group and listens for BECN packets published by
+ * XPlaneConnect server plugin. It allows to clients to discover XPlane instances and be sure that the XPlanePlugin is
+ * installed.
+ */
+public class XPlaneConnectDiscovery implements AutoCloseable {
+
+ private static int DEFAULT_PORT = 49710;
+ private static String DEFAULT_ADDRESS = "239.255.1.1";
+
+ private BeaconReceivedListener mListener;
+
+ private MulticastSocket socket = null;
+ private byte[] buf = new byte[256];
+
+ private int mPort;
+ private String mAddress;
+ private BeaconParser parser = new BeaconParser();
+
+ private XPlaneConnectDiscovery(int port, String address) {
+ mPort = port;
+ mAddress = address;
+ }
+
+ public XPlaneConnectDiscovery() {
+ this(DEFAULT_PORT, DEFAULT_ADDRESS);
+ }
+
+ public void start(DiscoveryConnectionCallback callback) throws IOException {
+ onBeaconReceived(beacon -> {
+ this.close();
+
+ try {
+ XPlaneConnect xpc = new XPlaneConnect(beacon);
+ callback.onConnectionEstablished(xpc);
+ } catch (SocketException e) {
+ System.err.println("Could not connect to a discovered XplaneConnect " +beacon+ ": " + e.getMessage());
+ }
+ });
+ start();
+
+ }
+
+ public void start() throws IOException {
+ System.out.println("Starting XPlane Connect discovery");
+ socket = new MulticastSocket(mPort);
+ InetAddress group = InetAddress.getByName(mAddress);
+ socket.joinGroup(group);
+ while (socket != null) {
+ DatagramPacket packet = new DatagramPacket(buf, buf.length);
+ socket.receive(packet);
+ if (mListener == null) {
+ continue;
+ }
+ try {
+ Beacon beacon = parser.readBCN(packet);
+ mListener.onBeaconReceived(beacon);
+ } catch (IOException ex) {
+ System.err.println("Received packet on discovery group but could not parse it: " + ex);
+ }
+ }
+
+ close();
+ System.out.println("XPlane Connect discovery ended");
+ }
+
+
+ public void onBeaconReceived(BeaconReceivedListener mListener) {
+ this.mListener = mListener;
+ }
+
+ @Override
+ public void close() {
+ if (socket != null) {
+ socket.close();
+ socket = null;
+ }
+ }
+
+ public void stop() {
+ close();
+ }
+}
diff --git a/MATLAB/+XPlaneConnect/XPlaneConnect.jar b/MATLAB/+XPlaneConnect/XPlaneConnect.jar
index f9d9e0b..a182eb3 100644
Binary files a/MATLAB/+XPlaneConnect/XPlaneConnect.jar and b/MATLAB/+XPlaneConnect/XPlaneConnect.jar differ
diff --git a/MATLAB/+XPlaneConnect/pauseSim.m b/MATLAB/+XPlaneConnect/pauseSim.m
index da44b71..c33dcf0 100644
--- a/MATLAB/+XPlaneConnect/pauseSim.m
+++ b/MATLAB/+XPlaneConnect/pauseSim.m
@@ -2,7 +2,7 @@ function pauseSim( pause, socket )
%pauseSim Pauses or unpauses X-Plane.
%
%Inputs
-% pause: binary value 0=run, 1=pause
+% pause: int 0= unpause all, 1= pause all, 2= switch all, 100:119= pause a/c 0:19, 200:219= unpause a/c 0:19
% socket (optional): The client to use when sending the command.
%
%Use
@@ -34,4 +34,4 @@ pause = int32(pause);
%% Send command
socket.pauseSim(pause);
-end
\ No newline at end of file
+end
diff --git a/MATLAB/+XPlaneConnect/sendPOSI.m b/MATLAB/+XPlaneConnect/sendPOSI.m
index bff56fb..a8c84a0 100644
--- a/MATLAB/+XPlaneConnect/sendPOSI.m
+++ b/MATLAB/+XPlaneConnect/sendPOSI.m
@@ -1,6 +1,6 @@
function sendPOSI( posi, ac, socket )
% sendPOSI Sets the position of the specified aircraft.
-%
+%
% Inputs
% posi: Position array where the elements are as follows:
% 1. Latitiude (deg)
@@ -12,14 +12,14 @@ function sendPOSI( posi, ac, socket )
% 7. Gear (0=up, 1=down)
% acft (optional): The aircraft to set. 0 for the player aircraft.
% socket (optional): The client to use when sending the command.
-%
+%
% Use
% 1. import XPlaneConnect.*;
% 2. sendPOSI([37.5242422, -122.06899, 2500, 0, 0, 0, 1], 1);
-%
+%
% Note: send the value -998 to not overwrite that parameter. That is, if
% -998 is sent, the parameter will stay at the current X-Plane value.
-%
+%
% Contributors
% [CT] Christopher Teubert (SGT, Inc.)
% christopher.a.teubert@nasa.gov
@@ -33,14 +33,14 @@ global clients;
if ~exist('socket', 'var')
assert(isequal(length(clients) < 2, 1), '[sendPOSI] ERROR: Multiple clients open. You must specify which client to use.');
if isempty(clients)
- socket = openUDP();
+ socket = openUDP();
else
socket = clients(1);
end
end
%% Validate input
-posi = single(posi);
+posi = double(posi);
if ~exist('ac', 'var')
ac = 0;
end
@@ -49,4 +49,4 @@ ac = logical(ac);
%% Send command
socket.sendPOSI(posi, ac);
-end
\ No newline at end of file
+end
diff --git a/MATLAB/MonitorExample/MonitorExample.m b/MATLAB/MonitorExample/MonitorExample.m
index ffbdc14..63e8cd4 100644
--- a/MATLAB/MonitorExample/MonitorExample.m
+++ b/MATLAB/MonitorExample/MonitorExample.m
@@ -11,9 +11,9 @@ 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
+closeUDP(Socket);
diff --git a/Python/src/playbackExample.py b/Python/src/playbackExample.py
index dfa4f52..21730b8 100644
--- a/Python/src/playbackExample.py
+++ b/Python/src/playbackExample.py
@@ -33,7 +33,7 @@ def playback(path, interval):
print "Unable to open file."
return
- with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client:
+ with xpc.XPlaneConnect("localhost", 49009, 0, 1000) as client:
print "Starting Playback..."
for line in fd:
try:
@@ -56,7 +56,7 @@ def printMenu(title, opts):
return int(raw_input("Please select and option: "))
def ex():
- print "X-Plane Connect Playback Example [Version 1.2.0]"
+ print "X-Plane Connect Playback Example [Version 1.3-rc.1]"
print "(c) 2013-2015 United States Government as represented by the Administrator"
print "of the National Aeronautics and Space Administration. All Rights Reserved."
@@ -79,4 +79,4 @@ def ex():
print "Unrecognized option."
if __name__ == "__main__":
- ex()
\ No newline at end of file
+ ex()
diff --git a/Python/src/xpc.py b/Python/src/xpc.py
index e2b5c5e..71b7eb2 100644
--- a/Python/src/xpc.py
+++ b/Python/src/xpc.py
@@ -8,7 +8,7 @@ class XPlaneConnect(object):
# Basic Functions
def __init__(self, xpHost = 'localhost', xpPort = 49009, port = 0, timeout = 100):
'''Sets up a new connection to an X-Plane Connect plugin running in X-Plane.
-
+
Args:
xpHost: The hostname of the machine running X-Plane.
xpPort: The port on which the XPC plugin is listening. Usually 49007.
@@ -33,7 +33,7 @@ class XPlaneConnect(object):
# Setup XPlane IP and port
self.xpDst = (xpIP, xpPort)
- # Create and bind socket
+ # Create and bind socket
clientAddr = ("0.0.0.0", port)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.socket.bind(clientAddr)
@@ -71,18 +71,18 @@ class XPlaneConnect(object):
# Configuration
def setCONN(self, port):
'''Sets the port on which the client sends and receives data.
-
+
Args:
port: The new port to use.
'''
- #Validate parameters
+ #Validate parameters
if port < 0 or port > 65535:
raise ValueError("The specified port is not a valid port number.")
#Send command
buffer = struct.pack("<4sxH", "CONN", port)
self.sendUDP(buffer)
-
+
#Rebind socket
clientAddr = ("0.0.0.0", port)
timeout = self.socket.gettimeout();
@@ -96,12 +96,14 @@ class XPlaneConnect(object):
def pauseSim(self, pause):
'''Pauses or un-pauses the physics simulation engine in X-Plane.
-
+
Args:
- pause: True to pause the simulation; False to resume.
+ pause: True to pause the simulation for all a/c; False to resume for all a/c.
+ 2 to switch the status for all a/c. 100:119 to pause a/c 0:19. 200:219 to
+ resume a/c 0:19
'''
pause = int(pause)
- if pause < 0 or pause > 2:
+ if pause < 0 or (pause > 2 and pause < 100) or (pause > 119 and pause < 200) or pause > 219 :
raise ValueError("Invalid argument for pause command.")
buffer = struct.pack("<4sxB", "SIMU", pause)
@@ -110,7 +112,7 @@ class XPlaneConnect(object):
# X-Plane UDP Data
def readDATA(self):
'''Reads X-Plane data.
-
+
Returns: A 2 dimensional array containing 0 or more rows of data. Each array
in the result will have 9 elements, the first of which is the row number which
that array represents data for, and the rest of which are the data elements in
@@ -127,7 +129,7 @@ class XPlaneConnect(object):
def sendDATA(self, data):
'''Sends X-Plane data over the underlying UDP socket.
-
+
Args:
data: An array of values representing data rows to be set. Each array in `data`
should have 9 elements, the first of which is a row number in the range (0-134),
@@ -143,7 +145,7 @@ class XPlaneConnect(object):
buffer += struct.pack(" 20:
raise ValueError("Aircraft number must be between 0 and 20.")
+ # FIXME update this to the 64-bit double lat/lon/h
# Pack message
buffer = struct.pack("<4sxB", "POSI", ac)
for i in range(7):
@@ -264,8 +267,8 @@ class XPlaneConnect(object):
# Send
self.sendUDP(buffer)
-
- # DREF Manipulation
+
+ # DREF Manipulation
def sendDREF(self, dref, values):
'''Sets the specified dataref to the specified value.
@@ -294,7 +297,7 @@ class XPlaneConnect(object):
raise ValueError("dref must be a non-empty string less than 256 characters.")
if value == None:
raise ValueError("value must be a scalar or sequence of floats.")
-
+
# Pack message
if hasattr(value, "__len__"):
if len(value) > 255:
@@ -310,7 +313,7 @@ class XPlaneConnect(object):
def getDREF(self, dref):
'''Gets the value of an X-Plane dataref.
-
+
Args:
dref: The name of the dataref to get.
diff --git a/Python3/src/basicExample.py b/Python3/src/basicExample.py
new file mode 100644
index 0000000..f381e5d
--- /dev/null
+++ b/Python3/src/basicExample.py
@@ -0,0 +1,72 @@
+from time import sleep
+import xpc
+
+def ex():
+ print "X-Plane Connect example script"
+ print "Setting up simulation"
+ with xpc.XPlaneConnect() as client:
+ # Verify connection
+ try:
+ # If X-Plane does not respond to the request, a timeout error
+ # will be raised.
+ client.getDREF("sim/test/test_float")
+ except:
+ print "Error establishing connection to X-Plane."
+ print "Exiting..."
+ return
+
+ # Set position of the player aircraft
+ print "Setting position"
+ # Lat Lon Alt Pitch Roll Yaw Gear
+ posi = [37.524, -122.06899, 2500, 0, 0, 0, 1]
+ client.sendPOSI(posi)
+
+ # Set position of a non-player aircraft
+ print "Setting NPC position"
+ # Lat Lon Alt Pitch Roll Yaw Gear
+ posi = [37.52465, -122.06899, 2500, 0, 20, 0, 1]
+ client.sendPOSI(posi, 1)
+
+ # Set angle of attack, velocity, and orientation using the DATA command
+ print "Setting orientation"
+ data = [\
+ [18, 0, -998, 0, -998, -998, -998, -998, -998],\
+ [ 3, 130, 130, 130, 130, -998, -998, -998, -998],\
+ [16, 0, 0, 0, -998, -998, -998, -998, -998]\
+ ]
+ client.sendDATA(data)
+
+ # Set control surfaces and throttle of the player aircraft using sendCTRL
+ print "Setting controls"
+ ctrl = [0.0, 0.0, 0.0, 0.8]
+ client.sendCTRL(ctrl)
+
+ # Pause the sim
+ print "Pausing"
+ client.pauseSim(True)
+ sleep(2)
+
+ # Toggle pause state to resume
+ print "Resuming"
+ client.pauseSim(False)
+
+ # Stow landing gear using a dataref
+ print "Stowing gear"
+ gear_dref = "sim/cockpit/switches/gear_handle_status"
+ client.sendDREF(gear_dref, 0)
+
+ # Let the sim run for a bit.
+ sleep(4)
+
+ # Make sure gear was stowed successfully
+ gear_status = client.getDREF(gear_dref)
+ if gear_status[0] == 0:
+ print "Gear stowed"
+ else:
+ print "Error stowing gear"
+
+ print "End of Python client example"
+ raw_input("Press any key to exit...")
+
+if __name__ == "__main__":
+ ex()
\ No newline at end of file
diff --git a/Python3/src/monitorExample.py b/Python3/src/monitorExample.py
new file mode 100644
index 0000000..c4cdb48
--- /dev/null
+++ b/Python3/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/Python3/src/playbackExample.py b/Python3/src/playbackExample.py
new file mode 100644
index 0000000..dfa4f52
--- /dev/null
+++ b/Python3/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/Python3/src/xpc.py b/Python3/src/xpc.py
new file mode 100644
index 0000000..ddf2be9
--- /dev/null
+++ b/Python3/src/xpc.py
@@ -0,0 +1,437 @@
+import socket
+import struct
+
+
+class XPlaneConnect(object):
+ """XPlaneConnect (XPC) facilitates communication to and from the XPCPlugin."""
+ socket = None
+
+ # Basic Functions
+ def __init__(self, xpHost='localhost', xpPort=49009, port=0, timeout=100):
+ """Sets up a new connection to an X-Plane Connect plugin running in X-Plane.
+
+ Args:
+ xpHost: The hostname of the machine running X-Plane.
+ xpPort: The port on which the XPC plugin is listening. Usually 49007.
+ port: The port which will be used to send and receive data.
+ timeout: The period (in milliseconds) after which read attempts will fail.
+ """
+
+ # Validate parameters
+ xpIP = None
+ try:
+ xpIP = socket.gethostbyname(xpHost)
+ except:
+ raise ValueError("Unable to resolve xpHost.")
+
+ if xpPort < 0 or xpPort > 65535:
+ raise ValueError("The specified X-Plane port is not a valid port number.")
+ if port < 0 or port > 65535:
+ raise ValueError("The specified port is not a valid port number.")
+ if timeout < 0:
+ raise ValueError("timeout must be non-negative.")
+
+ # Setup XPlane IP and port
+ self.xpDst = (xpIP, xpPort)
+
+ # Create and bind socket
+ clientAddr = ("0.0.0.0", port)
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ self.socket.bind(clientAddr)
+ timeout /= 1000.0
+ self.socket.settimeout(timeout)
+
+ def __del__(self):
+ self.close()
+
+ # Define __enter__ and __exit__ to support the `with` construct.
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.close()
+
+ def close(self):
+ """Closes the specified connection and releases resources associated with it."""
+ if self.socket is not None:
+ self.socket.close()
+ self.socket = None
+
+ def sendUDP(self, buffer):
+ """Sends a message over the underlying UDP socket."""
+ # Preconditions
+ if(len(buffer) == 0):
+ raise ValueError("sendUDP: buffer is empty.")
+
+ self.socket.sendto(buffer, 0, self.xpDst)
+
+ def readUDP(self):
+ """Reads a message from the underlying UDP socket."""
+ return self.socket.recv(16384)
+
+ # Configuration
+ def setCONN(self, port):
+ """Sets the port on which the client sends and receives data.
+
+ Args:
+ port: The new port to use.
+ """
+
+ #Validate parameters
+ if port < 0 or port > 65535:
+ raise ValueError("The specified port is not a valid port number.")
+
+ #Send command
+ buffer = struct.pack(b"<4sxH", b"CONN", port)
+ self.sendUDP(buffer)
+
+ #Rebind socket
+ clientAddr = ("0.0.0.0", port)
+ timeout = self.socket.gettimeout()
+ self.socket.close()
+ self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ self.socket.bind(clientAddr)
+ self.socket.settimeout(timeout)
+
+ #Read response
+ buffer = self.socket.recv(1024)
+
+ def pauseSim(self, pause):
+ """Pauses or un-pauses the physics simulation engine in X-Plane.
+
+ Args:
+ pause: True to pause the simulation; False to resume.
+ """
+ pause = int(pause)
+ if pause < 0 or pause > 2:
+ raise ValueError("Invalid argument for pause command.")
+
+ buffer = struct.pack(b"<4sxB", b"SIMU", pause)
+ self.sendUDP(buffer)
+
+ # X-Plane UDP Data
+ def readDATA(self):
+ """Reads X-Plane data.
+
+ Returns: A 2 dimensional array containing 0 or more rows of data. Each array
+ in the result will have 9 elements, the first of which is the row number which
+ that array represents data for, and the rest of which are the data elements in
+ that row.
+ """
+ buffer = self.readUDP()
+ if len(buffer) < 6:
+ return None
+ rows = (len(buffer) - 5) / 36
+ data = []
+ for i in range(rows):
+ data.append(struct.unpack_from(b"9f", buffer, 5 + 36*i))
+ return data
+
+ def sendDATA(self, data):
+ """Sends X-Plane data over the underlying UDP socket.
+
+ Args:
+ data: An array of values representing data rows to be set. Each array in `data`
+ should have 9 elements, the first of which is a row number in the range (0-134),
+ and the rest of which are the values to set for that data row.
+ """
+ if len(data) > 134:
+ raise ValueError("Too many rows in data.")
+
+ buffer = struct.pack(b"<4sx", b"DATA")
+ for row in data:
+ if len(row) != 9:
+ raise ValueError("Row does not contain exactly 9 values. <" + str(row) + ">")
+ buffer += struct.pack(b" 7:
+ raise ValueError("Must have between 0 and 7 items in values.")
+ if ac < 0 or ac > 20:
+ raise ValueError("Aircraft number must be between 0 and 20.")
+
+ # Pack message
+ buffer = struct.pack(b"<4sxB", b"POSI", ac)
+ for i in range(7):
+ val = -998
+ if i < len(values):
+ val = values[i]
+ buffer += struct.pack(b" 7:
+ raise ValueError("Must have between 0 and 6 items in values.")
+ if ac < 0 or ac > 20:
+ raise ValueError("Aircraft number must be between 0 and 20.")
+
+ # Pack message
+ buffer = struct.pack(b"<4sx", b"CTRL")
+ for i in range(6):
+ val = -998
+ if i < len(values):
+ val = values[i]
+ if i == 4:
+ val = -1 if (abs(val + 998) < 1e-4) else val
+ buffer += struct.pack(b"b", val)
+ else:
+ buffer += struct.pack(b" 255:
+ raise ValueError("dref must be a non-empty string less than 256 characters.")
+
+ if value is None:
+ raise ValueError("value must be a scalar or sequence of floats.")
+
+ # Pack message
+ if hasattr(value, "__len__"):
+ if len(value) > 255:
+ raise ValueError("value must have less than 256 items.")
+ fmt = " ViewType.FullscreenNoHud:
+ raise ValueError("Unknown view command.")
+
+ # Pack buffer
+ buffer = struct.pack(b"<4sxi", b"VIEW", view)
+
+ # Send message
+ self.sendUDP(buffer)
+
+ def sendWYPT(self, op, points):
+ """Adds, removes, or clears waypoints. Waypoints are three dimensional points on or
+ above the Earth's surface that are represented visually in the simulator. Each
+ point consists of a latitude and longitude expressed in fractional degrees and
+ an altitude expressed as meters above sea level.
+
+ Args:
+ op: The operation to perform. Pass `1` to add waypoints,
+ `2` to remove waypoints, and `3` to clear all waypoints.
+ points: A sequence of floating point values representing latitude, longitude, and
+ altitude triples. The length of this array should always be divisible by 3.
+ """
+ if op < 1 or op > 3:
+ raise ValueError("Invalid operation specified.")
+ if len(points) % 3 != 0:
+ raise ValueError("Invalid points. Points should be divisible by 3.")
+ if len(points) / 3 > 255:
+ raise ValueError("Too many points. You can only send 255 points at a time.")
+
+ if op == 3:
+ buffer = struct.pack(b"<4sxBB", b"WYPT", 3, 0)
+ else:
+ buffer = struct.pack(("<4sxBB" + str(len(points)) + "f").encode(), b"WYPT", op, len(points), *points)
+ self.sendUDP(buffer)
+
+
+class ViewType(object):
+ Forwards = 73
+ Down = 74
+ Left = 75
+ Right = 76
+ Back = 77
+ Tower = 78
+ Runway = 79
+ Chase = 80
+ Follow = 81
+ FollowWithPanel = 82
+ Spot = 83
+ FullscreenWithHud = 84
+ FullscreenNoHud = 85
diff --git a/Python3/xplaneConnect.pyproj b/Python3/xplaneConnect.pyproj
new file mode 100644
index 0000000..446a9ff
--- /dev/null
+++ b/Python3/xplaneConnect.pyproj
@@ -0,0 +1,58 @@
+
+
+
+ Debug
+ 2.0
+ 3c7a940d-17c8-4e91-882f-9bc8b1d2f54b
+ .
+ src\basicExample.py
+
+
+ .
+ .
+ XPlaneConnect
+ XPlaneConnect
+ {9a7a9026-48c1-4688-9d5d-e5699d47d074}
+ 2.7
+
+
+ true
+ false
+
+
+ true
+ false
+
+
+
+ Code
+
+
+ Code
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+ 10.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Python3/xplaneConnect.sln b/Python3/xplaneConnect.sln
new file mode 100644
index 0000000..862da8d
--- /dev/null
+++ b/Python3/xplaneConnect.sln
@@ -0,0 +1,24 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "XPlaneConnect", "XPlaneConnect.pyproj", "{3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}"
+EndProject
+Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "Tests", "..\TestScripts\Python Tests\Tests.pyproj", "{6931EBB2-4E01-4C5A-86B6-668C0E75051B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3C7A940D-17C8-4E91-882F-9BC8B1D2F54B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6931EBB2-4E01-4C5A-86B6-668C0E75051B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6931EBB2-4E01-4C5A-86B6-668C0E75051B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/README.md b/README.md
index 8240a07..af98065 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ the next official release.
### Notices
-Copyright ©2013-2017 United States Government as represented by the Administrator
+Copyright ©2013-2018 United States Government as represented by the Administrator
of the National Aeronautics and Space Administration. All Rights Reserved.
No Warranty: THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
diff --git a/TestScripts/C Tests/CtrlTests.h b/TestScripts/C Tests/CtrlTests.h
index 572ea07..83e7d36 100644
--- a/TestScripts/C Tests/CtrlTests.h
+++ b/TestScripts/C Tests/CtrlTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef CTRLTESTS_H
#define CTRLTESTS_H
@@ -23,7 +23,7 @@ int doCTRLTest(XPCSocket *sock, char* drefs[7], float values[], int size, int ac
{
result = getDREFs(*sock, drefs, data, 7, sizes);
}
-
+
if (result < 0)
{
return -1;
@@ -102,7 +102,7 @@ int basicCTRLTest(char** drefs, int ac)
CTRL[4] = -998;
CTRL[5] = -998;
result = doCTRLTest(&sock, drefs, CTRL, 6, ac, expected);
-
+
pauseSim(sock, 0);
closeUDP(sock);
if (result < 0)
@@ -200,4 +200,4 @@ 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
+#endif
diff --git a/TestScripts/C Tests/DataTests.h b/TestScripts/C Tests/DataTests.h
index 2bf1564..4fde0ab 100644
--- a/TestScripts/C Tests/DataTests.h
+++ b/TestScripts/C Tests/DataTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef DATATESTS_H
#define DATATESTS_H
@@ -59,4 +59,4 @@ int testDATA()
return 0;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/DrefTests.h b/TestScripts/C Tests/DrefTests.h
index 50338a7..c64ce6d 100644
--- a/TestScripts/C Tests/DrefTests.h
+++ b/TestScripts/C Tests/DrefTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef DREFTESTS_H
#define DREFTESTS_H
@@ -244,4 +244,4 @@ int testDREF()
return doDREFTest(drefs, values, expected, 6, sizes);
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/PosiTests.h b/TestScripts/C Tests/PosiTests.h
index 104e517..fc084ec 100644
--- a/TestScripts/C Tests/PosiTests.h
+++ b/TestScripts/C Tests/PosiTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef POSITESTS_H
#define POSITESTS_H
@@ -6,7 +6,7 @@
#include "Test.h"
#include "xplaneConnect.h"
-int doPOSITest(char* drefs[7], float values[], int size, int ac, float expected[7])
+int doPOSITest(char* drefs[7], double values[], int size, int ac, double expected[7])
{
float* data[7];
int sizes[7];
@@ -32,15 +32,15 @@ int doPOSITest(char* drefs[7], float values[], int size, int ac, float expected[
}
// Test values
- float actual[7];
+ double actual[7];
for (int i = 0; i < 7; ++i)
{
actual[i] = data[i][0];
}
- return compareArray(expected, actual, 7);
+ return compareDoubleArray(expected, actual, 7);
}
-int doGETPTest(float values[7], int ac, float expected[7])
+int doGETPTest(double values[7], int ac, double expected[7])
{
// Execute Test
float actual[7];
@@ -70,8 +70,8 @@ int doGETPTest(float values[7], int ac, float expected[7])
int basicPOSITest(char** drefs, int ac)
{
// Set psoition and initial orientation
- float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
- float expected[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
+ double POSI[7] = { 37.524, -122.06899, 2500, 0, 0, 0, 1 };
+ double expected[7] = { 37.524, -122.06899, 2500, 0, 0, 0, 1 };
int result = doPOSITest(drefs, POSI, 7, ac, expected);
if (result < 0)
{
@@ -79,9 +79,9 @@ int basicPOSITest(char** drefs, int ac)
}
// Set orientation
- POSI[0] = -998.0F;
- POSI[1] = -998.0F;
- POSI[2] = -998.0F;
+ POSI[0] = -998.0;
+ POSI[1] = -998.0;
+ POSI[2] = -998.0;
POSI[3] = 5.0F;
POSI[4] = -5.0F;
POSI[5] = 10.0F;
@@ -101,10 +101,10 @@ int basicPOSITest(char** drefs, int ac)
expected[0] = loc[0][0];
expected[1] = loc[1][0];
expected[2] = loc[2][0];
- expected[3] = 5.0F;
- expected[4] = -5.0F;
- expected[5] = 10.0F;
- expected[6] = 0.0F;
+ expected[3] = 5.0;
+ expected[4] = -5.0;
+ expected[5] = 10.0;
+ expected[6] = 0.0;
result = doPOSITest(drefs, POSI, 7, ac, expected);
if (result < 0)
{
@@ -145,14 +145,14 @@ int testPOSI_NonPlayer()
int testGetPOSI_Player()
{
- float POSI[7] = { 37.524F, -122.06899F, 2500, 0, 0, 0, 1 };
+ double POSI[7] = { 37.524, -122.06899, 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 };
+ double POSI[7] = { 37.624, -122.06899, 1500, 0, 0, 0, 1 };
return doGETPTest(POSI, 3, POSI);
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/SimuTests.h b/TestScripts/C Tests/SimuTests.h
index 0d12012..f4e81e4 100644
--- a/TestScripts/C Tests/SimuTests.h
+++ b/TestScripts/C Tests/SimuTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef SIMUTESTS_H
#define SIMIUTESTS_H
@@ -16,7 +16,7 @@ int doSIMUTest(int value, float expected)
int result = pauseSim(sock, value);
if (result >= 0)
{
- result = getDREF(sock, dref, &actual, &size);
+ result = getDREF(sock, dref, actual, &size);
}
closeUDP(sock);
if (result < 0)
@@ -78,4 +78,4 @@ int testSIMU_Toggle()
return 0;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/Test.c b/TestScripts/C Tests/Test.c
index 11ddc48..bff0c19 100644
--- a/TestScripts/C Tests/Test.c
+++ b/TestScripts/C Tests/Test.c
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#include "Test.h"
@@ -56,4 +56,28 @@ int compareArrays(float* expected[], int esizes[], float* actual[], int asizes[]
}
}
return 0;
-}
\ No newline at end of file
+}
+
+int compareDoubleArray(double expected[], double actual[], int size)
+{
+ return compareDoubleArrays(&expected, &size, &actual, &size, 1);
+}
+
+int compareDoubleArrays(double* expected[], int esizes[], double* actual[], int asizes[], int count)
+{
+ for (int i = 0; i < count; ++i)
+ {
+ if (esizes[i] != asizes[i])
+ {
+ return -100 - i;
+ }
+ for (int j = 0; j < esizes[i]; ++j)
+ {
+ if (!feq(actual[i][j], expected[i][j]) && !isnan(expected[i][j]))
+ {
+ return -1000 - i * 100 - j;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/TestScripts/C Tests/Test.h b/TestScripts/C Tests/Test.h
index 5c40d40..26b709f 100644
--- a/TestScripts/C Tests/Test.h
+++ b/TestScripts/C Tests/Test.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef TESTRUNNER_H
#define TESTRUNNER_H
@@ -33,5 +33,7 @@ void runTest(int(*test)(), char* name);
int compareFloat(float expected, float actual);
int compareArray(float expected[], float actual[], int size);
int compareArrays(float* expected[], int esizes[], float* actual[], int asizes[], int count);
+int compareDoubleArray(double expected[], double actual[], int size);
+int compareDoubleArrays(double* expected[], int esizes[], double* actual[], int asizes[], int count);
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/TextTests.h b/TestScripts/C Tests/TextTests.h
index badf16e..987fc15 100644
--- a/TestScripts/C Tests/TextTests.h
+++ b/TestScripts/C Tests/TextTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef TEXTTESTS_H
#define TEXTTESTS_H
@@ -29,4 +29,4 @@ int testTEXT()
return 0;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/UDPTests.h b/TestScripts/C Tests/UDPTests.h
index 3c3d3fa..1a4e3b6 100644
--- a/TestScripts/C Tests/UDPTests.h
+++ b/TestScripts/C Tests/UDPTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef UDPTESTS_H
#define UDPTESTS_H
@@ -52,4 +52,4 @@ int testCONN()
return 0;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/ViewTests.h b/TestScripts/C Tests/ViewTests.h
index a25c14f..58013c9 100644
--- a/TestScripts/C Tests/ViewTests.h
+++ b/TestScripts/C Tests/ViewTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef VIEWTESTS_H
#define VIEWTESTS_H
@@ -70,4 +70,4 @@ int testView()
return doViewTest(XPC_VIEW_CHASE, XPC_VDREF_CHASE);
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/WyptTests.h b/TestScripts/C Tests/WyptTests.h
index fce3c2c..c04ecc9 100644
--- a/TestScripts/C Tests/WyptTests.h
+++ b/TestScripts/C Tests/WyptTests.h
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#ifndef WYPTTESTS_H
#define WYPTTESTS_H
@@ -39,4 +39,4 @@ int testWYPT()
return 0;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/TestScripts/C Tests/main.c b/TestScripts/C Tests/main.c
index ec2b752..172ac09 100644
--- a/TestScripts/C Tests/main.c
+++ b/TestScripts/C Tests/main.c
@@ -1,4 +1,4 @@
-//Copyright (c) 2013-2015 United States Government as represented by the Administrator of the
+//Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
//National Aeronautics and Space Administration. All Rights Reserved.
#include "Test.h"
#include "UDPTests.h"
@@ -13,7 +13,7 @@
int main(int argc, const char * argv[]) {
printf("XPC Tests-c ");
-
+
#ifdef _WIN32
printf("(Windows)\n");
#elif (__APPLE__)
@@ -23,7 +23,7 @@ int main(int argc, const char * argv[]) {
#else
printf("(Unable to determine operating system) \n")
#endif
-
+
// Basic Networking
runTest(testOpen, "open");
crossPlatformUSleep(SLEEP_AMOUNT);
@@ -78,11 +78,10 @@ int main(int argc, const char * argv[]) {
// setConn
crossPlatformUSleep(SLEEP_AMOUNT);
runTest(testCONN, "CONN");
-
+
printf( "----------------\nTest Summary\n\tFailed: %i\n\tPassed: %i\n", testFailed, testPassed );
printf("Press any key to exit.");
getchar();
-
+
return 0;
}
-
diff --git a/TestScripts/Java Tests/XPlaneConnectTest.java b/TestScripts/Java Tests/XPlaneConnectTest.java
index 2e94bb5..c58fd04 100644
--- a/TestScripts/Java Tests/XPlaneConnectTest.java
+++ b/TestScripts/Java Tests/XPlaneConnectTest.java
@@ -542,15 +542,15 @@ public class XPlaneConnectTest
public void testSendPOSI() throws IOException
{
String[] drefs = {
- "sim/flightmodel/position/latitude",
- "sim/flightmodel/position/longitude",
- "sim/flightmodel/position/y_agl",
- "sim/flightmodel/position/phi",
- "sim/flightmodel/position/theta",
- "sim/flightmodel/position/psi",
- "sim/cockpit/switches/gear_handle_status"
+ "sim/flightmodel/position/latitude",
+ "sim/flightmodel/position/longitude",
+ "sim/flightmodel/position/y_agl",
+ "sim/flightmodel/position/phi",
+ "sim/flightmodel/position/theta",
+ "sim/flightmodel/position/psi",
+ "sim/cockpit/switches/gear_handle_status"
};
- float[] posi = new float[] {37.524F, -122.06899F, 2500, 0, 0, 0, 1};
+ double[] posi = new double[] {37.524, -122.06899, 2500, 0, 0, 0, 1};
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.pauseSim(true);
@@ -585,7 +585,7 @@ public class XPlaneConnectTest
@Test(expected = IllegalArgumentException.class)
public void testSendPOSI_LongCtrl() throws IOException
{
- float[] posi = new float[] {37.524F, -122.06899F, 2500, 0, 0, 0, 1, -998};
+ double[] posi = new double[] {37.524, -122.06899, 2500, 0, 0, 0, 1, -998};
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.sendPOSI(posi);
@@ -595,7 +595,7 @@ public class XPlaneConnectTest
@Test(expected = IllegalArgumentException.class)
public void testSendPOSI_NegativeAircraftNum() throws IOException
{
- float[] posi = new float[] {37.524F, -122.06899F, 2500, 0, 0, 0, 1, -998};
+ double[] posi = new double[] {37.524, -122.06899, 2500, 0, 0, 0, 1, -998};
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.sendPOSI(posi, -1);
@@ -605,7 +605,7 @@ public class XPlaneConnectTest
@Test(expected = IllegalArgumentException.class)
public void testSendPOSI_LargeAircraftNum() throws IOException
{
- float[] posi = new float[] {37.524F, -122.06899F, 2500, 0, 0, 0, 1, -998};
+ double[] posi = new double[] {37.524, -122.06899, 2500, 0, 0, 0, 1, -998};
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.sendPOSI(posi, 300);
@@ -702,12 +702,12 @@ public class XPlaneConnectTest
@Test
public void testGetPOSI() throws IOException
{
- float[] values = { 37.524F, -122.06899F, 2500.0F, 45.0F, -45.0F, 15.0F, 1.0F };
+ double[] values = { 37.524, -122.06899, 2500.0, 45.0, -45.0, 15.0, 1.0 };
try(XPlaneConnect xpc = new XPlaneConnect())
{
xpc.pauseSim(true);
xpc.sendPOSI(values);
- float[] actual = xpc.getPOSI(0);
+ double[] actual = xpc.getPOSI(0);
assertArrayEquals(values, actual, 1e-4F);
}
@@ -725,4 +725,4 @@ public class XPlaneConnectTest
assertArrayEquals(values, actual, 1e-4F);
}
}
-}
\ No newline at end of file
+}
diff --git a/TestScripts/Java Tests/XPlaneDiscoveryTests.java b/TestScripts/Java Tests/XPlaneDiscoveryTests.java
new file mode 100644
index 0000000..bb954f2
--- /dev/null
+++ b/TestScripts/Java Tests/XPlaneDiscoveryTests.java
@@ -0,0 +1,73 @@
+package gov.nasa.xpc.test;
+
+import static org.junit.Assert.*;
+
+import gov.nasa.xpc.discovery.Beacon;
+import gov.nasa.xpc.discovery.BeaconParser;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+
+
+public class XPlaneDiscoveryTests {
+
+ private BeaconParser parser;
+ private DatagramPacket packet;
+
+ @Before
+ public void setup() {
+ parser = new BeaconParser();
+ byte[] bytes = HexHelper.decodeHexString("4245434E0071BFFC2B0000312E332D72632E310000000000000000");
+ packet = new DatagramPacket(bytes, 0, bytes.length);
+ }
+
+ @Test
+ public void readBeaconParsesPort() throws IOException {
+ Beacon beacon = parser.readBCN(packet);
+ assertEquals(beacon.getPluginPort(), 49009);
+ }
+
+ @Test
+ public void readBeaconParsesXplaneVersion() throws IOException {
+ Beacon beacon = parser.readBCN(packet);
+ assertEquals(beacon.getXplaneVersion(), "112.60");
+
+ }
+
+ @Test
+ public void readBeaconParsesPluginVersion() throws IOException {
+ Beacon beacon = parser.readBCN(packet);
+ assertEquals(beacon.getPluginVersion(), "1.3-rc.1");
+ }
+}
+
+class HexHelper {
+ static byte[] decodeHexString(String hexString) {
+ if (hexString.length() % 2 == 1) {
+ throw new IllegalArgumentException("Invalid hexadecimal String supplied.");
+ }
+
+ byte[] bytes = new byte[hexString.length() / 2];
+ for (int i = 0; i < hexString.length(); i += 2) {
+ bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
+ }
+ return bytes;
+ }
+
+ private static byte hexToByte(String hexString) {
+ int firstDigit = toDigit(hexString.charAt(0));
+ int secondDigit = toDigit(hexString.charAt(1));
+ return (byte) ((firstDigit << 4) + secondDigit);
+ }
+
+ private static int toDigit(char hexChar) {
+ int digit = Character.digit(hexChar, 16);
+ if (digit == -1) {
+ throw new IllegalArgumentException(
+ "Invalid Hexadecimal Character: " + hexChar);
+ }
+ return digit;
+ }
+}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000..d04d24a
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,165 @@
+# Xcode
+# Build, test, and archive an Xcode workspace on macOS.
+# Add steps that install certificates, test, sign, and distribute an app, save build artifacts, and more:
+# https://docs.microsoft.com/azure/devops/pipelines/languages/xcode
+
+trigger:
+ branches:
+ include:
+ # Build all branches starting with 'ms-' This is useful when making and testing chanes made to this file
+ - ms-*
+ tags:
+ include:
+ - '*'
+ # pr:
+ # - '*'
+variables:
+ xpcBuildOutputPath: xpcPlugin/XPlaneConnect/
+ xpcArtifactNameLinux: xpc-linux
+ xpcArtifactNameMac: xpc-macos
+ xpcArtifactNameWindows: xpc-windows
+ xpcPackageName: XPlaneConnect
+
+ # The Github Release tasks requires a GH 'service connection' to be setup in the MS Azure Account
+ # This connection must be setup in with a GH token in the MS Azure Project Settings
+ #
+ # https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/github-release?view=azure-devops
+ # https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#sep-github
+ xpcGitHubConnection: XPC-Azure
+
+
+stages:
+ - stage: Build
+ jobs:
+ - job: macOS
+ pool:
+ vmImage: 'macos-latest'
+
+ steps:
+ - task: Xcode@5
+ inputs:
+ actions: 'build'
+ scheme: ''
+ sdk: 'macosx10.14'
+ configuration: 'Release'
+ xcWorkspacePath: 'xpcPlugin/xpcPlugin.xcodeproj'
+ xcodeVersion: 'default' # Options: 8, 9, 10, default, specifyPath
+ - task: PublishPipelineArtifact@0
+ inputs:
+ artifactName: '$(xpcArtifactNameMac)'
+ targetPath: '$(xpcBuildOutputPath)'
+
+ - job: Linux
+ pool:
+ vmImage: 'ubuntu-latest'
+
+ steps:
+ - script: |
+ sudo add-apt-repository ppa:ubuntu-toolchain-r/test
+ sudo apt-get update
+ sudo apt-get install -y g++-5 mesa-common-dev g++-multilib g++-5-multilib
+ displayName: 'apt install'
+
+ - script: |
+ cd xpcPlugin
+ cmake .
+ make
+ displayName: 'make'
+
+ - task: PublishPipelineArtifact@0
+ inputs:
+ artifactName: 'xpc-linux'
+ targetPath: '$(xpcBuildOutputPath)'
+
+ - job: Windows
+ pool:
+ vmImage: 'vs2017-win2016'
+
+ variables:
+ solution: 'xpcPlugin/xpcPlugin/xpcPlugin.sln'
+ buildPlatform: 'Win32|x64'
+ buildConfiguration: 'Release'
+ appxPackageDir: '$(build.artifactStagingDirectory)\AppxPackages\\'
+
+ steps:
+ - task: NuGetToolInstaller@0
+
+ - task: NuGetCommand@2
+ inputs:
+ restoreSolution: '$(solution)'
+
+ - task: VSBuild@1
+ inputs:
+ platform: 'Win32'
+ solution: '$(solution)'
+ configuration: '$(buildConfiguration)'
+ msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload'
+
+ - task: VSBuild@1
+ inputs:
+ platform: 'x64'
+ solution: '$(solution)'
+ configuration: '$(buildConfiguration)'
+ msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)" /p:AppxPackageDir="$(appxPackageDir)" /p:AppxBundle=Always /p:UapAppxPackageBuildMode=StoreUpload'
+
+ - task: Bash@3
+ inputs:
+ targetType: 'inline'
+ script: |
+ rm $(xpcBuildOutputPath)/win.exp
+ rm $(xpcBuildOutputPath)/win.lib
+ rm $(xpcBuildOutputPath)/64/win.exp
+ rm $(xpcBuildOutputPath)/64/win.lib
+
+ - task: PublishPipelineArtifact@0
+ inputs:
+ artifactName: '$(xpcArtifactNameWindows)'
+ targetPath: '$(xpcBuildOutputPath)'
+
+ - stage: Deploy
+ jobs:
+ - job: Package_Binaries
+ displayName: 'Package Binaries'
+ pool:
+ vmImage: 'ubuntu-latest'
+
+ steps:
+ - task: DownloadPipelineArtifact@0
+ inputs:
+ artifactName: '$(xpcArtifactNameMac)'
+ targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
+ - task: DownloadPipelineArtifact@0
+ inputs:
+ artifactName: '$(xpcArtifactNameLinux)'
+ targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
+
+ - task: DownloadPipelineArtifact@0
+ inputs:
+ artifactName: '$(xpcArtifactNameWindows)'
+ targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
+
+ - task: PublishPipelineArtifact@0
+ inputs:
+ artifactName: '$(xpcPackageName)'
+ targetPath: $(System.DefaultWorkingDirectory)/$(xpcPackageName)
+
+ # Archive Files
+ - task: ArchiveFiles@2
+ inputs:
+ rootFolderOrFile: '$(System.DefaultWorkingDirectory)/$(xpcPackageName)'
+ includeRootFolder: true
+ archiveType: 'zip'
+ archiveFile: '$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip'
+
+ # Uploads the .zip to GitHub and creates a new "Release" with the commit tag
+ # This tasks runs only on pushed tags
+ - task: GitHubRelease@0
+ inputs:
+ gitHubConnection: '$(xpcGitHubConnection)'
+ repositoryName: '$(Build.Repository.Name)'
+ action: 'create'
+ target: '$(Build.SourceVersion)'
+ tagSource: 'auto'
+ tag: $(tagName)
+ assets: '$(Build.ArtifactStagingDirectory)/$(xpcPackageName).zip'
+ isPreRelease: true
diff --git a/xpcPlugin/CMakeLists.txt b/xpcPlugin/CMakeLists.txt
index d07ae27..a82a220 100644
--- a/xpcPlugin/CMakeLists.txt
+++ b/xpcPlugin/CMakeLists.txt
@@ -10,6 +10,10 @@ add_definitions(-DXPLM200 -DLIN=1)
SET(CMAKE_C_COMPILER gcc)
SET(CMAKE_CXX_COMPILER g++)
+SET(CMAKE_CXX_STANDARD 11)
+
+SET(XPC_OUTPUT_DIR "XPlaneConnect")
+SET(XPC_OUTPUT_NAME "lin")
add_library(xpc64 SHARED XPCPlugin.cpp
DataManager.cpp
@@ -17,9 +21,12 @@ add_library(xpc64 SHARED XPCPlugin.cpp
Log.cpp
Message.cpp
MessageHandlers.cpp
+ Timer.cpp
UDPSocket.cpp)
set_target_properties(xpc64 PROPERTIES PREFIX "" SUFFIX ".xpl")
-set_target_properties(xpc64 PROPERTIES COMPILE_FLAGS "-m64" LINK_FLAGS "-shared -rdynamic -nodefaultlibs -undefined_warning -m64")
+set_target_properties(xpc64 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${XPC_OUTPUT_DIR}/64)
+set_target_properties(xpc64 PROPERTIES OUTPUT_NAME ${XPC_OUTPUT_NAME})
+set_target_properties(xpc64 PROPERTIES COMPILE_FLAGS "-m64 -fno-stack-protector" LINK_FLAGS "-shared -rdynamic -nodefaultlibs -undefined_warning -m64 -fno-stack-protector")
add_library(xpc32 SHARED XPCPlugin.cpp
DataManager.cpp
@@ -27,9 +34,12 @@ add_library(xpc32 SHARED XPCPlugin.cpp
Log.cpp
Message.cpp
MessageHandlers.cpp
+ Timer.cpp
UDPSocket.cpp)
set_target_properties(xpc32 PROPERTIES PREFIX "" SUFFIX ".xpl")
-set_target_properties(xpc32 PROPERTIES COMPILE_FLAGS "-m32" LINK_FLAGS "-shared -rdynamic -nodefaultlibs -undefined_warning -m32")
+set_target_properties(xpc32 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${XPC_OUTPUT_DIR})
+set_target_properties(xpc32 PROPERTIES OUTPUT_NAME ${XPC_OUTPUT_NAME})
+set_target_properties(xpc32 PROPERTIES COMPILE_FLAGS "-m32 -fno-stack-protector" LINK_FLAGS "-shared -rdynamic -nodefaultlibs -undefined_warning -m32 -fno-stack-protector")
# Switch install targets when uncommenting the 32 bit line above.
install(TARGETS xpc64 DESTINATION XPlaneConnect/64 RENAME lin.xpl)
diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp
index 83fca41..6e66abb 100644
--- a/xpcPlugin/DataManager.cpp
+++ b/xpcPlugin/DataManager.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// X-Plane API
@@ -109,7 +109,7 @@ namespace XPC
drefs.insert(make_pair(DREF_Latitude, XPLMFindDataRef("sim/flightmodel/position/latitude")));
drefs.insert(make_pair(DREF_Longitude, XPLMFindDataRef("sim/flightmodel/position/longitude")));
drefs.insert(make_pair(DREF_AGL, XPLMFindDataRef("sim/flightmodel/position/y_agl")));
- drefs.insert(make_pair(DREF_Elevation, XPLMFindDataRef("sim/flightmodel/position/elevation")));
+ drefs.insert(make_pair(DREF_Elevation, XPLMFindDataRef("sim/flightmodel/position/elevation")));
drefs.insert(make_pair(DREF_LocalX, XPLMFindDataRef("sim/flightmodel/position/local_x")));
drefs.insert(make_pair(DREF_LocalY, XPLMFindDataRef("sim/flightmodel/position/local_y")));
@@ -522,7 +522,7 @@ namespace XPC
Log::FormatLine(LOG_ERROR, "DMAN", "ERROR: invalid DREF %s", dref.c_str());
return;
}
- if (isnan(values[0]))
+ if (std::isnan(values[0]))
{
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Value must be a number (NaN received)");
return;
@@ -629,11 +629,12 @@ namespace XPC
if ((gear < -8.5 && gear > -9.5) || IsDefault(gear))
{
+ Log::WriteLine(LOG_INFO, "DMAN", "Not actually setting gear because of default value");
return;
}
- if (isnan(gear) || gear < 0 || gear > 1)
+ if (std::isnan(gear) || gear < 0 || gear > 1)
{
- Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Gear value must be 0 or 1");
+ Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Gear value must be between 0 and 1");
return;
}
@@ -657,11 +658,11 @@ namespace XPC
}
}
- void DataManager::SetPosition(float pos[3], char aircraft)
+ void DataManager::SetPosition(double pos[3], char aircraft)
{
Log::FormatLine(LOG_INFO, "DMAN", "Setting position (%f, %f, %f) for aircraft %i",
pos[0], pos[1], pos[2], aircraft);
- if (isnan(pos[0] + pos[1] + pos[2]))
+ if (std::isnan(pos[0] + pos[1] + pos[2]))
{
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Position must be a number (NaN received)");
return;
@@ -669,28 +670,28 @@ namespace XPC
if (IsDefault(pos[0]))
{
- pos[0] = (float)GetDouble(DREF_Latitude, aircraft);
+ pos[0] = GetDouble(DREF_Latitude, aircraft);
}
if (IsDefault(pos[1]))
{
- pos[1] = (float)GetDouble(DREF_Longitude, aircraft);
+ pos[1] = GetDouble(DREF_Longitude, aircraft);
}
if (IsDefault(pos[2]))
{
- pos[2] = (float)GetDouble(DREF_Elevation, aircraft);
+ pos[2] = GetDouble(DREF_Elevation, aircraft);
}
- // See: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane
- // Need to get the aircraft's current orientation before moving and
- // reset the orientation after moving to update the quaternion
- float orient[3];
- orient[0] = GetFloat(DREF_Pitch, aircraft);
- orient[1] = GetFloat(DREF_Roll, aircraft);
- orient[2] = GetFloat(DREF_HeadingTrue, aircraft);
+ // See: http://www.xsquawkbox.net/xpsdk/mediawiki/MovingThePlane
+ // Need to get the aircraft's current orientation before moving and
+ // reset the orientation after moving to update the quaternion
+ float orient[3];
+ orient[0] = GetFloat(DREF_Pitch, aircraft);
+ orient[1] = GetFloat(DREF_Roll, aircraft);
+ orient[2] = GetFloat(DREF_HeadingTrue, aircraft);
- // Now set the aircraft's position. Need to set world position for
- // "long" moves, but since there isn't an easy way to calculate "long",
- // we just set it every time.
+ // Now set the aircraft's position. Need to set world position for
+ // "long" moves, but since there isn't an easy way to calculate "long",
+ // we just set it every time.
double local[3];
XPLMWorldToLocal(pos[0], pos[1], pos[2], &local[0], &local[1], &local[2]);
// If the sim is paused, setting global position won't update the
@@ -699,19 +700,19 @@ namespace XPC
Set(DREF_LocalY, local[1], aircraft);
Set(DREF_LocalZ, local[2], aircraft);
// If the sim is unpaused, this will override the above settings.
- Set(DREF_Latitude, (double)pos[0], aircraft);
- Set(DREF_Longitude, (double)pos[1], aircraft);
- Set(DREF_Elevation, (double)pos[2], aircraft);
+ Set(DREF_Latitude, pos[0], aircraft);
+ Set(DREF_Longitude, pos[1], aircraft);
+ Set(DREF_Elevation, pos[2], aircraft);
- // Now reset orientation to update q
- SetOrientation(orient, aircraft);
+ // Now reset orientation to update q
+ SetOrientation(orient, aircraft);
}
void DataManager::SetOrientation(float orient[3], char aircraft)
{
Log::FormatLine(LOG_INFO, "DMAN", "Setting orientation (%f, %f, %f) for aircraft %i",
orient[0], orient[1], orient[2], aircraft);
- if (isnan(orient[0] + orient[1] + orient[2]))
+ if (std::isnan(orient[0] + orient[1] + orient[2]))
{
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Orientation must be a number (NaN received)");
return;
@@ -762,7 +763,7 @@ namespace XPC
{
Log::FormatLine(LOG_INFO, "DMAN", "Setting flaps (value:%f)", value);
- if (isnan(value))
+ if (std::isnan(value))
{
Log::WriteLine(LOG_ERROR, "DMAN", "ERROR: Flap value must be a number (NaN received)");
return;
@@ -784,7 +785,7 @@ namespace XPC
return -998.0F;
}
- bool DataManager::IsDefault(float value)
+ bool DataManager::IsDefault(double value)
{
return value < -997.9 && value > -999.1;
}
diff --git a/xpcPlugin/DataManager.h b/xpcPlugin/DataManager.h
index c0e80c2..15f1218 100644
--- a/xpcPlugin/DataManager.h
+++ b/xpcPlugin/DataManager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_DATAMANAGER_H_
#define XPCPLUGIN_DATAMANAGER_H_
@@ -396,7 +396,7 @@ namespace XPC
/// \param pos An array containing latitude, longitude and altitude in
/// fractional degrees and meters above sea level.
/// \param aircraft The aircraft to set the position of.
- static void SetPosition(float pos[3], char aircraft = 0);
+ static void SetPosition(double pos[3], char aircraft = 0);
/// Sets the orientation of the specified aircraft.
///
@@ -417,7 +417,7 @@ namespace XPC
///
/// \param value The value to check.
/// \returns true if value is a default value; otherwise false.
- static bool IsDefault(float value);
+ static bool IsDefault(double value);
};
}
#endif
diff --git a/xpcPlugin/Drawing.cpp b/xpcPlugin/Drawing.cpp
index d737e36..40599c7 100644
--- a/xpcPlugin/Drawing.cpp
+++ b/xpcPlugin/Drawing.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// X-Plane API
@@ -173,7 +173,7 @@ namespace XPC
glBegin(GL_LINE_STRIP);
for (size_t i = 0; i < numWaypoints; ++i)
{
- LocalPoint* l = &localPoints[i];
+ LocalPoint* l = &localPoints[i];
glVertex3f((float)l->x, (float)l->y, (float)l->z);
}
glEnd();
@@ -226,7 +226,7 @@ namespace XPC
// Enable drawing if necessary
if (!msgEnabled)
{
- XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
+ XPLMRegisterDrawCallback(MessageDrawCallback, xplm_Phase_Window, 0, NULL);
msgEnabled = true;
}
}
diff --git a/xpcPlugin/Drawing.h b/xpcPlugin/Drawing.h
index 51c39de..c6815e1 100644
--- a/xpcPlugin/Drawing.h
+++ b/xpcPlugin/Drawing.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_DRAWING_H_
#define XPCPLUGIN_DRAWING_H_
diff --git a/xpcPlugin/Log.cpp b/xpcPlugin/Log.cpp
index a059ebb..581ad7e 100644
--- a/xpcPlugin/Log.cpp
+++ b/xpcPlugin/Log.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#include "Log.h"
@@ -111,7 +111,7 @@ namespace XPC
std::fprintf(fd, "X-Plane Connect [Version %s]\n", version.c_str());
std::fprintf(fd, "Compiled %s %s\n", __DATE__, __TIME__);
- std::fprintf(fd, "Copyright (c) 2013-2017 United States Government as represented by the\n");
+ std::fprintf(fd, "Copyright (c) 2013-2018 United States Government as represented by the\n");
std::fprintf(fd, "Administrator of the National Aeronautics and Space Administration.\n");
std::fprintf(fd, "All Rights Reserved.\n\n");
diff --git a/xpcPlugin/Log.h b/xpcPlugin/Log.h
index cd6b6ba..5323678 100644
--- a/xpcPlugin/Log.h
+++ b/xpcPlugin/Log.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_LOG_H_
#define XPCPLUGIN_LOG_H_
@@ -27,7 +27,7 @@
namespace XPC
{
/// Handles logging for the plugin.
- ///
+ ///
/// \details Provides functions to write lines to the XPC log file.
/// \author Jason Watkins
/// \version 1.1
diff --git a/xpcPlugin/Message.cpp b/xpcPlugin/Message.cpp
index 81c5c9c..f3401f0 100644
--- a/xpcPlugin/Message.cpp
+++ b/xpcPlugin/Message.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#include "Message.h"
#include "Log.h"
@@ -11,7 +11,7 @@
namespace XPC
{
- Message::Message() {}
+ Message::Message() {}
Message Message::ReadFrom(const UDPSocket& sock)
{
@@ -110,17 +110,17 @@ namespace XPC
else if (head == "DREF")
{
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
- string dref((char*)buffer + 6, buffer[5]);
- Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref.c_str());
- ss.str("");
- int values = buffer[6 + buffer[5]];
- ss << " Values(size " << values << ") =";
- for (int i = 0; i < values; ++i)
- {
- ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i));
+ string dref((char*)buffer + 6, buffer[5]);
+ Log::FormatLine(LOG_DEBUG, "DBUG", " DREF (size %i) = %s", dref.length(), dref.c_str());
+ ss.str("");
+ int values = buffer[6 + buffer[5]];
+ ss << " Values(size " << values << ") =";
+ for (int i = 0; i < values; ++i)
+ {
+ ss << " " << *((float*)(buffer + values + 1 + sizeof(float) * i));
}
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
- }
+ }
else if (head == "GETC" || head == "GETP")
{
ss << " Aircraft:" << (int)buffer[5];
@@ -129,15 +129,15 @@ namespace XPC
else if (head == "GETD")
{
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
- int cur = 6;
- for (int i = 0; i < buffer[5]; ++i)
- {
- string dref((char*)buffer + cur + 1, buffer[cur]);
- Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s",
+ int cur = 6;
+ for (int i = 0; i < buffer[5]; ++i)
+ {
+ string dref((char*)buffer + cur + 1, buffer[cur]);
+ Log::FormatLine(LOG_DEBUG, "DBUG", " #%i/%i (size:%i) %s",
i + 1, buffer[5], dref.length(), dref.c_str());
- cur += 1 + buffer[cur];
- }
- }
+ cur += 1 + buffer[cur];
+ }
+ }
else if (head == "POSI")
{
char aircraft = buffer[5];
@@ -146,14 +146,14 @@ namespace XPC
float orient[3];
memcpy(pos, buffer + 6, 12);
memcpy(orient, buffer + 18, 12);
- ss << " AC:" << (int)aircraft;
+ ss << " AC:" << (int)aircraft;
ss << " Pos:(" << pos[0] << ' ' << pos[1] << ' ' << pos[2] << ") Orient:(";
- ss << orient[3] << ' ' << orient[4] << ' ' << orient[5] << ") Gear:";
+ ss << orient[0] << ' ' << orient[1] << ' ' << orient[2] << ") Gear:";
ss << gear;
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
- }
+ }
else if (head == "SIMU")
- {
+ {
ss << ' ' << (int)buffer[5];
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
@@ -163,9 +163,9 @@ namespace XPC
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
}
else
- {
+ {
ss << " UNKNOWN HEADER ";
Log::WriteLine(LOG_DEBUG, "DBUG", ss.str());
- }
+ }
}
}
diff --git a/xpcPlugin/Message.h b/xpcPlugin/Message.h
index 34071cb..7fd6046 100644
--- a/xpcPlugin/Message.h
+++ b/xpcPlugin/Message.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_MESSAGE_H_
#define XPCPLUGIN_MESSAGE_H_
diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp
index 89d6852..cc4ee18 100644
--- a/xpcPlugin/MessageHandlers.cpp
+++ b/xpcPlugin/MessageHandlers.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
//
// X-Plane API
@@ -20,9 +20,16 @@
#include "Log.h"
#include "XPLMUtilities.h"
+#include "XPLMGraphics.h"
+
#include
#include
+#include
+
+#define MULTICAST_GROUP "239.255.1.1"
+#define MULITCAST_PORT 49710
+
namespace XPC
{
@@ -32,6 +39,8 @@ namespace XPC
std::string MessageHandlers::connectionKey;
MessageHandlers::ConnectionInfo MessageHandlers::connection;
UDPSocket* MessageHandlers::sock;
+
+ static sockaddr multicast_address = UDPSocket::GetAddr(MULTICAST_GROUP, MULITCAST_PORT);
void MessageHandlers::SetSocket(UDPSocket* socket)
{
@@ -127,6 +136,28 @@ namespace XPC
MessageHandlers::HandleUnknown(msg);
}
}
+
+ void MessageHandlers::SendBeacon(const std::string& pluginVersion, unsigned short pluginReceivePort, int xplaneVersion) {
+
+ unsigned char response[128] = "BECN";
+
+ std::size_t cur = 5;
+
+ // 2 bytes plugin port
+ *((uint16_t *)(response + cur)) = pluginReceivePort;
+ cur += sizeof(uint16_t);
+
+ // 4 bytes xplane version
+ *((uint32_t*)(response + cur)) = xplaneVersion;
+ cur += sizeof(uint32_t);
+
+ // plugin version
+ int len = pluginVersion.length();
+ memcpy(response + cur, pluginVersion.c_str(), len);
+ cur += strlen(pluginVersion.c_str()) + len;
+
+ sock->SendTo(response, cur, &multicast_address);
+ }
void MessageHandlers::HandleConn(const Message& msg)
{
@@ -342,10 +373,11 @@ namespace XPC
}
case 20: // Position
{
- float pos[3];
- pos[0] = values[i][2];
- pos[1] = values[i][3];
- pos[2] = values[i][4];
+ // TODO: loss of precision here
+ double pos[3];
+ pos[0] = (double)values[i][2];
+ pos[1] = (double)values[i][3];
+ pos[2] = (double)values[i][4];
DataManager::SetPosition(pos);
break;
}
@@ -439,7 +471,7 @@ namespace XPC
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);
@@ -524,13 +556,14 @@ namespace XPC
unsigned char response[34] = "POSI";
response[5] = (char)DataManager::GetInt(DREF_GearHandle, aircraft);
+ // TODO change lat/lon/h to double?
*((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];
@@ -545,18 +578,37 @@ namespace XPC
const unsigned char* buffer = msg.GetBuffer();
const std::size_t size = msg.GetSize();
- if (size < 34)
+
+ char aircraftNumber = buffer[5];
+ float gear = *((float*)(buffer + 42));
+ double posd[3];
+ float orient[3];
+
+ if (size == 34) /* lat/lon/h as 32-bit float */
{
- Log::FormatLine(LOG_ERROR, "POSI", "ERROR: Unexpected size: %i (Expected at least 34)", size);
+ posd[0] = *((float*)&buffer[6]);
+ posd[1] = *((float*)&buffer[10]);
+ posd[2] = *((float*)&buffer[14]);
+ memcpy(orient, buffer + 18, 12);
+ }
+ else if (size == 46) /* lat/lon/h as 64-bit double */
+ {
+ memcpy(posd, buffer + 6, 3*8);
+ memcpy(orient, buffer + 30, 12);
+ }
+ else
+ {
+ Log::FormatLine(LOG_ERROR, "POSI", "ERROR: Unexpected size: %i (Expected 34 or 46)", size);
return;
}
- char aircraftNumber = buffer[5];
- float gear = *((float*)(buffer + 30));
- float pos[3];
- float orient[3];
- memcpy(pos, buffer + 6, 12);
- memcpy(orient, buffer + 18, 12);
+ /* convert float to double */
+ DataManager::SetPosition(posd, aircraftNumber);
+ DataManager::SetOrientation(orient, aircraftNumber);
+ if (gear >= 0)
+ {
+ DataManager::SetGear(gear, true, aircraftNumber);
+ }
if (aircraftNumber > 0)
{
@@ -569,13 +621,6 @@ namespace XPC
DataManager::Set(DREF_PauseAI, ai, 0, 20);
}
}
-
- DataManager::SetPosition(pos, aircraftNumber);
- DataManager::SetOrientation(orient, aircraftNumber);
- if (gear != -1)
- {
- DataManager::SetGear(gear, true, aircraftNumber);
- }
}
void MessageHandlers::HandleSimu(const Message& msg)
@@ -583,13 +628,13 @@ namespace XPC
// Update log
Log::FormatLine(LOG_TRACE, "SIMU", "Message Received (Conn %i)", connection.id);
- char v = msg.GetBuffer()[5];
- if (v < 0 || v > 2)
+ unsigned char v = msg.GetBuffer()[5];
+ if (v < 0 || (v > 2 && v < 100) || (v > 119 && v < 200) || v > 219)
{
Log::FormatLine(LOG_ERROR, "SIMU", "ERROR: Invalid argument: %i", v);
return;
}
-
+
int value[20];
if (v == 2)
{
@@ -599,6 +644,16 @@ namespace XPC
value[i] = value[i] ? 0 : 1;
}
}
+ else if ((v >= 100) && (v < 120))
+ {
+ DataManager::GetIntArray(DREF_Pause, value, 20);
+ value[v - 100] = 1;
+ }
+ else if ((v >= 200) && (v < 220))
+ {
+ DataManager::GetIntArray(DREF_Pause, value, 20);
+ value[v - 200] = 0;
+ }
else
{
for (int i = 0; i < 20; ++i)
@@ -610,18 +665,27 @@ namespace XPC
// Set DREF
DataManager::Set(DREF_Pause, value, 20);
- switch (v)
+ if (v == 0)
{
- case 0:
- Log::WriteLine(LOG_INFO, "SIMU", "Simulation resumed");
- break;
- case 1:
- Log::WriteLine(LOG_INFO, "SIMU", "Simulation paused");
- break;
- case 2:
- Log::FormatLine(LOG_INFO, "SIMU", "Simulation switched to %i", value[0]);
- break;
+ Log::WriteLine(LOG_INFO, "SIMU", "Simulation resumed for all a/c");
}
+ else if (v == 1)
+ {
+ Log::WriteLine(LOG_INFO, "SIMU", "Simulation paused for all a/c");
+ }
+ else if (v == 2)
+ {
+ Log::FormatLine(LOG_INFO, "SIMU", "Simulation switched to %i for all a/c", value[0]);
+ }
+ else if ((v >= 100) && (v < 120))
+ {
+ Log::FormatLine(LOG_INFO, "SIMU", "Simulation paused for a/c %i", (v-100));
+ }
+ else if ((v >= 200) && (v < 220))
+ {
+ Log::FormatLine(LOG_INFO, "SIMU", "Simulation resumed for a/c %i", (v-100));
+ }
+
}
void MessageHandlers::HandleText(const Message& msg)
@@ -653,21 +717,111 @@ namespace XPC
Log::WriteLine(LOG_INFO, "TEXT", "[TEXT] Text set");
}
}
-
+
void MessageHandlers::HandleView(const Message& msg)
{
// Update Log
Log::FormatLine(LOG_TRACE, "VIEW", "Message Received(Conn %i)", connection.id);
+ int enable_camera_location = 0;
+
const std::size_t size = msg.GetSize();
- if (size != 9)
+ if (size == 9)
{
- Log::FormatLine(LOG_ERROR, "VIEW", "Error: Unexpected length. Message was %d bytes, expected 9.", size);
+ // default view switcher as before
+ }
+ else if (size == 37)
+ {
+ // Allow camera location control
+ enable_camera_location = 1;
+ }
+ else
+ {
+ Log::FormatLine(LOG_ERROR, "VIEW", "Error: Unexpected length. Message was %d bytes, expected 9 or 37.", size);
return;
}
const unsigned char* buffer = msg.GetBuffer();
int type = *((int*)(buffer + 5));
XPLMCommandKeyStroke(type);
+
+ if(type == 79 && enable_camera_location == 1) // runway camera view
+ {
+ static struct CameraProperties campos;
+
+ campos.loc[0] = *(double*)(buffer+9);
+ campos.loc[1] = *(double*)(buffer+17);
+ campos.loc[2] = *(double*)(buffer+25);
+ campos.direction[0] = -998;
+ campos.direction[1] = -998;
+ campos.direction[2] = -998;
+ campos.zoom = *(float*)(buffer+33);
+
+ Log::FormatLine(LOG_TRACE, "VIEW", "Cam pos %f %f %f zoom %f", campos.loc[0], campos.loc[1], campos.loc[2],campos.zoom);
+
+ XPLMControlCamera(xplm_ControlCameraUntilViewChanges, CamFunc, &campos);
+ }
+ }
+
+ int MessageHandlers::CamFunc( XPLMCameraPosition_t * outCameraPosition, int inIsLosingControl, void *inRefcon)
+ {
+ if (outCameraPosition && !inIsLosingControl)
+ {
+ struct CameraProperties* campos = (struct CameraProperties*)inRefcon;
+
+ // camera position
+ double clat = campos->loc[0];
+ double clon = campos->loc[1];
+ double calt = campos->loc[2];
+
+ double cX, cY, cZ;
+ XPLMWorldToLocal(clat, clon, calt, &cX, &cY, &cZ);
+
+ outCameraPosition->x = cX;
+ outCameraPosition->y = cY;
+ outCameraPosition->z = cZ;
+
+// Log::FormatLine(LOG_TRACE, "CAM", "Cam pos %f %f %f", clat, clon, calt);
+
+ if(campos->direction[0] == -998) // calculate camera direction
+ {
+ // aircraft position
+ double x = XPC::DataManager::GetDouble(XPC::DREF_LocalX, 0);
+ double y = XPC::DataManager::GetDouble(XPC::DREF_LocalY, 0);
+ double z = XPC::DataManager::GetDouble(XPC::DREF_LocalZ, 0);
+
+ // relative position vector cam to plane
+ double dx = x - cX;
+ double dy = y - cY;
+ double dz = z - cZ;
+
+// Log::FormatLine(LOG_TRACE, "CAM", "Cam vect %f %f %f", dx, dy, dz);
+
+ double pi = 3.141592653589793;
+
+ // horizontal distance
+ double dist = sqrt(dx*dx + dz*dz);
+
+ outCameraPosition->pitch = atan2(dy, dist) * 180.0/pi;
+
+ double angle = atan2(dz, dx) * 180.0/pi; // rel to pos right (pos X)
+
+ outCameraPosition->heading = 90 + angle; // rel to north
+
+// Log::FormatLine(LOG_TRACE, "CAM", "Cam p %f hdg %f ", outCameraPosition->pitch, outCameraPosition->heading);
+
+ outCameraPosition->roll = 0;
+ }
+ else
+ {
+ outCameraPosition->roll = campos->direction[0];
+ outCameraPosition->pitch = campos->direction[1];
+ outCameraPosition->heading = campos->direction[2];
+ }
+
+ outCameraPosition->zoom = campos->zoom;
+ }
+
+ return 1;
}
void MessageHandlers::HandleWypt(const Message& msg)
diff --git a/xpcPlugin/MessageHandlers.h b/xpcPlugin/MessageHandlers.h
index 3a48007..a065c96 100644
--- a/xpcPlugin/MessageHandlers.h
+++ b/xpcPlugin/MessageHandlers.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013-2017 United States Government as represented by the Administrator of the
+// Copyright (c) 2013-2018 United States Government as represented by the Administrator of the
// National Aeronautics and Space Administration. All Rights Reserved.
#ifndef XPCPLUGIN_MESSAGEHANDLERS_H_
#define XPCPLUGIN_MESSAGEHANDLERS_H_
@@ -7,6 +7,9 @@
#include
#include