1
0
Fork 0

Merge branch 'master' into durk-atc

This commit is contained in:
Durk Talsma 2011-07-24 08:59:08 +02:00
commit c9ea2bf2aa
152 changed files with 95939 additions and 71785 deletions

View file

@ -3,7 +3,7 @@
## Nasal for dual control of a KR-87 ADF radio over the multiplayer
## network.
##
## Copyright (C) 2007 - 2010 Anders Gidenstam (anders(at)gidenstam.org)
## Copyright (C) 2007 - 2011 Anders Gidenstam (anders(at)gidenstam.org)
## This file is licensed under the GPL license version 2 or later.
##
###############################################################################
@ -116,21 +116,21 @@ var kr87 = [master_kr87.new(0), master_kr87.new(1)];
###########################################################################
# n - ADF#
swap = func(n) {
var swap = func(n) {
kr87[n].swap();
}
###########################################################################
# n - ADF#
# d - adjustment
adjust_frequency = func(n, d) {
var adjust_frequency = func(n, d) {
kr87[n].adjust_frequency(d);
}
###########################################################################
# n - ADF#
# p - pressed
toggle_BFO = func(n) {
var toggle_BFO = func(n) {
kr87[n].toggle_BFO();
}

View file

@ -3,7 +3,7 @@
## Nasal for dual control of a KX165 NavComm radio over the multiplayer
## network.
##
## Copyright (C) 2007 - 2010 Anders Gidenstam (anders(at)gidenstam.org)
## Copyright (C) 2007 - 2011 Anders Gidenstam (anders(at)gidenstam.org)
## This file is licensed under the GPL license version 2 or later.
##
###############################################################################
@ -150,13 +150,13 @@ var make_slave_to = func(n, airoot) {
###########################################################################
# n - NavComm#
swap_nav = func(n) {
var swap_nav = func(n) {
kx165tso[n].swap_nav();
}
###########################################################################
# n - NavComm#
swap_comm = func(n, b) {
var swap_comm = func(n, b) {
kx165tso[n].comm_base.getNode(swap_btn, 1).setValue(b);
if (b) kx165tso[n].swap_comm();
}
@ -164,14 +164,14 @@ swap_comm = func(n, b) {
###########################################################################
# n - NavComm#
# d - adjustment
adjust_nav_frequency = func(n, d) {
var adjust_nav_frequency = func(n, d) {
kx165tso[n].adjust_nav_frequency(d);
}
###########################################################################
# n - NavComm#
# d - adjustment
adjust_comm_frequency = func(n, d) {
var adjust_comm_frequency = func(n, d) {
kx165tso[n].adjust_comm_frequency(d);
}

View file

@ -254,7 +254,7 @@ var makePolylinePath = func (points, width, round_ends = 0) {
if (size(points) < 2) return nil;
var ret = LinePlane.new(points[0], points[1], width);
if (round_ends) {
ret = UnionConstraint.new(line,
ret = UnionConstraint.new(ret,
CircularXYSurface.new(points[0], width/2));
}
for (var i = 2; i < size(points); i += 1) {

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1,693 @@
<?xml version="1.0"?>
<PropertyList>
<path>BC-602-A.ac</path>
<animation>
<type>range</type>
<min-m>0</min-m>
<max-property>/sim/rendering/static-lod/detailed</max-property>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-OFF</object-name>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>rotate</type>
<object-name>SCR-522C-T-base</object-name>
<object-name>SCR-522C-T-handle</object-name>
<object-name>SCR-522C-T-stem</object-name>
<property>/instrumentation/comm/SCR-522C/tr</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>37.5</dep>
</entry>
<entry>
<ind>2</ind>
<dep>75</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>-0.01</y-m>
<z-m>0.06</z-m>
</center>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-REM</object-name>
<visible>false</visible>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/tr</property>
<value type="int">0</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-R</object-name>
<visible>false</visible>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/tr</property>
<value type="int">1</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-T</object-name>
<visible>false</visible>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/tr</property>
<value type="int">2</value>
</binding>
<mod-up>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/tr</property>
<value type="int">1</value>
</binding>
</mod-up>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-A</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>1</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-B</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>2</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-C</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>3</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-D</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>4</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-OFF</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>0</value>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-A</object-name>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-B</object-name>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-C</object-name>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-D</object-name>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>-0.005</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-A.d</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>1</value>
</equals>
<property>/instrumentation/comm/SCR-522C/mask</property>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-A.b</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>1</value>
</equals>
<not>
<property>/instrumentation/comm/SCR-522C/mask</property>
</not>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-B.d</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>2</value>
</equals>
<property>/instrumentation/comm/SCR-522C/mask</property>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-B.b</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>2</value>
</equals>
<not>
<property>/instrumentation/comm/SCR-522C/mask</property>
</not>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-C.d</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>3</value>
</equals>
<property>/instrumentation/comm/SCR-522C/mask</property>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-C.b</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>3</value>
</equals>
<not>
<property>/instrumentation/comm/SCR-522C/mask</property>
</not>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-D.d</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>4</value>
</equals>
<property>/instrumentation/comm/SCR-522C/mask</property>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-D.b</object-name>
<condition>
<and>
<equals>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<value>4</value>
</equals>
<not>
<property>/instrumentation/comm/SCR-522C/mask</property>
</not>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-T.d</object-name>
<condition>
<and>
<not>
<property>/instrumentation/comm/ptt</property>
</not>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<property>/instrumentation/comm/SCR-522C/mask</property>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-T.b</object-name>
<condition>
<and>
<not>
<property>/instrumentation/comm/ptt</property>
</not>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<not>
<property>/instrumentation/comm/SCR-522C/mask</property>
</not>
</and>
</condition>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522C-mask-1</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-cycle</command>
<property>/instrumentation/comm/SCR-522C/mask</property>
<value type="bool">true</value>
<value type="bool">false</value>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-mask-1</object-name>
<property>/instrumentation/comm/SCR-522C/mask</property>
<interpolation>
<entry>
<ind>1</ind>
<dep>0.007</dep>
</entry>
<entry>
<ind>0</ind>
<dep>0.0</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>pick</type>
<object-name>SCR-522-T-Lock</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-cycle</command>
<property>/instrumentation/comm/SCR-522C/tr-lock</property>
<value type="bool">true</value>
<value type="bool">false</value>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522-T-Lock</object-name>
<property>/instrumentation/comm/SCR-522C/tr-lock</property>
<interpolation>
<entry>
<ind>1</ind>
<dep>0.007</dep>
</entry>
<entry>
<ind>0</ind>
<dep>0.0</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-top</object-name>
<object-name>SCR-522C-box</object-name>
<object-name>a-covers</object-name>
<object-name>SCR-522C-A</object-name>
<object-name>SCR-522C-B</object-name>
<object-name>SCR-522C-C</object-name>
<object-name>SCR-522C-D</object-name>
<object-name>SCR-522C-T-base</object-name>
<object-name>SCR-522C-T-handle</object-name>
<object-name>SCR-522C-T-stem</object-name>
<object-name>SCR-522C-light-cont-2</object-name>
<object-name>SCR-522C-light-cont-1</object-name>
<object-name>SCR-522C-OFF</object-name>
<object-name>rivet-1</object-name>
<object-name>rivet-2</object-name>
<object-name>rivet-3</object-name>
<object-name>rivet-4</object-name>
<object-name>rivet-5</object-name>
<object-name>rivet-6</object-name>
<object-name>rivet-7</object-name>
<object-name>rivet-8</object-name>
<object-name>rivet-9</object-name>
<object-name>rivet-10</object-name>
<object-name>rivet-11</object-name>
<object-name>rivet-12</object-name>
<object-name>rivet-13</object-name>
<object-name>rivet-14</object-name>
<object-name>rivet-15</object-name>
<object-name>rivet-16</object-name>
<object-name>red-placard.001</object-name>
<object-name>red-placard.002</object-name>
<object-name>black-placard.001</object-name>
<object-name>black-placard.002</object-name>
<object-name>right-outer-bracket</object-name>
<object-name>left-inner-bracket</object-name>
<object-name>left-outer-bracket</object-name>
<object-name>baseScrew1</object-name>
<object-name>baseScrew2</object-name>
<object-name>baseScrew3</object-name>
<object-name>baseScrew4</object-name>
<object-name>bigConnector</object-name>
<object-name>bigConnectorBase</object-name>
<object-name>bigConnectorScrew1</object-name>
<object-name>bigConnectorScrew2</object-name>
<object-name>bigConnectorScrew3</object-name>
<object-name>bigConnectorScrew4</object-name>
<object-name>smallConnector</object-name>
<object-name>smallConnectorBase</object-name>
<object-name>smallConnectorScrw1</object-name>
<object-name>smallConnectorScrw2</object-name>
<object-name>smallConnectorScrw3</object-name>
<object-name>smallConnectorScrw4</object-name>
<object-name>uniluminatedMaskTR</object-name>
<object-name>uniluminatedMask</object-name>
<emission>
<factor-prop>controls/lighting/cabin-norm</factor-prop>
<red>0.28</red>
<green>0.18</green>
<blue>0.18</blue>
</emission>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-lamp-A.b</object-name>
<object-name>SCR-522C-lamp-B.b</object-name>
<object-name>SCR-522C-lamp-C.b</object-name>
<object-name>SCR-522C-lamp-D.b</object-name>
<emission>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<red>0.3</red>
<green>0.3</green>
<blue>1.0</blue>
</emission>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-lamp-A.d</object-name>
<object-name>SCR-522C-lamp-B.d</object-name>
<object-name>SCR-522C-lamp-C.d</object-name>
<object-name>SCR-522C-lamp-D.d</object-name>
<emission>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<red>0.01</red>
<green>0.01</green>
<blue>0.25</blue>
</emission>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-lamp-T.b</object-name>
<emission>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<red>1.0</red>
<green>1.0</green>
<blue>1.0</blue>
</emission>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-lamp-T.d</object-name>
<emission>
<property>/instrumentation/comm/SCR-522C/frequencies/channel-selected</property>
<red>0.07</red>
<green>0.07</green>
<blue>0.07</blue>
</emission>
</animation>
</PropertyList>

View file

@ -0,0 +1,231 @@
<?xml version="1.0"?>
<PropertyList>
<name>SCR-522C-radio</name>
<width>550</width>
<modal>false</modal>
<layout>vbox</layout>
<group>
<layout>hbox</layout>
<empty>
<stretch>1</stretch>
</empty>
<text>
<label>Radio Frequencies SCR-522C (US) or TR1133 (UK)</label>
</text>
<empty>
<stretch>1</stretch>
</empty>
<button>
<pref-width>16</pref-width>
<pref-height>16</pref-height>
<legend></legend>
<keynum>27</keynum>
<border>2</border>
<binding>
<command>dialog-close</command>
</binding>
</button>
</group>
<hrule/>
<group>
<layout>vbox</layout>
<text>
<label>Selected</label>
</text>
<group>
<layout>hbox</layout>
<text>
<label>Channel</label>
</text>
<text>
<label>Frequency</label>
</text>
</group>
<group>
<layout>hbox</layout>
<text>
<!--<label>xxxxxxx</label>-->
<label>Channel</label>
<live>true</live>
<!--<format>%.2f</format>-->
<property>/instrumentation/comm/SCR-522C/frequencies/channel</property>
</text>
<text>
<!--<label>xxxxxxx</label>-->
<label>MHz</label>
<live>true</live>
<format>%.2f</format>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
</text>
</group>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<text>
<label>Channel Assignment</label>
</text>
</group>
<group>
<layout>table</layout>
<!-- headers -->
<!--<text>
<row>0</row>
<col>4</col>
<label>Standby</label>
</text>-->
<!-- Dummy label to stretch table, as layout manager doesn't handle labels well -->
<text>
<row>0</row>
<col>6</col>
<label></label>
</text>
<text>
<row>1</row>
<col>0</col>
<halign>right</halign>
<label>A</label>
</text>
<input>
<name>channel A</name>
<row>1</row>
<col>1</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/A-mhz</property>
</input>
<text>
<row>1</row>
<col>3</col>
<halign>right</halign>
<label>B</label>
</text>
<input>
<name>channel B</name>
<row>1</row>
<col>4</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/B-mhz</property>
</input>
<text>
<row>2</row>
<col>0</col>
<halign>right</halign>
<label>C</label>
</text>
<input>
<name>channel C</name>
<row>2</row>
<col>1</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/C-mhz</property>
</input>
<text>
<row>2</row>
<col>3</col>
<halign>right</halign>
<label>D</label>
</text>
<input>
<name>channel D</name>
<row>2</row>
<col>4</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/D-mhz</property>
</input>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<button>
<legend>ATC Services in range</legend>
<binding>
<command>ATC-freq-search</command>
</binding>
</button>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<default-padding>6</default-padding>
<empty>
<stretch>true</stretch>
</empty>
<button>
<legend>OK</legend>
<default>true</default>
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>dialog-close</command>
</binding>
</button>
<button>
<legend>Apply</legend>
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
</button>
<button>
<legend>Reset</legend>
<equal>true</equal>
<binding>
<command>dialog-update</command>
</binding>
</button>
<button>
<legend>Cancel</legend>
<equal>true</equal>
<key>Esc</key>
<binding>
<command>dialog-close</command>
</binding>
</button>
<empty>
<stretch>true</stretch>
</empty>
</group>
</PropertyList>

View file

@ -0,0 +1,163 @@
#####################################################################################
# This script provides the gui to set up the TR1133/SCR-522C radio #
# using a BC-602-A control box #
# #
# Author Vivian Meazza June 2011 #
# #
# mods Hal V. Engel June 2011 #
# #
#####################################################################################
# ================================ Initalize ======================================
# Make sure all needed properties are present and accounted
# for, and that they have sane default values.
var channelA_node = props.globals.initNode("instrumentation/comm/channels/A-mhz", 0, "DOUBLE");
var channelB_node = props.globals.initNode("instrumentation/comm/channels/B-mhz", 0, "DOUBLE");
var channelC_node = props.globals.initNode("instrumentation/comm/channels/C-mhz", 0, "DOUBLE");
var channelD_node = props.globals.initNode("instrumentation/comm/channels/D-mhz", 0, "DOUBLE");
var channel_selected_node = props.globals.initNode("instrumentation/comm/SCR-522C/frequencies/channel-selected", 0, "INT");
var tr_node = props.globals.initNode("instrumentation/comm/SCR-522C/tr", 0, "INT");
props.globals.initNode("instrumentation/comm/SCR-522C/frequencies/channel", "", "STRING");
props.globals.initNode("instrumentation/comm/SCR-522C/mask", 0, "BOOL");
props.globals.initNode("instrumentation/comm/SCR-522C/tr-lock", 0, "BOOL");
props.globals.initNode("instrumentation/comm/SCR-522C/remote-pushed", 0, "BOOL");
# turn the radio off
props.globals.initNode("instrumentation/comm/serviceable", 0, "BOOL");
var comm_selected_node = props.globals.getNode("instrumentation/comm/frequencies/selected-mhz", 1);
var comm_standby_node = props.globals.getNode("instrumentation/comm/frequencies/standby-mhz", 1);
var comm1_selected_node = props.globals.getNode("instrumentation/comm[1]/frequencies/selected-mhz", 1);
var comm1_standby_node = props.globals.getNode("instrumentation/comm[1]/frequencies/standby-mhz", 1);
var channel = ["OFF","A","B","C","D"];
setprop("instrumentation/comm/SCR-522C/frequencies/channel", channel[channel_selected_node.getValue()]);
# override default Equipment --> radio menu item
# Radio needs to be global in scope since it needs to presist for this to work
var Radio = gui.Dialog.new("sim/gui/dialogs/SCR-522C/dialog",
"Aircraft/Instruments-3d/SCR-522C/Dialogs/radios.xml");
gui.menuBind("radio", "SCR_522C.Radio.open()");
# override controls.ptt. This implements a REMote ptt switch.
controls.ptt = func {
# T/R/REM set to REM remote ptt switch controls transmitter
# print("intercept ptt for BC-602-A");
if (getprop("instrumentation/comm/SCR-522C/tr") == 0 and # in REMote ptt switch mode
getprop("instrumentation/comm/SCR-522C/frequencies/channel-selected") > 0) # and radio is on
setprop("instrumentation/comm/ptt", arg[0]); # let remote ptt control transmitter
else # otherwise
setprop("instrumentation/comm/ptt", 0); # the remote ptt does nothing
setprop("instrumentation/comm/SCR-522C/remote-pushed", arg[0]); # use to animate remote ptt button
}
# =============================== listeners ===============================
#
# listener for channel selector. Will cause the frequency of the transceiver to be changed.
# will also turn the radio on and off
var listenChannelSelected = func(n) {
var channel_no = n.getValue();
if (channel_no == nil) channel_no = 0;
# print("channel", channel_no, " ", channel[channel_no]);
setprop("instrumentation/comm/SCR-522C/frequencies/channel", channel[channel_no]);
if (channel_no == 0)
setprop("instrumentation/comm/serviceable", 0);
else {
setprop("instrumentation/comm/serviceable", 1);
if (channel_no == 1) {
setprop("instrumentation/comm/frequencies/selected-mhz", getprop("instrumentation/comm/channels/A-mhz"));
setprop("instrumentation/comm/frequencies/standby-mhz", getprop("instrumentation/comm/channels/A-mhz"));
}
else if (channel_no == 2){
setprop("instrumentation/comm/frequencies/selected-mhz", getprop("instrumentation/comm/channels/B-mhz"));
setprop("instrumentation/comm/frequencies/standby-mhz", getprop("instrumentation/comm/channels/B-mhz"));
}
else if (channel_no == 3){
setprop("instrumentation/comm/frequencies/selected-mhz", getprop("instrumentation/comm/channels/C-mhz"));
setprop("instrumentation/comm/frequencies/standby-mhz", getprop("instrumentation/comm/channels/C-mhz"));
}
else if (channel_no == 4){
setprop("instrumentation/comm/frequencies/selected-mhz", getprop("instrumentation/comm/channels/D-mhz"));
setprop("instrumentation/comm/frequencies/standby-mhz", getprop("instrumentation/comm/channels/D-mhz"));
}
}
}
# listener for the local TR switch.
var listenTr = func(t) {
var tr = t.getValue();
# print("tr ",tr);
if (tr == nil) tr = 0;
if (tr == 2 and getprop("instrumentation/comm/SCR-522C/frequencies/channel-selected") > 0)
setprop("instrumentation/comm/ptt", 1);
else if (tr == 1)
setprop("instrumentation/comm/ptt", 0);
if (tr == 0 and getprop("/instrumentation/comm/SCR-522C/tr-lock"))
setprop("/instrumentation/comm/SCR-522C/tr", 1);
}
# listener for the local TR lock.
var listenTrLock = func(i) {
var tr_lock = i.getValue();
# print("tr_lock");
if (tr_lock == nil) tr_lock = false;
if (tr_lock and getprop("/instrumentation/comm/SCR-522C/tr") == 0)
setprop("/instrumentation/comm/SCR-522C/tr", 1);
# else if (getprop("/instrumentation/comm/SCR-522C/tr") == 2)
# setprop("/instrumentation/comm/SCR-522C/tr", 1);
}
var SCR_522C_init = func(){
print ("initializing SCR-522C ...");
var channelA_init = comm_selected_node.getValue();
var channelB_init = comm_standby_node.getValue();
var channelC_init = comm1_selected_node.getValue();
var channelD_init = comm1_standby_node.getValue();
channelA_node.setValue(channelA_init);
channelB_node.setValue(channelB_init);
channelC_node.setValue(channelC_init);
channelD_node.setValue(channelD_init);
comm_selected_node.setValue(0);
comm_standby_node.setValue(0);
comm1_selected_node.setValue(0);
comm1_standby_node.setValue(0);
# over ride F12
setprop("input/keyboard/key[268]/binding/dialog-name", "SCR-522C-radio");
setprop("input/keyboard/key[268]/binding/command", "dialog-show");
# =============================== start listeners ===============================
#
setlistener("instrumentation/comm/SCR-522C/frequencies/channel-selected", listenChannelSelected, 1, 0);
setlistener("instrumentation/comm/SCR-522C/tr", listenTr, 1, 0);
setlistener("instrumentation/comm/SCR-522C/tr-lock", listenTrLock, 1, 0);
# print("... done");
} # end func initialize
# run initialization
setlistener("sim/signals/fdm-initialized", SCR_522C_init);

View file

@ -0,0 +1,55 @@
To enable full functionality of the SCR-522C/TR1133 radio there are several things
that need to be done.
First change the <nasal> section of your *set.xml file so that it looks like the
following.
<nasal>
<!-- your other nasal files -->
<!-- the following three lines must be exactly like this -->
<SCR_522C>
<file>Aircraft/Instruments-3d/SCR-522C/Nasal/SCR-522C.nas</file>
</SCR_522C>
</nasal>
Menu:
The above will enable the F12 key to bring up the SCR-522C specific frequencies dialog. It
will also over ride the Equipment --> radios menu item so that it brings up the correct
menu for the SCR-522C/TR1133 radios. This also implements other radio features like support
for a remote TR switch, the TR switch on the BC-602-A control box, TR switch lock, channel
switching, power switch and so on.
Animation:
For dark conditions when cabin/cockpit illumination is used all of the radio models objects
are illuminated in response to
<property>/controls/lighting/cabin-norm</property>
If you are using a different property for your cabin/cockpit lights you will need to map your
lighting property to /controls/lighting/cabin-norm.
If your aircraft needs remote ptt button animation this should be linked to
instrumentation/comm/SCR-522C/remote-pushed
Since the Nasal script reimplements controls.ptt() and instrumentation/comm/ptt only becomes true
if the radio is turned on. Using the property above to control the animation of the remote ptt
button will result in the botton moving when ever the user pushes what ever key/button they have
setup to call controls.ptt(). An example of what this might look like is:
<animation>
<type>translate</type>
<object-name>MicButton</object-name>
<property>instrumentation/comm/SCR-522C/remote-pushed</property>
<factor>-0.007</factor>
<axis>
<x>0</x>
<y>1</y>
<z>0</z>
</axis>
</animation>

File diff suppressed because it is too large Load diff

View file

@ -1,356 +0,0 @@
<?xml version="1.0"?>
<PropertyList>
<path>SCR-522C.ac</path>
<animation>
<type>range</type>
<min-m>0</min-m>
<max-m>7</max-m>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-OFF</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>rotate</type>
<object-name>SCR-522C-T-base</object-name>
<object-name>SCR-522C-T-sw-handle</object-name>
<object-name>SCR-522C-T-stem</object-name>
<property>/systems/comm/SCR-522C/tr</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>30</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>-0.01</y-m>
<z-m>0.05</z-m>
</center>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-A</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-B</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-C</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-D</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>-0.005</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-A</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>1</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-B</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>2</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-C</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>3</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-D</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>4</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-T</object-name>
<condition>
<and>
<property>/systems/comm/SCR-522C/tr</property>
<greater-than>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>0</value>
</greater-than>
</and>
</condition>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-light-cont-1</object-name>
<property>/systems/comm/SCR-522C/channel-dimmer</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0.005</dep>
</entry>
<entry>
<ind>1.0</ind>
<dep>0</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-light-cont-2</object-name>
<property>/systems/comm/SCR-522C/tr-dimmer</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0.005</dep>
</entry>
<entry>
<ind>1.0</ind>
<dep>0</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-top</object-name>
<object-name>SCR-522C-box</object-name>
<object-name>SCR-522C-A</object-name>
<object-name>SCR-522C-B</object-name>
<object-name>SCR-522C-C</object-name>
<object-name>SCR-522C-D</object-name>
<object-name>SCR-522C-T-base</object-name>
<object-name>SCR-522C-T-sw-handle</object-name>
<object-name>SCR-522C-T-stem</object-name>
<object-name>SCR-522C-light-cont-2</object-name>
<object-name>SCR-522C-light-cont-1</object-name>
<object-name>SCR-522C-OFF</object-name>
<object-name>right-outer-bracket</object-name>
<object-name>left-inner-bracket</object-name>
<object-name>left-outer-bracket</object-name>
<emission>
<factor-prop>controls/lighting/cabin-norm</factor-prop>
<red>0.28</red>
<green>0.18</green>
<blue>0.18</blue>
</emission>
</animation>
</PropertyList>

View file

@ -0,0 +1,232 @@
<?xml version="1.0"?>
<PropertyList>
<name>radios</name>
<width>550</width>
<modal>false</modal>
<layout>vbox</layout>
<group>
<layout>hbox</layout>
<empty>
<stretch>1</stretch>
</empty>
<text>
<label>Radio Frequencies</label>
</text>
<empty>
<stretch>1</stretch>
</empty>
<button>
<pref-width>16</pref-width>
<pref-height>16</pref-height>
<legend></legend>
<keynum>27</keynum>
<border>2</border>
<binding>
<command>dialog-close</command>
</binding>
</button>
</group>
<hrule/>
<group>
<layout>vbox</layout>
<text>
<label>Selected</label>
</text>
<group>
<layout>hbox</layout>
<text>
<label>Channel</label>
</text>
<text>
<label>Frequency</label>
</text>
</group>
<group>
<layout>hbox</layout>
<text>
<!--<label>xxxxxxx</label>-->
<label>Channel</label>
<live>true</live>
<!--<format>%.2f</format>-->
<property>systems/comm/SCR-522C/frequencies/channel</property>
</text>
<text>
<!--<label>xxxxxxx</label>-->
<label>MHz</label>
<live>true</live>
<format>%.2f</format>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
</text>
</group>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<text>
<label>Channel Assignment</label>
</text>
</group>
<group>
<layout>table</layout>
<!-- headers -->
<!--<text>
<row>0</row>
<col>4</col>
<label>Standby</label>
</text>-->
<!-- Dummy label to stretch table, as layout manager doesn't handle labels well -->
<text>
<row>0</row>
<col>6</col>
<label></label>
</text>
<text>
<row>1</row>
<col>0</col>
<halign>right</halign>
<label>A</label>
</text>
<input>
<name>channel A</name>
<row>1</row>
<col>1</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/A-mhz</property>
</input>
<text>
<row>1</row>
<col>3</col>
<halign>right</halign>
<label>B</label>
</text>
<input>
<name>channel B</name>
<row>1</row>
<col>4</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/B-mhz</property>
</input>
<text>
<row>2</row>
<col>0</col>
<halign>right</halign>
<label>C</label>
</text>
<input>
<name>channel C</name>
<row>2</row>
<col>1</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/C-mhz</property>
</input>
<text>
<row>2</row>
<col>3</col>
<halign>right</halign>
<label>D</label>
</text>
<input>
<name>channel D</name>
<row>2</row>
<col>4</col>
<width>75</width>
<height>25</height>
<label>MHz</label>
<property>/instrumentation/comm/channels/D-mhz</property>
</input>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<button>
<legend>ATC Services in range</legend>
<binding>
<command>ATC-freq-search</command>
</binding>
</button>
</group>
<hrule/>
<group>
<layout>hbox</layout>
<default-padding>6</default-padding>
<empty>
<stretch>true</stretch>
</empty>
<button>
<legend>OK</legend>
<default>true</default>
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
<binding>
<command>dialog-close</command>
</binding>
</button>
<button>
<legend>Apply</legend>
<equal>true</equal>
<binding>
<command>dialog-apply</command>
</binding>
</button>
<button>
<legend>Reset</legend>
<equal>true</equal>
<binding>
<command>dialog-update</command>
</binding>
</button>
<button>
<legend>Cancel</legend>
<equal>true</equal>
<key>Esc</key>
<binding>
<command>dialog-close</command>
</binding>
</button>
<empty>
<stretch>true</stretch>
</empty>
</group>
</PropertyList>

View file

@ -0,0 +1,119 @@
#####################################################################################
# This script provides the gui to set up the TR1133 radio #
# #
# Author Vivian Meazza June 2011 #
#####################################################################################
# ================================ Initalize ======================================
# Make sure all needed properties are present and accounted
# for, and that they have sane default values.
var channelA_node = props.globals.initNode("instrumentation/comm/channels/A-mhz", 0, "DOUBLE");
var channelB_node = props.globals.initNode("instrumentation/comm/channels/B-mhz", 0, "DOUBLE");
var channelC_node = props.globals.initNode("instrumentation/comm/channels/C-mhz", 0, "DOUBLE");
var channelD_node = props.globals.initNode("instrumentation/comm/channels/D-mhz", 0, "DOUBLE");
var channel_selected_node = props.globals.initNode("systems/comm/SCR-522C/frequencies/channel-selected", 0, "INT");
var tr_node = props.globals.initNode("systems/comm/SCR-522C/tr", 1, "INT");
props.globals.initNode("systems/comm/SCR-522C/frequencies/channel", "", "STRING");
props.globals.initNode("systems/comm/SCR-522C/channel-dimmer", 0, "BOOL");
props.globals.initNode("systems/comm/SCR-522C/tr-lock", 1, "BOOL");
var comm_selected_node = props.globals.getNode("instrumentation/comm/frequencies/selected-mhz", 1);
var comm_standby_node = props.globals.getNode("instrumentation/comm/frequencies/standby-mhz", 1);
var comm1_selected_node = props.globals.getNode("instrumentation/comm[1]/frequencies/selected-mhz", 1);
var comm1_standby_node = props.globals.getNode("instrumentation/comm[1]/frequencies/standby-mhz", 1);
var radio_dlg = gui.Dialog.new("dialog","Aircraft/Instruments-3d/TR1133/Dialogs/radios.xml");
var channel = ["OFF","A","B","C","D"];
#getprop("systems/comm/SCR-522C/frequencies/channel", 1);
setprop("systems/comm/SCR-522C/frequencies/channel", channel[channel_selected_node.getValue()]);
var TR1133_init = func(){
print ("initializing TR1133 ...");
var channelA_init = comm_selected_node.getValue();
var channelB_init = comm_standby_node.getValue();
var channelC_init = comm1_selected_node.getValue();
var channelD_init = comm1_standby_node.getValue();
channelA_node.setValue(channelA_init);
channelB_node.setValue(channelB_init);
channelC_node.setValue(channelC_init);
channelD_node.setValue(channelD_init);
comm_selected_node.setValue(0);
comm_standby_node.setValue(0);
comm1_selected_node.setValue(0);
comm1_standby_node.setValue(0);
# override F12
setprop("input/keyboard/key[268]/binding/command", "nasal");
setprop("input/keyboard/key[268]/binding/script", "TR1133.radio_dlg.open()");
# Disable the menu item "Equipment > radio" so we use our own gui: " > Radio".
print("Disabling Menu: Equipment -> Radios GUI using TR1133 -> Radio");
gui.menuBind("radio", "TR1133.radio_dlg.open()");
# =============================== listeners ===============================
#
setlistener("systems/comm/SCR-522C/frequencies/channel-selected", func(n) {
var channel_no = n.getValue();
if (channel_no == nil) channel_no = 0;
# print("channel", channel_no, " ", channel[channel_no]);
setprop("systems/comm/SCR-522C/frequencies/channel", channel[channel_no]);
},
1,
0); #end listener
setlistener("systems/comm/SCR-522C/tr", func(t) {
var tr = t.getValue();
# print("tr ",tr);
if (tr == nil) tr = 1;
if (tr == 0)
setprop("instrumentation/comm/ptt", 1);
else
setprop("instrumentation/comm/ptt", 0);
},
1,
0); # end listener
print("... done");
}#end func initialize
setlistener("sim/signals/fdm-initialized", TR1133_init);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,588 @@
<?xml version="1.0"?>
<!-- The TR1133 was the progenitor of the SCR-522C. This is a mild rework of the
generic SCR-522C -->
<PropertyList>
<path>TR1133.ac</path>
<animation>
<type>range</type>
<min-m>0</min-m>
<max-m>25</max-m>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522C-OFF</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>0</value>
</binding>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
<value>0</value>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-OFF</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>rotate</type>
<object-name>SCR-522C-T-base</object-name>
<object-name>SCR-522C-T-sw-handle</object-name>
<object-name>SCR-522C-T-stem</object-name>
<property>/systems/comm/SCR-522C/tr</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>15</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>-15</dep>
</entry>
</interpolation>
<center>
<x-m>-0.03644</x-m>
<y-m>-0.0080818</y-m>
<z-m> 0.0495798</z-m>
</center>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522C-A</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>1</value>
</binding>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
<property>/instrumentation/comm/channels/A-mhz</property>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-A</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522C-B</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>2</value>
</binding>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
<property>/instrumentation/comm/channels/B-mhz</property>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-B</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522C-C</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>3</value>
</binding>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
<property>/instrumentation/comm/channels/C-mhz</property>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-C</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>-0.005</dep>
</entry>
<entry>
<ind>4</ind>
<dep>0</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522C-D</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>4</value>
</binding>
<binding>
<command>property-assign</command>
<property>/instrumentation/comm[0]/frequencies/selected-mhz</property>
<property>/instrumentation/comm/channels/D-mhz</property>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-D</object-name>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>-0</dep>
</entry>
<entry>
<ind>1</ind>
<dep>-0.0</dep>
</entry>
<entry>
<ind>2</ind>
<dep>0</dep>
</entry>
<entry>
<ind>3</ind>
<dep>0</dep>
</entry>
<entry>
<ind>4</ind>
<dep>-0.005</dep>
</entry>
</interpolation>
<center>
<x-m>-0.00</x-m>
<y-m>0.0</y-m>
<z-m>-0.0</z-m>
</center>
<axis>
<x>0</x>
<y>0</y>
<z>1</z>
</axis>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-A</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>1</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-B</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>2</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-C</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>3</value>
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-D</object-name>
<condition>
<equals>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>4</value>
</equals>
</condition>
</animation>
<animation>
<type>pick</type>
<visible>false</visible>
<object-name>Pick0</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/tr</property>
<value>0</value>
</binding>
<mod-up>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/tr</property>
<value>1</value>
</binding>
</mod-up>
</action>
</animation>
<animation>
<type>pick</type>
<visible>false</visible>
<object-name>Pick1</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/tr</property>
<value>1</value>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<visible>false</visible>
<object-name>Pick2</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<condition>
<not>
<property>/systems/comm/SCR-522C/tr-lock</property>
</not>
</condition>
<command>property-assign</command>
<property>/systems/comm/SCR-522C/tr</property>
<value>2</value>
</binding>
</action>
</animation>
<animation>
<type>select</type>
<object-name>SCR-522C-lamp-T</object-name>
<condition>
<and>
<greater-than>
<property>/systems/comm/SCR-522C/tr</property>
<value>0</value>
</greater-than>
<greater-than>
<property>/systems/comm/SCR-522C/frequencies/channel-selected</property>
<value>0</value>
</greater-than>
<not>
<property>/instrumentation/comm[0]/ptt</property>
</not>
<not>
<property>/instrumentation/comm[1]/ptt</property>
</not>
</and>
</condition>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522C-light-cont-1</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-toggle</command>
<property>/systems/comm/SCR-522C/channel-dimmer</property>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-light-cont-1</object-name>
<property>/systems/comm/SCR-522C/channel-dimmer</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0.0085</dep>
</entry>
<entry>
<ind>1.0</ind>
<dep>0</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522C-light-cont-2</object-name>
<property>/systems/comm/SCR-522C/channel-dimmer</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0.0085</dep>
</entry>
<entry>
<ind>1.0</ind>
<dep>0</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>pick</type>
<visible>true</visible>
<object-name>SCR-522-T-Lock</object-name>
<action>
<button>0</button>
<repeatable>false</repeatable>
<binding>
<command>property-toggle</command>
<property>/systems/comm/SCR-522C/tr-lock</property>
</binding>
</action>
</animation>
<animation>
<type>translate</type>
<object-name>SCR-522-T-Lock</object-name>
<property>/systems/comm/SCR-522C/tr-lock</property>
<interpolation>
<entry>
<ind>0</ind>
<dep>0.0</dep>
</entry>
<entry>
<ind>1.0</ind>
<dep>0.0085</dep>
</entry>
</interpolation>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<animation>
<type>material</type>
<object-name>SCR-522C-top</object-name>
<object-name>SCR-522C-box</object-name>
<object-name>SCR-522C-A</object-name>
<object-name>SCR-522C-B</object-name>
<object-name>SCR-522C-C</object-name>
<object-name>SCR-522C-D</object-name>
<object-name>SCR-522C-T-base</object-name>
<object-name>SCR-522C-T-sw-handle</object-name>
<object-name>SCR-522C-T-stem</object-name>
<object-name>SCR-522C-light-cont-2</object-name>
<object-name>SCR-522C-light-cont-1</object-name>
<object-name>SCR-522C-OFF</object-name>
<object-name>SCR-522-T-Lock</object-name>
<!--<object-name>right-outer-bracket</object-name>
<object-name>left-inner-bracket</object-name>
<object-name>left-outer-bracket</object-name>-->
<condition>
<greater-than>
<property>/controls/lighting/panel-norm</property>
<value>0</value>
</greater-than>
</condition>
<emission>
<factor-prop>/controls/lighting/panel-norm</factor-prop>
<red>0.75</red>
<green>0.25</green>
<blue>0.25</blue>
</emission>
</animation>
</PropertyList>

View file

@ -34,7 +34,7 @@
<object-name>drum-1</object-name>
<property>instrumentation/altimeter/indicated-altitude-ft</property>
<factor>0.001</factor>
<offset>-75</offset>
<offset>-100</offset>
<step>100</step>
<scroll>10</scroll>
<axis>
@ -50,7 +50,7 @@
<object-name>drum-2</object-name>
<property>instrumentation/altimeter/indicated-altitude-ft</property>
<factor>0.0001</factor>
<offset>-750</offset>
<offset>-1000</offset>
<step>1000</step>
<scroll>25</scroll>
<axis>
@ -66,7 +66,7 @@
<object-name>drum-3</object-name>
<property>instrumentation/altimeter/indicated-altitude-ft</property>
<factor>0.00001</factor>
<offset>-7500</offset>
<offset>-10000</offset>
<step>10000</step>
<scroll>25</scroll>
<axis>

File diff suppressed because it is too large Load diff

View file

@ -7,20 +7,17 @@
<texture alias="../../texture"/>
</animation>
<animation>
<type>material</type>
<effect>
<inherits-from>Effects/lightmap</inherits-from>
<parameters>
<texture n="1">
<image>Aircraft/Instruments-3d/cdu/boeing_lightmap.png</image>
</texture>
<condition><use>/instrumentation/cdu/serviceable</use></condition>
<factor><use>/controls/lighting/panel-norm</use></factor>
</parameters>
<object-name>CDU</object-name>
<emission>
<factor-prop>/controls/lighting/panel-norm</factor-prop>
<red>0.1</red>
<green>0.1</green>
<blue>0.1</blue>
</emission>
</animation>
<animation>
<type>material</type>
<object-name>Btn.A</object-name>
<object-name>Btn.A</object-name>
<object-name>Btn.B</object-name>
<object-name>Btn.C</object-name>
<object-name>Btn.D</object-name>
@ -89,15 +86,7 @@
<object-name>Btn.side.r.4</object-name>
<object-name>Btn.side.r.5</object-name>
<object-name>Btn.side.r.6</object-name>
<condition>
<property>/instrumentation/cdu/serviceable</property>
</condition>
<emission>
<red>1</red>
<green>0.8</green>
<blue>0.5</blue>
</emission>
</animation>
</effect>
<model>
<path>Aircraft/Instruments-3d/cdu/display-text-value.xml</path>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -261,26 +261,14 @@ properties' values.
</equals>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>vs-minus</object-name>
<object-name>vs-digits-minus</object-name>
<condition>
<and>
<equals>
<property>/autopilot/KAP140/annunciators/vs-number</property>
<value type="bool">true</value>
</equals>
<greater-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</greater-than>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>vs-digits</object-name>
<object-name>vs-digit2-minus</object-name>
<object-name>vs-digit3-minus</object-name>
<object-name>vs-digit4-minus</object-name>
<object-name>vs-digit5-minus</object-name>
<condition>
<and>
<equals>
@ -288,25 +276,38 @@ properties' values.
<value type="bool">true</value>
</equals>
<less-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<value>-1.0</value>
</less-than>
</and>
</condition>
</animation>
<animation>
<type>select</type>
<object-name>vs-digit2</object-name>
<object-name>vs-digit3</object-name>
<object-name>vs-digit4</object-name>
<object-name>vs-digit5</object-name>
<condition>
<and>
<equals>
<property>/autopilot/KAP140/annunciators/vs-number</property>
<value type="bool">true</value>
</equals>
<greater-than-equals>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<value>-1.0</value>
</greater-than-equals>
</and>
</condition>
</animation>
<animation>
<type>textranslate</type>
<object-name>vs-digit5</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<less-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-5800</factor>
<step>0.000017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.1</factor>
<step>1</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -316,16 +317,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit4</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<less-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-580</factor>
<step>0.00017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.01</factor>
<step>10</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -335,16 +330,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit3</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<less-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-58</factor>
<step>0.0017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.001</factor>
<step>100</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -354,16 +343,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit2</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<less-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-5.8</factor>
<step>0.017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.0001</factor>
<step>1000</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -373,16 +356,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit5-minus</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<greater-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>5800</factor>
<step>0.000017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.1</factor>
<step>1</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -392,16 +369,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit4-minus</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<greater-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>580</factor>
<step>0.00017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.01</factor>
<step>10</step>
<bias>9.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -411,16 +382,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit3-minus</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<greater-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>58</factor>
<step>0.0017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.001</factor>
<step>100</step>
<bias>99.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -430,22 +395,17 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit2-minus</object-name>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<condition>
<greater-than>
<value>0</value>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>5.8</factor>
<step>0.017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.0001</factor>
<step>1000</step>
<bias>999.5</bias>
<axis>
<x>1</x>
<y>0</y>
<z>0</z>
</axis>
</animation>
<!-- alt-setting -->
<animation>
<type>select</type>

View file

@ -0,0 +1,479 @@
AC3Db
MATERIAL "ac3dmat1" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0 0 0 shi 10 trans 0
MATERIAL "ac3dmat3" rgb 1 0 0 amb 1 0 0 emis 0 0 0 spec 0 0 0 shi 10 trans 0
MATERIAL "ac3dmat1" rgb 1 1 1 amb 1 1 1 emis 0 0 0 spec 0 0 0 shi 128 trans 1
OBJECT world
kids 2
OBJECT group
name "egt"
loc 0.00108333 -0.00211694 -0.00299342
kids 4
OBJECT poly
name "Knob"
loc 0.00141667 -0.00554903 0.00299342
crease 45.000000
numvert 25
0.00257786 0 0
-0.00257786 -1.86265e-09 0.0025
-0.00257786 -0.00125 0.00216506
-0.00257786 -0.00216506 0.00125
-0.00257786 -0.0025 0
-0.00257786 -0.00216506 -0.00125
-0.00257786 -0.00125 -0.00216506
-0.00257786 0 -0.0025
-0.00257786 0.00125 -0.00216506
-0.00257786 0.00216506 -0.00125
-0.00257786 0.0025 0
-0.00257786 0.00216506 0.00125
-0.00257786 0.00125 0.00216506
0.00257786 -1.86265e-09 0.0025
0.00257786 -0.00125 0.00216506
0.00257786 -0.00216506 0.00125
0.00257786 -0.0025 0
0.00257786 -0.00216506 -0.00125
0.00257786 -0.00125 -0.00216506
0.00257786 0 -0.0025
0.00257786 0.00125 -0.00216506
0.00257786 0.00216506 -0.00125
0.00257786 0.0025 0
0.00257786 0.00216506 0.00125
0.00257786 0.00125 0.00216506
numsurf 24
SURF 0x10
mat 1
refs 3
0 0 0
24 0 0
13 0 0
SURF 0x10
mat 1
refs 3
0 0 0
13 0 0
14 0 0
SURF 0x10
mat 1
refs 3
0 0 0
14 0 0
15 0 0
SURF 0x10
mat 1
refs 3
0 0 0
15 0 0
16 0 0
SURF 0x10
mat 1
refs 3
0 0 0
16 0 0
17 0 0
SURF 0x10
mat 1
refs 3
0 0 0
17 0 0
18 0 0
SURF 0x10
mat 1
refs 3
0 0 0
18 0 0
19 0 0
SURF 0x10
mat 1
refs 3
0 0 0
19 0 0
20 0 0
SURF 0x10
mat 1
refs 3
0 0 0
20 0 0
21 0 0
SURF 0x10
mat 1
refs 3
0 0 0
21 0 0
22 0 0
SURF 0x10
mat 1
refs 3
0 0 0
22 0 0
23 0 0
SURF 0x10
mat 1
refs 3
0 0 0
23 0 0
24 0 0
SURF 0x10
mat 1
refs 4
24 -2.98023e-08 1
12 -2.98023e-08 0
1 0.0833333 0
13 0.0833333 1
SURF 0x10
mat 1
refs 4
13 0.0833333 1
1 0.0833333 0
2 0.166667 0
14 0.166667 1
SURF 0x10
mat 1
refs 4
14 0.166667 1
2 0.166667 0
3 0.25 0
15 0.25 1
SURF 0x10
mat 1
refs 4
15 0.25 1
3 0.25 0
4 0.333333 0
16 0.333333 1
SURF 0x10
mat 1
refs 4
16 0.333333 1
4 0.333333 0
5 0.416667 0
17 0.416667 1
SURF 0x10
mat 1
refs 4
17 0.416667 1
5 0.416667 0
6 0.5 0
18 0.5 1
SURF 0x10
mat 1
refs 4
18 0.5 1
6 0.5 0
7 0.583333 0
19 0.583333 1
SURF 0x10
mat 1
refs 4
19 0.583333 1
7 0.583333 0
8 0.666667 0
20 0.666667 1
SURF 0x10
mat 1
refs 4
20 0.666667 1
8 0.666667 0
9 0.75 0
21 0.75 1
SURF 0x10
mat 1
refs 4
21 0.75 1
9 0.75 0
10 0.833333 0
22 0.833333 1
SURF 0x10
mat 1
refs 4
22 0.833333 1
10 0.833333 0
11 0.916667 0
23 0.916667 1
SURF 0x10
mat 1
refs 4
23 0.916667 1
11 0.916667 0
12 1 0
24 1 1
kids 0
OBJECT poly
name "Needle"
loc -0.00108333 0.00343209 -0.00598684
crease 45.000000
numvert 5
-0.001 0.00827315 0.0276479
-0.001 0.00968736 0.0262337
-0.001 -0.00827315 0.00827316
-0.001 -0.00968736 0.00968737
-0.001 0.00992145 0.027882
numsurf 1
SURF 0x0
mat 0
refs 5
3 0 0
2 1 0
1 1 1
4 0.5 1
0 0 1
kids 0
OBJECT poly
name "Bug"
loc -0.00158333 0.0034321 -0.00598683
crease 45.000000
numvert 5
-1.80444e-09 0.00992143 0.0278819
-1.74623e-10 -0.00915701 0.00915704
-1.74623e-10 -0.00880346 0.00880349
-1.68802e-09 0.00915702 0.026764
-1.80444e-09 0.00880346 0.0271175
numsurf 1
SURF 0x0
mat 1
refs 5
1 0 0
2 1 0
3 1 1
0 0.5 1
4 0 1
kids 0
OBJECT group
name "Pick"
loc 0.00141667 -0.00554903 0.00299342
kids 2
OBJECT poly
name "Knob.Pick.L"
crease 45.000000
numvert 15
0.00283564 0 0
-0.00283564 -2.32831e-09 0.003
-0.00283564 -0.0015 0.00259808
-0.00283564 -0.00259808 0.0015
-0.00283564 -0.003 0
-0.00283564 0.003 0
-0.00283564 0.00259808 0.0015
-0.00283564 0.0015 0.00259808
0.00283564 -2.32831e-09 0.003
0.00283564 -0.0015 0.00259808
0.00283564 -0.00259808 0.0015
0.00283564 -0.003 0
0.00283564 0.003 0
0.00283564 0.00259808 0.0015
0.00283564 0.0015 0.00259808
numsurf 12
SURF 0x10
mat 2
refs 3
0 0 0
14 0 0
8 0 0
SURF 0x10
mat 2
refs 3
0 0 0
8 0 0
9 0 0
SURF 0x10
mat 2
refs 3
0 0 0
9 0 0
10 0 0
SURF 0x10
mat 2
refs 3
0 0 0
10 0 0
11 0 0
SURF 0x10
mat 2
refs 3
0 0 0
12 0 0
13 0 0
SURF 0x10
mat 2
refs 3
0 0 0
13 0 0
14 0 0
SURF 0x10
mat 2
refs 4
14 -2.98023e-08 1
7 -2.98023e-08 0
1 0.0833333 0
8 0.0833333 1
SURF 0x10
mat 2
refs 4
8 0.0833333 1
1 0.0833333 0
2 0.166667 0
9 0.166667 1
SURF 0x10
mat 2
refs 4
9 0.166667 1
2 0.166667 0
3 0.25 0
10 0.25 1
SURF 0x10
mat 2
refs 4
10 0.25 1
3 0.25 0
4 0.333333 0
11 0.333333 1
SURF 0x10
mat 2
refs 4
12 0.833333 1
5 0.833333 0
6 0.916667 0
13 0.916667 1
SURF 0x10
mat 2
refs 4
13 0.916667 1
6 0.916667 0
7 1 0
14 1 1
kids 0
OBJECT poly
name "Knob.Pick.R"
crease 45.000000
numvert 15
0.00283564 0.003 0
0.00283564 0.00259808 -0.0015
0.00283564 0.0015 -0.00259808
0.00283564 0 -0.003
0.00283564 -0.0015 -0.00259808
0.00283564 -0.00259808 -0.0015
0.00283564 -0.003 0
-0.00283564 0.003 0
-0.00283564 0.00259808 -0.0015
-0.00283564 0.0015 -0.00259808
-0.00283564 0 -0.003
-0.00283564 -0.0015 -0.00259808
-0.00283564 -0.00259808 -0.0015
-0.00283564 -0.003 0
0.00283564 0 0
numsurf 12
SURF 0x10
mat 2
refs 4
1 0.75 1
8 0.75 0
7 0.833333 0
0 0.833333 1
SURF 0x10
mat 2
refs 4
2 0.666667 1
9 0.666667 0
8 0.75 0
1 0.75 1
SURF 0x10
mat 2
refs 4
3 0.583333 1
10 0.583333 0
9 0.666667 0
2 0.666667 1
SURF 0x10
mat 2
refs 4
4 0.5 1
11 0.5 0
10 0.583333 0
3 0.583333 1
SURF 0x10
mat 2
refs 4
5 0.416667 1
12 0.416667 0
11 0.5 0
4 0.5 1
SURF 0x10
mat 2
refs 4
6 0.333333 1
13 0.333333 0
12 0.416667 0
5 0.416667 1
SURF 0x10
mat 2
refs 3
14 0 0
1 0 0
0 0 0
SURF 0x10
mat 2
refs 3
14 0 0
2 0 0
1 0 0
SURF 0x10
mat 2
refs 3
14 0 0
3 0 0
2 0 0
SURF 0x10
mat 2
refs 3
14 0 0
4 0 0
3 0 0
SURF 0x10
mat 2
refs 3
14 0 0
5 0 0
4 0 0
SURF 0x10
mat 2
refs 3
14 0 0
6 0 0
5 0 0
kids 0
OBJECT poly
name "Face"
loc -0.001 0 0
texture "EGT.png"
crease 45.000000
numvert 10
0.000999999 -0.025 -0.025
0.000999999 -0.025 0.025
0.000999999 -9.31323e-10 0.025
0.000999999 -9.31323e-10 -0.025
-0.000999999 -9.31323e-10 -0.025
-0.000999999 -9.31323e-10 0.025
-0.000999999 -0.025 0.025
-0.000999999 -0.025 -0.025
-0.000999999 0.025 -0.025
-0.000999999 0.025 0.025
numsurf 3
SURF 0x0
mat 0
refs 4
2 0 0.245003
1 0 -0.117118
0 1 -0.117118
3 1 0.245003
SURF 0x0
mat 0
refs 4
5 0 0.214422
6 0 0
7 1 0
4 1 0.214422
SURF 0x0
mat 0
refs 4
4 1 0
8 1 1
9 0 1
5 0 0
kids 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -0,0 +1,162 @@
<?xml version="1.0" ?>
<!--
This file is part of FlightGear, the free flight simulator
http://www.flightgear.org/
Copyright (C) 2009 Torsten Dreyer, Torsten (at) t3r _dot_ de
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
-->
<PropertyList>
<path>EGT.ac</path>
<params>
<value>engines/engine[0]/egt-norm</value>
<bug>engines/engine[0]/egt-bug-norm</bug>
</params>
<animation>
<type>range</type>
<min-m>0</min-m>
<max-m>10</max-m>
</animation>
<animation>
<type>material</type>
<object-name>egt</object-name>
<emission>
<red-prop>sim/model/instrument-lighting/emission/red</red-prop>
<green-prop>sim/model/instrument-lighting/emission/green</green-prop>
<blue-prop>sim/model/instrument-lighting/emission/blue</blue-prop>
<factor-prop>controls/lighting/instruments-norm</factor-prop>
</emission>
</animation>
<animation>
<type>rotate</type>
<object-name>Needle</object-name>
<property alias="../../params/value"/>
<factor>-90.0</factor>
<axis>
<x>1.0</x>
<y>0.0</y>
<z>0.0</z>
</axis>
<center>
<x-m>-0.0015</x-m>
<y-m>0</y-m>
<z-m>-0.00766508</z-m>
</center>
</animation>
<animation>
<type>rotate</type>
<object-name>Bug</object-name>
<property alias="../../params/bug"/>
<factor>-90.0</factor>
<axis>
<x>1.0</x>
<y>0.0</y>
<z>0.0</z>
</axis>
<center>
<x-m>-0.0015</x-m>
<y-m>0</y-m>
<z-m>-0.00766508</z-m>
</center>
</animation>
<animation>
<type>pick</type>
<object-name>Knob.Pick.L</object-name>
<visible>false</visible>
<action>
<button>0</button>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property alias="../../../../params/bug"/>
<step>-0.01</step>
<min>0.0</min>
<max>1.0</max>
<wrap>0</wrap>
</binding>
</action>
<action>
<button>1</button>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property alias="../../../../params/bug"/>
<step>-0.1</step>
<min>0.0</min>
<max>1.0</max>
<wrap>0</wrap>
</binding>
</action>
<action>
<button>3</button>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property alias="../../../../params/bug"/>
<step>-0.01</step>
<min>0.0</min>
<max>1.0</max>
<wrap>0</wrap>
</binding>
</action>
</animation>
<animation>
<type>pick</type>
<object-name>Knob.Pick.R</object-name>
<visible>false</visible>
<action>
<button>0</button>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property alias="../../../../params/bug"/>
<step>0.01</step>
<min>0.0</min>
<max>1.0</max>
<wrap>0</wrap>
</binding>
</action>
<action>
<button>1</button>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property alias="../../../../params/bug"/>
<step>0.1</step>
<min>0.0</min>
<max>1.0</max>
<wrap>0</wrap>
</binding>
</action>
<action>
<button>4</button>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property alias="../../../../params/bug"/>
<step>0.01</step>
<min>0.0</min>
<max>1.0</max>
<wrap>0</wrap>
</binding>
</action>
</animation>
</PropertyList>

View file

@ -289,10 +289,10 @@ properties' values.
<property>/autopilot/KAP140/annunciators/vs-number</property>
<value type="bool">true</value>
</equals>
<greater-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</greater-than>
<less-than>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<value>-1.0</value>
</less-than>
</and>
</condition>
</animation>
@ -308,25 +308,20 @@ properties' values.
<property>/autopilot/KAP140/annunciators/vs-number</property>
<value type="bool">true</value>
</equals>
<less-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</less-than>
<greater-than-equals>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<value>-1.0</value>
</greater-than-equals>
</and>
</condition>
</animation>
<animation>
<type>textranslate</type>
<object-name>vs-digit5</object-name>
<condition>
<less-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-5800</factor>
<step>0.000017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.1</factor>
<step>1</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -336,15 +331,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit4</object-name>
<condition>
<less-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-580</factor>
<step>0.00017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.01</factor>
<step>10</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -354,15 +344,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit3</object-name>
<condition>
<less-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-58</factor>
<step>0.0017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.001</factor>
<step>100</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -372,15 +357,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit2</object-name>
<condition>
<less-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</less-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>-5.8</factor>
<step>0.017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>0.0001</factor>
<step>1000</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -390,15 +370,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit5-minus</object-name>
<condition>
<greater-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>5800</factor>
<step>0.000017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.1</factor>
<step>1</step>
<bias>0.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -408,15 +383,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit4-minus</object-name>
<condition>
<greater-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>580</factor>
<step>0.00017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.01</factor>
<step>10</step>
<bias>9.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -426,15 +396,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit3-minus</object-name>
<condition>
<greater-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>58</factor>
<step>0.0017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.001</factor>
<step>100</step>
<bias>99.5</bias>
<axis>
<x>1</x>
<y>0</y>
@ -444,15 +409,10 @@ properties' values.
<animation>
<type>textranslate</type>
<object-name>vs-digit2-minus</object-name>
<condition>
<greater-than>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<value>0</value>
</greater-than>
</condition>
<property>/autopilot/KAP140/settings/target-pressure-rate</property>
<factor>5.8</factor>
<step>0.017241379</step>
<property>/autopilot/KAP140/settings/target-pressure-rate-fpm</property>
<factor>-0.0001</factor>
<step>1000</step>
<bias>999.5</bias>
<axis>
<x>1</x>
<y>0</y>

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 126 KiB

File diff suppressed because it is too large Load diff

View file

@ -172,6 +172,16 @@
</offsets>
</model>
<model>
<name>EGT</name>
<path>Aircraft/c172p/Instruments/EGT/EGT.xml</path>
<offsets>
<x-m>-0.368996</x-m>
<y-m>-0.131332</y-m>
<z-m>-0.140618</z-m>
</offsets>
</model>
<model>
<name>hi</name>
<path>Aircraft/c172p/Instruments/hi/hi.xml</path>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 KiB

After

Width:  |  Height:  |  Size: 458 KiB

View file

@ -266,6 +266,15 @@
<filter-time>0.1</filter-time>
</filter>
<filter>
<name>conversion to fpm</name>
<debug>false</debug>
<type>gain</type>
<input>/autopilot/KAP140/settings/target-pressure-rate</input>
<output>/autopilot/KAP140/settings/target-pressure-rate-fpm</output>
<gain>-58000</gain>
</filter>
<!-- Vertical Speed (VS) Mode -->
<pid-controller>
<name>Vertical Speed (VS) Mode</name>

View file

@ -37,5 +37,22 @@
<output>instrumentation/nav[1]/filtered-gsNAV1-deflection</output>
</filter>
<filter>
<name>EGT lowpass</name>
<debug>false</debug>
<type>exponential</type>
<filter-time>4.0</filter-time>
<input>
<property>engines/engine[0]/egt-degf</property>
<!-- 1200 * scale + offset = 0.0
1700 * scale + offset = 1.0 -->
<scale>0.002</scale>
<offset>-2.4</offset>
</input>
<output>engines/engine[0]/egt-norm</output>
<min>0.0</min>
<max>1.0</max>
</filter>
</PropertyList>

View file

@ -13,7 +13,14 @@ Started October 23 2001 by John Check, fgpanels@rockfish.net
<description>Cessna 172P Skyhawk (1981 model)</description>
<author>David Megginson</author>
<status>production</status>
<status>early production</status>
<rating>
<FDM type="int">3</FDM>
<systems type="int">4</systems>
<model type="int">5</model>
<cockpit type="int">4</cockpit>
</rating>
<flight-model archive="y">jsb</flight-model>
<aero archive="y">c172p</aero>
@ -177,6 +184,7 @@ Started October 23 2001 by John Check, fgpanels@rockfish.net
<file>Aircraft/c172p/Nasal/ki266.nas</file>
<script><![CDATA[
ki266.new(0);
aircraft.data.add( "engines/engine[0]/egt-bug-norm" );
]]></script>
</c172p>

Binary file not shown.

View file

@ -83,8 +83,8 @@ property arguments.
<lat-deg> - Latitude t place the cloud, in degrees (default 0)
<alt-ft> - Altitude to place the cloud, relative to the layer (!) in ft
(default 0)
TODO: Add offset arguments for consistency with add-cloud.
<x-offset-m> - Offset in m from the lon-deg. +ve is south (default 0)
<y-offset-m> - Offset in m from the lat-deg. +ve is east (default 0)
Deleting Individual Clouds
===========================

View file

@ -162,6 +162,11 @@ Here's a quick syntax summary:
Contains two properties or a property and value, and is true if the
properties have equivalent values.
* <not-equals>...</not-equals>
Contains two properties or a property and value, and is true if the
properties have different values.
* <greater-than>...</greater-than>
Contains two properties or a property and a value, and is true if

View file

@ -400,6 +400,7 @@ The flags printed after the node type have the following meaning:
W -> trace write operations
A -> archive bit set
U -> user archive bit set
P -> preserved bit set (value is preserved on sim-reset)
T -> property is "tied"
Ln -> number of listeners attached to this node

View file

@ -1,20 +0,0 @@
This package contains the local weather AI scenarios 'local_weather_thunderstorm' and 'local_weather_cirrus'. Currently they populate the sky over San Francisco either with a thunderstorm or a Cirrus/Cirrocumulus sky. The thunderstorm comes with dynamical weather effects - rain, snow, turbulence and lightning inside the cloud tower. The packages contain more cloud models and textures than are actually needed in the scenarios, the scenarios are just to demonstrate what can be done using local weather AI models.
Known issues:
* AI model clouds seem to interfere with the standard layered clouds, thus for best effect any layered clouds need to be off - a low 3-d Cumulus layer on the other hand works well.
* Currently, the Cirrus and Cirrocumulus cloud models use the Aircraft AI system. Presumably, this (dependent on visibility) leads to their sudden (dis-)appearance, and a better solution should be found.
* The shading of the Cirrus clouds is imperfect - real Cirrus clouds appear to glow when the sund shines through them, caused by the dominance of forward scattering of light on the ice crystals of the cloud. This can be modelled setting emis 1 1 1 in the materials section of the 3-d model, however then the clouds also shine white at night. I have no good solution at present.
* The Nasal script of the thunderstorm sometimes crashes for no reason I could reproduce - pointers to fix that are welcome!
The package should be extracted in the Flightgear root directory.
The package is developed and tested using Flightgear 1.9.1, it may or may not work properly on other versions.
Thorsten Renk, Feb. 2010

View file

@ -1,13 +1,13 @@
<html>
<head>
<title>Local Weather </title>
</head>
<html>
<head>
<title>Local Weather </title>
</head>
<body>
<h1>Local Weather Package - v1.0</h1>
<h1>Local Weather Package - v1.18</h1>
<h2>1. Introduction</h2>
@ -15,18 +15,19 @@ The aim of a local weather system is to simulate weather phenomena tied to speci
This is in contrast to the current (v.2.0.0) global weather system of Flightgear where weather changes affect the weather everywhere in the simulated world and are (with few exceptions) not tied to specific locations. In such a system, it is impossible to observe e.g. the approach of a rainfront while flying in sunshine.<p>
The local weather package aims to provide the functionality to simulate such local phenomena. In version 1.0, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and effect volumes. The dynamics of the different systems is tied together - for instance clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution over the various terrain types. There is a simulation of the interaction of the convective cloud system with the terrain as a function of time. Clouds drifting in the wind flow over obstacles, i.e. they change their altitude dynamically. Convection is implemented with a life cycle model of Cumulus clouds - they are generated, evolve for a given lifetime dependent on the underlying terrain and decay at the end of their life cycle. Thermals associated with the clouds follow the same pattern. In particular, in the presence of wind favourable spots for convection generate 'alleys' of dense cloud cover downwind, or thermals and clouds generated over land decay rapidly once they reach open water.<p>
The local weather package aims to provide the functionality to simulate such local phenomena. In version 1.18, the package supplies various cloud placement algorithms, as well as local control over most major weather parameters (wind, visibility, pressure, temperature, rain, snow, thermal lift, turbulence...) through interpolation routines and effect volumes. The dynamics of the different systems is tied together - for instance clouds and weather effects drift in the specified wind field. The package also contains a fairly detailed algorithm to generate convective clouds and thermals with a realistic distribution over the various terrain types. There is a simulation of the interaction of the convective cloud system with the terrain as a function of time. Clouds drifting in the wind flow over obstacles, i.e. they change their altitude dynamically. Convection is implemented with a life cycle model of Cumulus clouds - they are generated, evolve for a given lifetime dependent on the underlying terrain and decay at the end of their life cycle. Thermals associated with the clouds follow the same pattern. In particular, in the presence of wind favourable spots for convection generate 'alleys' of dense cloud cover downwind, or thermals and clouds generated over land decay rapidly once they reach open water.<p>
For long-range flights, the system provides an offline weather system with plausible transitions between different large-scale weather patterns like fronts and low and high pressure areas, as well as the optional use of live METAR data. <p>
<h2>2. Installation</h2>
The package needs to be unpacked in the Flightgear root directory. It writes content into the <i>Nasal/, gui/, gui/dialogs/, Shaders, Effects/, Docs/</i>, and <i>Models/Weather/</i> subdirectories. The installation does not overwrite any of the default Flightgear files, but to be accessible from the menu for Flightgear 2.0.0, one must copy <i>gui/menubar.xml.alt</i> to the default <i>menubar.xml</i> or copy the three lines of the environemnt menu calling <i>local_weather</i>, <i>local_weather_tiles</i> and <i>local_weather_config</i> into the default file. More recent versions of Flightgear already provide the necessary menu items.<p>
The package needs to be unpacked in the Flightgear root directory. It writes content into the <i>Nasal/local_weather/, gui/, gui/dialogs/, Shaders, Effects/, Docs/</i>, <i>Environment/</i> and <i>Models/Weather/</i> subdirectories. The package requires run-time loadable Nasal submodule functionality and is not compatible with 2.0.0.<p>
This adds the items <i>Local Weather</i>, <i>Local Weather Tiles</i> and <i>Local Weather Config</i> to the <i>Environment</i> menu when Flightgear is up. Most of the basic functionality is contained in <i>local_weather.nas</i> which is loaded at startup and identifies itself with a message.<p>
This adds the items <i>Local Weather</i> and <i>Local Weather Settings</i> to the <i>Environment</i> and <i>Local Weather (test)</i> to the <i>Debug</i> menu when Flightgear is up. Most of the basic functionality is contained in <i>local_weather.nas</i> which is loaded at startup and identifies itself with a message. A compatibility layer (<i>compat_layer.nas</i>) tests for hard-coded support and ensures that, dependent on the version, hard-coded or Nasal-coded fallback functions are used.<p>
The Local Weather Nasal modules need to be loaded at runtime using the checkbox in <i>Environment/Local Weather Settings</i>, but once this is specified, the setting is remembered and the package will be automatically loaded upon startup. Unless asked to do so from the menu, Local Weather does <b>not</b> run any process in the background. Upon loading, the package does not set any properties already existing, but only generates properties necessary for the menu entries in its own subdirectory <i>/local-weather/</i> in the tree. The package also does a features check on startup if particular functions are available in hard-coded form. If the features are not present, the package will largely still function properly using slower Nasal fallback code.<p>
Unless asked to do so from the menu, local weather does <b>not</b> run any process in the background. Upon loading, the package does not set any properties already existing, but only generates properties necessary for the menu entries in its own subdirectory <i>/local-weather/</i> in the tree. The package also does a features check on startup if particular functions are available in hard-coded form. If the features are not present, the package will largely still function properly using slower Nasal fallback code.<p>
<h2>3. Functionality</h2>
@ -35,10 +36,10 @@ The general rule is that the gui is not hardened against problematic user input,
Cloud placement calls may sometimes take a significant time to execute especially for large numbers of clouds tied in a complicated way to the terrain. Placing 500 barrier clouds against a small barrier may take a minute to compute. During this time, a reduced framerate is to be expected<p>
The first menu <b>Local Weather</b> contains the low level cloud placement functions. Its purpose is mainly for developing cloud patterns without having to resort to re-type the underlying Nasal code every time. Currently five options are supported: <i>Place a single cloud</i>, <i>Place a cloud streak</i>, <i>Start the convective system</i>, <i>Create barrier clouds</i> , <i>Place a cloud layer</i> and <i>Make a cloudbox</i>.<p>
The menu item <i>Debug/Local Weather (Test) </i> contains the low level cloud placement functions. Its purpose is mainly for developing cloud patterns without having to resort to re-type the underlying Nasal code every time. Currently five options are supported: <i>Place a single cloud</i>, <i>Place a cloud streak</i>, <i>Start the convective system</i>, <i>Create barrier clouds</i> , <i>Place a cloud layer</i> and <i>Make a cloudbox</i>.<p>
<center>
<img src="menu1.jpg">
<img src="img/menu1.jpg">
</center><p>
<h3>Single cloud placement</h3>
@ -58,7 +59,7 @@ The pattern can then be randomized in x, y and altitude. Basically, specifying n
The convective system places Cumulus clouds centered on the current position based on the underlying terrain. Currently it models daily variation of convective strength and the latitude variation based on a simple sinusoidal model (i.e. it produces different results when called in the morning than at noon), but it does not take into account seasonal variation (i.e. it assumes the date to be the equinox). The actual placement is chosen based on the type of the underlying terrain, with Cumulus development more likely over city areas than on water. Details of the algorithm are described in the appendix. The parameters for this need some fine-tuning and are currently rather rough, but they lead for example to pronounced differences between land and sea in coastal regions. The following picture shows the result of a call of the system in the afternoon over TNCM.<p>
<center>
<img src="clouds-cumulus_afternoon.jpg">
<img src="img/clouds-cumulus_afternoon.jpg">
</center><p>
Unless 'Terrain presampling' is active, clouds are placed in a constant altitude <i>alt</i> in a tile with given <i>size</i> where the size measures the distance to the tile border, i.e. a size parameter of 15 km corresponds to a 30x30 km region. When 'Terrain presampling' is selected, the distribution of clouds in altitude is determined by a more complicated algorithm described in the appendix. Clouds are placed with constant density for given terrain type, so be careful with large area placements! <i>strength</i> is an overall multiplicative factor to fine-tune the amount of cloud generation.
@ -68,7 +69,7 @@ Unless 'Terrain presampling' is active, clouds are placed in a constant altitude
The barrier cloud system places a cloud at a random point within the region centered around the current position given by <i>size</i> with some probability if there is a terrain barrier downwind with the elevation <i>alt</i> within a distance <i>dist</i> or less. Cloud placement probability is larger for small distances to the barrier. The system tries to place <i>number</i> clouds and assumes that the wind comes from direction <i>wind</i>. If clouds cannot be placed (because there is no barrier within the specified altitude) the algorithm exits with a warning. The picture illustrates the result for the mountains above Las Vegas.<p>
<center>
<img src="barrier.jpg">
<img src="img/barrier.jpg">
</center><p>
Currently, the algorithm does not check for a barrier upstream - this may change in future versions. The ufo is a good way to explore the results of the algorithm by simply flying to a suitable location and calling it there for a relatively small region. Due to its large performance use, the barrier cloud system is currently not part of the large-scale weather generating system.
@ -82,7 +83,7 @@ If <i>rainflag</i> is set to 1, the system will also place external precipitatio
The picture illustrates the result of a layer generation call for Nimbostratus clouds with precipitation models. <p>
<center>
<img src="clouds-nimbostratus.jpg">
<img src="img/clouds-nimbostratus.jpg">
</center><p>
<h3>Cloudbox</h3>
@ -92,10 +93,10 @@ The cloudbox placement is an experimental routine allowing to define a cloud cor
<h3>Tile placement</h3>
The second menu is used to place complete weather patterns based on low-level calls. It is intended for the user to automatically create the various weather development during flight. Unless stated otherwise, all parameters in this menu are parsed at startup time of local weather only (i.e. when the user selects the <i>OK</i> button, but not while the system runs.<p>
The second menu <i>Environment/Local Weather</i> is used to place complete weather patterns based on low-level calls. It is intended for the user to automatically create the various weather development during flight. Unless stated otherwise, all parameters in this menu are parsed at startup time of Local Weather only (i.e. when the user selects the <i>OK</i> button, but not while the system runs.<p>
<center>
<img src="menu2.jpg">
<img src="img/menu2.jpg">
</center><p>
Weather is created in a series of 40x40 km squares, called tiles. Tiles are classified by airmass, such that the sequence of tiles can describe for example the transition from a high pressure area to a low pressure area. The dropdown menu is used to select the type of weather tile to build initially and to determine the rules according to which subsequent tiles are generated. <p>
@ -128,13 +129,13 @@ The slider <i>Thermal properties</i> is mainly relevant for soaring scenarios. I
The difference is apparent from the following pictures: Smooth and well-formed clouds characteristic of a calm day:<p>
<center>
<img src="detailed_clouds04.jpg">
<img src="img/detailed_clouds04.jpg">
</center><p>
Rough clouds characteristic of windshear and more turbulent conditions:<p>
<center>
<img src="detailed_clouds05.jpg">
<img src="img/detailed_clouds05.jpg">
</center><p>
As for the buttons, <i>OK</i> starts the local weather system with the selected options (note that almost all options in this menu are startup-time options, they are read once and changing them without restarting the system will not affect the behaviour of the system). <i>Clear/End</i> clears all clouds and ends all local weather functionality - the button brings the system back into the state before it was started. No loops or other subroutines are executed after the button is pressed. <i>Close</i> closes the dialog without starting the system.<p>
@ -142,7 +143,7 @@ As for the buttons, <i>OK</i> starts the local weather system with the selected
The button <i>Show winds</i> brings up the detailed wind menu which is needed for the wind models <i>aloft interpolated</i> and <i>aloft waypoints</i> when not in <i>METAR</i> mode:<p>
<center>
<img src="menu3.jpg">
<img src="img/menu3.jpg">
</center><p>
For <i>aloft interpolated</i>, the menu is used by inserting wind direction and speed for all given altitudes. After <i>OK</i>, the specified values are used. For <i>aloft waypoints</i>, the same info must be supplied for a series of waypoints. First, the latitude and longitude has to be inserted, afterwards the aloft winds for that point below. The button <i>Set waypoint</i> commits the windfield as specified in the menu for this position into memory. For orientation, the number of points inserted is counted on the lower right. <i>Clear Waypoints</i> removes all information entered so far. Note that <i>OK</i> does not commit the data for a waypoint. Entering a windfield in this way by hand is rather cumbersome, but may be useful occasionally - the main purpose of the wind model however is to work with live weather data.<p>
@ -153,51 +154,59 @@ For <i>aloft interpolated</i>, the menu is used by inserting wind direction and
The following pictures show possible results of tile setups 'High-pressure-border' and 'Low-pressure':<p>
<center>
<img src="high_pressure_border.jpg">
<img src="img/high_pressure_border.jpg">
</center><p>
<center>
<img src="low_pressure.jpg">
<img src="img/low_pressure.jpg">
</center><p>
<h3>Performance settings</h3>
The performance setting menu is available from the main menubar. It controls the allocation of system resources to the various tasks.<p>
The performance setting menu is available from the main menubar. It controls the allocation of system resources to the various tasks, as well as the behaviour of the offline weather system.<p>
The upper checkbox determines if the Nasal modules corresponding to Local Weather are loaded. Unless this box is checked, no Local Weather code is available and the corresponding other menu items are not functional.<p>
<center>
<img src="menu4.jpg">
<img src="img/menu4.jpg">
</center><p>
The first part controls the creation of new weather tiles, the second part controls the ranges up to which clouds inside a generated weather tile are shown as part of the scenery (clouds created but not shown are stored and processed in a buffer array). Clouds are visible if and only if they are 1) part of a created tile and 2) closer than the buffering range. This is illustrated in the following figure: Tiles are only created if their center is within range, clouds are only visible if the cloud model itself is within range, the resulting region dependent on the two ranges in which clouds are displayed is shown in red.<p>
<center>
<img src="tiles.gif">
<img src="img/tiles.gif">
</center><p>
From this, it is apparent that the two ranges should not be drastically different. Setting tile creation range to 55 km implies lots of work trying to asses the properties of faraway terrain to generate suitable cloud distributions, setting the buffering range to 15 km means that most of these clouds are never visible. Note that in low visibility, a large tile creation range can be problematic, as Flightgear may not have loaded the terrain. Note also that setting the tile creation range above 40 km means a slow startup of the system, as it needs to create 5 tiles on startup instead of just a single one.<p>
There are additional options to do asymmetric buffering, i.e. to 'cut' out a wedge in the rear of the aircraft in which the tile creation radius or the cloud visibility radius are lower. For example, setting the buffering ratio to 0.2 and the angle to 180 degrees corresponds to a very agressive buffering in which clouds in the rear hemisphere of the aircraft are almost absent and shown only to 20% of the set distance. This may increase performance by 20-30%, but only in straight flight - in fast turns, lots of clouds have to be shuffled from the buffer into the scenery and back, which is actually slower than never trying to cut a wedge at all. Also, agressive asymmetric buffering is not nice in external views as the reference is always the aircraft course.<p>
The last option controls the amout of resources allocated to making clouds drift in the wind if dynamical weather is on. In order to avoid freezes, the weather dynamics system processes only a fixed number of clouds per frame inside the field of view. That means that faraway clouds do not necessarily move even with dynamical weather on. The slider indirectly controls the distance out to which clouds are being processed.<p>
The buffering control can also optionally be tied to a target framerate. In this case, the visible range set with the slider is ignored and a control loop tries to adjust the visible range instead such that a 5-second average of the framerate is within 10% of a specified target framerate. Currently, the subsystem <i>only</i> adjusts the visible range of buffering - asymmetric buffering and/or tile loading range still needs to be specified manually. Also, the system cannot respond to unsolvable problems - if the framerate in the absence of clouds falls below 30, then no setting for the cloud range will bring it to 35. <p>
The next option controls the amout of resources allocated to making clouds drift in the wind if dynamical weather is on. In order to avoid freezes, the weather dynamics system processes only a fixed number of clouds per frame inside the field of view. That means that faraway clouds do not necessarily move even with dynamical weather on. The slider indirectly controls the distance out to which clouds are being processed.<p>
All performance setting menu-options work at runtime, but are processed over time rather than instantaneously, i.e. it takes 10-20 seconds till the new balance between buffered clouds and active clouds is reached after the slider is moved.<p>
The final options, weather pattern scales, deal with the long-range behaviour of the offline weather system. They are not parsed when the tile selection mode is set to <i>METAR</i>. The first slider controls the transition between different airmasses. In the default setting, the typical distance to encounter a different airmass when one flies in a 'High-pressure-core' tile is 200 km. The airmass slider allows to vary this distance between 200 and 800 km. <p>
Inside each tile, there are typically 4-8 different basic cloud patterns (distribution of layers) modelled, with a random pattern selected for each new tile of the given airmass classification. The <i>cloud patterns</i> slider adjusts the probability that the same cloud configuration is kept as long as a tile of the same airmass type is generated. Selecting a large scale in essence means that the theme of clouds will remain similar within each airmass, a small scale allows for more variation.<p>
<h2>4. Cloud models</h2>
The package contains a number of different cloud models, both static ones for Cirrus and Cirrocumulus clouds as well as rotated ones for Altocumulus, Cirrostratus, Cumulus, Cumulonimbus, Stratus and Nimbostratus cloudlet models. Neither the cloud textures, nor the models nor the transformations are perfected, and any aspect can be improved, albeit at the cost of performance consumption.<p>
The package contains a number of different cloud models, both static ones for Cirrus and Cirrocumulus clouds as well as rotated ones for Altocumulus, Cirrostratus, Cumulus, Cumulonimbus, Stratus and Nimbostratus cloudlet models. Thin high-altitude haze such as characteristic for Cirrostratus or Altostratus clouds is approximated by colouring the skydome as a function of altitude. Neither the cloud textures, nor the models nor the transformations are perfected, and any aspect can be improved, albeit at the cost of performance consumption.<p>
Static clouds project textures onto curved sheets into the sky. The advantage of the technique is that cloud layers consisting of thousands of cloudlets with different sizes can be modelled. However, the sheets do not look equally well from all perspectives and somewhat unrealistic from close up.<p>
<center>
<img src="clouds-static.jpg">
<img src="img/clouds-static.jpg">
</center><p>
Rotated cloud models have the advantage that they look much better from close up and hardly unrealistic from any perspective, but the size distribution of cloudlets is somewhat restricted and they use a lot more performance than static clouds.<p>
<center>
<img src="clouds-detailed01.jpg">
<img src="img/clouds-detailed01.jpg">
</center><p>
These are rendered by different techniques. While the default Cumulus models consist of multiple layers rotated around the center of the model, the detailed Cumulus clouds consist of multiple (up to 24) individual cloudlets, rotating each around its own center, randomly distributed into a box with different texture types used for the cloud bottom. This not only improves the visual appearance, but also leads to a more realistic distribution of cloud sizes and shapes in the sky. In addition, when circling below the cloud (as done when soaring) the effect of the cloudlet rotation is less pronounced. The price to pay is that rendering detailed clouds costs more performance, so they may not be suitable for all systems.<p>
@ -205,7 +214,7 @@ These are rendered by different techniques. While the default Cumulus models co
More complex clouds are rendered in sandwitched layers of several different textures. An example are Cumulonimbus towers, which use diffuse textures on the bottom, changing to more structured textures in the upper part of the cloud. With up to 2000 cloudlets, skies with multiple thunderstorms may not render with sufficient framerates on every system.<p>
<center>
<img src="clouds-tropical02.jpg">
<img src="img/clouds-tropical02.jpg">
</center><p>
The general problem is finding a good balance between spending a lot of CPU time to make a single cloud model appear perfect, and the performance degradation that occurs if hundreds of clouds are placed in the sky. The basic aim is to provide realistic appearance for clouds from a standard view position (in cockpit looking forward), to retain acceptable appearance from other positions and to allow large cloud layers.<p>
@ -228,6 +237,14 @@ While the station concept is designed to support easy connection with weather up
Technically, the structure of the interpolation system means that while it is running, neither setting weather parameters in the GUI menu nor changing visibility using the <i>z</i>-key will have an effect - any setting made there will be overwritten by the interpolation loop periodically, and local weather needs to be stopped before such changes have an effect. <p>
In addition to true weather parameters, Local Weather 1.18 also has a model for the light propagation in the atmosphere which is handled by the same interpolation routines. This model determines the amount of light reaching the current altitude and the amount of thin haze above the current position. The first property affects to what color distant objects fade - when there is lots of light available, faraway objects appear white, however in the presence of cloud layers casting shadows, distant objects fade into dark shapes. The following screenshot illustrates the effect:
<center>
<img src="img/lightning-gloom.jpg">
</center><p>
The second property is used to simulate diffuse structureless Cirrostratus and Altostratus clouds. Details of the modelling are given in the appendix.
<h3>Weather parameters in effect volumes</h3>
Effect volumes are 3-dim regions in space in which the weather is not set by the interpolation system but by a specific prescription. This can be to either set a parameter to a constant value inside the volume, or to specify it as a function inside the volume. Effect volume settings always override information from the interpolation subsystem.<p>
@ -235,7 +252,7 @@ Effect volumes are 3-dim regions in space in which the weather is not set by the
Effect volumes may be nested, and they may overlap. The rules determining their behaviour can be summarized as follows: 1) there is no cross-talk between weather parameters, i.e. an effect volume specifying visibility is never influenced by one specifying pressure, regardless if they overlap or not. 2) when an effect volume is entered, its settings overwrite all previous settings 3) when an effect volume is left, the settings are restored to what the interpolation routine specifies if no other effect volumes influence the weather parameter, to the values as they were on entering the volume when the number of active volumes has not changed between entering and leaving the volume (i.e. when volumes are nested) or nothing happens if the number of active volumes has increased (i.e. when volumes form a chain). This is illustrated in the following figure:<p>
<center>
<img src="effect_nesting.gif">
<img src="img/effect_nesting.gif">
</center><p>
Volumes 2 and 3 are nested inside volume 1, therefore all settings of 2 overwrite those of 1 when 2 is entered and are restored to 1 when region 2 is left directly into 1. Region 4 is a chain, and as such ill defined: When one leaves 2 into 4, the settings of volume 3 are used (because later definitions overwrite earlier ones). When one now leaves 4 into 3 (and hence leaves 2), it would be wrong to restore to the values on entry of 2 (i.e. the properties of 1), as 3 is still active. But the active volume count has changed, so leaving 4 into 3 does not lead to any changes. The reason why 4 is still ill-defined is that what weather is encountered in 4 depends on the direction from which it is entered - when it is entered from 2, it has the properties of 3, whereas when it is entered from 3, it has the properties of 2. <p>
@ -246,7 +263,7 @@ Effect volumes are always specified between a minimum and a maximum altitude, an
where <i>geometry</i> is a flag (1: circular, 2: elliptical and 3: rectangular), <i>lat</i> and <i>lon</i> are the latitude and longitude, <i>r1</i> and <i>r2</i> are the two size parameters for the elliptic or rectangular shape (for the circular shape, only the first is used), <i>phi</i> is the rotation angle of the shape (not used for circular shape), <i>alt_low</i> and <i>alt_high</i> are the altitude boundaries, <i>vis, rain, snow, turb</i> and <i>lift</i> are weather parameters which are either set to the value they should assume, or to -1 if they are not to be used, or to -2 if a function instead of a parameter is to be used and -3 if a function for wave lift is used. Since thermal lift can be set to negative values in a sink, a separate flag is provided in this case. <i>sat</i> finally determines the light saturation, a parameter between 0 (dark) and 1 (normal light) - it can be used to dim the light beneath cloud layers (which is not done automatically as objects don't cast shades in Flightgear, and given that most cloud models are rotated, their shade would look rather odd on any case).<p>
In version 1.0, thermal lift and wave lift are implemented by function (wave lift is not yet automatically placed, but can be easily from Nasal). There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.<p>
In version 1.18, thermal lift and wave lift are implemented by function (wave lift is not yet automatically placed, but can be easily from Nasal). There is no easy way to implement any weather parameter by function in an effect volume, as this requires some amount of Nasal coding.<p>
Both thermal lift and saturation require a more recent version of Flightgear than 2.0.0 in order to take effect.<p>
@ -325,7 +342,13 @@ The first important call sets up the conditions to be interpolated:<p>
<i>set_weather_station(latitude, longitude, visibility-m, temperature-degc, dewpoint-degc, pressure-sea-level-inhg);</i><p>
The cloud placement calls should be reasonably familiar, as they closely resemble the structure by which they are accessible from the 'Local Weather' menu.<p>
The atmosphere light propagation needs to be prepared as well by a call<p>
<i>set_atmosphere_ipoint(latitude, longitude, vis_aloft, vis_alt_aloft, vis_overcast, overcast,overcast_alt_low, overcast_alt_high, scattering, scattering_alt_low, scattering_alt_high);</i><p>
The meaning of these parameters is as follows: Visibility is linearly interpolated in altitude between several altitudes: the ground value is given in the weather station call. <i>vis_aloft</i> determines the visibility after passing the lowest inversion layer (i.e. usually the lowest cloud layer altitude) where the visibility suddenly increases. The altitude of the transition is given by <i>vis_alt_aloft</i>. The visibility higher up is determined by high-altitude haze, i.e. it takes the value <i>vis_overcast</i> at the position <i>overcast_alt_high</i> (the upper edge of the haze layer) and increases with constant rate from there. The amount of high-altitude haze is given by <i>overcase</i>, a number between 0 and 1, and the position of the layer is determined by <i>overcast_alt_low</i> and <i>overcast_alt_high</i>. Above the second value, the skydome is no longer coloured. Finally, <i>scattering</i> determines the amount of light reaching the surface (between 0 and 1, reasonable values range from 0.5 to 1) and the following altitudes specify where the layers casting shadow are found - there is always the full amount of light available above <i>scattering_alt_high</i>. See the appendix for details of the model.<p>
The cloud placement calls should be reasonably familiar, as they closely resemble the structure by which they are accessible from the 'Local Weather (Test)' menu.<p>
If the cloud layer has an orientation, then all placement coordinates should be rotated accordingly. Similarly, each placement call should include the altitude offset. Take care to nest effect volumes properly where possible, otherwise undesired effects might occur...<p>
@ -360,9 +383,9 @@ With default settings, the local weather package generates a 40x40 km weather ti
<li> Some cloud textures have artefacts, rain textures have too sharp boundaries and in general some things could look better. Please don't complain, but rather get me good photographs of the sky, cloud texture files or create better AC3D cloud models. I will eventually improve texture quality, but it's not high up in the to-do list, and the cloud model files are openly accessible for anyone with an editor.<p>
<li> Rain and snow may not start properly. For me, rain is only generated when I switch 'Shader effects' on and off in the menu on startup, otherwise neither moving the menu slider nor entering an effect volume generate rain. This seems to be a bug of some Flightgear versions, not of the local weather system.<p>
<li> [2.0.0] Rain and snow may not start properly. For me, rain is only generated when I switch 'Shader effects' on and off in the menu on startup, otherwise neither moving the menu slider nor entering an effect volume generate rain. This seems to be a bug of some Flightgear versions, not of the local weather system.<p>
<li> Especially with multiple overcast layers and weather fronts, loading and unloading weather tiles may take a long time / cause severe drops in framerate. It seems that a dual core processor is very valuable with this particular issue - try switching multiprocessor support on if needed, otherwise please refer to performance tuning to solve such problems. In general, overcast layers and tropical weather tiles do require a system on the high end of the performance scale to render properly.<p>
<li> [2.0.0] Especially with multiple overcast layers and weather fronts, loading and unloading weather tiles may take a long time / cause severe drops in framerate. It seems that a dual core processor is very valuable with this particular issue - try switching multiprocessor support on if needed, otherwise please refer to performance tuning to solve such problems. In more recent versions of Flightgear, an activated texture cache improves performance dramatically. In general, overcast layers and tropical weather tiles do require a system on the high end of the performance scale to render properly.<p>
<li> The local weather package is able to occasionally trigger errors like 'Warning:: Picked up error in TriangleIntersect'. These seem to be a problem in the core Flightgear code - the package does nothing but placing normal (rather simple) AC3D models into the scenery.<p>
@ -370,7 +393,7 @@ With default settings, the local weather package generates a 40x40 km weather ti
<li> [2.0.0] To smooth out changes in wind settings, rather than producing a sudden gust, the wind is changed over about 1 second. In sharp wind gradients, this produces a series of ripples like turbulence. This is actually a realistic behaviour in this situation and hence left as it is. Some systems seem to take an unreasonable effort to reinit the environment (as must be done to set wind) - here the function call <i>setWindSmoothly()</i> in <i>local_weather.nas</i> should perhaps be replace by <i>setWind</i> to ease the load.<p>
<li> Large tile creation distances can cause problems in low visibility weather, because Flightgear loads terrain only if it is within visual range. Thus, trying to sample the terrain for a tile 55 km away in 8 km visibility doesn't work because the terrain elevation and altitude is not known. This may cause improper placement of clouds - chiefly convective clouds, but also layered clouds may not appear on the proper altitude. Currently, there is a limit which restricts tile loading range to 3 times the visibility, but presumably a better solution can be found.<p>
<li> [2.0.0] Large tile creation distances can cause problems in low visibility weather, because Flightgear loads terrain only if it is within visual range. Thus, trying to sample the terrain for a tile 55 km away in 8 km visibility doesn't work because the terrain elevation and altitude is not known. This may cause improper placement of clouds - chiefly convective clouds, but also layered clouds may not appear on the proper altitude. Currently, there is a limit which restricts tile loading range to 3 times the visibility, but presumably a better solution can be found.<p>
<li> Using the 'aloft interpolated' wind option, it is possible to turn the wind direction sharply over a small distance (for example, one may turn the wind by 90 degrees from one tile to the next). Such sharp wind changes are (in most situations) unphysical, and they may cause problems for local weather because they rotate the coordinate system to a degree that the neighbouring tile may not be identified correctly. In essence, the system may not generate new tiles because the nearest tile is still the last generated one. The system largely tries to detect the problem and compensate, but occasionally this may be an issue.<p>
@ -379,6 +402,10 @@ With default settings, the local weather package generates a 40x40 km weather ti
<li> [2.0.0] No support for live weather data.<p>
<li> [2.0.0] No change of light saturation. <p>
<li> Currently, the light conditions need to be changed globally. This works fine for an 8/8 overcast layer, as there is little light on the ground, the light increases during the layer transition and is full above the layer. However, the effect is less than perfect for broken layers, as one can observe the ground during descent getting darker. A full solution to the problem would be (computationally rather demanding) individual cloud shadows rather than an average shading of the whole terrain, but as it stands, this is currently not feasible.
</ul>
<h2><a name="appendix_A">Appendix A: An introduction to the algorithms</a></h2>
@ -404,37 +431,37 @@ The following series of pictures, taken over KLSV (Nellis AFB, Las Vegas) illust
At 7:00 am, the thermal activity is weak, and there is no lift available in thermals yet.<p>
<center>
<img src="./KLSV-7_00.jpg">
<img src="img/./KLSV-7_00.jpg">
</center><p>
Some activity starts around 10:00 am the average available lift is 0.3 m/s, the more active clouds tend to be above city terrain.
<center>
<img src="KLSV-10_00.jpg">
<img src="img/KLSV-10_00.jpg">
</center><p>
At 12:00 noon, the maximal cloud number is reached. The average available lift is 1 m/s, in peaks up to 2 m/s.
<center>
<img src="KLSV-12_00.jpg">
<img src="img/KLSV-12_00.jpg">
</center><p>
The maximum of lift strength is reached close to 15:00 pm. The average lift is now 1.5 m/s, in peaks up to 3 m/s, and the strong convection leads to beginning overdevelopment, some clouds reach beyond the first inversion layer and tower higher up. At this point, the clouds may also overdevelop into a thunderstorm (which is not modelled explicitly by the convective algorithm as it requires somewhat different modelling, but is taken into account in the weather tiles).<p>
<center>
<img src="KLSV-15_00.jpg">
<img src="img/KLSV-15_00.jpg">
</center><p>
At 17:30 pm, the lift is still strong, 1.5 m/s on average and 2.5 m/s in peaks, but compared with the situation at noon, there are fewer clouds with stronger lift.<p>
<center>
<img src="KLSV-17_30.jpg">
<img src="img/KLSV-17_30.jpg">
</center><p>
At sunset around 19:00 pm, the number of clouds decreases quickly, but there is still a lot of residual thermal energy (the ground has not cooled down yet), therefore thermal lift of on average 1 m/s is still available even without solar energy input.
<center>
<img src="KLSV-19_00.jpg">
<img src="img/KLSV-19_00.jpg">
</center><p>
While not accurate in every respect, the model works fairly well to reproduce the actual time dependence of convective clouds and thermal lift during the day.<p>
@ -452,7 +479,7 @@ A cloud field is initialized with fractional lifetimes randomly distributed betw
The model of the distribution of lift inside a thermal is quite complex. <p>
<center>
<img src="thermal_lift.gif">
<img src="img/thermal_lift.gif">
</center><p>
Vertically, is is characterized in addition to height and radius by two parameters, 'coning' and 'shaping', which make it cone-shaped and wasp-waisted. From zero to 200 m above ground, the lift is smoothly fading in, above the cloudbase it is smoothly faded out to zero at 10% above the nominal altitude. Horizontally, there is an outer ring where the air becomes turbulent, followed by a region of sink which in turn is followed by the inner core of lift.<p>
@ -460,7 +487,7 @@ Vertically, is is characterized in addition to height and radius by two paramete
The distribution of lift and sink is time dependent.
<center>
<img src="thermal_lift_time.gif">
<img src="img/thermal_lift_time.gif">
</center><p>
In a young thermal, lift starts to develop from the ground, sink is initially absent. When the lift reaches the cloudbase, sink starts to develop from the ground and rises up as well. Only in a mature thermal are sink and lift in equilibrium. When the thermal starts to decay, lift initially decays from the ground upward, till it reaches the cloudbase. At this time the cap cloud dissolves. For a time there is a residual distribution of sink decaying from bottom to top till the thermal evolution is over and the thermal (and the associate turbulence field) is removed.<p>
@ -474,7 +501,7 @@ In nature, what determines the altitude of various clouds are the properties of
In the algorithm, various proxies for the structure of air layers and hence the condensation altitude are used. It is assumed that air layers must follow the general slope of the terrain (because there is nowhere else to go), but can (at least to some degree) flow around isolated obstacles. To get the general layout of the terrain, the algorithm first samples the altitude of an 80x80 km square around the 40x40 weather tile to be created. The choice of a larger sampling area reduces the sensitvity of the outcome to purely local terrain features and prevent pronounced transitions from one tile to the next. The result of this sampling is a distribution of probability to find the terrain at a given altitude:<p>
<center>
<img src="terrain1.jpg">
<img src="img/terrain1.jpg">
</center><p>
For instance, the terrain around Geneva is mostly flat around 1000 ft (where the peak of the distribution lies) with some mountains up to 4500 ft nearby. Based on such distributions, the algorithm next determines the minimum altitude <i>alt_min</i>, the maximum altitude <i>alt_max</i>, the altitude below which 20% of the terrain are found <i>alt_20</i> and the median altitude below which 50% of the terrain are found <i>alt_med</i>.<p>
@ -482,25 +509,25 @@ For instance, the terrain around Geneva is mostly flat around 1000 ft (where the
Cumulus clouds are always placed at a constant altitude above <i>alt_20</i>. This is done to ensure gorges and canyons do not provide a minimum in otherwise flat terrain so that clouds appear down in the gorge as opposed to on the rim where they would naturally occur. Basically, layers are assumed not to trace too fine structures in the terrain, so at least 20% of the terrain are required. In the test case of Grand Canyon, the algorithm correctly places the clouds at rim altitude rather than down in the canyon:
<center>
<img src="cloud_altitude_02.jpg">
<img src="img/cloud_altitude_02.jpg">
</center><p>
However, convective clouds are given some freedom to adjust to the terrain. The maximally possible upward shift is given by <i>alt_med - alt_20</i>. This is based on the notion that above <i>alt_med</i>, the terrain is not a significant factor any more because the air can simply flow around any obstacle. However, this maximal shift is not always used - if the cloud is placed far above the terrain in the first place, it would not follow the terrain much. Thus, a factor of <i>1000 ft / altitude above terrain</i>, required to be between 0 and 1, modifies the shift. As a result, a cloud layer placed high above the terrain has no sensitivity to terrain features. The result of this procedure is that clouds show some degree of following terrain elevation, as seen here in Grenoble<p>
<center>
<img src="cloud_altitude_03.jpg">
<img src="img/cloud_altitude_03.jpg">
</center><p>
but they do not follow all terrain features, especially not single isolated peaks as seen here at the example of Mt. Rainier:
<center>
<img src="cloud_altitude_01.jpg">
<img src="img/cloud_altitude_01.jpg">
</center><p>
Finally, layered clouds have essentially no capability to shift with terrain elevation. Moreover, they are caused by large-scale weather processes, hence they do not usually shift upward over even large mountain massives. Currently, the model places them at <i>0.5 * (alt_min + alt_20)</i> base altitude in order to retain, even in mountains, the sensitivity to the flat terrain surrounding the massiv. usually this works well, but may have a problem with gorges in flat terrain. The following picture shows a Nimbostratus layer close to Grenoble:<p>
<center>
<img src="cloud_altitude_04.jpg">
<img src="img/cloud_altitude_04.jpg">
</center><p>
<h3>The offline large scale weather pattern</h3>
@ -510,7 +537,7 @@ The local weather package generates semi-plausible weather changes even in the a
Weather tiles are classified chiefly by air pressure. What is currently in the models are three classes for a low pressure system, four different classes for the system of weather fronts and airmasses spiralling into the low pressure system and three classes for a hugh pressure system. The general rule is that low pressure tiles contain layered clouds, overcast skies and rain whereas the high pressure tiles contain clear skies and few convective clouds. The topology assumed for the weather system is apparent in the following diagram:
<center>
<img src="weather_patterns.jpg">
<img src="img/weather_patterns.jpg">
</center><p>
A transition between classes is possible whenever a class has a common border. However, if a transition actually takes place is probabilistic. Typically, the probability not to make a transition is about 80%. Since changes are only triggered for weather tiles one is actually in, the average distance over which weather patterns persist is 160 km. An exception to this are fronts - weather front tiles trigger changes based on direction rather than probability, so a warmfront will always be a sequence of 4 tiles, a coldfront will always be a small-scale phenomenon crossed within 30 km.
@ -529,21 +556,35 @@ Realistically, the boundary layer should also depend on terrain coverage. Due to
Gusty winds are assumed to be a bounday layer phenomenon and faded out to zero at a multiple of the boundary layer thickness which is given by <i>base wind speed [kt]/10</i>, i.e. for 25 kt winds the gusts are absent for 2.5 times the bounday layer thickness.<p>
<h3>The altitude model of visibility</h3>
<h3>Light propagation in the atmosphere</h3>
Relatively low visibility (as reported by METAR) is usually confined to low altitudes, due to the lower density of the upper atmosphere and the reduced level of dust and water the visibility at airliner cruise altitude is frequently in excess of 100 km. This means that an altitude model of the visibility is needed.<p>
Local weather assumes that haze and dust are confined to the lowest air layer. As a proxy for the transition to a higher layer, the altitude of the lowest cloud layer (or for 9000 ft for clear skies) is used. The visibility always increases with altitude, but below the first layer transition it increases slowly at a rate of 0.2 m / ft in altitude gain. During the layer transition where the transition occurs during 1500 ft, the visibility increases rather rapidly at a rate of 5 m / ft, which means that above the lowest air layer the visibility is almost 10 km more than on the ground. The visibility then continues to grow at a rate of 1 m / ft towards the Stratosphere. In all likelihood, this dramatically underestimates the true visibility at high altitudes, but has been chosen to limit the impact on performance.<p>
The following schematics illustrates the essential features of the light propagation model:<p>
<center>
<img src="img/light_model.gif">
</center><p>
The parameters to be set are <i>overcast</i> (the amount of colouring of the skydome, from 0 (no haze) to 1 (completely opaque)), <i>visibility</i> and <i>light</i> (the amount of light available, determining the shade of faraway objects). The assumptions underlying the model are:
<ul>
<li> The visibility increases above the lowest inversion layer quite rapidly. It can then be manually set up to a high-altitude haze layer (to account for phenomena such as weather fronts where a low-visibility airmass may be above), but increases with constant rate higher up.
<li> The high-altitude visibilities are not realistic, but kept artificially low to avoid framerate problems connected with excessive terrain loading.
<li> For the amount of light, a single transition region can be specified. This usually is the lowest inversion layer, but doesn't have to - for multiple layers, a more democratic choice can be made. Above the transition region, the full amount of light is always assumed.
<li>Currently, a single high-altitude haze layer is supported, and it's transition altitude can be given. No haze is simulated above it's highest altitude.
</ul>
<h2>Credits</h2>
The model of a thermal has been developed by Patrice Poly. The shader code used to transform clouds is heavily based on prior work by Stuart Buchanan. Hard-coding of some features by Torsten Dreyer is greatly appreciated.<p>
The model of a thermal has been developed by Patrice Poly. The shader code used to transform clouds is heavily based on prior work by Stuart Buchanan. Hard-coding of some features by Torsten Dreyer, Thorsten Brehm and Erik Hofman is greatly appreciated.<p>
Thorsten Renk, March 2011
Thorsten Renk, June 2011
</body>
</html>
</body>
</html>

View file

@ -72,7 +72,7 @@ in detail below:
<nasal>
... optional; Nasal code
</nasal>
<interval>10</inteval> optional; run loop next in this many seconds
<interval>10</interval> optional; run loop next in this many seconds
</init> (default: 5); doesn't change global
interval
@ -100,7 +100,7 @@ in detail below:
... optional; Nasal code that is executed when the
</nasal> step is entered
<interval>10</inteval> optional; run loop next in this many seconds
<interval>10</interval> optional; run loop next in this many seconds
<error> optional; allowed several times
<message>..</message> optional; text displayed/spoken
@ -114,7 +114,7 @@ in detail below:
... optional; Nasal code that is executed when the
</nasal> error condition was fulfilled
<interval>10</inteval> optional; run loop next in this many seconds
<interval>10</interval> optional; run loop next in this many seconds
</error>
<exit> optional; defines when to leave this <step>
@ -341,8 +341,11 @@ view to a new view position/direction. All parameters are optional. If, for
example, only <field-of-view> is set, then the view will only zoom in -- the
direction and position will remain the same. This feature is meant for cockpit
tutorials, where the pilot's view is directed to some switch or instrument.
view-number can be used to switch between different views, i.e. to tower-view,
copilot view etc. Default view-number is 0 (captain's view).
<view>
<view-number>0</view-number> 0=captain's view, 1=copilot,...
<heading-offset-deg>20</heading-offset-deg> positive is left
<pitch-offset-deg>-4</pitch-offset-deg> positive is up
<roll-offset-deg>0</roll-offset-deg> positive is roll right

View file

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

View file

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View file

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

View file

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View file

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View file

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View file

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View file

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View file

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View file

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View file

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View file

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View file

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View file

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View file

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View file

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View file

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View file

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View file

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

BIN
Docs/img/light_model.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

BIN
Docs/img/menu4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View file

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -9,7 +9,8 @@
<material>
<color-mode-uniform>1</color-mode-uniform> <!-- DIFFUSE -->
</material>
<condition><use>/sim/rendering/shader-effects</use></condition>
<condition><use>/sim/rendering/shader-effects</use></condition>
<factor>1</factor>
</parameters>
<technique n="10">
<predicate>
@ -104,6 +105,11 @@
<type>float</type>
<value><use>condition</use></value>
</uniform>
<uniform>
<name>lightmap_factor</name>
<type>float</type>
<value><use>factor</use></value>
</uniform>
</pass>
</technique>
<technique n="11">

View file

@ -90,7 +90,7 @@ EXAMPLES: You can find examples of both usages in the Hunter and Lightning model
<internal-format>normalized</internal-format>
</texture>
<texture n="8">
<image>Aircraft/737-300/Models/Effects/733LH.ReflectionMap3.png</image>
<image>Aircraft/Generic/Effects/ReflectMaps/reflectmap.png</image>
<filter>linear-mipmap-linear</filter>
<wrap-s>repeat</wrap-s>
<wrap-t>repeat</wrap-t>

View file

@ -277,6 +277,11 @@
</turbulence>
<params>
<!--
valid values for jsbsim-turbulence-model:
ttNone, ttStandard, ttBerndt, ttCulp, ttMilspec, ttTustin
-->
<jsbsim-turbulence-model type="string" userarchive="y">ttMilspec</jsbsim-turbulence-model>
<metar-max-age-min type="long">240</metar-max-age-min>
<metar-updates-environment type="bool">true</metar-updates-environment>
<metar-updates-winds-aloft type="bool">true</metar-updates-winds-aloft>
@ -314,4 +319,11 @@
<data type="string"/>
</wildfire>
<!-- create on instance of the terrains-sampler but start disabled -->
<terrain>
<area>
<enabled type="bool">false</enabled>
</area>
</terrain>
</PropertyList>

View file

@ -0,0 +1,106 @@
<PropertyList>
<config>
<asymmetric-buffering-flag type="bool" userarchive="y">0</asymmetric-buffering-flag>
<asymmetric-buffering-reduction type="double" userarchive="y">0.3</asymmetric-buffering-reduction>
<asymmetric-buffering-angle-deg type="double" userarchive="y">90.0</asymmetric-buffering-angle-deg>
<distance-to-load-tile-m type="double" userarchive="y">39000.0</distance-to-load-tile-m>
<distance-to-remove-tile-m type="double" userarchive="y">39500.0</distance-to-remove-tile-m>
<detailed-clouds-flag type="bool" userarchive="y">1</detailed-clouds-flag>
<dynamics-flag type="bool" userarchive="y">0</dynamics-flag>
<thermal-properties type="double" userarchive="y">1.0</thermal-properties>
<wind-model type="string" userarchive="y">constant</wind-model>
<buffer-flag type="bool" userarchive="y">1</buffer-flag>
<asymmetric-reduction type="double" userarchive="y">0.7</asymmetric-reduction>
<clouds-visible-range-m type="double" userarchive="y">30000.0</clouds-visible-range-m>
<clouds-in-dynamics-loop type="int" userarchive="y">250</clouds-in-dynamics-loop>
<debug-output-flag type="bool" userarchive="y">0</debug-output-flag>
<generate-thermal-lift-flag type="bool" userarchive="y">0</generate-thermal-lift-flag>
<dynamical-convection-flag type="bool" userarchive="y">0</dynamical-convection-flag>
<thread-flag type="bool" userarchive="n">1</thread-flag>
<presampling-flag type="bool" userarchive="y">1</presampling-flag>
<fps-control-flag type="bool" userarchive="y">0</fps-control-flag>
<target-framerate type="double" userarchive="y">25.0</target-framerate>
<large-scale-persistence type="double" userarchive="y">1.0</large-scale-persistence>
<small-scale-persistence type="double" userarchive="y">0.0</small-scale-persistence>
</config>
<tmp>
<tile-management type="string" userarchive="y">realistic weather</tile-management>
<tile-type type="string" userarchive="y">High-pressure</tile-type>
<tile-orientation-deg type="double" userarchive="y">260.0</tile-orientation-deg>
<windspeed-kt type="double" userarchive="y">8.0</windspeed-kt>
<gust-frequency-hz type="double" userarchive="n">0.0</gust-frequency-hz>
<gust-relative-strength type="double" userarchive="n">0.0</gust-relative-strength>
<gust-angular-variation-deg type="double" userarchive="n">0.0</gust-angular-variation-deg>
<tile-alt-offset-ft type="double" userarchive="n">0.0</tile-alt-offset-ft>
<asymmetric-tile-loading-flag type="bool" userarchive="y">0</asymmetric-tile-loading-flag>
<FL0-wind-from-heading-deg type="double" userarchive="y">260.0</FL0-wind-from-heading-deg>
<FL0-windspeed-kt type="double" userarchive="y">8.0</FL0-windspeed-kt>
<FL50-wind-from-heading-deg type="double" userarchive="y">262.0</FL50-wind-from-heading-deg>
<FL50-windspeed-kt type="double" userarchive="y">11.0</FL50-windspeed-kt>
<FL100-wind-from-heading-deg type="double" userarchive="y">264.0</FL100-wind-from-heading-deg>
<FL100-windspeed-kt type="double" userarchive="y">16.0</FL100-windspeed-kt>
<FL180-wind-from-heading-deg type="double" userarchive="y">265.0</FL180-wind-from-heading-deg>
<FL180-windspeed-kt type="double" userarchive="y">24.0</FL180-windspeed-kt>
<FL240-wind-from-heading-deg type="double" userarchive="y">269.0</FL240-wind-from-heading-deg>
<FL240-windspeed-kt type="double" userarchive="y">35.0</FL240-windspeed-kt>
<FL300-wind-from-heading-deg type="double" userarchive="y">273.0</FL300-wind-from-heading-deg>
<FL300-windspeed-kt type="double" userarchive="y">45.0</FL300-windspeed-kt>
<FL340-wind-from-heading-deg type="double" userarchive="y">274.0</FL340-wind-from-heading-deg>
<FL340-windspeed-kt type="double" userarchive="y">50.0</FL340-windspeed-kt>
<FL390-wind-from-heading-deg type="double" userarchive="y">273.0</FL390-wind-from-heading-deg>
<FL390-windspeed-kt type="double" userarchive="y">56.0</FL390-windspeed-kt>
<FL450-wind-from-heading-deg type="double" userarchive="y">272.0</FL450-wind-from-heading-deg>
<FL450-windspeed-kt type="double" userarchive="y">65.0</FL450-windspeed-kt>
<cloud-type type="string" userarchive="n">Altocumulus</cloud-type>
<alt type="double" userarchive="n">12000.0</alt>
<nx type="int" userarchive="n">5</nx>
<xoffset type="double" userarchive="n">800.0</xoffset>
<xedge type="double" userarchive="n">0.2</xedge>
<ny type="int" userarchive="n">15</ny>
<yoffset type="double" userarchive="n">800.0</yoffset>
<yedge type="double" userarchive="n">0.2</yedge>
<dir type="double" userarchive="n">0.0</dir>
<tri type="double" userarchive="n">1.0</tri>
<rnd-pos-x type="double" userarchive="n">400.0</rnd-pos-x>
<rnd-pos-y type="double" userarchive="n">400.0</rnd-pos-y>
<rnd-alt type="double" userarchive="n">300.0</rnd-alt>
<conv-strength type="double" userarchive="n">1.0</conv-strength>
<conv-size type="double" userarchive="n">15.0</conv-size>
<conv-alt type="double" userarchive="n">2000.0</conv-alt>
<bar-alt type="double" userarchive="n">3500.0</bar-alt>
<bar-n type="int" userarchive="n">150</bar-n>
<bar-dir type="double" userarchive="n">0.0</bar-dir>
<bar-dist type="double" userarchive="n">5.0</bar-dist>
<bar-size type="double" userarchive="n">10.0</bar-size>
<scloud-type type="string" userarchive="n">Altocumulus</scloud-type>
<scloud-subtype type="string" userarchive="n">small</scloud-subtype>
<scloud-alt type="double" userarchive="n">5000.0</scloud-alt>
<scloud-dir type="double" userarchive="n">0.0</scloud-dir>
<layer-type type="string" userarchive="n">Nimbus</layer-type>
<layer-rx type="double" userarchive="n">10.0</layer-rx>
<layer-ry type="double" userarchive="n">10.0</layer-ry>
<layer-phi type="double" userarchive="n">0.0</layer-phi>
<layer-alt type="double" userarchive="n">3000.0</layer-alt>
<layer-thickness type="double" userarchive="n">500.0</layer-thickness>
<layer-density type="double" userarchive="n">1.0</layer-density>
<layer-edge type="double" userarchive="n">10.0</layer-edge>
<layer-rain-flag type="bool" userarchive="n">1</layer-rain-flag>
<layer-rain-density type="double" userarchive="n">1.0</layer-rain-density>
<box-x-m type="double" userarchive="n">600.0</box-x-m>
<box-y-m type="double" userarchive="n">600.0</box-y-m>
<box-alt-ft type="double" userarchive="n">300.0</box-alt-ft>
<box-n type="int" userarchive="n">10</box-n>
<box-core-fraction type="double" userarchive="n">0.4</box-core-fraction>
<box-core-offset type="double" userarchive="n">0.2</box-core-offset>
<box-core-height type="double" userarchive="n">1.4</box-core-height>
<box-core-n type="int" userarchive="n">3</box-core-n>
<box-bottom-fraction type="double" userarchive="n">0.9</box-bottom-fraction>
<box-bottom-thickness type="double" userarchive="n">0.2</box-bottom-thickness>
<box-bottom-n type="int" userarchive="n">12</box-bottom-n>
</tmp>
</PropertyList>

View file

@ -161,7 +161,7 @@ ________________________________________________________________________________
<desc>Throttle</desc>
<number>
<unix>3</unix>
<mac>2</mac>
<mac>3</mac>
<windows>2</windows>
</number>
<binding>

View file

@ -1,56 +1,190 @@
<?xml version="1.0"?>
<!--
Only a few stick controls have been mapped here:
+ "Rocker" switch: Rudder
+ Top rotary dial: Mixture
+ Bottom rotary dial: Prop Advance
+ Top stick hat: Elevator & Aileron trim
+ Bottom stick hat: View direction
+ Top throttle hat: Flaps & Rudder trim
+ Stick button "A": Gear toggle
+ Stick button "C": Reset view (hackish)
Linux/Windows/Mac Axis Numbers:
0 Roll (positive == right)
1 Pitch (positive == down/back/nose-up)
2/5/5 top "rotary dial" on the throttle (positive == CCW)
3 Rocker switch ("rudder" control) on the throttle (positive == right)
4/2/2 Throttle (positive == back/down/idle)
5/4/4 Bottom "rotary dial" on the throttle (positive == CW)
Strange this axis doesn't seem to exist on Mac OS X!
6/6/6 Lower right hat horizontal axis (positive == right)
7/7/7 Lower right hat vertical axis (positive == down (Mac positive is UP))
This is X52.xml, the joystick config file for the Saitek X52
USB stick and throttle.
Button Numbers (Identical b/w Linux/Windows/Mac):
0 Trigger
1 Stick top "A" switch
2 Stick top "B" switch
3 Stick top "launch/fire" switch
4 Throttle "D" switch
5 Throttle "mouse" switch (tiny black thumb button)
6 Stick "pinkie" switch
7 Stick front "C" switch
8 -+ left position ("M1")
9 +- Throttle "mode" 3-way switch: middle position ("M2")
10 -+ right position ("M3")
11 -+ left position
12 +- Throttle "Aux" 3-way switch: middle position
13 -+ right position
14 Upper left hat in "up" position
15 Upper left hat in "right" position
16 Upper left hat in "down" position
17 Upper left hat in "left" position
18 Throttle forefinger hat in "up/back" position
19 Throttle forefinger hat in "right" position
20 Throttle forefinger hat in "down/forward" position
21 Throttle forefinger hat in "left" position
22 Throttle thumb hat in "up" position
23 Throttle thumb hat in "right" position
24 Throttle thumb hat in "down" position
25 Throttle thumb hat in "left" position
This configuration is meant to be suitable for a generic single-engine
aircraft.
Here are some of the features of a real aircraft that we would like to
implement, and our "preferred" way of assigning them on the X52.
Flight Controls:
Aileron stick L/R
Elevator stick fwd/aft
Rudder stick twist (or pedals!)
Flaps T3/T4
Speedbrakes shift T3/T4
Pitch trim stick upper hat
Rudder trim stick upper hat
Aileron trim throttle rocker
Engine controls:
Throttle throttle
Mixture throttle lower rotary
Propeller RPM throttle thumb slider
Carb heat (alt air) T5/T6
Cowl Flaps shift T5/T6
Fuel tank select **not implemented**
Mag switch keyboard "{" and "}"
Starter keyboard "s"
Other important stuff:
gear handle up/down T1/T2
Overall brakes pinky trigger
L/R toe brakes keyboard dot and comma
Parking brakes keyboard capital-B
Push-to-talk button (A) is a good candidate
also keyboard "space"
Timer reset MultiFunc reset
Timer -> clock MultiFunc function
Autopilot disengage **not implemented**
Autopilot go-around **not implemented**
Electrical:
Master **not implemented**
Radio master **not implemented**
Landing lights **not implemented**
Rotating beacon **not implemented**
Strobes **not implemented**
Cabin lights **not implemented**
Instrument lights **not implemented**
Circuit breakers **not implemented**
Instruments:
Alternate static **not implemented**
Instruments on/off mouse
Instruments tuning mouse or popup dialog
Simulator and view control:
Pan L/R Tilt Up/Dn throttle lower hat
Shift PoV D-shift + throttle lower hat
Restore std view (C) button
Zoom out (B) button
Zoom in D-shift + (B) button
=============================================================
Here are the Unix axes observed by jsd using js_demo, and Windows axes
observed by Satia Lumbar. Mac axes are little more than guesses based
on reports of X45 behavior. An "x" means observed to have no effect
using js_demo. U/M/W means unix/mac/windows:
U/ M/ W Hardware Function Interpretation
======= ================= ==============
0/ 0/ 0 Stick yaw +=R Aileron
1/ 1/ 1 Stick pitch +=aft Elevator
2/ 2/ 2 Throttle +=aft Throttle
3/ 5/ 5 Throttle lower rotary +=cw Mixture (fwd = rich)
4/ 8/ 8x Throttle upper rotary +=cw [unassigned]
5/ 3/ 3 Stick twist +=R Rudder
6/ 4/ 4 Throttle thumb slider +=up Prop (fwd = high RPM)
7/ 6/ 6 Stick lower hat +=R Pan View Left/Right ... or shift
8/ 7/ 7 Stick lower hat +=down Tilt View Up/Down ... or shift
9/ 9/ 9x Green mouse +=up [Mouse???]
10/10/10x Green mouse +=away [Mouse???]
On the Windows platform, the assignment of axes 8, 9, and 10 as
given above is only a hint, suggesting how the axes /should/ be
assigned, if the lower-level implementation ever gets around to
assigning them.
In the meantime, I have maintained cross-platform compatibility by not
assigning any critical functions to axes that don't work on the
Windows platform.
=======================
Now for the buttons. Unix button numbers observed by jsd using
js_demo. (Others rumored to be identical on Unix/Mac/Windows.)
Before 1 Jan 2007, all versions of plib had a bug that affecting bits
32 and 33. The C and C++ language spec says that an out-of-range
shift (such as shifting a 32-bit word 32 or more places) produces an
/undefined/ result. It is really not a good practice to be generating
undefined results.
I observe that on /some/ systems, bit #32 creates a conflict with bit
#0, and bit #33 creates a conflict with bit #1. That is, they "wrap
around" the word boundary. This means that if you roll the gray trim
wheel, it fires the gun! If that weren't bad enough, this behavior is
not guaranteed from compiler to compiler or from hardware to hardware.
Remember, according to the C language spec, an out-of-range shift
produces an "undefined" result, so implementers can do anything they
want with it.
Bit Binary Hardware Function Interpretation
=== ========= ================= ====================
0 = 0000 0001 Trigger (see also #14)
0' (see also #32)
1 = 0000 0002 FIRE Button (guarded)
1' (see also #33)
2 = 0000 0004 (A) Button (should be push-to-talk)
3 = 0000 0008 (B) Button Expand field of view (shift: contract)
4 = 0000 0010 (C) Button Reset View
5 = 0000 0020 Pinky subtrigger Apply brakes
6 = 0000 0040 (D) Button D-Shift, modifies other functions
7 = 0000 0080 (E) Button
8 = 0000 0100 T1 Gear Handle Up
9 = 0000 0200 T2 Gear Handle Down
10 = 0000 0400 T3 Flaps retract 1 notch (shift: speed brakes extend)
11 = 0000 0800 T4 Flaps extend 1 notch (shift: speed brakes retract)
12 = 0000 1000 T5 Carb heat on (shift: cowl flaps close)
13 = 0000 2000 T6 Carb heat off (shift: cowl flaps open)
14 = 0000 4001 More Trigger (==> #0)
15 = 0000 8000 Yoke upper hat Fwd Elevator trim
16 = 0001 0000 Yoke upper hat R Rudder trim
17 = 0002 0000 Yoke upper hat Aft Elevator trim
18 = 0004 0000 Yoke upper hat L Rudder trim
19 = 0008 0000 Throttle rocker aft
20 = 0010 0000 Throttle rocker R Aileron trim
21 = 0020 0000 Throttle rocker fwd
22 = 0040 0000 Throttle rocker L Aileron trim
23 = 0080 0000 Mode Aft (green)
24 = 0100 0000 Mode Mid (pink)
25 = 0200 0000 Mode Fwd (red)
26 = 0400 0000 MultiFunc Function Timer -> clock again
27 = 0800 0000 MultiFunc Start/Stop ** Timer start/stop not implemented **
28 = 1000 0000 MultiFunc Reset Timer reset
29 = 2000 0000 (i) Button
30 = 4000 0000 Green mouse click
31 (not observed)
32 = 1 0000 0000 Gray Roller step fwd (see also bit #0)
33 = 2 0000 0000 Gray Roller step aft (see also bit #1)
== Notes:
*) Note that some joystick configurations specify square-law behavior
for some flight control axes (e.g. ailerons and/or rudder). This is
very unlike the behavior of flight controls on real aircraft.
Aileron authority should be mushy at low airspeeds and crisp at high
airspeeds ... but this should be part of the flight dynamics of the
aircraft. Building the correct behavior into the control stick
configuration is neither possible nor desirable.
*) There are some aircraft in the simulator fleet that have very
little roll damping and very little yaw damping. (You could also say
they have very little damping of the Dutch roll mode, but this is
merely a natural consequence of the roll and yaw issues, not really a
separate issue.)
Such aircraft are vastly easier to fly with auto-coordination turned
on. In my opinion, you want auto-coordination on for flying an ILS
partial panel, i.e. no DG and no AI.
For a crosswind landing, you need to have auto-coordination turned off.
That's why the switch to do that is on the yoke.
*) This configuration does not bind the parking brake to any button on
the joystick (or throttle). Using the keyboard (shift-B) seems
entirely satisfactory and more realistic than putting the /parking/
brake button on the stick.
*) In general, he HOTAS idea (hands on throttle and stick) should
apply to things you need /in flight/.
$Id$
-->
<PropertyList>
<name>Saitek X52</name>
@ -61,99 +195,37 @@ $Id$
<name>Saitek X52 Flight Control System</name>
<name>Saitek Saitek X52</name>
<name>Saitek Saitek X52 Flight Stick (USB)</name>
<name>Saitek Saitek X52 Flight Control Stick </name>
<name>Saitek Saitek X52 Flight Control Stick</name>
<name>Saitek Saitek X52 Flight Control System</name>
<axis n="0">
<axis> <!-- 0/0/0 -->
<desc>Aileron</desc>
<number>
<unix>0</unix>
<mac>0</mac>
<windows>0</windows>
</number>
<binding>
<command>property-scale</command>
<property>/controls/flight/aileron</property>
<squared type="bool">true</squared>
</binding>
</axis>
</axis>
<axis n="1">
<axis> <!-- 1/1/1 -->
<desc>Elevator</desc>
<number>
<unix>1</unix>
<mac>1</mac>
<windows>1</windows>
</number>
<binding>
<command>property-scale</command>
<property>/controls/flight/elevator</property>
<factor type="double">-1.0</factor>
<squared type="bool">true</squared>
</binding>
</axis>
</axis>
<!--<axis n="3">
<desc>Rudder</desc>
<binding>
<command>property-scale</command>
<property>/controls/flight/rudder</property>
</binding>
<binding>
<command>nasal</command>
<script><![CDATA[
# In mode 3 (taxiing), map the wheelbrakes
if(getprop("/input/joysticks/js[0]/saitek-x45-mode") == 3) {
val = cmdarg().getNode("setting").getValue();
setprop("/controls/gear/brake-left", 0);
setprop("/controls/gear/brake-right", 0);
if(val > 0) { setprop("/controls/gear/brake-right", val); }
else { setprop("/controls/gear/brake-left", -val); }
}
]]></script>
</binding>
</axis>-->
<!-- View Direction Hat -->
<axis n="6">
<number>
<unix>7</unix>
<mac>6</mac>
<windows>6</windows>
</number>
<desc>View Direction</desc>
<low>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>view.panViewDir(1)</script>
</binding>
</low>
<high>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>view.panViewDir(-1)</script>
</binding>
</high>
</axis>
<axis>
<desc>View Elevation</desc>
<number>
<unix>8</unix>
<mac>7</mac>
<windows>7</windows>
</number>
<low>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>view.panViewPitch(1)</script>
</binding>
</low>
<high>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>view.panViewPitch(-1)</script>
</binding>
</high>
</axis>
<axis>
<axis> <!-- 2/2/2 -->
<desc>Throttle</desc>
<number>
<unix>2</unix>
@ -164,9 +236,9 @@ $Id$
<command>nasal</command>
<script>controls.throttleAxis()</script>
</binding>
</axis>
</axis>
<axis>
<axis> <!-- 3/5/5 -->
<desc>Mixture</desc>
<number>
<unix>3</unix>
@ -175,14 +247,45 @@ $Id$
</number>
<binding>
<command>nasal</command>
<script>controls.mixtureAxis()</script>
<script>controls.mixtureAxis(-1)</script>
</binding>
</axis>
</axis>
<axis>
<desc>Propeller Advance</desc>
<!-- Alternate Prop RPM :: throttle upper rotary -->
<axis> <!-- 4/8/8 -->
<desc>unassigned (was: Propeller RPM)</desc>
<number>
<unix>4</unix>
<mac>8</mac>
<windows>8</windows>
</number>
<binding>
<command>nasal</command>
<script>
####controls.propellerAxis(-1)
</script>
</binding>
</axis>
<!-- Rudder :: stick twist -->
<axis> <!-- 5/3/3 -->
<desc>Rudder</desc>
<number>
<unix>5</unix>
<mac>3</mac>
<windows>3</windows>
</number>
<binding>
<command>property-scale</command>
<property>/controls/flight/rudder</property>
</binding>
</axis>
<!-- Propeller RPM :: throttle thumb slider -->
<axis> <!-- 6/4/4 -->
<desc>Propeller RPM</desc>
<number>
<unix>6</unix>
<mac>4</mac>
<windows>4</windows>
</number>
@ -190,18 +293,132 @@ $Id$
<command>nasal</command>
<script>controls.propellerAxis(-1)</script>
</binding>
</axis>
</axis>
<button n="4">
<desc>Reset View</desc>
<!-- View Direction :: stick lower hat -->
<axis> <!-- 7/6/6 -->
<desc>View Pan Left/Right (D-shift: move PoV)</desc>
<number>
<unix>7</unix>
<mac>6</mac>
<windows>6</windows>
</number>
<low>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>
if (getprop("/sim/gui/d-button")){
setprop("/sim/current-view/x-offset-m",
-0.01 + getprop("/sim/current-view/x-offset-m"));
} else {
view.panViewDir(1)
}
</script>
</binding>
</low>
<high>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>
if (getprop("/sim/gui/d-button")){
setprop("/sim/current-view/x-offset-m",
0.01 + getprop("/sim/current-view/x-offset-m"));
} else {
view.panViewDir(-1)
}
</script>
</binding>
</high>
</axis>
<axis> <!-- 8/7/7 -->
<desc>View Tilt Up/Down (D-shift: move PoV)</desc>
<number>
<unix>8</unix>
<mac>7</mac>
<windows>7</windows>
</number>
<low>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>
if (getprop("/sim/gui/d-button")){
setprop("/sim/current-view/y-offset-m",
0.01 + getprop("/sim/current-view/y-offset-m"));
} else {
view.panViewPitch(1)
}
</script>
</binding>
</low>
<high>
<repeatable>true</repeatable>
<binding>
<command>nasal</command>
<script>
if (getprop("/sim/gui/d-button")){
setprop("/sim/current-view/y-offset-m",
-0.01 + getprop("/sim/current-view/y-offset-m"));
} else {
view.panViewPitch(-1)
}
</script>
</binding>
</high>
</axis>
<!-- End of axes; now on to buttons -->
<button n="1">
<desc>(FIRE) : Auto-Coordination off (D-shift: on)</desc>
<binding>
<command>nasal</command>
<script>
setprop("/sim/auto-coordination", ! !getprop("/sim/gui/d-button"));
</script>
</binding>
</button>
<button n="3">
<desc>(B) : Zoom out i.e. increase field of view (D-shift: zoom in)</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
view.decrease(0.5) # zoom in
} else {
view.increase(0.5) # zoom out
}
</script>
</binding>
</button>
<button n="4">
<desc>(C) : Reset View</desc>
<binding>
<command>nasal</command>
<script>view.resetView()</script>
<script>
view.resetView(); # only resets tilt/pan/zoom:
# must reset x/y/z view point separately
vn = getprop("/sim/current-view/view-number");
conf = sprintf("/sim/view[%d]/config", vn);
foreach (parm ; ["x-offset-m", "y-offset-m", "z-offset-m"]) {
setprop("/sim/current-view/", parm, getprop(conf, parm));
}
</script>
</binding>
</button>
</button>
<!-- <button n="0">
<desc>Brakes</desc>
<!-- Main brakes (not parking brakes) :: pinky subtrigger -->
<button n="5">
<desc>(Pinky) : Brakes</desc>
<binding>
<command>nasal</command>
<script>controls.applyBrakes(1)</script>
@ -212,183 +429,266 @@ $Id$
<script>controls.applyBrakes(0)</script>
</binding>
</mod-up>
</button> -->
</button>
<!-- Gear down on button T2 -->
<button n="9">
<desc>Landing Gear Down</desc>
<!-- Shift :: (D) button -->
<button n="6">
<desc>(D) : Shift Key</desc>
<binding>
<command>nasal</command>
<script>controls.gearDown(1)</script>
<script>
setprop("/sim/gui/d-button", 1);
</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>controls.gearDown(0)</script>
<script>
setprop("/sim/gui/d-button", 0);
</script>
</binding>
</mod-up>
</button>
</button>
<!-- Gear up on button T1 -->
<button n="8">
<desc>Landing Gear Up</desc>
<binding>
<command>nasal</command>
<script>controls.gearDown(-1)</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>controls.gearDown(0)</script>
</binding>
</mod-up>
</button>
<!-- gear handle up/down :: button T1/T2 -->
<button n="8">
<desc>(T1) : Landing Gear Handle Up</desc>
<binding>
<command>nasal</command>
<script>
controls.gearDown(-1)
</script>
</binding>
</button>
<button n="9">
<desc>(T2) : Landing Gear Handle Down</desc>
<binding>
<command>nasal</command>
<script>
controls.gearDown(1)
</script>
</binding>
</button>
<!-- mode switch (buttons 23-25) -->
<button n="23">
<desc>Mode 1</desc>
<binding>
<command>nasal</command>
<script>setprop("/input/joysticks/js[0]/saitek-x52-mode", 1)</script>
</binding>
</button>
<button n="24">
<desc>Mode 2</desc>
<binding>
<command>nasal</command>
<script>setprop("/input/joysticks/js[0]/saitek-x52-mode", 2)</script>
</binding>
</button>
<button n="25">
<desc>Mode 3</desc>
<binding>
<command>nasal</command>
<script>setprop("/input/joysticks/js[0]/saitek-x52-mode", 3)</script>
</binding>
</button>
<!-- Trim (upper) hat on the stick -->
<button n="15">
<!-- Pitch trim :: stick upper hat Up/Dn -->
<button n="15">
<desc>Elevator trim down</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.elevatorTrim(0.5)</script>
<script>controls.elevatorTrim(0.6)</script>
</binding>
</button>
</button>
<button n="17">
<desc>Elevator trim up</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.elevatorTrim(-0.6)</script>
</binding>
</button>
<button n="16">
<!-- Rudder trim :: stick upper hat L/R -->
<button n="16">
<desc>(Throttle Rocker) : Rudder trim right</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.rudderTrim(1)</script>
</binding>
</button>
<button n="18">
<desc>(Throttle Rocker) : Rudder trim left</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.rudderTrim(-1)</script>
</binding>
</button>
<!-- Flaps (shift: Speed Brakes) :: T3/T4 -->
<button n="10">
<desc>(T3) : Decrease flaps (shift: Speed Brakes extend)</desc>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
setprop("/controls/flight/speedbrake", 1);
} else {
controls.flapsDown(-1)
}
</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
} else {
controls.flapsDown(0)
}
</script>
</binding>
</mod-up>
</button>
<button n="11">
<desc>(T4) : Increase flaps (shift: Speed Brakes retract)</desc>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
setprop("/controls/flight/speedbrake", 0);
} else {
controls.flapsDown(1)
}
</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
} else {
controls.flapsDown(0)
}
</script>
</binding>
</mod-up>
</button>
<!-- Carb Heat (shift: Cowl Flaps) :: T6/T5 -->
<button n="12">
<desc>(T5) : Carb Heat On (shift: Cowl Flaps Close)</desc>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
props.setAll("/controls/engines/engine", "cowl-flaps-norm", 1);
} else {
props.setAll("/controls/engines/engine", "carb-heat", 0);
}
</script>
</binding>
</button>
<button n="13">
<desc>(T6) : Carb Heat Off (shift: Cowl Flaps Open)</desc>
<binding>
<command>nasal</command>
<script>
if ( getprop("/sim/gui/d-button") ) {
props.setAll("/controls/engines/engine", "cowl-flaps-norm", 0);
} else {
props.setAll("/controls/engines/engine", "carb-heat", 1);
}
</script>
</binding>
</button>
<!-- Aileron trim :: throttle rocker -->
<button n="20">
<desc>Aileron trim right</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.aileronTrim(0.5)</script>
</binding>
</button>
<button n="17">
<desc>Elevator trim up</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.elevatorTrim(-0.5)</script>
</binding>
</button>
<button n="18">
</button>
<button n="22">
<desc>Aileron trim left</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.aileronTrim(-0.5)</script>
</binding>
</button>
</button>
<!-- Flap control on the T3/T4 -->
<button n="10">
<desc>Decrease flaps</desc>
<!-- Mode roller switch (buttons 23-25). -->
<!-- Just put it in the property tree, -->
<!-- in case somebody wants to look at it. -->
<button n="23">
<desc>Mode 1</desc>
<binding>
<command>nasal</command>
<script>controls.flapsDown(-1)</script>
<script>
setprop("/input/joysticks/js/saitek-x52-mode",1)
</script>
</binding>
<mod-up>
<binding>
</button>
<button n="24">
<desc>Mode 2</desc>
<binding>
<command>nasal</command>
<script>
setprop("/input/joysticks/js/saitek-x52-mode",2)
</script>
</binding>
</button>
<button n="25">
<desc>Mode 3</desc>
<binding>
<command>nasal</command>
<script>
setprop("/input/joysticks/js/saitek-x52-mode",3)
</script>
</binding>
</button>
<button n="26">
<desc>(MFD Func) : Stopwatch timer becomes clock again</desc>
<binding>
<command>nasal</command>
<script>
props.globals.getNode("/instrumentation/clock/offset-sec", 1).setValue(0);
</script>
</binding>
</button>
<button n="28">
<desc>(MFD Reset) : Stopwatch timer reset</desc>
<binding>
<command>nasal</command>
<script>
<!-- Note that the "indicated-sec" variable is a large number,
probably seconds since midnight ... *not* modulo 60. -->
ttt = props.globals.getNode("/instrumentation/clock/indicated-sec", 1).getValue();
node = props.globals.getNode("/instrumentation/clock/offset-sec", 1);
off = node.getValue();
node.setValue(off-ttt);
</script>
</binding>
</button>
<!-- (i) button :: unbound key -->
<!-- just put it in the property tree -->
<button n="29">
<desc>Mode 1</desc>
<binding>
<command>nasal</command>
<script>controls.flapsDown(0)</script>
</binding>
</mod-up>
</button>
<button n="11">
<desc>Increase flaps</desc>
<binding>
<command>nasal</command>
<script>controls.flapsDown(1)</script>
<script>
setprop("/input/joysticks/js/saitek-x52-i",1)
</script>
</binding>
<mod-up>
<binding>
<mod-up><binding>
<command>nasal</command>
<script>controls.flapsDown(0)</script>
</binding>
</mod-up>
</button>
<script>
setprop("/input/joysticks/js/saitek-x52-i",0)
</script>
</binding></mod-up>
</button>
<!-- Rudder trim on the flap hat -->
<button n="20">
<desc>Rudder trim right</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.rudderTrim(1)</script>
</binding>
</button>
<button n="22">
<desc>Rudder trim left</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.rudderTrim(-1)</script>
</binding>
</button>
<!-- Fire button -->
<button n="0">
<!-- Parking brake :: unbound function -->
<button n="666">
<desc>Toggle parking brake on or off</desc>
<binding>
<!--<command>nasal</command>
<script>controls.applyParkingBrake4444(1)</script>-->
<command>property-toggle</command>
<property>/controls/gear/brake-parking</property>
</binding>
</button>
<!-- Speed brake control on the T6/T5 -->
<button n="13">
<desc>Decrease speedbrake</desc>
<binding>
<command>property-assign</command>
<property>/controls/flight/speedbrake</property>
<value>0</value>
</binding>
</button>
<button n="12">
<desc>Increase speedbrake</desc>
<binding>
<command>property-assign</command>
<property>/controls/flight/speedbrake</property>
<value>1</value>
</binding>
</button>
</button>
</PropertyList>

View file

@ -0,0 +1,168 @@
<?xml version="1.0"?>
<!--
Bindings for THRUSTMASTER T.Flight Stick X based on presets from the constructor.
by Joffrey Paris
-->
<PropertyList>
<name type="string">T.Flight Stick X</name>
<axis n="0">
<desc>Aileron</desc>
<binding>
<command>property-scale</command>
<property>/controls/flight/aileron</property>
<squared type="bool">true</squared>
</binding>
</axis>
<axis n="1">
<desc>Elevator</desc>
<binding>
<command>property-scale</command>
<property>/controls/flight/elevator</property>
<factor type="double">-1.0</factor>
<squared type="bool">true</squared>
</binding>
</axis>
<axis n="2">
<desc>Increase/Reduce Throttle</desc>
<binding>
<command>nasal</command>
<script>controls.throttleAxis()</script>
</binding>
</axis>
<axis n="3">
<desc>Rudder Left/Right</desc>
<binding>
<command>property-scale</command>
<property>/controls/flight/rudder</property>
<factor type="double">1.0</factor>
</binding>
</axis>
<axis n="6">
<desc>View Direction</desc>
<low>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property>/sim/current-view/goal-heading-offset-deg</property>
<step type="double">2.0</step>
</binding>
</low>
<high>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property>/sim/current-view/goal-heading-offset-deg</property>
<step type="double">-2.0</step>
</binding>
</high>
</axis>
<axis n="7">
<desc>View Elevation</desc>
<low>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property>/sim/current-view/goal-pitch-offset-deg</property>
<step type="double">-2.0</step>
</binding>
</low>
<high>
<repeatable>true</repeatable>
<binding>
<command>property-adjust</command>
<property>/sim/current-view/goal-pitch-offset-deg</property>
<step type="double">2.0</step>
</binding>
</high>
</axis>
<button n="0">
<desc>Brakes</desc>
<binding>
<command>nasal</command>
<script>controls.applyBrakes(1)</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>controls.applyBrakes(0)</script>
</binding>
</mod-up>
</button>
<button n="1">
<desc>Change View</desc>
<binding>
<command>nasal</command>
<script>view.stepView(1)</script>
</binding>
</button>
<button n="2">
<desc>Trim Nose Up</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.elevatorTrim(1)</script>
</binding>
</button>
<button n="3">
<desc>Trim Nose Down</desc>
<repeatable type="bool">true</repeatable>
<binding>
<command>nasal</command>
<script>controls.elevatorTrim(-1)</script>
</binding>
</button>
<button n="4">
<desc>Extend Flaps Incrementally</desc>
<repeatable>false</repeatable>
<binding>
<command>nasal</command>
<script>controls.flapsDown(1)</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>controls.flapsDown(0)</script>
</binding>
</mod-up>
</button>
<button n="5">
<desc>Retract Flaps Incrementally</desc>
<repeatable>false</repeatable>
<binding>
<command>nasal</command>
<script>controls.flapsDown(-1)</script>
</binding>
<mod-up>
<binding>
<command>nasal</command>
<script>controls.flapsDown(0)</script>
</binding>
</mod-up>
</button>
<button n="7">
<desc>Landing gear Up/Down</desc>
<repeatable>false</repeatable>
<binding>
<command>nasal</command>
<script>controls.gearToggle()</script>
</binding>
</button>
</PropertyList>
<!-- end of T.Flight Stick X.xml -->

View file

@ -28,6 +28,7 @@
READ ALLOW $FG_ROOT/*
READ ALLOW $FG_HOME/*
READ ALLOW $FG_AIRCRAFT/*
READ ALLOW $FG_SCENERY/*
WRITE ALLOW /tmp/*.xml
WRITE ALLOW $FG_HOME/*.sav
@ -36,3 +37,4 @@ WRITE ALLOW $FG_HOME/Export/*
WRITE ALLOW $FG_HOME/state/*.xml
WRITE ALLOW $FG_HOME/aircraft-data/*.xml
WRITE ALLOW $FG_HOME/Wildfire/*.xml
WRITE ALLOW $FG_HOME/runtime-jetways/*.xml

View file

@ -17,8 +17,8 @@ _setlistener("/sim/signals/fdm-initialized", func {
var callsign = tanker.getNode("callsign").getValue();
if( callsign == nil ) continue;
if( string.match(callsign,"ballon*") ) {
tanker.getNode("position/latitude-deg",1).setDoubleValue( position.lat() );
tanker.getNode("position/longitude-deg",1).setDoubleValue( position.lon() );
tanker.getNode("position/latitude-deg",1).setDoubleValue( position.lat() - 0.002 );
tanker.getNode("position/longitude-deg",1).setDoubleValue( position.lon() - 0.002 );
tanker.getNode("position/altitude-ft", 1 ).setDoubleValue( position.alt() );
}
}

View file

@ -134,8 +134,9 @@ var attributes = func(p, verbose = 1) {
var W = p.getAttribute("trace-write") ? "W" : "";
var A = p.getAttribute("archive") ? "A" : "";
var U = p.getAttribute("userarchive") ? "U" : "";
var P = p.getAttribute("preserve") ? "P" : "";
var T = p.getAttribute("tied") ? "T" : "";
var attr = r ~ w ~ R ~ W ~ A ~ U ~ T;
var attr = r ~ w ~ R ~ W ~ A ~ U ~ P ~ T;
var type = "(" ~ p.getType();
if (size(attr))
type ~= ", " ~ attr;

View file

@ -135,7 +135,6 @@ _setlistener("/sim/signals/nasal-dir-initialized", func {
}
menuEnable("autopilot", isAutopilotMenuEnabled() );
menuEnable("multiplayer", multiplayer.is_active());
menuEnable("tutorial-start", size(props.globals.getNode("/sim/tutorials", 1).getChildren("tutorial")));
menuEnable("joystick-info", size(props.globals.getNode("/input/joysticks").getChildren("js")));
# frame-per-second display
@ -1224,7 +1223,7 @@ var common_aircraft_keys = {
{ name: "g/G", desc: "gear up/down" },
{ name: "h", desc: "cycle HUD (head up display)" },
{ name: "H", desc: "cycle HUD brightness" },
{ name: "i/Shift-i", desc: "normal/alternative HUD" },
#{ name: "i/Shift-i", desc: "normal/alternative HUD" },
#{ name: "j", desc: "decrease spoilers" },
#{ name: "k", desc: "increase spoilers" },
{ name: "l", desc: "toggle tail-wheel lock" },
@ -1254,3 +1253,41 @@ var common_aircraft_keys = {
{ name: "Shift-F8", desc: "scroll 2D panel right" },
],
};
_setlistener("/sim/signals/screenshot", func {
var path = getprop("/sim/paths/screenshot-last");
var button = { button: { legend: "Ok", default: 1, binding: { command: "dialog-close" }}};
var success= getprop("/sim/signals/screenshot");
if (success) {
popupTip("Screenshot written to '" ~ path ~ "'", 3);
} else {
popupTip("Error writing screenshot '" ~ path ~ "'", 600, button);
}
});
var terrasync_stalled = 0;
_setlistener("/sim/terrasync/stalled", func {
var stalled = getprop("/sim/terrasync/stalled");
if (stalled and !terrasync_stalled)
{
var button = { button: { legend: "Ok", default: 1, binding: { command: "dialog-close" }}};
popupTip("Scenery download stalled. Too many errors reported. See log output.", 600, button);
}
terrasync_stalled = stalled;
});
var do_welcome = 1;
_setlistener("/sim/signals/fdm-initialized", func {
var haveTutorials = size(props.globals.getNode("/sim/tutorials", 1).getChildren("tutorial"));
gui.menuEnable("tutorial-start", haveTutorials);
if (do_welcome and haveTutorials)
settimer(func { setprop("/sim/messages/copilot", "Welcome aboard! Need help? Use 'Help -> Tutorials'.");}, 5.0);
do_welcome = 0;
});
# load ATC chatter module on demand
setprop("/nasal/atc-chatter/enabled", getprop("/sim/sound/chatter/enabled"));
_setlistener("/sim/sound/chatter/enabled", func {
setprop("/nasal/atc-chatter/enabled", getprop("/sim/sound/chatter/enabled"));
});

View file

@ -81,6 +81,33 @@ var read_properties = func(path, target = nil) {
return fgcommand("loadxml", args) ? ret : nil;
}
# Load XML file in FlightGear's native <PropertyList> format.
# file will be located in the airport-scenery directories according to
# ICAO and filename, i,e in Airports/I/C/A/ICAO.filename.xml
# If the second, optional target parameter is set, then the properties
# are loaded to this node in the global property tree. Otherwise they
# are returned as a separate props.Node tree. Returns the data as a
# props.Node on success or nil on error.
#
# Usage: io.read_airport_properties(<icao>, <filename> [, <props.Node or property-path>]);
#
# Examples:
#
# var data = io.read_properties("KSFO", "rwyuse");
#
var read_airport_properties = func(icao, fname, target = nil) {
var args = props.Node.new({ filename: fname, icao:icao });
if (target == nil) {
var ret = args.getNode("data", 1);
} elsif (isa(target, props.Node)) {
args.getNode("targetnode", 1).setValue(target.getPath());
var ret = target;
} else {
args.getNode("targetnode", 1).setValue(target);
var ret = props.globals.getNode(target, 1);
}
return fgcommand("loadxml", args) ? ret : nil;
}
# Write XML file in FlightGear's native <PropertyList> format.
# Returns the filename on success or nil on error. If the source
@ -245,6 +272,14 @@ _setlistener("/sim/signals/nasal-dir-initialized", func {
pattern = c.getValue() ~ "/" ~ p;
append(rules, [pattern, allow]);
printlog("info", "IORules: appending ", pattern);
}
} elsif (substr(pattern, 0, 12) == "$FG_SCENERY/") {
var p = substr(pattern, 12);
var sim = props.globals.getNode("/sim");
foreach (var c; sim.getChildren("fg-scenery")) {
pattern = c.getValue() ~ "/" ~ p;
append(rules, [pattern, allow]);
printlog("info", "IORules: appending ", pattern);
}
} else {
if (substr(pattern, 0, 9) == "$FG_ROOT/")

902
Nasal/jetways/jetways.nas Normal file
View file

@ -0,0 +1,902 @@
###############################################################################
##
## Animated Jetway System. Spawns and manages interactive jetway models.
##
## Copyright (C) 2011 Ryan Miller
## This file is licensed under the GPL license version 2 or later.
##
###############################################################################
###############################################################################
# (See http://wiki.flightgear.org/Howto:_Animated_jetways)
#
# Special jetway definition files located in $FG_ROOT/Airports/Jetways/XXXX.xml
# for each airport are loaded when the user's aircraft is within 50 nm of the
# airport. The script dynamically generates runtime model files, writes them to
# $FG_ROOT/Models/Airport/Jetway/runtimeX.xml, and places them into the
# simulator using the model manager.
#
# Different jetway models can be defined and are placed under
# $FG_ROOT/Models/Airport/Jetway/XXX.xml.
#
# Jetways can be extended/retracted independently either by user operation or
# by automatic extension for AI models and multiplayer aircraft.
#
# UTILITY FUNCTIONS
# -----------------
#
# print_debug(<message>) - prints debug messages
# <message> - message to print
#
# print_error(<messsage>) - prints error messages
# <message> - error to print
#
# alert(<message>) - displays an alert message in-sim
# <message> - the message
#
# normdeg(<angle>) - normalizes angle measures between -180° and 180°
# <angle> - angle to normalize
#
# remove(<vector>, <item>) - removes an element from a vector
# <vector> - vector
# <index> - item
#
# isin(<vector>, <item>) - checks if an item exists in a vector
# <vector> - vector
# <item> - item
#
# putmodel(<path>, <lat>, <lon>, <alt>, <hdg>) - add a model to the scene graph (unlike geo.put_model(), models added with this function can be adjusted)
# <path> - model path
# <lat> - latitude
# <lon> - longitude
# <alt> - altitude in m
# <hdg> - heading
#
# interpolate_table(<table>, <value>) - interpolates a value within a table
# <table> - interpolation table/vector, in the format of [[<ind>, <dep>], [<ind>, <dep>], ... ]
# <value> - value
#
# get_relative_filepath(<path>, <target>) - gets a relative file path from a directory
# <path> - directory path should be relative to
# <target> - target directory
#
# find_airports(<max dist>) - gets a list of nearest airports
# <max dist> - maximum search distance in nm (currently unused)
#
# JETWAY CLASS
# ------------
#
# Jetway. - creates a new jetway object/model
# new(<airport>, <model>, <gate>, <door>,
# <airline>, <lat>, <lon>, <alt>,
# <heading>, [, <init_extend>]
# [, <init_heading>] [, <init_pitch>]
# [, <init_ent_heading>])
# <airport> - ICAO of associated airport
# <model> - jetway model definition (i.e. Models/Airport/Jetway/generic.xml)
# <gate> - gate number (i.e. "A1")
# <door> - door number (i.e. 0)
# <airline> - airline code (i.e. "AAL")
# <lat> - latitude location of model
# <lon> - longitude location of model
# <alt> - elevation of model in m
# <heading> - (optional) heading of model
# <init_extend> - (optional) initial extension of tunnel in m
# <init_heading> - (optional) initial rotation of tunnel along the Z axis
# <init_pitch> - (optional) initial pitch of tunnel (rotation along Y axis)
# <init_ent_heading> - (optional) initial rotation of entrance along the Z axis
#
# toggle(<user>, <heading>, <coord> - extends/retracts a jetway
# [, <hood>])
# <user> - whether or not jetway is toggled by user command (0/1)
# <heading> - heading of aircraft to connect to
# <coord> - a geo.Coord of the target aircraft's door
# <hood> - (optional) amount to rotate jetway hood (only required when <user> != 1)
#
# extend(<user>, <heading>, <coord> - extends a jetway (should be called by Jetway.toggle())
# [, <hood>])
# <user> - whether or not jetway is toggled by user command (0/1)
# <heading> - heading of aircraft to connect to
# <coord> - a geo.Coord of the target aircraft's door
# <hood> - (optional) amount to rotate jetway hood (only required when <user> != 1)
#
# retract(<user>) - retracts a jetway (should be called by Jetway.toggle())
# <user> - whether or not a jetway is toggled by user command (0/1)
#
# remove() - removes a jetway object and its model
#
# reload() - reloads a jetway object and its model
#
# setpos(<lat>, <lon>, <heading>, <alt>) - moves a jetway to a new location
# <lat> - new latitude
# <lon> - new longitude
# <heading> - new heading
# <alt> - new altitude in m
#
# setmodel(<model>, <airline>, <gate>) - changes the jetway model
# <model> - new model
# <airline> - new airline sign code
# <gate> - new gate number
#
# INTERACTION FUNCTIONS
# ---------------------
#
# dialog() - open settings dialog
#
# toggle_jetway(<id>) - toggles a jetway by user command (should be called by a pick animation in a jetway model)
# <id> - id number of jetway to toggle
#
# toggle_jetway_from_coord(<door>, <hood>, - toggles a jetway with the target door at the specified coordinates
# <heading>, [<lat>,
# <lon>] [<coord>])
# <door> - door number (i.e. 0)
# <hood> - amount to rotate jetway hood
# <lat> - (required or <coord>) latitude location of door
# <lon> - (required or <coord>) longitude location of door
# <coord> - (required or <lat>, <lon>) a geo.Coord of the door
#
# toggle_jetway_from_model(<model node>) - toggles a jetway using an AI model instead of the user's aircraft
# <model node> - path of AI model (i.e. /ai/models/aircraft[0])- can be the path in a string or a props.Node
#
# INTERNAL FUNCTIONS
# ------------------
#
# load_airport_jetways(<airport>) - loads jetways at an airport
# <airport> - ICAO of airport
#
# unload_airport_jetways(<airport>) - unloads jetways at an airport
# <airport> - ICAO of airport
#
# update_jetways() - interpolates model animation values
#
# load_jetways() - loads new jetway models and unloads out-of-range models every 10 seconds; also connects AI and MP aircraft
#
## Utility functions
####################
# prints debug messages
var print_debug = func(msg)
{
if (debug_switch.getBoolValue())
{
print(msg);
}
};
# prints error messages
var print_error = func(msg)
{
print("\x1b[31m" ~ msg ~ "\x1b[m");
};
# alerts the user
var alert = func(msg)
{
setprop("/sim/messages/ground", msg);
};
# normalizes headings between -180 and 180
var normdeg = func(x)
{
while (x >= 180)
{
x -= 360;
}
while (x <= -180)
{
x += 360;
}
return x;
};
# deletes an item in a vector
var remove = func(vector, item)
{
var s = size(vector);
var found = 0;
for (var i = 0; i < s; i += 1)
{
if (found)
{
vector[i - 1] = vector[i];
}
elsif (vector[i] == item)
{
found = 1;
}
}
if (found) setsize(vector, s - 1);
return vector;
};
# checks if an item is in a vector
var isin = func(vector, v)
{
for (var i = 0; i < size(vector); i += 1)
{
if (vector[i] == v) return 1;
}
return 0;
};
# adds a model
var putmodel = func(path, lat, lon, alt, hdg)
{
var models = props.globals.getNode("/models");
var model = nil;
for (var i = 0; 1; i += 1)
{
if (models.getChild("model", i, 0) == nil)
{
model = models.getChild("model", i, 1);
break;
}
}
var model_path = model.getPath();
model.getNode("path", 1).setValue(path);
model.getNode("latitude-deg", 1).setDoubleValue(lat);
model.getNode("latitude-deg-prop", 1).setValue(model_path ~ "/latitude-deg");
model.getNode("longitude-deg", 1).setDoubleValue(lon);
model.getNode("longitude-deg-prop", 1).setValue(model_path ~ "/longitude-deg");
model.getNode("elevation-ft", 1).setDoubleValue(alt * M2FT);
model.getNode("elevation-ft-prop", 1).setValue(model_path ~ "/elevation-ft");
model.getNode("heading-deg", 1).setDoubleValue(hdg);
model.getNode("heading-deg-prop", 1).setValue(model_path ~ "/heading-deg");
model.getNode("pitch-deg", 1).setDoubleValue(0);
model.getNode("pitch-deg-prop", 1).setValue(model_path ~ "/pitch-deg");
model.getNode("roll-deg", 1).setDoubleValue(0);
model.getNode("roll-deg-prop", 1).setValue(model_path ~ "/roll-deg");
model.getNode("load", 1).remove();
return model;
};
# interpolates a value
var interpolate_table = func(table, v)
{
var x = 0;
forindex (i; table)
{
if (v >= table[i][0])
{
x = i + 1 < size(table) ? (v - table[i][0]) / (table[i + 1][0] - table[i][0]) * (table[i + 1][1] - table[i][1]) + table[i][1] : table[i][1];
}
}
return x;
};
# gets a relative file path
var get_relative_filepath = func(path, target)
{
var newpath = "";
for (var i = size(path) - 1; i >= 0; i -= 1)
{
var char = substr(path, i, 1);
if (char == "/") newpath ~= "../";
}
# we can just append the target path for UNIX systems, but we need to remove the drive letter prefix for DOS systems
return newpath ~ (string.match(substr(target, 0, 3), "?:/") ? substr(target, 2, size(target) - 2) : target);
};
# gets a list of nearest airports
# TODO: Don't use /sim/airport/nearest-airport-id, which restricts the list to 1 airport
var find_airports = func(max_distance)
{
var apt = getprop("/sim/airport/closest-airport-id");
return apt == "" ? nil : [apt];
};
## Global variables
###################
var root = nil;
var home = nil;
var scenery = [];
var UPDATE_PERIOD = 0;
var LOAD_PERIOD = 10;
var LOAD_DISTANCE = 50; # in nautical miles
var LOAD_JETWAY_PERIOD = 0.05;
var NUMBER_OF_JETWAYS = 1000; # approx max number of jetways loadable in FG
var runtime_files = NUMBER_OF_JETWAYS / LOAD_PERIOD * LOAD_JETWAY_PERIOD;
runtime_files = int(runtime_files) == runtime_files ? runtime_files : int(runtime_files) + 1;
var runtime_file = 0;
var update_loopid = -1;
var load_loopid = -1;
var load_listenerid = nil;
var loadids = {};
var dialog_object = nil;
var loaded_airports = [];
var jetways = [];
# properties
var on_switch = nil;
var debug_switch = nil;
var mp_switch = nil;
var jetway_id_prop = "/sim/jetways/last-loaded-jetway";
# interpolation tables
var extend_rate = 0.5;
var extend_table = [
[0.0, 0.0],
[0.2, 0.3],
[0.6, 0.3],
[0.8, 1.0],
[1.0, 1.0]
];
var pitch_rate = 1;
var pitch_table = [
[0.0, 0.0],
[0.4, 0.7],
[0.7, 1.0],
[1.0, 1.0]
];
var heading_rate = 1;
var heading_table = [
[0.0, 0.0],
[0.2, 0.0],
[0.6, 0.7],
[0.9, 1.0],
[1.0, 1.0]
];
var heading_entrance_rate = 5;
var heading_entrance_table = [
[0.0, 0.0],
[0.3, 0.0],
[0.6, 0.7],
[0.8, 1.0],
[1.0, 1.0]
];
var hood_rate = 1;
var hood_table = [
[0.0, 0.0],
[0.9, 0.0],
[1.0, 1.0]
];
## Classes
##########
# main jetway class
var Jetway =
{
new: func(airport, model, gate, door, airline, lat, lon, alt, heading, init_extend = 0, init_heading = 0, init_pitch = 0, init_ent_heading = 0)
{
var id = 0;
for (var i = 0; 1; i += 1)
{
if (i == size(jetways))
{
setsize(jetways, i + 1);
id = i;
break;
}
elsif (jetways[i] == nil)
{
id = i;
}
}
# locate the jetway model directory and load the model tree
var model_tree = nil;
var model_file = "";
var model_dir = "";
var airline_file = "";
if (props.globals.getNode("/sim/paths/use-custom-scenery-data", 1).getBoolValue())
{
# search in scenery directories
foreach (var scenery_path; scenery)
{
model_dir = scenery_path ~ "/Models/Airport/Jetway";
model_file = model_dir ~ "/" ~ model ~ ".xml";
airline_file = model_dir ~ "/" ~ model ~ ".airline." ~ airline ~ ".xml";
print_debug("Trying to load a jetway model from " ~ model_file);
if (io.stat(model_file) == nil) continue;
model_tree = io.read_properties(model_file);
if (io.stat(airline_file) != nil) props.copy(io.read_properties(airline_file), model_tree);
break;
}
}
if (model_tree == nil)
{
model_dir = root ~ "/Models/Airport/Jetway";
model_file = model_dir ~ "/" ~ model ~ ".xml";
airline_file = model_dir ~ "/" ~ model ~ ".airline." ~ airline ~ ".xml";
print_debug("Falling back to " ~ model_file);
if (io.stat(model_file) == nil)
{
print_error("Failed to load jetway model: " ~ model);
return;
}
model_tree = io.read_properties(model_file);
if (io.stat(airline_file) != nil) props.copy(io.read_properties(airline_file), model_tree);
}
var m =
{
parents: [Jetway]
};
m._active = 1; # set this to 'true' on the first run so that the offsets can take effect
m._edit = 0;
m.airport = airport;
m.gate = gate;
m.airline = airline;
m.id = id;
m.model = model;
m.extended = 0;
m.door = door;
m.lat = lat;
m.lon = lon;
m.alt = alt;
m.heading = geo.normdeg(180 - heading);
m.init_extend = init_extend;
m.init_heading = init_heading;
m.init_pitch = init_pitch;
m.init_ent_heading = init_ent_heading;
m.target_extend = 0;
m.target_pitch = 0;
m.target_heading = 0;
m.target_ent_heading = 0;
m.target_hood = 0;
m.rotunda_x = model_tree.getNode("rotunda/x-m").getValue();
m.rotunda_y = model_tree.getNode("rotunda/y-m").getValue();
m.rotunda_z = model_tree.getNode("rotunda/z-m").getValue();
m.offset_extend = model_tree.getNode("extend-offset-m").getValue();
m.offset_entrance = model_tree.getNode("entrance-offset-m").getValue();
m.min_extend = model_tree.getNode("min-extend-m").getValue();
m.max_extend = model_tree.getNode("max-extend-m").getValue();
# get the runtime file path
if (runtime_file == runtime_files)
{
runtime_file = 0;
}
var runtime_file_path = home ~ "/runtime-jetways/" ~ runtime_file ~ ".xml";
runtime_file += 1;
# create the model node and the door object
m.node = putmodel(runtime_file_path, lat, lon, alt, geo.normdeg(360 - heading));
var node_path = m.node.getPath();
m.door_object = aircraft.door.new(node_path ~ "/jetway-position", 0);
# manipulate the model tree
model_tree.getNode("path").setValue(model_dir ~ "/" ~ model_tree.getNode("path").getValue());
model_tree.getNode("toggle-action-script").setValue("jetways.toggle_jetway(" ~ id ~ ");");
model_tree.getNode("gate").setValue(m.gate);
model_tree.getNode("extend-m").setValue(props.globals.initNode(node_path ~ "/jetway-position/extend-m", 0, "DOUBLE").getPath());
model_tree.getNode("pitch-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/pitch-deg", 0, "DOUBLE").getPath());
model_tree.getNode("heading-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/heading-deg", 0, "DOUBLE").getPath());
model_tree.getNode("entrance-heading-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/entrance-heading-deg", 0, "DOUBLE").getPath());
model_tree.getNode("hood-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/hood-deg", 0, "DOUBLE").getPath());
# airline texture
var airline_tex = model_tree.getNode("airline-texture-path", 1).getValue();
var airline_node = model_tree.getNode(model_tree.getNode("airline-prop-path", 1).getValue());
if (airline_tex != nil and airline_node != nil)
{
airline_node.setValue(get_relative_filepath(home ~ "/runtime-jetways", model_dir ~ "/" ~ airline_tex));
}
# write the model tree
io.write_properties(runtime_file_path, model_tree);
jetways[id] = m;
print_debug("Loaded jetway #" ~ id);
jetway_id_prop.setValue(id);
return m;
},
toggle: func(user, heading, coord, hood = 0)
{
me._active = 1;
if (me.extended)
{
me.retract(user, heading, coord);
}
else
{
me.extend(user, heading, coord, hood);
}
},
extend: func(user, heading, door_coord, hood = 0)
{
me.extended = 1;
# get the coordinates of the jetway and offset for the rotunda position
var jetway_coord = geo.Coord.new();
jetway_coord.set_latlon(me.lat, me.lon);
jetway_coord.apply_course_distance(me.heading, me.rotunda_x);
jetway_coord.apply_course_distance(me.heading - 90, me.rotunda_y);
jetway_coord.set_alt(me.alt + me.rotunda_z);
if (debug_switch.getBoolValue())
{
# place UFO cursors at the calculated door and jetway positions for debugging purposes
geo.put_model("Aircraft/ufo/Models/cursor.ac", door_coord);
geo.put_model("Aircraft/ufo/Models/cursor.ac", jetway_coord);
}
# offset the door for the length of the jetway entrance
door_coord.apply_course_distance(heading - 90, me.offset_entrance);
# calculate the bearing to the aircraft and the distance from the door
me.target_heading = normdeg(jetway_coord.course_to(door_coord) - me.heading - me.init_heading);
me.target_extend = jetway_coord.distance_to(door_coord) - me.offset_extend - me.init_extend;
# check if distance exceeds maximum jetway extension length
if (me.target_extend + me.init_extend > me.max_extend)
{
me.extended = 0;
me.target_extend = 0;
me.target_heading = 0;
if (user) alert("Your aircraft is too far from this jetway.");
print_debug("Jetway #" ~ me.id ~ " is too far from the door");
return;
}
# check if distance fails to meet minimum jetway extension length
if (me.target_extend + me.init_extend < me.min_extend)
{
me.extended = 0;
me.target_extend = 0;
me.target_heading = 0;
if (user) alert("Your aircraft is too close to this jetway.");
print_debug("Jetway #" ~ me.id ~ " is too close to the door");
return;
}
# calculate the jetway pitch, entrance heading, and hood
me.target_pitch = math.atan2((door_coord.alt() - jetway_coord.alt()) / (me.target_extend + me.offset_extend + me.init_extend), 1) * R2D - me.init_pitch;
me.target_ent_heading = normdeg((heading + 90) - (me.heading + (me.target_heading + me.init_heading) + me.init_ent_heading));
me.target_hood = user ? getprop("/sim/model/door[" ~ me.door ~ "]/jetway-hood-deg") : hood;
# fire up the animation
if (user) alert("Extending jetway.");
var animation_time = math.abs(me.target_extend / extend_rate) + math.abs(me.target_pitch / pitch_rate) + math.abs(me.target_heading / heading_rate) + math.abs(me.target_ent_heading / heading_entrance_rate) + math.abs(me.target_hood / hood_rate);
me.door_object.swingtime = animation_time;
me.door_object.open();
print_debug("************************************************");
print_debug("Activated jetway #" ~ me.id);
print_debug("Using door #" ~ me.door);
print_debug("Jetway heading: " ~ me.heading ~ " deg");
print_debug("Extension: " ~ me.target_extend ~ " m");
print_debug("Pitch: " ~ me.target_pitch ~ " deg");
print_debug("Heading: " ~ me.target_heading ~ " deg");
print_debug("Entrance heading: " ~ me.target_ent_heading ~ " deg");
print_debug("Hood: " ~ me.target_hood ~ " deg");
print_debug("Total animation time: " ~ animation_time ~ " sec");
print_debug("Jetway extending");
print_debug("************************************************");
},
retract: func(user)
{
if (user) alert("Retracting jetway.");
me.door_object.close();
me.extended = 0;
print_debug("************************************************");
print_debug("Activated jetway #" ~ me.id);
print_debug("Total animation time: " ~ me.door_object.swingtime ~ " sec");
print_debug("Jetway retracting");
print_debug("************************************************");
},
remove: func
{
me.node.remove();
var id = me.id;
jetways[me.id] = nil;
print_debug("Unloaded jetway #" ~ id);
},
reload: func
{
var airport = me.airport;
var model = me.model;
var gate = me.gate;
var door = me.door;
var airline = me.airline;
var lat = me.lat;
var lon = me.lon;
var alt = me.alt;
var heading = geo.normdeg(180 - (me.heading - 360));
var init_extend = me.init_extend;
var init_heading = me.init_heading;
var init_pitch = me.init_pitch;
var init_ent_heading = me.init_ent_heading;
me.remove();
Jetway.new(airport, model, gate, door, airline, lat, lon, alt, heading, init_extend, init_heading, init_pitch, init_ent_heading);
},
setpos: func(lat, lon, hdg, alt)
{
me.node.getNode("latitude-deg").setValue(lat);
me.lat = lat;
me.node.getNode("longitude-deg").setValue(lon);
me.lon = lon;
me.node.getNode("heading-deg").setValue(geo.normdeg(hdg - 180));
me.heading = hdg;
me.node.getNode("elevation-ft").setValue(alt * M2FT);
me.alt = alt;
},
setmodel: func(model, airline, gate)
{
me.airline = airline;
me.gate = gate;
me.model = model;
me.extended = 0;
me.target_extend = 0;
me.target_pitch = 0;
me.target_heading = 0;
me.target_ent_heading = 0;
me.target_hood = 0;
me.door_object.setpos(0);
me.reload();
}
};
## Interaction functions
########################
var dialog = func
{
if (dialog_object == nil) dialog_object = gui.Dialog.new("/sim/gui/dialogs/jetways/dialog", "gui/dialogs/jetways.xml");
dialog_object.open();
};
var toggle_jetway = func(n)
{
var jetway = jetways[n];
if (jetway == nil) return;
var door = props.globals.getNode("/sim/model/door[" ~ jetway.door ~ "]");
if (door == nil)
{
alert("Your aircraft does not define the location of door " ~ (jetway.door + 1) ~ ", cannot extend this jetway.");
return;
}
# get the coordinates of the user's aircraft and offset for the door position and aircraft pitch
var coord = geo.aircraft_position();
var heading = getprop("/orientation/heading-deg");
var pitch = getprop("/orientation/pitch-deg");
coord.apply_course_distance(heading, -door.getChild("position-x-m").getValue());
coord.apply_course_distance(heading + 90, door.getChild("position-y-m").getValue());
coord.set_alt(coord.alt() + door.getChild("position-z-m").getValue());
coord.set_alt(coord.alt() + math.tan(pitch * D2R) * -door.getChild("position-x-m").getValue());
jetway.toggle(1, heading, coord);
};
var toggle_jetway_from_coord = func(door, hood, heading, lat, lon = nil)
{
if (isa(lat, geo.Coord))
{
var coord = lat;
}
else
{
var coord = geo.Coord.new();
coord.set_latlon(lat, lon);
}
var closest_jetway = nil;
var closest_jetway_dist = nil;
var closest_jetway_coord = nil;
for (var i = 0; i < size(jetways); i += 1)
{
if (jetways[i] == nil) continue;
var jetway = jetways[i];
var jetway_coord = geo.Coord.new();
jetway_coord.set_latlon(jetway.lat, jetway.lon);
var distance = jetway_coord.distance_to(coord);
if ((closest_jetway_dist == nil or distance < closest_jetway_dist) and jetway.door == door)
{
closest_jetway = jetway;
closest_jetway_dist = distance;
closest_jetway_coord = jetway_coord;
}
}
if (closest_jetway == nil)
{
print_debug("No jetways available");
}
elsif (!closest_jetway.extended)
{
closest_jetway.toggle(0, heading, coord, hood);
}
};
var toggle_jetway_from_model = func(model)
{
model = aircraft.makeNode(model);
var doors = model.getChildren("door");
if (doors == nil or size(doors) == 0) return;
for (var i = 0; i < size(doors); i += 1)
{
var coord = geo.Coord.new();
var hdg = model.getNode("orientation/true-heading-deg").getValue();
var lat = model.getNode("position/latitude-deg").getValue();
var lon = model.getNode("position/longitude-deg").getValue();
var alt = model.getNode("position/altitude-ft").getValue() * FT2M + doors[i].getNode("position-z-m").getValue();
coord.set_latlon(lat, lon, alt);
coord.apply_course_distance(hdg, -doors[i].getNode("position-x-m").getValue());
coord.apply_course_distance(hdg + 90, doors[i].getNode("position-y-m").getValue());
print_debug("Connecting a jetway to door #" ~ i ~ " for model " ~ model.getPath());
toggle_jetway_from_coord(i, doors[i].getNode("jetway-hood-deg").getValue(), hdg, coord);
}
};
## Internal functions
#####################
# loads jetways at an airport
var load_airport_jetways = func(airport)
{
if (isin(loaded_airports, airport)) return;
var tree = props.globals.getNode("/sim/paths/use-custom-scenery-data", 1).getBoolValue() ? io.read_airport_properties(airport, "jetways") : (io.stat(root ~ "/AI/Airports/" ~ airport ~ "/jetways.xml") == nil ? nil : io.read_properties(root ~ "/AI/Airports/" ~ airport ~ "/jetways.xml"));
if (tree == nil) return;
print_debug("Loading jetways for airport " ~ airport);
var nodes = tree.getChildren("jetway");
loadids[airport] = loadids[airport] == nil ? 0 : loadids[airport] + 1;
var i = 0;
var loop = func(id)
{
if (id != loadids[airport]) return;
if (i >= size(nodes))
{
append(loaded_airports, airport);
return;
}
var jetway = nodes[i];
var model = jetway.getNode("model", 1).getValue() or return;
var gate = jetway.getNode("gate", 1).getValue() or "";
var door = jetway.getNode("door", 1).getValue() or 0;
var airline = jetway.getNode("airline", 1).getValue() or "None";
var lat = jetway.getNode("latitude-deg", 1).getValue() or return;
var lon = jetway.getNode("longitude-deg", 1).getValue() or return;
var elev = jetway.getNode("elevation-m", 1).getValue() or 0;
var alt = geo.elevation(lat, lon) + elev;
var heading = jetway.getNode("heading-deg", 1).getValue() or 0;
var init_extend = jetway.getNode("initial-position/jetway-extension-m", 1).getValue() or 0;
var init_heading = jetway.getNode("initial-position/jetway-heading-deg", 1).getValue() or 0;
var init_pitch = jetway.getNode("initial-position/jetway-pitch-deg", 1).getValue() or 0;
var init_ent_heading = jetway.getNode("initial-position/entrance-heading-deg", 1).getValue() or 0;
Jetway.new(airport, model, gate, door, airline, lat, lon, alt, heading, init_extend, init_heading, init_pitch, init_ent_heading);
i += 1;
settimer(func loop(id), LOAD_JETWAY_PERIOD);
};
settimer(func loop(loadids[airport]), 0);
};
# unloads jetways at an airport
var unload_airport_jetways = func(airport)
{
print_debug("Unloading jetways for airport " ~ airport);
foreach (var jetway; jetways)
{
if (jetway != nil and jetway.airport == airport) jetway.remove();
}
remove(loaded_airports, airport);
};
# restarts the main update loop
var restart = func()
{
update_loopid += 1;
update_jetways(update_loopid);
settimer(func
{
load_loopid += 1;
load_jetways(load_loopid);
}, 2);
print("Animated jetways ... initialized");
};
# main update loop (runs when jetways are enable and actived)
var update_jetways = func(loopid)
{
# terminate if loopid does not match
if (loopid != update_loopid) return;
# if jetways disabled, unload jetways and terminate
if (!on_switch.getBoolValue())
{
for (var i = 0; i < size(jetways); i += 1)
{
if (jetways[i] != nil) jetways[i].remove();
}
setsize(jetways, 0);
setsize(loaded_airports, 0);
return;
}
# interpolate jetway values
for (var i = 0; i < size(jetways); i += 1)
{
var jetway = jetways[i];
if (jetway == nil) continue;
if (jetway._active or jetway._edit)
{
var position = jetway.door_object.getpos();
if (position == 0 or position == 1) jetway._active = 0;
jetway.node.getNode("jetway-position/extend-m").setValue(interpolate_table(extend_table, position) * jetway.target_extend + jetway.init_extend);
jetway.node.getNode("jetway-position/pitch-deg").setValue(interpolate_table(pitch_table, position) * jetway.target_pitch + jetway.init_pitch);
jetway.node.getNode("jetway-position/heading-deg").setValue(interpolate_table(heading_table, position) * jetway.target_heading + jetway.init_heading);
jetway.node.getNode("jetway-position/entrance-heading-deg").setValue(interpolate_table(heading_entrance_table, position) * jetway.target_ent_heading + jetway.init_ent_heading);
jetway.node.getNode("jetway-position/hood-deg").setValue(interpolate_table(hood_table, position) * jetway.target_hood);
}
}
settimer(func update_jetways(loopid), UPDATE_PERIOD);
};
# loading/unloading loop (runs continuously)
var load_jetways = func(loopid)
{
if (load_listenerid != nil) removelistener(load_listenerid);
# terminate if loopid does not match
# unloading jetways if jetways are disabled is handled by update loop
if (loopid != load_loopid or !on_switch.getBoolValue()) return;
var airports = find_airports(LOAD_DISTANCE);
if (airports == nil) return;
# search for any airports out of range and unload their jetways
foreach (var airport; loaded_airports)
{
if (!isin(airports, airport))
{
unload_airport_jetways(airport);
}
}
# load any airports in range
foreach (var airport; airports)
{
load_airport_jetways(airport);
}
var nearest_airport = airportinfo();
nearest_airport = nearest_airport == nil ? nil : nearest_airport.id;
if (isin(loaded_airports, nearest_airport))
{
# loop through the AI aircraft and extend/retract jetways
var ai_aircraft = props.globals.getNode("ai/models").getChildren("aircraft");
foreach (var aircraft; ai_aircraft)
{
if (!aircraft.getNode("valid", 1).getBoolValue()) continue;
var connected = aircraft.getNode("connected-to-jetways", 1);
var velocity = aircraft.getNode("velocities/true-airspeed-kt", 1).getValue();
# TODO: Find a better way to know when the aircraft is "parked"
if (velocity != nil and velocity > -1 and velocity < 1)
{
if (!connected.getBoolValue()) toggle_jetway_from_model(aircraft);
connected.setBoolValue(1);
}
else
{
if (connected.getBoolValue()) toggle_jetway_from_model(aircraft);
connected.setBoolValue(0);
}
}
# loop through the multiplayer aircraft and extend/retract jetways
# TODO: In the future, broadcast jetway properties over MP, making this part obselete
if (mp_switch.getBoolValue())
{
var multiplayers = props.globals.getNode("ai/models").getChildren("multiplayer");
foreach (var aircraft; multiplayers)
{
if (!aircraft.getNode("valid", 1).getBoolValue()) continue;
var connected = aircraft.getNode("connected-to-jetways", 1);
var velocity = aircraft.getNode("velocities/true-airspeed-kt", 1).getValue();
if (velocity != nil and velocity > -1 and velocity < 1)
{
if (!connected.getBoolValue()) toggle_jetway_from_model(aircraft);
connected.setBoolValue(1);
}
else
{
if (connected.getBoolValue()) toggle_jetway_from_model(aircraft);
connected.setBoolValue(0);
}
}
}
}
settimer(func load_jetways(loopid), LOAD_PERIOD);
};
## fire it up
_setlistener("/nasal/jetways/loaded", func
{
# global variables
root = string.normpath(getprop("/sim/fg-root"));
home = string.normpath(getprop("/sim/fg-home"));
foreach (var scenery_path; props.globals.getNode("/sim").getChildren("fg-scenery"))
{
append(scenery, string.normpath(scenery_path.getValue()));
}
if (size(scenery) == 0) append(scenery, root ~ "/Scenery");
# properties
on_switch = props.globals.getNode("/nasal/jetways/enabled", 1);
debug_switch = props.globals.getNode("/sim/jetways/debug", 1);
mp_switch = props.globals.getNode("/sim/jetways/interact-with-multiplay", 1);
jetway_id_prop = props.globals.getNode(jetway_id_prop, 1);
restart();
});

View file

@ -0,0 +1,601 @@
###############################################################################
##
## Animated Jetway System. Allows the user to edit jetways during runtime.
##
## Copyright (C) 2011 Ryan Miller
## This file is licensed under the GPL license version 2 or later.
##
###############################################################################
###############################################################################
# (See http://wiki.flightgear.org/Howto:_Animated_jetways)
#
### Static jetway model profiles ###
### This class specifies the offsets used when converting static jetways using the STG converter ###
var Static_jetway =
[
# Models/Airport/Jetway/jetway-movable.ac
# Models/Airport/Jetway/jetway-movable.xml
# Models/Airport/Jetway/jetway-movable-2.ac
# Models/Airport/Jetway/jetway-movable-2.xml
# Models/Airport/Jetway/jetway-movable-3.ac
# Models/Airport/Jetway/jetway-movable-3.xml
{
models:
[
"Models/Airport/Jetway/jetway-movable.ac",
"Models/Airport/Jetway/jetway-movable.xml",
"Models/Airport/Jetway/jetway-movable-2.ac",
"Models/Airport/Jetway/jetway-movable-2.xml",
"Models/Airport/Jetway/jetway-movable-3.ac",
"Models/Airport/Jetway/jetway-movable-3.xml"
],
offsets:
{
x: -2.042,
y: 0,
z: 0,
heading: 0
},
init_pos:
{
extend: 7.24,
heading: 0,
pitch: 0,
ent_heading: -90
},
model: "generic",
airline: "None"
},
# Models/Airport/Jetway/jetway.xml
# Models/Airport/Jetway/jetway-ba.ac
# Models/Airport/Jetway/jetway-ba.xml
{
models:
[
"Models/Airport/Jetway/jetway.xml",
"Models/Airport/Jetway/jetway-ba.ac",
"Models/Airport/Jetway/jetway-ba.xml"
],
offsets:
{
x: 0,
y: 0,
z: -0.25,
heading: 0
},
init_pos:
{
extend: 7.24,
heading: -6.7,
pitch: -3.6,
ent_heading: -83.3
},
model: "generic",
airline: "None"
},
# Models/Airport/Jetway/jetway-737-ba.ac
# Models/Airport/Jetway/jetway-737-ba.xml
{
models:
[
"Models/Airport/Jetway/jetway-737-ba.ac",
"Models/Airport/Jetway/jetway-737-ba.xml"
],
offsets:
{
x: 0,
y: 0,
z: -0.25,
heading: 0
},
init_pos:
{
extend: 7.24,
heading: -6.7,
pitch: -4,
ent_heading: -83.3
},
model: "generic",
airline: "None"
},
# Models/Airport/Jetway/jetway-747-ba.ac
# Models/Airport/Jetway/jetway-747-ba.xml
{
models:
[
"Models/Airport/Jetway/jetway-747-ba.ac",
"Models/Airport/Jetway/jetway-747-ba.xml"
],
offsets:
{
x: 0,
y: 0,
z: -0.25,
heading: 0
},
init_pos:
{
extend: 7.24,
heading: -6.7,
pitch: 2,
ent_heading: -83.3
},
model: "generic",
airline: "None"
},
# Models/Airport/Jetway/jetway-a320-ba.ac
# Models/Airport/Jetway/jetway-a320-ba.xml
{
models:
[
"Models/Airport/Jetway/jetway-a320-ba.ac",
"Models/Airport/Jetway/jetway-a320-ba.xml"
],
offsets:
{
x: 0,
y: 0,
z: -0.25,
heading: 0
},
init_pos:
{
extend: 7.24,
heading: -6.7,
pitch: -1.6,
ent_heading: -83.3
},
model: "generic",
airline: "None"
},
# Models/Airport/Jetway/AutoGate-ba.ac
# Models/Airport/Jetway/AutoGate.xml
{
models:
[
"Models/Airport/Jetway/AutoGate-ba.ac",
"Models/Airport/Jetway/AutoGate.xml"
],
offsets:
{
x: -10,
y: 25,
z: 0,
heading: -90
},
init_pos:
{
extend: 7.68,
heading: 0,
pitch: 0,
ent_heading: -90
},
model: "generic",
airline: "None"
},
# Models/Airport/Jetway/DockingGate-ba.ac
# Models/Airport/Jetway/DockingGate.xml
{
models:
[
"Models/Airport/Jetway/DockingGate-ba.ac",
"Models/Airport/Jetway/DockingGate.xml"
],
offsets:
{
x: -10,
y: 5,
z: 0,
heading: -90
},
init_pos:
{
extend: 7.68,
heading: 0,
pitch: 0,
ent_heading: -90
},
model: "generic",
airline: "None"
}
];
### Rest of script follows below ###
### Watch your step! :) ###
var dialog_object = nil;
var selected_jetway = nil;
var mouse_mmb = 0;
var kbd_shift = nil;
var kbd_ctrl = nil;
var kbd_alt = nil;
var enabled = nil;
var FLASH_PERIOD = 0.3;
var FLASH_NUM = 3;
var filedialog_listener = 0;
var click = func(pos)
{
if (kbd_alt.getBoolValue())
{
if (selected_jetway == nil) return;
selected_jetway.setpos(pos.lat(), pos.lon(), selected_jetway.heading, pos.alt());
}
elsif (kbd_shift.getBoolValue())
{
if (selected_jetway != nil) selected_jetway._edit = 0;
selected_jetway = nil;
}
elsif (kbd_ctrl.getBoolValue())
{
var nearest_jetway = nil;
var min_dist = geo.ERAD;
for (var i = 0; i < size(jetways.jetways); i += 1)
{
var jetway = jetways.jetways[i];
if (jetway == nil) continue;
var dist = geo.Coord.new().set_latlon(jetway.lat, jetway.lon, jetway.alt).direct_distance_to(pos);
if (dist < min_dist)
{
min_dist = dist;
nearest_jetway = jetway;
}
}
if (nearest_jetway != nil)
{
if (selected_jetway != nil) selected_jetway._edit = 0;
selected_jetway = nearest_jetway;
setprop("/sim/jetways/adjust/model", selected_jetway.model);
setprop("/sim/jetways/adjust/door", selected_jetway.door);
setprop("/sim/jetways/adjust/airline", selected_jetway.airline);
setprop("/sim/jetways/adjust/gate", selected_jetway.gate);
selected_jetway._edit = 1;
flash(nearest_jetway);
}
}
else
{
var airport = getprop("/sim/airport/closest-airport-id");
if (airport == "") return;
selected_jetway = jetways.Jetway.new(airport, "generic", "FG", 0, "FGFS", pos.lat(), pos.lon(), pos.alt(), 0);
selected_jetway._edit = 1;
if (!jetways.isin(jetways.loaded_airports, airport)) append(jetways.loaded_airports, airport);
setprop("/sim/jetways/adjust/model", selected_jetway.model);
setprop("/sim/jetways/adjust/door", selected_jetway.door);
setprop("/sim/jetways/adjust/airline", selected_jetway.airline);
setprop("/sim/jetways/adjust/gate", selected_jetway.gate);
flash(selected_jetway);
}
};
var delete = func
{
if (selected_jetway == nil) return;
selected_jetway.remove();
selected_jetway = nil;
};
var adjust = func(name, value)
{
if (selected_jetway == nil) return;
if (name == "longitudinal")
{
var jetway_pos = geo.Coord.new();
jetway_pos.set_latlon(selected_jetway.lat, selected_jetway.lon, selected_jetway.alt);
var dir = geo.aircraft_position().course_to(jetway_pos);
jetway_pos.apply_course_distance(dir, value);
selected_jetway.setpos(jetway_pos.lat(), jetway_pos.lon(), selected_jetway.heading, selected_jetway.alt);
}
elsif (name == "transversal")
{
var jetway_pos = geo.Coord.new();
jetway_pos.set_latlon(selected_jetway.lat, selected_jetway.lon, selected_jetway.alt);
var dir = geo.aircraft_position().course_to(jetway_pos) + 90;
jetway_pos.apply_course_distance(dir, value);
selected_jetway.setpos(jetway_pos.lat(), jetway_pos.lon(), selected_jetway.heading, selected_jetway.alt);
}
elsif (name == "altitude")
{
var alt = selected_jetway.alt + value * 0.4;
selected_jetway.setpos(selected_jetway.lat, selected_jetway.lon, selected_jetway.heading, alt);
}
elsif (name == "heading")
{
var hdg = geo.normdeg(selected_jetway.heading + value * 4);
selected_jetway.setpos(selected_jetway.lat, selected_jetway.lon, hdg, selected_jetway.alt);
}
elsif (name == "initial-extension")
{
var newvalue = selected_jetway.init_extend + value;
if (newvalue > selected_jetway.max_extend)
{
gui.popupTip("Value exceeds maximum jetway extension limit");
}
elsif (newvalue < selected_jetway.min_extend)
{
gui.popupTip("Value lower than minimum jetway extension limit");
}
else
{
selected_jetway.init_extend = newvalue;
}
}
elsif (name == "initial-pitch")
{
selected_jetway.init_pitch += value;
}
elsif (name == "initial-heading")
{
selected_jetway.init_heading += value;
}
elsif (name == "initial-entrance-heading")
{
selected_jetway.init_ent_heading += value;
}
elsif (name == "model")
{
selected_jetway.setmodel(value, selected_jetway.airline, selected_jetway.gate);
selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")];
}
elsif (name == "door")
{
selected_jetway.door = value;
}
elsif (name == "airline")
{
selected_jetway.setmodel(selected_jetway.model, value, selected_jetway.gate);
selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")];
}
elsif (name == "gate")
{
selected_jetway.setmodel(selected_jetway.model, selected_jetway.airline, value);
selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")];
}
};
var export = func
{
var path = getprop("/sim/fg-home") ~ "/Export/";
var airports = {};
var airportarray = [];
foreach (var jetway; jetways.jetways)
{
if (jetway == nil) continue;
if (airports[jetway.airport] == nil)
{
airports[jetway.airport] = [];
append(airportarray, jetway.airport);
}
var node = props.Node.new();
node.getNode("model", 1).setValue(jetway.model);
node.getNode("gate", 1).setValue(jetway.gate);
node.getNode("door", 1).setIntValue(jetway.door);
node.getNode("airline", 1).setValue(jetway.airline);
node.getNode("latitude-deg", 1).setDoubleValue(jetway.lat);
node.getNode("longitude-deg", 1).setDoubleValue(jetway.lon);
var alt = jetway.alt;
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD);
node.getNode("elevation-m", 1).setDoubleValue(alt - geo.elevation(jetway.lat, jetway.lon));
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, alt);
node.getNode("heading-deg", 1).setDoubleValue(geo.normdeg(180 - jetway.heading));
node.getNode("initial-position/jetway-extension-m", 1).setDoubleValue(jetway.init_extend);
node.getNode("initial-position/jetway-heading-deg", 1).setDoubleValue(jetway.init_heading);
node.getNode("initial-position/jetway-pitch-deg", 1).setDoubleValue(jetway.init_pitch);
node.getNode("initial-position/entrance-heading-deg", 1).setDoubleValue(jetway.init_ent_heading);
append(airports[jetway.airport], node);
}
foreach (var airport; airportarray)
{
var file = path ~ airport ~ ".xml";
var args = props.Node.new({ filename: file });
var nodes = airports[airport];
foreach (var node; nodes)
{
var data = args.getNode("data", 1);
for (var i = 0; 1; i += 1)
{
if (data.getChild("jetway", i, 0) == nil)
{
props.copy(node, data.getChild("jetway", i, 1));
break;
}
}
}
fgcommand("savexml", args);
print("jetway definitions for airport " ~ airport ~ " exported to " ~ file);
}
};
var convert_stg = func
{
fgcommand("dialog-show", props.Node.new({ "dialog-name": "file-select" }));
setprop("/sim/gui/dialogs/file-select/path", "");
filedialog_listener = setlistener("/sim/gui/dialogs/file-select/path", func(n)
{
removelistener(filedialog_listener);
var path = n.getValue();
if (path == "") return;
var stg = io.readfile(path);
var stg_lines = [[]];
var current_word = "";
for (var i = 0; i < size(stg); i += 1)
{
var char = substr(stg, i, 1);
if (char == " " or char == "\n")
{
append(stg_lines[size(stg_lines) - 1], current_word);
current_word = "";
if (char == "\n") append(stg_lines, []);
}
else
{
current_word ~= char;
}
}
var jetway_array = [];
foreach (var line; stg_lines)
{
if (size(line) < 6 or line[0] != "OBJECT_SHARED") continue;
var foundmodel = 0;
var jetway = nil;
foreach (var profile; Static_jetway)
{
foreach (var model; profile.models)
{
if (model == line[1]) foundmodel = 1;
}
if (foundmodel)
{
jetway = profile;
break;
}
}
if (jetway == nil) continue;
var heading = num(line[5]);
var coord = geo.Coord.new();
coord.set_latlon(line[3], line[2], line[4]);
coord.apply_course_distance(360 - heading, -jetway.offsets.x);
coord.apply_course_distance(360 - heading + 90, jetway.offsets.y);
coord.set_alt(coord.alt() + jetway.offsets.z);
var hash = {};
hash.coord = coord;
hash.heading = heading + jetway.offsets.heading;
hash.init_extend = jetway.init_pos.extend;
hash.init_heading = jetway.init_pos.heading;
hash.init_pitch = jetway.init_pos.pitch;
hash.init_ent_heading = jetway.init_pos.ent_heading;
hash.model = jetway.model;
hash.airline = jetway.airline;
append(jetway_array, hash);
}
var airport = getprop("/sim/airport/closest-airport-id");
if (airport == "") return;
var i = 0;
var loop = func
{
if (i >= size(jetway_array)) return;
var jetway = jetway_array[i];
jetways.Jetway.new(airport, jetway.model, "", 0, jetway.airline, jetway.coord.lat(), jetway.coord.lon(), jetway.coord.alt(), jetway.heading, jetway.init_extend, jetway.init_heading, jetway.init_pitch, jetway.init_ent_heading);
if (!jetways.isin(jetways.loaded_airports, airport)) append(jetways.loaded_airports, airport);
i += 1;
settimer(loop, jetways.LOAD_JETWAY_PERIOD);
};
settimer(loop, 0);
jetways.alert("Creating " ~ size(jetway_array) ~ " jetways for airport " ~ airport);
}, 0, 1);
};
var flash = func(jetway)
{
if (!contains(jetway, "_flashnum") or jetway._flashnum == -1)
{
jetway._alt = jetway.alt;
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD);
jetway._flashnum = 0;
settimer(func flash(jetway), FLASH_PERIOD);
}
elsif (!contains(jetway, "_alt"))
{
jetway._flashnum = -1;
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, geo.elevation(jetway.lat, jetway.lon));
return;
}
elsif (jetway._flashnum == FLASH_NUM + 1)
{
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, jetway._alt);
jetway._alt = nil;
jetway._flashnum = -1;
}
else
{
if (jetway.alt == -geo.ERAD)
{
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, jetway._alt);
}
else
{
jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD);
}
jetway._flashnum += 1;
settimer(func flash(jetway), FLASH_PERIOD);
}
};
var dialog = func
{
if (dialog_object == nil) dialog_object = gui.Dialog.new("/sim/gui/dialogs/jetways-adjust/dialog", "gui/dialogs/jetways-adjust.xml");
dialog_object.open();
};
var print_help = func
{
print("JETWAY EDITOR HELP");
print("*******************************************************");
print("See: http://wiki.flightgear.org/Howto:_Animated_jetways");
print("");
print("Adjust position, heading, and altitude with top sliders");
print("Adjust initial jetway positions with bottom sliders");
print("");
print("<Model> model of selected jetway");
print("<Door> aircraft door number of selected jetway");
print("<Airline sign> airline sign code of selected jetway");
print("<Gate> gate number of selected jetway");
print("");
print("[Center sliders] apply slider offsets and return sliders to 0");
print("[Export] export jetway definition file(s)");
print("[STG converter] convert static jetways in STG files to animated jetways");
print("[?] show this help text");
print("");
print("Click add jetway on click position");
print("Alt-click move selected jetway to click position");
print("Ctrl-click select a jetway near click position");
print("Shift-click deselect selected jetway");
print("Backspace delete selected jetway");
print("*******************************************************");
};
_setlistener("/nasal/jetways_edit/loaded", func
{
print("Animated jetway editor ... loaded");
kbd_shift = props.globals.getNode("/devices/status/keyboard/shift");
kbd_ctrl = props.globals.getNode("/devices/status/keyboard/ctrl");
kbd_alt = props.globals.getNode("/devices/status/keyboard/alt");
enabled = props.globals.getNode("/nasal/jetways_edit/enabled");
setlistener("/sim/jetways/adjust/model", func(n)
{
var v = n.getValue();
if (selected_jetway != nil and v != selected_jetway.model)
{
adjust("model", v);
}
}, 0, 0);
setlistener("/sim/jetways/adjust/door", func(n)
{
var v = n.getValue();
if (selected_jetway != nil and v != selected_jetway.door)
{
adjust("door", v);
}
}, 0, 0);
setlistener("/sim/jetways/adjust/airline", func(n)
{
var v = n.getValue();
if (selected_jetway != nil and v != selected_jetway.airline)
{
adjust("airline", v);
}
}, 0, 0);
setlistener("/sim/jetways/adjust/gate", func(n)
{
var v = n.getValue();
if (selected_jetway != nil and v != selected_jetway.gate)
{
adjust("gate", v);
}
}, 0, 0);
setlistener("/devices/status/keyboard/event", func(event)
{
if (!event.getNode("pressed").getValue()) return;
if (enabled.getBoolValue() and event.getNode("key").getValue() == 8) delete();
});
setlistener("/devices/status/mice/mouse/button[1]", func(n) mouse_mmb = n.getBoolValue(), 1, 0);
setlistener("/sim/signals/click", func if (!mouse_mmb and enabled.getBoolValue()) click(geo.click_position()));
});

View file

@ -67,8 +67,19 @@
# The compatibility layer is currently work in progress and will be extended as new Nasal
# APIs are being added to FlightGear.
var weather_dynamics = nil;
var weather_tile_management = nil;
var compat_layer = nil;
var weather_tiles = nil;
_setlistener("/nasal/local_weather/loaded", func {
compat_layer = local_weather;
weather_dynamics = local_weather;
weather_tile_management = local_weather;
weather_tiles = local_weather;
_setlistener("/sim/signals/nasal-dir-initialized", func {
var result = "yes";
@ -81,10 +92,16 @@ else
print("* can set light saturation: "~result);
if (props.globals.getNode("/rendering/scene/scattering", 0) == nil)
{result = "no"; features.can_set_scattering = 0;}
else
{result = "yes"; features.can_set_scattering = 1;}
print("* can set horizon scattering: "~result);
if (props.globals.getNode("/environment/terrain", 0) == nil)
{result = "no"; features.terrain_presampling = 0;}
else
{result = "yes"; features.terrain_presampling = 1;}
{result = "yes"; features.terrain_presampling = 1;setprop("/environment/terrain/area[0]/enabled",1);}
print("* hard coded terrain presampling: "~result);
if ((props.globals.getNode("/environment/terrain/area[0]/enabled",1).getBoolValue() == 1) and (features.terrain_presampling ==1))
@ -100,12 +117,13 @@ else
{result = "yes"; features.can_disable_environment = 1;}
print("* can disable global weather: "~result);
#if (features.terrain_presampling_active == 1)
# {
# setlistener("/environment/terrain/area[0]/output/valid", func {local_weather.manage_hardcoded_presampling(); });
# }
print("Compatibility layer: tests done.");
# do actual startup()
local_weather.updateMenu();
local_weather.startup();
});
@ -162,6 +180,47 @@ else
}
}
var setVisibilitySmoothly = func (vis) {
if (features.can_disable_environment == 0)
{setVisibility(vis); return;}
visibility_target = vis;
visibility_current = getprop("/environment/visibility-m");
if (smooth_visibility_loop_flag == 0)
{
smooth_visibility_loop_flag = 1;
visibility_loop();
}
}
var visibility_loop = func {
if (local_weather.local_weather_running_flag == 0) {return;}
if (visibility_target == visibility_current)
{smooth_visibility_loop_flag = 0; return;}
if (visibility_target < visibility_current)
{
var vis_goal = visibility_target;
if (vis_goal < 0.97 * visibility_current) {vis_goal = 0.97 * visibility_current;}
}
else
{
var vis_goal = visibility_target;
if (vis_goal > 1.03 * visibility_current) {vis_goal = 1.03 * visibility_current;}
}
setprop("/environment/visibility-m",vis_goal);
visibility_current = vis_goal;
settimer( func {visibility_loop(); },0);
}
####################################
# set thermal lift to given value
####################################
@ -328,6 +387,71 @@ if (features.can_set_light == 1)
}
}
var setLightSmoothly = func (s) {
if (features.can_set_light == 0)
{return;}
light_target = s;
light_current = getprop("/rendering/scene/saturation");
if (smooth_light_loop_flag == 0)
{
smooth_light_loop_flag = 1;
light_loop();
}
}
var light_loop = func {
if (local_weather.local_weather_running_flag == 0) {return;}
if (light_target == light_current)
{smooth_light_loop_flag = 0; return;}
if (light_target < light_current)
{
var light_goal = light_target;
if (light_goal < 0.97 * light_current) {light_goal = 0.97 * light_current;}
}
else
{
var light_goal = light_target;
if (light_goal > 1.03 * light_current) {light_goal = 1.03 * light_current;}
}
setprop("/rendering/scene/saturation",light_goal);
light_current = light_goal;
settimer( func {light_loop(); },0);
}
####################################
# set horizon scattering
####################################
var setScattering = func (s) {
if (features.can_set_scattering == 1)
{
setprop("/rendering/scene/scattering",s);
}
}
####################################
# set overcast haze
####################################
var setOvercast = func (o) {
if (features.can_set_scattering == 1)
{
setprop("/rendering/scene/overcast",o);
}
}
###########################################################
# set wind to given direction and speed
###########################################################
@ -566,14 +690,6 @@ if (local_weather.dynamics_flag == 1)
var blat = buffered_tile_latitude;
var blon = buffered_tile_longitude;
var alpha = buffered_tile_alpha;
#var blat1 = getprop(lw~"tiles/tmp/latitude-deg");
#var blon1 = getprop(lw~"tiles/tmp/longitude-deg");
#var alpha1 = getprop(lw~"tmp/tile-orientation-deg");
#print("Lat: ", blat1, " ", blat);
#print("Lon: ", blon1, " ", blon);
#print("Alp: ", alpha1, " ", alpha);
}
else
{
@ -698,6 +814,18 @@ var ec = "/environment/config/";
var mvec = [];
var msize = 0;
# loop flags and variables
var smooth_visibility_loop_flag = 0;
var visibility_target = 0.0;
var visibility_current = 0.0;
var smooth_light_loop_flag = 0;
var light_target = 0.0;
var light_current = 0.0;
# available hard-coded support
var features = {};

View file

@ -63,6 +63,8 @@ return windfield;
var timing_loop = func {
if (local_weather.local_weather_running_flag == 0) {return;}
dt_lw = getprop("/sim/time/delta-sec");
time_lw = time_lw + dt_lw;
@ -79,6 +81,7 @@ if (getprop(lw~"timing-loop-flag") ==1) {settimer(timing_loop, 0);}
var quadtree_loop = func {
if (local_weather.local_weather_running_flag == 0) {return;}
var vangle = 0.55 * getprop("/sim/current-view/field-of-view");
var viewdir = getprop("/sim/current-view/goal-heading-offset-deg");
@ -161,6 +164,8 @@ if (getprop(lw~"dynamics-loop-flag") ==1) {settimer(quadtree_loop, 0);}
var weather_dynamics_loop = func (index, cindex) {
if (local_weather.local_weather_running_flag == 0) {return;}
var n = 20;
var nc = 1;
@ -241,11 +246,16 @@ if (j >= csize) {cindex = 0;}
foreach (s; local_weather.weatherStationArray)
foreach (var s; local_weather.weatherStationArray)
{
s.move();
}
foreach (var a; local_weather.atmosphereIpointArray)
{
a.move();
}
if (getprop(lw~"dynamics-loop-flag") ==1) {settimer( func {weather_dynamics_loop(index, cindex); },0);}
}
@ -257,6 +267,8 @@ if (getprop(lw~"dynamics-loop-flag") ==1) {settimer( func {weather_dynamics_loop
var convective_loop = func {
if (local_weather.local_weather_running_flag == 0) {return;}
# a 30 second loop needs a different strategy to end, otherwise there is trouble if it is restarted while still running
if (convective_loop_kill_flag == 1)

View file

@ -34,6 +34,9 @@
var tile_management_loop = func {
if (local_weather.local_weather_running_flag == 0) {return;}
var tNode = props.globals.getNode(lw~"tiles", 1).getChildren("tile");
var viewpos = geo.aircraft_position(); # using viewpos here triggers massive tile ops for tower view...
var code = getprop(lw~"tiles/tile[4]/code");
@ -62,6 +65,35 @@ if ((local_weather.metar_flag == 1) and (getprop(lw~"METAR/station-id") != getpr
weather_tiles.set_METAR_weather_station();
}
# compute the averaged framerate and see if cloud visibility needs to be adjusted
if (local_weather.fps_control_flag == 1)
{
local_weather.fps_average = local_weather.fps_sum/local_weather.fps_samples;
# print("Average framerate: ", local_weather.fps_average);
local_weather.fps_sum = 0.0;
local_weather.fps_samples = 0;
if (local_weather.fps_average > 1.1 * local_weather.target_framerate)
{
var target_cloud_view_distance = cloud_view_distance * 1.1;
if (target_cloud_view_distance > 45000.0)
{target_cloud_view_distance = 45000.0;}
setprop(lw~"config/clouds-visible-range-m", target_cloud_view_distance);
}
if (local_weather.fps_average < 0.9 * local_weather.target_framerate)
{
var target_cloud_view_distance = cloud_view_distance * 0.9;
if (target_cloud_view_distance < 15000.0)
{target_cloud_view_distance = 15000.0;}
setprop(lw~"config/clouds-visible-range-m", target_cloud_view_distance);
}
}
foreach (var t; tNode) {
@ -441,7 +473,7 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile")
else if (code == "cold_sector") {weather_tiles.set_cold_sector_tile();}
else if (code == "warm_sector") {weather_tiles.set_warm_sector_tile();}
else if (code == "tropical_weather") {weather_tiles.set_tropical_weather_tile();}
#else if (code == "test") {weather_tiles.set_4_8_stratus_tile;}
else if (code == "test") {weather_tiles.set_4_8_stratus_tile();}
else
{
print("Repeat tile not implemented with this tile type!");
@ -450,50 +482,50 @@ if (getprop(lw~"tmp/tile-management") == "repeat tile")
}
else if (getprop(lw~"tmp/tile-management") == "realistic weather")
{
var rn = rand();
var rn = rand() * getprop(lw~"config/large-scale-persistence");
if (code == "low_pressure_core")
{
if (rn > 0.2) {weather_tiles.set_low_pressure_core_tile();}
if (rn > 0.1) {weather_tiles.set_low_pressure_core_tile();}
else {weather_tiles.set_low_pressure_tile();}
}
else if (code == "low_pressure")
{
if (rn > 0.2) {weather_tiles.set_low_pressure_tile();}
else if (rn > 0.1) {weather_tiles.set_low_pressure_core_tile();}
if (rn > 0.1) {weather_tiles.set_low_pressure_tile();}
else if (rn > 0.05) {weather_tiles.set_low_pressure_core_tile();}
else {weather_tiles.set_low_pressure_border_tile();}
}
else if (code == "low_pressure_border")
{
if (rn > 0.4) {weather_tiles.set_low_pressure_border_tile();}
else if (rn > 0.3) {weather_tiles.set_cold_sector_tile();}
else if (rn > 0.2) {weather_tiles.set_warm_sector_tile();}
else if (rn > 0.1) {weather_tiles.set_low_pressure_tile();}
if (rn > 0.2) {weather_tiles.set_low_pressure_border_tile();}
else if (rn > 0.15) {weather_tiles.set_cold_sector_tile();}
else if (rn > 0.1) {weather_tiles.set_warm_sector_tile();}
else if (rn > 0.05) {weather_tiles.set_low_pressure_tile();}
else {weather_tiles.set_high_pressure_border_tile();}
}
else if (code == "high_pressure_border")
{
if (rn > 0.4) {weather_tiles.set_high_pressure_border_tile();}
else if (rn > 0.3) {weather_tiles.set_cold_sector_tile();}
else if (rn > 0.2) {weather_tiles.set_warm_sector_tile();}
else if (rn > 0.1) {weather_tiles.set_high_pressure_tile();}
if (rn > 0.2) {weather_tiles.set_high_pressure_border_tile();}
else if (rn > 0.15) {weather_tiles.set_cold_sector_tile();}
else if (rn > 0.1) {weather_tiles.set_warm_sector_tile();}
else if (rn > 0.05) {weather_tiles.set_high_pressure_tile();}
else {weather_tiles.set_low_pressure_border_tile();}
}
else if (code == "high_pressure")
{
if (rn > 0.2) {weather_tiles.set_high_pressure_tile();}
else if (rn > 0.1) {weather_tiles.set_high_pressure_border_tile();}
if (rn > 0.1) {weather_tiles.set_high_pressure_tile();}
else if (rn > 0.05) {weather_tiles.set_high_pressure_border_tile();}
else {weather_tiles.set_high_pressure_core_tile();}
}
else if (code == "high_pressure_core")
{
if (rn > 0.2) {weather_tiles.set_high_pressure_core_tile();}
if (rn > 0.1) {weather_tiles.set_high_pressure_core_tile();}
else {weather_tiles.set_high_pressure_tile();}
}
else if (code == "cold_sector")
{
if (rn > 0.3) {weather_tiles.set_cold_sector_tile();}
else if (rn > 0.2)
if (rn > 0.15) {weather_tiles.set_cold_sector_tile();}
else if (rn > 0.1)
{
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_warmfront1_tile();}
@ -502,13 +534,13 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_coldfront_tile();}
}
else if (rn > 0.1) {weather_tiles.set_low_pressure_border_tile();}
else if (rn > 0.05) {weather_tiles.set_low_pressure_border_tile();}
else {weather_tiles.set_high_pressure_border_tile();}
}
else if (code == "warm_sector")
{
if (rn > 0.3) {weather_tiles.set_warm_sector_tile();}
else if (rn > 0.2)
if (rn > 0.15) {weather_tiles.set_warm_sector_tile();}
else if (rn > 0.1)
{
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_coldfront_tile();}
@ -517,7 +549,7 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warmfront4_tile();}
}
else if (rn > 0.1) {weather_tiles.set_low_pressure_border_tile();}
else if (rn > 0.05) {weather_tiles.set_low_pressure_border_tile();}
else {weather_tiles.set_high_pressure_border_tile();}
}
else if (code == "warmfront1")
@ -525,7 +557,12 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_warmfront2_tile();}
else if ((dir_index ==3) or (dir_index ==5))
{weather_tiles.set_warmfront1_tile();}
{
if (rand() > 0.15)
{weather_tiles.set_warmfront1_tile();}
else
{weather_tiles.set_high_pressure_border_tile();}
}
else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_cold_sector_tile();}
}
@ -534,7 +571,12 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_warmfront3_tile();}
if ((dir_index ==3) or (dir_index ==5))
{weather_tiles.set_warmfront2_tile();}
{
if (rand() > 0.15)
{weather_tiles.set_warmfront2_tile();}
else
{weather_tiles.set_high_pressure_border_tile();}
}
if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warmfront1_tile();}
}
@ -543,7 +585,12 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_warmfront4_tile();}
if ((dir_index ==3) or (dir_index ==5))
{weather_tiles.set_warmfront3_tile();}
{
if (rand() > 0.15)
{weather_tiles.set_warmfront3_tile();}
else
{weather_tiles.set_low_pressure_border_tile();}
}
if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warmfront2_tile();}
}
@ -552,7 +599,12 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_warm_sector_tile();}
if ((dir_index ==3) or (dir_index ==5))
{weather_tiles.set_warmfront4_tile();}
{
if (rand() > 0.15)
{weather_tiles.set_warmfront4_tile();}
else
{weather_tiles.set_low_pressure_tile();}
}
if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warmfront3_tile();}
}
@ -561,7 +613,12 @@ else if (getprop(lw~"tmp/tile-management") == "realistic weather")
if ((dir_index ==0) or (dir_index ==1) or (dir_index==2))
{weather_tiles.set_cold_sector_tile();}
else if ((dir_index ==3) or (dir_index ==5))
{weather_tiles.set_coldfront_tile();}
{
if (rand() > 0.15)
{weather_tiles.set_coldfront_tile();}
else
{weather_tiles.set_high_pressure_border_tile();}
}
else if ((dir_index ==6) or (dir_index ==7) or (dir_index==8))
{weather_tiles.set_warm_sector_tile();}
}
@ -1039,6 +1096,8 @@ setprop(lw~"tiles/tile[8]/orientation-deg",alpha);
var buffer_loop = func (index) {
if (local_weather.local_weather_running_flag == 0) {return;}
var n = 5;
var n_max = size(cloudBufferArray);
var s = size(active_tile_list);
@ -1171,6 +1230,8 @@ if (getprop(lw~"buffer-loop-flag") ==1) {settimer( func {buffer_loop(i)}, 0);}
var housekeeping_loop = func (index) {
if (local_weather.local_weather_running_flag == 0) {return;}
var n = 5;
var n_max = size(cloudSceneryArray);
n_cloudSceneryArray = n_max;
@ -1379,8 +1440,7 @@ var lon_to_m = 0.0; #local_weather.lon_to_m;
var m_to_lon = 0.0; # local_weather.m_to_lon;
var lw = "/local-weather/";
var cloud_view_distance = 20000.0;
var cloud_view_distance = getprop(lw~"config/clouds-visible-range-m");
var modelArrays = [];
var active_tile_list = [];

View file

@ -216,6 +216,7 @@ var window = {
#
# dpy.interval = 0; # update every frame
# dpy.format = "%.3g"; # max. 3 digits fractional part
# dpy.tagformat = "%-12s"; # align prop names to 12 spaces
# dpy.redraw(); # pick up new settings
#
#
@ -240,6 +241,7 @@ var display = {
m.tags = show_tags;
m.font = "HELVETICA_14";
m.color = [1, 1, 1, 1];
m.tagformat = "%s";
m.format = "%.12g";
m.interval = 0.1;
#
@ -319,9 +321,10 @@ var display = {
if (e.node.getPath() == path)
continue nextprop;
e.parent = e.node;
e.tag = me.nameof(e.node);
e.tag = sprintf(me.tagformat, me.nameof(e.node));
}
append(me.entries, { node: n, parent: n, tag: me.nameof(n),
append(me.entries, { node: n, parent: n,
tag: sprintf(me.tagformat, me.nameof(n)),
target: me.base.getChild("entry", size(me.entries), 1) });
}

View file

@ -16,7 +16,6 @@ var step_iter_count = 0; # number or step loop iterations
var last_step_time = nil; # for set_targets() eta calculation
var audio_dir = nil;
# property nodes (to be initialized with listener)
var markerN = nil;
var headingN = nil;
@ -26,7 +25,7 @@ var last_messageN = nil;
var step_countN = nil;
var step_timeN = nil;
_setlistener("/sim/signals/nasal-dir-initialized", func {
_setlistener("/nasal/tutorial/loaded", func {
markerN = props.globals.getNode("/sim/model/marker", 1);
headingN = props.globals.getNode("/orientation/heading-deg", 1);
slipN = props.globals.getNode("/orientation/side-slip-deg", 1);
@ -37,8 +36,6 @@ _setlistener("/sim/signals/nasal-dir-initialized", func {
setlistener("/sim/crashed", stopTutorial);
});
var startTutorial = func {
var name = getprop("/sim/tutorials/current-tutorial");
if (name == nil) {
@ -192,7 +189,16 @@ var step_tutorial = func(id) {
var exit = step.getNode("exit");
if (exit != nil) {
if (!props.condition(exit.getNode("condition")))
{
if (time_elapsedN.getValue() - step_start_time > 15.0)
{
# What's going on? Repeat last message.
last_messageN.setValue("");
step_start_time = time_elapsedN.getValue();
do_group(step, "Tutorial step " ~ current_step);
}
return continue_after(exit, step_interval);
}
do_group(exit);
}
@ -331,6 +337,9 @@ var set_view = func(node = nil) {
node != nil or return;
var v = node.getChild("view");
if (v != nil) {
# when changing view direction, switch to view 0 (captain's view),
# unless another view is explicitly specified
v.initNode("view-number", 0, "INT", 0);
view.point.move(v);
return 1;
}
@ -363,7 +372,6 @@ var is_running = func(which = nil) {
var prop = "/sim/tutorials/running";
if (which != nil) {
setprop(prop, which);
gui.menuEnable("tutorial-stop", which);
}
return getprop(prop);
}

Some files were not shown because too many files have changed in this diff Show more