diff --git a/C/src/xplaneConnect.c b/C/src/xplaneConnect.c index 9157b35..98be7f5 100755 --- a/C/src/xplaneConnect.c +++ b/C/src/xplaneConnect.c @@ -560,13 +560,13 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac) } if (size < 1 || size > 7) { - printError("sendCTRL", "size should be a value between 1 and 6."); + printError("sendCTRL", "size should be a value between 1 and 7."); return -2; } // Setup Command // 5 byte header + 5 float values * 4 + 2 byte values - unsigned char buffer[27] = "CTRL"; + unsigned char buffer[31] = "CTRL"; int cur = 5; for (int i = 0; i < 6; i++) { @@ -587,9 +587,10 @@ int sendCTRL(XPCSocket sock, float values[], int size, char ac) } } buffer[26] = ac; + *((float*)(buffer + 27)) = size == 7 ? values[6]: -998; // Send Command - if (sendUDP(sock, buffer, 27) < 0) + if (sendUDP(sock, buffer, 31) < 0) { printError("sendCTRL", "Failed to send command"); return -3; diff --git a/C/src/xplaneConnect.h b/C/src/xplaneConnect.h index e8520c3..abcfd1f 100644 --- a/C/src/xplaneConnect.h +++ b/C/src/xplaneConnect.h @@ -182,8 +182,8 @@ int sendPOSI(XPCSocket sock, float values[], int size, char ac); /// /// \param sock The socket to use to send the command. /// \param values An array representing position data about the aircraft. The format of values is -/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps]. If less than 6 values are -/// specified, the unspecified values will be left unchanged. +/// [Elevator, Aileron, Rudder, Throttle, Gear, Flaps, Speed Brakes]. If less than +/// 6 values are specified, the unspecified values will be left unchanged. /// \param size The number of elements in values. /// \param ac The aircraft number to set the control surfaces of. 0 for the player aircraft. /// \returns 0 if successful, otherwise a negative value. diff --git a/Java/src/XPlaneConnect.java b/Java/src/XPlaneConnect.java index 0bc6453..511613b 100644 --- a/Java/src/XPlaneConnect.java +++ b/Java/src/XPlaneConnect.java @@ -422,9 +422,9 @@ public class XPlaneConnect implements AutoCloseable { throw new IllegalArgumentException("ctrl must no be null."); } - if(values.length > 6) + if(values.length > 7) { - throw new IllegalArgumentException("ctrl must have 6 or fewer elements."); + throw new IllegalArgumentException("ctrl must have 7 or fewer elements."); } if(ac < 0 || ac > 9) { @@ -434,35 +434,28 @@ public class XPlaneConnect implements AutoCloseable //Pad command values and convert to bytes int i; int cur = 0; - ByteBuffer bb = ByteBuffer.allocate(22); + ByteBuffer bb = ByteBuffer.allocate(26); bb.order(ByteOrder.LITTLE_ENDIAN); - for(i = 0; i < values.length; ++i) + for(i = 0; i < 6; ++i) { if(i == 4) { bb.put(cur, (byte) values[i]); cur += 1; } + else if (i >= values.length) + { + bb.putFloat(cur, -998); + cur+= 4; + } else { bb.putFloat(cur, values[i]); cur += 4; } } - for(; i < 6; ++i) - { - if(i == 4) - { - bb.put(cur, (byte) 0); - cur += 1; - } - else - { - bb.putFloat(cur, -998); - cur += 4; - } - } - bb.put(cur, (byte) ac); + bb.put(cur++, (byte) ac); + bb.putFloat(cur, values.length == 7 ? values[6] : -998); //Build and send message ByteArrayOutputStream os = new ByteArrayOutputStream(); diff --git a/MATLAB/+XPlaneConnect/XPlaneConnect.jar b/MATLAB/+XPlaneConnect/XPlaneConnect.jar index 495f222..755bb29 100644 Binary files a/MATLAB/+XPlaneConnect/XPlaneConnect.jar and b/MATLAB/+XPlaneConnect/XPlaneConnect.jar differ diff --git a/MATLAB/+XPlaneConnect/sendCTRL.m b/MATLAB/+XPlaneConnect/sendCTRL.m index b3d85b3..822f5a0 100644 --- a/MATLAB/+XPlaneConnect/sendCTRL.m +++ b/MATLAB/+XPlaneConnect/sendCTRL.m @@ -9,6 +9,7 @@ function sendCTRL( values, ac, socket ) % 4. Throttle [-1, 1] % 5. Gear (0=up, 1=down) % 6. Flaps [0, 1] +% 7. Speed Brakes [-0.5, 1.5] % ac (optional): The aircraft to set. 0 for the player aircraft. % socket (optional): The client to use when sending the command. % diff --git a/MATLAB/+XPlaneConnect/sendDREF.m b/MATLAB/+XPlaneConnect/sendDREF.m index 65e23aa..cbf2119 100644 --- a/MATLAB/+XPlaneConnect/sendDREF.m +++ b/MATLAB/+XPlaneConnect/sendDREF.m @@ -35,4 +35,4 @@ end value = single(value); %%Send command -socket.setDREF(dref, value); \ No newline at end of file +socket.sendDREF(dref, value); \ No newline at end of file diff --git a/Python/src/xplaneConnect.py b/Python/src/xplaneConnect.py index 9a38c82..5138d55 100644 --- a/Python/src/xplaneConnect.py +++ b/Python/src/xplaneConnect.py @@ -174,10 +174,11 @@ class XPlaneConnect(object): * Throttle [-1, 1] * Gear (0=up, 1=down) * Flaps [0, 1] + * Speedbrakes [-0.5, 1.5] ac: The aircraft to set the control surfaces of. 0 is the main/player aircraft. ''' # Preconditions - if len(values) < 1 or len(values) > 6: + if len(values) < 1 or len(values) > 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.") @@ -189,11 +190,13 @@ class XPlaneConnect(object): if i < len(values): val = values[i] if i == 4: - val = -1 if val == -998 else val - buffer += struct.pack("B", val) + val = -1 if (abs(val + 998) < 1e-4) else val + buffer += struct.pack("b", val) else: buffer += struct.pack(" 1e-2) + if (fabs(data[i][0] - expected[i]) > 1e-2) { return -i - 31; } @@ -489,8 +490,9 @@ int sendCTRLTest() } sock = openUDP(IP); - // Execute 2 + // Execute 3 // Set non-zero pitch, roll, & yaw. Also set throttle, gear, and flaps + float expected[] = { data[0][0], data[1][0], data[2][0], CTRL[3], CTRL[4], CTRL[5] }; CTRL[0] = -998.0F; CTRL[1] = -998.0F; CTRL[2] = -998.0F; @@ -507,7 +509,7 @@ int sendCTRLTest() } for (int i = 0; i < 6; i++) { - if (fabs(data[i][0] - CTRL[i]) > 1e-2) + if (fabs(data[i][0] - expected[i]) > 1e-2) { return -i - 31; } @@ -516,6 +518,97 @@ int sendCTRLTest() return 0; } + +int sendCTRLspeedbrakeTest() // sendCTRL test +{ + // Initialize + char* dref = "sim/flightmodel/controls/sbrkrqst"; + float data; + int size = 1; + float CTRL[7] = { -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -0.5F }; + XPCSocket sock = openUDP(IP); + + // Execute 1 + // Arm speedbrakes + sendCTRL(sock, CTRL, 7, 0); + int result = getDREF(sock, dref, &data, &size); + + // Close socket + closeUDP(sock); + + // Tests + if (result < 0) + { + return -1; + } + if (fabs(data - CTRL[6]) > 1e-4) + { + return -11; + } + + sock = openUDP(IP); + // Execute 2 + // Set full speedbrakes + CTRL[6] = 1.0F; + sendCTRL(sock, CTRL, 7, 0); + result = getDREF(sock, dref, &data, &size); + + // Close socket + closeUDP(sock); + + // Tests + if (result < 0) + { + return -2; + } + if (fabs(data - CTRL[6]) > 1e-4) + { + return -21; + } + + sock = openUDP(IP); + // Execute 3 + // Retract speedbrakes + CTRL[6] = 0.0F; + sendCTRL(sock, CTRL, 7, 0); + result = getDREF(sock, dref, &data, &size); + + // Close socket + closeUDP(sock); + + // Tests + if (result < 0) + { + return -3; + } + if (fabs(data - CTRL[6]) > 1e-4) + { + return -31; + } + + sock = openUDP(IP); + // Execute 4 + // Verify -998 does not change value. + CTRL[6] = -998.0F; + sendCTRL(sock, CTRL, 7, 0); + result = getDREF(sock, dref, &data, &size); + + // Close socket + closeUDP(sock); + + // Tests + if (result < 0) + { + return -4; + } + if (data> 1e-4) + { + return -41; + } + + return 0; +} + int psendPOSITest() // sendPOSI test { // Initialization @@ -952,6 +1045,7 @@ int main(int argc, const char * argv[]) runTest(sendDATATest, "DATA"); runTest(sendCTRLTest, "CTRL"); runTest(psendCTRLTest, "CTRL (player)"); + runTest(sendCTRLspeedbrakeTest, "CTRL (speedbrake)"); runTest(sendPOSITest, "POSI"); runTest(psendPOSITest, "POSI (player)"); runTest(sendWYPTTest, "WYPT"); diff --git a/TestScripts/Java Tests/XPlaneConnectTest.java b/TestScripts/Java Tests/XPlaneConnectTest.java index 9223f3d..216a3d7 100644 --- a/TestScripts/Java Tests/XPlaneConnectTest.java +++ b/TestScripts/Java Tests/XPlaneConnectTest.java @@ -463,6 +463,34 @@ public class XPlaneConnectTest } } + @Test + public void testSendCTRL_Speedbrakes() throws IOException + { + String dref = "sim/flightmodel/controls/sbrkrqst"; + float[] ctrl = new float[] { -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -998.0F, -0.5F }; + try(XPlaneConnect xpc = new XPlaneConnect()) + { + // Speedbrakes armed + xpc.sendCTRL(ctrl); + float[] result = xpc.getDREF(dref); + + assertEquals(-0.5F, result[0], 1e-4); + + ctrl[6] = 1.0F; // Deploy speedbrakes + xpc.sendCTRL(ctrl); + result = xpc.getDREF(dref); + + assertEquals(1.0F, result[0], 1e-4); + + ctrl[6] = 0.0F; // Retract speedbrakes + xpc.sendCTRL(ctrl); + result = xpc.getDREF(dref); + + assertEquals(0.0F, result[0], 1e-4); + } + + } + @Test(expected = IllegalArgumentException.class) public void testSendCTRL_NullCtrl() throws IOException { @@ -475,7 +503,7 @@ public class XPlaneConnectTest @Test(expected = IllegalArgumentException.class) public void testSendCTRL_LongCtrl() throws IOException { - float[] ctrl = new float[] {0, 0, 1, 0.8F, 0, 1, -998}; + float[] ctrl = new float[] {0, 0, 1, 0.8F, 0, 1, 0, -998}; try(XPlaneConnect xpc = new XPlaneConnect()) { xpc.sendCTRL(ctrl); diff --git a/TestScripts/Python Tests/Tests.py b/TestScripts/Python Tests/Tests.py index 78c6e32..fc98c26 100644 --- a/TestScripts/Python Tests/Tests.py +++ b/TestScripts/Python Tests/Tests.py @@ -171,7 +171,7 @@ class XPCTests(unittest.TestCase): expected = 0.0 do_test() - + def test_sendCTRL(self): # Setup drefs = ["sim/cockpit2/controls/yoke_pitch_ratio",\ @@ -209,6 +209,36 @@ class XPCTests(unittest.TestCase): ctrl = [ 0.0, 0.0, 0.0, 0.8, 1.0, 0.0 ] do_test() + def test_sendCTRL_speedbrake(self): + # Setup + dref = "sim/flightmodel/controls/sbrkrqst" + ctrl = [] + + def do_test(): + client = xpc.XPlaneConnect() + + # Execute + client.sendCTRL(ctrl) + result = client.getDREF(dref) + + # Cleanup + client.close() + + # Tests + self.assertAlmostEqual(result[0], ctrl[6]) + + # Test 1 + ctrl = [-998, -998, -998, -998, -998, -998, -0.5] + do_test() + + # Test 2 + ctrl[6] = 1.0 + do_test() + + # Test 2 + ctrl[6] = 0.0 + do_test() + def test_sendPOSI(self): # Setup drefs = ["sim/flightmodel/position/latitude",\ diff --git a/xpcPlugin/DataManager.cpp b/xpcPlugin/DataManager.cpp index 720f799..178ac47 100644 --- a/xpcPlugin/DataManager.cpp +++ b/xpcPlugin/DataManager.cpp @@ -69,6 +69,9 @@ namespace XPC drefs.insert(make_pair(DREF_FlapSetting, XPLMFindDataRef("sim/flightmodel/controls/flaprqst"))); drefs.insert(make_pair(DREF_FlapActual, XPLMFindDataRef("sim/flightmodel/controls/flaprat"))); + drefs.insert(make_pair(DREF_SpeedBrakeSet, XPLMFindDataRef("sim/flightmodel/controls/sbrkrqst"))); + drefs.insert(make_pair(DREF_SpeedBrakeActual, XPLMFindDataRef("sim/flightmodel/controls/sbrkrat"))); + drefs.insert(make_pair(DREF_GearDeploy, XPLMFindDataRef("sim/aircraft/parts/acf_gear_deploy"))); drefs.insert(make_pair(DREF_GearHandle, XPLMFindDataRef("sim/cockpit/switches/gear_handle_status"))); drefs.insert(make_pair(DREF_BrakeParking, XPLMFindDataRef("sim/flightmodel/controls/parkbrakel"))); @@ -169,7 +172,7 @@ namespace XPC sprintf(multi, "sim/multiplayer/position/plane%i_spoiler_ratio", i); mdrefs[i][DREF_Spoiler] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_speedbrake_ratio", i); - mdrefs[i][DREF_BrakeSpeed] = XPLMFindDataRef(multi); + mdrefs[i][DREF_SpeedBrakeSet] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_slat_ratio", i); mdrefs[i][DREF_Slats] = XPLMFindDataRef(multi); sprintf(multi, "sim/multiplayer/position/plane%i_wing_sweep", i); diff --git a/xpcPlugin/DataManager.h b/xpcPlugin/DataManager.h index 9adb9ef..74010d2 100644 --- a/xpcPlugin/DataManager.h +++ b/xpcPlugin/DataManager.h @@ -105,7 +105,8 @@ namespace XPC // Multiplayer Aircraft DREF_FlapActual2, DREF_Spoiler, - DREF_BrakeSpeed, + DREF_SpeedBrakeSet, + DREF_SpeedBrakeActual, DREF_Sweep, DREF_Slats, diff --git a/xpcPlugin/MessageHandlers.cpp b/xpcPlugin/MessageHandlers.cpp index 1522458..17e5cb0 100644 --- a/xpcPlugin/MessageHandlers.cpp +++ b/xpcPlugin/MessageHandlers.cpp @@ -169,7 +169,7 @@ namespace XPC std::size_t size = msg.GetSize(); //Legacy packets that don't specify an aircraft number should be 26 bytes long. //Packets specifying an A/C num should be 27 bytes. - if (size != 26 && size != 27) + if (size != 26 && size != 27 && size != 31) { #if LOG_VERBOSITY > 0 Log::FormatLine("[CTRL] ERROR: Unexpected message length (%i)", size); @@ -185,25 +185,43 @@ namespace XPC char gear = buffer[21]; float flaps = *((float*)(buffer + 22)); unsigned char aircraft = 0; - if (size == 27) + if (size >= 27) { aircraft = buffer[26]; } - - float thrArray[8]; - for (int i = 0; i < 8; ++i) + float spdbrk = -998; + if (size >= 31) { - thrArray[i] = thr; + spdbrk = *((float*)(buffer + 27)); } - DataManager::Set(DREF_YokePitch, pitch, aircraft); - DataManager::Set(DREF_YokeRoll, roll, aircraft); - DataManager::Set(DREF_YokeHeading, yaw, aircraft); - DataManager::Set(DREF_ThrottleSet, thrArray, 8, aircraft); - DataManager::Set(DREF_ThrottleActual, thrArray, 8, aircraft); - if (aircraft == 0) + + if (pitch < -999.5 || pitch > -997.5) { - DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", thrArray, 1); + DataManager::Set(DREF_YokePitch, pitch, aircraft); + } + if (roll < -999.5 || roll > -997.5) + { + DataManager::Set(DREF_YokeRoll, roll, aircraft); + } + if (yaw < -999.5 || yaw > -997.5) + { + DataManager::Set(DREF_YokeHeading, yaw, aircraft); + } + if (thr < -999.5 || thr > -997.5) + { + + float thrArray[8]; + for (int i = 0; i < 8; ++i) + { + thrArray[i] = thr; + } + DataManager::Set(DREF_ThrottleSet, thrArray, 8, aircraft); + DataManager::Set(DREF_ThrottleActual, thrArray, 8, aircraft); + if (aircraft == 0) + { + DataManager::Set("sim/flightmodel/engine/ENGN_thro_override", thrArray, 1); + } } if (gear != -1) { @@ -213,6 +231,10 @@ namespace XPC { DataManager::Set(DREF_FlapSetting, flaps, aircraft); } + if (spdbrk < -999.5 || spdbrk > -997.5) + { + DataManager::Set(DREF_SpeedBrakeSet, spdbrk, aircraft); + } } void MessageHandlers::HandleData(Message& msg)