2 * This file is part of INAV Project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
36 #include "target/SITL/sim/realFlight.h"
37 #include "target/SITL/sim/simple_soap_client.h"
38 #include "target/SITL/sim/xplane.h"
39 #include "target/SITL/sim/simHelper.h"
40 #include "fc/runtime_config.h"
41 #include "drivers/time.h"
42 #include "drivers/accgyro/accgyro_fake.h"
43 #include "drivers/barometer/barometer_fake.h"
44 #include "sensors/battery_sensor_fake.h"
45 #include "sensors/acceleration.h"
46 #include "sensors/barometer.h"
47 #include "drivers/pitotmeter/pitotmeter_fake.h"
48 #include "drivers/compass/compass_fake.h"
49 #include "drivers/rangefinder/rangefinder_virtual.h"
50 #include "io/rangefinder.h"
51 #include "common/utils.h"
52 #include "common/maths.h"
53 #include "flight/mixer.h"
54 #include "flight/servos.h"
55 #include "flight/imu.h"
60 #define RF_MAX_CHANNEL_COUNT 12
62 // "RealFlight Ranch" is located in Sierra Nevada, southern Spain
63 // This is not the Position of the Ranch, it's the Point of 0,0 in the Map (bottom left corner)
64 #define FAKE_LAT 36.910610f
65 #define FAKE_LON -2.876605f
67 static uint8_t pwmMapping
[RF_MAX_PWM_OUTS
];
68 static uint8_t mappingCount
;
70 static pthread_cond_t sockcond1
= PTHREAD_COND_INITIALIZER
;
71 static pthread_cond_t sockcond2
= PTHREAD_COND_INITIALIZER
;
72 static pthread_mutex_t sockmtx
= PTHREAD_MUTEX_INITIALIZER
;
74 static soap_client_t
*client
= NULL
;
75 static soap_client_t
*clientNext
= NULL
;
77 static pthread_t soapThread
;
78 static pthread_t creationThread
;
80 static bool isInitalised
= false;
81 static bool useImu
= false;
85 float m_channelValues
[RF_MAX_PWM_OUTS
];
86 float m_currentPhysicsSpeedMultiplier
;
87 float m_currentPhysicsTime_SEC
;
89 float m_altitudeASL_MTR
;
90 float m_altitudeAGL_MTR
;
91 float m_groundspeed_MPS
;
92 float m_pitchRate_DEGpSEC
;
93 float m_rollRate_DEGpSEC
;
94 float m_yawRate_DEGpSEC
;
96 float m_inclination_DEG
;
98 float m_orientationQuaternion_X
;
99 float m_orientationQuaternion_Y
;
100 float m_orientationQuaternion_Z
;
101 float m_orientationQuaternion_W
;
102 float m_aircraftPositionX_MTR
;
103 float m_aircraftPositionY_MTR
;
104 float m_velocityWorldU_MPS
;
105 float m_velocityWorldV_MPS
;
106 float m_velocityWorldW_MPS
;
107 float m_velocityBodyU_MPS
;
108 float m_velocityBodyV_MPS
;
109 float m_velocityBodyW_MPS
;
110 float m_accelerationWorldAX_MPS2
;
111 float m_accelerationWorldAY_MPS2
;
112 float m_accelerationWorldAZ_MPS2
;
113 float m_accelerationBodyAX_MPS2
;
114 float m_accelerationBodyAY_MPS2
;
115 float m_accelerationBodyAZ_MPS2
;
120 float m_heliMainRotorRPM
;
121 float m_batteryVoltage_VOLTS
;
122 float m_batteryCurrentDraw_AMPS
;
123 float m_batteryRemainingCapacity_MAH
;
124 float m_fuelRemaining_OZ
;
126 bool m_hasLostComponents
;
127 bool m_anEngineIsRunning
;
128 bool m_isTouchingGround
;
129 bool m_flightAxisControllerIsActive
;
130 char* m_currentAircraftStatus
;
131 bool m_resetButtonHasBeenPressed
;
136 static void deleteClient(soap_client_t
*client
)
138 soapClientClose(client
);
143 static void startRequest(char* action
, const char* fmt
, ...)
145 pthread_mutex_lock(&sockmtx
);
146 while (clientNext
== NULL
) {
147 pthread_cond_wait(&sockcond1
, &sockmtx
);
153 pthread_cond_broadcast(&sockcond2
);
154 pthread_mutex_unlock(&sockmtx
);
158 soapClientSendRequestVa(client
, action
, fmt
, va
);
162 static char* endRequest(void)
164 char* ret
= soapClientReceive(client
);
165 deleteClient(client
);
169 // Simple, but fast ;)
170 static double getDoubleFromResponse(const char* response
, const char* elementName
)
176 char* pos
= strstr(response
, elementName
);
180 return atof(pos
+ strlen(elementName
) + 1);
186 static bool getBoolFromResponse(const char* response, const char* elementName)
192 char* pos = strstr(response, elementName);
196 return (strncmp(pos + strlen(elementName) + 1, "true", 4) == 0);
200 static char* getStringFromResponse(const char* response
, const char* elementName
)
206 char* pos
= strstr(response
, elementName
);
211 pos
+= strlen(elementName
) + 1;
212 char* end
= strstr(pos
, "</");
216 size_t length
= end
- pos
;
217 char* ret
= calloc(length
+ 1, sizeof(char));
218 strncpy(ret
, pos
, length
);
222 static bool getChannelValues(const char* response
, uint16_t* channelValues
)
228 const char* channelValueTag
= "<m-channelValues-0to1 xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:double[12]\">";
229 char* pos
= strstr(response
, channelValueTag
);
234 pos
+= strlen(channelValueTag
);
235 for (size_t i
= 0; i
< RF_MAX_CHANNEL_COUNT
; i
++) {
236 char* end
= strstr(pos
, "</");
241 channelValues
[i
] = FLOAT_0_1_TO_PWM((float)atof(pos
+ 6));
249 static void fakeCoords(float posX
, float posY
, float distanceX
, float distanceY
, float *lat
, float *lon
)
251 float m
= 1 / (2 * M_PIf
/ 360 * EARTH_RADIUS
) / 1000;
252 *lat
= (posX
+ (distanceX
* m
));
253 *lon
= (posY
+ (distanceY
* m
) / cosf(posX
* (M_PIf
/ 180)));
256 static float convertAzimuth(float azimuth
)
261 return 360 - fmodf(azimuth
+ 90, 360.0f
);
264 static void exchangeData(void)
266 double servoValues
[RF_MAX_PWM_OUTS
] = { };
267 for (int i
= 0; i
< mappingCount
; i
++) {
268 if (pwmMapping
[i
] & 0x80) { // Motor
269 servoValues
[i
] = (double)PWM_TO_FLOAT_0_1(motor
[pwmMapping
[i
] & 0x7f]);
271 servoValues
[i
] = (double)PWM_TO_FLOAT_0_1(servo
[pwmMapping
[i
]]);
275 startRequest("ExchangeData", "<ExchangeData><pControlInputs><m-selectedChannels>%u</m-selectedChannels>"
276 "<m-channelValues-0to1><item>%.4f</item><item>%.4f</item><item>%.4f</item><item>%.4f</item><item>%.4f</item><item>%.4f</item><item>%.4f</item><item>%.4f</item>"
277 "<item>%.4f</item><item>%.4f</item><item>%.4f</item><item>%.4f</item></m-channelValues-0to1></pControlInputs></ExchangeData>",
279 servoValues
[0], servoValues
[1], servoValues
[2], servoValues
[3], servoValues
[4], servoValues
[5], servoValues
[6], servoValues
[7],
280 servoValues
[8], servoValues
[9], servoValues
[10], servoValues
[11]);
281 char* response
= endRequest();
283 //rfValues.m_currentPhysicsTime_SEC = getDoubleFromResponse(response, "m-currentPhysicsTime-SEC");
284 //rfValues.m_currentPhysicsSpeedMultiplier = getDoubleFromResponse(response, "m-currentPhysicsSpeedMultiplier");
285 rfValues
.m_airspeed_MPS
= getDoubleFromResponse(response
, "m-airspeed-MPS");
286 rfValues
.m_altitudeASL_MTR
= getDoubleFromResponse(response
, "m-altitudeASL-MTR");
287 rfValues
.m_altitudeAGL_MTR
= getDoubleFromResponse(response
, "m-altitudeAGL-MTR");
288 rfValues
.m_groundspeed_MPS
= getDoubleFromResponse(response
, "m-groundspeed-MPS");
289 rfValues
.m_pitchRate_DEGpSEC
= getDoubleFromResponse(response
, "m-pitchRate-DEGpSEC");
290 rfValues
.m_rollRate_DEGpSEC
= getDoubleFromResponse(response
, "m-rollRate-DEGpSEC");
291 rfValues
.m_yawRate_DEGpSEC
= getDoubleFromResponse(response
, "m-yawRate-DEGpSEC");
292 rfValues
.m_azimuth_DEG
= getDoubleFromResponse(response
, "m-azimuth-DEG");
293 rfValues
.m_inclination_DEG
= getDoubleFromResponse(response
, "m-inclination-DEG");
294 rfValues
.m_roll_DEG
= getDoubleFromResponse(response
, "m-roll-DEG");
295 //rfValues.m_orientationQuaternion_X = getDoubleFromResponse(response, "m-orientationQuaternion-X");
296 //rfValues.m_orientationQuaternion_Y = getDoubleFromResponse(response, "m-orientationQuaternion-Y");
297 //rfValues.m_orientationQuaternion_Z = getDoubleFromResponse(response, "m-orientationQuaternion-Z");
298 //rfValues.m_orientationQuaternion_W = getDoubleFromResponse(response, "m-orientationQuaternion-W");
299 rfValues
.m_aircraftPositionX_MTR
= getDoubleFromResponse(response
, "m-aircraftPositionX-MTR");
300 rfValues
.m_aircraftPositionY_MTR
= getDoubleFromResponse(response
, "m-aircraftPositionY-MTR");
301 rfValues
.m_velocityWorldU_MPS
= getDoubleFromResponse(response
, "m-velocityWorldU-MPS");
302 rfValues
.m_velocityWorldV_MPS
= getDoubleFromResponse(response
, "m-velocityWorldV-MPS");
303 rfValues
.m_velocityWorldW_MPS
= getDoubleFromResponse(response
, "m-velocityWorldW-MPS");
304 //rfValues.m_velocityBodyU_MPS = getDoubleFromResponse(response, "m-velocityBodyU-MPS");
305 //rfValues.m_velocityBodyV_MPS = getDoubleFromResponse(response, "mm-velocityBodyV-MPS");
306 //rfValues.m_velocityBodyW_MPS = getDoubleFromResponse(response, "m-velocityBodyW-MPS");
307 //rfValues.m_accelerationWorldAX_MPS2 = getDoubleFromResponse(response, "m-accelerationWorldAX-MPS2");
308 //rfValues.m_accelerationWorldAY_MPS2 = getDoubleFromResponse(response, "m-accelerationWorldAY-MPS2");
309 //rfValues.m_accelerationWorldAZ_MPS2 = getDoubleFromResponse(response, "m-accelerationWorldAZ-MPS2");
310 rfValues
.m_accelerationBodyAX_MPS2
= getDoubleFromResponse(response
, "m-accelerationBodyAX-MPS2");
311 rfValues
.m_accelerationBodyAY_MPS2
= getDoubleFromResponse(response
, "m-accelerationBodyAY-MPS2");
312 rfValues
.m_accelerationBodyAZ_MPS2
= getDoubleFromResponse(response
, "m-accelerationBodyAZ-MPS2");
313 //rfValues.m_windX_MPS = getDoubleFromResponse(response, "m-windX-MPS");
314 //rfValues.m_windY_MPS = getDoubleFromResponse(response, "m-windY-MPS");
315 //rfValues.m_windZ_MPSPS = getDoubleFromResponse(response, "m-windZ-MPS");
316 //rfValues.m_propRPM = getDoubleFromResponse(response, "m-propRPM");
317 //rfValues.m_heliMainRotorRPM = getDoubleFromResponse(response, "m-heliMainRotorRPM");
318 rfValues
.m_batteryVoltage_VOLTS
= getDoubleFromResponse(response
, "m-batteryVoltage-VOLTS");
319 rfValues
.m_batteryCurrentDraw_AMPS
= getDoubleFromResponse(response
, "m-batteryCurrentDraw-AMPS");
320 //rfValues.m_batteryRemainingCapacity_MAH = getDoubleFromResponse(response, "m-batteryRemainingCapacity-MAH");
321 //rfValues.m_fuelRemaining_OZ = getDoubleFromResponse(response, "m-fuelRemaining-OZ");
322 //rfValues.m_isLocked = getBoolFromResponse(response, "m-isLocked");
323 //rfValues.m_hasLostComponents = getBoolFromResponse(response, "m-hasLostComponents");
324 //rfValues.m_anEngineIsRunning = getBoolFromResponse(response, "m-anEngineIsRunning");
325 //rfValues.m_isTouchingGround = getBoolFromResponse(response, "m-isTouchingGround");
326 //rfValues.m_flightAxisControllerIsActive= getBoolFromResponse(response, "m-flightAxisControllerIsActive");
327 rfValues
.m_currentAircraftStatus
= getStringFromResponse(response
, "m-currentAircraftStatus");
330 uint16_t channelValues
[RF_MAX_CHANNEL_COUNT
];
331 getChannelValues(response
, channelValues
);
332 rxSimSetChannelValue(channelValues
, RF_MAX_CHANNEL_COUNT
);
335 fakeCoords(FAKE_LAT
, FAKE_LON
, rfValues
.m_aircraftPositionX_MTR
, -rfValues
.m_aircraftPositionY_MTR
, &lat
, &lon
);
337 int16_t course
= (int16_t)roundf(RADIANS_TO_DECIDEGREES(atan2_approx(-rfValues
.m_velocityWorldU_MPS
,rfValues
.m_velocityWorldV_MPS
)));
338 int32_t altitude
= (int32_t)roundf(rfValues
.m_altitudeASL_MTR
* 100);
342 (int32_t)roundf(lat
* 10000000),
343 (int32_t)roundf(lon
* 10000000),
345 (int16_t)roundf(rfValues
.m_groundspeed_MPS
* 100),
347 0,//(int16_t)roundf(rfValues.m_velocityWorldV_MPS * 100), //not sure about the direction
348 0,//(int16_t)roundf(-rfValues.m_velocityWorldU_MPS * 100),
349 0,//(int16_t)roundf(rfValues.m_velocityWorldW_MPS * 100),
353 int32_t altitudeOverGround
= (int32_t)roundf(rfValues
.m_altitudeAGL_MTR
* 100);
354 if (altitudeOverGround
> 0 && altitudeOverGround
<= RANGEFINDER_VIRTUAL_MAX_RANGE_CM
) {
355 fakeRangefindersSetData(altitudeOverGround
);
357 fakeRangefindersSetData(-1);
360 const int16_t roll_inav
= (int16_t)roundf(rfValues
.m_roll_DEG
* 10);
361 const int16_t pitch_inav
= (int16_t)roundf(-rfValues
.m_inclination_DEG
* 10);
362 const int16_t yaw_inav
= (int16_t)roundf(convertAzimuth(rfValues
.m_azimuth_DEG
) * 10);
364 imuSetAttitudeRPY(roll_inav
, pitch_inav
, yaw_inav
);
365 imuUpdateAttitude(micros());
368 // RealFlights acc data is weird if the aircraft has not yet taken off. Fake 1G in horizontale position
372 if (rfValues
.m_currentAircraftStatus
&& strncmp(rfValues
.m_currentAircraftStatus
, "CAS-WAITINGTOLAUNCH", strlen(rfValues
.m_currentAircraftStatus
)) == 0) {
375 accZ
= (int16_t)(GRAVITY_MSS
* 1000.0f
);
377 accX
= constrainToInt16(rfValues
.m_accelerationBodyAX_MPS2
* 1000);
378 accY
= constrainToInt16(-rfValues
.m_accelerationBodyAY_MPS2
* 1000);
379 accZ
= constrainToInt16(-rfValues
.m_accelerationBodyAZ_MPS2
* 1000);
382 fakeAccSet(accX
, accY
, accZ
);
385 constrainToInt16(rfValues
.m_rollRate_DEGpSEC
* 16.0f
),
386 constrainToInt16(-rfValues
.m_pitchRate_DEGpSEC
* 16.0f
),
387 constrainToInt16(rfValues
.m_yawRate_DEGpSEC
* 16.0f
)
390 fakeBaroSet(altitudeToPressure(altitude
), DEGREES_TO_CENTIDEGREES(21));
391 fakePitotSetAirspeed(rfValues
.m_airspeed_MPS
* 100);
393 fakeBattSensorSetVbat((uint16_t)roundf(rfValues
.m_batteryVoltage_VOLTS
* 100));
394 fakeBattSensorSetAmperage((uint16_t)roundf(rfValues
.m_batteryCurrentDraw_AMPS
* 100));
401 computeQuaternionFromRPY(&quat
, roll_inav
, pitch_inav
, yaw_inav
);
402 transformVectorEarthToBody(&north
, &quat
);
404 constrainToInt16(north
.x
* 16000.0f
),
405 constrainToInt16(north
.y
* 16000.0f
),
406 constrainToInt16(north
.z
* 16000.0f
)
409 free(rfValues
.m_currentAircraftStatus
);
413 static void* soapWorker(void* arg
)
419 startRequest("RestoreOriginalControllerDevice", "<RestoreOriginalControllerDevice><a>1</a><b>2</b></RestoreOriginalControllerDevice>");
421 startRequest("InjectUAVControllerInterface", "<InjectUAVControllerInterface><a>1</a><b>2</b></InjectUAVControllerInterface>");
424 ENABLE_ARMING_FLAG(SIMULATOR_MODE_SITL
);
437 static void* creationWorker(void* arg
)
439 char* ip
= (char*)arg
;
442 pthread_mutex_lock(&sockmtx
);
443 while (clientNext
!= NULL
) {
444 pthread_cond_wait(&sockcond2
, &sockmtx
);
446 pthread_mutex_unlock(&sockmtx
);
448 soap_client_t
*cli
= malloc(sizeof(soap_client_t
));
449 if (!soapClientConnect(cli
, ip
, RF_PORT
)) {
454 pthread_mutex_lock(&sockmtx
);
455 pthread_cond_broadcast(&sockcond1
);
456 pthread_mutex_unlock(&sockmtx
);
462 bool simRealFlightInit(char* ip
, uint8_t* mapping
, uint8_t mapCount
, bool imu
)
464 memcpy(pwmMapping
, mapping
, mapCount
);
465 mappingCount
= mapCount
;
468 if (pthread_create(&soapThread
, NULL
, soapWorker
, NULL
) < 0) {
472 if (pthread_create(&creationThread
, NULL
, creationWorker
, (void*)ip
) < 0) {
476 // Wait until the connection is established, the interface has been initialised
477 // and the first valid packet has been received to avoid problems with the startup calibration.
478 while (!isInitalised
) {