From 48656f2b4c1172ee8b2270545c9aa0921b85aa2a Mon Sep 17 00:00:00 2001
From: Jason Watkins
Date: Thu, 22 Jun 2017 17:02:00 -0700
Subject: [PATCH 01/34] Minor improvements to the handling of landing gear by
the SetGear and HandlePOSI functions
---
xpcPlugin/DataManager.cpp | 29 +++++++++++++++--------------
xpcPlugin/MessageHandlers.cpp | 2 +-
2 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp
index 83fca41..ffac66a 100644
--- a/xpcPlugin/DataManager.cpp
+++ b/xpcPlugin/DataManager.cpp
@@ -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")));
@@ -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)
{
- 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;
}
@@ -680,17 +681,17 @@ namespace XPC
pos[2] = (float)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
@@ -703,8 +704,8 @@ namespace XPC
Set(DREF_Longitude, (double)pos[1], aircraft);
Set(DREF_Elevation, (double)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)
diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp
index 89d6852..a78448e 100644
--- a/xpcPlugin/MessageHandlers.cpp
+++ b/xpcPlugin/MessageHandlers.cpp
@@ -572,7 +572,7 @@ namespace XPC
DataManager::SetPosition(pos, aircraftNumber);
DataManager::SetOrientation(orient, aircraftNumber);
- if (gear != -1)
+ if (gear >= 0)
{
DataManager::SetGear(gear, true, aircraftNumber);
}
From 0e493920fa9c09d112892902613184f150936d94 Mon Sep 17 00:00:00 2001
From: Jan Zwiener
Date: Wed, 28 Jun 2017 21:04:59 +0200
Subject: [PATCH 02/34] sendPOSI command change (double for lat/lon/h) (#111)
* Updated POSI to use doubles for lat/lon/alt, as step size for floats was unacceptably large at high longitudes.
---
C/monitorExample/main.c | 4 +-
C/playbackExample/src/chrome.c | 4 +-
C/playbackExample/src/chrome.h | 4 +-
C/playbackExample/src/main.c | 4 +-
C/playbackExample/src/playback.c | 10 ++--
C/playbackExample/src/playback.h | 4 +-
C/src/xplaneConnect.c | 48 +++++++++++--------
C/src/xplaneConnect.h | 10 ++--
C/xpcExample/xpcExample/src/main.cpp | 2 +-
Java/Examples/BasicOperation/src/Main.java | 4 +-
.../ContinuousOperation/src/Main.java | 2 +-
.../Playback/src/gov/nasa/xpc/Main.java | 6 +--
Java/src/XPlaneConnect.java | 23 +++++----
MATLAB/+XPlaneConnect/sendPOSI.m | 14 +++---
MATLAB/MonitorExample/MonitorExample.m | 4 +-
Python/src/xpc.py | 27 ++++++-----
TestScripts/C Tests/CtrlTests.h | 4 +-
TestScripts/C Tests/DrefTests.h | 2 +-
TestScripts/C Tests/PosiTests.h | 32 ++++++-------
TestScripts/C Tests/SimuTests.h | 4 +-
TestScripts/C Tests/Test.c | 26 +++++++++-
TestScripts/C Tests/Test.h | 2 +
TestScripts/C Tests/main.c | 8 ++--
TestScripts/Java Tests/XPlaneConnectTest.java | 28 +++++------
xpcPlugin/DataManager.cpp | 16 +++----
xpcPlugin/DataManager.h | 4 +-
xpcPlugin/MessageHandlers.cpp | 48 ++++++++++++-------
27 files changed, 201 insertions(+), 143 deletions(-)
diff --git a/C/monitorExample/main.c b/C/monitorExample/main.c
index e78f0cf..998c893 100644
--- a/C/monitorExample/main.c
+++ b/C/monitorExample/main.c
@@ -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..563cef8 100644
--- a/C/playbackExample/src/chrome.c
+++ b/C/playbackExample/src/chrome.c
@@ -2,7 +2,7 @@
//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..35b3f60 100644
--- a/C/playbackExample/src/chrome.h
+++ b/C/playbackExample/src/chrome.h
@@ -2,7 +2,7 @@
//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..1787be6 100644
--- a/C/playbackExample/src/main.c
+++ b/C/playbackExample/src/main.c
@@ -2,7 +2,7 @@
//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
@@ -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..1f4025e 100644
--- a/C/playbackExample/src/playback.c
+++ b/C/playbackExample/src/playback.c
@@ -2,7 +2,7 @@
//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..889a7ee 100644
--- a/C/playbackExample/src/playback.h
+++ b/C/playbackExample/src/playback.h
@@ -2,7 +2,7 @@
//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..68529fb 100755
--- a/C/src/xplaneConnect.c
+++ b/C/src/xplaneConnect.c
@@ -2,7 +2,7 @@
//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;
@@ -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..1690e67 100644
--- a/C/src/xplaneConnect.h
+++ b/C/src/xplaneConnect.h
@@ -2,7 +2,7 @@
//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.
@@ -115,7 +115,7 @@ int setCONN(XPCSocket* sock, unsigned short port);
/// \param pause 0 to unpause the sim; any other value to pause.
/// \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/Examples/BasicOperation/src/Main.java b/Java/Examples/BasicOperation/src/Main.java
index dc82c9f..92412d0 100644
--- a/Java/Examples/BasicOperation/src/Main.java
+++ b/Java/Examples/BasicOperation/src/Main.java
@@ -25,11 +25,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/Playback/src/gov/nasa/xpc/Main.java b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java
index 0e0b376..477e8f5 100644
--- a/Java/Examples/Playback/src/gov/nasa/xpc/Main.java
+++ b/Java/Examples/Playback/src/gov/nasa/xpc/Main.java
@@ -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/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java
index 8b1f228..a212278 100644
--- a/Java/src/XPlaneConnect.java
+++ b/Java/src/XPlaneConnect.java
@@ -549,7 +549,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 +570,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 +600,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 +626,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 +644,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/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/xpc.py b/Python/src/xpc.py
index e2b5c5e..a543c7b 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,7 +96,7 @@ 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.
'''
@@ -110,7 +110,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 +127,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 +143,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 +265,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 +295,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 +311,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/TestScripts/C Tests/CtrlTests.h b/TestScripts/C Tests/CtrlTests.h
index 572ea07..52f2519 100644
--- a/TestScripts/C Tests/CtrlTests.h
+++ b/TestScripts/C Tests/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)
diff --git a/TestScripts/C Tests/DrefTests.h b/TestScripts/C Tests/DrefTests.h
index 50338a7..9d1c754 100644
--- a/TestScripts/C Tests/DrefTests.h
+++ b/TestScripts/C Tests/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..317a623 100644
--- a/TestScripts/C Tests/PosiTests.h
+++ b/TestScripts/C Tests/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..60a3767 100644
--- a/TestScripts/C Tests/SimuTests.h
+++ b/TestScripts/C Tests/SimuTests.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..eec3124 100644
--- a/TestScripts/C Tests/Test.c
+++ b/TestScripts/C Tests/Test.c
@@ -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..2a40fb9 100644
--- a/TestScripts/C Tests/Test.h
+++ b/TestScripts/C Tests/Test.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
diff --git a/TestScripts/C Tests/main.c b/TestScripts/C Tests/main.c
index ec2b752..802953c 100644
--- a/TestScripts/C Tests/main.c
+++ b/TestScripts/C Tests/main.c
@@ -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,11 @@ 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/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp
index ffac66a..9e098d0 100644
--- a/xpcPlugin/DataManager.cpp
+++ b/xpcPlugin/DataManager.cpp
@@ -658,7 +658,7 @@ 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);
@@ -670,15 +670,15 @@ 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
@@ -700,9 +700,9 @@ 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);
@@ -785,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..b0c416b 100644
--- a/xpcPlugin/DataManager.h
+++ b/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/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp
index a78448e..6dd9b69 100644
--- a/xpcPlugin/MessageHandlers.cpp
+++ b/xpcPlugin/MessageHandlers.cpp
@@ -342,10 +342,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 +440,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 +525,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 +547,29 @@ namespace XPC
const unsigned char* buffer = msg.GetBuffer();
const std::size_t size = msg.GetSize();
- if (size < 34)
- {
- Log::FormatLine(LOG_ERROR, "POSI", "ERROR: Unexpected size: %i (Expected at least 34)", size);
- return;
- }
char aircraftNumber = buffer[5];
- float gear = *((float*)(buffer + 30));
- float pos[3];
+ float gear = *((float*)(buffer + 42));
+ double posd[3];
float orient[3];
- memcpy(pos, buffer + 6, 12);
- memcpy(orient, buffer + 18, 12);
+
+ if (size == 34) /* lat/lon/h as 32-bit float */
+ {
+ 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;
+ }
if (aircraftNumber > 0)
{
@@ -570,7 +583,8 @@ namespace XPC
}
}
- DataManager::SetPosition(pos, aircraftNumber);
+ /* convert float to double */
+ DataManager::SetPosition(posd, aircraftNumber);
DataManager::SetOrientation(orient, aircraftNumber);
if (gear >= 0)
{
@@ -589,7 +603,7 @@ namespace XPC
Log::FormatLine(LOG_ERROR, "SIMU", "ERROR: Invalid argument: %i", v);
return;
}
-
+
int value[20];
if (v == 2)
{
From 53a10ede999eaed91892676220923c9008a8c0fe Mon Sep 17 00:00:00 2001
From: Jason Watkins
Date: Wed, 28 Jun 2017 12:39:41 -0700
Subject: [PATCH 03/34] Updated Java library for MATLAB, updated Java project,
and updated Windows plugin binaries
---
Java/.idea/misc.xml | 15 +--------------
Java/XPlaneConnect.iml | 15 ++++++++++++---
Java/lib/hamcrest-core-1.3.jar | Bin 0 -> 45024 bytes
Java/lib/junit-4.12.jar | Bin 0 -> 314932 bytes
MATLAB/+XPlaneConnect/XPlaneConnect.jar | Bin 8679 -> 8710 bytes
5 files changed, 13 insertions(+), 17 deletions(-)
create mode 100644 Java/lib/hamcrest-core-1.3.jar
create mode 100644 Java/lib/junit-4.12.jar
diff --git a/Java/.idea/misc.xml b/Java/.idea/misc.xml
index a61f7bc..d5d79e0 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/XPlaneConnect.iml b/Java/XPlaneConnect.iml
index 70873b5..f20ca9d 100644
--- a/Java/XPlaneConnect.iml
+++ b/Java/XPlaneConnect.iml
@@ -1,6 +1,6 @@
-
+
@@ -8,8 +8,17 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
\ 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 0000000000000000000000000000000000000000..9d5fe16e3dd37ebe79a36f61f5d0e1a69a653a8a
GIT binary patch
literal 45024
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
UgpPtD2z>DYYp}SJr5?)v0beu;=l}o!
literal 0
HcmV?d00001
diff --git a/Java/lib/junit-4.12.jar b/Java/lib/junit-4.12.jar
new file mode 100644
index 0000000000000000000000000000000000000000..3a7fc266c3e32283a2b21fe12166ebdcc33a1da1
GIT binary patch
literal 314932
zcmbrl1yH5St~QJ_I1KJGxVyW%ySuwPgF6f`xVyVExVyVMgS*?$+0syT2h2od8eo>e^
zS(`cj10DRI=D
zw7=!HvN1HW`~%lt*NFHhE-O7p1Ji$AB*LE45oI^@s77{-Oh%zazKOvo^DJvea`l
zv$6gI?_VYQTV87$M>AtHgTM0rRjywa{QHhN>sgu^>N(o{aixEq?a!Jpv;NSywca09
z_g4n2KTYu;hW~>OG5?GXHsl
z|1_hOp6ee>{;!O9|9w_RQzILDvww6rtC?_~C$MSKGIH8PTGeE8MJh+%*L0O%jzzxy=-OFah%
z`HwI}ZSd$CSTAS-~5WeN2HQ^G~O{Lof
z_(N{57ThV(X6GoUD{hbHyQeo`Q&(|kbtms;z@$g9X8N_N8S%||y=BU*Sv&Qf_~&s2
zlQQ+IRP$N>>vX#Kcx9ymeCR?xL<_SpRqGH|qY>gP4So}%`s?OgpS+29<;P`A!KbJ1
zQpONlIcu>)ExO0?DyN<~ZifuDx8T9gJ7V-+c#bAwcdYlDWhLQFj>1c>*W`LtktsCd
z<5XKUOkU6LA-o};=Xi<%YaL5`3+LG)-3}fRUN^Szn;ZoF_)*CTciT?I8D=0}YEq
z`h+zo7Wnf$Z}mb$5hc19Y+fEOW)y%_YAiDmzLhUd9g$VGtOz`5;Iu3$*P)ld`E$C%FfvP`}Mv%hua-bwC864=N=W9MNpmG-Q(|`0{%7_oB8H6
zp5f;0x|NMVi{m4K${XhDX;e{`nx5eCMQjFDyN5e52Mej^NI1A#NTKLi7^o|kQQeX
z7S*b!kIgPolF!M>%M4kHV>r$i)v~dtO60qNJq&&EX4Wx*Dk9FS4P@8ItLupw+vYH_wN&*LB6++a*G8YSk&7!d$Z6}1Lf^xn
zJXF=b&PjtATPCXEoS55cb<|KlH!Z8z&)0`E9xKXZr^W
zLzi|W&_4n=;j*8CWGbzT9hE_2Ovs~xHn@hG3Co;RuRm7fI@am>2*Q#+ID9S~_UFo?
zEQ%QVY)ab8X9vav9N})?A&_}$D4B57;PL?mx5t^20R7T(JGi{v$;cr?zG^;g2O+9s
zB>vmzyy=~4%HSfG!?^_!<+5mB7Rx=8AF56+E)KQUjF`-bOK9vAl;mDp85ub1%RO2B
z7=kA3*ei*sD`!BgswMOJVQ_foK!PPs)m=(am`hGt(J*ow6VOVo&7t-m)vbML}G(;8csPhN(W;
z9CRV^W$)3L(i1W@>AUp8$75V^48kNWf%Dm!}e+R4SU2<
z8Y3-!cMT^LDGh!7QXExE^Fy=4`yLPia>E`ACRUzcUhn6CN6wupdoH<30)D3@wBo-6ipZ4K=SoqJip>q7*_sw1e1Zg|LGdi03O{?Tsh+9tk^>+l!
z7~-tfFh*b_lMkJbnQI{#_bP;2yS$^%QU^EkkM2$vITz|Tp7s_O`R=zG9!%*I(k0^%
z=WT2#kQWeyXKH=Fjb9hC1R4z-N~BgpuMK$_F@Eos5s0s*VoJuL_t@Tzf~5Fnik}4l
z-CYV~3))Hz;Gs(jV29Rys=ZldX;*6{S*Tz}|1JnH{k7CNNZ}K(E^Q%vrDJ|Si7mY?
zBUjV