]> git.mxchange.org Git - flightgear.git/blob - src/AIModel/AICarrier.cxx
assign a unique module name to ai/mp embedded nasal (again): __model%u
[flightgear.git] / src / AIModel / AICarrier.cxx
1 // FGAICarrier - FGAIShip-derived class creates an AI aircraft carrier
2 //
3 // Written by David Culp, started October 2004.
4 // - davidculp2@comcast.net
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include <algorithm>
25 #include <string>
26 #include <vector>
27
28 #include <osg/Geode>
29 #include <osg/Drawable>
30 #include <osg/Transform>
31 #include <osg/NodeVisitor>
32 #include <osg/TemplatePrimitiveFunctor>
33
34 #include <simgear/sg_inlines.h>
35 #include <simgear/math/SGMath.hxx>
36 #include <simgear/math/sg_geodesy.hxx>
37 #include <simgear/scene/util/SGSceneUserData.hxx>
38 #include <simgear/scene/bvh/BVHGroup.hxx>
39 #include <simgear/scene/bvh/BVHLineGeometry.hxx>
40
41 #include <math.h>
42 #include <Main/util.hxx>
43 #include <Main/viewer.hxx>
44
45 #include "AICarrier.hxx"
46
47 /// Hmm: move that kind of configuration into the model file???
48 class LineCollector : public osg::NodeVisitor {
49     struct LinePrimitiveFunctor {
50         LinePrimitiveFunctor() : _lineCollector(0)
51         { }
52         void operator() (const osg::Vec3&, bool)
53         { }
54         void operator() (const osg::Vec3& v1, const osg::Vec3& v2, bool)
55         { if (_lineCollector) _lineCollector->addLine(v1, v2); }
56         void operator() (const osg::Vec3&, const osg::Vec3&, const osg::Vec3&,
57                          bool)
58         { }
59         void operator() (const osg::Vec3&, const osg::Vec3&, const osg::Vec3&,
60                          const osg::Vec3&, bool)
61         { }
62         LineCollector* _lineCollector;
63     };
64     
65 public:
66     LineCollector() :
67         osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
68                          osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
69     { }
70     virtual void apply(osg::Geode& geode)
71     {
72         osg::TemplatePrimitiveFunctor<LinePrimitiveFunctor> pf;
73         pf._lineCollector = this;
74         for (unsigned i = 0; i < geode.getNumDrawables(); ++i) {
75             geode.getDrawable(i)->accept(pf);
76         }
77     }
78     virtual void apply(osg::Node& node)
79     {
80         traverse(node);
81     }
82     virtual void apply(osg::Transform& transform)
83     {
84         osg::Matrix matrix = _matrix;
85         if (transform.computeLocalToWorldMatrix(_matrix, this))
86             traverse(transform);
87         _matrix = matrix;
88     }
89     
90     const std::vector<SGLineSegmentf>& getLineSegments() const
91     { return _lineSegments; }
92     
93     void addLine(const osg::Vec3& v1, const osg::Vec3& v2)
94     {
95         // Trick to get the ends in the right order.
96         // Use the x axis in the original coordinate system. Choose the
97         // most negative x-axis as the one pointing forward
98         SGVec3f tv1(_matrix.preMult(v1));
99         SGVec3f tv2(_matrix.preMult(v2));
100         if (tv1[0] > tv2[0])
101             _lineSegments.push_back(SGLineSegmentf(tv1, tv2));
102         else
103             _lineSegments.push_back(SGLineSegmentf(tv2, tv1));
104     }
105
106     void addBVHElements(osg::Node& node, simgear::BVHLineGeometry::Type type)
107     {
108         if (_lineSegments.empty())
109             return;
110
111         SGSceneUserData* userData;
112         userData = SGSceneUserData::getOrCreateSceneUserData(&node);
113
114         simgear::BVHNode* bvNode = userData->getBVHNode();
115         if (!bvNode && _lineSegments.size() == 1) {
116             simgear::BVHLineGeometry* bvLine;
117             bvLine = new simgear::BVHLineGeometry(_lineSegments.front(), type);
118             userData->setBVHNode(bvLine);
119             return;
120         }
121
122         simgear::BVHGroup* group = new simgear::BVHGroup;
123         if (bvNode)
124             group->addChild(bvNode);
125
126         for (unsigned i = 0; i < _lineSegments.size(); ++i) {
127             simgear::BVHLineGeometry* bvLine;
128             bvLine = new simgear::BVHLineGeometry(_lineSegments[i], type);
129             group->addChild(bvLine);
130         }
131         userData->setBVHNode(group);
132     }
133     
134 private:
135     osg::Matrix _matrix;
136     std::vector<SGLineSegmentf> _lineSegments;
137 };
138
139 class FGCarrierVisitor : public osg::NodeVisitor {
140 public:
141     FGCarrierVisitor(FGAICarrier* carrier,
142                      const std::list<std::string>& wireObjects,
143                      const std::list<std::string>& catapultObjects) :
144         osg::NodeVisitor(osg::NodeVisitor::NODE_VISITOR,
145                          osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
146         mWireObjects(wireObjects),
147         mCatapultObjects(catapultObjects)
148     { }
149     virtual void apply(osg::Node& node)
150     {
151         if (std::find(mWireObjects.begin(), mWireObjects.end(), node.getName())
152             != mWireObjects.end()) {
153             LineCollector lineCollector;
154             node.accept(lineCollector);
155             simgear::BVHLineGeometry::Type type;
156             type = simgear::BVHLineGeometry::CarrierWire;
157             lineCollector.addBVHElements(node, type);
158         }
159         if (std::find(mCatapultObjects.begin(), mCatapultObjects.end(),
160                       node.getName()) != mCatapultObjects.end()) {
161             LineCollector lineCollector;
162             node.accept(lineCollector);
163             simgear::BVHLineGeometry::Type type;
164             type = simgear::BVHLineGeometry::CarrierCatapult;
165             lineCollector.addBVHElements(node, type);
166         }
167         
168         traverse(node);
169     }
170     
171 private:
172     std::list<std::string> mWireObjects;
173     std::list<std::string> mCatapultObjects;
174 };
175
176 FGAICarrier::FGAICarrier() : FGAIShip(otCarrier) {
177 }
178
179 FGAICarrier::~FGAICarrier() {
180 }
181
182 void FGAICarrier::readFromScenario(SGPropertyNode* scFileNode) {
183   if (!scFileNode)
184     return;
185
186   FGAIShip::readFromScenario(scFileNode);
187
188   setRadius(scFileNode->getDoubleValue("turn-radius-ft", 2000));
189   setSign(scFileNode->getStringValue("pennant-number"));
190   setWind_from_east(scFileNode->getDoubleValue("wind_from_east", 0));
191   setWind_from_north(scFileNode->getDoubleValue("wind_from_north", 0));
192   setTACANChannelID(scFileNode->getStringValue("TACAN-channel-ID", "029Y"));
193   setMaxLat(scFileNode->getDoubleValue("max-lat", 0));
194   setMinLat(scFileNode->getDoubleValue("min-lat", 0));
195   setMaxLong(scFileNode->getDoubleValue("max-long", 0));
196   setMinLong(scFileNode->getDoubleValue("min-long", 0));
197
198   SGPropertyNode* flols = scFileNode->getChild("flols-pos");
199   if (flols) {
200     // Transform to the right coordinate frame, configuration is done in
201     // the usual x-back, y-right, z-up coordinates, computations
202     // in the simulation usual body x-forward, y-right, z-down coordinates
203     flols_off(0) = - flols->getDoubleValue("x-offset-m", 0);
204     flols_off(1) = flols->getDoubleValue("y-offset-m", 0);
205     flols_off(2) = - flols->getDoubleValue("z-offset-m", 0);
206   } else
207     flols_off = SGVec3d::zeros();
208
209   std::vector<SGPropertyNode_ptr> props = scFileNode->getChildren("wire");
210   std::vector<SGPropertyNode_ptr>::const_iterator it;
211   for (it = props.begin(); it != props.end(); ++it) {
212     std::string s = (*it)->getStringValue();
213     if (!s.empty())
214       wire_objects.push_back(s);
215   }
216
217   props = scFileNode->getChildren("catapult");
218   for (it = props.begin(); it != props.end(); ++it) {
219     std::string s = (*it)->getStringValue();
220     if (!s.empty())
221       catapult_objects.push_back(s);
222   }
223
224   props = scFileNode->getChildren("parking-pos");
225   for (it = props.begin(); it != props.end(); ++it) {
226     string name = (*it)->getStringValue("name", "unnamed");
227     // Transform to the right coordinate frame, configuration is done in
228     // the usual x-back, y-right, z-up coordinates, computations
229     // in the simulation usual body x-forward, y-right, z-down coordinates
230     double offset_x = -(*it)->getDoubleValue("x-offset-m", 0);
231     double offset_y = (*it)->getDoubleValue("y-offset-m", 0);
232     double offset_z = -(*it)->getDoubleValue("z-offset-m", 0);
233     double hd = (*it)->getDoubleValue("heading-offset-deg", 0);
234     ParkPosition pp(name, SGVec3d(offset_x, offset_y, offset_z), hd);
235     ppositions.push_back(pp);
236   }
237 }
238
239 void FGAICarrier::setWind_from_east(double fps) {
240     wind_from_east = fps;
241 }
242
243 void FGAICarrier::setWind_from_north(double fps) {
244     wind_from_north = fps;
245 }
246
247 void FGAICarrier::setMaxLat(double deg) {
248     max_lat = fabs(deg);
249 }
250
251 void FGAICarrier::setMinLat(double deg) {
252     min_lat = fabs(deg);
253 }
254
255 void FGAICarrier::setMaxLong(double deg) {
256     max_long = fabs(deg);
257 }
258
259 void FGAICarrier::setMinLong(double deg) {
260     min_long = fabs(deg);
261 }
262
263 void FGAICarrier::setSign(const string& s) {
264     sign = s;
265 }
266
267 void FGAICarrier::setTACANChannelID(const string& id) {
268     TACAN_channel_id = id;
269 }
270
271 void FGAICarrier::update(double dt) {
272     // Now update the position and heading. This will compute new hdg and
273     // roll values required for the rotation speed computation.
274     FGAIShip::update(dt);
275
276     //automatic turn into wind with a target wind of 25 kts otd
277     if(turn_to_launch_hdg){
278         TurnToLaunch();
279     } else if(OutsideBox() || returning) {// check that the carrier is inside the operating box
280         ReturnToBox();
281     } else {
282         TurnToBase();
283     }
284
285     UpdateWind(dt);
286     UpdateElevator(dt, transition_time);
287     UpdateJBD(dt, jbd_transition_time);
288
289     // Transform that one to the horizontal local coordinate system.
290     SGQuatd ec2hl = SGQuatd::fromLonLat(pos);
291     // The orientation of the carrier wrt the horizontal local frame
292     SGQuatd hl2body = SGQuatd::fromYawPitchRollDeg(hdg, pitch, roll);
293     // and postrotate the orientation of the AIModel wrt the horizontal
294     // local frame
295     SGQuatd ec2body = ec2hl*hl2body;
296     // The cartesian position of the carrier in the wgs84 world
297     SGVec3d cartPos = SGVec3d::fromGeod(pos);
298
299     // The position of the eyepoint - at least near that ...
300     SGVec3d eyePos(globals->get_current_view()->get_view_pos());
301     // Add the position offset of the AIModel to gain the earth
302     // centered position
303     SGVec3d eyeWrtCarrier = eyePos - cartPos;
304     // rotate the eyepoint wrt carrier vector into the carriers frame
305     eyeWrtCarrier = ec2body.transform(eyeWrtCarrier);
306     // the eyepoints vector wrt the flols position
307     SGVec3d eyeWrtFlols = eyeWrtCarrier - flols_off;
308     
309     // the distance from the eyepoint to the flols
310     dist = norm(eyeWrtFlols);
311     
312     // now the angle, positive angles are upwards
313     if (fabs(dist) < SGLimits<float>::min()) {
314       angle = 0;
315     } else {
316       double sAngle = -eyeWrtFlols(2)/dist;
317       sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
318       angle = SGMiscd::rad2deg(asin(sAngle));
319     }
320     
321     // set the value of source
322     if ( angle <= 4.35 && angle > 4.01 )
323       source = 1;
324     else if ( angle <= 4.01 && angle > 3.670 )
325       source = 2;
326     else if ( angle <= 3.670 && angle > 3.330 )
327       source = 3;
328     else if ( angle <= 3.330 && angle > 2.990 )
329       source = 4;
330     else if ( angle <= 2.990 && angle > 2.650 )
331       source = 5;
332     else if ( angle <= 2.650 )
333       source = 6;
334     else
335       source = 0;
336 } //end update
337
338 bool FGAICarrier::init(bool search_in_AI_path) {
339     if (!FGAIShip::init(search_in_AI_path))
340         return false;
341
342     _longitude_node = fgGetNode("/position/longitude-deg", true);
343     _latitude_node = fgGetNode("/position/latitude-deg", true);
344     _altitude_node = fgGetNode("/position/altitude-ft", true);
345
346     _launchbar_state_node = fgGetNode("/gear/launchbar/state", true);
347
348     _surface_wind_from_deg_node =
349             fgGetNode("/environment/config/boundary/entry[0]/wind-from-heading-deg", true);
350     _surface_wind_speed_node =
351             fgGetNode("/environment/config/boundary/entry[0]/wind-speed-kt", true);
352
353
354     turn_to_launch_hdg = false;
355     returning = false;
356
357     mOpBoxPos = pos;
358     base_course = hdg;
359     base_speed = speed;
360
361     pos_norm = 0;
362     elevators = false;
363     transition_time = 150;
364     time_constant = 0.005;
365     jbd_pos_norm = raw_jbd_pos_norm = 0;
366     jbd = false ;
367     jbd_transition_time = 3;
368     jbd_time_constant = 0.1;
369     return true;
370 }
371
372 void FGAICarrier::initModel(osg::Node *node)
373 {
374     // SG_LOG(SG_GENERAL, SG_BULK, "AICarrier::initModel()" );
375     FGAIShip::initModel(node);
376     // process the 3d model here
377     // mark some objects solid, mark the wires ...
378     FGCarrierVisitor carrierVisitor(this, wire_objects, catapult_objects);
379     model->accept(carrierVisitor);
380 }
381
382 void FGAICarrier::bind() {
383     FGAIShip::bind();
384
385     props->untie("velocities/true-airspeed-kt");
386
387     props->tie("controls/flols/source-lights",
388                 SGRawValuePointer<int>(&source));
389     props->tie("controls/flols/distance-m",
390                 SGRawValuePointer<double>(&dist));
391     props->tie("controls/flols/angle-degs",
392                 SGRawValuePointer<double>(&angle));
393     props->tie("controls/turn-to-launch-hdg",
394                 SGRawValuePointer<bool>(&turn_to_launch_hdg));
395     props->tie("controls/in-to-wind",
396                 SGRawValuePointer<bool>(&turn_to_launch_hdg));
397     props->tie("controls/base-course-deg",
398                 SGRawValuePointer<double>(&base_course));
399     props->tie("controls/base-speed-kts",
400                 SGRawValuePointer<double>(&base_speed));
401     props->tie("controls/start-pos-lat-deg",
402                SGRawValueMethods<SGGeod,double>(pos, &SGGeod::getLatitudeDeg));
403     props->tie("controls/start-pos-long-deg",
404                SGRawValueMethods<SGGeod,double>(pos, &SGGeod::getLongitudeDeg));
405     props->tie("velocities/speed-kts",
406                 SGRawValuePointer<double>(&speed));
407     props->tie("environment/surface-wind-speed-true-kts",
408                 SGRawValuePointer<double>(&wind_speed_kts));
409     props->tie("environment/surface-wind-from-true-degs",
410                 SGRawValuePointer<double>(&wind_from_deg));
411     props->tie("environment/rel-wind-from-degs",
412                 SGRawValuePointer<double>(&rel_wind_from_deg));
413     props->tie("environment/rel-wind-from-carrier-hdg-degs",
414                 SGRawValuePointer<double>(&rel_wind));
415     props->tie("environment/rel-wind-speed-kts",
416                 SGRawValuePointer<double>(&rel_wind_speed_kts));
417     props->tie("controls/flols/wave-off-lights",
418                 SGRawValuePointer<bool>(&wave_off_lights));
419     props->tie("controls/elevators",
420                 SGRawValuePointer<bool>(&elevators));
421     props->tie("surface-positions/elevators-pos-norm",
422                 SGRawValuePointer<double>(&pos_norm));
423     props->tie("controls/elevators-trans-time-s",
424                 SGRawValuePointer<double>(&transition_time));
425     props->tie("controls/elevators-time-constant",
426                 SGRawValuePointer<double>(&time_constant));
427     props->tie("controls/jbd",
428         SGRawValuePointer<bool>(&jbd));
429     props->tie("surface-positions/jbd-pos-norm",
430         SGRawValuePointer<double>(&jbd_pos_norm));
431     props->tie("controls/jbd-trans-time-s",
432         SGRawValuePointer<double>(&jbd_transition_time));
433     props->tie("controls/jbd-time-constant",
434         SGRawValuePointer<double>(&jbd_time_constant));
435
436     props->setBoolValue("controls/flols/cut-lights", false);
437     props->setBoolValue("controls/flols/wave-off-lights", false);
438     props->setBoolValue("controls/flols/cond-datum-lights", true);
439     props->setBoolValue("controls/crew", false);
440     props->setStringValue("navaids/tacan/channel-ID", TACAN_channel_id.c_str());
441     props->setStringValue("sign", sign.c_str());
442     props->setBoolValue("controls/lighting/deck-lights", false);
443     props->setDoubleValue("controls/lighting/flood-lights-red-norm", 0);
444 }
445
446
447 void FGAICarrier::unbind() {
448     FGAIShip::unbind();
449
450     props->untie("velocities/true-airspeed-kt");
451     props->untie("controls/flols/source-lights");
452     props->untie("controls/flols/distance-m");
453     props->untie("controls/flols/angle-degs");
454     props->untie("controls/turn-to-launch-hdg");
455     props->untie("velocities/speed-kts");
456     props->untie("environment/wind-speed-true-kts");
457     props->untie("environment/wind-from-true-degs");
458     props->untie("environment/rel-wind-from-degs");
459     props->untie("environment/rel-wind-speed-kts");
460     props->untie("controls/flols/wave-off-lights");
461     props->untie("controls/elevators");
462     props->untie("surface-positions/elevators-pos-norm");
463     props->untie("controls/elevators-trans-time-secs");
464     props->untie("controls/elevators-time-constant");
465     props->untie("controls/jbd");
466     props->untie("surface-positions/jbd-pos-norm");
467     props->untie("controls/jbd-trans-time-s");
468     props->untie("controls/jbd-time-constant");
469
470 }
471
472
473 bool FGAICarrier::getParkPosition(const string& id, SGGeod& geodPos,
474                                   double& hdng, SGVec3d& uvw)
475 {
476
477     // FIXME: does not yet cover rotation speeds.
478     list<ParkPosition>::iterator it = ppositions.begin();
479     while (it != ppositions.end()) {
480         // Take either the specified one or the first one ...
481         if ((*it).name == id || id.empty()) {
482             ParkPosition ppos = *it;
483             SGVec3d cartPos = getCartPosAt(ppos.offset);
484             geodPos = SGGeod::fromCart(cartPos);
485             hdng = hdg + ppos.heading_deg;
486             double shdng = sin(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
487             double chdng = cos(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
488             double speed_fps = speed*1.6878099;
489             uvw = SGVec3d(chdng*speed_fps, shdng*speed_fps, 0);
490             return true;
491         }
492         ++it;
493     }
494
495     return false;
496 }
497
498 // find relative wind
499 void FGAICarrier::UpdateWind( double dt) {
500
501     //get the surface wind speed and direction
502     wind_from_deg = _surface_wind_from_deg_node->getDoubleValue();
503     wind_speed_kts  = _surface_wind_speed_node->getDoubleValue();
504
505     //calculate the surface wind speed north and east in kts
506     double wind_speed_from_north_kts = cos( wind_from_deg / SGD_RADIANS_TO_DEGREES )* wind_speed_kts ;
507     double wind_speed_from_east_kts  = sin( wind_from_deg / SGD_RADIANS_TO_DEGREES )* wind_speed_kts ;
508
509     //calculate the carrier speed north and east in kts
510     double speed_north_kts = cos( hdg / SGD_RADIANS_TO_DEGREES )* speed ;
511     double speed_east_kts  = sin( hdg / SGD_RADIANS_TO_DEGREES )* speed ;
512
513     //calculate the relative wind speed north and east in kts
514     double rel_wind_speed_from_east_kts = wind_speed_from_east_kts + speed_east_kts;
515     double rel_wind_speed_from_north_kts = wind_speed_from_north_kts + speed_north_kts;
516
517     //combine relative speeds north and east to get relative windspeed in kts
518     rel_wind_speed_kts = sqrt((rel_wind_speed_from_east_kts * rel_wind_speed_from_east_kts)
519     + (rel_wind_speed_from_north_kts * rel_wind_speed_from_north_kts));
520
521     //calculate the relative wind direction
522     rel_wind_from_deg = atan2(rel_wind_speed_from_east_kts, rel_wind_speed_from_north_kts)
523                             * SG_RADIANS_TO_DEGREES;
524
525     //calculate rel wind
526     rel_wind = rel_wind_from_deg - hdg;
527     SG_NORMALIZE_RANGE(rel_wind, -180.0, 180.0);
528
529     //switch the wave-off lights
530     if (InToWind())
531        wave_off_lights = false;
532     else
533        wave_off_lights = true;
534
535     // cout << "rel wind: " << rel_wind << endl;
536
537 }// end update wind
538
539
540 void FGAICarrier::TurnToLaunch(){
541
542     //calculate tgt speed
543     double tgt_speed = 25 - wind_speed_kts;
544     if (tgt_speed < 10)
545         tgt_speed = 10;
546
547     //turn the carrier
548     FGAIShip::TurnTo(wind_from_deg);
549     FGAIShip::AccelTo(tgt_speed);
550
551 }
552
553
554 void FGAICarrier::TurnToBase(){
555
556     //turn the carrier
557     FGAIShip::TurnTo(base_course);
558     FGAIShip::AccelTo(base_speed);
559
560 }
561
562
563 void FGAICarrier::ReturnToBox(){
564     double course, distance, az2;
565
566     //calculate the bearing and range of the initial position from the carrier
567     geo_inverse_wgs_84(pos, mOpBoxPos, &course, &az2, &distance);
568
569     distance *= SG_METER_TO_NM;
570
571     //cout << "return course: " << course << " distance: " << distance << endl;
572     //turn the carrier
573     FGAIShip::TurnTo(course);
574     FGAIShip::AccelTo(base_speed);
575
576     if (distance >= 1)
577         returning = true;
578     else
579         returning = false;
580
581 } //  end turn to base
582
583
584 bool FGAICarrier::OutsideBox() { //returns true if the carrier is outside operating box
585
586     if ( max_lat == 0 && min_lat == 0 && max_long == 0 && min_long == 0) {
587         SG_LOG(SG_GENERAL, SG_DEBUG, "AICarrier: No Operating Box defined" );
588         return false;
589     }
590
591     if (mOpBoxPos.getLatitudeDeg() >= 0) { //northern hemisphere
592         if (pos.getLatitudeDeg() >= mOpBoxPos.getLatitudeDeg() + max_lat)
593             return true;
594
595         if (pos.getLatitudeDeg() <= mOpBoxPos.getLatitudeDeg() - min_lat)
596             return true;
597
598     } else {                  //southern hemisphere
599         if (pos.getLatitudeDeg() <= mOpBoxPos.getLatitudeDeg() - max_lat)
600             return true;
601
602         if (pos.getLatitudeDeg() >= mOpBoxPos.getLatitudeDeg() + min_lat)
603             return true;
604     }
605
606     if (mOpBoxPos.getLongitudeDeg() >=0) { //eastern hemisphere
607         if (pos.getLongitudeDeg() >= mOpBoxPos.getLongitudeDeg() + max_long)
608             return true;
609
610         if (pos.getLongitudeDeg() <= mOpBoxPos.getLongitudeDeg() - min_long)
611             return true;
612
613     } else {                 //western hemisphere
614         if (pos.getLongitudeDeg() <= mOpBoxPos.getLongitudeDeg() - max_long)
615             return true;
616
617         if (pos.getLongitudeDeg() >= mOpBoxPos.getLongitudeDeg() + min_long)
618             return true;
619     }
620
621     SG_LOG(SG_GENERAL, SG_DEBUG, "AICarrier: Inside Operating Box" );
622     return false;
623
624 } // end OutsideBox
625
626
627 bool FGAICarrier::InToWind() {
628     if ( fabs(rel_wind) < 5 )
629         return true;
630
631     return false;
632 }
633
634
635 void FGAICarrier::UpdateElevator(double dt, double transition_time) {
636
637     double step = 0;
638
639     if ((elevators && pos_norm >= 1 ) || (!elevators && pos_norm <= 0 ))
640         return;
641
642     // move the elevators
643     if ( elevators ) {
644         step = dt/transition_time;
645         if ( step > 1 )
646             step = 1;
647     } else {
648         step = -dt/transition_time;
649         if ( step < -1 )
650             step = -1;
651     }
652     // assume a linear relationship
653     raw_pos_norm += step;
654
655     //low pass filter
656     pos_norm = (raw_pos_norm * time_constant) + (pos_norm * (1 - time_constant));
657
658     //sanitise the output
659     if (raw_pos_norm >= 1) {
660         raw_pos_norm = 1;
661     } else if (raw_pos_norm <= 0) {
662         raw_pos_norm = 0;
663     }
664     return;
665
666 } // end UpdateElevator
667
668 void FGAICarrier::UpdateJBD(double dt, double jbd_transition_time) {
669
670     string launchbar_state = _launchbar_state_node->getStringValue();
671     double step = 0;
672
673     if (launchbar_state == "Engaged"){
674         jbd = true;
675     } else {
676         jbd = false;
677     }
678
679     if (( jbd && jbd_pos_norm >= 1 ) || ( !jbd && jbd_pos_norm <= 0 )){
680         return;
681     }
682
683     // move the jbds
684     if ( jbd ) {
685         step = dt/jbd_transition_time;
686         if ( step > 1 )
687             step = 1;
688     } else {
689         step = -dt/jbd_transition_time;
690         if ( step < -1 )
691             step = -1;
692     }
693
694     // assume a linear relationship
695     raw_jbd_pos_norm += step;
696
697     //low pass filter
698     jbd_pos_norm = (raw_jbd_pos_norm * jbd_time_constant) + (jbd_pos_norm * (1 - jbd_time_constant));
699
700     //sanitise the output
701     if (jbd_pos_norm >= 1) {
702         jbd_pos_norm = 1;
703     } else if (jbd_pos_norm <= 0) {
704         jbd_pos_norm = 0;
705     }
706
707     return;
708
709 } // end UpdateJBD