]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
6bcf7f0bd77616733798936ca3d8f89c997e53b0
[flightgear.git] / src / Main / fg_props.cxx
1 // fg_props.cxx -- support for FlightGear properties.
2 //
3 // Written by David Megginson, started 2000.
4 //
5 // Copyright (C) 2000, 2001 David Megginson - david@megginson.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear/compiler.h>
25 #endif
26
27 #include <simgear/misc/exception.hxx>
28
29 #include STL_IOSTREAM
30
31 #include <Autopilot/newauto.hxx>
32 #include <Aircraft/aircraft.hxx>
33 #include <Time/tmp.hxx>
34 #include <FDM/UIUCModel/uiuc_aircraftdir.h>
35 #ifndef FG_OLD_WEATHER
36 #  include <WeatherCM/FGLocalWeatherDatabase.h>
37 #else
38 #  include <Weather/weather.hxx>
39 #endif
40 #include <Objects/matlib.hxx>
41
42 #include <GUI/gui.h>
43
44 #include "fgfs.hxx"
45 #include "fg_props.hxx"
46 #include "viewmgr.hxx"
47
48 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
49 SG_USING_STD(istream);
50 SG_USING_STD(ostream);
51 #endif
52
53 static double getWindNorth ();
54 static double getWindEast ();
55 static double getWindDown ();
56
57 // Allow the view to be set from two axes (i.e. a joystick hat)
58 // This needs to be in FGViewer itself, somehow.
59 static double axisLong = 0.0;
60 static double axisLat = 0.0;
61
62 static bool winding_ccw = true; // FIXME: temporary
63
64 static bool fdm_data_logging = false; // FIXME: temporary
65
66
67 /**
68  * Utility function.
69  */
70 static inline void
71 _set_view_from_axes ()
72 {
73                                 // Take no action when hat is centered
74   if ( ( axisLong <  0.01 ) &&
75        ( axisLong > -0.01 ) &&
76        ( axisLat  <  0.01 ) &&
77        ( axisLat  > -0.01 )
78      )
79     return;
80
81   double viewDir = 999;
82
83   /* Do all the quick and easy cases */
84   if (axisLong < 0) {           // Longitudinal axis forward
85     if (axisLat == axisLong)
86       viewDir = 45;
87     else if (axisLat == - axisLong)
88       viewDir = 315;
89     else if (axisLat == 0)
90       viewDir = 0;
91   } else if (axisLong > 0) {    // Longitudinal axis backward
92     if (axisLat == - axisLong)
93       viewDir = 135;
94     else if (axisLat == axisLong)
95       viewDir = 225;
96     else if (axisLat == 0)
97       viewDir = 180;
98   } else if (axisLong == 0) {   // Longitudinal axis neutral
99     if (axisLat < 0)
100       viewDir = 90;
101     else if (axisLat > 0)
102       viewDir = 270;
103     else return; /* And assertion failure maybe? */
104   }
105
106   /* Do all the difficult cases */
107   if ( viewDir > 900 )
108     viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axisLat, -axisLong );
109   if ( viewDir < -1 ) viewDir += 360;
110
111 //  SG_LOG(SG_INPUT, SG_ALERT, "Joystick Lat=" << axisLat << "   and Long="
112 //      << axisLong << "  gave angle=" << viewDir );
113
114   globals->get_current_view()->set_goal_view_offset(viewDir*SGD_DEGREES_TO_RADIANS);
115 //   globals->get_current_view()->set_view_offset(viewDir*SGD_DEGREES_TO_RADIANS);
116 }
117
118 \f
119 ////////////////////////////////////////////////////////////////////////
120 // Default property bindings (not yet handled by any module).
121 ////////////////////////////////////////////////////////////////////////
122
123 struct LogClassMapping {
124   sgDebugClass c;
125   string name;
126 };
127
128 LogClassMapping log_class_mappings [] = {
129   {SG_NONE, "none"},
130   {SG_TERRAIN, "terrain"},
131   {SG_ASTRO, "astro"},
132   {SG_FLIGHT, "flight"},
133   {SG_INPUT, "input"},
134   {SG_GL, "gl"},
135   {SG_VIEW, "view"},
136   {SG_COCKPIT, "cockpit"},
137   {SG_GENERAL, "general"},
138   {SG_MATH, "math"},
139   {SG_EVENT, "event"},
140   {SG_AIRCRAFT, "aircraft"},
141   {SG_AUTOPILOT, "autopilot"},
142   {SG_IO, "io"},
143   {SG_CLIPPER, "clipper"},
144   {SG_NETWORK, "network"},
145   {SG_UNDEFD, ""}
146 };
147
148
149 /**
150  * Get the logging classes.
151  */
152 static string
153 getLoggingClasses ()
154 {
155   sgDebugClass classes = logbuf::get_log_classes();
156   string result = "";
157   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
158     if ((classes&log_class_mappings[i].c) > 0) {
159       if (result != "")
160         result += '|';
161       result += log_class_mappings[i].name;
162     }
163   }
164   return result;
165 }
166
167
168 static void addLoggingClass (const string &name)
169 {
170   sgDebugClass classes = logbuf::get_log_classes();
171   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
172     if (name == log_class_mappings[i].name) {
173       logbuf::set_log_classes(sgDebugClass(classes|log_class_mappings[i].c));
174       return;
175     }
176   }
177   SG_LOG(SG_GENERAL, SG_ALERT, "Unknown logging class: " << name);
178 }
179
180
181 /**
182  * Set the logging classes.
183  */
184 static void
185 setLoggingClasses (string classes)
186 {
187   logbuf::set_log_classes(SG_NONE);
188
189   if (classes == "none") {
190     SG_LOG(SG_GENERAL, SG_INFO, "Disabled all logging classes");
191     return;
192   }
193
194   if (classes == "" || classes == "all") { // default
195     logbuf::set_log_classes(SG_ALL);
196     SG_LOG(SG_GENERAL, SG_INFO, "Enabled all logging classes: "
197            << getLoggingClasses());
198     return;
199   }
200
201   string rest = classes;
202   string name = "";
203   int sep = rest.find('|');
204   while (sep > 0) {
205     name = rest.substr(0, sep);
206     rest = rest.substr(sep+1);
207     addLoggingClass(name);
208     sep = rest.find('|');
209   }
210   addLoggingClass(rest);
211   SG_LOG(SG_GENERAL, SG_INFO, "Set logging classes to "
212          << getLoggingClasses());
213 }
214
215
216 /**
217  * Get the logging priority.
218  */
219 static string
220 getLoggingPriority ()
221 {
222   switch (logbuf::get_log_priority()) {
223   case SG_BULK:
224     return "bulk";
225   case SG_DEBUG:
226     return "debug";
227   case SG_INFO:
228     return "info";
229   case SG_WARN:
230     return "warn";
231   case SG_ALERT:
232     return "alert";
233   default:
234     SG_LOG(SG_GENERAL, SG_WARN, "Internal: Unknown logging priority number: "
235            << logbuf::get_log_priority());
236     return "unknown";
237   }
238 }
239
240
241 /**
242  * Set the logging priority.
243  */
244 static void
245 setLoggingPriority (string priority)
246 {
247   if (priority == "bulk") {
248     logbuf::set_log_priority(SG_BULK);
249   } else if (priority == "debug") {
250     logbuf::set_log_priority(SG_DEBUG);
251   } else if (priority == "" || priority == "info") { // default
252     logbuf::set_log_priority(SG_INFO);
253   } else if (priority == "warn") {
254     logbuf::set_log_priority(SG_WARN);
255   } else if (priority == "alert") {
256     logbuf::set_log_priority(SG_ALERT);
257   } else {
258     SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
259   }
260   SG_LOG(SG_GENERAL, SG_INFO, "Logging priority is " << getLoggingPriority());
261 }
262
263
264 /**
265  * Get the pause state of the sim.
266  */
267 static bool
268 getFreeze ()
269 {
270   return globals->get_freeze();
271 }
272
273
274 /**
275  * Set the pause state of the sim.
276  */
277 static void
278 setFreeze (bool freeze)
279 {
280   globals->set_freeze(freeze);
281 }
282
283 /**
284  * Return the current aircraft directory (UIUC) as a string.
285  */
286 static string 
287 getAircraftDir ()
288 {
289   return aircraft_dir;
290 }
291
292
293 /**
294  * Set the current aircraft directory (UIUC).
295  */
296 static void
297 setAircraftDir (string dir)
298 {
299   if (getAircraftDir() != dir) {
300     aircraft_dir = dir;
301 //     needReinit(); FIXME!!
302   }
303 }
304
305
306 /**
307  * Get the current view offset in degrees.
308  */
309 static double
310 getViewOffset ()
311 {
312   return (globals->get_current_view()
313           ->get_view_offset() * SGD_RADIANS_TO_DEGREES);
314 }
315
316
317 static void
318 setViewOffset (double offset)
319 {
320   globals->get_current_view()->set_view_offset(offset * SGD_DEGREES_TO_RADIANS);
321 }
322
323 static double
324 getGoalViewOffset ()
325 {
326   return (globals->get_current_view()
327           ->get_goal_view_offset() * SGD_RADIANS_TO_DEGREES);
328 }
329
330 static void
331 setGoalViewOffset (double offset)
332 {
333     while ( offset < 0 ) {
334         offset += 360.0;
335     }
336     while ( offset > 360.0 ) {
337         offset -= 360.0;
338     }
339     // Snap to center if we are close
340     if ( fabs(offset) < 1.0 ||  fabs(offset) > 359.0 ) {
341         offset = 0.0;
342     }
343
344     globals->get_current_view()
345         ->set_goal_view_offset(offset * SGD_DEGREES_TO_RADIANS);
346 }
347
348
349 /**
350  * Pilot position offset from CG.
351  */
352 static float
353 getPilotPositionXOffset ()
354 {
355   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
356   float * offset = pilot_view->get_pilot_offset();
357   return offset[0];
358 }
359
360 static void
361 setPilotPositionXOffset (float x)
362 {
363   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
364   float * offset = pilot_view->get_pilot_offset();
365   pilot_view->set_pilot_offset(x, offset[1], offset[2]);
366 }
367
368 static float
369 getPilotPositionYOffset ()
370 {
371   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
372   float * offset = pilot_view->get_pilot_offset();
373   return offset[1];
374 }
375
376 static void
377 setPilotPositionYOffset (float y)
378 {
379   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
380   float * offset = pilot_view->get_pilot_offset();
381   pilot_view->set_pilot_offset(offset[0], y, offset[2]);
382 }
383
384 static float
385 getPilotPositionZOffset ()
386 {
387   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
388   float * offset = pilot_view->get_pilot_offset();
389   return offset[2];
390 }
391
392 static void
393 setPilotPositionZOffset (float z)
394 {
395   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
396   float * offset = pilot_view->get_pilot_offset();
397   pilot_view->set_pilot_offset(offset[0], offset[1], z);
398 }
399
400
401 /**
402  * Return the current Zulu time.
403  */
404 static string 
405 getDateString ()
406 {
407   string out;
408   char buf[64];
409   struct tm * t = globals->get_time_params()->getGmt();
410   sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
411           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
412           t->tm_hour, t->tm_min, t->tm_sec);
413   out = buf;
414   return out;
415 }
416
417
418 /**
419  * Set the current Zulu time.
420  */
421 static void
422 setDateString (string date_string)
423 {
424   SGTime * st = globals->get_time_params();
425   struct tm * current_time = st->getGmt();
426   struct tm new_time;
427
428                                 // Scan for basic ISO format
429                                 // YYYY-MM-DDTHH:MM:SS
430   int ret = sscanf(date_string.c_str(), "%d-%d-%dT%d:%d:%d",
431                    &(new_time.tm_year), &(new_time.tm_mon),
432                    &(new_time.tm_mday), &(new_time.tm_hour),
433                    &(new_time.tm_min), &(new_time.tm_sec));
434
435                                 // Be pretty picky about this, so
436                                 // that strange things don't happen
437                                 // if the save file has been edited
438                                 // by hand.
439   if (ret != 6) {
440     SG_LOG(SG_INPUT, SG_ALERT, "Date/time string " << date_string
441            << " not in YYYY-MM-DDTHH:MM:SS format; skipped");
442     return;
443   }
444
445                                 // OK, it looks like we got six
446                                 // values, one way or another.
447   new_time.tm_year -= 1900;
448   new_time.tm_mon -= 1;
449
450                                 // Now, tell flight gear to use
451                                 // the new time.  This was far
452                                 // too difficult, by the way.
453   long int warp =
454     mktime(&new_time) - mktime(current_time) + globals->get_warp();
455   double lon = current_aircraft.fdm_state->get_Longitude();
456   double lat = current_aircraft.fdm_state->get_Latitude();
457   globals->set_warp(warp);
458   st->update(lon, lat, warp);
459   fgUpdateSkyAndLightingParams();
460 }
461
462 /**
463  * Return the GMT as a string.
464  */
465 static string 
466 getGMTString ()
467 {
468   string out;
469   char buf[16];
470   struct tm * t = globals->get_time_params()->getGmt();
471   sprintf(buf, " %.2d:%.2d:%.2d",
472           t->tm_hour, t->tm_min, t->tm_sec);
473   out = buf;
474   return out;
475 }
476
477
478 /**
479  * Get the texture rendering state.
480  */
481 static bool
482 getTextures ()
483 {
484   return (material_lib.get_step() == 0);
485 }
486
487
488 /**
489  * Set the texture rendering state.
490  */
491 static void
492 setTextures (bool textures)
493 {
494   if (textures)
495     material_lib.set_step(0);
496   else
497     material_lib.set_step(1);
498 }
499
500
501 /**
502  * Return the magnetic variation
503  */
504 static double
505 getMagVar ()
506 {
507   return globals->get_mag()->get_magvar() * SGD_RADIANS_TO_DEGREES;
508 }
509
510
511 /**
512  * Return the magnetic dip
513  */
514 static double
515 getMagDip ()
516 {
517   return globals->get_mag()->get_magdip() * SGD_RADIANS_TO_DEGREES;
518 }
519
520
521 /**
522  * Return the current heading in degrees.
523  */
524 static double
525 getHeadingMag ()
526 {
527   return current_aircraft.fdm_state->get_Psi() * SGD_RADIANS_TO_DEGREES - getMagVar();
528 }
529
530
531 /**
532  * Return the fuel level in tank 1
533  */
534 static double
535 getTank1Fuel ()
536 {
537   return current_aircraft.fdm_state->get_Tank1Fuel();
538 }
539
540 static void
541 setTank1Fuel ( double gals )
542 {
543   current_aircraft.fdm_state->set_Tank1Fuel( gals );
544 }
545
546 /**
547  * Return the fuel level in tank 2
548  */
549 static double
550 getTank2Fuel ()
551 {
552   return current_aircraft.fdm_state->get_Tank2Fuel();
553 }
554
555 static void
556 setTank2Fuel ( double gals )
557 {
558   current_aircraft.fdm_state->set_Tank2Fuel( gals );
559 }
560
561
562 /**
563  * Get the autopilot altitude lock (true=on).
564  */
565 static bool
566 getAPAltitudeLock ()
567 {
568     return (current_autopilot->get_AltitudeEnabled() &&
569             current_autopilot->get_AltitudeMode()
570             == FGAutopilot::FG_ALTITUDE_LOCK);
571 }
572
573
574 /**
575  * Set the autopilot altitude lock (true=on).
576  */
577 static void
578 setAPAltitudeLock (bool lock)
579 {
580   current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_LOCK);
581   current_autopilot->set_AltitudeEnabled(lock);
582 }
583
584
585 /**
586  * Get the autopilot target altitude in feet.
587  */
588 static double
589 getAPAltitude ()
590 {
591   return current_autopilot->get_TargetAltitude() * SG_METER_TO_FEET;
592 }
593
594
595 /**
596  * Set the autopilot target altitude in feet.
597  */
598 static void
599 setAPAltitude (double altitude)
600 {
601     current_autopilot->set_TargetAltitude( altitude * SG_FEET_TO_METER );
602 }
603
604 /**
605  * Get the autopilot altitude lock (true=on).
606  */
607 static bool
608 getAPGSLock ()
609 {
610     return (current_autopilot->get_AltitudeEnabled() &&
611             (current_autopilot->get_AltitudeMode()
612              == FGAutopilot::FG_ALTITUDE_GS1));
613 }
614
615
616 /**
617  * Set the autopilot altitude lock (true=on).
618  */
619 static void
620 setAPGSLock (bool lock)
621 {
622   current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_GS1);
623   current_autopilot->set_AltitudeEnabled(lock);
624 }
625
626
627 /**
628  * Get the autopilot terrain lock (true=on).
629  */
630 static bool
631 getAPTerrainLock ()
632 {
633     return (current_autopilot->get_AltitudeEnabled() &&
634             (current_autopilot->get_AltitudeMode()
635              == FGAutopilot::FG_ALTITUDE_TERRAIN));
636 }
637
638
639 /**
640  * Set the autopilot terrain lock (true=on).
641  */
642 static void
643 setAPTerrainLock (bool lock)
644 {
645   current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_TERRAIN);
646   current_autopilot->set_AltitudeEnabled(lock);
647   current_autopilot->set_TargetAGL(
648       current_aircraft.fdm_state->get_Altitude_AGL() * SG_FEET_TO_METER
649     );
650   cout << "Target AGL = "
651        << current_aircraft.fdm_state->get_Altitude_AGL() * SG_FEET_TO_METER
652        << endl;
653 }
654
655
656 /**
657  * Get the autopilot target altitude in feet.
658  */
659 static double
660 getAPClimb ()
661 {
662   return current_autopilot->get_TargetClimbRate() * SG_METER_TO_FEET;
663 }
664
665
666 /**
667  * Set the autopilot target altitude in feet.
668  */
669 static void
670 setAPClimb (double rate)
671 {
672     current_autopilot->set_TargetClimbRate( rate * SG_FEET_TO_METER );
673 }
674
675
676 /**
677  * Get the autopilot heading lock (true=on).
678  */
679 static bool
680 getAPHeadingLock ()
681 {
682     return
683       (current_autopilot->get_HeadingEnabled() &&
684        current_autopilot->get_HeadingMode() == DEFAULT_AP_HEADING_LOCK);
685 }
686
687
688 /**
689  * Set the autopilot heading lock (true=on).
690  */
691 static void
692 setAPHeadingLock (bool lock)
693 {
694     if (lock) {
695         current_autopilot->set_HeadingMode(DEFAULT_AP_HEADING_LOCK);
696         current_autopilot->set_HeadingEnabled(true);
697     } else {
698         current_autopilot->set_HeadingEnabled(false);
699     }
700 }
701
702
703 /**
704  * Get the autopilot heading bug in degrees.
705  */
706 static double
707 getAPHeadingBug ()
708 {
709   return current_autopilot->get_DGTargetHeading();
710 }
711
712
713 /**
714  * Set the autopilot heading bug in degrees.
715  */
716 static void
717 setAPHeadingBug (double heading)
718 {
719   current_autopilot->set_DGTargetHeading( heading );
720 }
721
722
723 /**
724  * Get the autopilot wing leveler lock (true=on).
725  */
726 static bool
727 getAPWingLeveler ()
728 {
729     return
730       (current_autopilot->get_HeadingEnabled() &&
731        current_autopilot->get_HeadingMode() == FGAutopilot::FG_TC_HEADING_LOCK);
732 }
733
734
735 /**
736  * Set the autopilot wing leveler lock (true=on).
737  */
738 static void
739 setAPWingLeveler (bool lock)
740 {
741     if (lock) {
742         current_autopilot->set_HeadingMode(FGAutopilot::FG_TC_HEADING_LOCK);
743         current_autopilot->set_HeadingEnabled(true);
744     } else {
745         current_autopilot->set_HeadingEnabled(false);
746     }
747 }
748
749 /**
750  * Return true if the autopilot is locked to NAV1.
751  */
752 static bool
753 getAPNAV1Lock ()
754 {
755   return
756     (current_autopilot->get_HeadingEnabled() &&
757      current_autopilot->get_HeadingMode() == FGAutopilot::FG_HEADING_NAV1);
758 }
759
760
761 /**
762  * Set the autopilot NAV1 lock.
763  */
764 static void
765 setAPNAV1Lock (bool lock)
766 {
767   if (lock) {
768     current_autopilot->set_HeadingMode(FGAutopilot::FG_HEADING_NAV1);
769     current_autopilot->set_HeadingEnabled(true);
770   } else if (current_autopilot->get_HeadingMode() ==
771              FGAutopilot::FG_HEADING_NAV1) {
772     current_autopilot->set_HeadingEnabled(false);
773   }
774 }
775
776 /**
777  * Get the autopilot autothrottle lock.
778  */
779 static bool
780 getAPAutoThrottleLock ()
781 {
782   return current_autopilot->get_AutoThrottleEnabled();
783 }
784
785
786 /**
787  * Set the autothrottle lock.
788  */
789 static void
790 setAPAutoThrottleLock (bool lock)
791 {
792   current_autopilot->set_AutoThrottleEnabled(lock);
793 }
794
795
796 // kludge
797 static double
798 getAPRudderControl ()
799 {
800     if (getAPHeadingLock())
801         return current_autopilot->get_TargetHeading();
802     else
803         return globals->get_controls()->get_rudder();
804 }
805
806 // kludge
807 static void
808 setAPRudderControl (double value)
809 {
810     if (getAPHeadingLock()) {
811         SG_LOG(SG_GENERAL, SG_DEBUG, "setAPRudderControl " << value );
812         value -= current_autopilot->get_TargetHeading();
813         current_autopilot->HeadingAdjust(value < 0.0 ? -1.0 : 1.0);
814     } else {
815         globals->get_controls()->set_rudder(value);
816     }
817 }
818
819 // kludge
820 static double
821 getAPElevatorControl ()
822 {
823   if (getAPAltitudeLock())
824       return current_autopilot->get_TargetAltitude();
825   else
826     return globals->get_controls()->get_elevator();
827 }
828
829 // kludge
830 static void
831 setAPElevatorControl (double value)
832 {
833     if (getAPAltitudeLock()) {
834         SG_LOG(SG_GENERAL, SG_DEBUG, "setAPElevatorControl " << value );
835         value -= current_autopilot->get_TargetAltitude();
836         current_autopilot->AltitudeAdjust(value < 0.0 ? 100.0 : -100.0);
837     } else {
838         globals->get_controls()->set_elevator(value);
839     }
840 }
841
842 // kludge
843 static double
844 getAPThrottleControl ()
845 {
846   if (getAPAutoThrottleLock())
847     return 0.0;                 // always resets
848   else
849     return globals->get_controls()->get_throttle(0);
850 }
851
852 // kludge
853 static void
854 setAPThrottleControl (double value)
855 {
856   if (getAPAutoThrottleLock())
857     current_autopilot->AutoThrottleAdjust(value < 0.0 ? -0.01 : 0.01);
858   else
859     globals->get_controls()->set_throttle(FGControls::ALL_ENGINES, value);
860 }
861
862
863 /**
864  * Get the current visibility (meters).
865  */
866 static double
867 getVisibility ()
868 {
869 #ifndef FG_OLD_WEATHER
870   return WeatherDatabase->getWeatherVisibility();
871 #else
872   return current_weather.get_visibility();
873 #endif
874 }
875
876
877 /**
878  * Set the current visibility (meters).
879  */
880 static void
881 setVisibility (double visibility)
882 {
883 #ifndef FG_OLD_WEATHER
884   WeatherDatabase->setWeatherVisibility(visibility);
885 #else
886   current_weather.set_visibility(visibility);
887 #endif
888 }
889
890 /**
891  * Get the current wind north velocity (feet/second).
892  */
893 static double
894 getWindNorth ()
895 {
896   return current_aircraft.fdm_state->get_V_north_airmass();
897 }
898
899
900 /**
901  * Set the current wind north velocity (feet/second).
902  */
903 static void
904 setWindNorth (double speed)
905 {
906   current_aircraft.fdm_state
907     ->set_Velocities_Local_Airmass(speed, getWindEast(), getWindDown());
908 }
909
910
911 /**
912  * Get the current wind east velocity (feet/second).
913  */
914 static double
915 getWindEast ()
916 {
917   return current_aircraft.fdm_state->get_V_east_airmass();
918 }
919
920
921 /**
922  * Set the current wind east velocity (feet/second).
923  */
924 static void
925 setWindEast (double speed)
926 {
927   cout << "Set wind-east to " << speed << endl;
928   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
929                                                            speed,
930                                                            getWindDown());
931 }
932
933
934 /**
935  * Get the current wind down velocity (feet/second).
936  */
937 static double
938 getWindDown ()
939 {
940   return current_aircraft.fdm_state->get_V_down_airmass();
941 }
942
943
944 /**
945  * Set the current wind down velocity (feet/second).
946  */
947 static void
948 setWindDown (double speed)
949 {
950   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
951                                                            getWindEast(),
952                                                            speed);
953 }
954
955 /*
956  * Set the current engine0 running flag.
957  */
958 static void
959 setRunningFlag (bool flag)
960 {
961   if ( current_aircraft.fdm_state->get_num_engines() > 0 ) {
962     current_aircraft.fdm_state->get_engine(0)->set_Running_Flag( flag );
963   }
964 }
965
966 /*
967  * Set the current engine0 cranking flag.
968  */
969 //Although there is no real reason to want to tell the engine that it is cranking,
970 //this is currently necessary to avoid the cranking sound being played 
971 //before the engine inits.
972 static void
973 setCrankingFlag (bool flag)
974 {
975   if ( current_aircraft.fdm_state->get_num_engines() > 0 ) {
976     current_aircraft.fdm_state->get_engine(0)->set_Cranking_Flag( flag );
977   }
978 }
979
980 static double
981 getFOV ()
982 {
983   return globals->get_current_view()->get_fov();
984 }
985
986 static void
987 setFOV (double fov)
988 {
989   if ( fov < 180 ) {
990       globals->get_current_view()->set_fov( fov );
991   }
992 }
993
994 static long
995 getWarp ()
996 {
997   return globals->get_warp();
998 }
999
1000 static void
1001 setWarp (long warp)
1002 {
1003   globals->set_warp(warp);
1004 }
1005
1006 static long
1007 getWarpDelta ()
1008 {
1009   return globals->get_warp_delta();
1010 }
1011
1012 static void
1013 setWarpDelta (long delta)
1014 {
1015   globals->set_warp_delta(delta);
1016 }
1017
1018 static void
1019 setViewAxisLong (double axis)
1020 {
1021   axisLong = axis;
1022 }
1023
1024 static void
1025 setViewAxisLat (double axis)
1026 {
1027   axisLat = axis;
1028 }
1029
1030 static bool
1031 getWindingCCW ()
1032 {
1033   return winding_ccw;
1034 }
1035
1036 static void
1037 setWindingCCW (bool state)
1038 {
1039   winding_ccw = state;
1040   if ( winding_ccw )
1041     glFrontFace ( GL_CCW );
1042   else
1043     glFrontFace ( GL_CW );
1044 }
1045
1046 static bool
1047 getFullScreen ()
1048 {
1049 #if defined(FX) && !defined(WIN32)
1050   return global_fullscreen;
1051 #else
1052   return false;
1053 #endif
1054 }
1055
1056 static void
1057 setFullScreen (bool state)
1058 {
1059 #if defined(FX) && !defined(WIN32)
1060   global_fullscreen = state;
1061 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
1062   XMesaSetFXmode( global_fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW );
1063 #  endif
1064 #endif
1065 }
1066
1067 static bool
1068 getFDMDataLogging ()
1069 {
1070   return fdm_data_logging;
1071 }
1072
1073 static void
1074 setFDMDataLogging (bool state)
1075 {
1076                                 // kludge; no getter or setter available
1077   if (state != fdm_data_logging) {
1078     fgToggleFDMdataLogging();
1079     fdm_data_logging = state;
1080   }
1081 }
1082
1083 \f
1084 ////////////////////////////////////////////////////////////////////////
1085 // Tie the properties.
1086 ////////////////////////////////////////////////////////////////////////
1087
1088 void
1089 fgInitProps ()
1090 {
1091                                 // Simulation
1092   fgTie("/sim/logging/priority", getLoggingPriority, setLoggingPriority);
1093   fgTie("/sim/logging/classes", getLoggingClasses, setLoggingClasses);
1094   fgTie("/sim/freeze", getFreeze, setFreeze);
1095   fgTie("/sim/aircraft-dir", getAircraftDir, setAircraftDir);
1096   fgTie("/sim/view/offset-deg", getViewOffset, setViewOffset, false);
1097   fgSetArchivable("/sim/view/offset-deg");
1098   fgTie("/sim/view/goal-offset-deg", getGoalViewOffset, setGoalViewOffset, false);
1099   fgSetArchivable("/sim/view/goal-offset-deg");
1100   fgTie("/sim/view/pilot/x-offset-m",
1101         getPilotPositionXOffset, setPilotPositionXOffset);
1102   fgSetArchivable("/sim/view/pilot/x-offset-m");
1103   fgTie("/sim/view/pilot/y-offset-m",
1104         getPilotPositionYOffset, setPilotPositionYOffset);
1105   fgSetArchivable("/sim/view/pilot/y-offset-m");
1106   fgTie("/sim/view/pilot/z-offset-m",
1107         getPilotPositionZOffset, setPilotPositionZOffset);
1108   fgSetArchivable("/sim/view/pilot/z-offset-m");
1109   fgTie("/sim/time/gmt", getDateString, setDateString);
1110   fgSetArchivable("/sim/time/gmt");
1111   fgTie("/sim/time/gmt-string", getGMTString);
1112   fgTie("/sim/rendering/textures", getTextures, setTextures);
1113
1114                                 // Orientation
1115   fgTie("/orientation/heading-magnetic-deg", getHeadingMag);
1116
1117   //consumables
1118   fgTie("/consumables/fuel/tank[0]/level-gal_us",
1119         getTank1Fuel, setTank1Fuel, false);
1120   fgSetArchivable("/consumables/fuel/tank[0]/level-gal_us");
1121   fgTie("/consumables/fuel/tank[1]/level-gal_us",
1122         getTank2Fuel, setTank2Fuel, false);
1123   fgSetArchivable("/consumables/fuel/tank[1]/level-gal_us");
1124
1125                                 // Autopilot
1126   fgTie("/autopilot/locks/altitude", getAPAltitudeLock, setAPAltitudeLock);
1127   fgSetArchivable("/autopilot/locks/altitude");
1128   fgTie("/autopilot/settings/altitude-ft", getAPAltitude, setAPAltitude);
1129   fgSetArchivable("/autopilot/settings/altitude-ft");
1130   fgTie("/autopilot/locks/glide-slope", getAPGSLock, setAPGSLock);
1131   fgSetArchivable("/autopilot/locks/glide-slope");
1132   fgTie("/autopilot/locks/terrain", getAPTerrainLock, setAPTerrainLock);
1133   fgSetArchivable("/autopilot/locks/terrain");
1134   fgTie("/autopilot/settings/agl-ft", getAPAltitude, setAPAltitude);
1135   fgSetArchivable("/autopilot/settings/agl-ft");
1136   fgTie("/autopilot/settings/climb-rate-fpm", getAPClimb, setAPClimb, false);
1137   fgSetArchivable("/autopilot/settings/climb-rate-fpm");
1138   fgTie("/autopilot/locks/heading", getAPHeadingLock, setAPHeadingLock);
1139   fgSetArchivable("/autopilot/locks/heading");
1140   fgTie("/autopilot/settings/heading-bug-deg",
1141         getAPHeadingBug, setAPHeadingBug, false);
1142   fgSetArchivable("/autopilot/settings/heading-bug-deg");
1143   fgTie("/autopilot/locks/wing-leveler", getAPWingLeveler, setAPWingLeveler);
1144   fgSetArchivable("/autopilot/locks/wing-leveler");
1145   fgTie("/autopilot/locks/nav[0]", getAPNAV1Lock, setAPNAV1Lock);
1146   fgSetArchivable("/autopilot/locks/nav[0]");
1147   fgTie("/autopilot/locks/auto-throttle",
1148         getAPAutoThrottleLock, setAPAutoThrottleLock);
1149   fgSetArchivable("/autopilot/locks/auto-throttle");
1150   fgTie("/autopilot/control-overrides/rudder",
1151         getAPRudderControl, setAPRudderControl);
1152   fgSetArchivable("/autopilot/control-overrides/rudder");
1153   fgTie("/autopilot/control-overrides/elevator",
1154         getAPElevatorControl, setAPElevatorControl);
1155   fgSetArchivable("/autopilot/control-overrides/elevator");
1156   fgTie("/autopilot/control-overrides/throttle",
1157         getAPThrottleControl, setAPThrottleControl);
1158   fgSetArchivable("/autopilot/control-overrides/throttle");
1159
1160                                 // Environment
1161   fgTie("/environment/visibility-m", getVisibility, setVisibility);
1162   fgSetArchivable("/environment/visibility-m");
1163   fgTie("/environment/wind-north-fps", getWindNorth, setWindNorth);
1164   fgSetArchivable("/environment/wind-north-fps");
1165   fgTie("/environment/wind-east-fps", getWindEast, setWindEast);
1166   fgSetArchivable("/environment/wind-east-fps");
1167   fgTie("/environment/wind-down-fps", getWindDown, setWindDown);
1168   fgSetArchivable("/environment/wind-down-fps");
1169
1170   fgTie("/environment/magnetic-variation-deg", getMagVar);
1171   fgTie("/environment/magnetic-dip-deg", getMagDip);
1172
1173                                 // View
1174   fgTie("/sim/field-of-view", getFOV, setFOV);
1175   fgSetArchivable("/sim/field-of-view");
1176   fgTie("/sim/time/warp", getWarp, setWarp, false);
1177   fgTie("/sim/time/warp-delta", getWarpDelta, setWarpDelta);
1178   fgTie("/sim/view/axes/long", (double(*)())0, setViewAxisLong);
1179   fgTie("/sim/view/axes/lat", (double(*)())0, setViewAxisLat);
1180
1181                                 // Misc. Temporary junk.
1182   fgTie("/sim/temp/winding-ccw", getWindingCCW, setWindingCCW, false);
1183   fgTie("/sim/temp/full-screen", getFullScreen, setFullScreen);
1184   fgTie("/sim/temp/fdm-data-logging", getFDMDataLogging, setFDMDataLogging);
1185         
1186 }
1187
1188
1189 void
1190 fgUpdateProps ()
1191 {
1192   _set_view_from_axes();
1193 }
1194
1195
1196 \f
1197 ////////////////////////////////////////////////////////////////////////
1198 // Save and restore.
1199 ////////////////////////////////////////////////////////////////////////
1200
1201
1202 /**
1203  * Save the current state of the simulator to a stream.
1204  */
1205 bool
1206 fgSaveFlight (ostream &output)
1207 {
1208   try {
1209     writeProperties(output, globals->get_props());
1210   } catch (const sg_exception &e) {
1211     guiErrorMessage("Error saving flight: ", e);
1212     return false;
1213   }
1214   return true;
1215 }
1216
1217
1218 /**
1219  * Restore the current state of the simulator from a stream.
1220  */
1221 bool
1222 fgLoadFlight (istream &input)
1223 {
1224   SGPropertyNode props;
1225   try {
1226     readProperties(input, &props);
1227   } catch (const sg_exception &e) {
1228     guiErrorMessage("Error reading saved flight: ", e);
1229     return false;
1230   }
1231   copyProperties(&props, globals->get_props());
1232   // When loading a flight, make it the
1233   // new initial state.
1234   globals->saveInitialState();
1235   return true;
1236 }
1237
1238
1239 \f
1240 ////////////////////////////////////////////////////////////////////////
1241 // Implementation of FGCondition.
1242 ////////////////////////////////////////////////////////////////////////
1243
1244 FGCondition::FGCondition ()
1245 {
1246 }
1247
1248 FGCondition::~FGCondition ()
1249 {
1250 }
1251
1252
1253 \f
1254 ////////////////////////////////////////////////////////////////////////
1255 // Implementation of FGPropertyCondition.
1256 ////////////////////////////////////////////////////////////////////////
1257
1258 FGPropertyCondition::FGPropertyCondition (const string &propname)
1259   : _node(fgGetNode(propname, true))
1260 {
1261 }
1262
1263 FGPropertyCondition::~FGPropertyCondition ()
1264 {
1265 }
1266
1267
1268 \f
1269 ////////////////////////////////////////////////////////////////////////
1270 // Implementation of FGNotCondition.
1271 ////////////////////////////////////////////////////////////////////////
1272
1273 FGNotCondition::FGNotCondition (FGCondition * condition)
1274   : _condition(condition)
1275 {
1276 }
1277
1278 FGNotCondition::~FGNotCondition ()
1279 {
1280   delete _condition;
1281 }
1282
1283 bool
1284 FGNotCondition::test () const
1285 {
1286   return !(_condition->test());
1287 }
1288
1289
1290 \f
1291 ////////////////////////////////////////////////////////////////////////
1292 // Implementation of FGAndCondition.
1293 ////////////////////////////////////////////////////////////////////////
1294
1295 FGAndCondition::FGAndCondition ()
1296 {
1297 }
1298
1299 FGAndCondition::~FGAndCondition ()
1300 {
1301   for (unsigned int i = 0; i < _conditions.size(); i++)
1302     delete _conditions[i];
1303 }
1304
1305 bool
1306 FGAndCondition::test () const
1307 {
1308   int nConditions = _conditions.size();
1309   for (int i = 0; i < nConditions; i++) {
1310     if (!_conditions[i]->test())
1311       return false;
1312   }
1313   return true;
1314 }
1315
1316 void
1317 FGAndCondition::addCondition (FGCondition * condition)
1318 {
1319   _conditions.push_back(condition);
1320 }
1321
1322
1323 \f
1324 ////////////////////////////////////////////////////////////////////////
1325 // Implementation of FGOrCondition.
1326 ////////////////////////////////////////////////////////////////////////
1327
1328 FGOrCondition::FGOrCondition ()
1329 {
1330 }
1331
1332 FGOrCondition::~FGOrCondition ()
1333 {
1334   for (unsigned int i = 0; i < _conditions.size(); i++)
1335     delete _conditions[i];
1336 }
1337
1338 bool
1339 FGOrCondition::test () const
1340 {
1341   int nConditions = _conditions.size();
1342   for (int i = 0; i < nConditions; i++) {
1343     if (_conditions[i]->test())
1344       return true;
1345   }
1346   return false;
1347 }
1348
1349 void
1350 FGOrCondition::addCondition (FGCondition * condition)
1351 {
1352   _conditions.push_back(condition);
1353 }
1354
1355
1356 \f
1357 ////////////////////////////////////////////////////////////////////////
1358 // Implementation of FGComparisonCondition.
1359 ////////////////////////////////////////////////////////////////////////
1360
1361 static int
1362 doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
1363 {
1364   switch (left->getType()) {
1365   case SGPropertyNode::BOOL: {
1366     bool v1 = left->getBoolValue();
1367     bool v2 = right->getBoolValue();
1368     if (v1 < v2)
1369       return FGComparisonCondition::LESS_THAN;
1370     else if (v1 > v2)
1371       return FGComparisonCondition::GREATER_THAN;
1372     else
1373       return FGComparisonCondition::EQUALS;
1374     break;
1375   }
1376   case SGPropertyNode::INT: {
1377     int v1 = left->getIntValue();
1378     int v2 = right->getIntValue();
1379     if (v1 < v2)
1380       return FGComparisonCondition::LESS_THAN;
1381     else if (v1 > v2)
1382       return FGComparisonCondition::GREATER_THAN;
1383     else
1384       return FGComparisonCondition::EQUALS;
1385     break;
1386   }
1387   case SGPropertyNode::LONG: {
1388     long v1 = left->getLongValue();
1389     long v2 = right->getLongValue();
1390     if (v1 < v2)
1391       return FGComparisonCondition::LESS_THAN;
1392     else if (v1 > v2)
1393       return FGComparisonCondition::GREATER_THAN;
1394     else
1395       return FGComparisonCondition::EQUALS;
1396     break;
1397   }
1398   case SGPropertyNode::FLOAT: {
1399     float v1 = left->getFloatValue();
1400     float v2 = right->getFloatValue();
1401     if (v1 < v2)
1402       return FGComparisonCondition::LESS_THAN;
1403     else if (v1 > v2)
1404       return FGComparisonCondition::GREATER_THAN;
1405     else
1406       return FGComparisonCondition::EQUALS;
1407     break;
1408   }
1409   case SGPropertyNode::DOUBLE: {
1410     double v1 = left->getDoubleValue();
1411     double v2 = right->getDoubleValue();
1412     if (v1 < v2)
1413       return FGComparisonCondition::LESS_THAN;
1414     else if (v1 > v2)
1415       return FGComparisonCondition::GREATER_THAN;
1416     else
1417       return FGComparisonCondition::EQUALS;
1418     break;
1419   }
1420   case SGPropertyNode::STRING: 
1421   case SGPropertyNode::NONE:
1422   case SGPropertyNode::UNSPECIFIED: {
1423     string v1 = left->getStringValue();
1424     string v2 = right->getStringValue();
1425     if (v1 < v2)
1426       return FGComparisonCondition::LESS_THAN;
1427     else if (v1 > v2)
1428       return FGComparisonCondition::GREATER_THAN;
1429     else
1430       return FGComparisonCondition::EQUALS;
1431     break;
1432   }
1433   }
1434   throw sg_exception("Unrecognized node type");
1435   return 0;
1436 }
1437
1438
1439 FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
1440   : _type(type),
1441     _reverse(reverse),
1442     _left_property(0),
1443     _right_property(0),
1444     _right_value(0)
1445 {
1446 }
1447
1448 FGComparisonCondition::~FGComparisonCondition ()
1449 {
1450   delete _right_value;
1451 }
1452
1453 bool
1454 FGComparisonCondition::test () const
1455 {
1456                                 // Always fail if incompletely specified
1457   if (_left_property == 0 ||
1458       (_right_property == 0 && _right_value == 0))
1459     return false;
1460
1461                                 // Get LESS_THAN, EQUALS, or GREATER_THAN
1462   int cmp =
1463     doComparison(_left_property,
1464                  (_right_property != 0 ? _right_property : _right_value));
1465   if (!_reverse)
1466     return (cmp == _type);
1467   else
1468     return (cmp != _type);
1469 }
1470
1471 void
1472 FGComparisonCondition::setLeftProperty (const string &propname)
1473 {
1474   _left_property = fgGetNode(propname, true);
1475 }
1476
1477 void
1478 FGComparisonCondition::setRightProperty (const string &propname)
1479 {
1480   delete _right_value;
1481   _right_value = 0;
1482   _right_property = fgGetNode(propname, true);
1483 }
1484
1485 void
1486 FGComparisonCondition::setRightValue (const SGPropertyNode *node)
1487 {
1488   _right_property = 0;
1489   delete _right_value;
1490   _right_value = new SGPropertyNode(*node);
1491 }
1492
1493
1494 \f
1495 ////////////////////////////////////////////////////////////////////////
1496 // Read a condition and use it if necessary.
1497 ////////////////////////////////////////////////////////////////////////
1498
1499                                 // Forward declaration
1500 static FGCondition * readCondition (const SGPropertyNode * node);
1501
1502 static FGCondition *
1503 readPropertyCondition (const SGPropertyNode * node)
1504 {
1505   return new FGPropertyCondition(node->getStringValue());
1506 }
1507
1508 static FGCondition *
1509 readNotCondition (const SGPropertyNode * node)
1510 {
1511   int nChildren = node->nChildren();
1512   for (int i = 0; i < nChildren; i++) {
1513     const SGPropertyNode * child = node->getChild(i);
1514     FGCondition * condition = readCondition(child);
1515     if (condition != 0)
1516       return new FGNotCondition(condition);
1517   }
1518   SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
1519   return 0;
1520 }
1521
1522 static FGCondition *
1523 readAndConditions (const SGPropertyNode * node)
1524 {
1525   FGAndCondition * andCondition = new FGAndCondition;
1526   int nChildren = node->nChildren();
1527   for (int i = 0; i < nChildren; i++) {
1528     const SGPropertyNode * child = node->getChild(i);
1529     FGCondition * condition = readCondition(child);
1530     if (condition != 0)
1531       andCondition->addCondition(condition);
1532   }
1533   return andCondition;
1534 }
1535
1536 static FGCondition *
1537 readOrConditions (const SGPropertyNode * node)
1538 {
1539   FGOrCondition * orCondition = new FGOrCondition;
1540   int nChildren = node->nChildren();
1541   for (int i = 0; i < nChildren; i++) {
1542     const SGPropertyNode * child = node->getChild(i);
1543     FGCondition * condition = readCondition(child);
1544     if (condition != 0)
1545       orCondition->addCondition(condition);
1546   }
1547   return orCondition;
1548 }
1549
1550 static FGCondition *
1551 readComparison (const SGPropertyNode * node,
1552                 FGComparisonCondition::Type type,
1553                 bool reverse)
1554 {
1555   FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
1556   condition->setLeftProperty(node->getStringValue("property[0]"));
1557   if (node->hasValue("property[1]"))
1558     condition->setRightProperty(node->getStringValue("property[1]"));
1559   else
1560     condition->setRightValue(node->getChild("value", 0));
1561
1562   return condition;
1563 }
1564
1565 static FGCondition *
1566 readCondition (const SGPropertyNode * node)
1567 {
1568   const string &name = node->getName();
1569   if (name == "property")
1570     return readPropertyCondition(node);
1571   else if (name == "not")
1572     return readNotCondition(node);
1573   else if (name == "and")
1574     return readAndConditions(node);
1575   else if (name == "or")
1576     return readOrConditions(node);
1577   else if (name == "less-than")
1578     return readComparison(node, FGComparisonCondition::LESS_THAN, false);
1579   else if (name == "less-than-equals")
1580     return readComparison(node, FGComparisonCondition::GREATER_THAN, true);
1581   else if (name == "greater-than")
1582     return readComparison(node, FGComparisonCondition::GREATER_THAN, false);
1583   else if (name == "greater-than-equals")
1584     return readComparison(node, FGComparisonCondition::LESS_THAN, true);
1585   else if (name == "equals")
1586     return readComparison(node, FGComparisonCondition::EQUALS, false);
1587   else if (name == "not-equals")
1588     return readComparison(node, FGComparisonCondition::EQUALS, true);
1589   else
1590     return 0;
1591 }
1592
1593
1594 \f
1595 ////////////////////////////////////////////////////////////////////////
1596 // Implementation of FGConditional.
1597 ////////////////////////////////////////////////////////////////////////
1598
1599 FGConditional::FGConditional ()
1600   : _condition (0)
1601 {
1602 }
1603
1604 FGConditional::~FGConditional ()
1605 {
1606   delete _condition;
1607 }
1608
1609 void
1610 FGConditional::setCondition (FGCondition * condition)
1611 {
1612   delete _condition;
1613   _condition = condition;
1614 }
1615
1616 bool
1617 FGConditional::test () const
1618 {
1619   return ((_condition == 0) || _condition->test());
1620 }
1621
1622
1623 \f
1624 // The top-level is always an implicit 'and' group
1625 FGCondition *
1626 fgReadCondition (const SGPropertyNode * node)
1627 {
1628   return readAndConditions(node);
1629 }
1630
1631
1632 // end of fg_props.cxx