--- To download a copy of this file, click here: ./location-in-air.xml ---

--- A patch against the recent CVS version may be here: ./location-in-air.xml.diff ---


<?xml version="1.0"?>

<!--
     For information on how to use this feature, see
     Docs/README.location-in-air 
-->

<PropertyList>
  <name>location-in-air</name>
  <layout>vbox</layout>
  <halign>left</halign>

  <nasal>
    <open>

      me = "/sim/gui/dialogs/location-in-air";
      magvar = getprop("/environment/magnetic-variation-deg");

      mynode = props.globals.getNode(me, 1);
      mode = {
        "airport-btn": mynode.getNode("airport-btn", 1),
        "lonlat-btn":  mynode.getNode("lonlat-btn", 1),
        "vor-btn":     mynode.getNode("vor-btn", 1),
        "ndb-btn":     mynode.getNode("ndb-btn", 1),
        "fix-btn":     mynode.getNode("fix-btn", 1),
        "ppos-btn":    mynode.getNode("ppos-btn", 1),
      };

# Function to push a radio button and clear all the others:
      set_radio = func(m) {
        foreach (k; keys(mode)) {
          mode[k].setBoolValue(m == k);
        }
      }

      initialized = 0;
      foreach (k; keys(mode)) {
        if (mode[k].getType() == "NONE") {
          mode[k].setBoolValue(0);
        }
        initialized += mode[k].getBoolValue();
      }
      if (!initialized) {
        set_radio("airport-btn");
      }

# We need our own private variables, to have and to hold,
# without worrying about whether the FDM will mess with
# them.  A subset of these will be copied to FDM variables 
# with similar names.  Which subset depends on which radio
# button is pushed.

      myvars = {"airport-id" : 0, "runway" : 0, 
            "latitude-deg" : 4, "longitude-deg" : 4,
            "vor-id" : 0, "ndb-id" : 0, "fix" : 0, 
	    "offset-distance" : 0, "course-deg-mag" : 0,
	    "altitude-ft" : 0, "glideslope-deg" : 0,
	    "airspeed-kt" : 0, "heading-deg-mag" : 0,
      };

# Inherit a few things from the fgfs command line:
      if (!getprop(me, "did1")) { 
	setprop(me, "did1", 1);		# inherit only once

	foreach (varname ; keys(myvars)) {
	  nnn = props.globals.getNode("/sim/presets/" ~ varname, 1);
	  ttt = nnn.getType();
	  value = nnn.getValue();
  # The reason for the following "if" is to make
  # sure we assign a /string/ (not a numeric) to
  # our copy of the variable.
  # The reason for that is so that (later) we can distinguish
  # between "" (which means take the default)
  # and zero (which means the user wants a value of zero).
	  if (ttt == "STRING") {
	    setprop(me, varname, nnn.getValue());
	  } elsif (value != nil) {	
	    # must be some kind of numeric
	    fmt = sprintf("%%1.%df", myvars[varname]);
	    setprop(me, varname, sprintf(fmt, nnn.getValue()));
	  } else {
	    setprop(me, varname, "");
	  }
	}

# Now fix up a few of the inherited values, 
# taking care of special cases:

# Relocating to surface doesn't make sense;
# we are supposed to be relocating aloft:
	alt = getprop("/sim/presets/altitude-ft");
	if (alt == -9999) {
	  alt = "";
	}  # else default to "inherited" reset-to-air altitude
	setprop(me, "altitude-ft", alt);

# Inherit preset airspeed from command line:
        ktas = getprop("/sim/presets/airspeed-kt");
	if (ktas == 0) {
	  ktas = "";
	}
	setprop(me, "airspeed-kt", ktas);	

# Probably never a good idea to inherit a heading;
# instead use current heading as the default.
	if (1) {
	  setprop(me, "heading-deg-mag", "");
	} else {
# Preset heading from command line was treated as
# /true/ heading;  we want the corresponding
# /magnetic/ heading:
	  hdg = getprop("/sim/presets/heading-deg");
	  setprop(me, "heading-deg-mag", sprintf("%d", hdg - magvar));
	}
	
      }  # end inheriting things

      setprop(me, "altitude-agl-ft",
	sprintf("%5.2f", getprop("/position/altitude-agl-ft")));

      copycontrol = func(vvv, factor){
	setprop(me, vvv,
	  sprintf("%5.2f", factor * getprop("/controls/flight", vvv)));
      }
      copycontrol("aileron", 1);
      copycontrol("aileron-trim", 1);
      copycontrol("elevator", -1);
      copycontrol("elevator-trim", -1);
      copycontrol("rudder", 1);
      copycontrol("rudder-trim", 1);

# The rule is, if it doesn't say "mag" in the name, it
# is a true bearing, not a magnetic bearing.
# Like the FDM, we do as much as possible with true 
# bearings internally.

# When displaying magnetic bearings to a user,
# convert them /immediately/ beforehand to magnetic
# by subtracting magvar.
# Similarly, when getting magnetic bearings from the user,
# convert them /immediately/ afterward to true
# by adding magvar.

      hdg1 = getprop("/orientation/heading-deg");
      setprop(me, "this-heading-deg-mag", sprintf("%d", hdg1 - magvar));
      setprop(me, "this-throttle", sprintf("%5.2f", 
      		getprop("/controls/engines/engine/throttle")));

      setprop(me, "this-latitude-deg",  
        sprintf("%1.4f", getprop("/position/latitude-deg")));
      setprop(me, "this-longitude-deg", 
        sprintf("%1.4f", getprop("/position/longitude-deg")));

    </open>

    <close>   # just kept for educational purposes :-)

# Note that I would like to use the degree /symbol/
# rather than spelling out "deg" in the dialog labels.
# The utf-8 for this symbol is (°).
# However, alas, putting that into the label produces
# no visible result.  Rumor suggests that we are using
# a font that doesn't have a glyph for this character.

    </close>
  </nasal>

  <text>
    <label>Location In Air</label>
  </text>

  <hrule><dummy/></hrule>

  <text>
    <label>Reference Point</label>
  </text>

  <group>
    <layout>table</layout>
    <halign>left</halign>
    <radio>
      <row>0</row><col>0</col>
      <live>true</live>
      <property>/sim/gui/dialogs/location-in-air/airport-btn</property>
      <binding>
        <command>dialog-apply</command>
      </binding>
      <binding>
        <command>nasal</command>
        <script>set_radio("airport-btn")</script>
      </binding>
    </radio>
    <text>
      <row>0</row><col>1</col>
      <halign>left</halign>
      <label>Airport:</label>
    </text>
    <input>
      <row>0</row><col>2</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/airport-id</property>
    </input>

    <text>
      <row>0</row><col>3</col>
      <label>Runway:</label>
    </text>
    <input>
      <row>0</row><col>4</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/runway</property>
    </input>

    <radio>
      <row>1</row><col>0</col>
      <live>true</live>
      <property>/sim/gui/dialogs/location-in-air/lonlat-btn</property>
      <binding>
        <command>dialog-apply</command>
      </binding>
      <binding>
        <command>nasal</command>
        <script>set_radio("lonlat-btn")</script>
      </binding>
    </radio>
    <text>
      <row>1</row><col>1</col>
      <halign>left</halign>
      <label>Latitude:</label>
    </text>
    <input>
      <row>1</row><col>2</col>
      <pref-width>130</pref-width>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/latitude-deg</property>
    </input>
    <text>
      <row>1</row><col>3</col>
      <halign>left</halign>
      <label>Longitude:</label>
    </text>
    <input>
      <row>1</row><col>4</col>
      <pref-width>130</pref-width>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/longitude-deg</property>
    </input>

    <radio>
      <row>2</row><col>0</col>
      <live>true</live>
      <property>/sim/gui/dialogs/location-in-air/vor-btn</property>
      <binding>
        <command>dialog-apply</command>
      </binding>
      <binding>
        <command>nasal</command>
        <script>set_radio("vor-btn")</script>
      </binding>
    </radio>
    <text>
      <row>2</row><col>1</col>
      <halign>left</halign>
      <label>VOR:</label>
    </text>
    <input>
      <row>2</row><col>2</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/vor-id</property>
    </input>

    <radio>
      <row>3</row><col>0</col>
      <live>true</live>
      <property>/sim/gui/dialogs/location-in-air/ndb-btn</property>
      <binding>
        <command>dialog-apply</command>
      </binding>
      <binding>
        <command>nasal</command>
        <script>set_radio("ndb-btn")</script>
      </binding>
    </radio>
    <text>
      <row>3</row><col>1</col>
      <halign>left</halign>
      <label>NDB:</label>
    </text>
    <input>
      <row>3</row><col>2</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/ndb-id</property>
    </input>
    <text>
      <row>3</row><col>3</col>
      <halign>left</halign>
      <label>(3-letter ID)</label>
    </text>

    <radio>
      <row>4</row><col>0</col>
      <live>true</live>
      <property>/sim/gui/dialogs/location-in-air/fix-btn</property>
      <binding>
        <command>dialog-apply</command>
      </binding>
      <binding>
        <command>nasal</command>
        <script>set_radio("fix-btn")</script>
      </binding>
    </radio>
    <text>
      <row>4</row><col>1</col>
      <halign>left</halign>
      <label>Waypoint:</label>
    </text>
    <input>
      <row>4</row><col>2</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/fix</property>
    </input>
    <text>
      <row>4</row><col>3</col>
      <halign>left</halign>
      <label>(5-character ID)</label>
    </text>

    <radio>
      <row>5</row><col>0</col>
      <live>true</live>
      <property>/sim/gui/dialogs/location-in-air/ppos-btn</property>
      <binding>
        <command>dialog-apply</command>
      </binding>
      <binding>
        <command>nasal</command>
        <script>set_radio("ppos-btn")</script>
      </binding>
    </radio>
    <text>
      <row>5</row><col>1</col>
      <halign>left</halign>
      <label>Here</label>
    </text>
    <text>
      <row>5</row><col>2</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/this-latitude-deg</property>
    </text>
    <text>
      <row>5</row><col>4</col>
      <halign>left</halign>
      <property>/sim/gui/dialogs/location-in-air/this-longitude-deg</property>
    </text>

  </group>

  <text>
    <label>Offset from Reference Point</label>
  </text>

  <group>
    <layout>table</layout>
    <text>
      <row>0</row><col>0</col>
      <halign>right</halign>
      <label>Distance:</label>
    </text>
    <input>
      <row>0</row><col>1</col>
      <property>/sim/gui/dialogs/location-in-air/offset-distance</property>
    </input>
    <text>
      <row>0</row><col>2</col>
      <halign>left</halign>
      <label>nm    </label>
    </text>

    <text>
      <row>0</row><col>3</col>
      <halign>right</halign>
      <label>Course:</label>
    </text>
    <input>
      <row>0</row><col>4</col>
      <property>/sim/gui/dialogs/location-in-air/course-deg-mag</property>
    </input>
    <text>
      <row>0</row><col>5</col>
      <halign>left</halign>
      <label>deg mag</label>
    </text>

    <text>
      <row>1</row><col>2</col>
      <halign>left</halign>
      <label>     </label>
    </text>

    <text>
      <row>2</row><col>0</col>
      <halign>right</halign>
      <label>Altitude:</label>
    </text>
    <input>
      <row>2</row><col>1</col>
      <property>/sim/gui/dialogs/location-in-air/altitude-ft</property>
    </input>
    <text>
      <row>2</row><col>2</col>
      <halign>left</halign>
      <label>ft MSL</label>
    </text>

    <text>
      <row>2</row><col>3</col>
      <halign>right</halign>
      <label>Glideslope:</label>
    </text>
    <input>
      <row>2</row><col>4</col>
      <property>/sim/gui/dialogs/location-in-air/glideslope-deg</property>
    </input>
    <text>
      <row>2</row><col>5</col>
      <halign>left</halign>
      <label>deg</label>
    </text>


    <text>
      <row>3</row><col>0</col>
      <halign>right</halign>
      <label>Airspeed</label>
    </text>
    <input>
      <row>3</row><col>1</col>
      <property>/sim/gui/dialogs/location-in-air/airspeed-kt</property>
    </input>
    <text>
      <row>3</row><col>2</col>
      <halign>left</halign>
      <label>kt</label>
    </text>


    <text>
      <row>3</row><col>3</col>
      <halign>right</halign>
      <label>Heading:</label>
    </text>
    <input>
      <row>3</row><col>4</col>
      <property>/sim/gui/dialogs/location-in-air/heading-deg-mag</property>
    </input>
    <text>
      <row>3</row><col>5</col>
      <halign>left</halign>
      <label>deg mag</label>
    </text>

  </group>

  <group>
      <layout>table</layout>
      <text>
	<row>0</row>	<!-- blank row -->
      </text>

      <text>
	<row>3</row><col>1</col>
	<halign>left</halign>
	<label>__pos__</label>
      </text>
      <text>
	<row>3</row><col>2</col>
	<halign>left</halign>
	<label>__trim__</label>
      </text>

      <text>
	<row>5</row><col>0</col>
	<label>Aileron:</label>
      </text>
      <text>
	<row>5</row><col>1</col>
	<halign>left</halign>
	<property>/sim/gui/dialogs/location-in-air/aileron</property>
      </text>
      <text>
	<row>5</row><col>2</col>
	<halign>left</halign>
	<property>/sim/gui/dialogs/location-in-air/aileron-trim</property>
      </text>
      <text>
	<row>5</row><col>3</col>
	<halign>left</halign>
	<label>+ = right</label>
      </text>


      <text>
	<row>6</row><col>0</col>
	<label>Elevator:</label>
      </text>
      <text>
	<row>6</row><col>1</col>
	<halign>left</halign>
	<property>/sim/gui/dialogs/location-in-air/elevator</property>
      </text>
      <text>
	<row>6</row><col>2</col>
	<halign>left</halign>
	<property>/sim/gui/dialogs/location-in-air/elevator-trim</property>
      </text>
      <text>
	<row>6</row><col>3</col>
	<halign>left</halign>
	<label>+ = up</label>
      </text>

      <text>
	<row>7</row><col>0</col>
	<label>Rudder:</label>
      </text>
      <text>
	<row>7</row><col>1</col>
	<halign>left</halign>
	<property>/sim/gui/dialogs/location-in-air/rudder</property>
      </text>
      <text>
	<row>7</row><col>2</col>
	<halign>left</halign>
	<property>/sim/gui/dialogs/location-in-air/rudder-trim</property>
      </text>


  </group>

  <group>
    <layout>hbox</layout>
    <default-padding>10</default-padding>

    <button>
      <legend>OK</legend>
      <default>true</default>
      <equal>true</equal>
      <binding>
        <command>dialog-apply</command>
      </binding>

      <binding>
        <command>nasal</command>
        <script>		# bindings [13]

	  #props.dump(props.globals.getNode("/sim/presets"));

##### First, clear out all the FDM's variables:

	  foreach (varname ; keys(myvars)) {
	    setprop("/sim/presets", varname, "");
	  }

# Special treatment for some variables; they
# are numberic, not strings, so setting them to "" 
# would be the same as zero, which would be in-range.
          setprop("/sim/presets/longitude-deg", -9999);
          setprop("/sim/presets/latitude-deg", -9999);
          setprop("/sim/presets/altitude-ft", -9999);

# Special treatment for variables that are settable,
# but not part of our dialog:
          setprop("/sim/presets/onground", 0);
          setprop("/sim/presets/trim", 0);
          setprop("/sim/presets/onground", 0);

#### Next, set the ones we care about

          fixup = func(varname){
	    setprop("/sim/presets", varname, getprop(me, varname));
	  }

          checkup = func(button, varname){
	    if (mode[button].getBoolValue()) {
	      fixup(varname);
	    }
	  }

	  checkup("airport-btn", "airport-id");
	  checkup("airport-btn", "runway");
	  checkup("lonlat-btn", "latitude-deg");
	  checkup("lonlat-btn", "longitude-deg");
	  checkup("vor-btn", "vor-id");
	  checkup("ndb-btn", "ndb-id");
	  checkup("fix-btn", "fix");
	  
# implement "here" button i.e. present position:

          if (mode["ppos-btn"].getBoolValue()) {
            setprop("/sim/presets/latitude-deg", 
	       getprop("/position/latitude-deg"));
            setprop("/sim/presets/longitude-deg", 
	       getprop("/position/longitude-deg"));
          }

######
# now for the popup items that are /not/ connected to radio buttons: 
# there should be six of them.

# I have no idea whether this does anything:
	  fixup("glideslope-deg");

# The distance from the reference point:
	  offset = getprop(me, "offset-distance");

# azimuth defaults to current heading
# otherwise convert from course == "degrees mag from"
# to azimuth == "degrees true to" to 
	  magvar = getprop("/environment/magnetic-variation-deg");
          az = getprop(me, "course-deg-mag");
	  if (az == "") {
            az = getprop("/orientation/heading-deg");
	  } else {
	    az += magvar;	# az now /true/ bearing
	  }
# Handle negative offsets;
# otherwise convert radial "from" to azimuth "to"
	  if (!offset){
	    offset = 0;
	  }
	  if (offset &lt; 0) {
	    offset = - offset;
	  } else {
	    az += 180;
	  }
# Prettify the variable, in case anybody dumps it:
	  while (az &gt; 360){
	    az -= 360;
	  }
	  setprop("/sim/presets/offset-azimuth", az);
	  setprop("/sim/presets/offset-distance", offset);

# altitude defaults to current altitude
          alt = getprop(me, "altitude-ft");
	  if (alt == "") {
	    alt = getprop("/position/altitude-ft");
	  }
	  setprop("/sim/presets/altitude-ft", alt);

# airspeed defaults to current airspeed
          ktas = getprop(me, "airspeed-kt");
	  if (ktas == "") {
	    ktas = getprop("/velocities/airspeed-kt");
	  }
	  setprop("/sim/presets/airspeed-kt", ktas);

# heading defaults to current heading
          hdg_mag = getprop(me, "heading-deg-mag");

	  if (hdg_mag == "") {
# current heading was computed in the "open" section above:
	    hdg_mag = getprop(me, "this-heading-deg-mag");
	  }
	  setprop("/sim/presets/heading-deg", num(hdg_mag) + magvar);
  	  setprop(me, "last-heading-deg-mag", hdg_mag);


# Save magneto, throttle, gear, trim, and view settings 
# from the ravages of presets-commit.

	  xxx = props.globals.getNode("/controls/engines");
	  engs = xxx.getChildren("engine");
	  magvec = [];
	  thrvec = [];
	  setsize(magvec, size(engs));
	  setsize(thrvec, size(engs));
	  for(ii=0; ii &lt; size(engs); ii=ii+1) {
	    magvec[ii] = engs[ii].getNode("magnetos", 1).getValue();
	    thrvec[ii] = engs[ii].getNode("throttle", 1).getValue();
	  }
	  atrim = getprop("/controls/flight/aileron-trim");
	  etrim = getprop("/controls/flight/elevator-trim");
	  rtrim = getprop("/controls/flight/rudder-trim");
	  gear_down = getprop("/controls/gear/gear-down");

	  view_field = getprop("/sim/current-view/field-of-view");
	  view_hdg = getprop("/sim/current-view/goal-heading-offset-deg");
	  view_pitch = getprop("/sim/current-view/goal-pitch-offset-deg");

          #props.dump(props.globals.getNode("/sim/presets"));

        </script>
      </binding>

      <binding>
        <!-- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -->
        <!-- This is a nasty thing;  lots of brutal side-effects: -->
        <command>presets-commit</command>
        <!-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -->
      </binding>

      <binding>
        <command>nasal</command>
        <script>		<!-- binding [15] -->

# Restore magneto, throttle, view, and trim settings 
# after the ravages of presets-commit.

	  lim = size(magvec);		# same as size of thrvec

	  xxx = props.globals.getNode("/controls/engines");
	  engs = xxx.getChildren("engine");

	  for(ii=0; ii &lt; lim; ii=ii+1) {
	    thismag = magvec[ii];
	    engs[ii].getNode("magnetos", 1).setValue(thismag);
# Throttle setting doesn't work in JSBSim
# (but works OK in larcsim):
	    engs[ii].getNode("throttle", 1).setValue(thrvec[ii]);
          }
	  
	  setprop("/controls/flight/aileron-trim", atrim);
	  setprop("/controls/flight/elevator-trim", etrim);
	  setprop("/controls/flight/rudder-trim", rtrim);
	  setprop("/controls/gear/gear-down", gear_down);

	  setprop("/sim/current-view/field-of-view", view_field);
	  setprop("/sim/current-view/goal-heading-offset-deg", view_hdg);
	  setprop("/sim/current-view/goal-pitch-offset-deg", view_pitch);

        </script>
      </binding>

      <binding>
        <command>dialog-close</command>
      </binding>
    </button>

    <button>
      <legend>Cancel</legend>
      <equal>true</equal>
      <key>Esc</key>
      <binding>
        <command>dialog-close</command>
      </binding>
    </button>
  </group>
</PropertyList>