]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
Improve timing statistics
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <simgear/compiler.h>
28 #include <simgear/structure/exception.hxx>
29 #include <simgear/props/props_io.hxx>
30
31 #include <simgear/magvar/magvar.hxx>
32 #include <simgear/timing/sg_time.hxx>
33 #include <simgear/misc/sg_path.hxx>
34 #include <simgear/scene/model/particles.hxx>
35 #include <simgear/sound/soundmgr_openal.hxx>
36
37 #include <GUI/gui.h>
38
39 #include "globals.hxx"
40 #include "fg_props.hxx"
41
42
43 static bool winding_ccw = true; // FIXME: temporary
44
45 static bool frozen = false;     // FIXME: temporary
46
47 using std::string;
48 \f
49 ////////////////////////////////////////////////////////////////////////
50 // Default property bindings (not yet handled by any module).
51 ////////////////////////////////////////////////////////////////////////
52
53 struct LogClassMapping {
54   sgDebugClass c;
55   string name;
56   LogClassMapping(sgDebugClass cc, string nname) { c = cc; name = nname; }
57 };
58
59 LogClassMapping log_class_mappings [] = {
60   LogClassMapping(SG_NONE, "none"),
61   LogClassMapping(SG_TERRAIN, "terrain"),
62   LogClassMapping(SG_ASTRO, "astro"),
63   LogClassMapping(SG_FLIGHT, "flight"),
64   LogClassMapping(SG_INPUT, "input"),
65   LogClassMapping(SG_GL, "gl"),
66   LogClassMapping(SG_VIEW, "view"),
67   LogClassMapping(SG_COCKPIT, "cockpit"),
68   LogClassMapping(SG_GENERAL, "general"),
69   LogClassMapping(SG_MATH, "math"),
70   LogClassMapping(SG_EVENT, "event"),
71   LogClassMapping(SG_AIRCRAFT, "aircraft"),
72   LogClassMapping(SG_AUTOPILOT, "autopilot"),
73   LogClassMapping(SG_IO, "io"),
74   LogClassMapping(SG_CLIPPER, "clipper"),
75   LogClassMapping(SG_NETWORK, "network"),
76   LogClassMapping(SG_INSTR, "instrumentation"),
77   LogClassMapping(SG_SYSTEMS, "systems"),
78   LogClassMapping(SG_UNDEFD, "")
79 };
80
81
82 /**
83  * Get the logging classes.
84  */
85 // XXX Making the result buffer be global is a band-aid that hopefully
86 // delays its destruction 'til after its last use.
87 namespace
88 {
89 string loggingResult;
90 }
91
92 static const char *
93 getLoggingClasses ()
94 {
95   sgDebugClass classes = logbuf::get_log_classes();
96   loggingResult.clear();
97   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
98     if ((classes&log_class_mappings[i].c) > 0) {
99       if (!loggingResult.empty())
100         loggingResult += '|';
101       loggingResult += log_class_mappings[i].name;
102     }
103   }
104   return loggingResult.c_str();
105 }
106
107
108 static void
109 addLoggingClass (const string &name)
110 {
111   sgDebugClass classes = logbuf::get_log_classes();
112   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
113     if (name == log_class_mappings[i].name) {
114       logbuf::set_log_classes(sgDebugClass(classes|log_class_mappings[i].c));
115       return;
116     }
117   }
118   SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging class: " << name);
119 }
120
121
122 /**
123  * Set the logging classes.
124  */
125 static void
126 setLoggingClasses (const char * c)
127 {
128   string classes = c;
129   logbuf::set_log_classes(SG_NONE);
130
131   if (classes == "none") {
132     SG_LOG(SG_GENERAL, SG_INFO, "Disabled all logging classes");
133     return;
134   }
135
136   if (classes.empty() || classes == "all") { // default
137     logbuf::set_log_classes(SG_ALL);
138     SG_LOG(SG_GENERAL, SG_INFO, "Enabled all logging classes: "
139            << getLoggingClasses());
140     return;
141   }
142
143   string rest = classes;
144   string name = "";
145   int sep = rest.find('|');
146   while (sep > 0) {
147     name = rest.substr(0, sep);
148     rest = rest.substr(sep+1);
149     addLoggingClass(name);
150     sep = rest.find('|');
151   }
152   addLoggingClass(rest);
153   SG_LOG(SG_GENERAL, SG_INFO, "Set logging classes to "
154          << getLoggingClasses());
155 }
156
157
158 /**
159  * Get the logging priority.
160  */
161 static const char *
162 getLoggingPriority ()
163 {
164   switch (logbuf::get_log_priority()) {
165   case SG_BULK:
166     return "bulk";
167   case SG_DEBUG:
168     return "debug";
169   case SG_INFO:
170     return "info";
171   case SG_WARN:
172     return "warn";
173   case SG_ALERT:
174     return "alert";
175   default:
176     SG_LOG(SG_GENERAL, SG_WARN, "Internal: Unknown logging priority number: "
177            << logbuf::get_log_priority());
178     return "unknown";
179   }
180 }
181
182
183 /**
184  * Set the logging priority.
185  */
186 static void
187 setLoggingPriority (const char * p)
188 {
189   if (p == 0)
190       return;
191   string priority = p;
192   if (priority == "bulk") {
193     logbuf::set_log_priority(SG_BULK);
194   } else if (priority == "debug") {
195     logbuf::set_log_priority(SG_DEBUG);
196   } else if (priority.empty() || priority == "info") { // default
197     logbuf::set_log_priority(SG_INFO);
198   } else if (priority == "warn") {
199     logbuf::set_log_priority(SG_WARN);
200   } else if (priority == "alert") {
201     logbuf::set_log_priority(SG_ALERT);
202   } else {
203     SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
204   }
205   SG_LOG(SG_GENERAL, SG_DEBUG, "Logging priority is " << getLoggingPriority());
206 }
207
208
209 /**
210  * Return the current frozen state.
211  */
212 static bool
213 getFreeze ()
214 {
215   return frozen;
216 }
217
218
219 /**
220  * Set the current frozen state.
221  */
222 static void
223 setFreeze (bool f)
224 {
225     frozen = f;
226
227     // Stop sound on a pause
228     SGSoundMgr *smgr = globals->get_soundmgr();
229     if ( smgr != NULL ) {
230         if ( f ) {
231             smgr->suspend();
232         } else if (fgGetBool("/sim/sound/working")) {
233             smgr->resume();
234         }
235     }
236
237     // Pause the particle system
238     simgear::Particles::setFrozen(f);
239 }
240
241
242 /**
243  * Return the number of milliseconds elapsed since simulation started.
244  */
245 static double
246 getElapsedTime_sec ()
247 {
248   return globals->get_sim_time_sec();
249 }
250
251
252 /**
253  * Return the current Zulu time.
254  */
255 static const char *
256 getDateString ()
257 {
258   static char buf[64];          // FIXME
259   
260   SGTime * st = globals->get_time_params();
261   if (!st) {
262     buf[0] = 0;
263     return buf;
264   }
265   
266   struct tm * t = st->getGmt();
267   sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
268           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
269           t->tm_hour, t->tm_min, t->tm_sec);
270   return buf;
271 }
272
273
274 /**
275  * Set the current Zulu time.
276  */
277 static void
278 setDateString (const char * date_string)
279 {
280   SGTime * st = globals->get_time_params();
281   struct tm * current_time = st->getGmt();
282   struct tm new_time;
283
284                                 // Scan for basic ISO format
285                                 // YYYY-MM-DDTHH:MM:SS
286   int ret = sscanf(date_string, "%d-%d-%dT%d:%d:%d",
287                    &(new_time.tm_year), &(new_time.tm_mon),
288                    &(new_time.tm_mday), &(new_time.tm_hour),
289                    &(new_time.tm_min), &(new_time.tm_sec));
290
291                                 // Be pretty picky about this, so
292                                 // that strange things don't happen
293                                 // if the save file has been edited
294                                 // by hand.
295   if (ret != 6) {
296     SG_LOG(SG_INPUT, SG_WARN, "Date/time string " << date_string
297            << " not in YYYY-MM-DDTHH:MM:SS format; skipped");
298     return;
299   }
300
301                                 // OK, it looks like we got six
302                                 // values, one way or another.
303   new_time.tm_year -= 1900;
304   new_time.tm_mon -= 1;
305                                 // Now, tell flight gear to use
306                                 // the new time.  This was far
307                                 // too difficult, by the way.
308   long int warp =
309     mktime(&new_time) - mktime(current_time) + globals->get_warp();
310     
311   fgSetInt("/sim/time/warp", warp);
312 }
313
314 /**
315  * Return the GMT as a string.
316  */
317 static const char *
318 getGMTString ()
319 {
320   static char buf[16];
321   SGTime * st = globals->get_time_params();
322   if (!st) {
323     buf[0] = 0;
324     return buf;
325   }
326   
327   struct tm *t = st->getGmt();
328   snprintf(buf, 16, "%.2d:%.2d:%.2d",
329       t->tm_hour, t->tm_min, t->tm_sec);
330   return buf;
331 }
332
333 /**
334  * Return the magnetic variation
335  */
336 static double
337 getMagVar ()
338 {
339   return globals->get_mag()->get_magvar() * SGD_RADIANS_TO_DEGREES;
340 }
341
342
343 /**
344  * Return the magnetic dip
345  */
346 static double
347 getMagDip ()
348 {
349   return globals->get_mag()->get_magdip() * SGD_RADIANS_TO_DEGREES;
350 }
351
352
353 /**
354  * Return the current heading in degrees.
355  */
356 static double
357 getHeadingMag ()
358 {
359   double magheading;
360   magheading = fgGetDouble("/orientation/heading-deg") - getMagVar();
361   if (magheading < 0) magheading += 360;
362   return magheading;
363 }
364
365 /**
366  * Return the current track in degrees.
367  */
368 static double
369 getTrackMag ()
370 {
371   double magtrack;
372   magtrack = fgGetDouble("/orientation/track-deg") - getMagVar();
373   if (magtrack < 0) magtrack += 360;
374   return magtrack;
375 }
376
377 static bool
378 getWindingCCW ()
379 {
380   return winding_ccw;
381 }
382
383 static void
384 setWindingCCW (bool state)
385 {
386   winding_ccw = state;
387   if ( winding_ccw )
388     glFrontFace ( GL_CCW );
389   else
390     glFrontFace ( GL_CW );
391 }
392
393 static const char *
394 getLongitudeString ()
395 {
396   static SGConstPropertyNode_ptr n = fgGetNode("/position/longitude-deg", true);
397   static SGConstPropertyNode_ptr f = fgGetNode("/sim/lon-lat-format", true);
398   static char buf[32];
399   double d = n->getDoubleValue();
400   int format = f->getIntValue();
401   char c = d < 0.0 ? 'W' : 'E';
402
403   if (format == 0) {
404     snprintf(buf, 32, "%3.6f%c", d, c);
405
406   } else if (format == 1) {
407     // dd mm.mmm' (DMM-Format) -- uses a round-off factor tailored to the
408     // required precision of the minutes field (three decimal places),
409     // preventing minute values of 60.
410     double deg = fabs(d) + 5.0E-4 / 60.0;
411     double min = fabs(deg - int(deg)) * 60.0 - 4.999E-4;
412     snprintf(buf, 32, "%d*%06.3f%c", int(d < 0.0 ? -deg : deg), min, c);
413
414   } else {
415     // mm'ss.s'' (DMS-Format) -- uses a round-off factor tailored to the
416     // required precision of the seconds field (one decimal place),
417     // preventing second values of 60.
418     double deg = fabs(d) + 0.05 / 3600.0;
419     double min = (deg - int(deg)) * 60.0;
420     double sec = (min - int(min)) * 60.0 - 0.049;
421     snprintf(buf, 32, "%d*%02d %04.1f%c", int(d < 0.0 ? -deg : deg),
422         int(min), fabs(sec), c);
423   }
424   buf[31] = '\0';
425   return buf;
426 }
427
428 static const char *
429 getLatitudeString ()
430 {
431   static SGConstPropertyNode_ptr n = fgGetNode("/position/latitude-deg", true);
432   static SGConstPropertyNode_ptr f = fgGetNode("/sim/lon-lat-format", true);
433   static char buf[32];
434   double d = n->getDoubleValue();
435   int format = f->getIntValue();
436   char c = d < 0.0 ? 'S' : 'N';
437
438   if (format == 0) {
439     snprintf(buf, 32, "%3.6f%c", d, c);
440
441   } else if (format == 1) {
442     double deg = fabs(d) + 5.0E-4 / 60.0;
443     double min = fabs(deg - int(deg)) * 60.0 - 4.999E-4;
444     snprintf(buf, 32, "%d*%06.3f%c", int(d < 0.0 ? -deg : deg), min, c);
445
446   } else {
447     double deg = fabs(d) + 0.05 / 3600.0;
448     double min = (deg - int(deg)) * 60.0;
449     double sec = (min - int(min)) * 60.0 - 0.049;
450     snprintf(buf, 32, "%d*%02d %04.1f%c", int(d < 0.0 ? -deg : deg),
451         int(min), fabs(sec), c);
452   }
453   buf[31] = '\0';
454   return buf;
455 }
456
457
458
459 \f
460 ////////////////////////////////////////////////////////////////////////
461 // Tie the properties.
462 ////////////////////////////////////////////////////////////////////////
463
464 FGProperties::FGProperties ()
465 {
466 }
467
468 FGProperties::~FGProperties ()
469 {
470 }
471
472 void
473 FGProperties::init ()
474 {
475 }
476
477 void
478 FGProperties::bind ()
479 {
480                                 // Simulation
481   fgTie("/sim/logging/priority", getLoggingPriority, setLoggingPriority);
482   fgTie("/sim/logging/classes", getLoggingClasses, setLoggingClasses);
483   fgTie("/sim/freeze/master", getFreeze, setFreeze);
484
485   fgTie("/sim/time/elapsed-sec", getElapsedTime_sec);
486   fgTie("/sim/time/gmt", getDateString, setDateString);
487   fgSetArchivable("/sim/time/gmt");
488   fgTie("/sim/time/gmt-string", getGMTString);
489
490                                 // Position
491   fgTie("/position/latitude-string", getLatitudeString);
492   fgTie("/position/longitude-string", getLongitudeString);
493
494                                 // Orientation
495   fgTie("/orientation/heading-magnetic-deg", getHeadingMag);
496   fgTie("/orientation/track-magnetic-deg", getTrackMag);
497
498   fgTie("/environment/magnetic-variation-deg", getMagVar);
499   fgTie("/environment/magnetic-dip-deg", getMagDip);
500
501                                 // Misc. Temporary junk.
502   fgTie("/sim/temp/winding-ccw", getWindingCCW, setWindingCCW, false);
503 }
504
505 void
506 FGProperties::unbind ()
507 {
508                                 // Simulation
509   fgUntie("/sim/logging/priority");
510   fgUntie("/sim/logging/classes");
511   fgUntie("/sim/freeze/master");
512
513   fgUntie("/sim/time/elapsed-sec");
514   fgUntie("/sim/time/gmt");
515   fgUntie("/sim/time/gmt-string");
516                                 // Position
517   fgUntie("/position/latitude-string");
518   fgUntie("/position/longitude-string");
519
520                                 // Orientation
521   fgUntie("/orientation/heading-magnetic-deg");
522   fgUntie("/orientation/track-magnetic-deg");
523
524                                 // Environment
525   fgUntie("/environment/magnetic-variation-deg");
526   fgUntie("/environment/magnetic-dip-deg");
527
528                                 // Misc. Temporary junk.
529   fgUntie("/sim/temp/winding-ccw");
530 //  fgUntie("/sim/temp/full-screen");
531 //  fgUntie("/sim/temp/fdm-data-logging");
532 }
533
534 void
535 FGProperties::update (double dt)
536 {
537     static SGPropertyNode_ptr offset = fgGetNode("/sim/time/local-offset", true);
538     offset->setIntValue(globals->get_time_params()->get_local_offset());
539
540
541     // utc date/time
542     static SGPropertyNode_ptr uyear = fgGetNode("/sim/time/utc/year", true);
543     static SGPropertyNode_ptr umonth = fgGetNode("/sim/time/utc/month", true);
544     static SGPropertyNode_ptr uday = fgGetNode("/sim/time/utc/day", true);
545     static SGPropertyNode_ptr uhour = fgGetNode("/sim/time/utc/hour", true);
546     static SGPropertyNode_ptr umin = fgGetNode("/sim/time/utc/minute", true);
547     static SGPropertyNode_ptr usec = fgGetNode("/sim/time/utc/second", true);
548     static SGPropertyNode_ptr uwday = fgGetNode("/sim/time/utc/weekday", true);
549     static SGPropertyNode_ptr udsec = fgGetNode("/sim/time/utc/day-seconds", true);
550
551     struct tm *u = globals->get_time_params()->getGmt();
552     uyear->setIntValue(u->tm_year + 1900);
553     umonth->setIntValue(u->tm_mon + 1);
554     uday->setIntValue(u->tm_mday);
555     uhour->setIntValue(u->tm_hour);
556     umin->setIntValue(u->tm_min);
557     usec->setIntValue(u->tm_sec);
558     uwday->setIntValue(u->tm_wday);
559
560     udsec->setIntValue(u->tm_hour * 3600 + u->tm_min * 60 + u->tm_sec);
561
562
563     // real local date/time
564     static SGPropertyNode_ptr ryear = fgGetNode("/sim/time/real/year", true);
565     static SGPropertyNode_ptr rmonth = fgGetNode("/sim/time/real/month", true);
566     static SGPropertyNode_ptr rday = fgGetNode("/sim/time/real/day", true);
567     static SGPropertyNode_ptr rhour = fgGetNode("/sim/time/real/hour", true);
568     static SGPropertyNode_ptr rmin = fgGetNode("/sim/time/real/minute", true);
569     static SGPropertyNode_ptr rsec = fgGetNode("/sim/time/real/second", true);
570     static SGPropertyNode_ptr rwday = fgGetNode("/sim/time/real/weekday", true);
571
572     time_t real = time(0);
573     struct tm *r = localtime(&real);
574     ryear->setIntValue(r->tm_year + 1900);
575     rmonth->setIntValue(r->tm_mon + 1);
576     rday->setIntValue(r->tm_mday);
577     rhour->setIntValue(r->tm_hour);
578     rmin->setIntValue(r->tm_min);
579     rsec->setIntValue(r->tm_sec);
580     rwday->setIntValue(r->tm_wday);
581 }
582
583
584 \f
585 ////////////////////////////////////////////////////////////////////////
586 // Save and restore.
587 ////////////////////////////////////////////////////////////////////////
588
589
590 /**
591  * Save the current state of the simulator to a stream.
592  */
593 bool
594 fgSaveFlight (std::ostream &output, bool write_all)
595 {
596
597   fgSetBool("/sim/presets/onground", false);
598   fgSetArchivable("/sim/presets/onground");
599   fgSetBool("/sim/presets/trim", false);
600   fgSetArchivable("/sim/presets/trim");
601   fgSetString("/sim/presets/speed-set", "UVW");
602   fgSetArchivable("/sim/presets/speed-set");
603
604   try {
605     writeProperties(output, globals->get_props(), write_all);
606   } catch (const sg_exception &e) {
607     guiErrorMessage("Error saving flight: ", e);
608     return false;
609   }
610   return true;
611 }
612
613
614 /**
615  * Restore the current state of the simulator from a stream.
616  */
617 bool
618 fgLoadFlight (std::istream &input)
619 {
620   SGPropertyNode props;
621   try {
622     readProperties(input, &props);
623   } catch (const sg_exception &e) {
624     guiErrorMessage("Error reading saved flight: ", e);
625     return false;
626   }
627
628   fgSetBool("/sim/presets/onground", false);
629   fgSetBool("/sim/presets/trim", false);
630   fgSetString("/sim/presets/speed-set", "UVW");
631
632   copyProperties(&props, globals->get_props());
633   // When loading a flight, make it the
634   // new initial state.
635   globals->saveInitialState();
636   return true;
637 }
638
639
640 bool
641 fgLoadProps (const char * path, SGPropertyNode * props, bool in_fg_root, int default_mode)
642 {
643     string fullpath;
644     if (in_fg_root) {
645         SGPath loadpath(globals->get_fg_root());
646         loadpath.append(path);
647         fullpath = loadpath.str();
648     } else {
649         fullpath = path;
650     }
651
652     try {
653         readProperties(fullpath, props, default_mode);
654     } catch (const sg_exception &e) {
655         guiErrorMessage("Error reading properties: ", e);
656         return false;
657     }
658     return true;
659 }
660
661
662 \f
663 ////////////////////////////////////////////////////////////////////////
664 // Property convenience functions.
665 ////////////////////////////////////////////////////////////////////////
666
667 SGPropertyNode *
668 fgGetNode (const char * path, bool create)
669 {
670   return globals->get_props()->getNode(path, create);
671 }
672
673 SGPropertyNode * 
674 fgGetNode (const char * path, int index, bool create)
675 {
676   return globals->get_props()->getNode(path, index, create);
677 }
678
679 bool
680 fgHasNode (const char * path)
681 {
682   return (fgGetNode(path, false) != 0);
683 }
684
685 void
686 fgAddChangeListener (SGPropertyChangeListener * listener, const char * path)
687 {
688   fgGetNode(path, true)->addChangeListener(listener);
689 }
690
691 void
692 fgAddChangeListener (SGPropertyChangeListener * listener,
693                      const char * path, int index)
694 {
695   fgGetNode(path, index, true)->addChangeListener(listener);
696 }
697
698 bool
699 fgGetBool (const char * name, bool defaultValue)
700 {
701   return globals->get_props()->getBoolValue(name, defaultValue);
702 }
703
704 int
705 fgGetInt (const char * name, int defaultValue)
706 {
707   return globals->get_props()->getIntValue(name, defaultValue);
708 }
709
710 int
711 fgGetLong (const char * name, long defaultValue)
712 {
713   return globals->get_props()->getLongValue(name, defaultValue);
714 }
715
716 float
717 fgGetFloat (const char * name, float defaultValue)
718 {
719   return globals->get_props()->getFloatValue(name, defaultValue);
720 }
721
722 double
723 fgGetDouble (const char * name, double defaultValue)
724 {
725   return globals->get_props()->getDoubleValue(name, defaultValue);
726 }
727
728 const char *
729 fgGetString (const char * name, const char * defaultValue)
730 {
731   return globals->get_props()->getStringValue(name, defaultValue);
732 }
733
734 bool
735 fgSetBool (const char * name, bool val)
736 {
737   return globals->get_props()->setBoolValue(name, val);
738 }
739
740 bool
741 fgSetInt (const char * name, int val)
742 {
743   return globals->get_props()->setIntValue(name, val);
744 }
745
746 bool
747 fgSetLong (const char * name, long val)
748 {
749   return globals->get_props()->setLongValue(name, val);
750 }
751
752 bool
753 fgSetFloat (const char * name, float val)
754 {
755   return globals->get_props()->setFloatValue(name, val);
756 }
757
758 bool
759 fgSetDouble (const char * name, double val)
760 {
761   return globals->get_props()->setDoubleValue(name, val);
762 }
763
764 bool
765 fgSetString (const char * name, const char * val)
766 {
767   return globals->get_props()->setStringValue(name, val);
768 }
769
770 void
771 fgSetArchivable (const char * name, bool state)
772 {
773   SGPropertyNode * node = globals->get_props()->getNode(name);
774   if (node == 0)
775     SG_LOG(SG_GENERAL, SG_DEBUG,
776            "Attempt to set archive flag for non-existant property "
777            << name);
778   else
779     node->setAttribute(SGPropertyNode::ARCHIVE, state);
780 }
781
782 void
783 fgSetReadable (const char * name, bool state)
784 {
785   SGPropertyNode * node = globals->get_props()->getNode(name);
786   if (node == 0)
787     SG_LOG(SG_GENERAL, SG_DEBUG,
788            "Attempt to set read flag for non-existant property "
789            << name);
790   else
791     node->setAttribute(SGPropertyNode::READ, state);
792 }
793
794 void
795 fgSetWritable (const char * name, bool state)
796 {
797   SGPropertyNode * node = globals->get_props()->getNode(name);
798   if (node == 0)
799     SG_LOG(SG_GENERAL, SG_DEBUG,
800            "Attempt to set write flag for non-existant property "
801            << name);
802   else
803     node->setAttribute(SGPropertyNode::WRITE, state);
804 }
805
806 void
807 fgUntie(const char * name)
808 {
809   SGPropertyNode* node = globals->get_props()->getNode(name);
810   if (!node) {
811     SG_LOG(SG_GENERAL, SG_WARN, "fgUntie: unknown property " << name);
812     return;
813   }
814   
815   if (!node->isTied()) {
816     return;
817   }
818   
819   if (!node->untie()) {
820     SG_LOG(SG_GENERAL, SG_WARN, "Failed to untie property " << name);
821   }
822 }
823
824
825 // end of fg_props.cxx