1
0
Fork 0

- new version with doc by Zakharov

This commit is contained in:
helijah 2008-07-14 14:40:22 +00:00
parent ce1b5ac7e1
commit 763860f42c
7 changed files with 603 additions and 100 deletions

View file

@ -63,10 +63,6 @@ var screenAirportInfos = {
page : 0,
rwylist: [],
right : func {
me.page = 0;
displayed_screen = 4;# screenAirportMain
},
left : func {
np = int(size(me.rwylist) / 4) + (math.mod(size(me.rwylist),4) ? 1 : 0);
me.page = cycle(np, me.page, arg[0]);
},
@ -83,7 +79,7 @@ var screenAirportInfos = {
screenAirportMain.apt.runways[r].width]);
line[0].setValue(sprintf("%s", screenAirportMain.apt.name)); #TODO check length to truncate if too long
rwyindex = me.page * 4;
for (var l = 1; l < 5; l += 1) {
for (var l = 1; l < LINES; l += 1) {
rwyindex += 1;
if (rwyindex < size(me.rwylist))
line[l].setValue(sprintf("%s [%dm / %dm]",
@ -108,13 +104,14 @@ var screenSearchAirport = {
var found = screenAirportMain.search();
if (found != 0) {
screenAirportMain.searched = 1;
screenEdit.previous_page = 0;
return 1;
}
else
return 0;
},
lines : func {
EditMode(4, "AIRPORT CODE", "SEARCH", 2, 0);
EditMode(4, "AIRPORT CODE", "SEARCH");
}
};

View file

@ -1,41 +1,33 @@
var screenModeAndSettings = { # screen for changing the GPS mode and settings
help : 0,
mode_: 0,
page_: 0,
available_modes : ["POSITION","AIRPORT","TURNPOINT","TASK"],
quit_help : func {
me.help = 0;
me.lines();
},
right : func {
if (page == 1)
alt_unit = cycle(2, alt_unit, arg[0]);
alt_unit = cycle(size(alt_unit_full_name), alt_unit, arg[0]);
elsif (page == 2)
dist_unit = cycle(2, dist_unit, arg[0]);
dist_unit = cycle(size(dist_unit_full_name), dist_unit, arg[0]);
elsif (page == 3)
spd_unit = cycle(2, spd_unit, arg[0]);
spd_unit = cycle(size(spd_unit_full_name), spd_unit, arg[0]);
elsif (page == 4)
thresold_alert_index = cycle(size(thresold_alert), thresold_alert_index, arg[0]);
elsif (page == 5)
thresold_next_waypoint = cycle(10, thresold_next_waypoint, arg[0]);
},
changemode : func {
if (page == 0) me.mode_ = cycle(4, me.mode_, arg[0]);
},
enter : func {
if (!me.help) {
display ([
"HERE THERE WILL SEAT",
"A SIMPLE EXPLANATION",
"TEXT ABOUT USE OF GPS",
"PRESS ANY OF THE",
"THREE BUTTONS"
]);
display (NOT_YET_IMPLEMENTED);
me.help = 1;
}
else me.quit_help();
},
escape : func {
if (me.help) me.quit_help();
else me.dispatch();
},
start : func {
if (me.help) me.quit_help();
@ -47,10 +39,7 @@ var screenModeAndSettings = { # screen for changing the GPS mode and settings
},
lines : func {
if (page == 0) {
if (me.mode_ == 0) mode_str = "POSITION";
elsif (me.mode_ == 1) mode_str = "AIRPORT";
elsif (me.mode_ == 2) mode_str = "TURNPOINT";
else mode_str = "TASK";
var mode_str = me.available_modes[me.mode_];
l0 = " -- GPS STATUS : --";
l1 = sprintf("MODE: %s", mode_str);
}
@ -87,7 +76,7 @@ var screenPositionMain = { # screens for POSITION mode
enter : func {
var ac = geo.aircraft_position();
me.coord = [ac.lat(), ac.lon(), ac.alt()];
EditMode(6, "EDIT WAYPOINT ID", "SAVE", mode, page);
EditMode(6, "EDIT WAYPOINT ID", "SAVE");
},
escape : func {
},
@ -257,29 +246,29 @@ var screenNavigationMain = {
var screenEdit = {
previous_mode: 0,
previous_page: 0,
alphanum: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
"Q","R","S","T","U","V","W","X","Y","Z",
"0","1","2","3","4","5","6","7","8","9"],
numeric: ["0","1","2","3","4","5","6","7","8","9","."],
carset: [["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
"Q","R","S","T","U","V","W","X","Y","Z", "0","1","2","3","4","5","6","7","8","9"],
["0","1","2","3","4","5","6","7","8","9","."]],
start_command: "",
edit_zone: "",
edit_title : "",
carset: [],
set: 0,
map: [],
pointer: 0,
value: 0,
init: func (length, title, start_command, backmode, backpage, set = 0) {
init: func (length, title, start_command, set) {
me.map = [];
for (var i = 0; i < length; i += 1) append(me.map, "-");
me.edit_title = title;
me.start_command = start_command;
me.carset = set != 0 ? me.numeric : me.alphanum;
me.previous_mode = backmode;
me.previous_page = backpage;
me.set = set;
me.pointer = 0;
me.value = 0;
left_knob(0); # force display
},
right : func {
me.value = cycle(size(me.carset), me.value, arg[0]);
me.map[me.pointer] = me.carset[me.value];
me.value = cycle(size(me.carset[me.set]), me.value, arg[0]);
me.map[me.pointer] = me.carset[me.set][me.value];
},
enter : func {
me.pointer = cycle(size(me.map), me.pointer, 1);
@ -292,7 +281,7 @@ var screenEdit = {
me.start_command = "";
me.edit_zone = "";
me.edit_title = "";
me.carset = [];
me.set = 0;
me.map = [];
mode = me.previous_mode;
page = me.previous_page;
@ -302,7 +291,10 @@ var screenEdit = {
var str = "";
for (var i = 0; i < size(me.map); i += 1)
str ~= me.map[i] != "-" ? me.map[i] : "";
if (screen[page_list[me.previous_mode][me.previous_page]].start(str)) me.escape();
if (screen[page_list[me.previous_mode][me.previous_page]].start(str))
me.escape();
else
me.init(size(me.map), me.edit_title, me.start_command, me.set);
},
lines : func {
me.right(0); #init car

View file

@ -54,9 +54,9 @@ var screenTaskSelect = {
""
]);
}
else for (var l = 0; l < 5; l += 1) {
if ((me.page * 5 + l) < me.n) {
name = routes[me.page * 5 + l];
else for (var l = 0; l < LINES; l += 1) {
if ((me.page * LINES + l) < me.n) {
name = routes[me.page * LINES + l];
if (substr(name, -4) == ".xml") name = substr(name, 0, size(name) - 4);
name = string.uc(name);
line[l].setValue(sprintf("%s %s",me.pointer == l ? ">" : " ", name));
@ -83,9 +83,9 @@ var screenWaypointsList = {
start : func {
},
lines : func {
for (var l = 0; l < 5; l += 1) {
if ((me.page * 5 + l) < me.n) {
name = gps_data.getNode("route/Waypoint["~((me.page*5) + l)~"]/ID").getValue();
for (var l = 0; l < LINES; l += 1) {
if ((me.page * LINES + l) < me.n) {
name = gps_data.getNode("route/Waypoint["~((me.page * LINES) + l)~"]/ID").getValue();
line[l].setValue(sprintf("%s %s",me.pointer == l ? ">" : " ", name));
}
else

View file

@ -10,7 +10,7 @@ var screenTurnpointSelect = {
var t = browse(me.n, me.pointer, me.page, arg[0]);
me.pointer = t[0];
me.page = t[1];
me.selected = me.page*5 + me.pointer;
me.selected = me.page * LINES + me.pointer;
},
enter : func {
},
@ -24,10 +24,14 @@ var screenTurnpointSelect = {
gps_wp.getNode("wp/ID").setValue("startpos");
var bookmark = gps_data.getNode("bookmarks/bookmark["~me.selected~"]/");
gps_wp.getNode("wp[1]/latitude-deg",1).setValue(bookmark.getNode("latitude-deg",1).getValue());
gps_wp.getNode("wp[1]/longitude-deg",1).setValue(bookmark.getNode("longitude-deg",1).getValue());
gps_wp.getNode("wp[1]/altitude-ft",1).setValue(bookmark.getNode("altitude-ft",1).getValue());
gps_wp.getNode("wp[1]/ID").setValue(bookmark.getNode("ID",1).getValue());
gps_wp.getNode("wp[1]/latitude-deg",1).setValue(bookmark.getNode("latitude-deg").getValue());
gps_wp.getNode("wp[1]/longitude-deg",1).setValue(bookmark.getNode("longitude-deg").getValue());
gps_wp.getNode("wp[1]/altitude-ft",1).setValue(bookmark.getNode("altitude-ft").getValue());
gps_wp.getNode("wp[1]/ID").setValue(bookmark.getNode("ID").getValue());
if (bookmark.getNode("name") != nil)
gps_wp.getNode("wp[1]/name").setValue(bookmark.getNode("name").getValue());
if (bookmark.getNode("waypoint-type") != nil)
gps_wp.getNode("wp[1]/waypoint-type").setValue(bookmark.getNode("waypoint-type").getValue());
blocked = 0;
page = 1;
mode = 3;
@ -36,9 +40,9 @@ var screenTurnpointSelect = {
lines : func {
if (me.loaded != 1) blocked = 1;
if (me.n > 0)
for (var l = 0; l < 5; l += 1) {
if ((me.page * 5 + l) < me.n) {
name = gps_data.getNode("bookmarks/bookmark["~((me.page * 5) + l)~"]/ID").getValue();
for (var l = 0; l < LINES; l += 1) {
if ((me.page * LINES + l) < me.n) {
name = gps_data.getNode("bookmarks/bookmark["~((me.page * LINES) + l)~"]/ID").getValue();
line[l].setValue(sprintf("%s %s",me.pointer == l ? ">" : " ", name));
}
else

View file

@ -1,12 +1,12 @@
var mode = 0;
var mode = 0; #current mode
var displayed_screen = 0; #screenModeAndSettings
var page = 0;
var blocked = 0;
var isOn = 0;
var freq = 1;
var screen = [];
var line = [];
var routes = [];
var page = 0; #current page
var blocked = 0; #boolean: 0 -> possible to cycle pages
var isOn = 0; #ON/OFF: 0 -> OFF
var freq = 1; #settimer frequency (in sec)
var screen = []; #array containing all screens
var line = []; #array containing the displayed lines
var routes = []; #array containing the preprogrammed tasks
var alt_unit_full_name = ["Feet", "Meters"];
var dist_unit_full_name = ["Nautic Miles", "Kilometers"];
var spd_unit_full_name = ["Knots", "KM/H"];
@ -16,9 +16,8 @@ var spd_unit_short_name = ["kt", "km/h"];
var spd_unit = 0;
var dist_unit = 0;
var alt_unit = 0;
#var apt = nil;
var startpos = nil;
var waypointindex = 0;
var startpos = nil; #geo.nas aircraft position
var waypointindex = 0; #step in actual task
var thresold_alert = [120, 60, 30, 15];
var thresold_alert_index = 1;
var thresold_next_waypoint = 5;
@ -29,7 +28,7 @@ NOT_YET_IMPLEMENTED = [
"IMPLEMENTED",
""
];
var page = 0;
var LINES = 5; #lines in display
var page_list = [
[0,0,0,0,0,0], #0 ModeAndSettings: 1 page for mode, 5 pages for settings
[1,2,3], #1 PositionMain, Odometers, WindInfos
@ -52,54 +51,56 @@ var gps_data = props.globals.getNode("/instrumentation/gps",1);
var gps_wp = gps_data.getNode("wp",1);
#### warps for buttons and knobs ########################################"
var right_knob = func(dir) {
var right_knob = func(dir) { #manage right knob, depends of displayed screen
isOn == 1 or return;
screen[displayed_screen].right(dir);
refresh_display();
}
var enter_button = func() {
var enter_button = func() { #manage enter button, depends of displayed screen
isOn == 1 or return;
screen[displayed_screen].enter();
refresh_display();
}
var escape_button = func() {
var escape_button = func() { #manage escape button, depends of displayed screen
isOn == 1 or return;
screen[displayed_screen].escape();
refresh_display();
}
var start_button = func() {
var start_button = func() { #manage start button, depends of displayed screen
isOn == 1 or return;
screen[displayed_screen].start();
refresh_display();
}
var left_knob = func(dir) {
var left_knob = func(dir) { #manage left button, cycle in mode's pages if not blocked
isOn == 1 or return;
if (blocked == 0) {
page = cycle(size(page_list[mode]), page, dir);
if (blocked == 0) displayed_screen = page_list[mode][page];
displayed_screen = page_list[mode][page];
}
refresh_display();
}
var select_mode = func(dir) {
var select_mode = func(dir) { #manage mode knob, cycle into available modes
isOn == 1 or return;
blocked = 0;
if (displayed_screen != 0) {
displayed_screen = 0; #screenModeAndSettings
page = 0;
screen[displayed_screen].changemode(0);
mode = 0;
}
else
screen[displayed_screen].changemode(dir);
elsif (page == 0)
screen[0].mode_ = cycle(size(screen[0].available_modes), screen[0].mode_, dir);
refresh_display();
}
var switch_ON_OFF = func() {
var switch_ON_OFF = func() { #manage ON/OFF knob
if (isOn) {
isOn = 0;
for (var i = 0; i < 5; i += 1) line[i].setValue("");
for (var i = 0; i < LINES; i += 1) line[i].setValue("");
}
else {
isOn = 1;
@ -111,14 +112,15 @@ var switch_ON_OFF = func() {
}
### useful funcs #########################################################
var display = func () {
for (var i = 0; i < 5; i += 1) line[i].setValue(arg[0][i]);
var display = func () { #display the array line[]
for (var i = 0; i < LINES; i += 1) line[i].setValue(arg[0][i]);
}
var browse = func (entries_nbr, index_pointer, index_page,dir) {
nl = entries_nbr - (index_page * 5) > 5 ? 5 : math.mod(entries_nbr - (index_page * 5), 5);
#browse multipaged entries, returns [pointer in page, page]
nl = entries_nbr - (index_page * LINES) > LINES ? LINES : math.mod(entries_nbr - (index_page * LINES), LINES);
if (index_pointer + 1 == nl) {
np = int(entries_nbr / 5) + (math.mod(entries_nbr,5) ? 1 : 0);
np = int(entries_nbr / LINES) + (math.mod(entries_nbr,LINES) ? 1 : 0);
index_page = cycle(np, index_page, dir);
}
index_pointer = cycle(nl, index_pointer, dir);
@ -126,18 +128,19 @@ var browse = func (entries_nbr, index_pointer, index_page,dir) {
}
var cycle = func (entries_nbr, actual_entrie, dir) {
#cycle through entries, return entry index
entries_nbr -= 1;
if (dir == 1 and actual_entrie == entries_nbr) return 0;
elsif (dir == -1 and actual_entrie == 0) return entries_nbr;
else return actual_entrie + dir;
}
var refresh_display = func() {
var refresh_display = func() { #refresh displayed lines, settimer if necessary
screen[displayed_screen].lines();
if (isOn and 0 < displayed_screen < 5 ) settimer(func { refresh_display(); }, freq, 1);
}
var seconds_to_string = func (time) {
var seconds_to_string = func (time) { #converts secs (double) in string "hh:mm:ss"
var hh = int(time / 3600);
if (hh > 100) return "--:--:--";
var mm = int((time - (hh * 3600)) / 60);
@ -146,7 +149,7 @@ var seconds_to_string = func (time) {
}
### route management ######################################################
var list_routes = func {
var list_routes = func { #load preprogrammed tasks
routes = [];
var path = getprop("/sim/fg-home") ~ "/Routes";
var s = io.stat(path);
@ -163,7 +166,7 @@ var list_routes = func {
return size(routes);
}
var add_waypoint = func (ID, name, type, coord) {
var add_waypoint = func (ID, name, type, coord) { #add a waypoint to a route
var waypoint = gps_data.getNode("route/Waypoint["~screenWaypointsList.n~"]/",1);
screenWaypointsList.n += 1;
waypoint.getNode("ID",1).setValue(ID);
@ -175,7 +178,8 @@ var add_waypoint = func (ID, name, type, coord) {
waypoint.getNode("waypoint-type",1).setValue(type);
}
var save_route = func {
var save_route = func { #save the route
screenWaypointsList.n != 0 or return;
var first_id = gps_data.getNode("route/Waypoint/ID").getValue();
var last_id = gps_data.getNode("route/Waypoint["~(screenWaypointsList.n - 1)~"]/ID").getValue();
var path = getprop("/sim/fg-home") ~ "/Export/"~first_id~"-"~last_id~".xml";
@ -185,7 +189,7 @@ var save_route = func {
fgcommand("savexml", args);
}
var waypointAlert = func {
var waypointAlert = func { #alert pilot about waypoint approach
mode > 0 or return;
var ttw = gps_wp.getNode("wp[1]/TTW",1).getValue();
var ttw_secs = 9999;
@ -197,12 +201,12 @@ var waypointAlert = func {
else
gps_data.getNode("waypoint-alert",1).setBoolValue(0);
if (mode == 3 and ttw_secs < thresold_next_waypoint)
if (mode == 4 and ttw_secs < thresold_next_waypoint)
screenNavigationMain.nextWaypoint();
}
### turnpoints management ######################################################
var load_bookmarks = func {
var load_bookmarks = func { #load turnpoints
var n = 0;
gps_data.getNode("bookmarks",1).removeChildren("bookmark");
var file = getprop("/sim/fg-home") ~ "/Export/bookmarks.xml";
@ -217,7 +221,7 @@ var load_bookmarks = func {
return n;
}
var save_bookmarks = func {
var save_bookmarks = func { #save turnpoints
var path = getprop("/sim/fg-home") ~ "/Export/bookmarks.xml";
var args = props.Node.new({ filename : path });
var export = args.getNode("data", 1);
@ -225,30 +229,39 @@ var save_bookmarks = func {
fgcommand("savexml", args);
}
var add_bookmark = func (ID, name, type, coord) {
var add_bookmark = func (ID, name, type, coord) { #add turnpoint
var bookmark = gps_data.getNode("bookmarks/bookmark["~screenTurnpointSelect.n~"]/",1);
screenTurnpointSelect.n += 1;
bookmark.getNode("ID",1).setValue(ID);
bookmark.getNode("latitude-deg",1).setDoubleValue(coord[0]);
bookmark.getNode("longitude-deg",1).setDoubleValue(coord[1]);
bookmark.getNode("altitude-ft",1).setDoubleValue(coord[2]*alt_conv[1][0]);
bookmark.getNode("name",1).setValue(name);
bookmark.getNode("desc",1).setValue("no infos");
bookmark.getNode("name",1).setValue(name);
bookmark.getNode("waypoint-type",1).setValue(type);
save_bookmarks();
}
var EditMode = func (length, start_command, start_func, backmode, backpage, numcar = 0) {
#screenEdit.previous_mode = backmode;
#screenEdit.previous_page = backpage;
var EditMode = func (length, title, start_command, numcar = 0) {
#special mode for editing simple text
screenEdit.previous_mode = mode;
screenEdit.previous_page = page;
mode = 5; #ID edition
page = 0;
screenEdit.init(length, start_command, start_func, backmode, backpage, numcar = 0);
screenEdit.init(length, title, start_command, numcar);
}
### initialisation stuff ###################################################
var init = func() {
for (var i = 0; i < 5; i += 1) {
mode = 0;
page = 0;
displayed_screen = 0; #screenModeAndSettings
blocked = 0; #unlock left_knob
isOn = 0; #start OFF
startpos = nil; #unset start position
waypointindex = 0; #route waypoint index on beginning
screen = []; #empty screens
for (var i = 0; i < LINES; i += 1) {
append(line, props.globals.getNode("/instrumentation/zkv500/line[" ~ i ~ "]", 1));
line[i].setValue("");
}

View file

@ -0,0 +1,389 @@
############## GPL
# GPS ZKV500 # Sébastien MARQUE
############## 2008, Paris (France)
note that it is still under heavy development, some functions planned haven't
been 100% tested yet... there surely will be bugs or errors (most of the time:
nasal runtime error : props.setValue() with non number, etc.)
CONTENTS
=========
1. A bit of history
2. Install onboard
1. Normal installation
2. Debugging installation
3. Without 3D interface
3. Global presentation
4. User's Manual
1. Powering ON
2. List of screens
3. The modes
4. Editing some text
5. FG integration
6. Examples
7. The future
1. A BIT OF HISTORY
===================
Don't search on the market this device, it doesn't exists! :p Indeed I was
looking how to create a GPS device in my favorite aircraft, I found the
nice 3D object in the not less nice Grob-G115, which it was told me it
is intented for the LX5000 gps (http://www.wingsandwheels.com/manuals.htm).
So I began to write some nasal code, using the C++ implementation of KLN89
already built-in FG, to fit as much as possible the LX5000 specifications.
But after some tries, I began to create on my own way the interface (which
is much more funny for me than following a manual :)). The ZKV500 was born.
(read ZaKharoV, which is my current nickname, from Proktor Zakharov in Sid
Meyer's Alpha Centauri game :))
It keeps by default the very nice 3D object from Jon Stockill, a bit modified.
I created also for this device a special font in order to make the LCD diplay
more... lcd-compliant (see Fonts/lcd.README).
2. INSTALL THE ZKV500 ONBOARD
=============================
1. Normal installation
For now the ZKV500 seats in Aircraft/Instruments-3d/zvk500/, making it easily
available for all aircrafts. To install it, just add these lines in your config
files, inside the <nasal> section:
<zkv500>
<file>Aircraft/Instruments-3d/zkv500/ZKV500.nas</file>
<file>Aircraft/Instruments-3d/zkv500/MainScreens.nas</file>
<file>Aircraft/Instruments-3d/zkv500/TurnpointScreens.nas</file>
<file>Aircraft/Instruments-3d/zkv500/TaskScreens.nas</file>
<file>Aircraft/Instruments-3d/zkv500/AirportScreens.nas</file>
</zkv500>
[example from Lionceau: Aircraft/Lionceau/lionceau-base.xml]
Please note that <zkv500> is mandatory for namespaces reasons.
Then in your panel configuration file, just put the 3D model, as usual:
<!-- GPS -->
<model>
<path>Aircraft/Instruments-3d/zkv500/ZKV500.xml</path>
<offsets>
<x-m> 0.002 </x-m>
<y-m> -0.051 </y-m>
<z-m> -0.022 </z-m>
</offsets>
</model>
[example from Lionceau: Aircraft/Lionceau/Models/Panel/front-panel.xml]
Your ZKV500 is ready to be powered on!
The ZKV500 has IO capacities: routes are red from $FGHOME/Routes and
written in $FGHOME/Export, the bookmarks are red and written in
$FGHOME/Export/bookmarks.xml
An easy way to populate your $FGHOME/Routes directory is a perl script
of my own (see the end of file for an how-to). You can also editing by
hand, the format is simple and conform to the format used by the KLN89
<PropertyList>
<Waypoint n="0">
<ID type="double">TL</ID>
<latitude-deg type="double">43.28838900</latitude-deg>
<longitude-deg type="double">000.04963900</longitude-deg>
<altitude-ft type="double">0</altitude-ft>
<waypoint-type type="string">NBD</waypoint-type>
<name type="string">TARBES NDB</name>
<desc type="string">Xroads freq: 321</desc>
</Waypoint>
<Waypoint n="1">
<ID type="string">LFCB</ID>
<latitude-deg type="double">42.800000</latitude-deg>
<longitude-deg type="double">000.600000</longitude-deg>
<altitude-ft type="double">2028</altitude-ft>
<waypoint-type type="string">APT</waypoint-type>
<name type="string">Bagneres de Luchon</name>
<desc type="string">In a valley</desc>
</Waypoint>
</PropertyList>
The less easy way is to edit your flightplan direcly from the ZKV500, but
as it has very limited editing capacities, and the search of already-known
waypoints is limited to airports, it is not a really recommended way.
The $FGHOME/Export/bookmarks.xml file contains favorites points, it has
quite the same format than a route file:
<PropertyList>
...
<bookmark n="2">
<ID type="string">LFBT</ID>
<latitude-deg type="double">43.178643</latitude-deg>
<longitude-deg type="double">-0.006341</longitude-deg>
<altitude-ft type="double">1179.785445</altitude-ft>
<name type="string">Tarbes Ossun Lourdes</name>
<desc type="string">no infos</desc>
<waypoint-type type="string">APT</waypoint-type>
</bookmark>
...
</PropertyList>
The ZKV500 is quite useful to create bookmarks on the fly.
2. Debugging installation
if you want to debug the nasal, you can use the "debugging tool" I've created:
- comment in your aircraft files all gps-related installation
- edit the Aircraft/Instruments-3d/zkv500/*.nas files with your favourite
text editor
- in the Nasal console from FGside type:
var zkv_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/zkv500/";
io.load_nasal(zkv_dir ~ "zkv_dbg.nas", "zkv_dbg");
zkv_dbg.reload_zkv_code();
- click on "Execute"
- find errors
- modify source code then re-click on "Execute" until satisfaction (or death :D)
3. Without 3D interface
if you want to use the zkv500 without the 3D interface and all installation steps
as described above, type in a Nasal console:
var zkv_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/zkv500/";
io.load_nasal(zkv_dir ~ "zkv_dbg.nas", "zkv_dbg");
zkv_dbg.test();
3. GLOBAL PRESENTATION
======================
The interface is simple:
(http://seb.marque.free.fr/fichiers/flightgear/zkv500/presentation.png)
* 4 knobs turning left (left mouse clic) or right (right mous clic)
* 3 white buttons
* 1 LCD display
* 2 LEDs (red and green)
enter escape start
button button button
PAGE|------------------------|SELECT
KNOB| |KNOB
| LCD DISPLAY |
| 5 LINES |
MODE| |ON/OFF
KNOB|------------------------|KNOB
red green
led led
-> PAGE knob allows you to cycle in the different pages available for
each mode.
-> SELECT knob allows yo to navigate in a page, or select element, or
edit some simple text (depending of screen).
-> MODE knob allows you to select a mode, and acces to the ZKV500 settings
you can access this knob at any time, any screen.
-> ON/OFF knob allows you to turn ON and OFF the ZKV500, note it is a knob
because I plan to associate some sounds with the ZKV500, this knob then
could be used to change the volume.
The goal of each button depends of the displayed screen.
4. USER'S MANUAL
================
1. Powering ON
To power ON, just left click on the ON/OFF knob. Clicking again wil make it
turning OFF. When powered you are on the ModeAndSettings screen.
2. Screens list
*** ModeAndSettings ***
From this screen you can change from a mode to another mode (see 4.3 Modes, below)
START...enter in a mode
PAGE....enter in settings pages (cycle)
SELECT..modify settings
the available settings are:
* units
you can change the displayed unit for altitude, distance and speed.
the units are long named, on other screens only short name wilm be
displayed.
* thresolds
you can change the thresold from when the ZKV500 will alert you about
next waypoint approach, or will load the next waypoint into calculation.
*** PositionMain ***
This page gives you infos about the aircraft position.
You find it in every modes.
ENTER...save the position as a bookmark (entering in Edit screen)
LAT: latitude in deg:min:sec
LON: longitude in deg:min:sec
ALT: altitude
HDG: geographic cap (! not magnetic cap, or aircraft heading)
SPD: speed
*** Odometers ***
This screen gives you odo-values. You can find it in every modes.
ESCAPE...reset odometers, except TRIP
ODO: travelled distance
TRIP: total travelled distance
TIME: elapsed time from last reset
AVG HDG: average heading
AVG SPD: average speed
*** WindInfos ***
This screens gives you infos about wind as it should be calculated by the
GPS, indeed, as the calculation is already done elsewhere, it takes the value
from /environnement/* node (btw I don't really know how to calculate correctly
these values). You need to have a airspped > 10kts for having it work. This
screen is available on every modes
SPEED: windspeed
FROM: the direction where the wind come from (geographic North)
*** NavigationMain ***
This screen gives the related position of the aircraft about the calcualted
route. It is only available in TURNPOINT and TASK modes.
ENTER...TURNPOINT mode: add the actual position to the route,
TASK mode: jump to next waypoint
START...save the route (mode TURNPOINT only)
ID: Id of the waypoint to go [waypoint type]
BRG: bearing from the aircraft to the waypoint (geographic)
DST: remaining distance
XCRS: course error in degree (in distance unit)
TTW: remaining estimated time
The graph represent the position of the aircraft along the leg. Each + means 1°,
the | symbol represents the route, the little aircraft represents... the aircraft.
*** AirportMain ***
This screen gives infos about the nearest, or a specific searched airport (see
SearcAirport screen, below). It is only reachable from AIRPORT mode.
ENTER...add the airport to route
ESCAPE..get the nearest airport
START...add airport to bookmarks, and enter TURNPOINT mode, to go directly
NEAREST or SEARCHED APT: OACI code of airport
ELEV: airport altitude
DIST: distance between aircraft and airport
BRG: bearing from aircraft to airport (geographic)
RWY: best runway calculated from known wind, it is to say not really useful
when airport is very far away...
ETE: estimated time enroute to go
*** AirportInfos ***
This screen gives infos about the airport (nearest or searched). It is only
reachable from AIRPORT mode.
SELECT...navigate in the runway list (not yet implemented)
First line gives the full name of airport
Lines below show the runways with the format
heading (length in meters / width in meters)
*** SearchAirport ***
This screen allows you to search an airport from OACI code. It is reachable from
AIRPORT mode.
If the search is successful, the AirportMain screen is shown, in other case the text
is erased, and you are invited to retry.
SELECT...modify letter (from A to Z then 0 to 9, cycled)
ENTER....next step
ESCAPE...back to AirportMain screen, without search
START....search the given OACI code
*** TurnpointSelect ***
This screen shows a list of already bookmarked gps points. It is only available
in TURNPOINT mode.
SELECT...navigate through the list (eventually through multiple pages)
START....begin the navigation from actual position to the bookmark
*** TurnpointInfos ***
This screen is not yet implemented. It will gives infos about the bookmark
*** TaskSelect ***
This screen allows you to choose a route (if one available). It is only available
in TASK mode.
SELECT...navigate through the routes list (eventually through multiple pages)
START....load the route, and begins navigation
*** WaypointInfos ***
Not yet implemented, will give infos about waypoint. Only available in TASK mode.
*** WaypointsList ***
This screen shows the list of waypoint loaded from a route.
SELECT...navigate through the list
*** WaypointEdit ***
This screen allows you to name a gps point in order to bookmark it.
SELECT...cycle letters (from A to Z then 1 to 9)
ENTER....next step
ESCAPE...abort saving bookmark
START....save bookmark
3. the 4 modes
*** POSITION ***
The simpliest one, just give information about the aircraft position. You can
save bookmarks from this mode (pressing ENTER in PositionMain screen).
PAGE...cycle through these screens (both ways):
PositionMain, Odometers, WindInfos
*** AIRPORT ***
Useful to know wher you can put you aircraft safely on the ground, gives other infos
as the best runway, it is possible to search an airport (turn the PAGE knob until
Search airport page), add the airport to a route or as a bookmark (automatically
called by airports ID and name).
PAGE...cycle through these screens (both ways)
AirportMain, NavigationMain, PositionMain, Odometers, WindInfos, AirportInfos, SearchAirport
*** TURNPOINT ***
Useful mode to join a specific point of interest, or to navigate around.
PAGE...cycle through these screens (both ways)
TurnpointSelect, NavigationMain, PositionMain, Odometers, WindInfos, TurnpointInfos
*** TASK ***
Most useful mode :). Follow the route to go through long distances, without being
lost at anytime (even with no VOR or NDB near enough to get capted by radios). Or
if you aren't IFR skilled :D
When approaching a waypoint, a red led blink (this can be set in ModeAndSettings screens)
When on the point (idem), it loads autmatically the next point and calculate the leg infos
When the last point is reached, it come back to TaskSelect screen to allow chosing an other
route.
PAGE...cycle through these screens (both ways)
TaskSelect, NavigationMain, PositionMain, Odometers, WindInfos, WaypointInfos, WaypointsList
5. FG INTEGRATION
=================
You can change the waypoints directly from the property tree (/instrumentation/gps)
or using the GUI (Menu Instruments -> GPS Settings).
Please note that, at this stage of development, I haven't yes set listeners
on the properties, when changing the waypoint, new route is automatically
calculated, but changing them don't call yet the correct mode and page.
6. PRACTICAL EXAMPLES
=====================
7. THE FUTURE
=============
The ZKV500 still lacks many features I plan to implement. I stop adding features
for now, as I want it to be the more bug-free possible before creating other bugs.
1. PLANNED FEATURES
* integrated help
* description pages (waypoints and airports)
* more useful informations
* make use of green led (for which use? surely depends of the above)
* altitude control
* two ways routes (toward and back)
2. PLANNED FEATURES LIMITED BY FG "API'S"
* search for VOR/TAC, NDB and FIX capacities
2. NOT PLANNED FEATURES
* graphical view of regions
8. ANNEXES
==========
1. creating flightplans with ./flightplan
You can get ./flightplan from this link: http://seb.marque.free.fr/fichiers/scripts/perl/flightplan
Make it executable, best to set $FGROOT and $FGHOME to respectives folders
For creating a flightplan from LFPT to LFBD:
$ ./flightplan -d lfpt -a lfbd --wpt
This will write in $FGHOME/Routes a file named lfpt-lfbd.xml, suitable for the
ZKV500
Sébastien MARQUE
seb.marque@free.fr

View file

@ -0,0 +1,108 @@
var dialog = nil;
var namenode = nil;
var close = func {
zkv500.isOn = 0;
fgcommand("dialog-close", namenode);
delete(gui.dialog, "\"zkv500\"");
dialog = nil;
}
var _title = func {
var titlebar = dialog.addChild("group");
titlebar.set("layout", "hbox");
var wdg = titlebar.addChild("text");
wdg.set("label", "test zkv500");
titlebar.addChild("empty").set("stretch", 1);
var wdg = titlebar.addChild("button");
wdg.node.setValues({"pref-width": 16, "pref-height": 16, legend: "", default: 0});
wdg.setBinding("nasal", "zkv_dbg.close()");
}
var _top_buttons = func {
dialog.addChild("hrule");
var buttons = dialog.addChild("group");
buttons.set("layout", "hbox");
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "P", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.left_knob(1)");
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "en", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.enter_button()");
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "es", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.escape_button()");
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "st", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.start_button()");
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "S", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.right_knob(1)");
}
var _content = func {
dialog.addChild("hrule");
var content = dialog.addChild("group");
content.set("layout", "table");
content.set("default-padding", 0);
for (var i = 0; i < 5; i += 1) {
var line = content.addChild("text");
line.node.setValues({"row":i,"col":0,"label":" "});
var line = content.addChild("text");
line.node.setValues({
"row": i,
"col": 1,
"property": "/instrumentation/zkv500/line["~i~"]",
"halign": "left",
"live": 1
});
var line = content.addChild("text");
line.node.setValues({"row":i,"col":2,"label":" "});
}
}
var _bottom_buttons = func {
dialog.addChild("hrule");
var buttons = dialog.addChild("group");
buttons.set("layout", "hbox");
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "M", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.select_mode(1)");
buttons.addChild("empty").set("stretch", 1);
var wdg = buttons.addChild("button");
wdg.node.setValues({legend: "0/1", "pref-height": 20});
wdg.setBinding("nasal", "zkv500.switch_ON_OFF()");
}
var reload_zkv_code = func {
var zkv500_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/zkv500/";
io.load_nasal(zkv500_dir ~ "ZKV500.nas","zkv500");
io.load_nasal(zkv500_dir ~ "AirportScreens.nas","zkv500");
io.load_nasal(zkv500_dir ~ "TurnpointScreens.nas","zkv500");
io.load_nasal(zkv500_dir ~ "MainScreens.nas","zkv500");
io.load_nasal(zkv500_dir ~ "TaskScreens.nas","zkv500");
print("debugger: zkv500 loaded");
zkv500.isOn = 0;
zkv500.init();
}
var test = func {
dialog == nil or close();
reload_zkv_code();
namenode = props.Node.new({"dialog-name" : "zkv500" });
dialog = gui.Widget.new();
dialog.set("name", "zkv500");
dialog.set("layout", "vbox");
dialog.set("default-padding", 0);
_title();
_top_buttons();
_content();
_bottom_buttons();
fgcommand("dialog-new", dialog.prop());
fgcommand("dialog-show", namenode);
print("debugger: zkv500 testing interface loaded");
}