remove README.Protocol and add a README that refers to the "real"
[fg:toms-fgdata.git] / Nasal / track_target.nas
1 # This is a small script that adjusts autopilot target values to track
2 # (fly in formation with) an AI or Multiplayer aircraft.
3
4 # Quick start instructions:
5 #
6 #
7 # 1. Copy this file into $FGROOT/data/Nasal (along with the other
8 #    system nasal scripts.)
9 #
10 # 2. Start up FlightGear selecting an airplane with a reasonably configured
11 #    autopilot that responds to and works with the standard autopilot 
12 #    dialog box (F11).  The MiG 15 is one that works, the 777-200 works,
13 #    the Citation Bravo does not work, the default c172 probably does not
14 #    work, etc.
15 #
16 # 3. Take off and establish stable flight.
17 #
18 # 4. Open the property browser (File->Browse Internal Properties) and navigate
19 #    to /ai/models/  Choose one of the available aircraft[] or multiplayer[]
20 #    entries.  You can look at all those subtrees to find the call sign you
21 #    want.  Also note that the subtree for each entity has a radar area that
22 #    will show range and offset from your current heading.
23 #
24 # 5. Open a second property browser window (upper left click box in the first
25 #    property browser window.)  Navigate to /autopilot/target-tracking/
26 #
27 # 6. Set "/autopilot/target-tracking/target-root" to point to the entity
28 #    path you discovered in step #4.  For instance, this should be set to
29 #    something like /ai/models/multiplayer[2] or /ai/models/aircraft[0]
30 #
31 # 7. Set "/autopilot/target-tracking/goal-range-nm" to the follow distance
32 #    you want.
33 #
34 # 8. Set "/autopilot/target-tracking/enable" = 1, this will turn on the radar
35 #    computation for each ai/multiplayer entity and will tell the tracking
36 #    script to start updating the autopilot settings.
37 #
38 # 9. Open up the autopilot configuration window (F11) and activate any of the
39 #    heading, pitch, and speed axes.  The script will begin updating the heading
40 #    bug angle, the "speed with throttle" value, and the "altitude hold" value.
41 #
42 # 10. You can choose to mix and match any of the autopilot modes you want, i.e.
43 #     you could turn off the heading control and turn manually while the system
44 #     holds speed and altitude for you.
45 #
46 # 11. It always helps to have a sensible target arcraft to chase.  You are
47 #     flying within the turn radius and climb rate limits of your autopilot.
48 #
49 #     Don't forget you are pilot in command and at all times responsible for
50 #     maintaining safe airspeed and altitude.
51 #
52 #     Enjoy the ride!
53
54
55 # print("Target Tracking script loading ...");
56
57 # script defaults (configurable if you like)
58 default_update_period = 0.05;
59 default_goal_range_nm = 0.05;
60 default_target_root = "/ai/models/aircraft[0]";
61 default_min_speed_kt = 120;
62
63 # master enable switch
64 target_tracking_enable = 0;
65
66 # update period
67 update_period = default_update_period;
68
69 # goal range to acheive when following target
70 goal_range_nm = 0;
71
72 # minimum speed so we don't drop out of the sky
73 min_speed_kt = 0;
74
75 # Target property tree root
76 target_root = "";
77
78 # Initialize target tracking
79 TrackInit = func {
80     target_tracking_enable = getprop("/autopilot/target-tracking/enable");
81     if ( target_tracking_enable == nil ) {
82         target_tracking_enable = 0;
83         setprop("/autopilot/target-tracking/enable", target_tracking_enable);
84     }
85
86     update_period = getprop("/autopilot/target-tracking/update-period");
87     if ( update_period == nil ) {
88         update_period = default_update_period;
89         setprop("/autopilot/target-tracking/update-period", update_period);
90     }
91
92     goal_range_nm = getprop("/autopilot/target-tracking/goal-range-nm");
93     if ( goal_range_nm == nil ) {
94         goal_range_nm = default_goal_range_nm;
95         setprop("/autopilot/target-tracking/goal-range-nm", goal_range_nm);
96     }
97
98     min_speed_kt = getprop("/autopilot/target-tracking/min-speed-kt");
99     if ( min_speed_kt == nil ) {
100         min_speed_kt = default_min_speed_kt;
101         setprop("/autopilot/target-tracking/min-speed-kt", min_speed_kt);
102     }
103
104     target_root = getprop("/autopilot/target-tracking/target-root");
105     if ( target_root == nil ) {
106         target_root = default_target_root;
107         setprop("/autopilot/target-tracking/target-root", target_root);
108     }
109 }
110 settimer(TrackInit, 0);
111
112
113 # If enabled, update our AP target values based on the target range,
114 # bearing, and speed
115 TrackUpdate = func {
116     target_tracking_enable = getprop("/autopilot/target-tracking/enable");
117     update_period = getprop("/autopilot/target-tracking/update-period");
118
119     if ( target_tracking_enable == 1 ) {
120         # refresh user configurable values
121         goal_range_nm = getprop("/autopilot/target-tracking/goal-range-nm");
122         target_root = getprop("/autopilot/target-tracking/target-root");
123
124         # force radar debug-mode on (forced radar calculations even if
125         # no radar instrument and ai aircraft are out of range
126         setprop("/instrumentation/radar/debug-mode", 1);
127
128         my_hdg_prop = sprintf("/orientation/heading-magnetic-deg" );
129         my_hdg = getprop(my_hdg_prop);
130
131         my_hdg_true_prop = sprintf("/orientation/heading-deg" );
132         my_hdg_true = getprop(my_hdg_true_prop);
133
134         alt_prop = sprintf("%s/position/altitude-ft", target_root );
135         alt = getprop(alt_prop);
136         if ( alt == nil ) {
137             print("bad property path: ", alt_prop);
138             return;
139         }
140     
141         speed_prop = sprintf("%s/velocities/true-airspeed-kt", target_root );
142         speed = getprop(speed_prop);
143         if ( speed == nil ) {
144             print("bad property path: ", speed_prop);
145             return;
146         }
147     
148         range_prop = sprintf("%s/radar/range-nm", target_root );
149         range = getprop(range_prop);
150         if ( range == nil ) {
151             print("bad property path: ", range_prop);
152             return;
153         }
154     
155         h_offset_prop = sprintf("%s/radar/h-offset", target_root );
156         h_offset = getprop(h_offset_prop);
157         if ( h_offset == nil ) {
158             print("bad property path: ", h_offset_prop);
159             return;
160         }
161
162         if ( h_offset > -90 and h_offset < 90 ) {
163             # in front of us
164             range_error = range - goal_range_nm;
165         } else {
166             # behind us
167             range_error = goal_range_nm - range;
168         }
169         target_speed = speed + range_error * 100.0;
170         if ( target_speed < min_speed_kt ) {
171             target_speed = min_speed_kt;
172         }
173
174         setprop( "/autopilot/settings/target-altitude-ft", alt );
175         setprop( "/autopilot/settings/heading-bug-deg", my_hdg + h_offset );
176         setprop( "/autopilot/settings/true-heading-deg",
177                  my_hdg_true + h_offset );
178         setprop( "/autopilot/settings/target-speed-kt", target_speed );
179     }
180
181     # last thing to do before we return from this function
182     registerTimer();
183 }
184
185
186 # timer handling to cause our update function to be called periodially
187 registerTimer = func {
188     settimer(TrackUpdate, update_period );
189 }
190 registerTimer();
191