PageRenderTime 63ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/ground/openpilotgcs/src/plugins/hitlnew/il2simulator.cpp

https://github.com/CorvusCorax/my_OpenPilot_mods
C++ | 363 lines | 266 code | 19 blank | 78 comment | 2 complexity | 516c9e6cfc0f100e7fccd02791b10e87 MD5 | raw file
  1. /**
  2. ******************************************************************************
  3. *
  4. * @file il2simulator.cpp
  5. * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
  6. * @brief
  7. * @see The GNU Public License (GPL) Version 3
  8. * @defgroup hitlplugin
  9. * @{
  10. *
  11. *****************************************************************************/
  12. /*
  13. * This program is free software; you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation; either version 3 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful, but
  19. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  20. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  21. * for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License along
  24. * with this program; if not, write to the Free Software Foundation, Inc.,
  25. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  26. */
  27. /*
  28. * Description of DeviceLink Protocol:
  29. * A Request is initiated with R/ followed by id's of to be requested settings
  30. * even id's indicate read only values, odd are write only
  31. * (usually id =get value id+1= set - for same setting)
  32. * id's are separated by /
  33. * requests can contain values to set, or to select a subsystem
  34. * values are separated by \
  35. * example: R/30/48/64\0/64\1/
  36. * request read only settings 30,48 and 64 with parameters 0 and 1
  37. * the answer consists of an A followed by id value pairs in the same format
  38. * example: A/30\0/48\0/64\0\22/64\1\102/
  39. *
  40. * A full protocol description as well as a list of ID's and their meanings
  41. * can be found shipped with IL2 in the file DeviceLink.txt
  42. *
  43. * id's used in this file:
  44. * 30: IAS in km/h (float)
  45. * 32: vario in m/s (float)
  46. * 38: angular speed °/s (float) (which direction? azimuth?)
  47. * 40: barometric alt in m (float)
  48. * 42: flight course in ° (0-360) (float)
  49. * 46: roll angle in ° (-180 - 180) (floatniguration)
  50. * 48: pitch angle in ° (-90 - 90) (float)
  51. * 80/81: engine power (-1.0 (0%) - 1.0 (100%)) (float)
  52. * 84/85: aileron servo (-1.0 - 1.0) (float)
  53. * 86/87: elevator servo (-1.0 - 1.0) (float)
  54. * 88/89: rudder servo (-1.0 - 1.0) (float)
  55. *
  56. * IL2 currently offers no useful way of providing GPS data
  57. * therefore fake GPS data will be calculated using IMS
  58. *
  59. * unfortunately angular acceleration provided is very limited, too
  60. */
  61. #include "il2simulator.h"
  62. #include "extensionsystem/pluginmanager.h"
  63. #include <coreplugin/icore.h>
  64. #include <coreplugin/threadmanager.h>
  65. #include <math.h>
  66. #include <qxtlogger.h>
  67. const float IL2Simulator::FT2M = 0.3048;
  68. const float IL2Simulator::KT2MPS = 0.514444444;
  69. const float IL2Simulator::MPS2KMH = 3.6;
  70. const float IL2Simulator::KMH2MPS = (1.0/3.6);
  71. const float IL2Simulator::INHG2KPA = 3.386;
  72. const float IL2Simulator::RAD2DEG = (180.0/M_PI);
  73. const float IL2Simulator::DEG2RAD = (M_PI/180.0);
  74. const float IL2Simulator::M2DEG = 60.*1852.; // 60 miles per degree times 1852 meters per mile
  75. const float IL2Simulator::DEG2M = (1.0/(60.*1852.));
  76. const float IL2Simulator::AIR_CONST = 287.058; // J/(kg*K)
  77. const float IL2Simulator::GROUNDDENSITY = 1.225; // kg/m³ ;)
  78. const float IL2Simulator::TEMP_GROUND = (15.0 + 273.0); // 15°C in Kelvin
  79. const float IL2Simulator::TEMP_LAPSE_RATE = -0.0065; //degrees per meter
  80. const float IL2Simulator::AIR_CONST_FACTOR = -0.0341631947363104; //several nature constants calculated into one
  81. IL2Simulator::IL2Simulator(const SimulatorSettings& params) :
  82. Simulator(params)
  83. {
  84. }
  85. IL2Simulator::~IL2Simulator()
  86. {
  87. }
  88. void IL2Simulator::setupUdpPorts(const QString& host, int inPort, int outPort)
  89. {
  90. inSocket->connectToHost(host,inPort); // IL2
  91. if(!inSocket->waitForConnected())
  92. qxtLog->error(Name() + " cann't connect to UDP Port: " + QString::number(inPort));
  93. }
  94. void IL2Simulator::transmitUpdate()
  95. {
  96. // Read ActuatorDesired from autopilot
  97. ActuatorDesired::DataFields actData = actDesired->getData();
  98. float ailerons = actData.Roll;
  99. float elevator = actData.Pitch;
  100. float rudder = actData.Yaw;
  101. float throttle = actData.Throttle*2-1.0;
  102. // Send update to Il2
  103. QString cmd;
  104. cmd=QString("R/30/32/40/42/46/48/81\\%1/85\\%2/87\\%3/89\\%4/")
  105. .arg(throttle)
  106. .arg(ailerons)
  107. .arg(elevator)
  108. .arg(rudder);
  109. QByteArray data = cmd.toAscii();
  110. //outSocket->write(data);
  111. inSocket->write(data); // for IL2 must send to the same port as input!!!!!!!!!!!!!
  112. }
  113. /**
  114. * calculate air density from altitude
  115. */
  116. float IL2Simulator::DENSITY(float alt) {
  117. return (GROUNDDENSITY * pow(
  118. ((TEMP_GROUND+(TEMP_LAPSE_RATE*alt))/TEMP_GROUND),
  119. ((AIR_CONST_FACTOR/TEMP_LAPSE_RATE)-1) )
  120. );
  121. }
  122. /**
  123. * calculate air pressure from altitude
  124. */
  125. float IL2Simulator::PRESSURE(float alt) {
  126. return DENSITY(alt)*(TEMP_GROUND+(alt*TEMP_LAPSE_RATE))*AIR_CONST;
  127. }
  128. /**
  129. * calculate TAS from IAS and altitude
  130. */
  131. float IL2Simulator::TAS(float IAS, float alt) {
  132. return (IAS*sqrt(GROUNDDENSITY/DENSITY(alt)));
  133. }
  134. /**
  135. * process data string from flight simulator
  136. */
  137. void IL2Simulator::processUpdate(const QByteArray& inp)
  138. {
  139. // save old flight data to calculate delta's later
  140. old=current;
  141. QString data(inp);
  142. // Split
  143. QStringList fields = data.split("/");
  144. // split up response string
  145. int t;
  146. for (t=0; t<fields.length(); t++) {
  147. QStringList values = fields[t].split("\\");
  148. // parse values
  149. if (values.length()>=2) {
  150. int id = values[0].toInt();
  151. float value = values[1].toFloat();
  152. switch (id) {
  153. case 30:
  154. current.ias=value * KMH2MPS;
  155. break;
  156. case 32:
  157. current.dZ=value;
  158. break;
  159. case 40:
  160. current.Z=value;
  161. break;
  162. case 42:
  163. current.azimuth=value;
  164. break;
  165. case 46:
  166. current.roll=-value;
  167. break;
  168. case 48:
  169. current.pitch=value;
  170. break;
  171. }
  172. }
  173. }
  174. // measure time
  175. current.dT = ((float)time->restart()) / 1000.0;
  176. if (current.dT<0.001) current.dT=0.001;
  177. current.T = old.T+current.dT;
  178. current.i = old.i+1;
  179. if (current.i==1) {
  180. old.dRoll=0;
  181. old.dPitch=0;
  182. old.dAzimuth=0;
  183. old.ddX=0;
  184. old.ddX=0;
  185. old.ddX=0;
  186. }
  187. // calculate TAS from alt and IAS
  188. current.tas = TAS(current.ias,current.Z);
  189. // assume the plane actually flies straight and no wind
  190. // groundspeed is horizontal vector of TAS
  191. current.groundspeed = current.tas*cos(current.pitch*DEG2RAD);
  192. // x and y vector components
  193. current.dX = current.groundspeed*sin(current.azimuth*DEG2RAD);
  194. current.dY = current.groundspeed*cos(current.azimuth*DEG2RAD);
  195. // simple IMS - integration over time the easy way...
  196. current.X = old.X + (current.dX*current.dT);
  197. current.Y = old.Y + (current.dY*current.dT);
  198. // accelerations (filtered)
  199. if (isnan(old.ddX) || isinf(old.ddX)) old.ddX=0;
  200. if (isnan(old.ddY) || isinf(old.ddY)) old.ddY=0;
  201. if (isnan(old.ddZ) || isinf(old.ddZ)) old.ddZ=0;
  202. #define SPEED_FILTER 10
  203. current.ddX = ((current.dX-old.dX)/current.dT + SPEED_FILTER * (old.ddX)) / (SPEED_FILTER+1);
  204. current.ddY = ((current.dY-old.dY)/current.dT + SPEED_FILTER * (old.ddY)) / (SPEED_FILTER+1);
  205. current.ddZ = ((current.dZ-old.dZ)/current.dT + SPEED_FILTER * (old.ddZ)) / (SPEED_FILTER+1);
  206. #define TURN_FILTER 10
  207. // turn speeds (filtered)
  208. if (isnan(old.dAzimuth) || isinf(old.dAzimuth)) old.dAzimuth=0;
  209. if (isnan(old.dPitch) || isinf(old.dPitch)) old.dPitch=0;
  210. if (isnan(old.dRoll) || isinf(old.dRoll)) old.dRoll=0;
  211. current.dAzimuth = (angleDifference(current.azimuth,old.azimuth)/current.dT + TURN_FILTER * (old.dAzimuth)) / (TURN_FILTER+1);
  212. current.dPitch = (angleDifference(current.pitch,old.pitch)/current.dT + TURN_FILTER * (old.dPitch)) / (TURN_FILTER+1);
  213. current.dRoll = (angleDifference(current.roll,old.roll)/current.dT + TURN_FILTER * (old.dRoll)) / (TURN_FILTER+1);
  214. // Update AltitudeActual object
  215. BaroAltitude::DataFields altActualData;
  216. memset(&altActualData, 0, sizeof(BaroAltitude::DataFields));
  217. altActualData.Altitude = current.Z;
  218. altActualData.Temperature = TEMP_GROUND + (current.Z * TEMP_LAPSE_RATE) - 273.0;
  219. altActualData.Pressure = PRESSURE(current.Z)/1000.0; // kpa
  220. // Update attActual object
  221. AttitudeActual::DataFields attActualData;
  222. memset(&attActualData, 0, sizeof(AttitudeActual::DataFields));
  223. attActualData.Roll = current.roll;
  224. attActualData.Pitch = current.pitch;
  225. attActualData.Yaw = current.azimuth;
  226. float rpy[3];
  227. float quat[4];
  228. rpy[0]=current.roll;
  229. rpy[1]=current.pitch;
  230. rpy[2]=current.azimuth;
  231. Utils::CoordinateConversions().RPY2Quaternion(rpy,quat);
  232. attActualData.q1 = quat[0];
  233. attActualData.q2 = quat[1];
  234. attActualData.q3 = quat[2];
  235. attActualData.q4 = quat[3];
  236. // Update positionActual objects
  237. PositionActual::DataFields posData;
  238. memset(&posData, 0, sizeof(PositionActual::DataFields));
  239. posData.North = current.Y*100;
  240. posData.East = current.X*100;
  241. posData.Down = current.Z*-100;
  242. // Update velocityActual objects
  243. VelocityActual::DataFields velData;
  244. memset(&velData, 0, sizeof(VelocityActual::DataFields));
  245. velData.North = current.dY*100;
  246. velData.East = current.dX*100;
  247. velData.Down = current.dZ*100;
  248. // Update AttitudeRaw object (filtered gyros only for now)
  249. AttitudeRaw::DataFields rawData;
  250. memset(&rawData, 0, sizeof(AttitudeRaw::DataFields));
  251. rawData = attRaw->getData();
  252. rawData.gyros[0] = current.dRoll;
  253. rawData.gyros[1] = cos(DEG2RAD * current.roll) * current.dPitch + sin(DEG2RAD * current.roll) * current.dAzimuth;
  254. rawData.gyros[2] = cos(DEG2RAD * current.roll) * current.dAzimuth - sin(DEG2RAD * current.roll) * current.dPitch;
  255. rawData.accels[0] = current.ddX;
  256. rawData.accels[1] = current.ddY;
  257. rawData.accels[2] = current.ddZ;
  258. // Update homelocation
  259. HomeLocation::DataFields homeData;
  260. memset(&homeData, 0, sizeof(HomeLocation::DataFields));
  261. homeData = posHome->getData();
  262. homeData.Latitude = settings.latitude.toFloat() * 10e6;
  263. homeData.Longitude = settings.longitude.toFloat() * 10e6;
  264. homeData.Altitude = 0;
  265. double LLA[3];
  266. LLA[0]=settings.latitude.toFloat();
  267. LLA[1]=settings.longitude.toFloat();
  268. LLA[2]=0;
  269. double ECEF[3];
  270. double RNE[9];
  271. Utils::CoordinateConversions().RneFromLLA(LLA,(double (*)[3])RNE);
  272. for (int t=0;t<9;t++) {
  273. homeData.RNE[t]=RNE[t];
  274. }
  275. Utils::CoordinateConversions().LLA2ECEF(LLA,ECEF);
  276. homeData.ECEF[0]=ECEF[0]*100;
  277. homeData.ECEF[1]=ECEF[1]*100;
  278. homeData.ECEF[2]=ECEF[2]*100;
  279. homeData.Be[0]=0;
  280. homeData.Be[1]=0;
  281. homeData.Be[2]=0;
  282. homeData.Set=1;
  283. // Update gps objects
  284. GPSPosition::DataFields gpsData;
  285. memset(&gpsData, 0, sizeof(GPSPosition::DataFields));
  286. gpsData.Altitude = current.Z;
  287. gpsData.Heading = current.azimuth;
  288. gpsData.Groundspeed = current.groundspeed;
  289. double NED[3];
  290. NED[0] = current.Y;
  291. NED[1] = current.X;
  292. NED[2] = -current.Z;
  293. Utils::CoordinateConversions().GetLLA(ECEF,NED,LLA);
  294. gpsData.Latitude = LLA[0] * 10e6;
  295. gpsData.Longitude = LLA[1] * 10e6;
  296. gpsData.Satellites = 7;
  297. gpsData.Status = GPSPosition::STATUS_FIX3D;
  298. // issue manual update
  299. // update every time (50ms)
  300. attActual->setData(attActualData);
  301. attActual->updated();
  302. attRaw->setData(rawData);
  303. attRaw->updated();
  304. // update every 5th time (250 ms)
  305. if ( ! ((old.i-1) % 5) ) {
  306. velActual->setData(velData);
  307. velActual->updated();
  308. posActual->setData(posData);
  309. posActual->updated();
  310. altActual->setData(altActualData);
  311. altActual->updated();
  312. gpsPos->setData(gpsData);
  313. gpsPos->updated();
  314. }
  315. // update every 20th time (1000ms)
  316. if ( ! ((old.i-1) % 20) ) {
  317. posHome->setData(homeData);
  318. posHome->updated();
  319. }
  320. }
  321. /**
  322. * process data string from flight simulator
  323. */
  324. float IL2Simulator::angleDifference(float a, float b)
  325. {
  326. float d=a-b;
  327. if (d>180) d-=360;
  328. if (d<-180)d+=360;
  329. return d;
  330. }