Rigs of Rods 2026.01
Soft-body Physics Simulation
Loading...
Searching...
No Matches
AutoPilot.cpp
Go to the documentation of this file.
1/*
2 This source file is part of Rigs of Rods
3 Copyright 2005-2012 Pierre-Michel Ricordel
4 Copyright 2007-2012 Thomas Fischer
5
6 For more information, see http://www.rigsofrods.org/
7
8 Rigs of Rods is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 3, as
10 published by the Free Software Foundation.
11
12 Rigs of Rods is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "AutoPilot.h"
22
23#include "Application.h"
24#include "GameContext.h"
25#include "SimData.h"
26#include "SoundScriptManager.h"
27#include "Terrain.h"
28#include "GfxWater.h"
29
30using namespace Ogre;
31using namespace RoR;
32
34 m_actor_id(actor_id)
35{
36 ref_l = nullptr;
37 ref_r = nullptr;
38 ref_b = nullptr;
39 ref_c = nullptr;
40 ref_span = 1.0f;
41 reset();
42}
43
45{
46 force_disabled = false;
49 mode_ias = false;
50 mode_gpws = true;
51 heading = 0;
52 alt = 1000;
53 vs = 0;
54 ias = 150;
55 last_elevator = 0;
56 last_rudder = 0;
57 last_aileron = 0;
60 m_ils_angle_vdev = -90;
61 m_ils_angle_hdev = -90;
65 wantsdisconnect = false;
66
69}
70
82
83void Autopilot::setForceDisabled(bool disabled)
84{
85 force_disabled = disabled;
86}
87
89{
90 ref_l = refl;
91 ref_r = refr;
92 ref_b = refb;
93 ref_c = refc; // ar_camera_node_pos(0)
94 ref_span = (refl->RelPosition - refr->RelPosition).length();
95}
96
98{
99 if (!force_disabled)
100 {
101 float val = 0;
102 if (ref_l && ref_r)
103 {
104 float rat = (ref_r->RelPosition.y - ref_l->RelPosition.y) / ref_span;
105 float bank = 90.0;
106 if (rat >= 1.0)
107 bank = 90.0;
108 else if (rat <= -1.0)
109 bank = -90.0;
110 else
111 bank = 57.3 * asin(rat);
113 {
114 val = bank / 100.0;
115 if (val > 0.5)
116 val = 0.5;
117 if (val < -0.5)
118 val = -0.5;
119 }
121 {
122 Vector3 vel = (ref_l->Velocity + ref_r->Velocity) / 2.0;
123 float curdir = atan2(vel.x, -vel.z) * 57.295779513082;
124 float want_bank = curdir - (float)heading;
125 if (want_bank < -180.0)
126 want_bank += 360.0;
127 want_bank = want_bank * 2.0;
128 if (want_bank > 45.0)
129 want_bank = 45.0;
130 if (want_bank < -45.0)
131 want_bank = -45.0;
132 val = (bank - want_bank) / 100.0;
133 if (val > 0.5)
134 val = 0.5;
135 if (val < -0.5)
136 val = -0.5;
137 }
139 {
140 //compute intercept heading
141 float error_heading = m_ils_angle_hdev / 10.0;
142 if (error_heading > 1.0)
143 error_heading = 1.0;
144 if (error_heading < -1.0)
145 error_heading = -1.0;
146 float offcourse_tolerance = m_ils_runway_distance / 30.0;
147 if (offcourse_tolerance > 60.0)
148 offcourse_tolerance = 60.0;
149 float intercept_heading = m_ils_runway_heading + error_heading * offcourse_tolerance;
150 Vector3 vel = (ref_l->Velocity + ref_r->Velocity) / 2.0;
151 float curdir = atan2(vel.x, -vel.z) * 57.295779513082;
152 float want_bank = curdir - intercept_heading;
153 if (want_bank < -180.0)
154 want_bank += 360.0;
155 want_bank = want_bank * 2.0;
156 if (want_bank > 45.0)
157 want_bank = 45.0;
158 if (want_bank < -45.0)
159 want_bank = -45.0;
160 val = (bank - want_bank) / 100.0;
161 if (val > 0.5)
162 val = 0.5;
163 if (val < -0.5)
164 val = -0.5;
165 }
166 }
167 last_aileron = (last_aileron + val) / 2.0;
168 return last_aileron;
169 }
170 else
171 {
172 return 0;
173 }
174}
175
177{
178 float val = 0;
179 if (ref_l && ref_r && ref_b && !force_disabled)
180 {
181 float wanted_vs = (float)vs / 196.87;
182 float current_vs = (ref_l->Velocity.y + ref_r->Velocity.y) / 2.0;
183 float pitch_var = current_vs - ref_b->Velocity.y;
184 if (mode_alt == ALT_VS)
185 {
187 {
188 //this is cheating a bit
189 float ch = m_ils_runway_distance * sin((m_ils_angle_vdev + 4.0) / 57.295779513082);
190 float oh = m_ils_runway_distance * sin((4.0) / 57.295779513082);
191 wanted_vs = 5000.0 / 196.87;
192 float wanted_vs2 = (-ch + oh) / 5.0;
193 if (wanted_vs2 < -wanted_vs)
194 wanted_vs2 = -wanted_vs;
195 if (wanted_vs2 > wanted_vs)
196 wanted_vs2 = wanted_vs;
197 val = (wanted_vs2 - current_vs) / 40.0 + pitch_var / 40.0;
198 if (val > 0.75)
199 val = 0.75;
200 if (val < -0.75)
201 val = -0.75;
202 }
203 else
204 {
205 val = (wanted_vs - current_vs) / 40.0 + pitch_var / 40.0;
206 if (val > 0.5)
207 val = 0.5;
208 if (val < -0.5)
209 val = -0.5;
210 }
211 }
212 if (mode_alt == ALT_FIXED)
213 {
214 float wanted_alt = (float)alt * 0.3048;
215 float current_alt = (ref_l->AbsPosition.y + ref_r->AbsPosition.y) / 2.0;
216 if (wanted_vs < 0)
217 wanted_vs = -wanted_vs; //absolute value
218 float wanted_vs2 = (wanted_alt - current_alt) / 8.0;
219 if (wanted_vs2 < -wanted_vs)
220 wanted_vs2 = -wanted_vs;
221 if (wanted_vs2 > wanted_vs)
222 wanted_vs2 = wanted_vs;
223 val = (wanted_vs2 - current_vs) / 40.0 + pitch_var / 40.0;
224 if (val > 0.5)
225 val = 0.5;
226 if (val < -0.5)
227 val = -0.5;
228 }
229 }
230 return val;
231}
232
234{
235 return 0;
236}
237
238float Autopilot::getThrottle(float thrtl, float dt)
239{
240 if (!mode_ias || force_disabled) { return thrtl; };
241
242 float val = thrtl;
243 if (ref_l && ref_r)
244 {
245 //tropospheric model valid up to 11.000m (33.000ft)
246 float altitude = ref_l->AbsPosition.y;
247 //float sea_level_temperature=273.15+15.0; //in Kelvin
248 float sea_level_pressure = 101325; //in Pa
249 //float airtemperature=sea_level_temperature-altitude*0.0065; //in Kelvin
250 float airpressure = sea_level_pressure * pow(1.0 - 0.0065 * altitude / 288.15, 5.24947); //in Pa
251 float airdensity = airpressure * 0.0000120896;//1.225 at sea level
252
253 float gspd = 1.94384449 * ((ref_l->Velocity + ref_r->Velocity) / 2.0).length();
254
255 float spd = gspd * sqrt(airdensity / 1.225); //KIAS
256
257 if (spd > (float)ias)
258 val = val - dt / 15.0;
259 if (spd < (float)ias)
260 val = val + dt / 15.0;
261 if (val > 1.0)
262 val = 1.0;
263 if (val < 0.02)
264 val = 0.02;
265 }
266 return val;
267}
268
270{
271 if (mode == mode_heading)
273 else
274 mode_heading = mode;
275 return mode_heading;
276}
277
279{
280 if (mode == mode_alt)
282 else
283 mode_alt = mode;
284 return mode_alt;
285}
286
288{
290 return mode_ias;
291}
292
294{
296 return mode_gpws;
297}
298
300{
301 heading += d;
302 if (heading < 0)
303 heading += 360;
304 if (heading > 359)
305 heading -= 360;
306 return heading;
307}
308
310{
311 alt += d;
312 return alt;
313}
314
316{
317 vs += d;
318 if (vs > 9900)
319 vs = 9900;
320 if (vs < -9900)
321 vs = -9900;
322 return vs;
323}
324
326{
327 ias += d;
328 if (ias < 0)
329 ias = 0;
330 if (ias > 350)
331 ias = 350;
332 return ias;
333}
334
335void Autopilot::gpws_update(float spawnheight)
336{
337#ifdef USE_OPENAL
338 if (App::GetSoundScriptManager()->isDisabled())
339 return;
340 if (mode_gpws && ref_b)
341 {
343 if (App::GetGameContext()->GetTerrain()->getWater() && groundalt < App::GetGameContext()->GetTerrain()->getWater()->GetStaticWaterHeight())
345 float height = (ref_c->AbsPosition.y - groundalt - spawnheight) * 3.28083f; //in feet!
346 //skip height warning sounds when the plane is slower then ~10 knots
347 if ((ref_c->Velocity.length() * 1.9685f) > 10.0f)
348 {
349 if (height < 10 && last_gpws_height > 10)
351 if (height < 20 && last_gpws_height > 20)
353 if (height < 30 && last_gpws_height > 30)
355 if (height < 40 && last_gpws_height > 40)
357 if (height < 50 && last_gpws_height > 50)
359 if (height < 100 && last_gpws_height > 100)
361 }
362 last_gpws_height = height;
363
364 // pullup warning calculation
365 // height to meters
366 height *= 0.3048;
367 // get the y-velocity in meters/s
368 float yVel = ref_c->Velocity.y * 1.9685f;
369 // will trigger the pullup sound when vvi is high (avoid pullup warning when landing normal) and groundcontact will be in less then 10 seconds
370 if (yVel * 10.0f < -height && yVel < -10.0f)
372 }
373#endif //OPENAL
374}
375
377{
378 if (!ref_l || !ref_r)
379 return;
380 Vector3 position = (ref_l->AbsPosition + ref_r->AbsPosition) / 2.0;
381 float closest_hdist = -1;
382 float closest_hangle = -90;
383 float closest_vdist = -1;
384 float closest_vangle = -90;
386
388 for (size_t i = 0; i < localizers.size(); i++)
389 {
390 Plane hplane = Plane(Vector3::UNIT_Y, 0);
391 Vector3 plocd = hplane.projectVector(localizers[i].rotation * Vector3::UNIT_Z);
392 float loc = atan2(plocd.z, plocd.x);
393 Vector3 posd = hplane.projectVector(position - localizers[i].position);
394 float dir = atan2(posd.z, posd.x);
395 float diff = (dir - loc) * 57.295779513082;
396 if (diff > 180.0)
397 diff -= 360.0;
398 if (diff < -180.0)
399 diff += 360.0;
400 if (diff < 80 && diff > -80)
401 {
402 //we are in the visibility cone, we search the closest
403 float dist = (localizers[i].position - position).length();
404 //horizontal
405 if (localizers[i].type == LOCALIZER_HORIZONTAL)
406 {
407 if (closest_hdist < 0 || closest_hdist > dist)
408 {
409 closest_hdist = dist;
410 closest_hangle = diff;
411 m_ils_runway_heading = loc * 57.295779513082 - 90.0; //dont ask me why
412 if (m_ils_runway_heading < 0.0)
413 m_ils_runway_heading += 360.0;
415 }
416 }
417 //vertical
418 if (localizers[i].type == LOCALIZER_VERTICAL)
419 {
420 if (closest_vdist < 0 || closest_vdist > dist)
421 {
422 Vector3 normv = (localizers[i].rotation * Vector3::UNIT_Z).crossProduct(Vector3::UNIT_Y);
423 Plane vplane = Plane(normv, 0);
424 float glideslope = 4; //4 degree is the norm
425 Vector3 posd2 = vplane.projectVector(position - localizers[i].position);
426 float d = posd2.length();
427 if (d < 0.01)
428 d = 0.01;
429 float dir2 = 90.0;
430 d = posd2.y / d;
431 if (d >= 1.0)
432 dir2 = 90.0;
433 else if (d <= -1.0)
434 dir2 = -90.0;
435 else
436 dir2 = asin(d) * 57.295779513082;
437 float diff2 = dir2 - glideslope;
438 closest_vdist = dist;
439 closest_vangle = diff2;
440 }
441 }
442 }
443 }
444 m_ils_angle_hdev = closest_hangle;
445 m_ils_angle_vdev = closest_vangle;
446
447 m_horizontal_locator_available = (closest_hdist != -1);
448 m_vertical_locator_available = (closest_vdist != -1);
449
450 if (mode_heading == HEADING_NAV && mode_gpws && closest_hdist > 10.0 && closest_hdist < 350.0 && last_closest_hdist > 10.0 && last_closest_hdist > 350.0)
451 {
453 }
454
455 last_closest_hdist = closest_hdist;
456
457 // We let custom autopilot scripts to have their own NAV mode
458 // engagement/disengagement logic when force_disabled is true.
460 {
461 // disconnect if close to runway or no locators are available
462 if (closest_hdist < 20.0 || closest_vdist < 20.0)
463 wantsdisconnect = true;
464 if (!this->IsIlsAvailable())
465 wantsdisconnect = true;
466 }
467}
Central state/object manager and communications hub.
Game state manager and message-queue provider.
Core data structures for simulation; Everything affected by by either physics, network or user intera...
#define SOUND_PLAY_ONCE(_ACTOR_, _TRIG_)
int toggleHeading(int mode)
float getRudder()
float m_ils_runway_distance
Definition AutoPilot.h:123
void setInertialReferences(node_t *refl, node_t *refr, node_t *refb, node_t *refc)
Definition AutoPilot.cpp:88
Autopilot(int actor_id)
Definition AutoPilot.cpp:33
int adjALT(int d)
float m_ils_runway_heading
Definition AutoPilot.h:122
int adjIAS(int d)
float getAilerons()
Definition AutoPilot.cpp:97
float last_closest_hdist
Definition AutoPilot.h:124
void gpws_update(float spawnheight)
node_t * ref_r
Definition AutoPilot.h:108
float last_aileron
Definition AutoPilot.h:113
node_t * ref_c
Definition AutoPilot.h:110
float m_ils_angle_hdev
Definition AutoPilot.h:121
int adjVS(int d)
bool m_vertical_locator_available
Definition AutoPilot.h:118
node_t * ref_b
Definition AutoPilot.h:109
float last_gpws_height
Definition AutoPilot.h:115
bool m_horizontal_locator_available
Definition AutoPilot.h:119
float m_ils_angle_vdev
Definition AutoPilot.h:120
float last_elevator
Definition AutoPilot.h:112
float last_pullup_height
Definition AutoPilot.h:116
float getElevator()
bool wantsdisconnect
Definition AutoPilot.h:55
void setForceDisabled(bool disabled)
Definition AutoPilot.cpp:83
node_t * ref_l
Definition AutoPilot.h:107
int adjHDG(int d)
bool IsIlsAvailable()
Definition AutoPilot.h:82
void disconnect()
Definition AutoPilot.cpp:71
float last_rudder
Definition AutoPilot.h:114
float getThrottle(float thrtl, float dt)
bool force_disabled
Definition AutoPilot.h:98
int toggleAlt(int mode)
const TerrainPtr & GetTerrain()
TerrainObjectManager * getObjectManager()
Definition Terrain.h:80
float getHeightAt(float x, float z)
Definition Terrain.cpp:511
Wavefield * getWater()
Definition Terrain.h:87
float GetStaticWaterHeight()
Returns static water level configured in 'terrn2'.
Definition Wavefield.cpp:75
@ LOCALIZER_HORIZONTAL
Definition SimData.h:227
@ LOCALIZER_VERTICAL
Definition SimData.h:226
@ SS_TRIG_GPWS_PULLUP
@ SS_TRIG_GPWS_APDISCONNECT
@ SS_TRIG_GPWS_MINIMUMS
std::vector< Localizer > LocalizerVec
SoundScriptManager * GetSoundScriptManager()
GameContext * GetGameContext()
Physics: A vertex in the softbody structure.
Definition SimData.h:260
Ogre::Vector3 AbsPosition
absolute position in the world (shaky)
Definition SimData.h:267
Ogre::Vector3 Velocity
Definition SimData.h:268
Ogre::Vector3 RelPosition
relative to the local physics origin (one origin per actor) (shaky)
Definition SimData.h:266