FlightGear next
ControlMap.cpp
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <cstring>
6
7#include "Jet.hpp"
8#include "Thruster.hpp"
9#include "PropEngine.hpp"
10#include "PistonEngine.hpp"
11#include "TurbineEngine.hpp"
12#include "Gear.hpp"
13#include "Hook.hpp"
14#include "Launchbar.hpp"
15#include "Wing.hpp"
16#include "Rotor.hpp"
17#include "Math.hpp"
18#include "Propeller.hpp"
19#include "Hitch.hpp"
20
21#include "ControlMap.hpp"
22namespace yasim {
23
25static const std::vector<std::string> ControlNames = {
26 "THROTTLE",
27 "MIXTURE",
28 "CONDLEVER",
29 "STARTER",
30 "MAGNETOS",
31 "ADVANCE",
32 "REHEAT",
33 "PROP",
34 "BRAKE",
35 "STEER",
36 "EXTEND",
37 "HEXTEND",
38 "LEXTEND",
39 "LACCEL",
40 "INCIDENCE",
41 "FLAP0",
42 "FLAP1",
43 "SLAT",
44 "SPOILER",
45 "VECTOR",
46 "FLAP0EFFECTIVENESS",
47 "FLAP1EFFECTIVENESS",
48 "BOOST",
49 "CASTERING",
50 "PROPPITCH",
51 "PROPFEATHER",
52 "COLLECTIVE",
53 "CYCLICAIL",
54 "CYCLICELE",
55 "ROTORGEARENGINEON",
56 "TILTYAW",
57 "TILTPITCH",
58 "TILTROLL",
59 "ROTORBRAKE",
60 "ROTORENGINEMAXRELTORQUE",
61 "ROTORRELTARGET",
62 "ROTORBALANCE",
63 "REVERSE_THRUST",
64 "WASTEGATE",
65 "WINCHRELSPEED",
66 "HITCHOPEN",
67 "PLACEWINCH",
68 "FINDAITOW"
69};
70
71ControlMap::~ControlMap()
72{
73 for(int i=0; i<_inputs.size(); i++) {
74 Vector* v = (Vector*)_inputs.get(i);
75 for(int j=0; j<v->size(); j++)
76 delete (MapRec*)v->get(j);
77 delete v;
78 }
79
80 for(int i=0; i<_outputs.size(); i++)
81 delete (OutRec*)_outputs.get(i);
82
83 for(int i=0; i<_properties.size(); i++) {
84 PropHandle* p = (PropHandle*)_properties.get(i);
85 delete p;
86 }
87}
88
96void ControlMap::addMapping(const char* inputProp, ControlType control, ObjectID id, int options, float src0, float src1, float dst0, float dst1)
97{
98 OutRec* out = getOutRec(id, control);
99
100 // Make a new input record
101 MapRec* map = new MapRec();
102 map->opt = options;
103 map->id = out->maps.add(map);
104
105 // The default ranges differ depending on type!
106 map->src1 = map->dst1 = rangeMax(control);
107 map->src0 = map->dst0 = rangeMin(control);
108
109 // And add it to the approproate vectors.
110 Vector* maps = (Vector*)_inputs.get(getInputPropertyHandle(inputProp));
111 maps->add(map);
112
113 map->src0 = src0;
114 map->src1 = src1;
115 map->dst0 = dst0;
116 map->dst1 = dst1;
117}
118
119ControlMap::OutRec* ControlMap::getOutRec(ObjectID id, ControlType control)
120{
121 OutRec* out {nullptr};
122 for(int i = 0; i < _outputs.size(); i++) {
123 OutRec* o = (OutRec*)_outputs.get(i);
124 if(o->oid.object == id.object && o->oid.subObj == id.subObj
125 && o->control == control)
126 {
127 out = o;
128 break;
129 }
130 }
131
132 // Create one if it doesn't
133 if(out == nullptr) {
134 out = new OutRec();
135 out->control = control;
136 out->oid = id;
137 out->id = _outputs.add(out);
138 }
139 return out;
140}
141
142void ControlMap::reset()
143{
144 // Set all the values to zero
145 for(int i = 0; i < _outputs.size(); i++) {
146 OutRec* o = (OutRec*)_outputs.get(i);
147 for(int j = 0; j < o->maps.size(); j++) {
148 ((MapRec*)(o->maps.get(j)))->val = 0;
149 }
150 }
151}
152
153void ControlMap::setInput(int propHandle, float val)
154{
155 Vector* maps = (Vector*)_inputs.get(propHandle);
156 for(int i = 0; i < maps->size(); i++) {
157 MapRec* m = (MapRec*)maps->get(i);
158 float val2 = val;
159 // Do the scaling operation. Clamp to [src0:src1], rescale to
160 // [0:1] within that range, then map to [dst0:dst1].
161 val2 = Math::clamp(val2, m->src0, m->src1);
162 val2 = (val2 - m->src0) / (m->src1 - m->src0);
163 m->val = m->dst0 + val2 * (m->dst1 - m->dst0);
164 }
165}
166
167int ControlMap::getOutputHandle(ObjectID id, ControlType control)
168{
169 return getOutRec(id, control)->id;
170}
171
172void ControlMap::setTransitionTime(int handle, float time)
173{
174 ((OutRec*)_outputs.get(handle))->transitionTime = time;
175}
176
177float ControlMap::getOutput(int handle)
178{
179 return ((OutRec*)_outputs.get(handle))->oldValueLeft;
180}
181
182float ControlMap::getOutputR(int handle)
183{
184 return ((OutRec*)_outputs.get(handle))->oldValueRight;
185}
186
187void ControlMap::applyControls(float dt)
188{
189 for(int outrec=0; outrec<_outputs.size(); outrec++)
190 {
191 OutRec* o = (OutRec*)_outputs.get(outrec);
192
193 // Generate a summed value. Note the check for "split"
194 // control axes like ailerons.
195 float lval = 0, rval = 0;
196 for(int i = 0; i < o->maps.size(); i++) {
197 MapRec* m = (MapRec*)o->maps.get(i);
198 float val = m->val;
199
200 if(m->opt & OPT_SQUARE) { val = val * Math::abs(val); }
201 if(m->opt & OPT_INVERT) { val = -val; }
202 lval += val;
203 if(m->opt & OPT_SPLIT) { rval -= val; }
204 else { rval += val; }
205 }
206
207 // If there is a finite transition time, clamp the values to
208 // the maximum travel allowed in this dt.
209 if(o->transitionTime > 0) {
210 float dl = lval - o->oldValueLeft;
211 float dr = rval - o->oldValueRight;
212 float adl = Math::abs(dl);
213 float adr = Math::abs(dr);
214
215 float maxDelta = (dt/o->transitionTime) * (rangeMax(o->control) - rangeMin(o->control));
216 if(adl > maxDelta) {
217 dl = dl*maxDelta/adl;
218 lval = o->oldValueLeft + dl;
219 }
220 if(adr > maxDelta) {
221 dr = dr*maxDelta/adr;
222 rval = o->oldValueRight + dr;
223 }
224 }
225
226 o->oldValueLeft = lval;
227 o->oldValueRight = rval;
228
229 void* obj = o->oid.object;
230 switch(o->control) {
231 case THROTTLE:
232 ((Thruster*)obj)->setThrottle(lval);
233 break;
234 case MIXTURE:
235 ((Thruster*)obj)->setMixture(lval);
236 break;
237 case CONDLEVER:
238 ((TurbineEngine*)((PropEngine*)obj)->getEngine())->setCondLever(lval);
239 break;
240 case STARTER:
241 ((Thruster*)obj)->setStarter(lval != 0.0);
242 break;
243 case MAGNETOS:
244 ((PropEngine*)obj)->setMagnetos((int)lval);
245 break;
246 case ADVANCE:
247 ((PropEngine*)obj)->setAdvance(lval);
248 break;
249 case PROPPITCH:
250 ((PropEngine*)obj)->setPropPitch(lval);
251 break;
252 case PROPFEATHER:
253 ((PropEngine*)obj)->setPropFeather((int)lval);
254 break;
255 case REHEAT:
256 ((Jet*)obj)->setReheat(lval);
257 break;
258 case VECTOR:
259 ((Jet*)obj)->setRotation(lval);
260 break;
261 case BRAKE:
262 ((Gear*)obj)->setBrake(lval);
263 break;
264 case STEER:
265 ((Gear*)obj)->setRotation(lval);
266 break;
267 case EXTEND:
268 ((Gear*)obj)->setExtension(lval);
269 break;
270 case HEXTEND:
271 ((Hook*)obj)->setExtension(lval);
272 break;
273 case LEXTEND:
274 ((Launchbar*)obj)->setExtension(lval);
275 break;
276 case LACCEL:
277 ((Launchbar*)obj)->setAcceleration(lval);
278 break;
279 case CASTERING:
280 ((Gear*)obj)->setCastering(lval != 0);
281 break;
282 case SLAT:
283 ((Wing*)obj)->setFlapPos(WING_SLAT,lval);
284 break;
285 case FLAP0:
286 ((Wing*)obj)->setFlapPos(WING_FLAP0, lval, rval);
287 break;
288 case FLAP0EFFECTIVENESS:
289 ((Wing*)obj)->setFlapEffectiveness(WING_FLAP0,lval);
290 break;
291 case FLAP1:
292 ((Wing*)obj)->setFlapPos(WING_FLAP1,lval, rval);
293 break;
294 case FLAP1EFFECTIVENESS:
295 ((Wing*)obj)->setFlapEffectiveness(WING_FLAP1,lval);
296 break;
297 case SPOILER:
298 ((Wing*)obj)->setFlapPos(WING_SPOILER, lval, rval);
299 break;
300 case COLLECTIVE:
301 ((Rotor*)obj)->setCollective(lval);
302 break;
303 case CYCLICAIL:
304 ((Rotor*)obj)->setCyclicail(lval,rval);
305 break;
306 case CYCLICELE:
307 ((Rotor*)obj)->setCyclicele(lval,rval);
308 break;
309 case TILTPITCH:
310 ((Rotor*)obj)->setTiltPitch(lval);
311 break;
312 case TILTYAW:
313 ((Rotor*)obj)->setTiltYaw(lval);
314 break;
315 case TILTROLL:
316 ((Rotor*)obj)->setTiltRoll(lval);
317 break;
318 case ROTORBALANCE:
319 ((Rotor*)obj)->setRotorBalance(lval);
320 break;
321 case ROTORBRAKE:
322 ((Rotorgear*)obj)->setRotorBrake(lval);
323 break;
324 case ROTORGEARENGINEON:
325 ((Rotorgear*)obj)->setEngineOn((int)lval);
326 break;
327 case ROTORENGINEMAXRELTORQUE:
328 ((Rotorgear*)obj)->setRotorEngineMaxRelTorque(lval);
329 break;
330 case ROTORRELTARGET:
331 ((Rotorgear*)obj)->setRotorRelTarget(lval);
332 break;
333 case REVERSE_THRUST:
334 ((Jet*)obj)->setReverse(lval != 0);
335 break;
336 case BOOST:
337 ((PistonEngine*)((Thruster*)obj)->getEngine())->setBoost(lval);
338 break;
339 case WASTEGATE:
340 ((PistonEngine*)((Thruster*)obj)->getEngine())->setWastegate(lval);
341 break;
342 case WINCHRELSPEED:
343 ((Hitch*)obj)->setWinchRelSpeed(lval);
344 break;
345 case HITCHOPEN:
346 ((Hitch*)obj)->setOpen(lval!=0);
347 break;
348 case PLACEWINCH:
349 ((Hitch*)obj)->setWinchPositionAuto(lval!=0);
350 break;
351 case FINDAITOW:
352 ((Hitch*)obj)->findBestAIObject(lval!=0);
353 break;
354 case PROP:
355 break;
356 case INCIDENCE:
357 ((Wing*)obj)->setIncidence(lval);
358 break;
359 }
360 }
361}
362
363float ControlMap::rangeMin(ControlType control)
364{
365 // The minimum of the range for each type of control
366 switch(control) {
367 case INCIDENCE: return INCIDENCE_MIN;
368 case FLAP0: return -1; // [-1:1]
369 case FLAP1: return -1;
370 case STEER: return -1;
371 case CYCLICELE: return -1;
372 case CYCLICAIL: return -1;
373 case COLLECTIVE: return -1;
374 case WINCHRELSPEED: return -1;
375 case MAGNETOS: return 0; // [0:3]
376 case FLAP0EFFECTIVENESS: return 1; // [0:10]
377 case FLAP1EFFECTIVENESS: return 1; // [0:10]
378 default: return 0; // [0:1]
379 }
380}
381
382float ControlMap::rangeMax(ControlType control)
383{
384 // The maximum of the range for each type of control
385 switch(control) {
386 case INCIDENCE: return INCIDENCE_MAX;
387 case FLAP0: return 1; // [-1:1]
388 case FLAP1: return 1;
389 case STEER: return 1;
390 case MAGNETOS: return 3; // [0:3]
391 case FLAP0EFFECTIVENESS: return 10;// [0:10]
392 case FLAP1EFFECTIVENESS: return 10;// [0:10]
393 default: return 1; // [0:1]
394 }
395}
396
398int ControlMap::getInputPropertyHandle(const char* name)
399{
400 // search for existing
401 for(int i=0; i < _properties.size(); i++) {
402 PropHandle* p = (PropHandle*)_properties.get(i);
403 if(p->name == name)
404 return p->handle;
405 }
406
407 // else create new
408 PropHandle* p = new PropHandle();
409 p->name = name;
410
411 fgGetNode(p->name, true);
412
413 Vector* v = new Vector();
414 p->handle = _inputs.add(v);
415
416 _properties.add(p);
417 return p->handle;
418}
419
420ControlMap::ControlType ControlMap::getControlByName(const std::string& name)
421{
422 auto it = std::find(ControlNames.begin(), ControlNames.end(), name);
423 if (it == ControlNames.end()) {
424 SG_LOG(SG_FLIGHT,SG_ALERT,"Unrecognized control type '" << name
425 << "' in YASim aircraft description.");
426 exit(1);
427 }
428 return static_cast<ControlType>(std::distance(ControlNames.begin(), it));
429}
430
431std::string ControlMap::getControlName(ControlType c)
432{
433 return ControlNames.at(static_cast<int>(c));
434}
435
436ControlMap::ControlType ControlMap::parseControl(const char* name)
437{
438 std::string n(name);
439 return getControlByName(n);
440}
441
442ControlMap::ObjectID ControlMap::getObjectID(void* object, int subObj)
443{
444 assert(object != nullptr);
445 ObjectID o;
446 o.object = object;
447 o.subObj = subObj;
448 return o;
449}
450
451// used at runtime in FGFDM::getExternalInput
452ControlMap::PropHandle* ControlMap::getProperty(const int i) {
453 assert((i >= 0) && (i < _properties.size()));
454 return ((PropHandle*)_properties.get(i));
455}
456
457} // namespace yasim
#define p(x)
bool options(int, char **)
Definition JSBSim.cpp:568
#define i(x)
const char * name
static const std::vector< std::string > ControlNames
keep this list in sync with the enum ControlType in ControlMap.hpp !
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27