first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

11
scripts/CMakeLists.txt Normal file
View File

@@ -0,0 +1,11 @@
# Install bash and zsh completion files.
#
# The install path is not detected at runtime (contrary to what the
# bash-completion documentation suggests, see
# <https://github.com/scop/bash-completion/blob/master/README.md>) to account
# for those cases where the FlightGear installation prefix is not /usr.
# See <https://sourceforge.net/p/flightgear/flightgear/merge-requests/96/#e6f4>
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
install(FILES completion/fg-completion.bash DESTINATION share/bash-completion/completions RENAME fgfs)
install(FILES completion/fg-completion.zsh DESTINATION share/zsh/site-functions RENAME _fgfs)
endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")

50
scripts/atis/README Normal file
View File

@@ -0,0 +1,50 @@
The ATIS Voice Generation HowTo
-------------------------------
Required packages / installation hints by J. Denker
---------------------------------------------------
cpan Audio::Wav
apt-get install festival mbrola sox festlex-oald
cd \$tars
wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/en1/en1-980910.zip
wget http://www.cstr.ed.ac.uk/downloads/festival/1.95/festvox_en1.tar.gz
cd /usr/share/festival/voices/english
mkdir en1_mbrola
cd en1_mbrola
unzip \$tars/en1-980910.zip
cd /usr/share/festival
mkdir lib
cd lib
ln -s ../voices ./
cd /usr/share
tar -xpzvf \$tars/festvox_en1.tar.gz
Generating Voice Files
----------------------
1. Configure paths to fgdata and flightgear sources
export FG_ROOT=/home/whatever/fgdata
export FG_SRC=/home/whatever/flightgear
2. Create phraseology word list
./atis-lex.pl > phraseology.vlist
3. Create airport word list
export ATIS_ONLY=yes
./list-airports.pl | ./words_per_line.sh > airports.vlist
4. Check for and fix non-UTF8 encoded airport names
./find_nonUTF8.pl
5. Generate phraseology voice file
./synth.pl phraseology.vlist phraseology.vce phraseology.wav
6. Generate airport voice file
./synth.pl airports.vlist airports.vce airports.wav
7. Install *.vce and *.wav.gz files in fgdata:
cp phraseology.vce $(FG_ROOT)/ATC/voices/default/.
cp phraseology.wav.gz $(FG_ROOT)/ATC/voices/default/.
cp airports.vce $(FG_ROOT)/ATC/voices/default/.
cp airports.wav.gz $(FG_ROOT)/ATC/voices/default/.

80
scripts/atis/atis-lex.pl Executable file
View File

@@ -0,0 +1,80 @@
#! /usr/bin/perl -w
sub usage {
print <<EoF;
Read the atis_lexicon.hxx file and print
the vocabulary words ... plus phonetic digits and letters.
See also list-airports.pl
Typical usage:
FG_ROOT=/home/whatever/fgdata FG_SRC=/home/whatever/flightgear ./atis-lex.pl > phraseology.vlist
EoF
}
use strict;
use Symbol;
my $fgroot = $ENV{'FG_ROOT'} || '.';
main: {
if (@ARGV) {
usage;
exit;
}
my $mapfn = "$ENV{'FG_SRC'}/src/ATCDCL/atis_lexicon.hxx";
my $mapch = Symbol::gensym;
if (!open($mapch, '<', $mapfn)) {
print STDERR "Could not open abbreviation file '$mapfn'\n";
print STDERR "Maybe you need to set FG_ROOT\n";
exit(1);
}
print "/\n";
while (my $line = <$mapch>) {
chomp $line;
if ($line =~ s/^[ \t]*Q[(]//) {
$line =~ s/[)][ \t]*$//;
print "$line\n";
}
}
print <<EoF;
zero
one
two
three
four
five
six
seven
eight
nine
niner
alpha
bravo
charlie
delta
echo
foxtrot
golf
hotel
india
juliet
kilo
lima
mike
november
oscar
papa
quebec
romeo
sierra
tango
uniform
victor
whiskey
xray
yankee
zulu
decimal
EoF
}

21
scripts/atis/find_nonUTF8.pl Executable file
View File

@@ -0,0 +1,21 @@
#! /usr/bin/perl
my($content, $length);
open(FILE, "< airports.vlist") || die "Unable to open file small. <$!>\n";
while( chomp($content = <FILE>) ) {
$length = length($content);
for( $i = 0; $i < $length; $i++ ) {
if( ord(substr($content, $i, 1)) > 127 )
{
print "$content\n";
last;
}
}
}
close(FILE);
exit 0

155
scripts/atis/list-airports.pl Executable file
View File

@@ -0,0 +1,155 @@
#! /usr/bin/perl -w
sub usage {
print <<EoF;
Read the apt.dat (or apt.dat.gz) file.
Remap airport names to remove ugly abbreviations.
Print airport names, one per line.
Remapping is done by reference to the atis_remap.hxx file.
Typical usage:
FG_ROOT=whatever FG_SRC=whatever ATIS_ONLY=yes ./list-airports.pl | ./words_per_line.sh > airports.vlist
EoF
}
use strict;
use Symbol;
my $noparen = 1;
my $verbose = 0;
my $apt_name = '';
my $lat;
my $lon;
my $atis;
my $country = '';
my $elev;
my $tower;
my $bldgs;
my $apt_id;
my $shapefile;
my $namer = 'NAME';
my $skipping = 0;
my $tot_apts = 0;
my %states = ();
my %short_country = ();
my $fgroot = $ENV{'FG_ROOT'} || '.';
my $atis_only = $ENV{'ATIS_ONLY'} || 0;
my $mapfn = "$ENV{'FG_SRC'}/src/ATCDCL/atis_remap.hxx";
sub process_apt {
if ($atis_only && ! $atis) {
return;
}
my $str .= $apt_name;
$str =~ s' *$''; ## remove trailing spaces
if ($noparen) {
$str =~ s/[(][^)]*[)]?//g;
}
print "$str\n";
$tot_apts++;
}
my %remap = ();
sub get_remap {
# Note: in this context, GKI probably stands for Gereja Kristen Indonesia
# I guess the church builds lots of airports.
my $mapch = Symbol::gensym;
if (!open($mapch, '<', $mapfn)) {
print STDERR "Could not open abbreviation file '$mapfn'\n";
print STDERR "Maybe you need to set FG_ROOT\n";
exit(1);
}
while (my $line = <$mapch>) {
chomp $line;
if ($line =~ s/[ \t]*REMAP[(]//) {
$line =~ s/[)][ \t]*$//;
my @stuff = split(',', $line, 2);
my $from = $stuff[0];
my $to = $stuff[1];
$to =~ s/^[ \t]*//;
if ($to eq 'NIL') {
$to = '';
}
$remap{$from} = $to;
}
}
}
main: {
if (@ARGV) {
usage;
exit;
}
get_remap;
my $delim = '-';
my $fgroot = $ENV{'FG_ROOT'} || 0;
my $incmd = "zcat $fgroot/Airports/apt.dat.gz";
my $inch = Symbol::gensym;
open ($inch, '-|', $incmd)
|| die "Couldn't open pipe from '$incmd'\n";
my $junk = <$inch>;
$junk = <$inch>;
liner: while (my $line = <$inch>) {
chomp $line;
my @stuff = split(' ', $line);
my $type = shift @stuff || 0;
### print "..$type ... $line ...\n";
if ($type == 1) {
## Here if new airport.
##
## First, print results of previous work, i.e. airport
## stanzas already seen ... since the apt.dat file
## doesn't have a clear way of signaling the end of a stanza.
if ($apt_name) {
process_apt();
}
$apt_name = '';
$atis = '';
$lat = 0;
$lon = 0;
$country = '';
$elev = shift @stuff;
$tower = shift @stuff;
$bldgs = shift @stuff;
$apt_id = shift @stuff;
my $name = join $delim, @stuff;
for my $from (keys %remap) {
my $to = $remap{$from};
$name =~ s/\b$from\b/$to/gi;
}
## option for plain words, not hyphenated phrases
if (1) {
$name =~ s/$delim/ /g;
}
$apt_name = "$name";
}
if ($type == 10) {
$lat = $stuff[0];
$lon = $stuff[1];
}
if ($type == 50) {
$atis = join(' ', @stuff);
}
}
process_apt(); ## flush out the very last one
print STDERR "Total airports: $tot_apts\n";
}

BIN
scripts/atis/quiet0.500.wav Normal file

Binary file not shown.

349
scripts/atis/synth.pl Executable file
View File

@@ -0,0 +1,349 @@
#! /usr/bin/perl -w
use strict;
use Symbol;
use Audio::Wav;
sub usage {
print <<EoF;
Run a bunch of words through the speech synthesizer
and collect the results.
Typical usage:
./synth.pl [options] sport.vlist sport.vce sport.wav
Options include:
-skip # skip a line from the .vlist file;
-one # take only the first word from each line
Note: -skip -one is useful if the .vlist file is actually in .vce format.
Other usages:
./synth.pl -dump foo.wav # dump headers 'foo.wav'
./synth.pl -help # print this message
where sport.vlist is a file containing a list of words, we use the
first word on each line and ignore anything else on that line
(which means a .vce file is acceptable as input, using -skip -one).
Note that "atis-lex.pl" and "list-airports.pl" are useful for
creating the .vlist file.
Note that you may also need to:
cpan Audio::Wav
apt-get install festival mbrola sox festlex-oald
cd \$tars
wget http://tcts.fpms.ac.be/synthesis/mbrola/dba/en1/en1-980910.zip
wget http://www.cstr.ed.ac.uk/downloads/festival/1.95/festvox_en1.tar.gz
cd /usr/share/festival/voices/english
mkdir en1_mbrola
cd en1_mbrola
unzip \$tars/en1-980910.zip
cd /usr/share/festival
mkdir lib
cd lib
ln -s ../voices ./
cd /usr/share
tar -xpzvf \$tars/festvox_en1.tar.gz
You may also need to scrounge a non-buggy version of
/usr/local/share/perl/5.10.1/Audio/Wav/Read.pm
EoF
}
my $proto = <<EoF;
(voice_en1_mbrola) ;; best voice I know of
(setq voice_rab_diphone voice_en1_mbrola)
(setq voice_en1 voice_en1_mbrola)
(setq voice_el_diphone voice_en1_mbrola)
(setq voice_ked_diphone voice_en1_mbrola)
(utt.save.wave (utt.synth (Utterance Text "XXX")) "YYY" 'riff)
EoF
my %list;
my %count;
sub doit {
my ($word) = @_;
if (exists $list{lc $word}
&& $list{lc $word} =~ m'^[A-Z][a-z]') {
; # tasteful capitalization; leave as is
} else {
$list{lc $word} = $word;
}
$count{lc $word}++;
}
## This is an ugly way to fix festival's bad guess as to
## pronunciation in special cases.
## In the case of wind (windh versus wynd),
## nominally there are ways of tagging parts-of-speech,
## i.e. noun versus verb,
## but they don't seem to be reliable.
my %fixup = (
'wind' => 'windh',
'romeo' => 'Rome E O',
'xray' => 'X ray',
);
main: {
my $skip = 0;
my $fmtcheck = 1;
my $oneword = 0;
my $gripe = 0;
my $out_bits_sample = 8; ## this is what FGFS expects
my @plain_args = ();
argx: while (@ARGV) {
my $arg = shift @ARGV;
$arg =~ s/^--/-/;
if ($arg eq '-help' || $arg eq '-h') {
usage;
exit(0);
}
if ($arg eq '-dump') {
my $ifn = shift @ARGV || 'foo.wav';
my $wav = new Audio::Wav;
print "About to open '$ifn' ...\n";
my $waver = $wav -> read( $ifn );
print "xxxxxxxxxxxxxxxx\n";
for my $detail (keys %{$waver->details()}) {
printf("%-20s %s\n", $detail, ${$waver->details()}{$detail});
}
exit(0);
}
if ($arg eq '-skip') {
$skip++;
next argx;
}
if ($arg eq '-gripe') {
$gripe++;
next argx;
}
if ($arg eq '-one' || $arg eq '-1') {
$oneword++;
next argx;
}
if ($arg eq '-nocheck') {
$fmtcheck=0;
next argx;
}
if ($arg =~ '^-') {
die "Unrecognized option '$arg'\n";
}
push @plain_args, $arg;
}
my $nargs = @plain_args;
if ($nargs != 3) {
die "Wrong number of arguments ($nargs); for help try:\n $0 -help\n";
}
my @todo = ();
my ($ifn, $indexfn, $out_wav) = @plain_args;
my $inch = Symbol::gensym;
open ($inch, '<', $ifn)
|| die "Couldn't open input file '$ifn'\n";
# Skip some lines from the input list, as requested:
for (my $ii = 0; $ii < $skip; $ii++) {
my $ignore = <$inch>;
}
# Read the rest of the input file:
while (my $line = <$inch>) {
chomp($line);
if ($oneword) {
my @stuff = split(' ', $line, 2);
doit($stuff[0]);
} else {
for my $word (split(' ', $line)) {
doit($word);
}
}
}
close $inch;
# Optionally print a list of things that the input file
# requested more than once.
if ($gripe) {
foreach my $thing (sort keys %count) {
if ($count{$thing} > 1) {
printf("%4d %s\n", $count{$thing}, $list{$thing});
}
}
}
my $nsnip = (keys %list);
if (0 && $nsnip > 10) {
$nsnip = 10;
}
print STDERR "nsnip: $nsnip\n";
my $index = Symbol::gensym;
open ($index, '>', $indexfn)
|| die "Couldn't write index file '$indexfn'\n";
print $index "$nsnip\n";
if (! -d 'snip') {
mkdir('snip')
|| die "Could not create directory 'snip' : $!\n";
}
my $wav = new Audio::Wav;
my $waver = $wav -> read("quiet0.500.wav");
my $sample_rate = -1;
my $channels = -1;
my $bits_sample = -1;
$sample_rate = ${$waver->details()}{'sample_rate'};
$channels = ${$waver->details()}{'channels'};
$bits_sample = ${$waver->details()}{'bits_sample'};
############## system "/bin/cp nothing.wav t1.wav";
my $where = 0;
my $ii = 0;
snipper: for my $thing (sort keys %list) {
$ii++;
my $iix = sprintf('%05d', $ii);
my $xfn = "./snip/x$iix";
print( "$xfn\n");
my $fraise = lc($thing);
if (exists $fixup{$fraise}) {
#xxxx print "fixing $fraise\n";
$fraise = $fixup{$fraise};
}
## This turns dashes and other funny stuff into spaces
## in the phrase to be processed:
$fraise =~ s%[^a-z']+% %gi;
if ($thing eq '/' || $thing eq '/_') {
system("/bin/cp quiet0.500.wav $xfn.wav");
} else {
my $script = $proto;
$script =~ s/XXX/$fraise/;
$script =~ s|YYY|$xfn.wav|;
#xxxx print "$fraise ... $script\n";
my $cmd = '/usr/bin/festival';
my $pipe = Symbol::gensym;
open ($pipe, '|-', $cmd)
|| die "Couldn't open pipe to '$cmd'\n";
print $pipe $script;
close $pipe;
if ($? != 0){
print STDERR "Error in festival script: '$script'\n";
next snipper;
}
}
}
$ii = 0;
snipper: for my $thing (sort keys %list) {
$ii++;
my $iix = sprintf('%05d', $ii);
my $xfn = "./snip/x$iix";
if ($fmtcheck == 1) {
my $wav = new Audio::Wav;
my $waver = $wav -> read("$xfn.wav");
if ($sample_rate < 0) {
$sample_rate = ${$waver->details()}{'sample_rate'};
$channels = ${$waver->details()}{'channels'};
$bits_sample = ${$waver->details()}{'bits_sample'};
} else {
$sample_rate == ${$waver->details()}{'sample_rate'}
&& $channels == ${$waver->details()}{'channels'}
&& $bits_sample == ${$waver->details()}{'bits_sample'}
|| die "audio format not the same: $xfn.wav";
}
}
my $statcmd = "2>&1 sox $xfn.wav -n stat";
my $stat = Symbol::gensym;
open ($stat, '-|', $statcmd)
|| die "Couldn't open pipe from '$statcmd'\n";
my $vol = 0;
my $size = 0;
my $lastline;
while (my $line = <$stat>) {
chomp $line;
$lastline = $line;
my @stuff = split ':', $line;
my $nw = @stuff;
#### print STDERR "$nw +++ $line\n";
if ($nw == 2) {
if ($stuff[0] eq 'Volume adjustment') {
$vol = 0+$stuff[1];
}
elsif ($stuff[0] eq 'Samples read') {
$size = 0+$stuff[1];
}
}
}
my $status = close $stat;
if ($?) {
print STDERR "Stat command failed: $statcmd\n" . ": $lastline \n";
next snipper;
}
if ($size == 0) {
print STDERR "?Warning! Zero-size audio file for $iix '$thing'\n";
}
if ($vol > 20) {
## unreasonable volume, happens with 'silent' files
$vol = 0;
}
printf("%s %6.3f %6d '%s'\n", $iix, $vol, $size, $thing);
my $subsize = int($size/2);
printf $index ("%-45s %10d %10d\n", $thing, $where, $subsize);
$where += $subsize;
my $volume_cmd = sprintf("sox -v %6.3f %s.wav %s.raw",
$vol*0.9, $xfn, $xfn);
########## print "+ $volume_cmd\n";
if (1) {
my $vol_handle = Symbol::gensym;
open ($vol_handle, '|-', $volume_cmd)
|| die "Couldn't open pipe to command '$volume_cmd'\n";
close $vol_handle;
if ($?) {
die "Volume command failed: $statcmd\n" . ": $lastline";
}
}
push @todo, "$xfn.raw";
}
close $index;
my $cat_cmd = "cat " . join(' ', @todo) . " > ./snip/everything.raw";
my $cat_handle = Symbol::gensym;
open ($cat_handle, '|-', $cat_cmd)
|| die "Couldn't open pipe to command '$cat_cmd'\n";
close $cat_handle;
if ($?) {
die "Cat command failed: $cat_cmd";
}
## Convert RAW to WAVE format
my $wav_cmd = "sox --rate $sample_rate --bits $bits_sample"
. " --encoding signed-integer"
. " ./snip/everything.raw --rate 8000 --bits $out_bits_sample $out_wav";
my $wav_handle = Symbol::gensym;
open ($wav_handle, '|-', $wav_cmd)
|| die "Couldn't open pipe to command '$wav_cmd'\n";
close $wav_handle;
if ($?) {
die ".wav command failed: $wav_cmd";
}
## Compress WAVE file
my $gz_cmd = "gzip -f $out_wav";
my $gz_handle = Symbol::gensym;
open ($gz_handle, '|-', $gz_cmd)
|| die "Couldn't open pipe to command '$gz_cmd'\n";
close $gz_handle;
system("rm snip/*; rmdir snip");
}

102
scripts/atis/voice.pl Executable file
View File

@@ -0,0 +1,102 @@
#! /usr/bin/perl -w
use strict;
use Symbol;
sub usage {
print <<EoF;
EoF
}
my $fgroot = $ENV{'FG_ROOT'} || '.';
my $dir="$fgroot/ATC";
my %start=();
my %len=();
my $str = 'Tucson International airport_information
Ryan automated_weather_observation
zero four one five zulu weather
/ Wind one one zero at one five
/ Visibility one zero
/ sky_condition two thousand four hundred scattered
/ Temperature one zero celsius dewpoint five celsius
/ Altimeter two niner niner two
/ Landing_and_departing_runway one one right
/ on_initial_contact_advise_you_have_information zulu ';
main: {
setup();
unlink 'tmp.raw';
$str =~ s/\n/ /g;
##print "$start{'decimal'} ... $len{'decimal'}\n";
my $didsome = 0;
for my $arg (@ARGV) {
if ($arg ne '-') {
say1($arg);
$didsome++;
} else {
for my $word (split(' ', $str)){
say1($word);
$didsome++;
}
}
}
if ($didsome) {
my $cmd = 'sox -q -r 8000 -t raw -e signed-integer -b 16 tmp.raw'
. ' tmp.wav';
# . ' -t alsa';
print "$cmd\n";
system $cmd;
}
}
sub say1{
my ($arg) = @_;
$arg = lc($arg);
if (exists $start{$arg}) {
my $cmd = "sox -q $dir/voice.wav "
. " -t raw -r 8000 -e signed-integer -b 16 - "
. " trim $start{$arg}s $len{$arg}s"
. " >> tmp.raw ";
print "$cmd\n";
system $cmd;
my $end = $start{$arg} + $len{$arg};
print "$start{$arg} + $len{$arg} = $end\n";
} else {
print "Can't find '$arg'\n";
}
}
sub setup{
my $inch = Symbol::gensym();
my $file = "$dir/voice.vce";
open($inch, "<$file") || die "Cannot open input file '$file'\n";
my $header = <$inch>;
chomp $header;
my $ii=1;
liner: while (my $line = <$inch>){
chomp $line;
my @word = split(" ", $line);
my $nn = @word;
if ($nn != 3) {
next liner;
}
my $id = lc($word[0]);
my $st = $word[1];
my $ln = $word[2];
if ($ln =~ s/^x//) {
$ln = $ln - $st;
print "$id $st $ln\n";
}
$start{$id} = $st;
$len{$id} = $ln;
##print "$ii $nn '$line'\n";
$ii++;
}
print "(($header)) --> $ii\n";
}

43
scripts/atis/words_per_line.sh Executable file
View File

@@ -0,0 +1,43 @@
#! /bin/bash
##
##
if test "x$1" = "x-h" ; then
1>&2 echo "Usage: "
1>&2 echo " $0 [filename]"
1>&2 echo "Read words from input, treating all whitespace like,"
1>&2 echo "and write exactly N words per line on output."
1>&2 echo "Options: "
1>&2 echo " -n [N] specify N (default: 1)"
1>&2 echo " filename = '-' or '' ==> read from standard input"
exit 1
fi
: ${wordmax:=1}
files=""
while test -n "$*" ; do
this=$1 ; shift
case $this in
-n) wordmax=$1 ; shift
;;
*) files="$files $this"
;;
esac
done
awk '{
for (ii = 1; ii <=NF; ii++) {
printf ("%s", $ii);
words++;
if (words >= wordmax) {
print "";
words = 0;
} else {
printf (" ");
}
}
}' wordmax=$wordmax $files

View File

@@ -0,0 +1,397 @@
# © 2014 Raphael Dümig ( duemig at in dot tum dot de ) forum handle: "hamster"
# distributed under the terms of the GPLv3
local_config_file="$HOME/.fgfsrc"
_get_opt()
{
local opt len
opt=$1
len=`expr ${#opt} + 3`
if [ -e "$local_config_file" ]
then
stored_opts="$(cat "$local_config_file")"
fi
for word in $stored_opts "${words[@]}"
do
if [ "${word:0:$len}" == "--$opt=" ]
then
# TODO: we have to get rid of at least the most important escape sequences!!!
value="$(echo "${word:$len}" | sed 's/\\ / /g')"
fi
done
}
_get_opts()
{
local opt len
opt=$1
len=`expr ${#opt} + 3`
value=""
if [ -e "$local_config_file" ]
then
stored_opts="$(cat "$local_config_file")"
fi
for word in "$stored_opts ${words[@]}"
do
if [ "${word:0:$len}" == "--$opt=" ]
then
value+="$(echo "${word:$len}" | sed 's/\\ / /g') "
fi
done
}
_get_fg_root()
{
local value
_get_opt "fg-root"
# value on the command line?
if [ -n "$value" ]
then
root="$value"
return 0
# value stored in environment variable $FG_ROOT?
else if [ -n "$FG_ROOT" ]
then
root="$FG_ROOT"
return 0
# no clue! search at the most common places
else
for path in /usr{/local,}/share{/games,}/{F,f}light{G,g}ear{/data,} {~,}/Applications/FlightGear.app/Contents/Resources/data
do
if [ -e "$path" ]
then
root="$path"
break
fi
done
fi
fi
}
_get_fg_scenery()
{
local value
_get_opt "fg-scenery"
# value on command line?
if [ -n "$value" ]
then
scenery="$value"
# value stored in environment variable $FG_SCENERY?
else if [ -n "$FG_SCENERY" ]
then
scenery="$FG_SCENERY"
# no clue! try default:
else
local root
_get_fg_root
scenery="$root/Scenery"
fi
fi
return 0
}
_get_fg_aircraft()
{
local value
_get_opt "fg-aircraft"
# value on command line?
if [ -n "$value" ]
then
aircraft_dir="$value"
# value stored in environment variable $FG_AIRCRAFT?
else if [ -n "$FG_AIRCRAFT" ]
then
aircraft_dir="$FG_AIRCRAFT"
# no clue! try default:
else
local root
_get_fg_root
aircraft_dir="$root/Aircraft"
fi
fi
}
_get_airport()
{
local value
_get_opt "airport"
if [ -z "$value" ]
then
airport="KSFO"
else
airport=$value
fi
}
_get_carrier()
{
local value
_get_opt "carrier"
carrier=$value
}
_get_scenarios()
{
local value
_get_opts "ai-scenario"
scenarios="$value nimitz_demo"
}
_fgfs()
{
local cur prev words cword split
_init_completion -s || return
local root airport aircraft_dir carrier scenarios scenery value
# auto-completion for values of keys ( --key=value )
case "$prev" in
--fg-aircraft|--fg-root|--fg-scenery|--flight-plan|--terrasync-dir|--materials-file|--config|--browser-app)
# completion of filesystem path
_filedir
return 0 ;;
--ai-scenario)
# list of scenarios in $FG_ROOT/AI
_get_fg_root
scenarios="$(find "$root/AI" -maxdepth 1 -iname *.xml -printf '%f\n' | sed -e 's/.xml$//')"
COMPREPLY=( $(compgen -W "${scenarios}" -- ${cur}) )
return 0 ;;
--aircraft|--vehicle)
# list of aircrafts in $FG_AIRCRAFT
_get_fg_aircraft
aircrafts="$(find "$aircraft_dir" -maxdepth 2 -mindepth 2 -iname *-set.xml -printf '%f\n' | sed -e 's/-set.xml$//')"
COMPREPLY=( $(compgen -W "${aircrafts}" -- ${cur}) )
return 0 ;;
--airport)
_get_fg_root
_get_fg_scenery
# is an index file present in the scenery?
if [ -e "$scenery/Airports/index.txt" ]
then
COMPREPLY=( $(compgen -W "$(awk 'BEGIN { FS="|"; } { print $1 }' "$scenery/Airports/index.txt")" -- ${cur}) )
return 0
# or at least the apt.dat file?
else if [ -e "$root/Airports/apt.dat.gz" ]
then
COMPREPLY=( $(compgen -W "$(zcat "$root/Airports/apt.dat.gz" | awk '/^1 / { print $5 }')" -- ${cur}) )
return 0
fi
fi
return 0 ;;
--carrier)
_get_fg_root
_get_scenarios
carriers=""
for scenario in $scenarios
do
carriers+="$(awk -- '
BEGIN { carrier=0; name=""; }
/<type>/ {
a=index($0,"<type>")+6; s=index(substr($0,a),"</type>")-1;
if(substr($0,a,s) == "carrier") carrier=1;
}
/<name>/ {
if(carrier) {
a=index($0,"<name>")+6; s=index(substr($0,a),"</name>")-1;
print substr($0,a,s);
carrier=0;
}
}
/<\/entry>/ {
carrier=0;
name="";
}' "$root/AI/$scenario.xml") "
done
COMPREPLY=( $(compgen -W "${carriers}" ${cur}) )
return 0 ;;
--runway)
_get_fg_scenery
_get_airport
# try to find a thresholds file
path="$scenery/Airports/${airport:0:1}/${airport:1:1}/${airport:2:1}"
if [ -e "$path/$airport.threshold.xml" ]
then
runways="$(awk -- '
/<rwy>/ {
a=index($0,"<rwy>")+5;
s=index(substr($0,a),"</rwy>")-1;
print substr($0,a,s)
}' "$path/$airport.threshold.xml")"
fi
COMPREPLY=( $(compgen -W "${runways}" -- ${cur}) )
return 0 ;;
--parkpos)
# try to find out the name of the carrier or the ID of the airport
_get_carrier
if [ -n "$carrier" ]
then
_get_fg_root
_get_scenarios
for scenario in $scenarios
do
positions="$(awk -v carrier_name="$carrier" '
BEGIN { carrier=0; name=0; parkpos=0; }
/<type>/ {
a=index($0,"<type>")+6; s=index(substr($0,a),"</type>")-1;
if(substr($0,a,s) == "carrier") carrier=1;
}
/<parking-pos>/ {
parkpos=(carrier && name);
}
/<name>/ {
a=index($0,"<name>")+6; s=index(substr($0,a),"</name>")-1;
if(parkpos) print substr($0,a,s);
else if(carrier) name=(substr($0,a,s) == carrier_name);
}
/<\/parking-pos>/ {
parkpos=0;
}
/<\/entry>/ {
carrier=name=parkpos=0;
}' "$root/AI/$scenario.xml")"
if [ -n "$positions" ]
then
break
fi
done
else
_get_fg_scenery
_get_airport
# search for the groundnet or parking file
path="$scenery/Airports/${airport:0:1}/${airport:1:1}/${airport:2:1}"
if [ -e "$path/$airport.groundnet.xml" ]
then
file="$airport.groundnet.xml"
else if [ -e "$path/$airport.parking.xml" ]
then
file="$airport.parking.xml"
else
# no file found => do not try to analyze it!
return 0
fi
fi
# build a list of the parking positions at that airport
positions="$(awk -- '
/<Parking/ {
pos_active=1;
name=number="";
}
/name="/ {
if(pos_active) {
a=index($0,"name=\"")+6;
s=index(substr($0,a),"\"")-1;
name=substr($0,a,s);
}
}
/number="/ {
if(pos_active) {
a=index($0,"number=\"")+8;
s=index(substr($0,a),"\"")-1;
number=substr($0,a,s);
}
}
/\/>/ {
if(pos_active == 1 && (name!="" || number!="")) print name number;
pos_active=0;
name=number="";
}' "$path/$file")"
fi
COMPREPLY=( $(compgen -W "${positions}" -- ${cur}) )
return 0 ;;
--control)
COMPREPLY=( $(compgen -W "joystick keyboard mouse" -- ${cur}) )
return 0 ;;
--failure)
COMPREPLY=( $(compgen -W "pitot static vacuum electrical" -- ${cur}) )
return 0 ;;
--fix)
_get_fg_root
COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/fix.dat.gz" | awk '{ print substr($3,0,5) }')" -- ${cur}) )
return 0 ;;
--fdm)
COMPREPLY=( $(compgen -W "jsb larcsim yasim magic balloon ada external null" -- ${cur}) )
return 0 ;;
--min-status)
COMPREPLY=( $(compgen -W "alpha beta early-production production" -- ${cur}) )
return 0 ;;
--log-class)
COMPREPLY=( $(compgen -W "ai environment flight general io network sound terrain" -- ${cur}) )
return 0 ;;
--log-level)
COMPREPLY=( $(compgen -W "bulk debug info warn alert" -- ${cur}) )
return 0 ;;
--ndb)
_get_fg_root
COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk '/^2 / { print $8 }')" -- ${cur}) )
return 0 ;;
--ndb-frequency)
_get_fg_root
_get_opt "ndb"
COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk -v nav=$value '/^2 / { if($8 == nav) print $5 }')" -- ${cur}) )
return 0 ;;
--season)
COMPREPLY=( $(compgen -W "summer winter" -- ${cur}) )
return 0 ;;
--texture-filtering)
COMPREPLY=( $(compgen -W "1 2 4 8 16" -- ${cur}) )
return 0 ;;
--timeofday)
COMPREPLY=( $(compgen -W "real dawn morning noon afternoon dusk evening midnight" -- ${cur}) )
return 0 ;;
--view-offset)
COMPREPLY=( $(compgen -W "LEFT RIGHT CENTER" -- ${cur}) )
return 0 ;;
--vor)
_get_fg_root
COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk '/^3 / { print $8 }')" -- ${cur}) )
return 0 ;;
--vor-frequency)
_get_fg_root
_get_opt "vor"
COMPREPLY=( $(compgen -W "$(zcat "$root/Navaids/nav.dat.gz" | awk -v nav=$value '/^3 / { if($8 == nav) print substr($5,0,3) "." substr($5,4) }')" -- ${cur}) )
return 0 ;;
esac
case "${cur}" in
-*)
# auto completion for options
COMPREPLY=( $(compgen -W "$(_parse_help fgfs "--help --verbose")" -- ${cur}) )
# no whitespace after keys
[[ $COMPREPLY == *= ]] && compopt -o nospace ;;
*)
_filedir
esac
return 0
}
complete -F _fgfs fgfs

View File

@@ -0,0 +1,303 @@
#compdef fgfs
local _fgfs_root
local _fgfs_options
local state
_fgfs_root=${FG_ROOT:-/usr/local/share/FlightGear}
_fgfs_options=(
'(-h --help)'{-h,--help}'[Show the most relevant command line options]' \
'--version[FlightGear version]' \
'--verbose[Show all command line options when combined --help or -h]' \
'--disable-intro-music[Disable introduction music]' \
'--enable-intro-music[Enable introduction music]' \
'--units-feet[Use feet for distances]' \
'--units-meters[Use meters for distances]' \
'--disable-sound[Disable sound effects]' \
'--enable-sound[Enable sound effects]' \
'--disable-panel[Disable instrument panel]' \
'--enable-panel[Enable instrument panel]' \
'--disable-hud[Disable Heads Up Display (HUD)]' \
'--enable-hud[Enable Heads Up Display (HUD)]' \
'--disable-anti-alias-hud[Disable anti-aliased HUD]' \
'--enable-anti-alias-hud[Enable anti-aliased HUD]' \
'--disable-hud-3d[Disable 3D HUD]' \
'--enable-hud-3d[Enable 3D HUD]' \
'--hud-tris[Hud displays number of triangles rendered]' \
'--hud-culled[Hud displays percentage of triangles culled]' \
'--disable-random-objects[Exclude random scenery objects (buildings, etc.)]' \
'--enable-random-objects[Include random scenery objects (buildings, etc.)]' \
'--disable-ai-models[Disable the artifical traffic subsystem]' \
'--enable-ai-models[Enable the artifical traffic]' \
'--disable-freeze[Start in a running state]' \
'--enable-freeze[Start in a frozen state]' \
'--disable-fuel-freeze[Fuel is consumed normally]' \
'--enable-fuel-freeze[Fuel tank quantity forced to remain constant]' \
'--disable-clock-freeze[Clock advances normally]' \
'--enable-clock-freeze[Do not advance clock]' \
'--disable-splash-screen[Disable splash screen]' \
'--enable-splash-screen[Enable splash screen]' \
'--disable-mouse-pointer[Disable extra mouse pointer]' \
'--enable-mouse-pointer[Enable extra mouse pointer]' \
'--fog-disable[Disable fog/haze]' \
'--fog-fastest[Enable fastest fog/haze]' \
'--fog-nicest[Enable nicest fog/haze]' \
'--disable-distance-attenuation[Disable runway light distance attenuation]' \
'--enable-distance-attenuation[Enable runway light distance attenuation]' \
'--disable-specular-highlight[Disable specular reflections on textured objects]' \
'--enable-specular-highlight[Enable specular reflections on textured objects]' \
'--disable-fullscreen[Disable fullscreen mode]' \
'--enable-fullscreen[Enable fullscreen mode]' \
'--disable-game-mode[Disable full-screen game mode]' \
'--enable-game-mode[Enable full-screen game mode]' \
'--shading-flat[Enable flat shading]' \
'--shading-smooth[Enable smooth shading]' \
'--disable-skyblend[Disable sky blending]' \
'--enable-skyblend[Enable sky blending]' \
'--disable-textures[Disable textures]' \
'--enable-textures[Enable textures]' \
'--disable-vr[Disable VR]' \
'--enable-vr[Enable VR]' \
'--disable-wireframe[Disable wireframe drawing mode]' \
'--enable-wireframe[Enable wireframe drawing mode]' \
'--notrim[Do NOT attempt to trim the model (only with fdm=jsbsim)]' \
'--on-ground[Start at ground level (default)]' \
'--in-air[Start in air (implied when using --altitude)]' \
'--enable-auto-coordination[Enable auto coordination]' \
'--disable-auto-coordination[Disable auto coordination]' \
'--show-aircraft[Print a list of the currently available aircraft types]' \
'--time-match-real[Synchronize time with real-world time]' \
'--time-match-local[Synchronize time with local real-world time]' \
'--disable-real-weather-fetch[Disbale METAR based real weather fetching]' \
'--enable-real-weather-fetch[Enable METAR based real weather fetching]' \
'--disable-horizon-effect[Disable celestial body growth illusion near the horizon]' \
'--enable-horizon-effect[Enable celestial body growth illusion near the horizon]' \
'--enable-clouds[Enable 2D (flat) cloud layers]' \
'--disable-clouds[Disable 2D (flat) cloud layers]' \
'--enable-clouds3d[Enable 3D (volumetric) cloud layers]' \
'--disable-clouds3d[Disable 3D (volumetric) cloud layers]' \
'--atc610x[Enable atc610x interface]' \
'--enable-save-on-exit[Allow saving preferences at program exit]' \
'--disable-save-on-exit[Do not save preferences upon program exit]' \
'--ai-scenario=[Add and enable a new scenario]:AI scenario:->ai-scenario' \
'--fg-root=[Specify the root data path]:Directories:_directories' \
'--fg-scenery=[Specify the base scenery path]:Directories:_directories' \
'--language=[Select the language for this session]:Language:->language' \
'--browser-app=[Specify path to your web browser]:Directories:_directories' \
'--config=[Load additional properties from path]' \
'--failure=[Fail the pitot, static, vacuum, or electrical system]:Failure system:(pitot static vaccum electical)'
'--bpp=[Specify the bits per pixel]' \
'--fov=[Specify field of view angle]' \
'--callsign=[Assign a unique name to a player]' \
'--aspect-ratio-multiplier=[Specify a multiplier for the aspect ratio]' \
'--geometry=[Specify window geometry (640x480, etc)]' \
'--view-offset=[Specify the default forward view direction as an offset from straight ahead]' \
'--aircraft=[Select an aircraft profile]:Aircraft:->aircraft' \
'--min-status=[Allows you to define a minimum status level for all listed aircraft]:Minimum status level:(alpha beta early-production production)' \
'--fdm=[Select the core flight dynamics model]:Core flight dynamics model:(jsb larcsim yasim magic balloon ada external)' \
'--aero=[Select aircraft aerodynamics model to load]' \
'--model-hz=[Run the FDM this rate (iterations per second)]' \
'--speed=[Run the FDM n times faster than real time]' \
'--aircraft-dir=[Aircraft directory relative to the path of the executable]:Aircraft directory:_directories' \
'--timeofday=[Specify a time of day]:Time of day:(real dawn morning noon afternoon dusk evening midnight)' \
'--time-offset=[Add this time offset (+/-hh:mm:ss)]' \
'--start-date-sys=[Specify a starting date/time with respect to system time (yyyy:mm:dd:hh:mm:ss)]' \
'--start-date-gmt=[Specify a starting date/time with respect to Greenwich Mean Time (yyyy:mm:dd:hh:mm:ss)]' \
'--start-date-lat=[Specify a starting date/time with respect to Local Aircraft Time (yyyy:mm:dd:hh:mm:ss)]' \
'--airport=[Specify starting position relative to an airport]:Airport:->airport' \
'--runway=[Specify starting runway (must also specify an airport)]:Runway:->runway' \
'--carrier=[Specify starting position on an AI carrier]:AI carrier:(Nimitz Eisenhower Foch)' \
'--parkpos=[Specify which starting position on an AI carrier]:Park position:->parkpos' \
'--vor=[Specify starting position relative to a VOR]:VOR:->vor' \
'--ndb=[Specify starting position relative to an NDB]:NDB:->ndb' \
'--fix=[Specify starting position relative to a fix]:FIX:->fix' \
'--offset-distance=[Specify distance to reference point (in miles)]' \
'--offset-azimuth=[Specify heading to reference point (in degrees)]' \
'--lon=[Starting longitude (in degrees)]' \
'--lat=[Starting latitude (in degrees)]' \
'--altitude=[Starting altitude]' \
'--heading=[Specify heading (yaw) angle (Psi)]' \
'--roll=[Specify roll angle (Phi)]' \
'--pitch=[Specify pitch angle (Theta)]' \
'--uBody=[Specify velocity along the body X axis]' \
'--vBody=[Specify velocity along the body Y axis]' \
'--wBody=[Specify velocity along the body Z axis]' \
'--vc=[Specify initial airspeed (in knots)]' \
'--mach=[Specify initial mach number]' \
'--glideslope=[Specify flight path angle (in degrees)]' \
'--roc=[Specify initial climb rate]' \
'--wp=[Specify a waypoint for the GC autopilot]' \
'--flight-plan=[Read all waypoints from a file]:Waypoints file:_files' \
'--nav1=[Set the NAV1 radio frequency, optionally preceded by a radial]' \
'--nav2=[Set the NAV2 radio frequency, optionally preceded by a radial]' \
'--adf1=[Set the ADF1 radio frequency, optionally preceded by a card rotation]' \
'--adf2=[Set the ADF2 radio frequency, optionally preceded by a card rotation]' \
'--dme=[Slave the ADF to one of the NAV radios, or set its internal frequency]' \
'--visibility=[Specify initial visibility (in meters)]' \
'--visibility-miles=[Specify initial visibility (in miles)]' \
'--wind=[Specify wind coming from DIR (degrees) - SPEED (knots) - (DIR@SPEED)]' \
'--turbulence=[Specify turbulence from 0.0 (calm) to 1.0 (severe)]' \
'--ceiling=[Create an overcast ceiling, optionally with a specific thickness]' \
'--multiplay=[Specify multipilot communication settings ({in|out},hz,address,port)]' \
'--proxy=[Specify which proxy server (and port) to use (user:pwd@host:port)]' \
'--httpd=[Enable http server on the specified port]' \
'--telnet=[Enable telnet server on the specified port]' \
'--jpg-httpd=[Enable screen shot http server on the specified port]' \
'--generic=[Open connection using a predefined communication interface]' \
'--garmin=[Open connection using the Garmin GPS protocol]' \
'--joyclient=[Open connection to an Agwagon joystick]' \
'--jsclient=[Open connection to a remote joystick]' \
'--native-ctrls=[Open connection using the FG Native Controls protocol]' \
'--native-fdm=[Open connection using the FG Native FDM protocol]' \
'--native=[Open connection using the FG Native protocol]' \
'--nmea=[Open connection using the NMEA protocol]' \
'--opengc=[Open connection using the OpenGC protocol]' \
'--props=[Open connection using the interactive property manager]' \
'--pve=[Open connection using the PVE protocol]' \
'--ray=[Open connection using the Ray Woodworth motion chair protocol]' \
'--rul=[Open connection using the RUL protocol]' \
'--log-level=[Specify which loggin level to use]:Log level:(bulk debug info warn alert)' \
'--trace-read=[Trace the reads for a property]' \
'--trace-write=[Trace the writes for a property]' \
'--season=[Specify the startup season]:Season:(summer winter)' \
'--vehicle=[Select a vehicle profile]:Vehicle:->vehicle' \
'--prop:[]'
)
_fgfs_ai_scenario() {
local i
local result
if ! zstyle -a ":completion:${curcontext}:" fgfs ai_scenario; then
(( $+_cache_ai_scenario )) ||
for i in $_fgfs_root/AI/*.xml; do
i=${i%.xml}
_cache_ai_scenario+=( ${i##*/} )
done
result=( "$_cache_ai_scenario[@]" )
fi
compadd -a "$@" - result
}
_fgfs_aircraft() {
local i
local result
if ! zstyle -a ":completion:${curcontext}:" fgfs aircraft; then
(( $+_cache_aircraft )) ||
for i in $_fgfs_root/Aircraft/*/*-set.xml; do
i=${i%-set.xml}
_cache_aircraft+=( ${i##*/} )
done
result=( "$_cache_aircraft[@]" )
fi
compadd -a "$@" - result
}
_fgfs_airport() {
local line
local result
if ! zstyle -a ":completion:${curcontext}:" fgfs airport; then
(( $+_cache_airport )) ||
gunzip -c $_fgfs_root/Airports/apt.dat.gz |
while read line; do
if [[ $line = "" ]]; then
read line
_cache_airport+=( $line[(w)5] )
fi
done
result=( "$_cache_airport[@]" )
fi
compadd -a "$@" - result
}
_fgfs_runway() {
local airport
local line
local result
[[ $words == *--airport=(#b)([a-zA-Z]#)* ]] && airport=$match[1]
if [[ $airport = "" ]]; then
_message "Please choose airport !"
return
fi
if ! zstyle -a ":completion:${curcontext}:" fgfs runway_$airport; then
(( $+_cache_runway )) ||
gunzip -c $_fgfs_root/Airports/apt.dat.gz |
while read line; do
if [[ $line = "" ]]; then
read line
name=( $line[(w)5] )
if [[ $name = $airport ]]; then
while read line; do
_cache_runway+=( $line[(w)4] )
break
done
break
fi
fi
done
_cache_airport_name=$airport
result=( "$_cache_runway[@]" );
fi
compadd -a "$@" - result
}
_arguments -C -s "$_fgfs_options[@]" && return 0
case $state in
ai-scenario)
_fgfs_ai_scenario && return 0
;;
language)
;;
aircraft|vehicle)
_fgfs_aircraft && return 0
;;
airport)
_fgfs_airport && return 0
;;
runway)
_fgfs_runway && return 0
;;
parkpos)
;;
vor)
;;
ndb)
;;
fix)
;;
esac

93
scripts/debug/debug-fgfs Executable file
View File

@@ -0,0 +1,93 @@
#!/bin/bash
# $Id$
#
#
# To use this script you need
# - a Linux system >=2.4 with libc >=2.2.*
# - the valgrind debugger (http://www.valgrind.org/)
# - lots of memory (RAM + swap space > 600MB!) and
# - a fast computer =or= a lot of patience.
#
# It is useful (but not required) to compile FlightGear without
# any optimizations (-O0 without -funroll-loops etc.). Otherwise
# the debug messages might not seem to make sense in some cases.
#
# The sample suppression file is designed for a SuSE 7.1 system
# with Linux 2.4.18, XFree86 4.2.0 and a 3dfx card. Don't use it
# as it is, but create your own! You will likely have different
# libraries with different bugs.
#
# If you have problems with valgrind and FlightGear, don't bother
# the valgrind author -- ask on the flightgear-devel list first!
#
#
#
# adapt the following two entries to your needs:
#
fgfs="../../src/Main/fgfs"
opt="--log-level=info --disable-game-mode --aircraft=ufo --geometry=800x600"
gdb="--db-attach=yes"
cmd=
extra=
while true; do
case "$1" in
-h|--help) cat <<-EOF
debug [-r] [-g] [-i] [-b] [-c] [-x] [-h]
-r run fgfs
-g|-d run fgfs with gdb
-D run fgfs with ddd
-i interactive valgrind run (default)
-p calltree profile run
-b valgrind batch run
-c start gdb with most recent core dump
-s print suppression info
-l enable leak/reachable check
-h show this help screen
EOF
exit 0
;;
-r) cmd="$fgfs $opt" ;;
-g|-d) cmd="gdb -x .gdbinit $fgfs" ;;
-D) cmd="ddd -x .gdbinit $fgfs" ;;
-m) cmd="make -C ../.." ;;
-i) cmd=""; gdb="" ;;
-p) cmd="callgrind --dump-instr=yes --trace-jump=yes --dump-threads=yes $fgfs $opt"; gdb="" ;;
-b) gdb="" ;;
-s) extra="$extra --gen-suppressions=yes" ;;
-l) extra="$extra --leak-check=yes --show-reachable=yes" ;;
-c) core=$(ls -t core* 2>/dev/null|head -1)
if test -z $core; then
echo "$0: there's no core file"
exit 1
fi
cmd="gdb $fgfs $core"
;;
*) break ;;
esac
shift
done
if ! test -e "$fgfs"; then
echo "$0: there's no fgfs executable $fgfs"
exit 2
fi
echo "set args $opt" > .gdbinit
echo "handle SIG32 nostop noprint" >> .gdbinit # work around gdb bug
test -e fgfs.supp || head -14 sample.fgfs.supp > fgfs.supp
test -z "$cmd" && cmd="nice valgrind \
--tool=memcheck $gdb --num-callers=64 \
--suppressions=fgfs.supp \
--error-limit=no \
$extra $* $fgfs $opt"
echo "command: $cmd"
#export LD_PRELOAD='/usr/lib/GL/libGL.so.1.3.mesasoft'
#export __GL_FORCE_GENERIC_CPU=1
exec $cmd

View File

@@ -0,0 +1,100 @@
# valgrind suppression rules for FlightGear
# Format of this file is:
# {
# name_of_suppression
# kind: one of Param Value1 Value2 Value4 Value8
# Cond Free PThread Addr1 Addr2 Addr4 Addr8
# (if Param: name of system call param, if Free: name of free-ing fn)
# caller0 name, or /name/of/so/file.so
# caller1 name, or ditto
# (optionally: caller2 name)
# (optionally: caller3 name)
# }
# sample rules for Linux kernel 2.4, glibc 2.2.4, XFree86 4.2.0, 3dfx
{
_dlopen_signal_error(Addr1)
Addr1
fun:_dl_signal_error
fun:_dl_open
fun:dlopen_doit
}
{
_dl_signal_error(Addr4)
Addr4
fun:_dl_signal_error
fun:_dl_open
fun:dlopen_doit
}
{
memcpy(Addr1)
Addr1
fun:memcpy
fun:_dl_signal_error
fun:_dl_open
fun:dlopen_doit
}
{
memcpy(Addr4)
Addr4
fun:memcpy
fun:_dl_signal_error
fun:_dl_open
fun:dlopen_doit
}
{
ioctl(generic)/(Param)
Param
ioctl(generic)
fun:__ioctl
fun:driMesaCreateScreen
fun:__driCreateScreen
}
{
Mesa/texsubimage2d...(Addr1)
Addr1
fun:texsubimage2d_*_to_*
fun:convert_texsubimage2d_*
fun:_mesa_convert_texsubimage2d
fun:tdfxDDTexImage2D
}
{
Mesa/texsubimage2d...(Addr4)
Addr4
fun:texsubimage2d_*_to_*
fun:convert_texsubimage2d_*
fun:_mesa_convert_texsubimage2d
fun:tdfxDDTexImage2D
}
{
Mesa/texsubimage2d...(Value1)
Value1
fun:texsubimage2d_*_to_*
fun:convert_texsubimage2d_*
fun:_mesa_convert_texsubimage2d
fun:tdfxDDTexImage2D
}
{
known-plib-bug/already_reported(Cond)
Cond
fun:removeEntity__7ssgListUi
}
{
zlib/pthread-bug(PThread)
PThread
fun:pthread_error
fun:__pthread_mutex_destroy
fun:_IO_default_finish
}

View File

@@ -0,0 +1,153 @@
/* $Id$ */
/* gcc -O2 -g -pedantic -Wall fgfsclient.c -o fgfsclient */
/* USAGE: ./fgfsclient [hostname [port]] */
/* Public Domain */
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <string.h>
#define DFLTHOST "localhost"
#define DFLTPORT 5501
#define MAXMSG 256
#define fgfsclose close
void init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned port);
int fgfswrite(int sock, char *msg, ...);
const char *fgfsread(int sock, int wait);
void fgfsflush(int sock);
int fgfswrite(int sock, char *msg, ...)
{
va_list va;
ssize_t len;
char buf[MAXMSG];
va_start(va, msg);
vsnprintf(buf, MAXMSG - 2, msg, va);
va_end(va);
printf("SEND: \t<%s>\n", buf);
strcat(buf, "\015\012");
len = write(sock, buf, strlen(buf));
if (len < 0) {
perror("fgfswrite");
exit(EXIT_FAILURE);
}
return len;
}
const char *fgfsread(int sock, int timeout)
{
static char buf[MAXMSG];
char *p;
fd_set ready;
struct timeval tv;
ssize_t len;
FD_ZERO(&ready);
FD_SET(sock, &ready);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if (!select(32, &ready, 0, 0, &tv))
return NULL;
len = read(sock, buf, MAXMSG - 1);
if (len < 0) {
perror("fgfsread");
exit(EXIT_FAILURE);
}
if (len == 0)
return NULL;
for (p = &buf[len - 1]; p >= buf; p--)
if (*p != '\015' && *p != '\012')
break;
*++p = '\0';
return strlen(buf) ? buf : NULL;
}
void fgfsflush(int sock)
{
const char *p;
while ((p = fgfsread(sock, 0)) != NULL) {
printf("IGNORE: \t<%s>\n", p);
}
}
int fgfsconnect(const char *hostname, const int port)
{
struct sockaddr_in serv_addr;
struct hostent *hostinfo;
int sock;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
perror("fgfsconnect/socket");
return -1;
}
hostinfo = gethostbyname(hostname);
if (hostinfo == NULL) {
fprintf(stderr, "fgfsconnect: unknown host: \"%s\"\n", hostname);
close(sock);
return -2;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr = *(struct in_addr *)hostinfo->h_addr;
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("fgfsconnect/connect");
close(sock);
return -3;
}
return sock;
}
int main(int argc, char **argv)
{
int sock;
unsigned port;
const char *hostname, *p;
hostname = argc > 1 ? argv[1] : DFLTHOST;
port = argc > 2 ? atoi(argv[2]) : DFLTPORT;
sock = fgfsconnect(hostname, port);
if (sock < 0)
return EXIT_FAILURE;
fgfswrite(sock, "data");
fgfswrite(sock, "set /controls/engines/engine[%d]/throttle %d", 0, 1);
fgfswrite(sock, "get /sim/aircraft");
p = fgfsread(sock, 3);
if (p != NULL)
printf("READ: \t<%s>\n", p);
fgfswrite(sock, "quit");
fgfsclose(sock);
return EXIT_SUCCESS;
}

View File

@@ -0,0 +1,201 @@
// $Id$
// g++ -O2 -g -pedantic -Wall fgfsclient.cxx -o fgfsclient -lstdc++
// USAGE: ./fgfsclient [hostname [port]]
// Public Domain
#include <errno.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
const char* HOST = "localhost";
const unsigned PORT = 5501;
const int BUFLEN = 256;
class FGFSSocket
{
public:
FGFSSocket(const char* name, unsigned port);
~FGFSSocket();
int write(const char* msg, ...);
const char* read(void);
inline void flush(void);
void settimeout(unsigned t) { _timeout = t; }
private:
int close(void);
int _sock;
bool _connected;
unsigned _timeout;
char _buffer[BUFLEN];
bool _isShutdown = false;
};
FGFSSocket::FGFSSocket(const char* hostname = HOST, unsigned port = PORT) : _sock(-1),
_connected(false),
_timeout(1)
{
_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sock < 0)
throw("FGFSSocket/socket");
struct hostent* hostinfo;
hostinfo = gethostbyname(hostname);
if (!hostinfo) {
close();
throw("FGFSSocket/gethostbyname: unknown host");
}
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr = *(struct in_addr*)hostinfo->h_addr;
if (connect(_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
close();
throw("FGFSSocket/connect");
}
_connected = true;
try {
write("data");
} catch (...) {
close();
throw;
}
}
FGFSSocket::~FGFSSocket()
{
_isShutdown = true;
close();
}
int FGFSSocket::close(void)
{
if (_connected)
write("quit");
if (_sock < 0)
return 0;
int ret = ::close(_sock);
_sock = -1;
return ret;
}
int FGFSSocket::write(const char* msg, ...)
{
va_list va;
ssize_t len;
char buf[BUFLEN];
fd_set fd;
struct timeval tv;
FD_ZERO(&fd);
FD_SET(_sock, &fd);
tv.tv_sec = _timeout;
tv.tv_usec = 0;
if (!select(FD_SETSIZE, 0, &fd, 0, &tv))
if (_isShutdown) // don't throw during dtor
std::cout << "FGFSSocket::write/select: timeout exceeded" << std::endl;
else
throw("FGFSSocket::write/select: timeout exceeded");
va_start(va, msg);
vsnprintf(buf, BUFLEN - 2, msg, va);
va_end(va);
std::cout << "SEND: " << buf << std::endl;
strcat(buf, "\015\012");
len = ::write(_sock, buf, strlen(buf));
if (len < 0)
if (_isShutdown) // don't throw during dtor
std::cout << "FGFSSocket::write error" << std::endl;
else
throw("FGFSSocket::write");
return len;
}
const char* FGFSSocket::read(void)
{
char* p;
fd_set fd;
struct timeval tv;
ssize_t len;
FD_ZERO(&fd);
FD_SET(_sock, &fd);
tv.tv_sec = _timeout;
tv.tv_usec = 0;
if (!select(FD_SETSIZE, &fd, 0, 0, &tv)) {
if (_timeout == 0)
return 0;
else
throw("FGFSSocket::read/select: timeout exceeded");
}
len = ::read(_sock, _buffer, BUFLEN - 1);
if (len < 0)
throw("FGFSSocket::read/read");
if (len == 0)
return 0;
for (p = &_buffer[len - 1]; p >= _buffer; p--)
if (*p != '\015' && *p != '\012')
break;
*++p = '\0';
return strlen(_buffer) ? _buffer : 0;
}
inline void FGFSSocket::flush(void)
{
int i = _timeout;
_timeout = 0;
while (read())
;
_timeout = i;
}
int main(const int argc, const char* argv[])
try {
const char* hostname = argc > 1 ? argv[1] : "localhost";
int port = argc > 2 ? atoi(argv[2]) : 5501;
FGFSSocket f(hostname, port);
f.flush();
f.write("set /controls/engines/engine[%d]/throttle %lg", 0, 1.0);
f.write("set /controls/engines/engine[%d]/throttle %lg", 1, 1.0);
f.write("get /sim/aircraft");
const char* p = f.read();
if (p)
std::cout << "RECV: " << p << std::endl;
return EXIT_SUCCESS;
} catch (const char s[]) {
std::cerr << "Error: " << s << ": " << strerror(errno) << std::endl;
return EXIT_FAILURE;
} catch (...) {
std::cerr << "Error: unknown exception" << std::endl;
return EXIT_FAILURE;
}
// vim:cindent

View File

@@ -0,0 +1,95 @@
#!/usr/bin/perl -w
# USAGE: fgfsscript [host [port]]
# $Id$
# Public Domain
use strict;
use IO::Socket;
my $host = (shift || 'localhost');
my $port = (shift || 5501);
my ($fgfs, $i);
# main()
{
chdir;
$fgfs = &connect($host, $port, 120) || die " can't open socket\n";
&send("data");
# wait for random altitude (0--3000 ft.) to be reached
my $alt = int(rand(3000));
print "disaster begins at $alt ft. AGL\n";
while (1) {
sleep(1);
$i = &get("/position/altitude-agl-ft");
print "\r" . int($i) . " ft.";
print "\n" and last if $i > $alt;
}
print "start fuel dumping :-)\n";
for ($i = 0; $i < 4; $i++) {
sleep(rand(60));
&set("/consumables/fuel/tank[$i]/level-gal_us", 0);
print "tank $i empty\n";
}
&send("quit");
close $fgfs;
}
sub get()
{
&send("get " . shift);
eof $fgfs and die "\nconnection closed by host";
$_ = <$fgfs>;
s/\015?\012$//;
/^-ERR (.*)/ and die "\nfgfs error: $1\n";
return $_;
}
sub set()
{
my $prop = shift;
my $value = shift;
&send("set $prop $value");
}
sub send()
{
print $fgfs shift, "\015\012";
}
sub connect()
{
my $host = shift;
my $port = shift;
my $timeout = (shift || 120);
my $socket;
STDOUT->autoflush(1);
print "connect ";
while ($timeout--) {
if ($socket = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => $host,
PeerPort => $port)) {
print ".. done.\n";
$socket->autoflush(1);
sleep 1;
return $socket;
}
print ".";
sleep(1);
}
return 0;
}
# vi:ts=8:sw=8:noet:nowrap:cindent

124
scripts/example/remote.html Normal file
View File

@@ -0,0 +1,124 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>FlightGear: Remote control</title>
<meta name="Author" content="Melchior FRANZ">
<meta name="KeyWords" content="flightgear,fgfs,flightsimulator,simulator,remote,control,perl,c,c++">
<link rel="StyleSheet" type="text/css" href="fgfs.css">
<link rel="SHORTCUT ICON" href="mf.ico">
</head>
<body>
<h1>FlightGear: Remote control</h1>
<p>FlightGear has several interfaces that provide access to internal
parameters. The HTTP interface is best suited for human interaction
via a web browser. The telnet interface is the ideal choice for
remotely controlling FlightGear by means of external programs.
</p>
<p>To activate FlightGear's telnet server capabilities, call it with
a <em>--telnet</em> specifiaction:
<blockquote>
<pre>
$ fgfs --telnet=socket,bi,5,localhost,5501,tcp
</pre>
<br>
<br>
<table>
<tr><td><strong>socket:</strong></td><td>FlightGear protocol</td></tr>
<tr><td><strong>bi:</strong></td><td>bidirectional</td></tr>
<tr><td><strong>5:</strong></td><td>polling frequency in Hertz</td></tr>
<tr><td><strong>localhost: </strong></td><td>server name or IP-address</td></tr>
<tr><td><strong>5501: </strong></td><td>server port</td></tr>
<tr><td><strong>tcp:</strong></td><td>internet protocol type</td></tr>
</table>
</blockquote>
<br>
<br>
In newer versions of FlightGear just type:
<blockquote>
<pre>
$ fgfs --telnet=5501
</pre>
</blockquote>
</p>
<p>To learn more about the supported commands, connect to FlightGear
with a telnet program and type in "help&lt;RETURN&gt;". This is what you'll get:<p>
<blockquote>
<pre>
$ telnet localhost 5501
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
help
Valid commands are:
help show help message
ls [&lt;dir&gt;] list directory
dump dump current state (in xml)
cd &lt;dir&gt; cd to a directory, '..' to move back
pwd display your current path
get &lt;var&gt; show the value of a parameter
show &lt;var&gt; synonym for get
set &lt;var&gt; &lt;val&gt; set &lt;var&gt; to a new &lt;val&gt;
data switch to raw data mode
prompt switch to interactive mode (default)
quit terminate connection
/&gt;
</pre>
</blockquote>
<p>Now you can browse in the property system like in a Linux file
system with <em>cd, ls, pwd</em>.</p>
<p>Here you can <a href="fgfsscript">download a sample script</a>
written in Perl, that shows how to access and manipulate FlightGear's
internal parameters. It can be started before FlightGear (in which
case it tries up to 2&nbsp;minutes to connect) or afterwards. Then
it picks a random AGL altitude and checks every 5&nbsp;seconds
if the aircraft has already climbed at this height. Now it starts
to empty all four tanks, one after the other, until the engines
stop working. Try to find a place where you can land safely. :-)</p>
<blockquote>
<pre>
$ fgfsscript&amp;
$ fgfs --telnet=socket,bi,5,localhost,5501,tcp
</pre>
</blockquote>
<p>The script defaults to <em>localhost</em> and <em>port 5501</em>,
but you can let the script control FlightGear on another host and
under another port.</p>
<blockquote>
<pre>
$ fgfsscript some.host.org 1234&amp;
</pre>
</blockquote>
<p>Demo programs are available in:<br>
<ul>
<li><a href="fgfsclient.c">C</a></li>
<li><a href="fgfsclient.cxx">C++</a></li>
<li><a href="fgfsscript">Perl (same as mentioned above)</a></li>
</ul>
</p>
</body>
</html>

View File

@@ -0,0 +1,11 @@
This directory contains a simple, Java network client library for the
FlightGear flight simulator. See the JavaDoc documentation for
org.flightgear.fgfsclient.FGFSConnection for more information.
There is a simple demo application, FGFSDemo, that you can try like this:
fgfs --telnet=9000
java FGFSDemo localhost 9000
To rebuild the program, use the Apache ant utility.

View File

@@ -0,0 +1,49 @@
<?xml version="1.0"?>
<project name="fgfsclient" default="compile" basedir=".">
<target name="init">
<tstamp/>
</target>
<target name="prepare" depends="init">
<mkdir dir="classes"/>
<mkdir dir="docs/javadoc"/>
</target>
<target name="compile" depends="prepare">
<javac debug="on" optimize="off" srcdir="src" destdir="classes" excludes="**/Makefile"/>
<copy todir="classes">
<fileset dir="src" includes="**/*.xml,**/*.dtd"/>
</copy>
</target>
<target name="jar" depends="compile">
<jar manifest="main-class.txt" jarfile="fgfsclient.jar" basedir="classes" excludes="**/Makefile"/>
</target>
<target name="javadoc" depends="prepare">
<javadoc Public="yes" Use="yes" packagenames="org.flightgear.fgfsclient.*"
sourcepath="src" destdir="docs/javadoc" Author="Yes" Version="Yes">
<link href="http://java.sun.com/products/jdk/1.3/docs/api"/>
</javadoc>
</target>
<target name="run" depends="compile">
<java classname="FGFSDemo" fork="yes" failonerror="yes">
<classpath>
<pathelement location="./classes"/>
<pathelement path="${java.class.path}"/>
</classpath>
<arg value="localhost"/>
<arg value="9000"/>
</java>
</target>
<target name="clean" depends="init">
<deltree dir="classes"/>
<deltree dir="docs/javadoc"/>
<delete file="fgfsclient.jar"/>
</target>
</project>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
All Classes
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<FONT size="+1" CLASS="FrameHeadingFont">
<B>All Classes</B></FONT>
<BR>
<TABLE BORDER="0" WIDTH="100%">
<TR>
<TD NOWRAP><FONT CLASS="FrameItemFont"><A HREF="org/flightgear/fgfsclient/FGFSConnection.html" TARGET="classFrame">FGFSConnection</A>
<BR>
</FONT></TD>
</TR>
</TABLE>
</BODY>
</HTML>

View File

@@ -0,0 +1,91 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Deprecated List
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Deprecated</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H2>
<B>Deprecated API</B></H2>
</CENTER>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Deprecated</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,144 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: API Help
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Help</B></FONT>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H1>
How This API Document Is Organized</H1>
</CENTER>
This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.<H3>
Package</H3>
<BLOCKQUOTE>
<P>
Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain four categories:<UL>
<LI>Interfaces (italic)<LI>Classes<LI>Exceptions<LI>Errors</UL>
</BLOCKQUOTE>
<H3>
Class/Interface</H3>
<BLOCKQUOTE>
<P>
Each class, interface, inner class and inner interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:<UL>
<LI>Class inheritance diagram<LI>Direct Subclasses<LI>All Known Subinterfaces<LI>All Known Implementing Classes<LI>Class/interface declaration<LI>Class/interface description
<P>
<LI>Inner Class Summary<LI>Field Summary<LI>Constructor Summary<LI>Method Summary
<P>
<LI>Field Detail<LI>Constructor Detail<LI>Method Detail</UL>
Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.</BLOCKQUOTE>
<H3>
Use</H3>
<BLOCKQUOTE>
Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.</BLOCKQUOTE>
<H3>
Tree (Class Hierarchy)</H3>
<BLOCKQUOTE>
There is a <A HREF="overview-tree.html">Class Hierarchy</A> page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with <code>java.lang.Object</code>. The interfaces do not inherit from <code>java.lang.Object</code>.<UL>
<LI>When viewing the Overview page, clicking on "Tree" displays the hierarchy for all packages.<LI>When viewing a particular package, class or interface page, clicking "Tree" displays the hierarchy for only that package.</UL>
</BLOCKQUOTE>
<H3>
Deprecated API</H3>
<BLOCKQUOTE>
The <A HREF="deprecated-list.html">Deprecated API</A> page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.</BLOCKQUOTE>
<H3>
Index</H3>
<BLOCKQUOTE>
The <A HREF="index-all.html">Index</A> contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.</BLOCKQUOTE>
<H3>
Prev/Next</H3>
These links take you to the next or previous class, interface, package, or related page.<H3>
Frames/No Frames</H3>
These links show and hide the HTML frames. All pages are available with or without frames.
<P>
<H3>
Serialized Form</H3>
Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
<P>
<FONT SIZE="-1">
<EM>
This help file applies to API documentation generated using the standard doclet. </EM>
</FONT>
<BR>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Help</B></FONT>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,153 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Index
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Index</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<A HREF="#_C_">C</A> <A HREF="#_F_">F</A> <A HREF="#_G_">G</A> <A HREF="#_O_">O</A> <A HREF="#_S_">S</A> <HR>
<A NAME="_C_"><!-- --></A><H2>
<B>C</B></H2>
<DL>
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#close()"><B>close()</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Close the connection to FlightGear.
</DL>
<HR>
<A NAME="_F_"><!-- --></A><H2>
<B>F</B></H2>
<DL>
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html"><B>FGFSConnection</B></A> - class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>.<DD>A connection to a running instance of FlightGear.<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#FGFSConnection(java.lang.String, int)"><B>FGFSConnection(String, int)</B></A> -
Constructor for class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Constructor.
</DL>
<HR>
<A NAME="_G_"><!-- --></A><H2>
<B>G</B></H2>
<DL>
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)"><B>get(String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Get the raw string value for a property.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#getBoolean(java.lang.String)"><B>getBoolean(String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Get a property value as a boolean.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#getDouble(java.lang.String)"><B>getDouble(String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Get a property value as a double.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#getFloat(java.lang.String)"><B>getFloat(String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Get a property value as a float.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#getInt(java.lang.String)"><B>getInt(String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Get a property value as an integer.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#getLong(java.lang.String)"><B>getLong(String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Get a property value as a long.
</DL>
<HR>
<A NAME="_O_"><!-- --></A><H2>
<B>O</B></H2>
<DL>
<DT><A HREF="org/flightgear/fgfsclient/package-summary.html"><B>org.flightgear.fgfsclient</B></A> - package org.flightgear.fgfsclient<DD>&nbsp;</DL>
<HR>
<A NAME="_S_"><!-- --></A><H2>
<B>S</B></H2>
<DL>
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)"><B>set(String, String)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Set the raw string value for a property.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#setBoolean(java.lang.String, boolean)"><B>setBoolean(String, boolean)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Set a property value from a boolean.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#setDouble(java.lang.String, double)"><B>setDouble(String, double)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Set a property value from a double.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#setFloat(java.lang.String, float)"><B>setFloat(String, float)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Set a property value from a float.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#setInt(java.lang.String, int)"><B>setInt(String, int)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Set a property value from an int.
<DT><A HREF="org/flightgear/fgfsclient/FGFSConnection.html#setLong(java.lang.String, long)"><B>setLong(String, long)</B></A> -
Method in class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html">FGFSConnection</A>
<DD>Set a property value from a long.
</DL>
<HR>
<A HREF="#_C_">C</A> <A HREF="#_F_">F</A> <A HREF="#_G_">G</A> <A HREF="#_O_">O</A> <A HREF="#_S_">S</A>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Index</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,22 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd>
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002-->
<TITLE>
Generated Documentation (Untitled)
</TITLE>
</HEAD>
<FRAMESET cols="20%,80%">
<FRAME src="allclasses-frame.html" name="packageFrame">
<FRAME src="org/flightgear/fgfsclient/package-summary.html" name="classFrame">
</FRAMESET>
<NOFRAMES>
<H2>
Frame Alert</H2>
<P>
This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
<BR>
Link to <A HREF="org/flightgear/fgfsclient/package-summary.html">Non-frame version.</A></NOFRAMES>
</HTML>

View File

@@ -0,0 +1,533 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Class FGFSConnection
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="class-use/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV CLASS&nbsp;
&nbsp;NEXT CLASS</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
<TR>
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<!-- ======== START OF CLASS DATA ======== -->
<H2>
<FONT SIZE="-1">
org.flightgear.fgfsclient</FONT>
<BR>
Class FGFSConnection</H2>
<PRE>
<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html">java.lang.Object</A>
|
+--<B>org.flightgear.fgfsclient.FGFSConnection</B>
</PRE>
<HR>
<DL>
<DT>public class <B>FGFSConnection</B><DT>extends <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html">Object</A></DL>
<P>
A connection to a running instance of FlightGear.
<p>This class currently uses the FlightGear telnet interface,
though it may be modified to use a different TCP/IP interface in
the future. Client applications can use this library to examine
and modify internal FlightGear properties.</p>
<p>To start FlightGear with the telnet server activated, use a
command like this (to listen on port 9000):</p>
<blockquote><pre>
fgfs --telnet=9000
</pre></blockquote>
<p>Then create a connection to FlightGear from your Java client
application:</p>
<blockquote><pre>
FGFSConnection fgfs = new FGFSConnection("localhost", 9000);
</pre></blockquote>
<p>Now you can use the connection to get and set FlightGear
properties:</p>
<blockquote><pre>
double altitude = fgfs.getDouble("/position/altitude-ft");
fgfs.setDouble("/orientation/heading", 270.0);
</pre></blockquote>
<p>All methods that communicate directly with FlightGear are
synchronized, since they must work over a single telnet
connection.</p>
<P>
<HR>
<P>
<!-- ======== INNER CLASS SUMMARY ======== -->
<!-- =========== FIELD SUMMARY =========== -->
<!-- ======== CONSTRUCTOR SUMMARY ======== -->
<A NAME="constructor_summary"><!-- --></A>
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
<TD COLSPAN=2><FONT SIZE="+2">
<B>Constructor Summary</B></FONT></TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#FGFSConnection(java.lang.String, int)">FGFSConnection</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;host,
int&nbsp;port)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Constructor.</TD>
</TR>
</TABLE>
&nbsp;
<!-- ========== METHOD SUMMARY =========== -->
<A NAME="method_summary"><!-- --></A>
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
<TD COLSPAN=2><FONT SIZE="+2">
<B>Method Summary</B></FONT></TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#close()">close</A></B>()</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Close the connection to FlightGear.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A></CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)">get</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get the raw string value for a property.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;boolean</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getBoolean(java.lang.String)">getBoolean</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get a property value as a boolean.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;double</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getDouble(java.lang.String)">getDouble</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get a property value as a double.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;float</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getFloat(java.lang.String)">getFloat</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get a property value as a float.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;int</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getInt(java.lang.String)">getInt</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get a property value as an integer.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;long</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getLong(java.lang.String)">getLong</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Get a property value as a long.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)">set</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;value)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set the raw string value for a property.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setBoolean(java.lang.String, boolean)">setBoolean</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
boolean&nbsp;value)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set a property value from a boolean.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setDouble(java.lang.String, double)">setDouble</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
double&nbsp;value)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set a property value from a double.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setFloat(java.lang.String, float)">setFloat</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
float&nbsp;value)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set a property value from a float.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setInt(java.lang.String, int)">setInt</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
int&nbsp;value)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set a property value from an int.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setLong(java.lang.String, long)">setLong</A></B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
long&nbsp;value)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set a property value from a long.</TD>
</TR>
</TABLE>
&nbsp;<A NAME="methods_inherited_from_class_java.lang.Object"><!-- --></A>
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
<TR BGCOLOR="#EEEEFF" CLASS="TableSubHeadingColor">
<TD><B>Methods inherited from class java.lang.<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html">Object</A></B></TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD><CODE><A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#equals(java.lang.Object)">equals</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#getClass()">getClass</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#hashCode()">hashCode</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#notify()">notify</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#notifyAll()">notifyAll</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#toString()">toString</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#wait()">wait</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#wait(long)">wait</A>, <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html#wait(long, int)">wait</A></CODE></TD>
</TR>
</TABLE>
&nbsp;
<P>
<!-- ============ FIELD DETAIL =========== -->
<!-- ========= CONSTRUCTOR DETAIL ======== -->
<A NAME="constructor_detail"><!-- --></A>
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
<TD COLSPAN=1><FONT SIZE="+2">
<B>Constructor Detail</B></FONT></TD>
</TR>
</TABLE>
<A NAME="FGFSConnection(java.lang.String, int)"><!-- --></A><H3>
FGFSConnection</H3>
<PRE>
public <B>FGFSConnection</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;host,
int&nbsp;port)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Constructor.
<p>Create a new connection to a running FlightGear program.
The program must have been started with the --telnet=&lt;port&gt;
command-line option.</p><DD><DL>
<DT><B>Parameters:</B><DD><CODE>host</CODE> - The host name or IP address to connect to.<DD><CODE>port</CODE> - The port number where FlightGear is listening.<DT><B>Throws:</B><DD><CODE><A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></CODE> - If it is not possible to connect to
a FlightGear process.</DL>
</DD>
</DL>
<!-- ============ METHOD DETAIL ========== -->
<A NAME="method_detail"><!-- --></A>
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
<TD COLSPAN=1><FONT SIZE="+2">
<B>Method Detail</B></FONT></TD>
</TR>
</TABLE>
<A NAME="close()"><!-- --></A><H3>
close</H3>
<PRE>
public void <B>close</B>()
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Close the connection to FlightGear.
<p>The client application should always invoke this method when
it has finished with a connection, to allow cleanup.</p><DD><DL>
<DT><B>Throws:</B><DD><CODE><A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></CODE> - If there is an error closing the
connection.</DL>
</DD>
</DL>
<HR>
<A NAME="get(java.lang.String)"><!-- --></A><H3>
get</H3>
<PRE>
public <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A> <B>get</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Get the raw string value for a property.
<p>This is the primitive method for all property lookup;
everything comes in as a string, and is only later converted by
methods like <A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getDouble(java.lang.String)"><CODE>getDouble(String)</CODE></A>. As a result, if you
need the value as a string anyway, it makes sense to use this
method directly rather than forcing extra conversions.</p><DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The FlightGear property name to look up.<DT><B>Returns:</B><DD>The property value as a string (non-existant properties
return the empty string).<DT><B>Throws:</B><DD><CODE><A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></CODE> - If there is an error communicating with
FlightGear or if the connection is lost.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getBoolean(java.lang.String)"><CODE>getBoolean(String)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getInt(java.lang.String)"><CODE>getInt(String)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getLong(java.lang.String)"><CODE>getLong(String)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getFloat(java.lang.String)"><CODE>getFloat(String)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#getDouble(java.lang.String)"><CODE>getDouble(String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="set(java.lang.String, java.lang.String)"><!-- --></A><H3>
set</H3>
<PRE>
public void <B>set</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;value)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Set the raw string value for a property.
<p>This is the primitive method for all property modification;
everything goes out as a string, after it has been converted by
methods like <A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setDouble(java.lang.String, double)"><CODE>setDouble(String,double)</CODE></A>. As a result, if
you have the value as a string already, it makes sense to use
this method directly rather than forcing extra conversions.</p><DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The FlightGear property name to modify or create.<DD><CODE>value</CODE> - The new value for the property, as a string.<DT><B>Throws:</B><DD><CODE><A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></CODE> - If there is an error communicating with
FlightGear or if the connection is lost.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setBoolean(java.lang.String, boolean)"><CODE>setBoolean(String,boolean)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setInt(java.lang.String, int)"><CODE>setInt(String,int)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setLong(java.lang.String, long)"><CODE>setLong(String,long)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setFloat(java.lang.String, float)"><CODE>setFloat(String,float)</CODE></A>,
<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#setDouble(java.lang.String, double)"><CODE>setDouble(String,double)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="getBoolean(java.lang.String)"><!-- --></A><H3>
getBoolean</H3>
<PRE>
public boolean <B>getBoolean</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Get a property value as a boolean.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to look up.<DT><B>Returns:</B><DD>The property value as a boolean.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)"><CODE>get(String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="getInt(java.lang.String)"><!-- --></A><H3>
getInt</H3>
<PRE>
public int <B>getInt</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Get a property value as an integer.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to look up.<DT><B>Returns:</B><DD>The property value as an int.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)"><CODE>get(String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="getLong(java.lang.String)"><!-- --></A><H3>
getLong</H3>
<PRE>
public long <B>getLong</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Get a property value as a long.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to look up.<DT><B>Returns:</B><DD>The property value as a long.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)"><CODE>get(String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="getFloat(java.lang.String)"><!-- --></A><H3>
getFloat</H3>
<PRE>
public float <B>getFloat</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Get a property value as a float.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to look up.<DT><B>Returns:</B><DD>The property value as a float.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)"><CODE>get(String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="getDouble(java.lang.String)"><!-- --></A><H3>
getDouble</H3>
<PRE>
public double <B>getDouble</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Get a property value as a double.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to look up.<DT><B>Returns:</B><DD>The property value as a double.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#get(java.lang.String)"><CODE>get(String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="setBoolean(java.lang.String, boolean)"><!-- --></A><H3>
setBoolean</H3>
<PRE>
public void <B>setBoolean</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
boolean&nbsp;value)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Set a property value from a boolean.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to create or modify.<DD><CODE>value</CODE> - The new property value as a boolean.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)"><CODE>set(String,String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="setInt(java.lang.String, int)"><!-- --></A><H3>
setInt</H3>
<PRE>
public void <B>setInt</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
int&nbsp;value)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Set a property value from an int.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to create or modify.<DD><CODE>value</CODE> - The new property value as an int.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)"><CODE>set(String,String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="setLong(java.lang.String, long)"><!-- --></A><H3>
setLong</H3>
<PRE>
public void <B>setLong</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
long&nbsp;value)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Set a property value from a long.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to create or modify.<DD><CODE>value</CODE> - The new property value as a long.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)"><CODE>set(String,String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="setFloat(java.lang.String, float)"><!-- --></A><H3>
setFloat</H3>
<PRE>
public void <B>setFloat</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
float&nbsp;value)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Set a property value from a float.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to create or modify.<DD><CODE>value</CODE> - The new property value as a float.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)"><CODE>set(String,String)</CODE></A></DL>
</DD>
</DL>
<HR>
<A NAME="setDouble(java.lang.String, double)"><!-- --></A><H3>
setDouble</H3>
<PRE>
public void <B>setDouble</B>(<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A>&nbsp;name,
double&nbsp;value)
throws <A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/io/IOException.html">IOException</A></PRE>
<DL>
<DD>Set a property value from a double.<DD><DL>
<DT><B>Parameters:</B><DD><CODE>name</CODE> - The property name to create or modify.<DD><CODE>value</CODE> - The new property value as a double.<DT><B>See Also: </B><DD><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#set(java.lang.String, java.lang.String)"><CODE>set(String,String)</CODE></A></DL>
</DD>
</DL>
<!-- ========= END OF CLASS DATA ========= -->
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="class-use/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV CLASS&nbsp;
&nbsp;NEXT CLASS</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
<TR>
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
SUMMARY: &nbsp;INNER&nbsp;|&nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_summary">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_summary">METHOD</A></FONT></TD>
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
DETAIL: &nbsp;FIELD&nbsp;|&nbsp;<A HREF="#constructor_detail">CONSTR</A>&nbsp;|&nbsp;<A HREF="#method_detail">METHOD</A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,93 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Uses of Class org.flightgear.fgfsclient.FGFSConnection
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../org/flightgear/fgfsclient/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Class</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H2>
<B>Uses of Class<br>org.flightgear.fgfsclient.FGFSConnection</B></H2>
</CENTER>
No usage of org.flightgear.fgfsclient.FGFSConnection
<P>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../org/flightgear/fgfsclient/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Class</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,26 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Package org.flightgear.fgfsclient
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<FONT size="+1" CLASS="FrameTitleFont">
<A HREF="../../../org/flightgear/fgfsclient/package-summary.html" TARGET="classFrame">org.flightgear.fgfsclient</A></FONT>
<TABLE BORDER="0" WIDTH="100%">
<TR>
<TD NOWRAP><FONT size="+1" CLASS="FrameHeadingFont">
Classes</FONT>&nbsp;
<FONT CLASS="FrameItemFont">
<BR>
<A HREF="FGFSConnection.html" TARGET="classFrame">FGFSConnection</A></FONT></TD>
</TR>
</TABLE>
</BODY>
</HTML>

View File

@@ -0,0 +1,104 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Package org.flightgear.fgfsclient
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-use.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV PACKAGE&nbsp;
&nbsp;NEXT PACKAGE</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<H2>
Package org.flightgear.fgfsclient
</H2>
<TABLE BORDER="1" CELLPADDING="3" CELLSPACING="0" WIDTH="100%">
<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
<TD COLSPAN=2><FONT SIZE="+2">
<B>Class Summary</B></FONT></TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD WIDTH="15%"><B><A HREF="FGFSConnection.html">FGFSConnection</A></B></TD>
<TD>A connection to a running instance of FlightGear.</TD>
</TR>
</TABLE>
&nbsp;
<P>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Package</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-use.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV PACKAGE&nbsp;
&nbsp;NEXT PACKAGE</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,99 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: org.flightgear.fgfsclient Class Hierarchy
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H2>
Hierarchy For Package org.flightgear.fgfsclient
</H2>
</CENTER>
<H2>
Class Hierarchy
</H2>
<UL>
<LI TYPE="circle">class java.lang.<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html"><B>Object</B></A><UL>
<LI TYPE="circle">class org.flightgear.fgfsclient.<A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html"><B>FGFSConnection</B></A></UL>
</UL>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,93 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Uses of Package org.flightgear.fgfsclient
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="package-use.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H2>
<B>Uses of Package<br>org.flightgear.fgfsclient</B></H2>
</CENTER>
No usage of org.flightgear.fgfsclient
<P>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-summary.html"><FONT CLASS="NavBarFont1"><B>Package</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="package-use.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,101 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
: Class Hierarchy
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H2>
Hierarchy For All Packages</H2>
</CENTER>
<DL>
<DT><B>Package Hierarchies: </B><DD><A HREF="org/flightgear/fgfsclient/package-tree.html">org.flightgear.fgfsclient</A></DL>
<HR>
<H2>
Class Hierarchy
</H2>
<UL>
<LI TYPE="circle">class java.lang.<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/Object.html"><B>Object</B></A><UL>
<LI TYPE="circle">class org.flightgear.fgfsclient.<A HREF="org/flightgear/fgfsclient/FGFSConnection.html"><B>FGFSConnection</B></A></UL>
</UL>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> &nbsp;<FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1 @@
org.flightgear.fgfsclient

View File

@@ -0,0 +1,26 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<BR>
<BR>
<BR>
<CENTER>
The front page has been relocated.Please see:
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="index.html">Frame version</A>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="org/flightgear/fgfsclient/package-summary.html">Non-frame version.</A></CENTER>
</BODY>
</HTML>

View File

@@ -0,0 +1,91 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN""http://www.w3.org/TR/REC-html40/frameset.dtd">
<!--NewPage-->
<HTML>
<HEAD>
<!-- Generated by javadoc on Sat Jun 08 09:45:47 EDT 2002 -->
<TITLE>
Serialized Form
</TITLE>
<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
</HEAD>
<BODY BGCOLOR="white">
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_top"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_top_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
<CENTER>
<H1>
Serialized Form</H1>
</CENTER>
<HR>
<!-- ========== START OF NAVBAR ========== -->
<A NAME="navbar_bottom"><!-- --></A>
<TABLE BORDER="0" WIDTH="100%" CELLPADDING="1" CELLSPACING="0">
<TR>
<TD COLSPAN=2 BGCOLOR="#EEEEFF" CLASS="NavBarCell1">
<A NAME="navbar_bottom_firstrow"><!-- --></A>
<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="3">
<TR ALIGN="center" VALIGN="top">
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Package</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A>&nbsp;</TD>
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A>&nbsp;</TD>
</TR>
</TABLE>
</TD>
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
</EM>
</TD>
</TR>
<TR>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
&nbsp;PREV&nbsp;
&nbsp;NEXT</FONT></TD>
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A> &nbsp;
&nbsp;<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
</TR>
</TABLE>
<!-- =========== END OF NAVBAR =========== -->
<HR>
</BODY>
</HTML>

View File

@@ -0,0 +1,29 @@
/* Javadoc style sheet */
/* Define colors, fonts and other style attributes here to override the defaults */
/* Page background color */
body { background-color: #FFFFFF }
/* Table colors */
.TableHeadingColor { background: #CCCCFF } /* Dark mauve */
.TableSubHeadingColor { background: #EEEEFF } /* Light mauve */
.TableRowColor { background: #FFFFFF } /* White */
/* Font used in left-hand frame lists */
.FrameTitleFont { font-size: normal; font-family: normal }
.FrameHeadingFont { font-size: normal; font-family: normal }
.FrameItemFont { font-size: normal; font-family: normal }
/* Example of smaller, sans-serif font in frames */
/* .FrameItemFont { font-size: 10pt; font-family: Helvetica, Arial, sans-serif } */
/* Navigation bar fonts and colors */
.NavBarCell1 { background-color:#EEEEFF;}/* Light mauve */
.NavBarCell1Rev { background-color:#00008B;}/* Dark Blue */
.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;}
.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}

Binary file not shown.

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0
Main-Class: FGFSDemo

View File

@@ -0,0 +1,178 @@
// FGFSDemo.java - Simple demo application.
import java.io.IOException;
import java.util.HashMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import org.flightgear.fgfsclient.FGFSConnection;
import org.flightgear.fgfsclient.PropertyPage;
/**
* Simple GUI demo.
*
* <p>This demo connects to a running FlightGear process and displays
* the current altitude, longitude, and latitude in a GUI window, with
* updates every second.</p>
*
* <p>Usage:</p>
*
* <blockquote><pre>
* fgfs --telnet=9000
* java FGFSDemo localhost 9000
* </pre></blockquote>
*/
public class FGFSDemo
extends JFrame
{
public FGFSDemo (String host, int port)
throws IOException
{
super("FlightGear Client Console");
fgfs = new FGFSConnection(host, port);
tabs = new JTabbedPane();
pages = new HashMap();
PropertyPage page = new PropertyPage(fgfs, "Simulation");
page.addField("/sim/aircraft", "Aircraft:");
page.addField("/sim/startup/airport-id", "Airport ID:");
page.addField("/sim/time/gmt", "Current time (GMT):");
page.addField("/sim/startup/trim", "Trim on ground (true/false):");
page.addField("/sim/sound/audible", "Sound enabled (true/false):");
page.addField("/sim/startup/browser-app", "Web browser:");
addPage(page);
page = new PropertyPage(fgfs, "View");
page.addField("/sim/view-mode", "View mode:");
page.addField("/sim/current-view/field-of-view",
"Field of view (deg):");
page.addField("/sim/current-view/pitch-offset-deg",
"View pitch offset (deg):");
page.addField("/sim/current-view/heading-offset-deg",
"View heading offset (deg):");
addPage(page);
page = new PropertyPage(fgfs, "Location");
page.addField("/position/altitude-ft", "Altitude (ft):");
page.addField("/position/longitude-deg", "Longitude (deg):");
page.addField("/position/latitude-deg", "Latitude (deg):");
page.addField("/orientation/roll-deg", "Roll (deg):");
page.addField("/orientation/pitch-deg", "Pitch (deg):");
page.addField("/orientation/heading-deg", "Heading (deg):");
addPage(page);
page = new PropertyPage(fgfs, "Weather");
page.addField("/environment/wind-from-heading-deg",
"Wind direction (deg FROM):");
page.addField("/environment/params/base-wind-speed-kt",
"Wind speed (kt):");
page.addField("/environment/params/gust-wind-speed-kt",
"Maximum gust (kt):");
page.addField("/environment/wind-from-down-fps",
"Updraft (fps):");
page.addField("/environment/temperature-degc", "Temperature (degC):");
page.addField("/environment/dewpoint-degc", "Dewpoint (degC):");
page.addField("/environment/pressure-sea-level-inhg",
"Altimeter setting (inHG):");
addPage(page);
page = new PropertyPage(fgfs, "Clouds");
page.addField("/environment/clouds/layer[0]/type",
"Layer 0 type:");
page.addField("/environment/clouds/layer[0]/elevation-ft",
"Layer 0 height (ft):");
page.addField("/environment/clouds/layer[0]/thickness-ft",
"Layer 0 thickness (ft):");
page.addField("/environment/clouds/layer[1]/type",
"Layer 1 type:");
page.addField("/environment/clouds/layer[1]/elevation-ft",
"Layer 1 height (ft):");
page.addField("/environment/clouds/layer[1]/thickness-ft",
"Layer 1 thickness (ft):");
page.addField("/environment/clouds/layer[2]/type",
"Layer 2 type:");
page.addField("/environment/clouds/layer[2]/elevation-ft",
"Layer 2 height (ft):");
page.addField("/environment/clouds/layer[2]/thickness-ft",
"Layer 2 thickness (ft):");
page.addField("/environment/clouds/layer[3]/type",
"Layer 3 type:");
page.addField("/environment/clouds/layer[3]/elevation-ft",
"Layer 3 height (ft):");
page.addField("/environment/clouds/layer[3]/thickness-ft",
"Layer 3 thickness (ft):");
page.addField("/environment/clouds/layer[4]/type",
"Layer 4 type:");
page.addField("/environment/clouds/layer[4]/elevation-ft",
"Layer 4 height (ft):");
page.addField("/environment/clouds/layer[4]/thickness-ft",
"Layer 4 thickness (ft):");
addPage(page);
page = new PropertyPage(fgfs, "Velocities");
page.addField("/velocities/airspeed-kt", "Airspeed (kt):");
page.addField("/velocities/speed-down-fps", "Descent speed (fps):");
addPage(page);
getContentPane().add(tabs);
new Thread(new Updater()).start();
}
private void addPage (PropertyPage page)
{
tabs.add(page.getName(), new JScrollPane(page));
pages.put(page.getName(), page);
}
private FGFSConnection fgfs;
private JTabbedPane tabs;
private HashMap pages;
public static void main (String args[])
throws Exception
{
if (args.length != 2) {
System.err.println("Usage: FGFSDemo <host> <port>");
System.exit(2);
}
FGFSDemo gui = new FGFSDemo(args[0], Integer.parseInt(args[1]));
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.pack();
gui.show();
}
class Updater
implements Runnable
{
public void run ()
{
while (true) {
int index = tabs.getSelectedIndex();
if (index > -1) {
String name = tabs.getTitleAt(index);
PropertyPage page = (PropertyPage)pages.get(name);
try {
page.update();
} catch (IOException e) {
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
}
}
// end of FGFSDemo.java

View File

@@ -0,0 +1,317 @@
// FGFSConnection.java - client library for the FlightGear flight simulator.
// Started June 2002 by David Megginson, david@megginson.com
// This library is in the Public Domain and comes with NO WARRANTY.
package org.flightgear.fgfsclient;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
/**
* A connection to a running instance of FlightGear.
*
* <p>This class currently uses the FlightGear telnet interface,
* though it may be modified to use a different TCP/IP interface in
* the future. Client applications can use this library to examine
* and modify internal FlightGear properties.</p>
*
* <p>To start FlightGear with the telnet server activated, use a
* command like this (to listen on port 9000):</p>
*
* <blockquote><pre>
* fgfs --telnet=9000
* </pre></blockquote>
*
* <p>Then create a connection to FlightGear from your Java client
* application:</p>
*
* <blockquote><pre>
* FGFSConnection fgfs = new FGFSConnection("localhost", 9000);
* </pre></blockquote>
*
* <p>Now you can use the connection to get and set FlightGear
* properties:</p>
*
* <blockquote><pre>
* double altitude = fgfs.getDouble("/position/altitude-ft");
* fgfs.setDouble("/orientation/heading", 270.0);
* </pre></blockquote>
*
* <p>All methods that communicate directly with FlightGear are
* synchronized, since they must work over a single telnet
* connection.</p>
*/
public class FGFSConnection
{
////////////////////////////////////////////////////////////////////
// Constructor.
////////////////////////////////////////////////////////////////////
/**
* Constructor.
*
* <p>Create a new connection to a running FlightGear program.
* The program must have been started with the --telnet=&lt;port&gt;
* command-line option.</p>
*
* @param host The host name or IP address to connect to.
* @param port The port number where FlightGear is listening.
* @exception IOException If it is not possible to connect to
* a FlightGear process.
*/
public FGFSConnection (String host, int port)
throws IOException
{
socket = new Socket(host, port);
in =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println("data\r");
}
////////////////////////////////////////////////////////////////////
// Primitive getter and setter.
////////////////////////////////////////////////////////////////////
/**
* Close the connection to FlightGear.
*
* <p>The client application should always invoke this method when
* it has finished with a connection, to allow cleanup.</p>
*
* @exception IOException If there is an error closing the
* connection.
*/
public synchronized void close ()
throws IOException
{
out.println("quit\r");
in.close();
out.close();
socket.close();
}
/**
* Get the raw string value for a property.
*
* <p>This is the primitive method for all property lookup;
* everything comes in as a string, and is only later converted by
* methods like {@link #getDouble(String)}. As a result, if you
* need the value as a string anyway, it makes sense to use this
* method directly rather than forcing extra conversions.</p>
*
* @param name The FlightGear property name to look up.
* @return The property value as a string (non-existant properties
* return the empty string).
* @exception IOException If there is an error communicating with
* FlightGear or if the connection is lost.
* @see #getBoolean(String)
* @see #getInt(String)
* @see #getLong(String)
* @see #getFloat(String)
* @see #getDouble(String)
*/
public synchronized String get (String name)
throws IOException
{
out.println("get " + name + '\r');
return in.readLine();
}
/**
* Set the raw string value for a property.
*
* <p>This is the primitive method for all property modification;
* everything goes out as a string, after it has been converted by
* methods like {@link #setDouble(String,double)}. As a result, if
* you have the value as a string already, it makes sense to use
* this method directly rather than forcing extra conversions.</p>
*
* @param name The FlightGear property name to modify or create.
* @param value The new value for the property, as a string.
* @exception IOException If there is an error communicating with
* FlightGear or if the connection is lost.
* @see #setBoolean(String,boolean)
* @see #setInt(String,int)
* @see #setLong(String,long)
* @see #setFloat(String,float)
* @see #setDouble(String,double)
*/
public synchronized void set (String name, String value)
throws IOException
{
out.println("set " + name + ' ' + value + '\r');
}
////////////////////////////////////////////////////////////////////
// Derived getters and setters.
////////////////////////////////////////////////////////////////////
/**
* Get a property value as a boolean.
*
* @param name The property name to look up.
* @return The property value as a boolean.
* @see #get(String)
*/
public boolean getBoolean (String name)
throws IOException
{
return get(name).equals("true");
}
/**
* Get a property value as an integer.
*
* @param name The property name to look up.
* @return The property value as an int.
* @see #get(String)
*/
public int getInt (String name)
throws IOException
{
return Integer.parseInt(get(name));
}
/**
* Get a property value as a long.
*
* @param name The property name to look up.
* @return The property value as a long.
* @see #get(String)
*/
public long getLong (String name)
throws IOException
{
return Long.parseLong(get(name));
}
/**
* Get a property value as a float.
*
* @param name The property name to look up.
* @return The property value as a float.
* @see #get(String)
*/
public float getFloat (String name)
throws IOException
{
return Float.parseFloat(get(name));
}
/**
* Get a property value as a double.
*
* @param name The property name to look up.
* @return The property value as a double.
* @see #get(String)
*/
public double getDouble (String name)
throws IOException
{
return Double.parseDouble(get(name));
}
/**
* Set a property value from a boolean.
*
* @param name The property name to create or modify.
* @param value The new property value as a boolean.
* @see #set(String,String)
*/
public void setBoolean (String name, boolean value)
throws IOException
{
set(name, (value ? "true" : "false"));
}
/**
* Set a property value from an int.
*
* @param name The property name to create or modify.
* @param value The new property value as an int.
* @see #set(String,String)
*/
public void setInt (String name, int value)
throws IOException
{
set(name, Integer.toString(value));
}
/**
* Set a property value from a long.
*
* @param name The property name to create or modify.
* @param value The new property value as a long.
* @see #set(String,String)
*/
public void setLong (String name, long value)
throws IOException
{
set(name, Long.toString(value));
}
/**
* Set a property value from a float.
*
* @param name The property name to create or modify.
* @param value The new property value as a float.
* @see #set(String,String)
*/
public void setFloat (String name, float value)
throws IOException
{
set(name, Float.toString(value));
}
/**
* Set a property value from a double.
*
* @param name The property name to create or modify.
* @param value The new property value as a double.
* @see #set(String,String)
*/
public void setDouble (String name, double value)
throws IOException
{
set(name, Double.toString(value));
}
////////////////////////////////////////////////////////////////////
// Internal state.
////////////////////////////////////////////////////////////////////
private Socket socket;
private BufferedReader in;
private PrintWriter out;
}
// end of FGFSConnection.java

View File

@@ -0,0 +1,59 @@
package org.flightgear.fgfsclient;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class PropertyField
extends JPanel
{
public PropertyField (FGFSConnection fgfs,
String name,
String caption)
{
this.fgfs = fgfs;
propertyName = name;
label = new JLabel(caption);
value = new JTextField(10);
value.addActionListener(new ActionListener () {
public void actionPerformed (ActionEvent ev)
{
try {
modify();
grabFocus();
} catch (IOException ex) {
System.err.println("Failed to update " + propertyName);
}
}
});
add(label);
add(value);
}
public void update ()
throws IOException
{
if (!value.hasFocus())
value.setText(fgfs.get(propertyName));
}
public void modify ()
throws IOException
{
fgfs.set(propertyName, value.getText());
}
private FGFSConnection fgfs;
private String propertyName;
private JLabel label;
private JTextField value;
}

View File

@@ -0,0 +1,48 @@
package org.flightgear.fgfsclient;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
public class PropertyPage
extends JPanel
{
public PropertyPage (FGFSConnection fgfs, String name)
{
this.fgfs = fgfs;
this.name = name;
fields = new ArrayList();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
}
public String getName ()
{
return name;
}
public void addField (String name, String caption)
{
PropertyField field = new PropertyField(fgfs, name, caption);
add(field);
fields.add(field);
}
public void update ()
throws IOException
{
Iterator it = fields.iterator();
while (it.hasNext()) {
((PropertyField)it.next()).update();
}
}
private FGFSConnection fgfs;
private String name;
private ArrayList fields;
}

View File

@@ -0,0 +1,70 @@
package org.flightgear.fgfsclient;
import java.io.IOException;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public final class Util
{
public static void forceConnection (JFrame frame, final FGFSConnection fgfs)
{
while (!fgfs.isConnected()) {
final JDialog dialog =
new JDialog(frame, "Connect to FlightGear", true);
dialog.getContentPane().setLayout(new GridLayout(3, 2));
dialog.getContentPane().add(new JLabel("Host:"));
final JTextField hostField = new JTextField(20);
hostField.setText(fgfs.getHost());
dialog.getContentPane().add(hostField);
dialog.getContentPane().add(new JLabel("Port:"));
final JTextField portField = new JTextField(5);
portField.setText(Integer.toString(fgfs.getPort()));
dialog.getContentPane().add(portField);
JButton connectButton = new JButton("Connect");
connectButton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent ev)
{
try {
fgfs.open(hostField.getText(),
Integer.parseInt(portField.getText()));
} catch (IOException ex) {
JOptionPane.showMessageDialog(dialog, ex.getMessage(),
"Alert", JOptionPane.ERROR_MESSAGE);
}
dialog.hide();
}
});
dialog.getContentPane().add(connectButton);
dialog.getRootPane().setDefaultButton(connectButton);
JButton quitButton = new JButton("Quit");
quitButton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent ev)
{
System.exit(0); // FIXME
}
});
dialog.getContentPane().add(quitButton);
dialog.pack();
dialog.show();
}
}
}

765
scripts/perl/dafif/build_ils.pl Executable file
View File

@@ -0,0 +1,765 @@
#!/usr/bin/perl
########################################################################
# Convert DAFIFT ARPT/ILS.TXT to FlightGear format.
########################################################################
use strict;
my($faa_ils_file) = shift(@ARGV);
my($dafift_arpt_file) = shift(@ARGV);
my($dafift_ils_file) = shift(@ARGV);
my($fgfs_ils_file) = shift(@ARGV);
my($fgfs_apt_file) = shift(@ARGV);
my($output_file) = shift(@ARGV);
my($calc_loc)
= "/home/curt/projects/FlightGear-0.9/source/src/Airports/calc_loc";
die "Usage: $0 " .
"<faa_ils_file> <dafift_arpt_file> <dafift_ils_file> <xplane_ils_file> <fgfs_apt_file> <output_file>\n"
if !defined($faa_ils_file) || !defined($dafift_arpt_file)
|| !defined($dafift_ils_file) || !defined($fgfs_ils_file)
|| !defined($fgfs_apt_file) || !defined($output_file);
my( %CODES );
my( %CodesByICAO );
my( %Elevations );
my( %ILS );
my( %AIRPORTS );
my( %RUNWAYS );
&load_dafift( $dafift_arpt_file, $dafift_ils_file );
&load_faa( $faa_ils_file );
&load_fgfs( $fgfs_ils_file );
&load_fgfs_airports( $fgfs_apt_file );
&fix_localizer();
&fudge_missing_gs_elev();
&write_result( $output_file );
exit;
########################################################################
# Process DAFIFT data
########################################################################
sub load_dafift() {
my( $arpt_file ) = shift;
my( $ils_file ) = shift;
my( $record );
my( $id, $rwy, $type );
my( $has_dme, $has_gs, $has_loc, $has_im, $has_mm, $has_om );
my( $dme_lon, $dme_lat, $dme_elev, $dme_bias );
my( $gs_lon, $gs_lat, $gs_elev, $gs_angle );
my( $loc_type, $loc_lon, $loc_lat, $loc_elev, $loc_freq, $loc_hdg,
$loc_width, $loc_id );
my( $im_lon, $im_lat, $mm_lon, $mm_lat, $om_lon, $om_lat );
# load airport file so we can lookup ICAO from internal ID
open( ARPT, "<$arpt_file" ) || die "Cannot open DAFIFT: $arpt_file\n";
<ARPT>; # skip header line
while ( <ARPT> ) {
chomp;
my(@F) = split(/\t/);
my($icao) = $F[3];
if ( length($icao) < 3 ) {
if ( length( $F[4] ) >= 3 ) {
$icao = $F[4];
} else {
$icao = "[none]";
}
}
$CODES{$F[0]} = $icao;
$CodesByICAO{$icao} = 1;
# print "$F[0] - $icao\n";
$Elevations{$icao} = $F[11];
# print "$icao $F[11]\n";
}
# Load the DAFIFT ils file
my( $last_id, $last_rwy ) = ("", "");
open( DAFIFT_ILS, "<$ils_file" ) || die "Cannot open DAFIFT: $ils_file\n";
<DAFIFT_ILS>; # skip header line
while ( <DAFIFT_ILS> ) {
chomp;
my @F = split(/\t/);
$id = $F[0];
$rwy = $F[1];
if ( $last_id ne "" && ($last_id ne $id || $last_rwy ne $rwy) ) {
# just hist the start of the next record, dump the current data
if ( ! $has_gs ) {
( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
}
if ( ! $has_dme ) {
( $dme_lat, $dme_lon ) = ( 0, 0 );
}
if ( ! $has_om ) {
( $om_lat, $om_lon ) = ( 0, 0 );
}
if ( ! $has_mm ) {
( $mm_lat, $mm_lon ) = ( 0, 0 );
}
if ( ! $has_im ) {
( $im_lat, $im_lon ) = ( 0, 0 );
}
if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
print "DAFIFT adding: $CODES{$last_id} - $last_rwy\n";
&safe_add_record( $CODES{$last_id}, $last_rwy, "ILS",
$loc_freq, $loc_id, $loc_hdg, $loc_lat,
$loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat,
$om_lon, $mm_lat, $mm_lon, $im_lat,
$im_lon );
}
$has_dme = 0;
$has_gs = 0;
$has_loc = 0;
$has_im = 0;
$has_mm = 0;
$has_om = 0;
}
$type = $F[2];
if ( $type eq "D" ) {
# DME entry
$has_dme = 1;
$dme_lon = make_dcoord( $F[16] );
$dme_lat = make_dcoord( $F[14] );
$dme_elev = $F[10];
if ( $dme_elev !~ m/\d/ ) {
$dme_elev = "";
} else {
$dme_elev += 0;
}
$dme_bias = $F[27];
# print "$id DME $dme_lon $dme_lat $dme_elev $dme_bias\n";
} elsif ( $type eq "G" ) {
# GlideSlope entry
$has_gs = 1;
$gs_lon = make_dcoord( $F[16] );
$gs_lat = make_dcoord( $F[14] );
$gs_elev = $F[10];
if ( $gs_elev !~ m/\d/ ) {
$gs_elev = "";
} else {
$gs_elev += 0;
}
$gs_angle = $F[7];
# print "$id GS $gs_lon $gs_lat $gs_elev $gs_angle\n";
} elsif ( $type eq "Z" ) {
# Localizer entry
$has_loc = 1;
$loc_lon = make_dcoord( $F[16] );
$loc_lat = make_dcoord( $F[14] );
$loc_elev = $F[10];
if ( $loc_elev !~ m/\d/ ) {
$loc_elev = "";
} else {
$loc_elev += 0;
}
($loc_freq) = $F[5] =~ m/(\d\d\d\d\d\d)/;
$loc_freq /= 1000.0;
my( $magvar ) = make_dmagvar( $F[22] );
# print "mag var = $F[22] (" . $magvar . ")\n";
$loc_hdg = $F[24] + make_dmagvar( $F[22] );
$loc_width = $F[25];
$loc_id = $F[18];
if ( length( $loc_id ) >= 4 ) {
$loc_id =~ s/^I//;
}
# print "$id LOC $loc_lon $loc_lat $loc_elev $loc_freq $loc_hdg $loc_width\n";
} elsif ( $type eq "I" ) {
# Inner marker entry
$has_im = 1;
$im_lon = make_dcoord( $F[16] );
$im_lat = make_dcoord( $F[14] );
# print "$id IM $im_lon $im_lat\n";
} elsif ( $type eq "M" ) {
# Middle marker entry
$has_mm = 1;
$mm_lon = make_dcoord( $F[16] );
$mm_lat = make_dcoord( $F[14] );
# print "$id MM $mm_lon $mm_lat\n";
} elsif ( $type eq "O" ) {
# Outer marker entry
$has_om = 1;
$om_lon = make_dcoord( $F[16] );
$om_lat = make_dcoord( $F[14] );
# print "$id OM $om_lon $om_lat\n";
}
$last_id = $id;
$last_rwy = $rwy;
# printf("%-5s %10.6f %11.6f\n", $F[0], $F[14], $F[16]);
}
if ( ! $has_gs ) {
( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
}
if ( ! $has_dme ) {
( $dme_lat, $dme_lon ) = ( 0, 0 );
}
if ( ! $has_om ) {
( $om_lat, $om_lon ) = ( 0, 0 );
}
if ( ! $has_mm ) {
( $mm_lat, $mm_lon ) = ( 0, 0 );
}
if ( ! $has_im ) {
( $im_lat, $im_lon ) = ( 0, 0 );
}
if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
print "DAFIFT adding (last): $CODES{$last_id} - $last_rwy\n";
&safe_add_record( $CODES{$last_id}, $last_rwy, "ILS", $loc_freq,
$loc_id, $loc_hdg, $loc_lat, $loc_lon,
$gs_elev, $gs_angle, $gs_lat, $gs_lon,
$dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
}
########################################################################
# Process FAA data
########################################################################
sub load_faa() {
my( $file ) = shift;
open( FAA_ILS, "<$file" ) || die "Cannot open FAA data: $file\n";
<FAA_ILS>; # skip header line
while ( <FAA_ILS> ) {
chomp;
my ( $rec_type, $faa_id, $rwy, $type, $faa_date,
$faa_apt_name, $faa_city, $faa_st, $faa_state,
$faa_region, $id, $faa_len, $faa_wid, $faa_cat,
$faa_owner, $faa_operator, $faa_bearing, $faa_magvar,
$loc_type, $loc_id, $loc_freq, $faa_loc_latd,
$faa_loc_lats, $faa_loc_lond, $faa_loc_lons, $loc_width,
$faa_stop_dist, $faa_app_dist, $faa_gs_type, $gs_angle,
$faa_gs_freq, $faa_gs_latd, $faa_gs_lats, $faa_gs_lond,
$faa_gs_lons, $faa_gs_dist, $gs_elev, $faa_im_type,
$faa_im_latd, $faa_im_lats, $faa_im_lond, $faa_im_lons,
$faa_im_dist, $faa_mm_type, $faa_mm_id, $faa_mm_name,
$faa_mm_freq, $faa_mm_latd, $faa_mm_lats, $faa_mm_lond,
$faa_mm_lons, $faa_mm_dist, $faa_om_type, $faa_om_id,
$faa_om_name, $faa_om_freq, $faa_om_latd, $faa_om_lats,
$faa_om_lond, $faa_om_lons, $faa_om_dist,
$faa_om_backcourse, $faa_dme_channel, $faa_dme_latd,
$faa_dme_lats, $faa_dme_lond, $faa_dme_lons, $faa_dme_app_dist,
$faa_dme_stop_dist, $blank)
= $_ =~
m/^(.{4})(.{11})(.{3})(.{10})(.{10})(.{42})(.{26})(.{2})(.{20})(.{3})(.{4})(.{5})(.{4})(.{9})(.{50})(.{50})(.{3})(.{3})(.{15})(.{5})(.{6})(.{14})(.{11})(.{14})(.{11})(.{5})(.{5})(.{6})(.{15})(.{4})(.{6})(.{14})(.{11})(.{14})(.{11})(.{6})(.{7})(.{15})(.{14})(.{11})(.{14})(.{11})(.{6})(.{15})(.{2})(.{5})(.{3})(.{14})(.{11})(.{14})(.{11})(.{6})(.{15})(.{2})(.{5})(.{3})(.{14})(.{11})(.{14})(.{11})(.{6})(.{9})(.{4})(.{14})(.{11})(.{14})(.{11})(.{6})(.{5})(.{34})/;
$id = &strip_ws( $id );
$rwy = &strip_ws( $rwy );
$rwy =~ s/\/$//;
$rwy =~ s/\/$//;
$loc_id =~ s/^I-//;
my( $loc_hdg ) = $faa_bearing + make_dmagvar($faa_magvar);
my( $loc_lat ) = make_dcoord($faa_loc_lats) / 3600.0;
my( $loc_lon ) = make_dcoord($faa_loc_lons) / 3600.0;
# print "$loc_lon $loc_lat $faa_loc_lons $faa_loc_lats\n";
my( $gs_lat ) = make_dcoord($faa_gs_lats) / 3600.0;
my( $gs_lon ) = make_dcoord($faa_gs_lons) / 3600.0;
my( $im_lat ) = make_dcoord($faa_im_lats) / 3600.0;
my( $im_lon ) = make_dcoord($faa_im_lons) / 3600.0;
my( $mm_lat ) = make_dcoord($faa_mm_lats) / 3600.0;
my( $mm_lon ) = make_dcoord($faa_mm_lons) / 3600.0;
my( $om_lat ) = make_dcoord($faa_om_lats) / 3600.0;
my( $om_lon ) = make_dcoord($faa_om_lons) / 3600.0;
my( $dme_lat ) = make_dcoord($faa_dme_lats) / 3600.0;
my( $dme_lon ) = make_dcoord($faa_dme_lons) / 3600.0;
# my( $key );
# print "$id - $rwy\n";
# $key = $id . $rwy;
# print "-> $key -> $ILS{$key}\n";
# $key = "K" . $id . $rwy;
# print "-> $key -> $ILS{$key}\n";
if ( $rec_type eq "ILS1" ) {
if ( length( $id ) < 4 ) {
if ( $CodesByICAO{"K" . $id} ) {
$id = "K" . $id;
}
}
if ( $ILS{$id . $rwy} ne "" ) {
print "FAA updating: $id - $rwy $type\n";
&update_type( $id, $rwy, $type );
if ( $gs_elev > 0 ) {
&maybe_update_gs_elev( $id . $rwy, $gs_elev );
}
} else {
print "FAA adding: $id - $rwy\n";
&safe_add_record( $id, $rwy, $type, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
}
}
}
########################################################################
# Process FlightGear ILS data
########################################################################
sub load_fgfs() {
my( $ils_file ) = shift;
open( FGILS, "zcat $ils_file|" ) || die "Cannot open FGFS: $ils_file\n";
<FGILS>; # skip header line
while ( <FGILS> ) {
chomp;
if ( ! m/\[End\]/ && length($_) > 1 ) {
# print "$_\n";
my( $type_code, $type_name, $icao, $rwy, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
$im_lat, $im_lon ) = split(/\s+/);
my( $code ) = $icao;
$code =~ s/^K//;
if ( $ILS{$icao . $rwy} ne "" ) {
print "FGFS: Skipping $icao - $rwy - already exists\n";
# skip approaches already in FAA or DAFIFT data
} elsif ( length( $icao ) < 4 || $icao =~ m/^K/ ) {
print "FGFS: Skipping $icao - $rwy - USA\n";
# skip USA approaches not found in FAA or DAFIFT data
} else {
print "FGFS adding: $icao $rwy\n";
&safe_add_record( $icao, $rwy, $type_name, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
} else {
print "FGFS discarding: $_\n";
}
}
}
########################################################################
# Load the x-plane/flightgear airport/runway information
########################################################################
sub load_fgfs_airports() {
my( $infile ) = shift;
my( $id );
open( IN, "zcat $infile|" ) || die "Cannot open: $infile\n";
<IN>; # skip header line
while ( <IN> ) {
chomp;
my(@F) = split(/\s+/);
if ( $F[0] eq "A" ) {
$id = $F[1];
} elsif ( $F[0] eq "R" ) {
my($recip) = &rwy_recip( $F[1] );
$RUNWAYS{ $id . $F[1] } = "FOR $_";
$RUNWAYS{ $id . $recip } = "REV $_";
}
}
}
########################################################################
# Run through the final ils list and adjust the heading to match the runway
########################################################################
sub fix_localizer() {
my( $key );
foreach $key ( sort (keys %ILS) ) {
my(@F) = split( /\s+/, $ILS{$key} );
print "FIXING: $key $F[2] $F[3] $F[4]\n";
if ( $RUNWAYS{$key} ) {
my(@G) = split( /\s+/, $RUNWAYS{$key} );
print " LocPos = $F[7] $F[8]\n";
print " RwyInfo = $G[0] $G[3] $G[4] $G[5] $G[6] $G[7]\n";
my($cmd)
= "$calc_loc $F[7] $F[8] $G[0] $G[3] $G[4] $G[5] $G[6] $G[7]";
open( CALC, "$cmd |" ) || die "Cannot open $calc_loc\n";
my( $j, $lat, $lon );
while ( <CALC> ) {
if ( m/New localizer/ ) {
($j, $j, $j, $lat, $lon) = split( /\s+/ );
}
}
if ( $G[0] eq "REV" ) {
$G[5] += 180.0;
if ( $G[5] >= 360.0 ) {
$G[5] -= 360.0;
}
}
&update_loc( $F[2], $F[3], $lat, $lon, $G[5]);
}
}
}
########################################################################
# Run through the final ils list and fixup any missing glide slope elevations
# to match that of the field elevation.
########################################################################
sub fudge_missing_gs_elev() {
my( $key );
foreach $key ( sort (keys %ILS) ) {
my(@F) = split( /\s+/, $ILS{$key} );
if ( $F[9] <= 0 ) {
print "FUDGING GS: $key $F[2] $F[3] $F[4] - $F[9]\n";
&maybe_update_gs_elev( $key, $Elevations{$F[2]});
}
}
}
########################################################################
# Write out the accumulated combined result
########################################################################
sub write_result() {
my( $outfile ) = shift;
open( OUT, ">$outfile" ) || die "Cannot write to: $outfile\n";
# dump out the final results
print OUT "// FlightGear ILS data, generated from DAFIFT ARPT/ILS.TXT and FAA data\n";
my( $key );
foreach $key ( sort (keys %ILS) ) {
print OUT "$ILS{$key}\n";
}
print OUT "[End]\n";
}
########################################################################
# Utility functions
########################################################################
# add a record to the master list if it doesn't already exist
sub safe_add_record() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $type ) = shift;
my( $loc_freq ) = shift;
my( $loc_id ) = shift;
my( $loc_hdg ) = shift;
my( $loc_lat ) = shift;
my( $loc_lon ) = shift;
my( $gs_elev ) = shift;
my( $gs_angle ) = shift;
my( $gs_lat ) = shift;
my( $gs_lon ) = shift;
my( $dme_lat ) = shift;
my( $dme_lon ) = shift;
my( $om_lat ) = shift;
my( $om_lon ) = shift;
my( $mm_lat ) = shift;
my( $mm_lon ) = shift;
my( $im_lat ) = shift;
my( $im_lon ) = shift;
if ( $ILS{$apt_id . $rwy} eq "" ) {
# print "Safe adding (common): $apt_id - $rwy\n";
&update_record( $apt_id, $rwy, $type, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
}
# replace a record in the master list (or add it if it doesn't exist)
sub update_record() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $type ) = shift;
my( $loc_freq ) = shift;
my( $loc_id ) = shift;
my( $loc_hdg ) = shift;
my( $loc_lat ) = shift;
my( $loc_lon ) = shift;
my( $gs_elev ) = shift;
my( $gs_angle ) = shift;
my( $gs_lat ) = shift;
my( $gs_lon ) = shift;
my( $dme_lat ) = shift;
my( $dme_lon ) = shift;
my( $om_lat ) = shift;
my( $om_lon ) = shift;
my( $mm_lat ) = shift;
my( $mm_lon ) = shift;
my( $im_lat ) = shift;
my( $im_lon ) = shift;
my( $record );
# remap $type as needed
$type = &strip_ws( $type );
if ( $type eq "LOCALIZER" ) {
$type = "LOC";
} elsif ( $type eq "ILS/DME" ) {
$type = "ILS";
} elsif ( $type eq "SDF/DME" ) {
$type = "SDF";
} elsif ( $type eq "LOC/DME" ) {
$type = "ILS";
} elsif ( $type eq "LOC/GS" ) {
$type = "LOC";
} elsif ( $type eq "LDA/DME" ) {
$type = "LDA";
}
$record = sprintf( "%1s %-5s %-4s %-3s %06.2f %-4s %06.2f %10.6f %11.6f ",
substr( $type, 0, 1 ), $type, $apt_id, $rwy,
$loc_freq, $loc_id, $loc_hdg, $loc_lat, $loc_lon );
$record .= sprintf( "%5d %5.2f %10.6f %11.6f ",
$gs_elev, $gs_angle, $gs_lat, $gs_lon );
$record .= sprintf( "%10.6f %11.6f ", $dme_lat, $dme_lon );
$record .= sprintf( "%10.6f %11.6f ", $om_lat, $om_lon );
$record .= sprintf( "%10.6f %11.6f ", $mm_lat, $mm_lon );
$record .= sprintf( "%10.6f %11.6f ", $im_lat, $im_lon );
# print "Updating (common): $apt_id - $rwy\n";
$ILS{$apt_id . $rwy} = $record;
$AIRPORTS{$apt_id} = 1;
}
# update the $type of the record
sub update_type() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $new_type ) = shift;
my( $record );
if ( $ILS{$apt_id . $rwy} ne "" ) {
my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
$im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
# print "Updating type: $apt_id $rwy: $type_name -> $new_type\n";
$type_name = $new_type;
&update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
} else {
die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
}
}
# update the glide slope elevation of the record (but only if it is 0)
sub maybe_update_gs_elev() {
my( $key ) = shift;
my( $new_gs_elev ) = shift;
my( $record );
if ( $ILS{$key} ne "" ) {
my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
$im_lat, $im_lon ) = split( /\s+/, $ILS{$key} );
if ( $gs_elev == 0 ) {
print "Updating gs elev: $apt_id $rwy: $gs_elev -> $new_gs_elev\n";
$gs_elev = $new_gs_elev;
&update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
} else {
die "Error, trying to update $key which doesn't exist\n";
}
}
# update the localizer position of the record
sub update_loc() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $nloc_lat ) = shift;
my( $nloc_lon ) = shift;
my( $nloc_hdg ) = shift;
my( $record );
if ( $ILS{$apt_id . $rwy} ne "" ) {
my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
$im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
print " Old pos = " . $loc_lat . "," . $loc_lon . " " . $loc_hdg . "\n";
print " New pos = " . $nloc_lat . "," . $nloc_lon . " " . $nloc_hdg . "\n";
$loc_lat = $nloc_lat;
$loc_lon = $nloc_lon;
$loc_hdg = $nloc_hdg;
&update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
} else {
die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
}
}
# convert a lon/lat coordinate in various formats to signed decimal
sub make_dcoord() {
my($coord) = shift;
my( $dir, $deg, $min, $sec );
my( $value ) = 0.0;
$coord = &strip_ws( $coord );
if ( $coord =~ m/^[WE]/ ) {
( $dir, $deg, $min, $sec )
= $coord =~ m/^([EW])(\d\d\d)(\d\d)(\d\d\d\d)/;
$value = $deg + $min/60.0 + ($sec/100)/3600.0;
if ( $dir eq "W" ) {
$value = -$value;
}
} elsif ( $coord =~ m/^[NS]/ ) {
( $dir, $deg, $min, $sec )
= $coord =~ m/^([NS])(\d\d)(\d\d)(\d\d\d\d)/;
$value = $deg + $min/60.0 + ($sec/100)/3600.0;
if ( $dir eq "S" ) {
$value = -$value;
}
} elsif ( $coord =~ m/[EW]$/ ) {
($value, $dir) = $coord =~ m/([\d\s\.]+)([EW])/;
if ( $dir eq "W" ) {
$value = -$value;
}
} elsif ( $coord =~ m/[NS]$/ ) {
($value, $dir) = $coord =~ m/([\d\s\.]+)([NS])/;
if ( $dir eq "S" ) {
$value = -$value;
}
}
# print "$dir $deg:$min:$sec = $value\n";
return $value;
}
# convert a magnetic variation in various formats to signed decimal
sub make_dmagvar() {
my( $coord ) = shift;
my( $value );
if ( $coord =~ m/^[EW]/ ) {
my( $dir, $deg, $min, $date )
= $coord =~ m/^([EW])(\d\d\d)(\d\d\d) (\d\d\d\d)/;
$value = $deg + ($min/10)/60.0;
if ( $dir eq "W" ) {
$value = -$value;
}
} elsif ( $coord =~ m/[EW]$/ ) {
my( $deg, $dir )
= $coord =~ m/^(\d\d)([EW])/;
$value = $deg;
if ( $dir eq "W" ) {
$value = -$value;
}
}
# print "$dir $deg:$min = $value\n";
return $value;
}
# strip white space off front and back of string
sub strip_ws() {
my( $string ) = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
}
# return the reciprical runway number
sub rwy_recip() {
my( $input ) = shift;
my($num, $letter);
if ( length($input) == 3 ) {
($num, $letter) = $input =~ m/(\d\d)(.)/;
} elsif ( length($input) == 2 ) {
$num = $input;
$letter = "";
} else {
$num = "";
$letter = $input;
}
# print "RWY: $num - $letter <==> ";
if ( $num ne "" ) {
$num += 18;
if ( $num > 35 ) {
$num -= 36;
}
if ( $num < 10 ) {
$num = "0$num";
}
}
if ( $letter eq "R" ) {
$letter = "L";
} elsif ( $letter eq "L" ) {
$letter = "R";
} elsif ( $letter eq "C" ) {
$letter = "C";
} elsif ( $letter eq "N" ) {
$letter = "S";
} elsif ( $letter eq "S" ) {
$letter = "N";
} elsif ( $letter eq "E" ) {
$letter = "W";
} elsif ( $letter eq "W" ) {
$letter = "E";
}
# print "$num - $letter\n";
return "$num$letter";
}

View File

@@ -0,0 +1,21 @@
#!/usr/bin/perl
########################################################################
# Convert DAFIFT WPT.TXT to FlightGear format.
########################################################################
use strict;
<>; # skip header line
print "// FlightGear intersection data, generated from DAFIFT WPT.TXT\n";
while (<>)
{
chop;
my @F = split(/\t/);
printf("%-5s %10.6f %11.6f\n", $F[0], $F[14], $F[16]);
}
print "[End]\n";
# end of dafif2fix.pl

566
scripts/perl/dafif/dafift2ils.pl Executable file
View File

@@ -0,0 +1,566 @@
#!/usr/bin/perl
########################################################################
# Convert DAFIFT ARPT/ILS.TXT to FlightGear format.
########################################################################
use strict;
my($faa_ils_file) = shift(@ARGV);
my($dafift_arpt_file) = shift(@ARGV);
my($dafift_ils_file) = shift(@ARGV);
my($fgfs_ils_file) = shift(@ARGV);
my($output_file) = shift(@ARGV);
die "Usage: $0 " .
"<faa_ils_file> <dafift_arpt_file> <dafift_ils_file> <fgfs_ils_file> <output_file>\n"
if !defined($faa_ils_file) || !defined($dafift_arpt_file)
|| !defined($dafift_ils_file) || !defined($fgfs_ils_file)
|| !defined($output_file);
my( %CODES );
my( %CodesByICAO );
my( %ILS );
my( %AIRPORTS );
&load_dafift( $dafift_arpt_file, $dafift_ils_file );
&load_faa( $faa_ils_file );
&load_fgfs( $fgfs_ils_file );
&write_result( $output_file );
exit;
########################################################################
# Process DAFIFT data
########################################################################
sub load_dafift() {
my( $arpt_file ) = shift;
my( $ils_file ) = shift;
my( $record );
my( $id, $rwy, $type );
my( $has_dme, $has_gs, $has_loc, $has_im, $has_mm, $has_om );
my( $dme_lon, $dme_lat, $dme_elev, $dme_bias );
my( $gs_lon, $gs_lat, $gs_elev, $gs_angle );
my( $loc_type, $loc_lon, $loc_lat, $loc_elev, $loc_freq, $loc_hdg,
$loc_width, $loc_id );
my( $im_lon, $im_lat, $mm_lon, $mm_lat, $om_lon, $om_lat );
# load airport file so we can lookup ICAO from internal ID
open( ARPT, "<$arpt_file" ) || die "Cannot open DAFIFT: $arpt_file\n";
<ARPT>; # skip header line
while ( <ARPT> ) {
chomp;
my(@F) = split(/\t/);
my($icao) = $F[3];
if ( length($icao) < 3 ) {
if ( length( $F[4] ) >= 3 ) {
$icao = $F[4];
} else {
$icao = "[none]";
}
}
$CODES{$F[0]} = $icao;
$CodesByICAO{$icao} = 1;
# print "$F[0] - $icao\n";
}
# Load the DAFIFT ils file
my( $last_id, $last_rwy ) = ("", "");
open( DAFIFT_ILS, "<$ils_file" ) || die "Cannot open DAFIFT: $ils_file\n";
<DAFIFT_ILS>; # skip header line
while ( <DAFIFT_ILS> ) {
chomp;
my @F = split(/\t/);
$id = $F[0];
$rwy = $F[1];
if ( $last_id ne "" && ($last_id ne $id || $last_rwy ne $rwy) ) {
# just hist the start of the next record, dump the current data
if ( ! $has_gs ) {
( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
}
if ( ! $has_dme ) {
( $dme_lat, $dme_lon ) = ( 0, 0 );
}
if ( ! $has_om ) {
( $om_lat, $om_lon ) = ( 0, 0 );
}
if ( ! $has_mm ) {
( $mm_lat, $mm_lon ) = ( 0, 0 );
}
if ( ! $has_im ) {
( $im_lat, $im_lon ) = ( 0, 0 );
}
if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
print "DAFIFT adding: $CODES{$last_id} - $last_rwy\n";
&safe_add_record( $CODES{$last_id}, $last_rwy, "ILS",
$loc_freq, $loc_id, $loc_hdg, $loc_lat,
$loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat,
$om_lon, $mm_lat, $mm_lon, $im_lat,
$im_lon );
}
$has_dme = 0;
$has_gs = 0;
$has_loc = 0;
$has_im = 0;
$has_mm = 0;
$has_om = 0;
}
$type = $F[2];
if ( $type eq "D" ) {
# DME entry
$has_dme = 1;
$dme_lon = make_dcoord( $F[16] );
$dme_lat = make_dcoord( $F[14] );
$dme_elev = $F[10];
if ( $dme_elev !~ m/\d/ ) {
$dme_elev = "";
} else {
$dme_elev += 0;
}
$dme_bias = $F[27];
# print "$id DME $dme_lon $dme_lat $dme_elev $dme_bias\n";
} elsif ( $type eq "G" ) {
# GlideSlope entry
$has_gs = 1;
$gs_lon = make_dcoord( $F[16] );
$gs_lat = make_dcoord( $F[14] );
$gs_elev = $F[10];
if ( $gs_elev !~ m/\d/ ) {
$gs_elev = "";
} else {
$gs_elev += 0;
}
$gs_angle = $F[7];
# print "$id GS $gs_lon $gs_lat $gs_elev $gs_angle\n";
} elsif ( $type eq "Z" ) {
# Localizer entry
$has_loc = 1;
$loc_lon = make_dcoord( $F[16] );
$loc_lat = make_dcoord( $F[14] );
$loc_elev = $F[10];
if ( $loc_elev !~ m/\d/ ) {
$loc_elev = "";
} else {
$loc_elev += 0;
}
($loc_freq) = $F[5] =~ m/(\d\d\d\d\d\d)/;
$loc_freq /= 1000.0;
my( $magvar ) = make_dmagvar( $F[22] );
# print "mag var = $F[22] (" . $magvar . ")\n";
$loc_hdg = $F[24] + make_dmagvar( $F[22] );
$loc_width = $F[25];
$loc_id = $F[18];
if ( length( $loc_id ) >= 4 ) {
$loc_id =~ s/^I//;
}
# print "$id LOC $loc_lon $loc_lat $loc_elev $loc_freq $loc_hdg $loc_width\n";
} elsif ( $type eq "I" ) {
# Inner marker entry
$has_im = 1;
$im_lon = make_dcoord( $F[16] );
$im_lat = make_dcoord( $F[14] );
# print "$id IM $im_lon $im_lat\n";
} elsif ( $type eq "M" ) {
# Middle marker entry
$has_mm = 1;
$mm_lon = make_dcoord( $F[16] );
$mm_lat = make_dcoord( $F[14] );
# print "$id MM $mm_lon $mm_lat\n";
} elsif ( $type eq "O" ) {
# Outer marker entry
$has_om = 1;
$om_lon = make_dcoord( $F[16] );
$om_lat = make_dcoord( $F[14] );
# print "$id OM $om_lon $om_lat\n";
}
$last_id = $id;
$last_rwy = $rwy;
# printf("%-5s %10.6f %11.6f\n", $F[0], $F[14], $F[16]);
}
if ( ! $has_gs ) {
( $gs_elev, $gs_angle, $gs_lat, $gs_lon ) = ( 0, 0, 0, 0 );
}
if ( ! $has_dme ) {
( $dme_lat, $dme_lon ) = ( 0, 0 );
}
if ( ! $has_om ) {
( $om_lat, $om_lon ) = ( 0, 0 );
}
if ( ! $has_mm ) {
( $mm_lat, $mm_lon ) = ( 0, 0 );
}
if ( ! $has_im ) {
( $im_lat, $im_lon ) = ( 0, 0 );
}
if ( $ILS{$CODES{$last_id} . $last_rwy} eq "" ) {
print "DAFIFT adding (last): $CODES{$last_id} - $last_rwy\n";
&safe_add_record( $CODES{$last_id}, $last_rwy, "ILS", $loc_freq,
$loc_id, $loc_hdg, $loc_lat, $loc_lon,
$gs_elev, $gs_angle, $gs_lat, $gs_lon,
$dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
}
########################################################################
# Process FAA data
########################################################################
sub load_faa() {
my( $file ) = shift;
open( FAA_ILS, "<$file" ) || die "Cannot open FAA data: $file\n";
<FAA_ILS>; # skip header line
while ( <FAA_ILS> ) {
chomp;
my ( $rec_type, $faa_id, $rwy, $type, $faa_date,
$faa_apt_name, $faa_city, $faa_st, $faa_state,
$faa_region, $id, $faa_len, $faa_wid, $faa_cat,
$faa_owner, $faa_operator, $faa_bearing, $faa_magvar,
$loc_type, $loc_id, $loc_freq, $faa_loc_latd,
$faa_loc_lats, $faa_loc_lond, $faa_loc_lons, $loc_width,
$faa_stop_dist, $faa_app_dist, $faa_gs_type, $gs_angle,
$faa_gs_freq, $faa_gs_latd, $faa_gs_lats, $faa_gs_lond,
$faa_gs_lons, $faa_gs_dist, $gs_elev, $faa_im_type,
$faa_im_latd, $faa_im_lats, $faa_im_lond, $faa_im_lons,
$faa_im_dist, $faa_mm_type, $faa_mm_id, $faa_mm_name,
$faa_mm_freq, $faa_mm_latd, $faa_mm_lats, $faa_mm_lond,
$faa_mm_lons, $faa_mm_dist, $faa_om_type, $faa_om_id,
$faa_om_name, $faa_om_freq, $faa_om_latd, $faa_om_lats,
$faa_om_lond, $faa_om_lons, $faa_om_dist,
$faa_om_backcourse, $faa_dme_channel, $faa_dme_latd,
$faa_dme_lats, $faa_dme_lond, $faa_dme_lons, $faa_dme_app_dist,
$faa_dme_stop_dist, $blank)
= $_ =~
m/^(.{4})(.{11})(.{3})(.{10})(.{10})(.{42})(.{26})(.{2})(.{20})(.{3})(.{4})(.{5})(.{4})(.{9})(.{50})(.{50})(.{3})(.{3})(.{15})(.{5})(.{6})(.{14})(.{11})(.{14})(.{11})(.{5})(.{5})(.{6})(.{15})(.{4})(.{6})(.{14})(.{11})(.{14})(.{11})(.{6})(.{7})(.{15})(.{14})(.{11})(.{14})(.{11})(.{6})(.{15})(.{2})(.{5})(.{3})(.{14})(.{11})(.{14})(.{11})(.{6})(.{15})(.{2})(.{5})(.{3})(.{14})(.{11})(.{14})(.{11})(.{6})(.{9})(.{4})(.{14})(.{11})(.{14})(.{11})(.{6})(.{5})(.{34})/;
$id = &strip_ws( $id );
$rwy = &strip_ws( $rwy );
$rwy =~ s/\/$//;
$rwy =~ s/\/$//;
$loc_id =~ s/^I-//;
my( $loc_hdg ) = $faa_bearing + make_dmagvar($faa_magvar);
my( $loc_lat ) = make_dcoord($faa_loc_lats) / 3600.0;
my( $loc_lon ) = make_dcoord($faa_loc_lons) / 3600.0;
# print "$loc_lon $loc_lat $faa_loc_lons $faa_loc_lats\n";
my( $gs_lat ) = make_dcoord($faa_gs_lats) / 3600.0;
my( $gs_lon ) = make_dcoord($faa_gs_lons) / 3600.0;
my( $im_lat ) = make_dcoord($faa_im_lats) / 3600.0;
my( $im_lon ) = make_dcoord($faa_im_lons) / 3600.0;
my( $mm_lat ) = make_dcoord($faa_mm_lats) / 3600.0;
my( $mm_lon ) = make_dcoord($faa_mm_lons) / 3600.0;
my( $om_lat ) = make_dcoord($faa_om_lats) / 3600.0;
my( $om_lon ) = make_dcoord($faa_om_lons) / 3600.0;
my( $dme_lat ) = make_dcoord($faa_dme_lats) / 3600.0;
my( $dme_lon ) = make_dcoord($faa_dme_lons) / 3600.0;
# my( $key );
# print "$id - $rwy\n";
# $key = $id . $rwy;
# print "-> $key -> $ILS{$key}\n";
# $key = "K" . $id . $rwy;
# print "-> $key -> $ILS{$key}\n";
if ( $rec_type eq "ILS1" ) {
if ( length( $id ) < 4 ) {
if ( $CodesByICAO{"K" . $id} ) {
$id = "K" . $id;
}
}
if ( $ILS{$id . $rwy} ne "" ) {
print "FAA updating: $id - $rwy $type\n";
&update_type( $id, $rwy, $type );
} else {
print "FAA adding: $id - $rwy\n";
&safe_add_record( $id, $rwy, $type, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
}
}
}
########################################################################
# Process FlightGear ILS data
########################################################################
sub load_fgfs() {
my( $ils_file ) = shift;
open( FGILS, "zcat $ils_file|" ) || die "Cannot open FGFS: $ils_file\n";
<FGILS>; # skip header line
while ( <FGILS> ) {
chomp;
if ( ! m/\[End\]/ && length($_) > 1 ) {
# print "$_\n";
my( $type_code, $type_name, $icao, $rwy, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
$im_lat, $im_lon ) = split(/\s+/);
my( $code ) = $icao;
$code =~ s/^K//;
if ( $ILS{$icao . $rwy} ne "" ) {
print "FGFS: Skipping $icao - $rwy - already exists\n";
# skip approaches already in FAA or DAFIFT data
} elsif ( length( $icao ) < 4 || $icao =~ m/^K/ ) {
print "FGFS: Skipping $icao - $rwy - USA\n";
# skip USA approaches not found in FAA or DAFIFT data
} else {
print "FGFS adding: $icao $rwy\n";
&safe_add_record( $icao, $rwy, $type_name, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
} else {
print "FGFS discarding: $_\n";
}
}
}
########################################################################
# Write out the accumulated combined result
########################################################################
sub write_result() {
my( $outfile ) = shift;
open( OUT, ">$outfile" ) || die "Cannot write to: $outfile\n";
# dump out the final results
print OUT "// FlightGear ILS data, generated from DAFIFT ARPT/ILS.TXT and FAA data\n";
my( $key );
foreach $key ( sort (keys %ILS) ) {
print OUT "$ILS{$key}\n";
}
print OUT "[End]\n";
}
########################################################################
# Utility functions
########################################################################
# add a record to the master list if it doesn't already exist
sub safe_add_record() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $type ) = shift;
my( $loc_freq ) = shift;
my( $loc_id ) = shift;
my( $loc_hdg ) = shift;
my( $loc_lat ) = shift;
my( $loc_lon ) = shift;
my( $gs_elev ) = shift;
my( $gs_angle ) = shift;
my( $gs_lat ) = shift;
my( $gs_lon ) = shift;
my( $dme_lat ) = shift;
my( $dme_lon ) = shift;
my( $om_lat ) = shift;
my( $om_lon ) = shift;
my( $mm_lat ) = shift;
my( $mm_lon ) = shift;
my( $im_lat ) = shift;
my( $im_lon ) = shift;
if ( $ILS{$apt_id . $rwy} eq "" ) {
# print "Safe adding (common): $apt_id - $rwy\n";
&update_record( $apt_id, $rwy, $type, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
}
}
# replace a record in the master list (or add it if it doesn't exist)
sub update_record() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $type ) = shift;
my( $loc_freq ) = shift;
my( $loc_id ) = shift;
my( $loc_hdg ) = shift;
my( $loc_lat ) = shift;
my( $loc_lon ) = shift;
my( $gs_elev ) = shift;
my( $gs_angle ) = shift;
my( $gs_lat ) = shift;
my( $gs_lon ) = shift;
my( $dme_lat ) = shift;
my( $dme_lon ) = shift;
my( $om_lat ) = shift;
my( $om_lon ) = shift;
my( $mm_lat ) = shift;
my( $mm_lon ) = shift;
my( $im_lat ) = shift;
my( $im_lon ) = shift;
my( $record );
# remap $type as needed
$type = &strip_ws( $type );
if ( $type eq "LOCALIZER" ) {
$type = "LOC";
} elsif ( $type eq "ILS/DME" ) {
$type = "ILS";
} elsif ( $type eq "SDF/DME" ) {
$type = "SDF";
} elsif ( $type eq "LOC/DME" ) {
$type = "ILS";
} elsif ( $type eq "LOC/GS" ) {
$type = "LOC";
} elsif ( $type eq "LDA/DME" ) {
$type = "LDA";
}
$record = sprintf( "%1s %-5s %-4s %-3s %06.2f %-4s %06.2f %10.6f %11.6f ",
substr( $type, 0, 1 ), $type, $apt_id, $rwy,
$loc_freq, $loc_id, $loc_hdg, $loc_lat, $loc_lon );
$record .= sprintf( "%5d %5.2f %10.6f %11.6f ",
$gs_elev, $gs_angle, $gs_lat, $gs_lon );
$record .= sprintf( "%10.6f %11.6f ", $dme_lat, $dme_lon );
$record .= sprintf( "%10.6f %11.6f ", $om_lat, $om_lon );
$record .= sprintf( "%10.6f %11.6f ", $mm_lat, $mm_lon );
$record .= sprintf( "%10.6f %11.6f ", $im_lat, $im_lon );
# print "Updating (common): $apt_id - $rwy\n";
$ILS{$apt_id . $rwy} = $record;
$AIRPORTS{$apt_id} = 1;
}
# update the $type of the record
sub update_type() {
my( $apt_id ) = shift;
my( $rwy ) = shift;
my( $new_type ) = shift;
my( $record );
if ( $ILS{$apt_id . $rwy} ne "" ) {
my( $type_code, $type_name, $apt_id, $rwy, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev, $gs_angle, $gs_lat,
$gs_lon, $dme_lat, $dme_lon, $om_lat, $om_lon, $mm_lat, $mm_lon,
$im_lat, $im_lon ) = split( /\s+/, $ILS{$apt_id . $rwy} );
# print "Updating type: $apt_id $rwy: $type_name -> $new_type\n";
$type_name = $new_type;
&update_record( $apt_id, $rwy, $type_name, $loc_freq, $loc_id,
$loc_hdg, $loc_lat, $loc_lon, $gs_elev,
$gs_angle, $gs_lat, $gs_lon, $dme_lat,
$dme_lon, $om_lat, $om_lon, $mm_lat,
$mm_lon, $im_lat, $im_lon );
} else {
die "Error, trying to update $apt_id - $rwy which doesn't exist\n";
}
}
# convert a lon/lat coordinate in various formats to signed decimal
sub make_dcoord() {
my($coord) = shift;
my( $dir, $deg, $min, $sec );
my( $value ) = 0.0;
$coord = &strip_ws( $coord );
if ( $coord =~ m/^[WE]/ ) {
( $dir, $deg, $min, $sec )
= $coord =~ m/^([EW])(\d\d\d)(\d\d)(\d\d\d\d)/;
$value = $deg + $min/60.0 + ($sec/100)/3600.0;
if ( $dir eq "W" ) {
$value = -$value;
}
} elsif ( $coord =~ m/^[NS]/ ) {
( $dir, $deg, $min, $sec )
= $coord =~ m/^([NS])(\d\d)(\d\d)(\d\d\d\d)/;
$value = $deg + $min/60.0 + ($sec/100)/3600.0;
if ( $dir eq "S" ) {
$value = -$value;
}
} elsif ( $coord =~ m/[EW]$/ ) {
($value, $dir) = $coord =~ m/([\d\s\.]+)([EW])/;
if ( $dir eq "W" ) {
$value = -$value;
}
} elsif ( $coord =~ m/[NS]$/ ) {
($value, $dir) = $coord =~ m/([\d\s\.]+)([NS])/;
if ( $dir eq "S" ) {
$value = -$value;
}
}
# print "$dir $deg:$min:$sec = $value\n";
return $value;
}
# convert a magnetic variation in various formats to signed decimal
sub make_dmagvar() {
my( $coord ) = shift;
my( $value );
if ( $coord =~ m/^[EW]/ ) {
my( $dir, $deg, $min, $date )
= $coord =~ m/^([EW])(\d\d\d)(\d\d\d) (\d\d\d\d)/;
$value = $deg + ($min/10)/60.0;
if ( $dir eq "W" ) {
$value = -$value;
}
} elsif ( $coord =~ m/[EW]$/ ) {
my( $deg, $dir )
= $coord =~ m/^(\d\d)([EW])/;
$value = $deg;
if ( $dir eq "W" ) {
$value = -$value;
}
}
# print "$dir $deg:$min = $value\n";
return $value;
}
# strip white space off front and back of string
sub strip_ws() {
my( $string ) = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
}

160
scripts/perl/dafif/dafift2nav.pl Executable file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/perl
########################################################################
# Convert DAFIFT NAV.TXT to FlightGear format.
########################################################################
use strict;
my @TYPES = (
'', # Unknown
'V', # VOR
'V', # VORTAC
'D', # TACAN
'V', # VOR/DME
'N', # NDB
'',
'N', # NDB/DME
'',
'D' # DME
);
my @TYPE_NAMES = (
'',
'VOR',
'VORTAC',
'TACAN',
'VOR/DME',
'NDB',
'',
'NDB/DME',
'',
'DME'
);
my @HAS_DME = (
'N', # Unknown
'N', # VOR
'Y', # VORTAC
'Y', # TACAN
'Y', # VOR/DME
'N', # NDB
'N',
'Y', # NDB/DME (not used, though)
'N',
'Y' # DME
);
# Make a frequency from a DME channel
sub make_freq {
my ($type, $channel) = (@_);
my $offset = 0;
$offset = 0.05 if ($channel =~ /Y$/);
if ($channel < 67) {
return 108 + (($channel - 17)/10.0) + $offset;
} else {
return 112 + (($channel - 67)/10.0) + $offset;
}
}
# Make a range based on navaid type and purpose
sub make_range {
my ($type, $usage) = (@_);
if ($type == 1 || $type == 2 || $type ==4) { # VOR
if ($usage == 'H' || $usage == 'B') {
return 200;
} elsif ($usage == 'T') {
return 20;
} else {
return 50;
}
} elsif ($type == 3 || $type == 7) { # DME
if ($usage == 'T') {
return 50;
} else {
return 200;
}
} else { # NDB
if ($usage == 'T') {
return 50;
} else {
return 200;
}
}
}
sub write_navaid {
my ($type, $lat, $lon, $elev, $freq, $range, $dme, $id, $magvar, $name)
= (@_);
printf("%s %10.6f %11.6f %6d %7.2f %4d %s %-4s %s %s %s\n",
$TYPES[$type], $lat, $lon, $elev, $freq, $range, $dme, $id,
$magvar, $name, $TYPE_NAMES[$type]);
}
sub make_dmagvar {
my($coord) = shift;
my( $value );
my( $dir, $deg, $date ) = $coord =~ m/^([EW])(\d\d\d\d)(\d\d\d\d)/;
$value = $deg / 10.0;
if ( $dir eq "W" ) {
$value = -$value;
}
return $value;
}
<>; # skip header line
print "// FlightGear navaid data, generated from DAFIFT NAV.TXT\n";
while (<>)
{
chop;
my @F = split(/\t/);
my $type = $F[1];
if ($TYPES[$type] eq '') {
warn("Bad type for " . $F[0] . "(" . $F[5] . ")\n");
next;
}
my $lat = $F[18];
my $lon = $F[20];
my $elev = $F[23];
my $freq = $F[8]/1000;
if ($type == 3 || $type == 9) {
$freq = make_freq($type, $F[10]);
}
my $range = 0 + $F[14];
if ($range == 0) {
$range = make_range($type, $F[9]);
}
my $id = $F[0];
my $magvar = $F[21];
if ($magvar eq '') {
$magvar = 'XXX';
} else {
my $tmp = make_dmagvar( $magvar );
# print "$magvar $tmp\n";
if ( $tmp <= 0 ) {
$magvar = sprintf("%02.0fW", -$tmp );
} else {
$magvar = sprintf("%02.0fE", $tmp );
}
}
my $name = $F[5];
if ($type == 7) { # NDB/DME
write_navaid(9, $lat, $lon, $elev, make_freq(9, $F[10]), $range,
'Y', $id, $magvar, $name);
$type = 5;
}
write_navaid($type, $lat, $lon, $elev, $freq, $range,
$HAS_DME[$F[1]], $id, $magvar, $name);
}
print "[End]\n";
# end of dafif2fix.pl

109
scripts/perl/examples/aircraft.pl Executable file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/perl
#
# aircraft.pl - Handle aircraft functions
#
# Written by Curtis L. Olson, started January 2004
#
# Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
#
# This code is placed in the public domain by Curtis L. Olson.
# There is no warranty, etc. etc. etc.
#
# $Id$
# ----------------------------------------------------------------------------
require "telnet.pl";
use strict;
sub start_engine {
my( $fgfs ) = shift;
my( $engine_num ) = shift;
my( $prop, $value );
my( %HASH ) = ();
&set_prop( $fgfs, "/controls/engines/engine[$engine_num]/magnetos", "3" );
&set_prop( $fgfs, "/controls/engines/engine[$engine_num]/starter", "true" );
sleep(3);
&set_prop( $fgfs, "/controls/engines/engine[$engine_num]/starter",
"false" );
}
sub set_throttle {
my( $fgfs ) = shift;
my( $engine ) = shift;
my( $throttle_norm ) = shift;
&set_prop( $fgfs, "/controls/engines/engine[$engine]/throttle",
$throttle_norm );
}
sub set_mixture {
my( $fgfs ) = shift;
my( $engine ) = shift;
my( $mix_norm ) = shift;
&set_prop( $fgfs, "/controls/engines/engine[$engine]/mixture", $mix_norm );
}
sub set_weight {
my( $fgfs ) = shift;
my( $lbs ) = shift;
&set_prop( $fgfs, "/sim/aircraft-weight-lbs", $lbs );
}
sub set_cg {
my( $fgfs ) = shift;
my( $inches ) = shift;
&set_prop( $fgfs, "/sim/aircraft-cg-offset-inches", $inches );
}
sub set_parking_brake {
my( $fgfs ) = shift;
my( $pos_norm ) = shift;
&set_prop( $fgfs, "/controls/gear/brake-parking", $pos_norm );
}
sub set_flaps {
my( $fgfs ) = shift;
my( $pos_norm ) = shift;
&set_prop( $fgfs, "/controls/flight/flaps", $pos_norm );
}
sub set_aileron {
my( $fgfs ) = shift;
my( $pos_norm ) = shift;
&set_prop( $fgfs, "/controls/flight/aileron", $pos_norm );
}
sub set_elevator {
my( $fgfs ) = shift;
my( $pos_norm ) = shift;
&set_prop( $fgfs, "/controls/flight/elevator", $pos_norm );
}
sub set_elevator_trim {
my( $fgfs ) = shift;
my( $pos_norm ) = shift;
&set_prop( $fgfs, "/controls/flight/elevator-trim", $pos_norm );
}
sub set_rudder {
my( $fgfs ) = shift;
my( $pos_norm ) = shift;
&set_prop( $fgfs, "/controls/flight/rudder", $pos_norm );
}

View File

@@ -0,0 +1,154 @@
#!/usr/bin/perl
#
# autopilot.pl - Handle autopilot functions
#
# Written by Curtis L. Olson, started January 2004
#
# Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
#
# This code is placed in the public domain by Curtis L. Olson.
# There is no warranty, etc. etc. etc.
#
# $Id$
# ----------------------------------------------------------------------------
require "telnet.pl";
use strict;
sub autopilot_off {
my( $fgfs ) = shift;
&set_prop( $fgfs, "/autopilot/locks/heading", "" );
&set_prop( $fgfs, "/autopilot/locks/altitude", "" );
&set_prop( $fgfs, "/autopilot/locks/speed", "" );
}
sub wing_leveler {
my( $fgfs ) = shift;
my( $state ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/heading", "wing-leveler" );
} else {
&set_prop( $fgfs, "/autopilot/locks/heading", "" );
}
}
sub bank_hold {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $bank_deg ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/heading", "bank-hold" );
&set_prop( $fgfs, "/autopilot/settings/target-bank-deg", $bank_deg );
} else {
&set_prop( $fgfs, "/autopilot/locks/heading", "" );
}
}
sub heading_hold {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $hdg_deg ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/heading", "dg-heading-hold" );
&set_prop( $fgfs, "/autopilot/settings/heading-bug-deg", $hdg_deg );
} else {
&set_prop( $fgfs, "/autopilot/locks/heading", "" );
}
}
sub pitch_hold_trim {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $pitch_deg ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/altitude", "pitch-hold" );
&set_prop( $fgfs, "/autopilot/settings/target-pitch-deg", $pitch_deg );
} else {
&set_prop( $fgfs, "/autopilot/locks/altitude", "" );
}
}
sub pitch_hold_yoke {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $pitch_deg ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/altitude", "pitch-hold-yoke" );
&set_prop( $fgfs, "/autopilot/settings/target-pitch-deg", $pitch_deg );
} else {
&set_prop( $fgfs, "/autopilot/locks/altitude", "" );
}
}
sub altitude_hold {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $alt_ft ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/altitude", "altitude-hold" );
&set_prop( $fgfs, "/autopilot/settings/target-altitude-ft", $alt_ft );
} else {
&set_prop( $fgfs, "/autopilot/locks/altitude", "" );
}
}
sub auto_speed_throttle {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $kts ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/speed", "speed-with-throttle" );
&set_prop( $fgfs, "/autopilot/settings/target-speed-kt", $kts );
} else {
&set_prop( $fgfs, "/autopilot/locks/speed", "" );
}
}
sub auto_speed_pitch_trim {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $kts ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/speed", "speed-with-pitch-trim" );
&set_prop( $fgfs, "/autopilot/settings/target-speed-kt", $kts );
} else {
&set_prop( $fgfs, "/autopilot/locks/speed", "" );
}
}
sub auto_speed_pitch_yoke {
my( $fgfs ) = shift;
my( $state ) = shift;
my( $kts ) = shift;
if ( $state ) {
&set_prop( $fgfs, "/autopilot/locks/speed", "speed-with-pitch-yoke" );
&set_prop( $fgfs, "/autopilot/settings/target-speed-kt", $kts );
} else {
&set_prop( $fgfs, "/autopilot/locks/speed", "" );
}
}

View File

@@ -0,0 +1,138 @@
#!/usr/bin/perl
#
# environment.pl - Handle environment setup
#
# Written by Curtis L. Olson, started January 2004
#
# Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
#
# This code is placed in the public domain by Curtis L. Olson.
# There is no warranty, etc. etc. etc.
#
# $Id$
# ----------------------------------------------------------------------------
require "telnet.pl";
use strict;
sub set_timeofday {
my( $fgfs ) = shift;
my( $timeofday ) = shift;
&send( $fgfs, "run timeofday $timeofday" );
}
sub set_env_layer {
my( $fgfs ) = shift;
my( $layer_type ) = shift; # boundary or aloft
my( $layer_num ) = shift;
my( $wind_hdg_deg ) = shift;
my( $wind_spd_kt ) = shift;
my( $turb_norm ) = shift;
my( $temp_degc ) = shift;
my( $press_inhg ) = shift;
my( $dew_degc ) = shift;
my( $vis ) = shift;
my( $elevation ) = shift;
my( $prop, $value );
my( %HASH ) = ();
my( $prefix ) = "/environment/config/$layer_type/entry[$layer_num]";
$HASH{ "$prefix/wind-from-heading-deg" } = $wind_hdg_deg;
$HASH{ "$prefix/wind-speed-kt" } = $wind_spd_kt;
$HASH{ "$prefix/turbulence/magnitude-norm" } = $turb_norm;
$HASH{ "$prefix/temperature-degc" } = $temp_degc;
$HASH{ "$prefix/pressure-sea-level-inhg" } = $press_inhg;
$HASH{ "$prefix/dewpoint-degc" } = $dew_degc;
$HASH{ "$prefix/visibility-m" } = $vis;
$HASH{ "$prefix/elevation-ft" } = $elevation;
foreach $prop ( keys(%HASH) ) {
$value = $HASH{$prop};
# print "setting $prop = $value\n";
&set_prop( $fgfs, $prop, $value );
}
}
sub set_oat {
my( $fgfs ) = shift;
my( $oat ) = shift;
# set the outside air temperature (simply)
&send( $fgfs, "run set-outside-air-temp-degc $oat" );
}
sub set_pressure {
my( $fgfs ) = shift;
my( $pressure_inhg ) = shift;
my( $layer_type ) = shift; # boundary or aloft
my( $layer_num ) = shift;
my( $wind_hdg_deg ) = shift;
my( $wind_spd_kt ) = shift;
my( $turb_norm ) = shift;
my( $temp_degc ) = shift;
my( $press_inhg ) = shift;
my( $dew_degc ) = shift;
my( $vis ) = shift;
my( $elevation ) = shift;
my( $prop, $value );
my( %HASH ) = ();
my( $i );
for ( $i = 0; $i < 3; ++$i ) {
my( $prefix ) = "/environment/config/boundary/entry[$i]";
$HASH{ "$prefix/pressure-sea-level-inhg" } = $pressure_inhg;
}
for ( $i = 0; $i < 5; ++$i ) {
my( $prefix ) = "/environment/config/aloft/entry[$i]";
$HASH{ "$prefix/pressure-sea-level-inhg" } = $pressure_inhg;
}
foreach $prop ( keys(%HASH) ) {
$value = $HASH{$prop};
# print "setting $prop = $value\n";
&set_prop( $fgfs, $prop, $value );
}
}
sub set_cloud_layer {
my( $fgfs ) = shift;
my( $layer ) = shift;
my( $coverage ) = shift;
my( $elevation_ft ) = shift;
my( $thickness_ft ) = shift;
my( $transition_ft ) = shift;
my( $prop, $value );
my( %HASH ) = ();
$HASH{ "/environment/clouds/layer[$layer]/coverage" } = $coverage;
$HASH{ "/environment/clouds/layer[$layer]/elevation-ft" } = $elevation_ft;
$HASH{ "/environment/clouds/layer[$layer]/thickness-ft" } = $thickness_ft;
$HASH{ "/environment/clouds/layer[$layer]/transition-ft" } = $transition_ft;
foreach $prop ( keys(%HASH) ) {
$value = $HASH{$prop};
# print "setting $prop = $value\n";
&set_prop( $fgfs, $prop, $value );
}
}
sub reinit_environment() {
my( $fgfs ) = shift;
&send( $fgfs, "run reinit environment" );
}

View File

@@ -0,0 +1,88 @@
#!/usr/bin/perl
#
# Written by Curtis L. Olson, started January 2003
#
# This file is in the Public Domain and comes with no warranty.
#
# $Id$
# ----------------------------------------------------------------------------
# This script will calculate the flightgear ground elevation for a
# serious of lon/lat pairs, given one per line via stdin. Result it
# written to stdout. Lon/lat must be specified in decimal degrees,
# i.e. "-110.2324 39.872"
#
# This requires a copy of flightgear running with "--fdm=null" on the
# specified "$server" host name, at the specified "$port".
#
# I highly recommend that you if you plan to feed a large number of
# coordinates through this script that you presort your list by tile id #
# That will minimize the load on the FG tile pager since you will process
# all coordinates for a particular tile before moving on to the next.
# Also, there is a chance the next tile will already be loaded if it is near
# the previous (which it will tend to be if you sort by tile id.)
use strict;
use Time::HiRes qw( usleep );
require "telnet.pl";
my( $server ) = "localhost";
my( $port ) = 5401;
my( $timeout ) = 10;
# open the connection to the running copy of flightgear
my( $fgfs );
if ( !( $fgfs = &connect($server, $port, $timeout) ) ) {
die "Error: can't open socket\n";
}
&send( $fgfs, "data" ); # switch to raw data mode
# elevate ourselves only to make the view more interesting, this
# doesn't affect the results
set_prop( $fgfs, "/position/altitude-ft", "5000" );
my( $last_lon ) = -1000.0;
my( $last_lat ) = -1000.0;
my( $last_elev ) = -1000.0;
# iterate through the requested coordinates
while ( <> ) {
my( $lon, $lat ) = split;
set_prop( $fgfs, "/position/longitude-deg", $lon );
set_prop( $fgfs, "/position/latitude-deg", $lat );
# wait 1 second for scenery to load
usleep(500000);
# then fetch ground elevation
my( $elev ) = get_prop( $fgfs, "/position/ground-elev-m" );
if ( $lon != $last_lon || $lat != $last_lat ) {
my($waitcount) = 0;
while ( $elev == $last_elev && $waitcount < 5 ) {
print "(WARNING: waiting an addition 1 second and requerying.)\n";
# same answer as last time, scenery is probably still loading,
# let's wait 1 more seconds and hope we get it right the next
# time, we bail after 5 seconds.
usleep(1000000);
$elev = get_prop( $fgfs, "/position/ground-elev-m" );
$waitcount++;
}
}
print "$lon $lat $elev\n";
$last_elev = $elev;
$last_lon = $lon;
$last_lat = $lat;
}
# shutdown our connection (this leaves FG running)
&send( $fgfs, "quit");
close $fgfs;

View File

@@ -0,0 +1,91 @@
#!/usr/bin/perl
#
# Written by Curtis L. Olson, started January 2003
#
# This file is in the Public Domain and comes with no warranty.
#
# $Id$
# ----------------------------------------------------------------------------
use strict;
require "telnet.pl";
my( $server ) = "localhost";
my( $port ) = 5401;
my( $timeout ) = 10;
my( %Route );
$Route{0} = "OAK:116.80:020";
$Route{1} = "OAK:116.80:019:27";
$Route{2} = "SAC:115.20:020";
$Route{3} = "SAC:115.20:080:43";
$Route{4} = "ECA:116.0:209";
my( $i );
foreach $i ( keys(%Route) ) {
&fly_to( $Route{$i} );
}
sub fly_to() {
my( $waypoint ) = shift;
# decode waypoint
my( $id, $freq, $radial, $dist ) = split( /:/, $waypoint );
print "Next way point is $id - $freq\n";
print " Target radial is $radial\n";
if ( $dist ne "" ) {
print " Flying outbound for $dist nm\n";
} else {
print " Flying inbound to station\n";
}
# tune radio and set autopilot
my( $fgfs );
if ( !( $fgfs = &connect($server, $port, $timeout) ) ) {
print "Error: can't open socket\n";
return;
}
&send( $fgfs, "data" ); # switch to raw data mode
set_prop( $fgfs, "/radios/nav[0]/frequencies/selected-mhz", $freq );
set_prop( $fgfs, "/radios/nav[0]/radials/selected-deg", $radial );
set_prop( $fgfs, "/radios/dme/switch-position", "1" );
set_prop( $fgfs, "/autopilot/locks/nav", "true" );
# monitor progress until goal is achieved
my( $done ) = 0;
my( $last_range ) = 9999.0;
while ( !$done ) {
my( $inrange ) = get_prop( $fgfs, "/radios/nav[0]/in-range" );
if ( $inrange eq "false" ) {
print "Warning, VOR not in range, we are lost!\n";
}
my( $cur_range ) = get_prop( $fgfs, "/radios/dme/distance-nm" );
print " range = $cur_range\n";
if ( $dist ne "" ) {
# a target dist is specified so assume we are flying outbound
if ( $cur_range > $dist ) {
$done = 1;
}
} else {
# no target dist is specified, assume we are flying
# inbound to the station
if ( $cur_range < 0.25 && $cur_range > 0.0 ) {
$done = 1;
} elsif ( $last_range < $cur_range ) {
$done = 1;
}
}
$last_range = $cur_range;
# loop once per second
sleep(1);
}
&send( $fgfs, "quit");
close $fgfs;
}

209
scripts/perl/examples/logging.pl Executable file
View File

@@ -0,0 +1,209 @@
#!/usr/bin/perl
#
# logging.pl - Handle logging
#
# Written by Curtis L. Olson, started February 2004
#
# Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
#
# This code is placed in the public domain by Curtis L. Olson.
# There is no warranty, etc. etc. etc.
#
# $Id$
# ----------------------------------------------------------------------------
use strict;
require "telnet.pl";
my( $lognum ) = 1;
my( @FIELDS, @FIELDS_E );
my( $tmp_dir ) = "/tmp";
my( $field_index ) = 0;
sub clear_logging {
my( $fgfs ) = shift;
my( $done ) = 0;
my( $i ) = 0;
my( $prop );
while ( !$done ) {
$prop = &get_prop( $fgfs, "/logging/log[$lognum]/entry[$i]/property" );
if ( $prop ne "" ) {
&set_prop( $fgfs,
"/logging/log[$lognum]/entry[$i]/enabled",
"false" );
} else {
$done = 1;
}
$i++;
}
$field_index = 0;
}
sub add_field {
my( $fgfs ) = shift;
my( $title ) = shift;
my( $prop ) = shift;
# spaces seem to not work well.
$title =~ s/ /\_/g;
# print "$title - $prop\n";
&set_prop( $fgfs,
"/logging/log[$lognum]/entry[$field_index]/title", $title );
&set_prop( $fgfs,
"/logging/log[$lognum]/entry[$field_index]/property", $prop );
&set_prop( $fgfs,
"/logging/log[$lognum]/entry[$field_index]/enabled", "true" );
$field_index++;
}
sub add_default_fields() {
my( $fgfs ) = shift;
push( @FIELDS, ( "Longitude (deg)", "/position/longitude-deg" ) );
push( @FIELDS, ( "Latitude (deg)", "/position/latitude-deg" ) );
push( @FIELDS, ( "Altitude (ft MSL)", "/position/altitude-ft" ) );
push( @FIELDS, ( "Altitude (ft AGL)", "/position/altitude-agl-ft" ) );
push( @FIELDS, ( "Roll (deg)", "/orientation/roll-deg" ) );
push( @FIELDS, ( "Pitch (deg)", "/orientation/pitch-deg" ) );
push( @FIELDS, ( "Heading (deg)", "/orientation/heading-deg" ) );
push( @FIELDS, ( "Calibrated Air Speed (kt)", "/velocities/airspeed-kt" ) );
push( @FIELDS, ( "Vertical Speed (fps)",
"/velocities/vertical-speed-fps" ) );
push( @FIELDS, ( "Roll Rate (degps)", "/orientation/roll-rate-degps" ) );
push( @FIELDS, ( "Pitch Rate (degps)", "/orientation/pitch-rate-degps" ) );
push( @FIELDS, ( "Yaw Rate (degps)", "/orientation/yaw-rate-degps" ) );
push( @FIELDS, ( "Alpha (deg)", "/orientation/alpha-deg" ) );
push( @FIELDS, ( "Beta (deg)", "/orientation/side-slip-deg" ) );
push( @FIELDS, ( "Prop (RPM)", "/engines/engine[0]/rpm" ) );
push( @FIELDS, ( "Left Aileron Pos (norm)",
"/surface-positions/left-aileron-pos-norm" ) );
push( @FIELDS, ( "Right Aileron Pos (norm)",
"/surface-positions/right-aileron-pos-norm" ) );
push( @FIELDS, ( "Elevator Pos (norm)",
"/surface-positions/elevator-pos-norm" ) );
push( @FIELDS, ( "Elevator Trim Tab Pos (norm)",
"/surface-positions/elevator-trim-tab-pos-norm" ) );
push( @FIELDS, ( "Rudder Pos (norm)",
"/surface-positions/rudder-pos-norm" ) );
push( @FIELDS, ( "Flap Pos (norm)",
"/surface-positions/flap-pos-norm" ) );
push( @FIELDS, ( "Nose Wheel Pos (norm)",
"/surface-positions/nose-wheel-pos-norm" ) );
push( @FIELDS, ( "Wheel Pos (norm)", "/controls/flight/aileron" ) );
push( @FIELDS, ( "Column Pos (norm)", "/controls/flight/elevator" ) );
push( @FIELDS, ( "Trim Wheel Pos (norm)",
"/controls/flight/elevator-trim" ) );
push( @FIELDS, ( "Pedal Pos (norm)", "/controls/flight/rudder" ) );
push( @FIELDS, ( "Throttle Pos (norm)",
"/controls/engines/engine[0]/throttle" ) );
# initially enable all fields
for ( my($i) = 0; $i <= ($#FIELDS / 2); ++$i ) {
&add_field( $fgfs, $FIELDS[2*$i], $FIELDS[2*$i+1] );
}
}
sub start_logging {
my( $fgfs ) = shift;
my( $log_file ) = shift;
&set_prop( $fgfs, "/logging/log[$lognum]/filename", $log_file );
&set_prop( $fgfs, "/logging/log[$lognum]/interval-ms", "100" );
&set_prop( $fgfs, "/logging/log[$lognum]/enabled", "true" );
&send( $fgfs, "run data-logging-commit" );
}
sub stop_logging {
my( $fgfs ) = shift;
&set_prop( $fgfs, "/logging/log[$lognum]/enabled", "false" );
&send( $fgfs, "run data-logging-commit" );
}
sub quick_plot_vs_time {
my( $data_file ) = shift;
my( $plot_file ) = shift;
my( $title ) = shift;
my( $column ) = shift;
print "quick plot -> $plot_file\n";
my( $tmpcmd ) = "$tmp_dir/plot_cmd_tmp.$$";
my( $tmpdata ) = "$tmp_dir/plot_data_tmp.$$";
my( $png_image ) = "$plot_file.png";
# strip the leading header off the file so gnuplot doesn't squawk
system( "tail -n +2 $data_file | sed -e \"s/,/ /g\" > $tmpdata" );
# create the gnuplot command file
open( CMD, ">$tmpcmd" );
print CMD "set terminal png\n";
print "png_image = $png_image\n";
print CMD "set output \"$png_image\"\n";
print CMD "set xlabel \"Time (sec)\"\n";
print CMD "set ylabel \"$title\"\n";
print CMD "plot \"$tmpdata\" using 1:$column title \"$title\" with lines\n";
print CMD "quit\n";
close( CMD );
# plot the graph
system( "gnuplot $tmpcmd" );
# clean up all our droppings
unlink( $tmpcmd );
unlink( $tmpdata );
}
sub quick_plot {
my( $data_file ) = shift;
my( $plot_file ) = shift;
my( $xtitle ) = shift;
my( $ytitle ) = shift;
my( $xcolumn ) = shift;
my( $ycolumn ) = shift;
print "quick plot -> $plot_file\n";
my( $tmpcmd ) = "$tmp_dir/plot_cmd_tmp.$$";
my( $tmpdata ) = "$tmp_dir/plot_data_tmp.$$";
my( $png_image ) = "$plot_file.png";
# strip the leading header off the file so gnuplot doesn't squawk
system( "tail -n +2 $data_file | sed -e \"s/,/ /g\" > $tmpdata" );
# create the gnuplot command file
open( CMD, ">$tmpcmd" );
print CMD "set terminal png\n";
print "png_image = $png_image\n";
print CMD "set output \"$png_image\"\n";
print CMD "set xlabel \"$xtitle\"\n";
print CMD "set ylabel \"$ytitle\"\n";
print CMD "plot \"$tmpdata\" using $xcolumn:$ycolumn title \"$xtitle vs. $ytitle\" with lines\n";
print CMD "quit\n";
close( CMD );
# plot the graph
system( "gnuplot $tmpcmd" );
# clean up all our droppings
unlink( $tmpcmd );
unlink( $tmpdata );
}
return 1; # make perl happy

107
scripts/perl/examples/position.pl Executable file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/perl
#
# position.pl - Handle repositioning aircraft (in air/on ground)
#
# Written by Curtis L. Olson, started January 2004
#
# Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
#
# This code is placed in the public domain by Curtis L. Olson.
# There is no warranty, etc. etc. etc.
#
# $Id$
# ----------------------------------------------------------------------------
require "telnet.pl";
use strict;
my( $airport_id ) = "KSNA";
my( $rwy_no ) = "19R";
my( $reset_sec ) = 300;
my( $server ) = "localhost";
my( $port ) = 5401;
my( $timeout ) = 5;
sub reset_in_air {
my( $fgfs ) = shift;
my( $aptid ) = shift;
my( $rwy ) = shift;
my( $offset_dist ) = shift;
my( $glideslope_deg ) = shift;
my( $altitude_ft ) = shift;
my( $airspeed_kt ) = shift;
my( $prop, $value );
my( %HASH ) = ();
$HASH{ "/sim/presets/airport-id" } = $aptid;
$HASH{ "/sim/presets/runway" } = $rwy;
$HASH{ "/sim/presets/offset-distance" } = $offset_dist;
if ( $glideslope_deg > 0 ) {
$HASH{ "/sim/presets/glideslope-deg" } = $glideslope_deg;
$HASH{ "/sim/presets/altitude-ft" } = "";
} else {
$HASH{ "/sim/presets/glideslope-deg" } = "";
$HASH{ "/sim/presets/altitude-ft" } = $altitude_ft;
}
$HASH{ "/sim/presets/airspeed-kt" } = $airspeed_kt;
$HASH{ "/sim/presets/vor-id" } = "";
$HASH{ "/sim/presets/vor-freq" } = "";
$HASH{ "/sim/presets/ndb-id" } = "";
$HASH{ "/sim/presets/ndb-freq" } = "";
$HASH{ "/sim/presets/fix" } = "";
$HASH{ "/sim/presets/longitude-deg" } = "-9999.0";
$HASH{ "/sim/presets/latitude-deg" } = "-9999.0";
$HASH{ "/sim/presets/offset-azimuth" } = "";
$HASH{ "/sim/presets/heading-deg" } = "-9999.0";
foreach $prop ( keys(%HASH) ) {
$value = $HASH{$prop};
print "setting $prop = $value\n";
&set_prop( $fgfs, $prop, $value );
}
&send( $fgfs, "run presets-commit" );
}
sub reset_on_ground {
my( $fgfs ) = shift;
my( $aptid ) = shift;
my( $rwy ) = shift;
my( $prop, $value );
my( %HASH ) = ();
$HASH{ "/sim/presets/airport-id" } = $aptid;
$HASH{ "/sim/presets/runway" } = $rwy;
$HASH{ "/sim/presets/offset-distance" } = "";
$HASH{ "/sim/presets/glideslope-deg" } = "";
$HASH{ "/sim/presets/altitude-ft" } = "";
$HASH{ "/sim/presets/airspeed-kt" } = "";
$HASH{ "/sim/presets/vor-id" } = "";
$HASH{ "/sim/presets/vor-freq" } = "";
$HASH{ "/sim/presets/ndb-id" } = "";
$HASH{ "/sim/presets/ndb-freq" } = "";
$HASH{ "/sim/presets/fix" } = "";
$HASH{ "/sim/presets/longitude-deg" } = "-9999.0";
$HASH{ "/sim/presets/latitude-deg" } = "-9999.0";
$HASH{ "/sim/presets/offset-azimuth" } = "";
$HASH{ "/sim/presets/heading-deg" } = "-9999.0";
foreach $prop ( keys(%HASH) ) {
$value = $HASH{$prop};
print "setting $prop = $value\n";
&set_prop( $fgfs, $prop, $value );
}
&send( $fgfs, "run presets-commit" );
}

79
scripts/perl/examples/reset.pl Executable file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/perl
require "telnet.pl";
use strict;
my( $airport_id ) = "KSNA";
my( $rwy_no ) = "19R";
my( $reset_sec ) = 300;
my( $server ) = "localhost";
my( $port ) = 5401;
my( $timeout ) = 5;
while ( 1 ) {
print "Reseting to $airport_id $rwy_no\n";
reset_position( $airport_id, $rwy_no );
sleep( $reset_sec );
}
sub reset_position {
my( $aptid ) = shift;
my( $rwy ) = shift;
my( $prop, $value );
my( %HASH ) = ();
$HASH{ "/sim/presets/airport-id" } = $aptid;
$HASH{ "/sim/presets/runway" } = $rwy;
$HASH{ "/sim/presets/vor-id" } = "";
$HASH{ "/sim/presets/vor-freq" } = "";
$HASH{ "/sim/presets/ndb-id" } = "";
$HASH{ "/sim/presets/ndb-freq" } = "";
$HASH{ "/sim/presets/fix" } = "";
$HASH{ "/sim/presets/longitude-deg" } = "-9999.0";
$HASH{ "/sim/presets/latitude-deg" } = "-9999.0";
$HASH{ "/sim/presets/offset-distance" } = "";
$HASH{ "/sim/presets/offset-azimuth" } = "";
$HASH{ "/sim/presets/heading-deg" } = "-9999.0";
$HASH{ "/sim/presets/altitude-ft" } = "";
$HASH{ "/sim/presets/glideslope-deg" } = "";
$HASH{ "/sim/presets/airspeed-kt" } = "";
my( $fgfs );
if ( !( $fgfs = &connect($server, $port, $timeout) ) ) {
print "Error: can't open socket\n";
return;
}
&send( $fgfs, "data" ); # switch to raw data mode
foreach $prop ( keys(%HASH) ) {
$value = $HASH{$prop};
# if ( $value eq "" ) {
# $value = 0;
# }
print "setting $prop = $value\n";
&set_prop( $fgfs, $prop, $value );
}
&send( $fgfs, "run presets-commit" );
# set time of day to noon
&send( $fgfs, "run timeofday noon" );
# start the engine
&set_prop( $fgfs, "/controls/engines/engine[0]/magnetos", "3" );
&set_prop( $fgfs, "/controls/engines/engine[0]/starter", "true" );
sleep(2);
&set_prop( $fgfs, "/controls/engines/engine[0]/starter", "false" );
&send( $fgfs, "quit" );
close $fgfs;
}

View File

@@ -0,0 +1,84 @@
#!/usr/bin/perl
#
# Written by Curtis L. Olson, started December 2002
# Some code portions courtesy of Melchior FRANZ
#
# This file is in the Public Domain and comes with no warranty.
#
# $Id$
# ----------------------------------------------------------------------------
use IO::Socket;
use strict;
sub my_not() {
my( $val ) = shift;
if ( $val eq "true" ) {
return 0;
} elsif ( $val eq "false" ) {
return 1;
} elsif ( $val eq "" ) {
return 1;
} else {
return 0;
}
}
sub get_prop() {
my( $handle ) = shift;
&send( $handle, "get " . shift );
eof $handle and die "\nconnection closed by host";
$_ = <$handle>;
s/\015?\012$//;
/^-ERR (.*)/ and die "\nfgfs error: $1\n";
return $_;
}
sub set_prop() {
my( $handle ) = shift;
my( $prop ) = shift;
my( $value ) = shift;
&send( $handle, "set $prop $value");
# eof $handle and die "\nconnection closed by host";
}
sub send() {
my( $handle ) = shift;
print $handle shift, "\015\012";
}
sub connect() {
my( $host ) = shift;
my( $port ) = shift;
my( $timeout ) = (shift || 120);
my( $socket );
STDOUT->autoflush(1);
while ($timeout--) {
if ($socket = IO::Socket::INET->new( Proto => 'tcp',
PeerAddr => $host,
PeerPort => $port) )
{
$socket->autoflush(1);
return $socket;
}
print "Attempting to connect to $host ... " . $timeout . "\n";
sleep(1);
}
return 0;
}
return 1; # make perl happy

View File

@@ -0,0 +1,159 @@
#!/usr/bin/perl -w
########################################################################
# calc-tile.pl
#
# Synopsis: Calculate a FlightGear tile base on longitude and latitude.
# Usage: perl calc-tile.pl <lon> <lat>
########################################################################
use strict;
use POSIX;
########################################################################
# Constants.
########################################################################
my $EPSILON = 0.0000001;
my $DIRSEP = '/';
########################################################################
# Functions.
########################################################################
#
# Calculate the number of columns of tiles in a degree of longitude.
#
sub bucket_span {
my ($lat) = (@_);
if ($lat>= 89.0 ) {
return 360.0;
} elsif ($lat>= 88.0 ) {
return 8.0;
} elsif ($lat>= 86.0 ) {
return 4.0;
} elsif ($lat>= 83.0 ) {
return 2.0;
} elsif ($lat>= 76.0 ) {
return 1.0;
} elsif ($lat>= 62.0 ) {
return 0.5;
} elsif ($lat>= 22.0 ) {
return 0.25;
} elsif ($lat>= -22.0 ) {
return 0.125;
} elsif ($lat>= -62.0 ) {
return 0.25;
} elsif ($lat>= -76.0 ) {
return 0.5;
} elsif ($lat>= -83.0 ) {
return 1.0;
} elsif ($lat>= -86.0 ) {
return 2.0;
} elsif ($lat>= -88.0 ) {
return 4.0;
} elsif ($lat>= -89.0 ) {
return 8.0;
} else {
return 360.0;
}
}
#
# Format longitude as e/w.
#
sub format_lon {
my ($lon) = (@_);
if ($lon < 0) {
return sprintf("w%03d", int(0-$lon));
} else {
return sprintf("e%03d", int($lon));
}
}
#
# Format latitude as n/s.
#
sub format_lat {
my ($lat) = (@_);
if ($lat < 0) {
return sprintf("s%02d", int(0-$lat));
} else {
return sprintf("n%02d", int($lat));
}
}
#
# Generate the directory name for a location.
#
sub directory_name {
my ($lon, $lat) = (@_);
my $lon_floor = POSIX::floor($lon);
my $lat_floor = POSIX::floor($lat);
my $lon_chunk = POSIX::floor($lon/10.0) * 10;
my $lat_chunk = POSIX::floor($lat/10.0) * 10;
return format_lon($lon_chunk) . format_lat($lat_chunk) . $DIRSEP
. format_lon($lon_floor) . format_lat($lat_floor);
}
#
# Generate the tile index for a location.
#
sub tile_index {
my ($lon, $lat) = (@_);
my $lon_floor = POSIX::floor($lon);
my $lat_floor = POSIX::floor($lat);
my $span = bucket_span($lat);
my $x;
if ($span < $EPSILON) {
$lon = 0;
$x = 0;
} elsif ($span <= 1.0) {
$x = int(($lon - $lon_floor) / $span);
} else {
if ($lon >= 0) {
$lon = int(int($lon/$span) * $span);
} else {
$lon = int(int(($lon+1)/$span) * $span - $span);
if ($lon < -180) {
$lon = -180;
}
}
$x = 0;
}
my $y;
$y = int(($lat - $lat_floor) * 8);
my $index = 0;
$index += ($lon_floor + 180) << 14;
$index += ($lat_floor + 90) << 6;
$index += $y << 3;
$index += $x;
return $index;
}
########################################################################
# Main program.
########################################################################
my ($lon, $lat) = (@ARGV);
my $dir = directory_name($lon, $lat);
my $index = tile_index($lon, $lat);
my $path = "$dir$DIRSEP$index.stg";
print "Longitude: $lon\n";
print "Latitude: $lat\n";
print "Tile: $index\n";
print "Path: \"$path\"\n";
1;

117
scripts/perl/traffic/conf2xml.pl Executable file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/perl -w
sub parseTime {
# print "Parsing time @_\n";
#die;
my $timeStr = $_[0];
@timeArray = split(":", $timeStr);
# print STDERR "TimeArray: @timeArray\n";
return ($timeArray[0] + $timeArray[1]/60.0);
}
sub writeFlight {
print XMLFILE " <flight>\n";
print XMLFILE " <callsign>$_[0]</callsign>\n";
print XMLFILE " <required-aircraft>$_[1]</required-aircraft>\n";
print XMLFILE " <fltrules>$_[2]</fltrules>\n";
print XMLFILE " <departure>\n";
print XMLFILE " <port>$_[3]</port>\n";
if ($_[4] =~ /[0-6]/) { print XMLFILE " <time>$_[4]/$_[5]:00</time>\n" }
else { print XMLFILE " <time>$_[5]:00</time>\n" };
print XMLFILE " </departure>\n";
print XMLFILE " <cruise-alt>$_[6]</cruise-alt>\n";
print XMLFILE " <arrival>\n";
print XMLFILE " <port>$_[7]</port>\n";
if ($_[8] =~ /[0-6]/) { print XMLFILE " <time>$_[8]/$_[9]:00</time>\n" }
else { print XMLFILE " <time>$_[9]:00</time>\n" };
print XMLFILE " </arrival>\n";
if (($_[4] =~ /[0-6]/) && ($_[8] =~ /[0-6]/)) { print XMLFILE " <repeat>WEEK</repeat>\n" }
else { print XMLFILE " <repeat>24Hr</repeat>\n" };
print XMLFILE " </flight>\n";
return;
}
@inputfiles = glob("???.conf");
while ($infile = shift(@inputfiles)) {
open (CONF, $infile) or die "Unable to open input configuration file";
($outname = $infile) =~ s/conf/xml/;
print "Opening $outname\n";
open XMLFILE, ">$outname";
while ($buf = readline(CONF)) {
push @DataList, $buf;
}
close (CONF);
print XMLFILE "<?xml version=\"1.0\"?>\n";
print XMLFILE "<trafficlist>\n";
while ($dataline = shift(@DataList)) {
# print STDERR "Dataline: $dataline\n";
@token = split(" ", $dataline);
if (scalar(@token) > 0) {
# print STDERR "Token: @token\n";
if ($token[0] eq "AC")
{
print XMLFILE " <aircraft>\n";
print XMLFILE " <model>$token[12]</model>\n";
print XMLFILE " <livery>$token[6]</livery>\n";
print XMLFILE " <airline>$token[5]</airline>\n";
print XMLFILE " <home-port>$token[1]</home-port>\n";
print XMLFILE " <required-aircraft>$token[3]$token[5]</required-aircraft>\n";
print XMLFILE " <actype>$token[4]</actype>\n";
print XMLFILE " <offset>$token[7]</offset>\n";
print XMLFILE " <radius>$token[8]</radius>\n";
print XMLFILE " <flighttype>$token[9]</flighttype>\n";
print XMLFILE " <performance-class>$token[10]</performance-class>\n";
print XMLFILE " <registration>$token[2]</registration>\n";
print XMLFILE " <heavy>$token[11]</heavy>\n";
print XMLFILE " </aircraft>\n";
}
if ($token[0] eq "FLIGHT") {
$weekdays = $token[3];
if (!(($weekdays =~ /^(0|\.)?(1|\.)?(2|\.)?(3|\.)?(4|\.)?(5|\.)?(6|\.)?$/) || ($weekdays eq "DAILY"))) {
die "Syntax Error! Check days $weekdays for flight no. $token[1]!\n";
}
if ($token[4] !~ /^(([0-1]?[0-9])|([2][0-3])):([0-5]?[0-9])$/) {
die "Syntax Error! Check departure time $token[4] for flight no. $token[1]!\n"
}
if ($token[6] !~ /^(([0-1]?[0-9])|([2][0-3])):([0-5]?[0-9])$/) {
die "Syntax Error! Check arrival time $token[6] for flight no. $token[1]!\n"
}
# print STDERR "Weekdays: $weekdays\n";
if ($weekdays =~ /^(0|\.)?(1|\.)?(2|\.)?(3|\.)?(4|\.)?(5|\.)?(6|\.)?$/) {
# print STDERR "Weekly for flight no. $token[1]\n";
# print STDERR "Day: $_\n";
@day = split(//, $weekdays);
foreach (@day) {
if ($_ eq "\.") {
next;
} else {
$depTime = parseTime($token[4]);
# print STDERR "depTime: $depTime\n";
$arrTime = parseTime($token[6]);
# print STDERR "arrTime: $arrTime\n";
$depDay = $_ + 1;
if ($depDay > 6) { $depDay = 0 };
$arrDay = $depDay;
if ($depTime > $arrTime) { $arrDay++ };
if ($arrDay > 6) { $arrDay = 0 };
# print STDERR "depDay: $depDay, arrDay: $arrDay\n";
writeFlight ($token[1], $token[9], $token[2], $token[5], $depDay, $token[4], $token[8], $token[7], $arrDay, $token[6]);
}
}
}
elsif ($weekdays eq "DAILY") {
# print STDERR "Daily flights for flight no. $token[1]\n";
$depTime = parseTime($token[4]);
$arrTime = parseTime($token[6]);
writeFlight ($token[1], $token[9], $token[2], $token[5], 7, $token[4], $token[8], $token[7], 7, $token[6]);
}
else {
die "System Error! Can't find days to place a flight!\n";
}
}
}
}
print XMLFILE "</trafficlist>\n";
close XMLFILE;
# print "Closing $outname\n";
}

View File

@@ -0,0 +1,99 @@
#!/usr/bin/perl -w
#use strict;
#use warnings;
# DEBUG
# use Data::Dumper;
# print Dumper($data) . "\n";
# END
if (scalar (@ARGV) == 1) {
@files = glob("$ARGV[0]");
print "Processing : ", @files, "\n";
} else {
die "Usage : conf2xml.pl <inputfile> [ > outputfile ]\n";
}
$file = shift(@files);
use Switch;
use XML::LibXML;
my $parser = XML::LibXML->new();
my $doc = $parser->load_xml(location => $file);
my $data;
# reformatting days
# According to http://wiki.flightgear.org/index.php/Interactive_Traffic
# 0 = Sunday and 6 = saturday
# For convenience we switch here to "classical" numbering
# where 0 = Monday and 6 = sunday
sub parseDay {
my $day;
$day = substr($_[0],0,1);
switch ($day) {
case 0 {$day="......6"} # Sunday
case 1 {$day="0......"} # Monday
case 2 {$day=".1....."} # Tuesday
case 3 {$day="..2...."} # Wednesday
case 4 {$day="...3..."} # Thrusday
case 5 {$day="....4.."} # Friday
case 6 {$day=".....5."} # Saturday
else {$day="0123456"} # Daily
};
return $day;
}
# reformatting times
sub parseTime {
return substr($_[0],2,5);
}
print "# AC Homeport Registration RequiredAC AcTyp Airline Livery Offset Radius Flighttype PerfClass Heavy Model\n";
# get aircraft data
foreach $data ($doc->findnodes('/trafficlist/aircraft')) {
my $AcMdl = $data->findnodes('./model');
my $AcLvy = $data->findnodes('./livery');
my $AcAln = $data->findnodes('./airline');
my $AcHp = $data->findnodes('./home-port');
my $AcReq = $data->findnodes('./required-aircraft');
my $AcTyp = $data->findnodes('./actype');
my $AcO = $data->findnodes('./offset');
my $AcRad = $data->findnodes('./radius');
my $AcFt = $data->findnodes('./flighttype');
my $AcPrf = $data->findnodes('./performance-class');
my $AcReg = $data->findnodes('./registration');
my $AcHvy = $data->findnodes('./heavy');
print "AC $AcHp $AcReg $AcReq $AcTyp $AcAln $AcLvy $AcO $AcRad $AcFt $AcPrf $AcHvy $AcMdl\n";
}
print "\n# FLIGHT Callsign Flightrule Days DeparTime DepartPort ArrivalTime ArrivalPort Altitude RequiredAc\n# 0 = Monday, 6 = Sunday\n";
# get flight data
foreach $data ($doc->findnodes('/trafficlist/flight')) {
my $FlRep = $data->findnodes('repeat');
my $FlDepPrt = $data->findnodes('departure/port');
my $FlArrPrt = $data->findnodes('arrival/port');
my $FlCs = $data->findnodes('callsign');
my $FlFr = $data->findnodes('fltrules');
my $FlCa = $data->findnodes('cruise-alt');
my $FlReq = $data->findnodes('required-aircraft');
my $FlDepDay = $data->findnodes('departure/time');
my $FlDepTime = $data->findnodes('departure/time');
my $FlArrDay = $data->findnodes('arrival/time');
my $FlArrTime = $data->findnodes('arrival/time');
my $FlDays = ".......";
# handle flights depending on weekly or daily schedule
if (lc($FlRep) eq "week") {
$FlDays = parseDay($FlDepTime);
$FlDepTime = parseTime($FlDepTime);
$FlArrTime = parseTime($FlArrTime);
} elsif (lc($FlRep) eq "24hr") {
$FlDepDay = $data->findnodes('departure/time');
$FlDepTime = substr($data->findnodes('departure/time'),0,5);
$FlArrDay = $data->findnodes('arrival/time');
$FlArrTime = substr($data->findnodes('arrival/time'),0,5);
$FlDays = "0123456";
} else {
die "Error! No proper repetition found in XML!\n";
}
# output data
print "FLIGHT $FlCs $FlFr $FlDays $FlDepTime $FlDepPrt $FlArrTime $FlArrPrt $FlCa $FlReq\n";
}

509
scripts/perl/web/mkindex.pl Executable file
View File

@@ -0,0 +1,509 @@
#!/usr/bin/perl
#
# adapted from a script by Bob Hain, 11/30/99
#
#
# default values
#
$outfile = "new.index.html";
#
# process arguments
#
$use_large = 1;
while ( $arg = shift @ARGV ) {
if ( $arg eq "--large" ) {
$use_large = 1;
}
if ( $arg eq "--outfile" ) {
$outfile = shift @ARGV;
}
}
#
# Generate all images
#
$src = "Source";
$ldir = "Large";
$sdir = "Small";
$mdir = "Movies";
$columns = 2;
$swidth = 320;
$sheight = 233;
$lwidth = 1024;
$lheight = 768;
#
# Make sure directories exist
#
if ( ! -e $ldir ) {
mkdir $ldir, 0755;
}
if ( ! -e $sdir ) {
mkdir $sdir, 0755;
}
# return 1 if file1 is newer than rile2
sub is_newer {
my($file1, $file2) = @_;
# print " - $file1 - $file2 - \n";
($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime1,
$ctime, $blksize, $blocks) = stat($file1);
($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime2,
$ctime, $blksize, $blocks) = stat($file2);
if ( $mtime1 > $mtime2 ) {
return 1;
} else {
return 0;
}
}
#
# Make images (both large and small)
#
@FILES = `ls $src/*.jpg $src/*.JPG $src/*.png`;
foreach $file ( @FILES ) {
chop $file;
$file =~ s/$src\///;
if ( is_newer( "$src/$file", "$ldir/$file" ) || ! -e "$ldir/$file" ) {
print "Updating $ldir/$file\n";
system("cp -f $src/$file $ldir");
system("mogrify -geometry \'$lwidth" . "X" .
"$lheight>\' -interlace LINE -quality 80 $ldir/$file");
}
if ( is_newer( "$ldir/$file", "$sdir/$file" ) || ! -e "$sdir/$file" ) {
print "Updating $sdir/$file\n";
system("cp -f $ldir/$file $sdir");
system("mogrify -geometry \'$swidth" . "X" .
"$sheight>\' -interlace LINE -quality 80 $sdir/$file");
}
}
#
# Check for large and small images to remove
#
@FILES = `ls $ldir`;
foreach $file ( @FILES ) {
chop($file);
# print "$file\n";
if ( ! -f "$src/$file" ) {
print "No matching src file - deleting large image $file ...\n";
unlink( "$ldir/$file" );
}
}
@FILES = `ls $sdir`;
foreach $file ( @FILES ) {
chop($file);
if ( ! -f "$src/$file" ) {
print "No matching src file - deleting small image $file ...\n";
unlink( "$sdir/$file" );
}
}
#
# Build image list (for next/previous/first/last links)
#
open( MASTER, "<master.idx" );
@imagelist = ();
while ( <MASTER> ) {
chop;
if ( m/\.jpg$/ || m/\.JPG$/ || m/\.png$/ ) {
push @imagelist, $_;
} else {
# just ignore everything else
}
}
close( MASTER );
#
# Prepair $link subdirectory
#
$link = "Link";
system("rm -rf $link");
mkdir $link, 0755;
#
# Assemble index.html
#
$dir = `pwd`;
chop($dir);
$title = `basename $dir`;
chop($title);
open( MASTER, "<master.idx" );
open( OUT, ">$outfile" );
$j = 1;
$in_table = 0;
while ( <MASTER> ) {
chop;
if ( m/^#/ ) {
# ignore comments
} elsif ( m/\.txt$/ ) {
# insert text
if ( $in_table ) {
$in_table = 0;
print OUT "</TR>\n";
print OUT "</TABLE>\n";
}
$file = $_;
open( IN, "<$src/$file" );
while ( <IN> ) {
print OUT $_;
}
close( IN );
print OUT "<P>\n";
$j = 1;
$in_table = 0;
} elsif ( m/\.jpg$/ || m/\.JPG$/ || m/\.png$/ ) {
# insert image in 3 wide tables
$in_table = 1;
$file = $_;
$i = `basename $file`;
chop($i);
if ( $j == 1 ) {
print OUT "<!-- Begin Row -->\n";
print OUT "<TABLE ALIGN=CENTER>\n";
print OUT "<TR VALIGN=TOP>\n";
}
if ( $i =~ m/\.jpg$/ ) {
$linkname = `basename $i .jpg`;
} elsif ( $i =~ m/\.JPG$/ ) {
$linkname = `basename $i .JPG`;
} elsif ($i =~ m/\.png$/ ) {
$linkname = `basename $i .png`;
}
chop($linkname);
$thumbinfo = `identify $sdir/$i`;
($name, $type, $geom, $junk) = split(/\s+/, $thumbinfo, 4);
($twidth, $theight) = split(/x/, $geom);
$theight =~ s/\+.*$//;
# print OUT "<TD WIDTH=220 HEIGHT=160>\n";
print OUT "<TD WIDTH=$twidth HEIGHT=$sheight>\n";
print OUT "<A HREF=\"$link/$linkname.html\">";
print OUT "<IMG WIDTH=$twidth HEIGHT=$theight SRC=\"$sdir/$i\" ALT=\"$linkname\">";
print OUT "</A><BR>\n";
if ( -f "$src/$linkname.txt" ) {
print OUT "<FONT SIZE=-1 id=\"fgfs\">\n";
open( IN, "<$src/$linkname.txt" );
while ( <IN> ) {
print OUT $_;
}
close( IN );
print OUT "</FONT>\n";
} else {
print OUT "<FONT SIZE=-1 id=\"fgfs\">\n";
print OUT "$linkname\n";
print OUT "</FONT>\n";
}
print OUT "</TD>\n";
if ( $j == $columns ) {
$in_table = 0;
print OUT "</TR>\n";
print OUT "</TABLE>\n";
}
if ( ++$j > $columns ) {
$j = 1;
}
} elsif ( m/\.AVI$/ || m/\.mpg$/ || m/\.mov$/ ) {
# insert image in 3 wide tables
$in_table = 1;
$file = $_;
$i = `basename $file`;
chop($i);
if ( $j == 1 ) {
print OUT "<!-- Begin Row -->\n";
print OUT "<TABLE ALIGN=CENTER>\n";
print OUT "<TR VALIGN=TOP>\n";
}
if ( $i =~ m/\.AVI$/ ) {
$linkname = `basename $i .AVI`;
} elsif ( $i =~ m/\.mpg$/ ) {
$linkname = `basename $i .mpg`;
} elsif ( $i =~ m/\.mov$/ ) {
$linkname = `basename $i .mov`;
} else {
die "unknown movie type\n";
}
chop($linkname);
# print OUT "<TD WIDTH=220 HEIGHT=160>\n";
print OUT "<TD WIDTH=$swidth HEIGHT=$sheight>\n";
$thumbinfo = `identify $mdir/$linkname.jpg`;
($name, $type, $geom, $junk) = split(/\s+/, $thumbinfo, 4);
$geom =~ s/\+.*//;
($twidth, $theight) = split(/x/, $geom);
print "movie thumb geom = $geom $twidth $theight\n";
print OUT "<A HREF=\"$mdir/$i\">";
if ( -f "$mdir/$linkname.jpg" ) {
print OUT "<IMG WIDTH=$twidth HEIGHT=$theight SRC=\"$mdir/$linkname.jpg\" ALT=\"$linkname\">";
} else {
print OUT "$linkname";
}
print OUT "</A>\n";
if ( -f "$mdir/$linkname.txt" ) {
print OUT "<BR>\n";
print OUT "<FONT SIZE=-1 id=\"fgfs\">\n";
open( IN, "<$mdir/$linkname.txt" );
while ( <IN> ) {
print OUT $_;
}
close( IN );
print OUT "</FONT>\n";
} else {
print OUT "<BR>\n";
print OUT "<FONT SIZE=-1 id=\"fgfs\">\n";
print OUT "$linkname\n";
print OUT "</FONT>\n";
}
print OUT "</TD>\n";
if ( $j == $columns ) {
$in_table = 0;
print OUT "</TR>\n";
print OUT "</TABLE>\n";
}
if ( ++$j > $columns ) {
$j = 1;
}
} else {
# just pass along the rest as is
$j = 1;
if ( $in_table ) {
$in_table = 0;
print OUT "</TR>\n";
print OUT "</TABLE>\n";
}
print OUT "$_\n";
}
}
#
# Generate Links
#
# @FILES = `ls $src/*.jpg $src/*.JPG $src/*.png`;
$first = $imagelist[0];
if ( $first =~ m/\.jpg$/ ) {
# print " ext = jpg\n";
$firstname = `basename $first .jpg`;
} elsif ( $first =~ m/\.JPG$/ ) {
# print " ext = JPG\n";
$firstname = `basename $first .JPG`;
} else {
# print " ext = png\n";
$firstname = `basename $first .png`;
}
chop($firstname);
$last = $imagelist[$#imagelist];
if ( $last =~ m/\.jpg$/ ) {
# print " ext = jpg\n";
$lastname = `basename $last .jpg`;
} elsif ( $last =~ m/\.JPG$/ ) {
# print " ext = JPG\n";
$lastname = `basename $last .JPG`;
} else {
# print " ext = png\n";
$lastname = `basename $last .png`;
}
chop($lastname);
for ($i = 0; $i <= $#imagelist; $i++) {
$file = $imagelist[$i];
# print "'$file'\n";
if ( $i > 0 ) {
$prev = $imagelist[$i - 1];
} else {
$prev = "null";
}
if ( $i < $#imagelist ) {
$next = $imagelist[$i + 1];
} else {
$next = "null";
}
if ( $file =~ m/\.jpg$/ ) {
$linkname = `basename $file .jpg`;
$ext = "jpg";
} elsif ( $file =~ m/\.JPG$/ ) {
$linkname = `basename $file .JPG`;
$ext = "JPG";
} else {
$linkname = `basename $file .png`;
$ext = "png";
}
chop($linkname);
$nice_name = $linkname;
$nice_name =~ s/\_/ /g;
if ( $prev =~ m/\.jpg$/ ) {
# print " ext = jpg\n";
$prevname = `basename $prev .jpg`;
} elsif ( $prev =~ m/\.JPG$/ ) {
# print " ext = JPG\n";
$prevname = `basename $prev .JPG`;
} else {
# print " ext = png\n";
$prevname = `basename $prev .png`;
}
chop($prevname);
if ( $next =~ m/\.jpg$/ ) {
$nextname = `basename $next .jpg`;
} elsif ( $next =~ m/\.JPG$/ ) {
$nextname = `basename $next .JPG`;
} else {
$nextname = `basename $next .png`;
}
chop($nextname);
$outfile = "$link/$linkname.html";
open( OUT, ">$outfile" );
print OUT <<EOF;
<HTML>
<HEAD>
<TITLE>$linkname.$ext</TITLE>
</HEAD>
<!-- <BODY BGCOLOR="#000000" ALINK="#000000" VLINK="#000000" LINK="#000000" TEXT="#FFFFFF"> -->
<BODY>
<A HREF="$firstname.html">[First]</A>
EOF
if ( $prevname ne "null" ) {
print OUT "<A HREF=\"$prevname.html\">[Previous]</A>\n";
} else {
print OUT "[Previous]\n";
}
if ( $nextname ne "null" ) {
print OUT "<A HREF=\"$nextname.html\">[Next]</A>\n";
} else {
print OUT "[Next]\n";
}
print OUT <<EOF;
<A HREF="$lastname.html">[Last]</A>
<TABLE ALIGN=CENTER>
<TR>
<TD ALIGN=CENTER>
<FONT SIZE=+1>
$nice_name
</FONT>
</TD>
</TR>
<TR>
<TD ALIGN=CENTER>
<FONT SIZE=-1>
EOF
if ( -f "$src/$linkname.txt" ) {
# print OUT "<BR>\n";
open( IN, "<$src/$linkname.txt" );
while ( <IN> ) {
print OUT $_;
}
close( IN );
}
print OUT <<EOF;
</FONT>
</TD>
</TR>
</TABLE>
Click on the image for the full size version.
<TABLE ALIGN=CENTER>
<TR>
<TD ALIGN=CENTER>
<A HREF="../$src/$linkname.$ext">
EOF
if ( $use_large ) {
print OUT " <IMG SRC=\"../$ldir/$linkname.$ext\">\n";
} else {
print OUT " <IMG SRC=\"../$src/$linkname.$ext\">\n";
}
print OUT <<EOF;
</TD>
</A>
</TR>
</TABLE>
</BODY>
</HTML>
EOF
close( OUT );
}

195
scripts/python/FGFSDemo.py Executable file
View File

@@ -0,0 +1,195 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
from tkinter import tix as Tix
from FlightGear import FlightGear
import os
import socket
import sys
PROGNAME = os.path.basename(sys.argv[0])
class PropertyField:
def __init__(self, parent, prop, label):
self.prop = prop
self.field = Tix.LabelEntry( parent, label=label,
options='''
label.width 30
label.anchor e
entry.width 30
''' )
self.field.pack( side=Tix.TOP, padx=20, pady=2 )
def update_field(self,fgfs):
val = fgfs[self.prop]
self.field.entry.delete(0,'end')
self.field.entry.insert(0, val)
class PropertyPage(Tix.Frame):
def __init__(self,parent,fgfs):
Tix.Frame.__init__(self,parent)
self.fgfs = fgfs
self.pack( side=Tix.TOP, padx=2, pady=2, fill=Tix.BOTH, expand=1 )
self.fields = []
def addField(self, prop, label):
f = PropertyField(self, prop, label)
self.fields.append(f)
def update_fields(self):
for f in self.fields:
f.update_field(self.fgfs)
Tix.Frame.update(self)
class FGFSDemo(Tix.Frame):
def __init__(self,fgfs,root=None):
Tix.Frame.__init__(self,root)
z = root.winfo_toplevel()
z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.quitcmd())
self.fgfs = fgfs
self.pack()
self.pages = {}
self.after_id = None
self.createWidgets()
self.update()
def createWidgets(self):
self.nb = Tix.NoteBook(self)
self.nb.add( 'sim', label='Simulation',
raisecmd= lambda self=self: self.update_page() )
self.nb.add( 'view', label='View',
raisecmd= lambda self=self: self.update_page() )
self.nb.add( 'loc', label='Location',
raisecmd= lambda self=self: self.update_page() )
self.nb.add( 'weather', label='Weather',
raisecmd= lambda self=self: self.update_page() )
self.nb.add( 'clouds', label='Clouds',
raisecmd= lambda self=self: self.update_page() )
self.nb.add( 'velocities', label='Velocities',
raisecmd= lambda self=self: self.update_page() )
page = PropertyPage( self.nb.sim, self.fgfs )
self.pages['sim'] = page
page.addField( '/sim/aircraft', 'Aircraft:')
page.addField( '/sim/presets/airport-id', 'Airport ID:')
page.addField( '/sim/time/gmt', 'Current time (GMT):')
page.addField( '/sim/presets/trim', 'Trim on ground (true/false):')
page.addField( '/sim/sound/enabled', 'Sound enabled (true/false):')
page.addField( '/sim/startup/browser-app', 'Web browser:')
page = PropertyPage( self.nb.view, self.fgfs )
self.pages['view'] = page
page.addField( '/sim/view-mode', 'View mode:')
page.addField( "/sim/current-view/field-of-view", "Field of view (deg):" )
page.addField( "/sim/current-view/pitch-offset-deg", "View pitch offset (deg):" )
page.addField( "/sim/current-view/heading-offset-deg", "View heading offset (deg):" )
page = PropertyPage( self.nb.loc, self.fgfs )
self.pages['loc'] = page
page.addField( "/position/altitude-ft", "Altitude (ft):" )
page.addField( "/position/longitude-deg", "Longitude (deg):" )
page.addField( "/position/latitude-deg", "Latitude (deg):" )
page.addField( "/orientation/roll-deg", "Roll (deg):" )
page.addField( "/orientation/pitch-deg", "Pitch (deg):" )
page.addField( "/orientation/heading-deg", "Heading (deg):" )
page = PropertyPage( self.nb.weather, self.fgfs )
self.pages['weather'] = page
page.addField("/environment/wind-from-heading-deg",
"Wind direction (deg FROM):")
page.addField("/environment/metar/base-wind-speed-kt",
"Wind speed (kt):")
page.addField("/environment/metar/gust-wind-speed-kt",
"Maximum gust (kt):")
page.addField("/environment/wind-from-down-fps",
"Updraft (fps):")
page.addField("/environment/temperature-degc", "Temperature (degC):")
page.addField("/environment/dewpoint-degc", "Dewpoint (degC):")
page.addField("/environment/pressure-sea-level-inhg",
"Altimeter setting (inHG):")
page = PropertyPage( self.nb.clouds, self.fgfs )
self.pages['clouds'] = page
page.addField("/environment/clouds/layer[0]/layer-type",
"Layer 0 type:")
page.addField("/environment/clouds/layer[0]/elevation-ft",
"Layer 0 height (ft):")
page.addField("/environment/clouds/layer[0]/thickness-ft",
"Layer 0 thickness (ft):")
page.addField("/environment/clouds/layer[1]/layer-type",
"Layer 1 type:")
page.addField("/environment/clouds/layer[1]/elevation-ft",
"Layer 1 height (ft):")
page.addField("/environment/clouds/layer[1]/thickness-ft",
"Layer 1 thickness (ft):")
page.addField("/environment/clouds/layer[2]/layer-type",
"Layer 2 type:")
page.addField("/environment/clouds/layer[2]/elevation-ft",
"Layer 2 height (ft):")
page.addField("/environment/clouds/layer[2]/thickness-ft",
"Layer 2 thickness (ft):")
page.addField("/environment/clouds/layer[3]/layer-type",
"Layer 3 type:")
page.addField("/environment/clouds/layer[3]/elevation-ft",
"Layer 3 height (ft):")
page.addField("/environment/clouds/layer[3]/thickness-ft",
"Layer 3 thickness (ft):")
page.addField("/environment/clouds/layer[4]/layer-type",
"Layer 4 type:")
page.addField("/environment/clouds/layer[4]/elevation-ft",
"Layer 4 height (ft):")
page.addField("/environment/clouds/layer[4]/thickness-ft",
"Layer 4 thickness (ft):")
page = PropertyPage( self.nb.velocities, self.fgfs )
self.pages['velocities'] = page
page.addField("/velocities/airspeed-kt", "Airspeed (kt):")
page.addField("/velocities/speed-down-fps", "Descent speed (fps):")
self.nb.pack( expand=1, fill=Tix.BOTH, padx=5, pady=5, side=Tix.TOP )
self.QUIT = Tix.Button(self)
self.QUIT['text'] = 'Quit'
self.QUIT['command'] = self.quitcmd
self.QUIT.pack({"side": "bottom"})
def quitcmd(self):
if self.after_id:
self.after_cancel(self.after_id)
#self.quit()
self.destroy()
def update_page(self):
page = self.pages[ self.nb.raised() ]
page.update_fields()
self.update()
self.after_id = self.after( 1000, lambda self=self: self.update_page() )
def main():
if len(sys.argv) != 3:
print('Usage: {} host port'.format(PROGNAME))
sys.exit(1)
host = sys.argv[1]
try:
port = int( sys.argv[2] )
except ValueError:
print('Error: expected a number for the port argument, not {!r}'
.format(sys.argv[2]))
sys.exit(1)
fgfs = None
try:
fgfs = FlightGear( host, port )
except socket.error as msg:
print('Error connecting to flightgear:', msg.strerror)
sys.exit(1)
root = Tix.Tk()
app = FGFSDemo( fgfs, root )
app.mainloop()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,211 @@
from telnetlib import Telnet
import sys
import socket
import re
import time
__all__ = ["FlightGear"]
CRLF = '\r\n'
class FGTelnet(Telnet):
def __init__(self,host,port):
Telnet.__init__(self,host,port)
self.prompt = [re.compile('/[^>]*> '.encode('utf-8'))]
self.timeout = 5
#Telnet.set_debuglevel(self,2)
def help(self):
return
def ls(self,dir=None):
"""
Returns a list of properties.
"""
if dir is None:
self._putcmd('ls')
else:
self._putcmd('ls %s' % dir )
return self._getresp()
def ls2(self, dir_):
self._putcmd(f'ls2 {dir_}')
return self._getresp()
def dump(self):
"""Dump current state as XML."""
self._putcmd('dump')
return self._getresp()
def cd(self, dir):
"""Change directory."""
self._putcmd('cd ' + dir)
self._getresp()
return
def pwd(self):
"""Display current path."""
self._putcmd('pwd')
return self._getresp()
def get(self,var):
"""Retrieve the value of a parameter."""
self._putcmd('get %s' % var )
return self._getresp()
def set(self,var,value):
"""Set variable to a new value"""
self._putcmd('set %s %s' % (var,value))
self._getresp() # Discard response
def quit(self):
"""Terminate connection"""
self._putcmd('quit')
self.close()
return
# Internal: send one command to FlightGear
def _putcmd(self,cmd):
cmd = cmd + CRLF
Telnet.write(self, cmd.encode('utf-8'))
return
def _getresp(self):
# Telnet.expect() can return short result, so we call it in a loop.
response = b''
while 1:
_i, _match, data = Telnet.expect(self, self.prompt, self.timeout)
response += data
if _i == 0:
break # We have the prompt that marks the end of the data.
assert _i == -1, f'i={i}'
# Remove the terminating prompt.
# Everything preceding it is the response.
return response.decode('utf-8').split('\n')[:-1]
class LsItem:
def __init__(self, num_children, name, index, type_, value_text):
self.num_children = num_children
self.name = name
self.index = index
self.type_ = type_
self.value_text = value_text
# Convert to correct type; type_ is originally from
# flightgear/src/Network/props.cxx:getValueTypeString().
#
if type_ in ('unknown', 'unspecified', 'none'):
value = value_text
elif type_ == 'bool':
value = (value_text == 'true')
elif type_ in ('int', 'long'):
value = int(value_text)
elif type_ in ('float', 'double'):
self.value = float(value_text)
elif type_ == 'string':
self.value = value_text
else:
assert 0, f'Unrecognised type: {type_}'
def __str__(self):
return f'num_children={self.num_children} name={self.name}[{self.index}] type={self.type_}: {self.value!r}'
class FlightGear:
"""FlightGear interface class.
An instance of this class represents a connection to a FlightGear telnet
server.
Properties are accessed using a dictionary style interface:
For example:
# Connect to flightgear telnet server.
fg = FlightGear('myhost', 5500)
# parking brake on
fg['/controls/gear/brake-parking'] = 1
# Get current heading
heading = fg['/orientation/heading-deg']
Other non-property related methods
"""
def __init__( self, host = 'localhost', port = 5500 ):
try:
self.telnet = FGTelnet(host,port)
except socket.error as msg:
self.telnet = None
raise msg
def __del__(self):
# Ensure telnet connection is closed cleanly.
self.quit()
def __getitem__(self,key):
"""Get a FlightGear property value.
Where possible the value is converted to the equivalent Python type.
"""
s = self.telnet.get(key)[0]
match = re.compile( r'[^=]*=\s*\'([^\']*)\'\s*([^\r]*)\r').match( s )
if not match:
return None
value,type = match.groups()
#value = match.group(1)
#type = match.group(2)
if value == '':
return None
if type == '(double)':
return float(value)
elif type == '(int)':
return int(value)
elif type == '(bool)':
if value == 'true':
return 1
else:
return 0
else:
return value
def __setitem__(self, key, value):
"""Set a FlightGear property value."""
if value is True:
# Flightgear props doesn't treat string 'True' as true - see
# SGPropertyNode::setStringValue().
value = 'true'
self.telnet.set( key, value )
def ls(self, dir_):
'''
Returns list of LsItem's.
'''
lines = self.telnet.ls2(dir_)
ret = []
for line in lines:
if line.endswith('\r'):
line = line[:-1]
#print(f'line={line!r}')
try:
num_children, name, index, type_, value = line.split(' ', 4)
except Exception as e:
print(f'*** dir_={dir_!r} len(lines)={len(lines)}. failed to read items from line={line!r}. lines is: {lines!r}')
raise
index = int(index)
num_children = int(num_children)
item = LsItem(num_children, name, index, type_, value)
#print(f'item={item}')
ret.append( item)
return ret
def quit(self):
"""Close the telnet connection to FlightGear."""
if self.telnet:
self.telnet.quit()
self.telnet = None
def view_next(self):
"""Move to next view."""
self.telnet.set( "/command/view/next", "true")
def view_prev(self):
"""Move to next view."""
self.telnet.set( "/command/view/prev", "true")

View File

@@ -0,0 +1,26 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# terrasync.py --- Synchronize TerraScenery data to your local disk
# Copyright (C) 2018 Florent Rougon
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# terrasync.py development was started by Torsten Dreyer in 2016. This file is
# just the normal entry point for users.
import terrasync.main
terrasync.main.main()

View File

@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
# dirindex.py --- Class used to parse .dirindex files
#
# Copyright (C) 2016 Torsten Dreyer
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Parser for .dirindex files."""
from .exceptions import InvalidDirIndexFile
from .virtual_path import VirtualPath
class DirIndex:
"""Parser for .dirindex files."""
def __init__(self, dirIndexFile):
self.directories = []
self.files = []
self.tarballs = []
self.version = 0
self.path = None # will be a VirtualPath instance when set
# readFrom() stores the raw contents of the .dirindex file in this
# attribute. This is useful for troubleshooting.
self._rawContents = None
with open(dirIndexFile, "r", encoding="ascii") as f:
self.readFrom(f)
self._sanityCheck()
@classmethod
def checkForBackslashOrLeadingSlash(cls, line, path):
if '\\' in path or path.startswith('/'):
raise InvalidDirIndexFile(
r"invalid '\' or leading '/' in path field from line {!r}"
.format(line))
@classmethod
def checkForSlashBackslashOrDoubleColon(cls, line, name):
if '/' in name or '\\' in name:
raise InvalidDirIndexFile(
r"invalid '\' or '/' in name field from line {!r}"
.format(line))
if name == "..":
raise InvalidDirIndexFile(
r"invalid name field equal to '..' in line {!r}".format(line))
def readFrom(self, readable):
self._rawContents = readable.read()
for line in self._rawContents.split('\n'):
line = line.strip()
if line.startswith('#'):
continue
tokens = line.split(':')
if len(tokens) == 0:
continue
elif tokens[0] == "version":
self.version = int(tokens[1])
elif tokens[0] == "path":
self.checkForBackslashOrLeadingSlash(line, tokens[1])
# This is relative to the repository root
self.path = VirtualPath(tokens[1])
if ".." in self.path.parts:
raise InvalidDirIndexFile(
"'..' component found in 'path' entry {!r}"
.format(self.path))
elif tokens[0] == "d":
self.checkForSlashBackslashOrDoubleColon(line, tokens[1])
self.directories.append({'name': tokens[1], 'hash': tokens[2]})
elif tokens[0] == "f":
self.checkForSlashBackslashOrDoubleColon(line, tokens[1])
self.files.append({'name': tokens[1],
'hash': tokens[2], 'size': int(tokens[3])})
elif tokens[0] == "t":
self.checkForSlashBackslashOrDoubleColon(line, tokens[1])
self.tarballs.append({'name': tokens[1], 'hash': tokens[2],
'size': int(tokens[3])})
def _sanityCheck(self):
if self.path is None:
assert self._rawContents is not None
firstLines = self._rawContents.split('\n')[:5]
raise InvalidDirIndexFile(
"no 'path' field found; the first lines of this .dirindex file "
"follow:\n\n" + '\n'.join(firstLines))

View File

@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# exceptions.py --- Custom exception classes for terrasync.py
#
# Copyright (C) 2018 Florent Rougon
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Generic exception class for terrasync.py, to be subclassed for each specific
# kind of exception.
class TerraSyncPyException(Exception):
def __init__(self, message=None, *, mayCapitalizeMsg=True):
"""Initialize a TerraSyncPyException instance.
Except in cases where 'message' starts with a proper noun or
something like that, its first character should be given in
lower case. Automated treatments of this exception may print the
message with its first character changed to upper case, unless
'mayCapitalizeMsg' is False. In other words, if the case of the
first character of 'message' must not be changed under any
circumstances, set 'mayCapitalizeMsg' to False.
"""
self.message = message
self.mayCapitalizeMsg = mayCapitalizeMsg
def __str__(self):
return self.completeMessage()
def __repr__(self):
return "{}.{}({!r})".format(__name__, type(self).__name__, self.message)
# Typically overridden by subclasses with a custom constructor
def detail(self):
return self.message
def completeMessage(self):
if self.message:
return "{shortDesc}: {detail}".format(
shortDesc=self.ExceptionShortDescription,
detail=self.detail())
else:
return self.ExceptionShortDescription
ExceptionShortDescription = "terrasync.py generic exception"
class UserError(TerraSyncPyException):
"""Exception raised when the program is used in an incorrect way."""
ExceptionShortDescription = "User error"
class NetworkError(TerraSyncPyException):
"""Exception raised when getting a network error even after retrying."""
ExceptionShortDescription = "Network error"
class UnsupportedURLScheme(TerraSyncPyException):
"""Exception raised when asked to handle an unsupported URL scheme."""
ExceptionShortDescription = "Unsupported URL scheme"
class RepoDataError(TerraSyncPyException):
"""
Exception raised when getting invalid data from the TerraSync repository."""
ExceptionShortDescription = "Invalid data from the TerraSync repository"
class InvalidDirIndexFile(RepoDataError):
"""Exception raised when getting invalid data from a .dirindex file."""
ExceptionShortDescription = "Invalid .dirindex file"

View File

@@ -0,0 +1,743 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# main.py --- Main module for terrasync.py
#
# Copyright (C) 2016 Torsten Dreyer
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import argparse
import enum
import hashlib
import os
import re
import shutil
import ssl
import sys
import time
import urllib
from urllib.parse import urlparse, urljoin
from http.client import HTTPConnection, HTTPSConnection, HTTPException
from os import listdir
from os.path import isfile, isdir, join
from base64 import b64encode
from . import dirindex
from .exceptions import UserError, NetworkError, RepoDataError, \
InvalidDirIndexFile, UnsupportedURLScheme
from .virtual_path import VirtualPath
PROGNAME = os.path.basename(sys.argv[0])
class ExitStatus(enum.Enum):
SUCCESS = 0
# The program exit status is 1 when an exception isn't caught.
ERROR = 1
CHECK_MODE_FOUND_MISMATCH = 2
# *****************************************************************************
# * Utility functions *
# *****************************************************************************
# If a path matches this regexp, we really don't want to delete it recursively
# (“cre” stands for “compiled regexp”).
_removeDirectoryTree_dangerous_cre = re.compile(
r"""^(/ (home (/ [^/]*)? )? /* | # for Unix-like systems
[a-zA-Z]: [\/]* # for Windows
)$""", re.VERBOSE)
def removeDirectoryTree(base, whatToRemove):
"""Recursively remove directory 'whatToRemove', with safety checks.
This function ensures that 'whatToRemove' does not resolve to a
directory such as /, /home, /home/foobar, C:\, d:\, etc. It is also
an error if 'whatToRemove' does not literally start with the value
of 'base' (IOW, this function refuses to erase anything that is not
under 'base').
'whatToRemove' is *not* interpreted relatively to 'base' (this would
be doable, just a different API).
"""
assert os.path.isdir(base), "Not a directory: {!r}".format(base)
assert (base and
whatToRemove.startswith(base) and
whatToRemove[len(base):].startswith(os.sep)), \
"Unexpected base path for removeDirectoryTree(): {!r}".format(base)
absPath = os.path.abspath(whatToRemove)
if not os.path.isfile(join(absPath, ".dirindex")):
raise UserError("refusing to recursively delete '{}' because "
"it does not contain a .dirindex file".format(absPath))
elif _removeDirectoryTree_dangerous_cre.match(absPath):
raise UserError("in order to protect your data, refusing to "
"recursively delete '{}'".format(absPath))
else:
shutil.rmtree(absPath)
def computeHash(fileLike):
hash = hashlib.sha1()
for chunk in iter(lambda: fileLike.read(4096), b""):
hash.update(chunk)
return hash.hexdigest()
def hashForFile(fname):
with open(fname, "rb") as f:
return computeHash(f)
# *****************************************************************************
# * Network-related classes *
# *****************************************************************************
class HTTPGetCallback:
def __init__(self, src, callback):
"""Initialize an HTTPGetCallback instance.
src -- a VirtualPath instance (corresponding to the path on
the server for which a GET request is to be issued)
callback -- a function taking two parameters: the URL (string)
and an http.client.HTTPResponse instance. When
invoked, the callback return value will be returned
by HTTPGetter.get().
"""
if callback is not None:
self.callback = callback
self.src = src
class HTTPGetter:
def __init__(self, baseUrl, maxPending=10, auth=""):
self.baseUrl = baseUrl
self.parsedBaseUrl = urlparse(baseUrl)
self.maxPending = maxPending
self.requests = []
self.pendingRequests = []
if self.parsedBaseUrl.scheme == "http":
self.httpConnection = HTTPConnection(self.parsedBaseUrl.netloc)
elif self.parsedBaseUrl.scheme == "https":
context = ssl.create_default_context()
self.httpConnection = HTTPSConnection(self.parsedBaseUrl.netloc,
context=context)
else:
raise UnsupportedURLScheme(self.parsedBaseUrl.scheme)
self.httpRequestHeaders = headers = {'Host':self.parsedBaseUrl.netloc,'Content-Length':0,'Connection':'Keep-Alive','User-Agent':'FlightGear terrasync.py'}
if( auth and not auth.isspace() ):
self.httpRequestHeaders['Authorization'] = 'Basic %s' % b64encode(auth.encode("utf-8")).decode("ascii")
def assemblePath(self, httpGetCallback):
"""Return the path-on-server for the file to download.
Example: '/scenery/Airports/N/E/4/.dirindex'
"""
assert not self.parsedBaseUrl.path.endswith('/'), \
repr(self.parsedBaseUrl)
return self.parsedBaseUrl.path + str(httpGetCallback.src)
def assembleUrl(self, httpGetCallback):
"""Return the URL of the file to download."""
baseUrl = self.parsedBaseUrl.geturl()
assert not baseUrl.endswith('/'), repr(baseUrl)
return urljoin(baseUrl + '/', httpGetCallback.src.asRelative())
def doGet(self, httpGetCallback):
time.sleep(1.25) # throttle the rate
pathOnServer = self.assemblePath(httpGetCallback)
self.httpConnection.request("GET", pathOnServer, None,
self.httpRequestHeaders)
httpResponse = self.httpConnection.getresponse()
# 'httpResponse' is an http.client.HTTPResponse instance
return httpGetCallback.callback(self.assembleUrl(httpGetCallback),
httpResponse)
def get(self, httpGetCallback):
nbRetries = nbRetriesLeft = 5
while True:
try:
return self.doGet(httpGetCallback)
except HTTPException as exc:
if nbRetriesLeft == 0:
raise NetworkError(
"after {nbRetries} retries for URL {url}: {errMsg}"
.format(nbRetries=nbRetries,
url=self.assembleUrl(httpGetCallback),
errMsg=exc)) from exc
# Try to reconnect
self.httpConnection.close()
time.sleep(1)
self.httpConnection.connect()
nbRetriesLeft -= 1
class HTTPDownloadRequest(HTTPGetCallback):
def __init__(self, src, dst, callback=None):
"""Initialize an HTTPDownloadRequest instance.
src -- a VirtualPath instance (corresponding to the path
on the server for which a GET request is to be
issued)
dst -- file path (or whatever open() accepts) where the
downloaded data is to be stored
callback -- a function that will be called if the download is
successful, or None if no such callback is desired.
The function must take one parameter: when invoked,
it will be passed this HTTPDownloadRequest
instance. Its return value is ignored.
"""
HTTPGetCallback.__init__(self, src, None)
self.dst = dst
self.mycallback = callback
# 'httpResponse' is an http.client.HTTPResponse instance
def callback(self, url, httpResponse):
# I suspect this doesn't handle HTTP redirects and things like that. As
# mentioned at <https://docs.python.org/3/library/http.client.html>,
# http.client is a low-level interface that should normally not be used
# directly!
if httpResponse.status != 200:
raise NetworkError("HTTP callback got status {status} for URL {url}"
.format(status=httpResponse.status, url=url))
try:
with open(self.dst, 'wb') as f:
f.write(httpResponse.read())
except HTTPException as exc:
raise NetworkError("for URL {url}: {error}"
.format(url=url, error=exc)) from exc
if self.mycallback is not None:
self.mycallback(self)
class HTTPSocketRequest(HTTPGetCallback):
"""HTTPGetCallback class whose callback returns a file-like object.
The file-like object returned by the callback, and thus by
HTTPGetter.get(), is a socket or similar. This allows one to read
the data obtained from the network without necessarily storing it
to a file.
"""
def __init__(self, src):
"""Initialize an HTTPSocketRequest object.
src -- VirtualPath instance for the resource on the server
(presumably a file)
"""
HTTPGetCallback.__init__(self, src, None)
def callback(self, url, httpResponse):
# Same comment as for HTTPDownloadRequest.callback()
if httpResponse.status != 200:
raise NetworkError("HTTP callback got status {status} for URL {url}"
.format(status=httpResponse.status, url=url))
return httpResponse
#################################################################################################################################
class Coordinate:
def __init__(self, lat, lon):
self.lat = lat
self.lon = lon
class DownloadBoundaries:
def __init__(self, top, left, bottom, right):
if top < bottom:
raise ValueError("top cannot be less than bottom")
if right < left:
# right may be less than left when wrapping across the antimeridian
if not (left >= 0 and right < 0):
raise ValueError("right cannot be less than left")
if top > 90 or bottom < -90:
raise ValueError("top and bottom must be a valid latitude")
if left < -180 or right >= 180:
raise ValueError("left and right must be a valid longitude")
self.top = top
self.left = left
self.bottom = bottom
self.right = right
def is_coordinate_inside_boundaries(self, coordinate, isOuterBucket):
bigTileBottom = coordinate.lat
bigTileTop = bigTileBottom + (10 if isOuterBucket else 1)
bigTileLeft = coordinate.lon
bigTileRight = bigTileLeft + (10 if isOuterBucket else 1)
# if the two regions do not overlap then we are done
if bigTileTop <= self.bottom or bigTileBottom > self.top:
return False
if bigTileRight <= self.left or bigTileLeft > self.right:
# check for spanning across the antimeridian
if self.left >= 0 and self.right < 0:
# determine which side we are on and check of region overlap
if bigTileLeft >= 0:
if bigTileRight <= self.left:
return False
elif bigTileLeft > self.right:
return False
else:
return False
# at least a partial overlap exists, so more processing will be needed
return True
def parse_terrasync_coordinate(coordinate):
matches = re.match("(w|e)(\d{3})(n|s)(\d{2})", coordinate)
if not matches:
return None
lon = int(matches.group(2))
if matches.group(1) == "w":
lon *= -1
lat = int(matches.group(4))
if matches.group(3) == "s":
lat *= -1
return Coordinate(lat, lon)
class Report:
"""Gather and format data about the state of a TerraSync mirror."""
def __init__(self, targetDir):
self.targetDir = targetDir
self.dirsWithMissingIndex = set()
self.dirsWithMismatchingDirIndexHash = set()
self.missingFiles = set()
self.filesWithMismatchingHash = set()
self.dirsSkippedDueToBoundaries = set()
self.orphanFiles = set()
self.orphanDirs = set()
def addMissingDirIndex(self, directoryVirtualPath):
self.dirsWithMissingIndex.add(directoryVirtualPath)
def addDirIndexWithMismatchingHash(self, directoryVirtualPath):
self.dirsWithMismatchingDirIndexHash.add(directoryVirtualPath)
def addMissingFile(self, virtualPath):
self.missingFiles.add(virtualPath)
def addFileWithMismatchingHash(self, virtualPath):
self.filesWithMismatchingHash.add(virtualPath)
def addSkippedDueToBoundaries(self, virtualPath):
self.dirsSkippedDueToBoundaries.add(virtualPath)
def addOrphanFile(self, virtualPath):
self.orphanFiles.add(virtualPath)
def addOrphanDir(self, virtualPath):
self.orphanDirs.add(virtualPath)
def summaryString(self):
reportElements = [
("Directories with missing index", self.dirsWithMissingIndex),
("Directories whose .dirindex file had a mismatching hash",
self.dirsWithMismatchingDirIndexHash),
("Missing files", self.missingFiles),
("Files with a mismatching hash", self.filesWithMismatchingHash),
("Directories skipped because of the specified boundaries",
self.dirsSkippedDueToBoundaries),
("Orphan files", self.orphanFiles),
("Orphan directories", self.orphanDirs)]
l = []
for heading, setOfFilesOrDirs in reportElements:
if setOfFilesOrDirs:
l.append(heading + ":\n")
l.extend( (" " + str(f) for f in sorted(setOfFilesOrDirs)) )
l.append('') # ensure a blank line follows the list
else:
l.append(heading + ": none")
return '\n'.join(l)
def printReport(self):
title = "{prg} report".format(prg=PROGNAME)
print("\n" + title + '\n' + len(title)*"=", end="\n\n")
print(self.summaryString())
@enum.unique
class FailedCheckReason(enum.Enum):
"""Reasons that can cause 'check' mode to report a mismatch.
Note that network errors and things like that do *not* belong here.
"""
missingDirIndexFile, mismatchingHashForDirIndexFile, \
missingNormalFile, mismatchingHashForNormalFile, \
orphanFile, orphanDirectory = range(6)
# 'path': VirtualPath instance for a file or directory
def explain(self, path):
if self is FailedCheckReason.missingDirIndexFile:
res = ".dirindex file '{}' is missing locally".format(path)
elif self is FailedCheckReason.mismatchingHashForDirIndexFile:
res = ".dirindex file '{}' doesn't have the hash it " \
"should have according to the server".format(path)
elif self is FailedCheckReason.missingNormalFile:
res = "file '{}' is present on the server but missing locally" \
.format(path)
elif self is FailedCheckReason.mismatchingHashForNormalFile:
res = "file '{}' doesn't have the hash given in the " \
".dirindex file of its containing directory".format(path)
elif self is FailedCheckReason.orphanFile:
res = "file '{}' was found locally but is not present on the " \
"server".format(path)
elif self is FailedCheckReason.orphanDirectory:
res = "directory '{}' was found locally but is not present " \
"on the server".format(path)
else:
assert False, "Unhandled enum value: {!r}".format(self)
return res
class TerraSync:
@enum.unique
class Mode(enum.Enum):
"""Main modes of operation for the TerraSync class."""
# Using lower case for the member names, because this way
# enumMember.name is exactly the mode string passed to --mode on the
# command line (can be useful for messages destined to users).
check, sync = range(2)
def __init__(self, mode, doReport, url, target, quick, removeOrphan,
downloadBoundaries, auth):
self.mode = self.Mode[mode]
self.doReport = doReport
self.setUrl(url).setTarget(target)
self.auth = auth
self.quick = quick
self.removeOrphan = removeOrphan
self.httpGetter = None
self.downloadBoundaries = downloadBoundaries
# Status of the local repository (as compared to what the server says),
# before any update we might do to it.
self.report = Report(self.target)
def inSyncMode(self):
return self.mode == self.Mode.sync
def setUrl(self, url):
self.url = url.rstrip('/').strip()
return self
def setTarget(self, target):
# Using os.path.abspath() here is safer in case the process later uses
# os.chdir(), which would change the meaning of the "." directory.
self.target = os.path.abspath(target)
return self
def start(self, virtualSubdir=VirtualPath('/')):
"""Start the 'sync' or 'check' process.
The 'virtualSubdir' argument must be a VirtualPath instance and
allows one to start the 'sync' or 'check' process in a chosen
subdirectory of the TerraSync repository, instead of at its
root.
"""
# Remove the leading '/' from 'virtualSubdir' and convert to native
# separators ('/' or '\' depending on the platform).
localSubdir = os.path.normpath(virtualSubdir.asRelative())
if localSubdir == ".": # just ugly, but it wouldn't hurt
localSubdir = ""
assert not os.path.isabs(localSubdir), repr(localSubdir)
self.httpGetter = HTTPGetter(baseUrl=self.url,auth=self.auth)
# Get the hash of the .dirindex file for 'virtualSubdir'
try:
request = HTTPSocketRequest(virtualSubdir / ".dirindex")
with self.httpGetter.get(request) as fileLike:
dirIndexHash = computeHash(fileLike)
except HTTPException as exc:
raise NetworkError("for the root .dirindex file: {errMsg}"
.format(errMsg=exc)) from exc
# Process the chosen part of the repository (recursive)
self.processDirectoryEntry(virtualSubdir, localSubdir, dirIndexHash)
return self.report
def processFileEntry(self, virtualPath, localPath, fileHash):
"""Process a file entry from a .dirindex file."""
localFullPath = join(self.target, localPath)
failedCheckReason = None
if not os.path.isfile(localFullPath):
self.report.addMissingFile(virtualPath)
failedCheckReason = FailedCheckReason.missingNormalFile
elif hashForFile(localFullPath) != fileHash:
self.report.addFileWithMismatchingHash(virtualPath)
failedCheckReason = FailedCheckReason.mismatchingHashForNormalFile
else:
# The file exists and has the hash mentioned in the .dirindex file
return
assert failedCheckReason is not None
if self.inSyncMode():
if os.path.isdir(localFullPath):
# 'localFullPath' is a directory (locally), but on the server
# it is a file -> remove the dir so that we can store the file.
removeDirectoryTree(self.target, localFullPath)
print("Downloading '{}'".format(virtualPath))
request = HTTPDownloadRequest(virtualPath, localFullPath)
self.httpGetter.get(request)
else:
self.abortCheckMode(failedCheckReason, virtualPath)
def processDirectoryEntry(self, virtualPath, localPath, dirIndexHash):
"""Process a directory entry from a .dirindex file."""
print("Processing '{}'...".format(virtualPath))
isOuterBucket = True if len(virtualPath.parts) <= 3 else False
coord = parse_terrasync_coordinate(virtualPath.name)
if (coord and
not self.downloadBoundaries.is_coordinate_inside_boundaries(coord, isOuterBucket)):
self.report.addSkippedDueToBoundaries(virtualPath)
return
localFullPath = join(self.target, localPath)
localDirIndex = join(localFullPath, ".dirindex")
failedCheckReason = None
if not os.path.isfile(localDirIndex):
failedCheckReason = FailedCheckReason.missingDirIndexFile
self.report.addMissingDirIndex(virtualPath)
elif hashForFile(localDirIndex) != dirIndexHash:
failedCheckReason = FailedCheckReason.mismatchingHashForDirIndexFile
self.report.addDirIndexWithMismatchingHash(virtualPath)
if failedCheckReason is None:
if not self.quick:
self.handleDirindexFile(localDirIndex)
elif self.inSyncMode():
if os.path.isfile(localFullPath):
os.unlink(localFullPath) # file on server became a directory
if not os.path.exists(localFullPath):
os.makedirs(localFullPath)
request = HTTPDownloadRequest(virtualPath / ".dirindex",
localDirIndex,
self.handleDirindexRequest)
self.httpGetter.get(request)
else:
self.abortCheckMode(failedCheckReason, virtualPath / ".dirindex")
def handleDirindexRequest(self, dirindexRequest):
self.handleDirindexFile(dirindexRequest.dst)
def handleDirindexFile(self, dirindexFile):
dirIndex = dirindex.DirIndex(dirindexFile)
virtualBase = dirIndex.path # VirtualPath instance
relativeBase = virtualBase.asRelative() # string, doesn't start with '/'
serverFiles = []
serverDirs = []
for file in dirIndex.files:
f = file['name']
self.processFileEntry(virtualBase / f,
join(relativeBase, f),
file['hash'])
serverFiles.append(f)
for subdir in dirIndex.directories:
d = subdir['name']
self.processDirectoryEntry(virtualBase / d,
join(relativeBase, d),
subdir['hash'])
serverDirs.append(d)
for tarball in dirIndex.tarballs:
# Tarballs are handled the same as normal files.
f = tarball['name']
self.processFileEntry(virtualBase / f,
join(relativeBase, f),
tarball['hash'])
serverFiles.append(f)
localFullPath = join(self.target, relativeBase)
localFiles = [ f for f in listdir(localFullPath)
if isfile(join(localFullPath, f)) ]
for f in localFiles:
if f != ".dirindex" and f not in serverFiles:
virtualPath = virtualBase / f
self.report.addOrphanFile(virtualPath)
if self.inSyncMode():
if self.removeOrphan:
os.remove(join(self.target, virtualPath.asRelative()))
else:
self.abortCheckMode(FailedCheckReason.orphanFile,
virtualPath)
localDirs = [ f for f in listdir(localFullPath)
if isdir(join(localFullPath, f)) ]
for d in localDirs:
if d not in serverDirs:
virtualPath = virtualBase / d
self.report.addOrphanDir(virtualPath)
if self.inSyncMode():
if self.removeOrphan:
removeDirectoryTree(self.target,
join(self.target,
virtualPath.asRelative()))
else:
self.abortCheckMode(FailedCheckReason.orphanDirectory,
virtualPath)
# 'reason' is a member of the FailedCheckReason enum
def abortCheckMode(self, reason, fileOrDirVirtualPath):
assert self.mode == self.Mode.check, repr(self.mode)
print("{prg}: exiting from 'check' mode because {explanation}."
.format(prg=PROGNAME,
explanation=reason.explain(fileOrDirVirtualPath)))
if self.doReport:
self.report.printReport()
sys.exit(ExitStatus.CHECK_MODE_FOUND_MISMATCH.value)
#################################################################################################################################
def parseCommandLine():
parser = argparse.ArgumentParser()
parser.add_argument("-u", "--url", dest="url", metavar="URL",
default="http://flightgear.sourceforge.net/scenery",
help="server URL [default: %(default)s]")
parser.add_argument("-a", "--auth", dest="auth", metavar="user:password",
default="", help="""\
authentication credentials for basic auth [default: empty, no authentication]""")
parser.add_argument("-t", "--target", dest="target", metavar="DIR",
default=".", help="""\
directory where to store the files [default: the current directory]""")
parser.add_argument("--only-subdir", dest="onlySubdir", metavar="SUBDIR",
default="", help="""\
restrict processing to this subdirectory of the TerraSync repository. Use
a path relative to the repository root, for instance 'Models/Residential'
[default: process the whole repository]""")
parser.add_argument("-q", "--quick", dest="quick", action="store_true",
default=False, help="enable quick mode")
parser.add_argument("-r", "--remove-orphan", dest="removeOrphan",
action="store_true",
default=False, help="remove old scenery files")
parser.add_argument("--mode", default="sync", choices=("check", "sync"),
help="""\
main mode of operation (default: '%(default)s'). In 'sync' mode, contents
is downloaded from the server to the target directory. On the other hand,
in 'check' mode, {progname} compares the contents of the target directory
with the remote repository without writing nor deleting anything on
disk.""".format(progname=PROGNAME))
parser.add_argument("--report", dest="report", action="store_true",
default=False,
help="""\
before normal exit, print a report of what was found""")
parser.add_argument("--top", dest="top", type=int, default=90, help="""\
maximum latitude to include in download [default: %(default)d]""")
parser.add_argument("--bottom", dest="bottom", type=int, default=-90,
help="""\
minimum latitude to include in download [default: %(default)d]""")
parser.add_argument("--left", dest="left", type=int, default=-180, help="""\
minimum longitude to include in download [default: %(default)d]""")
parser.add_argument("--right", dest="right", type=int, default=179,
help="""\
maximum longitude to include in download [default: %(default)d]""")
args = parser.parse_args()
# Perform consistency checks on the arguments
if args.mode == "check" and args.removeOrphan:
print("{prg}: 'check' mode is read-only and thus doesn't make sense "
"with\noption --remove-orphan (-r)".format(prg=PROGNAME),
file=sys.stderr)
sys.exit(ExitStatus.ERROR.value)
# Replace backslashes with forward slashes, remove leading and trailing
# slashes, collapse consecutive slashes. Yes, this implies that we tolerate
# leading slashes for --only-subdir (which makes sense because virtual
# paths are printed like that by this program, therefore it is natural for
# users to copy & paste such paths in order to use them for --only-subdir).
args.virtualSubdir = VirtualPath(args.onlySubdir.replace('\\', '/'))
# Be nice to our user in case the path starts with '\', 'C:\', etc.
if os.path.isabs(args.virtualSubdir.asRelative()):
print("{prg}: option --only-subdir expects a *relative* path, but got "
"'{subdir}'".format(prg=PROGNAME, subdir=args.onlySubdir),
file=sys.stderr)
sys.exit(ExitStatus.ERROR.value)
return args
def main():
args = parseCommandLine()
terraSync = TerraSync(args.mode, args.report, args.url, args.target,
args.quick, args.removeOrphan,
DownloadBoundaries(args.top, args.left, args.bottom,
args.right),args.auth)
report = terraSync.start(args.virtualSubdir)
if args.report:
report.printReport()
sys.exit(ExitStatus.SUCCESS.value)

View File

@@ -0,0 +1,506 @@
# -*- coding: utf-8 -*-
# virtual_path.py --- Classes used to manipulate slash-separated virtual paths
#
# Copyright (C) 2018 Florent Rougon
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Module containing the VirtualPath and MutableVirtualPath classes."""
import pathlib
class VirtualPath:
"""Class used to represent virtual paths using the slash separator.
This class always uses the slash ('/') as the separator between
components. For terrasync.py, the root path '/' corresponds to the
repository root, regardless of where it is stored (hard drive,
remote server, etc.).
Note: because of this, the class is not supposed to be used directly
for filesystem accesses, since some root directory or
protocol://server/root-dir prefix would have to be prepended
to provide reasonably useful functionality. This is why the
paths managed by this class are said to be virtual. This also
implies that even in Python 3.6 or later, this class should
*not* inherit from os.PathLike.
Whenever a given feature exists in pathlib.PurePath, this class
replicates the corresponding pathlib.PurePath API, but using
mixedCaseStyle instead of underscore_style (the latter being used
for every method of pathlib.PurePath). Of course, types are adapted:
for instance, methods of this class often return a VirtualPath
instance, whereas the corresponding pathlib.PurePath methods would
return a pathlib.PurePath instance.
"""
def __init__(self, p):
# Once this function exits, self._path *must not be changed* anymore
# (doing so would violate the contract for a hashable object: the
# hash must not change once the object has been constructed).
self._path = self.normalizeStringPath(p)
# This check could of course be skipped if it is found to really affect
# performance.
self._check()
def __str__(self):
"""Return a string representation of the path in self.
The return value:
- always starts with a '/';
- never ends with a '/' except if it is exactly '/' (i.e.,
the root virtual path).
"""
return self._path
def asPosix(self):
"""Return a string representation of the path in self.
This method returns str(self), it is only present for
compatibility with pathlib.PurePath.
"""
return str(self)
def __repr__(self):
return "{}.{}({!r})".format(__name__, type(self).__name__, self._path)
def __lt__(self, other):
# Allow sorting with instances of VirtualPath, or of any subclass. Note
# that the == operator (__eq__()) and therefore also != are stricter
# with respect to typing.
if isinstance(other, VirtualPath):
return self._path < other._path
else:
return NotImplemented
def __le__(self, other):
if isinstance(other, VirtualPath):
return self._path <= other._path
else:
return NotImplemented
def __eq__(self, other):
# The types must be the same, therefore a VirtualPath never compares
# equal to a MutableVirtualPath with the == operator. For such
# comparisons, use the samePath() method. If __eq__() (and thus
# necessarily __hash__()) were more lax about typing, adding
# VirtualPath instances and instances of hashable subclasses of
# VirtualPath with the same _path to a set or frozenset would lead to
# unintuitive behavior, since they would all be considered equal.
return type(self) == type(other) and self._path == other._path
# intentionally not implemented. Python3 provides a default implementation.
# def __ne__(self, other):
def __gt__(self, other):
if isinstance(other, VirtualPath):
return self._path > other._path
else:
return NotImplemented
def __ge__(self, other):
if isinstance(other, VirtualPath):
return self._path >= other._path
else:
return NotImplemented
def __hash__(self):
# Be strict about typing, as for __eq__().
return hash((type(self), self._path))
def samePath(self, other):
"""Compare the path with another instance, possibly of a subclass.
other -- instance of VirtualPath, or of a subclass of
VirtualPath
"""
if isinstance(other, VirtualPath):
return self._path == other._path
else:
raise TypeError("{obj!r} is of type {klass}, which is neither "
"VirtualPath nor a subclass thereof"
.format(obj=other, klass=type(other).__name__))
def _check(self):
"""Run consistency checks on self."""
assert (self._path.startswith('/') and not self._path.startswith('//')
and (self._path == '/' or not self._path.endswith('/'))), \
repr(self._path)
@classmethod
def normalizeStringPath(cls, path):
"""Normalize a string representing a virtual path.
path -- input path (string)
Return a string that always starts with a slash, never contains
consecutive slashes and only ends with a slash if it's the root
virtual path ('/').
If 'path' doesn't start with a slash ('/'), it is considered
relative to the root. This implies that if 'path' is the empty
string, the return value is '/'.
"""
if not path.startswith('/'):
# / is the “virtual root” of the TerraSync repository
path = '/' + path
elif path.startswith('//') and not path.startswith('///'):
# Nasty special case. As allowed (but not mandated!) by POSIX[1],
# in pathlib.PurePosixPath('//some/path'), no collapsing happens[2].
# This is only the case for exactly *two* *leading* slashes.
# [1] http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
# [2] https://www.python.org/dev/peps/pep-0428/#construction
path = path[1:]
return pathlib.PurePosixPath(path).as_posix()
def __truediv__(self, s):
"""Path concatenation with the '/' operator.
's' must be a string representing a relative path using the '/'
separator, for instance "dir/subdir/other-subdir".
Return a new instance of type(self).
"""
assert not (s.startswith('/') or s.endswith('/')), repr(s)
if self._path == '/':
return type(self)(self._path + s)
else:
return type(self)(self._path + '/' + s)
def joinpath(self, *args):
"""Combine 'self' with each given string argument in turn.
Each argument should be of the form "foo", "foo/bar",
"foo/bar/baz", etc. Return the corresponding instance of
type(self).
>>> p = VirtualPath("/foo").joinpath("bar", "baz", "quux/zoot")
>>> str(p)
'/foo/bar/baz/quux/zoot'
"""
return self / '/'.join(args)
@property
def name(self):
"""Return a string representing the final path component.
>>> p = VirtualPath("/foo/bar/baz")
>>> p.name
'baz'
"""
pos = self._path.rfind('/')
assert pos != -1, (pos, self._path)
return self._path[pos+1:]
@property
def parts(self):
"""Return a tuple containing the paths components.
>>> p = VirtualPath('/usr/bin/python3')
>>> p.parts
('/', 'usr', 'bin', 'python3')
"""
if self._path == "/":
return ('/',)
else:
# Skip the leading slash before splitting
return ('/',) + tuple(self._path[1:].split('/'))
def generateParents(self):
"""Generator function for the parents of the path.
See the 'parents' property for details.
"""
if self._path == '/':
return
assert self._path.startswith('/'), repr(self._path)
prevPos = len(self._path)
while True:
pos = self._path.rfind('/', 0, prevPos)
if pos > 0:
yield type(self)(self._path[:pos])
prevPos = pos
else:
assert pos == 0, pos
break
yield type(self)('/')
@property
def parents(self):
"""The path ancestors.
Return an immutable sequence providing access to the logical
ancestors of the path.
>>> p = VirtualPath('/foo/bar/baz')
>>> len(p.parents)
3
>>> p.parents[0]
terrasync.virtual_path.VirtualPath('/foo/bar')
>>> p.parents[1]
terrasync.virtual_path.VirtualPath('/foo')
>>> p.parents[2]
terrasync.virtual_path.VirtualPath('/')
"""
return tuple(self.generateParents())
@property
def parent(self):
"""The logical parent of the path.
>>> p = VirtualPath('/foo/bar/baz')
>>> p.parent
terrasync.virtual_path.VirtualPath('/foo/bar')
>>> q = VirtualPath('/')
>>> q.parent
terrasync.virtual_path.VirtualPath('/')
"""
pos = self._path.rfind('/')
assert pos >= 0, pos
if pos == 0:
return type(self)('/')
else:
return type(self)(self._path[:pos])
@property
def suffix(self):
"""The extension of the final component, if any.
>>> VirtualPath('/my/library/setup.py').suffix
'.py'
>>> VirtualPath('/my/library.tar.gz').suffix
'.gz'
>>> VirtualPath('/my/library').suffix
''
"""
name = self.name
pos = name.rfind('.')
return name[pos:] if pos != -1 else ''
@property
def suffixes(self):
"""A list of the paths extensions.
>>> VirtualPath('/my/library/setup.py').suffixes
['.py']
>>> VirtualPath('/my/library.tar.gz').suffixes
['.tar', '.gz']
>>> VirtualPath('/my/library').suffixes
[]
"""
name = self.name
prevPos = len(name)
l = []
while True:
pos = name.rfind('.', 0, prevPos)
if pos == -1:
break
else:
l.insert(0, name[pos:prevPos])
prevPos = pos
return l
@property
def stem(self):
"""The final path component, without its suffix.
>>> VirtualPath('/my/library.tar.gz').stem
'library.tar'
>>> VirtualPath('/my/library.tar').stem
'library'
>>> VirtualPath('/my/library').stem
'library'
>>> VirtualPath('/').stem
''
"""
name = self.name
pos = name.rfind('.')
return name if pos == -1 else name[:pos]
def asRelative(self):
"""Return the virtual path without its leading '/'.
>>> p = VirtualPath('/usr/bin/python3')
>>> p.asRelative()
'usr/bin/python3'
>>> VirtualPath('').asRelative()
''
>>> VirtualPath('/').asRelative()
''
"""
assert self._path.startswith('/'), repr(self._path)
return self._path[1:]
def relativeTo(self, other):
"""Return the portion of this path that follows 'other'.
The return value is a string. If the operation is impossible,
ValueError is raised.
>>> VirtualPath('/etc/passwd').relativeTo('/')
'etc/passwd'
>>> VirtualPath('/etc/passwd').relativeTo('/etc')
'passwd'
"""
normedOther = self.normalizeStringPath(other)
if normedOther == '/':
return self._path[1:]
elif self._path.startswith(normedOther):
rest = self._path[len(normedOther):]
if rest.startswith('/'):
return rest[1:]
raise ValueError("{!r} does not start with '{}'".format(self, other))
def withName(self, newName):
"""Return a new VirtualPath instance with the 'name' part changed.
If the original path is '/' (which doesnt have a name in the
sense of the 'name' property), ValueError is raised.
>>> p = VirtualPath('/foobar/downloads/pathlib.tar.gz')
>>> p.withName('setup.py')
terrasync.virtual_path.VirtualPath('/foobar/downloads/setup.py')
"""
if self._path == '/':
raise ValueError("{!r} has an empty name".format(self))
else:
pos = self._path.rfind('/')
assert pos != -1, (pos, self._path)
if newName.startswith('/'):
raise ValueError("{!r} starts with a '/'".format(newName))
elif newName.endswith('/'):
raise ValueError("{!r} ends with a '/'".format(newName))
else:
return VirtualPath(self._path[:pos]) / newName
def withSuffix(self, newSuffix):
"""Return a new VirtualPath instance with the suffix changed.
If the original path doesnt have a suffix, the new suffix is
appended:
>>> p = VirtualPath('/foobar/downloads/pathlib.tar.gz')
>>> p.withSuffix('.bz2')
terrasync.virtual_path.VirtualPath('/foobar/downloads/pathlib.tar.bz2')
>>> p = VirtualPath('/foobar/README')
>>> p.withSuffix('.txt')
terrasync.virtual_path.VirtualPath('/foobar/README.txt')
If 'self' is the root virtual path ('/') or 'newSuffix' doesn't
start with '.', ValueError is raised.
"""
if not newSuffix.startswith('.'):
raise ValueError("new suffix {!r} doesn't start with '.'"
.format(newSuffix))
name = self.name
if not name:
raise ValueError("{!r} has an empty 'name' part".format(self))
pos = name.rfind('.')
if pos == -1:
return self.withName(name + newSuffix) # append suffix
else:
return self.withName(name[:pos] + newSuffix) # replace suffix
class MutableVirtualPath(VirtualPath):
"""Mutable subclass of VirtualPath.
Contrary to VirtualPath objects, instances of this class can be
modified in-place with the /= operator, in order to append path
components. The price to pay for this advantage is that they can't
be used as dictionary keys or as elements of a set or frozenset,
because they are not hashable.
"""
__hash__ = None # ensure the type is not hashable
def _normalize(self):
self._path = self.normalizeStringPath(self._path)
def __itruediv__(self, s):
"""Path concatenation with the '/=' operator.
's' must be a string representing a relative path using the '/'
separator, for instance "dir/subdir/other-subdir".
"""
# This check could of course be skipped if it is found to really affect
# performance.
self._check()
assert not (s.startswith('/') or s.endswith('/')), repr(s)
if self._path == '/':
self._path += s
else:
self._path += '/' + s
# Collapse multiple slashes, remove trailing '/' except if the whole
# path is '/', etc.
self._normalize()
return self
if __name__ == "__main__":
# The doctest setup below works, but for full test coverage, use the
# unittest framework (it is set up to automatically run all doctests from
# this module!).
#
# Hint: 'python3 -m unittest discover' from the TerraSync directory
# should do the trick.
import doctest
doctest.testmod()

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
d:some\illegal directory name with a backslash:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
f:some\illegal file name with a backslash:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
t:some\illegal tarball name with a backslash.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
d:..:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e

View File

@@ -0,0 +1,2 @@
version:1
path:some/path/with/a/../component

View File

@@ -0,0 +1,2 @@
version:1
path:some/path/non-ASCII chars like é, ê, €, Œ, Ÿ, etc./foo/bar

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
f:..:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123

View File

@@ -0,0 +1,2 @@
version:1
path:some/path/that/contains \ a/backslash

View File

@@ -0,0 +1,2 @@
version:1
path:/some/path/that/starts/with/a/slash

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
d:some/illegal directory name with a slash:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
f:some/illegal file name with a slash:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
t:some/illegal tarball name with a slash.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567

View File

@@ -0,0 +1,3 @@
version:1
path:some/path
t:..:b63a067d82824f158d6bde66f9e76654274277fe:1234567

View File

@@ -0,0 +1,16 @@
# Comment line
version:1
path:some/path
time:20200926-10:38Z
d:Airports:8a93b5d8a2b04d2fb8de4ef58ad02f9e8819d314
d:Models:bee221c9d2621dc9b69cd9e0ad7dd0605f6ea928
d:Objects:10ae32c986470fa55b56b8eefbc6ed565cce0642
# Other comment line
d:Terrain:e934024dc0f959f9a433e47c646d256630052c2e
d:Buildings:19060725efc2a301fa6844991e2922d42d8de5e2
d:Pylons:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
d:Roads:89f8f10406041948368c76c0a2e794d45ac536b7
f:some file:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
f:other file:62726252f7183eef31001c1c565e149f3c4527b9:4567
f:third file:303adcc1747d8dc438096307189881e987e9bb61:89012
t:Airports_archive.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567

View File

@@ -0,0 +1,95 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# test_dirindex.py --- Test module for terrasync.dirindex
# Copyright (C) 2020 Florent Rougon
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# In order to exercise all tests, run the following command from the parent
# directory (you may omit the 'discover' argument):
#
# python3 -m unittest discover
"""Test module for terrasync.dirindex"""
import os
import unittest
from terrasync.dirindex import DirIndex
from terrasync.exceptions import InvalidDirIndexFile
from terrasync.virtual_path import VirtualPath
baseDir = os.path.dirname(__file__)
def testData(*args):
return os.path.join(baseDir, "data", "dirindex", *args)
directories_in_sample_dirindex_1 = [
{'name': 'Airports', 'hash': '8a93b5d8a2b04d2fb8de4ef58ad02f9e8819d314'},
{'name': 'Models', 'hash': 'bee221c9d2621dc9b69cd9e0ad7dd0605f6ea928'},
{'name': 'Objects', 'hash': '10ae32c986470fa55b56b8eefbc6ed565cce0642'},
{'name': 'Terrain', 'hash': 'e934024dc0f959f9a433e47c646d256630052c2e'},
{'name': 'Buildings', 'hash': '19060725efc2a301fa6844991e2922d42d8de5e2'},
{'name': 'Pylons', 'hash': '378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e'},
{'name': 'Roads', 'hash': '89f8f10406041948368c76c0a2e794d45ac536b7'}]
files_in_sample_dirindex_1 = [
{'name': 'some file',
'hash': '4cbf3d1746a1249bff7809e4b079dd80cfce594c',
'size': 123},
{'name': 'other file',
'hash': '62726252f7183eef31001c1c565e149f3c4527b9',
'size': 4567},
{'name': 'third file',
'hash': '303adcc1747d8dc438096307189881e987e9bb61',
'size': 89012}]
tarballs_in_sample_dirindex_1 = [
{'name': 'Airports_archive.tgz',
'hash': 'b63a067d82824f158d6bde66f9e76654274277fe',
'size': 1234567}]
class TestDirIndex(unittest.TestCase):
"""Unit tests for the DirIndex class."""
def test_constructor(self):
d = DirIndex(testData("good", "sample_dirindex_1"))
self.assertEqual(d.version, 1)
self.assertEqual(d.path, VirtualPath("some/path"))
self.assertEqual(d.directories, directories_in_sample_dirindex_1)
self.assertEqual(d.files, files_in_sample_dirindex_1)
self.assertEqual(d.tarballs, tarballs_in_sample_dirindex_1)
stems = ("path_starts_with_slash",
"path_contains_a_backslash",
"dotdot_in_path",
"slash_in_directory_name",
"slash_in_file_name",
"slash_in_tarball_name",
"backslash_in_directory_name",
"backslash_in_file_name",
"backslash_in_tarball_name",
"directory_name_is_double_colon",
"file_name_is_double_colon",
"tarball_name_is_double_colon",)
for stem in stems:
with self.assertRaises(InvalidDirIndexFile):
DirIndex(testData("bad", "bad_dirindex_" + stem))
with self.assertRaises(UnicodeDecodeError):
d = DirIndex(testData("bad", "bad_dirindex_encoding"))

View File

@@ -0,0 +1,357 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# test_virtual_path.py --- Test module for terrasync.virtual_path
# Copyright (C) 2018 Florent Rougon
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# In order to exercise all tests, run the following command from the parent
# directory (you may omit the 'discover' argument):
#
# python3 -m unittest discover
import collections
import unittest
from terrasync.virtual_path import VirtualPath, MutableVirtualPath
# Hook doctest-based tests into the unittest test discovery mechanism
import doctest
import terrasync.virtual_path
def load_tests(loader, tests, ignore):
# Tell unittest to run doctests from terrasync.virtual_path
tests.addTests(doctest.DocTestSuite(terrasync.virtual_path))
return tests
class VirtualPathCommonTests:
"""Common tests to run for both VirtualPath and MutableVirtualPath.
The tests inside this class must exercice the class (VirtualPath or
MutableVirtualPath) stored in the 'cls' class attribute. They must
work for both VirtualPath and MutableVirtualPath, otherwise they
don't belong here!
"""
def test_normalizeStringPath(self):
self.assertEqual(self.cls.normalizeStringPath("/"), "/")
self.assertEqual(self.cls.normalizeStringPath(""), "/")
self.assertEqual(
self.cls.normalizeStringPath("/abc/Def ijk//l Mn///op/q/rst/"),
"/abc/Def ijk/l Mn/op/q/rst")
self.assertEqual(self.cls.normalizeStringPath("abc/def"), "/abc/def")
self.assertEqual(self.cls.normalizeStringPath("/abc/def"), "/abc/def")
self.assertEqual(self.cls.normalizeStringPath("//abc/def"),
"/abc/def")
self.assertEqual(self.cls.normalizeStringPath("///abc/def"),
"/abc/def")
self.assertEqual(self.cls.normalizeStringPath("/abc//def"),
"/abc/def")
# Unless the implementation of VirtualPath.__init__() has changed
# meanwhile, the following function must be essentially the same as
# test_normalizeStringPath().
def test_constructor_and_str(self):
p = self.cls("/")
self.assertEqual(str(p), "/")
p = self.cls("")
self.assertEqual(str(p), "/")
p = self.cls("/abc/Def ijk//l Mn///op/q/rst/")
self.assertEqual(str(p), "/abc/Def ijk/l Mn/op/q/rst")
p = self.cls("abc/def")
self.assertEqual(str(p), "/abc/def")
p = self.cls("/abc/def")
self.assertEqual(str(p), "/abc/def")
p = self.cls("//abc/def")
self.assertEqual(str(p), "/abc/def")
p = self.cls("///abc/def")
self.assertEqual(str(p), "/abc/def")
p = self.cls("/abc//def")
self.assertEqual(str(p), "/abc/def")
def test_asPosix (self):
self.assertEqual(self.cls("").asPosix(), "/")
self.assertEqual(self.cls("/").asPosix(), "/")
self.assertEqual(self.cls("/abc//def").asPosix(), "/abc/def")
self.assertEqual(self.cls("/abc//def/").asPosix(), "/abc/def")
self.assertEqual(self.cls("//abc//def//").asPosix(), "/abc/def")
self.assertEqual(self.cls("////abc//def//").asPosix(), "/abc/def")
def test_samePath(self):
self.assertTrue(self.cls("").samePath(self.cls("")))
self.assertTrue(self.cls("").samePath(self.cls("/")))
self.assertTrue(self.cls("/").samePath(self.cls("")))
self.assertTrue(self.cls("/").samePath(self.cls("/")))
self.assertTrue(
self.cls("/abc/def").samePath(self.cls("/abc/def")))
self.assertTrue(
self.cls("/abc//def").samePath(self.cls("/abc/def")))
self.assertTrue(
self.cls("/abc/def/").samePath(self.cls("/abc/def")))
def test_comparisons(self):
self.assertEqual(self.cls("/abc/def"), self.cls("/abc/def"))
self.assertEqual(self.cls("/abc//def"), self.cls("/abc/def"))
self.assertEqual(self.cls("/abc/def/"), self.cls("/abc/def"))
self.assertNotEqual(self.cls("/abc/dEf"), self.cls("/abc/def"))
self.assertNotEqual(self.cls("/abc/def "), self.cls("/abc/def"))
self.assertLessEqual(self.cls("/foo/bar"), self.cls("/foo/bar"))
self.assertLessEqual(self.cls("/foo/bar"), self.cls("/foo/bbr"))
self.assertLess(self.cls("/foo/bar"), self.cls("/foo/bbr"))
self.assertGreaterEqual(self.cls("/foo/bar"), self.cls("/foo/bar"))
self.assertGreaterEqual(self.cls("/foo/bbr"), self.cls("/foo/bar"))
self.assertGreater(self.cls("/foo/bbr"), self.cls("/foo/bar"))
def test_truedivOperators(self):
"""
Test operators used to add paths components to a VirtualPath instance."""
p = self.cls("/foo/bar/baz/quux/zoot")
self.assertEqual(p, self.cls("/") / "foo" / "bar" / "baz/quux/zoot")
self.assertEqual(p, self.cls("/foo") / "bar" / "baz/quux/zoot")
self.assertEqual(p, self.cls("/foo/bar") / "baz/quux/zoot")
def test_joinpath(self):
p = self.cls("/foo/bar/baz/quux/zoot")
self.assertEqual(
p,
self.cls("/foo").joinpath("bar", "baz", "quux/zoot"))
def test_nameAttribute(self):
self.assertEqual(self.cls("/").name, "")
p = self.cls("/foo/bar/baz/quux/zoot")
self.assertEqual(p.name, "zoot")
def test_partsAttribute(self):
self.assertEqual(self.cls("/").parts, ("/",))
p = self.cls("/foo/bar/baz/quux/zoot")
self.assertEqual(p.parts, ("/", "foo", "bar", "baz", "quux", "zoot"))
def test_parentsAttribute(self):
def pathify(*args):
return tuple( (self.cls(s) for s in args) )
p = self.cls("/")
self.assertEqual(tuple(p.parents), pathify()) # empty tuple
p = self.cls("/foo")
self.assertEqual(tuple(p.parents), pathify("/"))
p = self.cls("/foo/bar")
self.assertEqual(tuple(p.parents), pathify("/foo", "/"))
p = self.cls("/foo/bar/baz")
self.assertEqual(tuple(p.parents), pathify("/foo/bar", "/foo", "/"))
def test_parentAttribute(self):
def pathify(s):
return self.cls(s)
p = self.cls("/")
self.assertEqual(p.parent, pathify("/"))
p = self.cls("/foo")
self.assertEqual(p.parent, pathify("/"))
p = self.cls("/foo/bar")
self.assertEqual(p.parent, pathify("/foo"))
p = self.cls("/foo/bar/baz")
self.assertEqual(p.parent, pathify("/foo/bar"))
def test_suffixAttribute(self):
p = self.cls("/")
self.assertEqual(p.suffix, '')
p = self.cls("/foo/bar/baz.py")
self.assertEqual(p.suffix, '.py')
p = self.cls("/foo/bar/baz.py.bla")
self.assertEqual(p.suffix, '.bla')
p = self.cls("/foo/bar/baz")
self.assertEqual(p.suffix, '')
def test_suffixesAttribute(self):
p = self.cls("/")
self.assertEqual(p.suffixes, [])
p = self.cls("/foo/bar/baz.py")
self.assertEqual(p.suffixes, ['.py'])
p = self.cls("/foo/bar/baz.py.bla")
self.assertEqual(p.suffixes, ['.py', '.bla'])
p = self.cls("/foo/bar/baz")
self.assertEqual(p.suffixes, [])
def test_stemAttribute(self):
p = self.cls("/")
self.assertEqual(p.stem, '')
p = self.cls("/foo/bar/baz.py")
self.assertEqual(p.stem, 'baz')
p = self.cls("/foo/bar/baz.py.bla")
self.assertEqual(p.stem, 'baz.py')
def test_asRelative(self):
self.assertEqual(self.cls("/").asRelative(), "")
self.assertEqual(self.cls("/foo/bar/baz/quux/zoot").asRelative(),
"foo/bar/baz/quux/zoot")
def test_relativeTo(self):
self.assertEqual(self.cls("").relativeTo(""), "")
self.assertEqual(self.cls("").relativeTo("/"), "")
self.assertEqual(self.cls("/").relativeTo("/"), "")
self.assertEqual(self.cls("/").relativeTo(""), "")
p = self.cls("/foo/bar/baz/quux/zoot")
self.assertEqual(p.relativeTo(""), "foo/bar/baz/quux/zoot")
self.assertEqual(p.relativeTo("/"), "foo/bar/baz/quux/zoot")
self.assertEqual(p.relativeTo("foo"), "bar/baz/quux/zoot")
self.assertEqual(p.relativeTo("foo/"), "bar/baz/quux/zoot")
self.assertEqual(p.relativeTo("/foo"), "bar/baz/quux/zoot")
self.assertEqual(p.relativeTo("/foo/"), "bar/baz/quux/zoot")
self.assertEqual(p.relativeTo("foo/bar/baz"), "quux/zoot")
self.assertEqual(p.relativeTo("foo/bar/baz/"), "quux/zoot")
self.assertEqual(p.relativeTo("/foo/bar/baz"), "quux/zoot")
self.assertEqual(p.relativeTo("/foo/bar/baz/"), "quux/zoot")
with self.assertRaises(ValueError):
p.relativeTo("/foo/ba")
with self.assertRaises(ValueError):
p.relativeTo("/foo/balloon")
def test_withName(self):
p = self.cls("/foo/bar/baz/quux/zoot")
self.assertEqual(p.withName(""),
VirtualPath("/foo/bar/baz/quux"))
self.assertEqual(p.withName("pouet"),
VirtualPath("/foo/bar/baz/quux/pouet"))
self.assertEqual(p.withName("pouet/zdong"),
VirtualPath("/foo/bar/baz/quux/pouet/zdong"))
# The self.cls object has no 'name' (referring to the 'name' property)
with self.assertRaises(ValueError):
self.cls("").withName("foobar")
with self.assertRaises(ValueError):
self.cls("/").withName("foobar")
def test_withSuffix(self):
p = self.cls("/foo/bar/baz.tar.gz")
self.assertEqual(p.withSuffix(".bz2"),
VirtualPath("/foo/bar/baz.tar.bz2"))
p = self.cls("/foo/bar/baz")
self.assertEqual(p.withSuffix(".tar.xz"),
VirtualPath("/foo/bar/baz.tar.xz"))
# The self.cls object has no 'name' (referring to the 'name' property)
with self.assertRaises(ValueError):
self.cls("/foo/bar/baz.tar.gz").withSuffix("no-leading-dot")
with self.assertRaises(ValueError):
# The root virtual path ('/') can't be used for this
self.cls("/").withSuffix(".foobar")
class TestVirtualPath(unittest.TestCase, VirtualPathCommonTests):
"""Tests for the VirtualPath class.
These are the tests using the common infrastructure from
VirtualPathCommonTests.
"""
cls = VirtualPath
class TestVirtualPathSpecific(unittest.TestCase):
"""Tests specific to the VirtualPath class."""
def test_isHashableType(self):
p = VirtualPath("/foo")
self.assertTrue(isinstance(p, collections.abc.Hashable))
def test_insideSet(self):
l1 = [ VirtualPath("/foo/bar"),
VirtualPath("/foo/baz") ]
l2 = l1 + [ VirtualPath("/foo/bar") ] # l2 has a duplicate element
# Sets allow one to ignore duplicate elements when comparing
self.assertEqual(set(l1), set(l2))
self.assertEqual(frozenset(l1), frozenset(l2))
class TestMutableVirtualPath(unittest.TestCase, VirtualPathCommonTests):
"""Tests for the MutableVirtualPath class.
These are the tests using the common infrastructure from
VirtualPathCommonTests.
"""
cls = MutableVirtualPath
class TestMutableVirtualPathSpecific(unittest.TestCase):
"""Tests specific to the MutableVirtualPath class."""
def test_mixedComparisons(self):
self.assertTrue(
VirtualPath("/abc/def").samePath(MutableVirtualPath("/abc/def")))
self.assertTrue(
VirtualPath("/abc//def").samePath(MutableVirtualPath("/abc/def")))
self.assertTrue(
VirtualPath("/abc/def/").samePath(MutableVirtualPath("/abc/def")))
self.assertTrue(
MutableVirtualPath("/abc/def").samePath(VirtualPath("/abc/def")))
self.assertTrue(
MutableVirtualPath("/abc//def").samePath(VirtualPath("/abc/def")))
self.assertTrue(
MutableVirtualPath("/abc/def/").samePath(VirtualPath("/abc/def")))
def test_inPlacePathConcatenation(self):
p = VirtualPath("/foo/bar/baz/quux/zoot")
q = MutableVirtualPath("/foo")
q /= "bar"
q /= "baz/quux/zoot"
self.assertTrue(p.samePath(q))
def test_isNotHashableType(self):
p = MutableVirtualPath("/foo")
self.assertFalse(isinstance(p, collections.abc.Hashable))

49
scripts/python/demo.py Executable file
View File

@@ -0,0 +1,49 @@
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
from FlightGear import FlightGear
import time
def main():
fg = FlightGear('localhost', 5500)
# Wait five seconds for simulator to settle down
while 1:
if fg['/sim/time/elapsed-sec'] > 5:
break
time.sleep(1.0)
print(fg['/sim/time/elapsed-sec'])
# parking brake on
fg['/controls/parking-brake'] = 1
# heading = fg['/orientation/heading-deg']
# Switch to external view for for 'walk around'.
fg.view_next()
fg['/sim/current-view/goal-heading-offset-deg'] = 180.0
#fg.wait_for_prop_eq('/sim/current-view/heading-offset-deg', 180.0)
fg['/sim/current-view/goal-heading-offset-deg'] = 90.0
#fg.wait_for_prop_eq('/sim/current-view/heading-offset-deg', 90.0)
fg['/sim/current-view/goal-heading-offset-deg'] = 0.0
#fg.wait_for_prop_eq('/sim/current-view/heading-offset-deg', 0.0)
time.sleep(2.0)
# Switch back to cockpit view
fg.view_prev()
time.sleep(2.0)
# Flaps to take off position
fg['/controls/flaps'] = 0.34
#fg.wait_for_prop_eq('/surface-positions/flap-pos-norm', 0.34)
fg.quit()
if __name__ == '__main__':
main()

301
scripts/python/nasal_api_doc.py Executable file
View File

@@ -0,0 +1,301 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2012 Adrian Musceac
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, sys, glob
import io
import re, string
"""Script which generates an API documentation file for Nasal libraries
located inside $FGROOT/Nasal/
Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]
Or configure the local path below, and ommit the path in the console.
The API doc in HTML format is generated in the current working directory"""
########### Local $FGROOT/Nasal/ path ##########
NASAL_PATH="../fgfs/fgdata/Nasal/"
def get_files(nasal_dir):
if nasal_dir[-1]!='/':
nasal_dir+='/'
try:
os.stat(nasal_dir)
except:
print("The path does not exist")
sys.exit()
fgroot_dir = nasal_dir.rstrip('/').replace('Nasal','')
try:
f_version = open(fgroot_dir+'version','rb')
version = f_version.read(256).rstrip('\n')
finally:
f_version.close()
top_level = []
modules = []
top_namespaces = []
files_list = os.listdir(nasal_dir)
for f in files_list:
if f.find(".nas")!=-1:
top_level.append(f)
continue
if os.path.isdir(nasal_dir + f):
modules.append(f)
top_level.sort()
modules.sort()
if len(top_level) ==0:
print("This does not look like the correct $FGROOT/Nasal path")
sys.exit()
if len(modules)==0:
print("Warning: could not find any submodules")
for f in top_level:
namespace=f.replace(".nas","")
functions=parse_file(nasal_dir + f)
top_namespaces.append([namespace,functions])
for m in modules:
files=glob.glob(nasal_dir+m+"/*.nas")
for f in files:
functions=parse_file(f)
top_namespaces.append([m,functions])
output_text(top_namespaces,modules,version)
def output_text(top_namespaces,modules,version):
fw=open('./nasal_api_doc.html','wb')
buf='<html><head>\
<title>Nasal API</title>\
<style>\n\
a.main_module_link {margin-left:30px;display:block;float:left;}\
div.container {background-color:#eee;clear:left;margin-top:20px;}\
h2.namespace_title {padding-left:20px;color:#fff;background-color:#8888AC}\
h4.class_function {padding-left:20px;background-color:#eee;color:#000033}\
h4.class_definition {padding-left:20px;background-color:#eee;color:#000033}\
h4.function {padding-left:20px;background-color:#eee;color:#000033}\
hr {margin-left:30px;margin-right:30px;}\
div.comments {padding-left:40px;display:inline;font-size:12px;}\
</style>\n\
</head><body style="width:1024px;">'
buf+='<h1 style="padding-left:20px;display:block;color:#fff;background-color:#555588;">\
Nasal $FGROOT Library<br/><span style="font-size:12px;">Flightgear version: '+version+'\
<br/>This file is generated automatically by scripts/python/nasal_api_doc.py\
</span></h1>\
<br/><a href="http://plausible.org/nasal">Nasal documentation</a>&nbsp;&nbsp;\
<a href="http://wiki.flightgear.org/Nasal_scripting_language">Flightgear Nasal documentation</a>\n<div style="float:right;">&nbsp;'
buf+='<h2 style="font-size:14px;height:450px;width:250px;overflow:scroll;display:block;position:fixed;top:20px;right:20px;background-color:#8888AC;border:1px solid black;">\n'
done=[]
for namespace in top_namespaces:
color='0000cc'
if namespace[0] in modules:
color='cc0000'
if namespace[0] not in done:
buf+='<a class="main_module_link" style="color:'+color+'" href="#'+namespace[0]+'">'+namespace[0]+'</a>&nbsp;<br/>\n'
done.append(namespace[0])
buf+='</h2></div>\n'
done2=[]
for namespace in top_namespaces:
if namespace[0] not in done2:
buf+='<div class="container" style="">\n'
buf += '<h2 class="namespace_title"><a name="'+namespace[0]+'">'+namespace[0]+'</a></h2>\n'
done2.append(namespace[0])
for functions in namespace[1]:
class_func=functions[0].split('.')
if len(class_func)>1:
f_name=''
if class_func[1].find('_')==0:
f_name='<font color="#0000cc">'+class_func[1]+'</font>'
else:
f_name=class_func[1]
if class_func[1]!='':
buf+= '<div><h4 class="class_function"><b>'\
+namespace[0]+'</b>'+ "." + '<b><i>'+class_func[0]+'</i></b>'+'<b>.'+f_name+'</b>'+' ( <font color="#cc0000">'+ functions[1]+ '</font> )' +'</h4>\n'
else:
buf+= '<div><h4 class="class_definition"><b>'\
+namespace[0]+'</b>'+ "." + '<b><i><u><font color="#000000">'+class_func[0]+'</font></u></i></b>' +'</h4>\n'
else:
if functions[0].find('_')==0:
f_name='<font color="#0000cc">'+functions[0]+'</font>'
else:
f_name=functions[0]
buf+= '<div><h4 class="function"><b>'\
+namespace[0]+'</b>'+ "." + '<b>'+f_name+'</b>'+ ' ( <font color="#cc0000">'+ functions[1]+ '</font> )' +'</h4>\n'
for comment in functions[2]:
if comment.find('=====')!=-1:
buf+='<hr/>'
else:
tempComment = comment.replace('#','').replace('<','&lt;').replace('>','&gt;')
if tempComment.strip()!="":
buf+= '<div class="comments">'+tempComment+'</div><br/>\n'
buf+='</div>\n'
if namespace[0] not in done2:
buf+='</div>\n'
buf+='</body></html>'
fw.write(buf)
fw.close()
def parse_file(filename):
with open(filename,'rb') as fr:
content = fr.readlines()
i=0
retval=[]
classname=""
for line in content:
match=re.search(r'^var\s+([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
if match is not None:
func_name=match.group(1)
comments=[]
param=match.group(2)
if(line.find(')')==-1 and line.find('(')!=-1):
k=i+1
while(content[k].find(')')==-1):
param+=content[k].rstrip('\n')
k+=1
param+=content[k].split(')')[0]
j=i-1
count=0
while ( j>i-35 and j>-1):
if count>3:
break
if len(content[j])<2:
j-=1
count+=1
continue
if re.search(r'^\s*#',content[j]) is not None:
comments.append(content[j].rstrip('\n'))
j-=1
else:
break
if(len(comments)>1):
comments.reverse()
retval.append((func_name, param,comments))
i+=1
continue
match3=re.search(r'^var\s*([A-Za-z0-9_-]+)\s*=\s*{\s*(\n|})',line)
if match3 is not None:
classname=match3.group(1)
comments=[]
j=i-1
count=0
while ( j>i-35 and j>-1):
if count>3:
break
if len(content[j])<2:
j-=1
count+=1
continue
if re.search(r'^\s*#',content[j]) is not None:
comments.append(content[j].rstrip('\n'))
j-=1
else:
break
if(len(comments)>1):
comments.reverse()
retval.append((classname+'.', '',comments))
i+=1
continue
match2=re.search(r'^\s*([A-Za-z0-9_-]+)\s*:\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
if match2 is not None:
func_name=match2.group(1)
comments=[]
param=match2.group(2)
if(line.find(')')==-1 and line.find('(')!=-1):
k=i+1
while(content[k].find(')')==-1):
param+=content[k].rstrip('\n')
k+=1
param+=content[k].split(')')[0]
j=i-1
count=0
while ( j>i-35 and j>-1):
if count>3:
break
if len(content[j])<2:
j-=1
count+=1
continue
if re.search(r'^\s*#',content[j]) is not None:
comments.append(content[j].rstrip('\n'))
j-=1
else:
break
if(len(comments)>1):
comments.reverse()
if classname =='':
continue
retval.append((classname+'.'+func_name, param,comments))
i+=1
continue
match4=re.search(r'^([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=\n.-]*)\)?',line)
if match4 is not None:
classname=match4.group(1)
func_name=match4.group(2)
comments=[]
param=match4.group(3)
if(line.find(')')==-1 and line.find('(')!=-1):
k=i+1
while(content[k].find(')')==-1):
param+=content[k].rstrip('\n')
k+=1
param+=content[k].split(')')[0]
j=i-1
count=0
while ( j>i-35 and j>-1):
if count>3:
break
if len(content[j])<2:
j-=1
count+=1
continue
if re.search(r'^\s*#',content[j]) is not None:
comments.append(content[j].rstrip('\n'))
j-=1
else:
break
if(len(comments)>1):
comments.reverse()
retval.append((classname+'.'+func_name, param,comments))
i+=1
continue
i+=1
return retval
if __name__ == "__main__":
if len(sys.argv) <2:
print('Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]')
sys.exit()
else:
if sys.argv[1]=='parse':
if len(sys.argv) <3:
nasal_path=NASAL_PATH
else:
nasal_path=sys.argv[2]
get_files(nasal_path)
else:
print('Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]')
sys.exit()

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env python3
'''
Replays a Flightgear recording and shows framerate statistics.
Usage:
-f <fgfs>
Name of Flightear executable/script, e.g.: -f run_fgfs.sh
-i <tape>
Name of recording, for use with --load-tape.
'''
import math
import sys
import time
import recordreplay
def average_stddev(items):
'''
Returns (average, stddev).
'''
total = 0
total_sq = 0
for item in items:
total += item
total_sq += item*item
n = len(items)
average = total / n
variance = total_sq / n - (total / n)**2
stddev = math.sqrt(variance)
return average, stddev
if __name__ == '__main__':
fgfs = None
tape = None
args = iter(sys.argv[1:])
while 1:
try:
arg = next(args)
except StopIteration:
break
if arg in ('-h', '--help'):
print(__doc__)
elif arg == '-f':
fgfs = next(args)
elif arg == '-i':
tape = next(args)
else:
raise Exception(f'Unrecognised arg: {arg}')
if not fgfs:
raise Exception(f'Specify fgfs executable/run-script with -f')
if not tape:
raise Exception(f'Specify tape to replay with -i')
fg = recordreplay.Fg( None,
f'{fgfs}'
f' --load-tape={tape}'
f' --timeofday=noon'
f' --prop:bool:/sim/replay/log-frame-times=true'
f' --prop:bool:/sim/replay/replay-main-view=true'
f' --prop:bool:/sim/replay/replay-main-window-size=true'
f' --prop:bool:/sim/replay/looped=false'
)
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.waitfor('/sim/replay/replay-state', 1)
fg.waitfor('/sim/replay/replay-state-eof', 1, timeout=600)
print(f'Reading frame-time statistics...')
t = time.time()
items = fg.fg.ls('/sim/replay/log-frame-times')
t = time.time() - t
print(f'fg.fg.ls took t={t}')
fg.close()
dts = []
for item in items:
if item.name == 'dt':
dts.append(float(item.value))
def statistics_text(dts):
dt_average, dt_stddev = average_stddev(dts)
t_total = sum(dts)
return f'n={len(dts)} dt_average={dt_average} dt_stddev={dt_stddev} t_total={t_total} fps={len(dts)/t_total}'
print(f'-' * 40)
print(f'')
print(f'Overall frame time (dt) statistics:')
print(f' {statistics_text(dts)}')
print(f'Ignoring first 4 frames:')
print(f' {statistics_text(dts[4:])}')
print(f'')
print(f'Raw frame times:')
dts_text = ' '.join(map(lambda dt: f'{dt:.4f}', dts))
print(f'dts: {dts_text}')

847
scripts/python/recordreplay.py Executable file
View File

@@ -0,0 +1,847 @@
#!/usr/bin/env python3
'''
Test script for record/replay. Only tested on Unix.
E.g.:
./flightgear/scripts/python/recordreplay.py -f run_fgfs.sh
Args:
--all
Run all tests (this is default).
--continuous BOOLS
--extra-properties BOOLS
--it-max <it>
Set min iteration (inclusive) to run; useful when fixing tests and
retrying.
--it-min <it>
Set max iteration (exclusive) to run; useful when fixing tests and
retrying.
--main-view BOOLS
--multiplayer
-f <fgfs>
The fgfs executable to use. Default assumes the Walk build system.
--f-old <fgfs-old>
A second fgfs executable. If specified we run all tests twice, first
using <fgfs-old> to create the recording and <fgfs> to replay it,
second the other way round.
--test-motion
Checks that speed of aircraft on replay is not affected by frame rate.
We deliberately change frame rate while recording UFO moving at
constant speed.
--test-motion-mp
Checks that speed of MP on replay is not affected by frame rate.
We deliberately change frame rate while recording UFO moving at
constant speed.
BOOLS is comma-sparated list of 0 or 1, with 1 activating the particular
feature. So for example '--continuous 0' tests normal recording/replay',
'--continuous 1' tests continuous recording/replay, and continuous 0,1'
tests both.
We test all combinations of continuous, extra-properties, main-view and
multiplayer recordings. For each test we check that we can create a
recording, and replay it in a new fgfs instance. When replaying we check
a small number of basic things such as the recording length, and whether
extra-properties are replayed.
'''
import os
import signal
import subprocess
import sys
import time
try:
import resource
except Exception:
# We don't mind if 'resource' module is not available, e.g. on Windows.
resource = None
import FlightGear
def log(text):
print(text, file=sys.stderr)
sys.stderr.flush()
g_cleanup = []
g_tapedir = './recordreplay.py.tapes'
def remove(path):
'''
Removes file, ignoring any error.
'''
log(f'Removing: {path}')
try:
os.remove(path)
except Exception as e:
log(f'Failed to remove {path}: {e}')
def readlink(path):
'''
Returns absolute path destination of link.
'''
ret = os.readlink(path)
if not os.path.isabs(ret):
ret = os.path.join(os.path.dirname(path), ret)
return ret
class Fg:
'''
Runs flightgear, with support for setting/getting properties etc.
self.fg is a FlightGear.FlightGear instance, which uses telnet to
communicate with Flightgear.
'''
def __init__(self, aircraft, args, env=None, telnet_port=None, telnet_hz=None, out=None, screensaver_suspend=True):
'''
aircraft:
Specified as: --aircraft={aircraft}. This is separate from <args>
because we need to know the name of recording files.
args:
Miscellenous args either space-separated name=value strings or a
dict.
env:
Environment to set. If DISPLAY is not in <env> we add 'DISPLAY=:0'.
telnet_port:
telnet_hz:
.
'''
self.child = None
self.aircraft = aircraft
if aircraft:
args += f' --aircraft={aircraft}'
if telnet_port is None:
telnet_port = 5500
if telnet_hz is None:
args += f' --telnet={telnet_port}'
else:
args += f' --telnet=_,_,{telnet_hz},_,{telnet_port},_'
args += f' --prop:/sim/replay/tape-directory={g_tapedir}'
args += f' --prop:bool:/sim/startup/screensaver-suspend={"true" if screensaver_suspend else "false"}'
args2 = args.split()
environ = os.environ.copy()
if isinstance(env, str):
for nv in env.split():
n, v = nv.split('=', 1)
environ[n] = v
if 'DISPLAY' not in environ:
environ['DISPLAY'] = ':0'
# Run flightgear in new process, telling it to open telnet server.
#
# We run not in a shell, otherwise self.child.terminate() doesn't
# work - it would kill the shell but leave fgfs running (there are
# workarounds for this, such as prefixing the command with 'exec').
#
log(f'Command is: {args}')
log(f'Running: {args2}')
if resource:
def preexec():
try:
resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
except Exception as e:
log(f'*** preexec failed with e={e}')
raise
else:
preexec = None
if out:
out = open(out, 'w')
self.child = subprocess.Popen(
args2,
env=environ,
preexec_fn=preexec,
stdout=out,
stderr=subprocess.STDOUT,
)
# Connect to flightgear's telnet server.
timeout = 15
t0 = time.time()
while 1:
time.sleep(1)
dt = time.time() - t0
if dt > timeout:
text = f'Timeout trying to connect. timeout={timeout}'
log(text)
raise Exception(text)
try:
log('Connecting... ')
self.fg = FlightGear.FlightGear('localhost', telnet_port)
log(f'Connected. timeout={timeout} dt={dt:.1f}')
return
except Exception as e:
log(f'Failed to connect timeout={timeout} dt={dt:.1f}: {e}')
def waitfor(self, name, value, timeout=30):
'''
Waits for specified property to be <value>. Returns time taken.
'''
t0 = time.time()
while 1:
time.sleep(1)
dt = time.time() - t0
try:
v = self.fg[name]
log(f'Waiting for {name}={value} current value: {v}. timeout={timeout} dt={dt:.1f}')
except Exception as e:
log(f'Failed to get value of property {name}: {e}. timeout={timeout} dt={dt:.1f}')
v = None
if v == value:
return dt
if dt > timeout:
raise Exception(f'Timeout waiting for {name}={value}; current value: {v}. timeout={timeout}')
def run_command(self, command):
self.fg.telnet._putcmd(command)
ret = self.fg.telnet._getresp()
log(f'command={command!r} ret: {ret}')
return ret
def close(self):
assert self.child
log(f'close(): stopping flightgear pid={self.child.pid}')
if 1:
# Kill any child processes so that things work if fgfs is being run
# by download_and_compile.sh's run_fgfs.sh script.
#
# This is Unix-only.
try:
child_pids = subprocess.check_output(f'pgrep -P {self.child.pid}', shell=True)
except Exception:
# We get here if self.child has no child processes.
child_pids = b''
child_pids = child_pids.decode('utf-8')
child_pids = child_pids.split()
for child_pid in child_pids:
#log(f'*** close() child_pid={child_pid}')
child_pid = int(child_pid)
#log(f'*** close() killing child_pid={child_pid}')
os.kill(child_pid, signal.SIGTERM)
self.child.terminate()
self.child.wait()
self.child = None
#log(f'*** close() returning.')
def __del__(self):
if self.child:
log('*** Fg.__del__() calling self.close()')
self.close()
def make_recording(
fg,
continuous=0, # 2 means continuous with compression.
extra_properties=0,
main_view=0,
length=5,
):
'''
Makes a recording, and returns its path.
We check that the recording file is newly created.
'''
t = time.time()
fg.fg['/sim/replay/record-signals'] = True # Just in case they are disabled by user.
if continuous:
assert not fg.fg['/sim/replay/record-continuous']
if continuous == 2:
fg.fg['/sim/replay/record-continuous-compression'] = 1
fg.fg['/sim/replay/record-continuous'] = 1
t0 = time.time()
while 1:
if time.time() > t0 + length:
break
time.sleep(1)
fg.run_command('run view-step step=1')
fg.fg['/sim/replay/record-continuous'] = 0
path = f'{g_tapedir}/{fg.aircraft}-continuous.fgtape'
time.sleep(1)
else:
# Normal recording will have effectively already started, so we sleep
# for the remaining time. This is a little inaccurate though because it
# looks like normal recording starts a little after t=0, e.g. at t=0.5.
#
# Also, it looks like /sim/time/elapsed-sec doesn't quite track real
# time, so we sometimes need to sleep a little longer.
#
while 1:
# Telnet interface seems very slow even if we set telnet_hz to
# 100 (for example). We want to make recording have near to the
# specified length, so we are cautious about overrunning.
#
#log(f'a: time.time()-t={time.time()-t}')
t_record_begin = fg.fg['sim/replay/record-normal-begin']
#log(f'b: time.time()-t={time.time()-t}')
t_record_end = fg.fg['sim/replay/record-normal-end']
#log(f'c: time.time()-t={time.time()-t}')
t_delta = t_record_end - t_record_begin
log(f't_record_begin={t_record_begin} t_record_end={t_record_end} t_delta={t_delta}')
if t_delta >= length:
break
ts = max(length - t_delta - 1, 0.2)
log(f'd: ts={ts}')
time.sleep(ts)
log(f'/sim/time/elapsed-sec={t}')
log(f'/sim/replay/start-time={fg.fg["/sim/replay/start-time"]}')
log(f'/sim/replay/end-time={fg.fg["/sim/replay/end-time"]}')
fg.fg.telnet._putcmd('run save-tape tape-data/starttime= tape-data/stoptime=')
response = fg.fg.telnet._getresp()
log(f'response: {response!r}')
path = f'{g_tapedir}/{fg.aircraft}.fgtape'
# Check recording is new.
os.system(f'ls -lL {path}')
s = os.stat(path, follow_symlinks=True)
assert s.st_mtime > t
path2 = readlink(path)
log(f'path={path} path2={path2}')
return path
def test_record_replay(
fgfs_save,
fgfs_load,
multiplayer,
continuous,
extra_properties,
main_view,
length,
):
if not fgfs_load:
fgfs_load = fgfs_save
log(f'=== save: {fgfs_save}')
log(f'=== load: {fgfs_load}')
log(f'=== --multiplayer {multiplayer} --continuous {continuous} --extra-properties {extra_properties} --main-view {main_view}')
log(f'===')
aircraft = 'harrier-gr3'
args = f'--state=vto --airport=egtk'
args += f' --prop:bool:/sim/replay/record-extra-properties={extra_properties}'
args += f' --prop:bool:/sim/replay/record-main-view={main_view}'
args += f' --prop:bool:/sim/replay/record-main-window=0'
#args += f' --prop:bool:/sim/time/simple-time/enabled=0'
# Start Flightgear.
fg = Fg(aircraft, f'{fgfs_save} {args}',
#env='SG_LOG_DELTAS=flightgear/src/Network/props.cxx=4',
telnet_hz=100,
)
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
assert fg.fg['sim/replay/record-extra-properties'] == extra_properties
assert fg.fg['sim/replay/record-main-view'] == main_view
log(f'sim/replay/record-extra-properties={fg.fg["sim/replay/record-extra-properties"]}')
# Save recording:
path = make_recording(fg,
continuous=continuous,
extra_properties=extra_properties,
main_view=main_view,
length=length,
)
g_cleanup.append(lambda: remove(path))
fg.close()
# Load recording into new Flightgear.
path = f'{g_tapedir}/{aircraft}-continuous.fgtape' if continuous else f'{g_tapedir}/{aircraft}.fgtape'
fg = Fg(aircraft, f'{fgfs_load} {args} --load-tape={path}')
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.waitfor('/sim/replay/replay-state', 1)
t0 = time.time()
# Check replay time is ok.
rtime_begin = fg.fg['/sim/replay/start-time']
rtime_end = fg.fg['/sim/replay/end-time']
rtime = rtime_end - rtime_begin
log(f'rtime={rtime_begin}..{rtime_end}, recording length: {rtime}, length={length}')
assert rtime > length-1 and rtime <= length+2, \
f'length={length} rtime_begin={rtime_begin} rtime_end={rtime_end} rtime={rtime}'
num_frames_extra_properties = fg.fg['/sim/replay/continuous-stats-num-frames-extra-properties']
log(f'num_frames_extra_properties={num_frames_extra_properties}')
if continuous:
if main_view:
assert num_frames_extra_properties > 1, f'num_frames_extra_properties={num_frames_extra_properties}'
else:
assert num_frames_extra_properties == 0
else:
assert num_frames_extra_properties in (0, None), \
f'num_frames_extra_properties={num_frames_extra_properties}'
fg.run_command('run dialog-show dialog-name=replay')
while 1:
t = time.time()
if t < t0 + length - 1:
pass
# Disabled because it seems that Flightgear starts replaying before
# we see replay-state set to 1 because scenery loading blocks
# things.
#
#assert not fg.fg['/sim/replay/replay-state-eof'], f'Replay has finished too early; lenth={length} t-t0={t-t0}'
if t > t0 + length + 1:
assert fg.fg['/sim/replay/replay-state-eof'], f'Replay has not finished on time; lenth={length} t-t0={t-t0}'
break
e = fg.fg['sim/replay/replay-error']
assert not e, f'Replay failed: e={e}'
time.sleep(1)
fg.close()
remove(path)
log('Test passed')
def test_motion(fgfs, multiplayer=False):
'''
Records UFO moving with constant velocity with varying framerates, then
replays with varying framerates and checks that replayed UFO moves with
expected constant speed.
If <multiplayer> is true we also record MP UFO running in second Flightgear
instance and check that it too moves at constant speed when replaying.
'''
log('')
log('='*80)
log('== Record')
aircraft = 'ufo'
fgfs += ' --prop:bool:/sim/time/simple-time/enabled=true'
if multiplayer:
fg = Fg( aircraft, f'{fgfs} --prop:/sim/replay/log-raw-speed-multiplayer=cgdae-t')
else:
fg = Fg( aircraft, f'{fgfs}')
path = f'{g_tapedir}/{fg.aircraft}-continuous.fgtape'
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.fg['/controls/engines/engine[0]/throttle'] = 0
# Throttle/speed for ufo is set in fgdata/Aircraft/ufo/ufo.nas.
#
speed_max = 2000 # default for ufo; current=7.
fixed_speed = 100
throttle = fixed_speed / speed_max
if multiplayer:
fg.fg['/sim/replay/record-multiplayer'] = True
fg2 = Fg( aircraft, f'{fgfs} --callsign=cgdae-t --multiplay=in,4,,5033 --read-only', telnet_port=5501)
fg2.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.fg['/controls/engines/engine[0]/throttle'] = throttle
fg2.fg['/controls/engines/engine[0]/throttle'] = throttle
time.sleep(1)
fgt = fg.fg['/controls/engines/engine[0]/throttle']
fg2t = fg2.fg['/controls/engines/engine[0]/throttle']
log(f'fgt={fgt} fg2t={fg2t}')
else:
fg.fg['/controls/engines/engine[0]/throttle'] = throttle
# Run UFO with constant speed, varying the framerate so we check whether
# recorded speeds are affected.
#
fg.fg['/sim/frame-rate-throttle-hz'] = 5
if multiplayer:
fg2.fg['/sim/frame-rate-throttle-hz'] = 5
# Delay to let frame rate settle.
time.sleep(10)
# Start recording.
fg.fg['/sim/replay/record-continuous'] = 1
time.sleep(5)
# Change frame rate.
fg.fg['/sim/frame-rate-throttle-hz'] = 2
time.sleep(5)
# Change frame rate.
fg.fg['/sim/frame-rate-throttle-hz'] = 5
if multiplayer:
fg2.fg['/sim/frame-rate-throttle-hz'] = 2
time.sleep(5)
# Stop recording.
fg.fg['/sim/replay/record-continuous'] = 0
fg.close()
if multiplayer:
fg2.close()
time.sleep(2)
path2 = readlink( path)
log(f'*** path={path} path2={path2}')
g_cleanup.append(lambda: remove(path2))
log('')
log('='*80)
log('== Replay')
if multiplayer:
fg = Fg( aircraft, f'{fgfs} --load-tape={path}'
f' --prop:/sim/replay/log-raw-speed-multiplayer=cgdae-t'
f' --prop:/sim/replay/log-raw-speed=true'
)
else:
fg = Fg( aircraft,
f'{fgfs} --load-tape={path} --prop:/sim/replay/log-raw-speed=true',
#env='SG_LOG_DELTAS=flightgear/src/Aircraft/flightrecorder.cxx:replay=3',
)
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.fg['/sim/frame-rate-throttle-hz'] = 10
fg.waitfor('/sim/replay/replay-state', 1)
time.sleep(3)
fg.fg['/sim/frame-rate-throttle-hz'] = 2
time.sleep(5)
fg.fg['/sim/frame-rate-throttle-hz'] = 5
time.sleep(3)
fg.fg['/sim/frame-rate-throttle-hz'] = 7
fg.waitfor('/sim/replay/replay-state-eof', 1)
errors = []
def examine_values(infix=''):
'''
Looks at /sim/replay/log-raw-speed{infix}-values/value[], which will
contain measured speed of user/MP UFO. We check that the values are all
as expected - constant speed.
'''
log(f'== Looking at /sim/replay/log-raw-speed{infix}-values/value[]')
items0 = fg.fg.ls( f'/sim/replay/log-raw-speed{infix}-values')
log(f'{infix} len(items0)={len(items0)}')
assert items0, f'Failed to read items in /sim/replay/log-raw-speed{infix}-values/'
items = []
descriptions = []
for item in items0:
if item.name == 'value':
#log(f'have read item: {item}')
items.append(item)
elif item.name == 'description':
descriptions.append(item)
num_errors = 0
for i in range(len(items)-1): # Ignore last item because replay at end interpolates.
item = items[i]
description = ''
if i < len(descriptions):
description = descriptions[i].value
speed = float(item.value)
prefix = ' '
if abs(speed - fixed_speed) > 0.1:
num_errors += 1
prefix = '*'
log( f' {infix} {prefix} speed={speed:12.4} details: {item}: {description}')
if num_errors != 0:
log( f'*** Replay showed uneven speed')
errors.append('1')
def show_values(paths):
if isinstance(paths, str):
paths = paths,
log(f'Values in {paths}:')
line2values = dict()
for i, path in enumerate(paths):
line = 0
for item in fg.fg.ls(path):
if item.name == 'value':
line2values.setdefault(line, []).append(item.value)
line += 1
for line in sorted(line2values.keys()):
t = ''
for value in line2values[line]:
t += f' {value}'
log(f' {t}')
if multiplayer:
examine_values()
examine_values('-multiplayer')
examine_values('-multiplayer-post')
if 0:
show_values('/sim/replay/log-raw-speed-multiplayer-post-relative-distance')
show_values('/sim/replay/log-raw-speed-multiplayer-post-relative-bearing')
show_values('/sim/replay/log-raw-speed-multiplayer-post-absolute-distance')
show_values('/sim/replay/log-raw-speed-multiplayer-post-user-absolute-distance')
def get_values(path):
'''
Returns <path>/value[] as a list.
'''
ret = []
for item in fg.fg.ls(path):
if item.name == 'value':
ret.append(item.value)
return ret
# Check that distance between user and mp is constant.
#
# The two paths below contain values[] that are the distances of the
# mp and user aircraft from their starting points. Both are moving at
# the same speed in the same direction, so the differences between each
# pair of values should be constant.
#
distances_mp = get_values('/sim/replay/log-raw-speed-multiplayer-post-absolute-distance')
distances_user = get_values('/sim/replay/log-raw-speed-multiplayer-post-user-absolute-distance')
log(f'len(distances_user)={len(distances_user)} len(distances_mp)={len(distances_mp)}')
assert len(distances_user) == len(distances_mp)
assert len(distances_user) > 20
for i in range(len(distances_user)):
distance_mp = distances_mp[i]
distance_user = distances_user[i]
delta = distance_mp - distance_user
if i == 0:
delta_original = delta
prefix = ' '
if abs(delta - delta_original) > 0.01:
#log('replay shows varying differences between user and mp aircraft')
errors.append('1')
prefix = '*'
log(f' {prefix} user={distance_user} mp={distance_mp} delta={delta}')
else:
examine_values()
fg.close()
if errors:
raise Exception('Failure')
log('test_motion() passed')
def test_carrier(fgfs):
'''
Checks that mp carrier motion is even.
'''
# We require simple-time. Can probably also work by setting the default
# timing system's lag parameters but haven't figured this out yet.
#
simple_time = 'true'
fg = Fg( 'harrier-gr3',
f'{fgfs} --prop:int:/sim/mp-carriers/latch-always=1 --prop:bool:/sim/time/simple-time/enabled={simple_time} --callsign=cgdae3 --airport=ksfo',
telnet_port=5500,
telnet_hz=100,
#out='out-rr-carrier-1',
)
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg_carrier = Fg('Nimitz',
f'{fgfs} --prop:int:/sim/mp-carriers/latch-always=1 --prop:bool:/sim/time/simple-time/enabled={simple_time} --callsign=cgdae4 --multiplay=in,1,,5033 --read-only',
telnet_port=5501,
#out='out-rr-carrier-2',
)
fg_carrier.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.fg['/sim/replay/log-raw-speed-multiplayer'] = 'cgdae4'
fg.fg['/sim/log-multiplayer-callsign'] = 'cgdae4'
def get_items(path, leafname, out=None):
'''
Finds list of tuples from properties <path>/<leafname>[]/*. Appends new
items to <out> and returns new items.
Runs rather slowly because telnet commands appear to be throttled.
'''
if out is None:
out = []
out_len_original = len(out)
items = fg.fg.ls(path)
i = 0
for item_i, item in enumerate(items):
if item.name == leafname:
if i == len(out):
#print(f'len(items)={len(items)} item_i={item_i}: looking at {path}/{leafname}[{item.index}]')
class Item:
pass
item2 = Item()
item2.i = i
for j in fg.fg.ls(f'{path}/{leafname}[{item.index}]'):
setattr( item2, j.name, j)
out.append(item2)
i += 1
return out[out_len_original:]
t0 = time.time()
mps = []
mppackets = []
while 1:
time.sleep(1)
t = time.time() - t0
log(f'test_carrier(): t={t:.1f}')
if t > 60:
print(f'finished, t={t}')
break
mps_new = get_items( '/sim/log-multiplayer', 'mp', mps)
mppackets_new = get_items( '/sim/log-multiplayer', 'mppacket', mppackets)
for mp in mps_new:
log(f'test_carrier(): mp: i={mp.i}:'
f' speed={mp.speed.value:20}'
f' distance={mp.distance.value:20}'
f' t={mp.t.value:20}'
f' dt={mp.dt.value:20}={mp.dt.value*120:20}/120'
f' ubody={mp.ubody.value:20}'
f' vbody={mp.vbody.value:20}'
f' wbody={mp.wbody.value:20}'
)
for mppacket in mppackets_new:
log(f'test_carrier(): mppacket: i={mppacket.i}:'
f' speed={mppacket.speed.value:20}'
f' distance={mppacket.distance.value:20}'
f' t={mppacket.t.value:20}'
f' linear_vel={mppacket.linear_vel.value:20}'
f' dt={mppacket.dt.value:20}={mppacket.dt.value*120:20}/120'
)
# Check speed of multiplayer carrier is constant:
knots2si = 1852.0/3600
speed_expected = 10 * knots2si
num_incorrect = 0
for mp in mps[2:]: # First two items have bogus values.
delta = mp.speed.value - speed_expected
if abs(delta) > 0.001:
num_incorrect += 1
print(f' * speed={mp.speed.value:20}')
assert num_incorrect == 0, f'num_incorrect={num_incorrect}'
fg.close()
fg_carrier.close()
if __name__ == '__main__':
fgfs = f'./build-walk/fgfs.exe-run.sh'
fgfs_old = None
do_test = 'all'
continuous_s = [0, 1, 2] # 2 is continuous with compression.
extra_properties_s = [0, 1]
main_view_s = [0, 1]
multiplayer_s = [0, 1]
fgfs_reverse_s = [0]
it_min = None
it_max = None
if len(sys.argv) == 1:
do_all = True
args = iter(sys.argv[1:])
while 1:
try:
arg = next(args)
except StopIteration:
break
if arg == '--all':
do_all = True
elif arg == '--carrier':
do_test = 'carrier'
elif arg == '--continuous':
continuous_s = [int(x) for x in next(args).split(',')]
log(f'continuous_s={continuous_s}')
elif arg == '--tape-dir':
g_tapedir = next(args)
elif arg == '--extra-properties':
extra_properties_s = [int(x) for x in next(args).split(',')]
elif arg == '--it-max':
it_max = int(next(args))
elif arg == '--it-min':
it_min = int(next(args))
elif arg == '--main-view':
main_view_s = [int(x) for x in next(args).split(',')]
elif arg == '--multiplayer':
multiplayer_s = [int(x) for x in next(args).split(',')]
elif arg == '-f':
fgfs = next(args)
elif arg == '--f-old':
fgfs_old = next(args)
fgfs_reverse = [0, 1]
elif arg == '--test-motion':
do_test = 'motion'
elif arg == '--test-motion-mp':
do_test = 'motion-mp'
else:
raise Exception(f'Unrecognised arg: {arg!r}')
g_tapedir = os.path.abspath(g_tapedir)
os.makedirs( g_tapedir, exist_ok=True)
if 0:
pass
elif do_test == 'carrier':
test_carrier(fgfs)
elif do_test == 'motion':
test_motion( fgfs)
elif do_test == 'motion-mp':
test_motion( fgfs, True)
elif do_test == 'all':
try:
if fgfs_old:
for fgfs1, fgfs2 in [(fgfs, fgfs_old), (fgfs_old, fgfs)]:
for multiplayer in 0, 1:
test_record_replay(
fgfs1,
fgfs2,
multiplayer,
continuous=0,
extra_properties=0,
main_view=0,
length=10,
)
else:
log(f'continuous_s={continuous_s}')
its_max = len(multiplayer_s) * len(continuous_s) * len(extra_properties_s) * len(main_view_s) * len(fgfs_reverse_s)
it = 0
for multiplayer in multiplayer_s:
for continuous in continuous_s:
for extra_properties in extra_properties_s:
for main_view in main_view_s:
for fgfs_reverse in fgfs_reverse_s:
if fgfs_reverse:
fgfs_save = fgfs_old
fgfs_load = fgfs
else:
fgfs_save = fgfs
fgfs_load = fgfs_old
ok = True
if it_min is not None:
if it < it_min:
ok = False
if it_max is not None:
if it >= it_max:
ok = False
log('')
log(f'===')
log(f'=== {it}/{its_max}')
if ok:
test_record_replay(
fgfs_save,
fgfs_load,
multiplayer=multiplayer,
continuous=continuous,
extra_properties=extra_properties,
main_view=main_view,
length=10
)
it += 1
finally:
pass
else:
assert 0, f'do_test={do_test}'
# If everything passed, cleanup. Otherwise leave recordings in place, as
# they can be useful for debugging.
#
for f in g_cleanup:
try:
f()
except Exception:
pass
log(f'{__file__}: Returning 0')

165
scripts/python/video.py Executable file
View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
'''
Test script for video encoding.
Example usage:
scripts/python/video.py -f dac/run_fgfs.sh
'''
import recordreplay
import os
import sys
import time
def main():
fgfs = f'./build-walk/fgfs.exe-run.sh'
args = iter(sys.argv[1:])
while 1:
try:
arg = next(args)
except StopIteration:
break
if arg == '-f':
fgfs = next(args)
else:
raise Exception(f'Unrecognised arg: {arg}')
fg = recordreplay.Fg(
'harrier-gr3',
f'{fgfs}'
+ f' --state=vto --airport=egtk'
+ f' --prop:/sim/replay/record-main-view=1'
+ f' --prop:bool:/sim/replay/record-main-window=0'
,
)
fg.waitfor('/sim/fdm-initialized', 1, timeout=45)
fg.fg['/sim/current-view/view-number-raw'] = 1 # helicopter
# Rotation speed.
fg.fg['/controls/auto-hover/rotation-speed-target'] = 4
fg.fg['/controls/auto-hover/rotation-mode'] = 'speed'
# These will have been set by --state=vto
# Sideways speed.
fg.fg['/controls/auto-hover/x-speed-target'] = '0'
fg.fg['/controls/auto-hover/x-mode'] = 'speed'
# Vertical speed.
fg.fg['/controls/auto-hover/y-speed-target'] = '0'
fg.fg['/controls/auto-hover/y-mode'] = 'speed'
# Forwards speed.
fg.fg['/controls/auto-hover/z-speed-target'] = '0'
fg.fg['/controls/auto-hover/z-mode'] = 'speed'
results = []
def make_recording(codec, container, quality, speed, fixed_dt=None):
'''
Create recording using specified codec etc.
'''
fg.fg['/sim/video/container'] = container
fdm_time_begin = fg.fg['/sim/time/simple-time/fdm']
if fixed_dt:
fg.fg['/sim/time/simple-time/enabled'] = True
fg.fg['/sim/time/fixed-dt'] = fixed_dt
name = f'video-test-c={codec}-q={quality}-s={speed}.{container}'
frames_start = fg.fg['/sim/frame-number']
fg.run_command( f'run video-start name={name} quality={quality} speed={speed} codec={codec}')
dt = 10
time.sleep(dt)
frames = fg.fg['/sim/frame-number'] - frames_start
fdm_time_end = fg.fg['/sim/time/simple-time/fdm']
fg.run_command( f'run video-stop')
fdm_time = fdm_time_end - fdm_time_begin
e = fg.fg['/sim/video/error']
e = f'error e={e:3}' if e else 'success'
e = f'{e:12}'
if not e:
if not os.path.isfile(name):
e = 'error no output file'
result = f'Video encoding result: {e}: codec={codec:12} container={container:6} quality={quality:6} speed={speed:6} frames={frames:6} frame_rate={frames/dt:6} fdm_time={fdm_time:6} size={os.path.getsize(name):9}: {name}'
results.append( result)
print( result)
if 1:
# Create Continuous recording, replay and create video, check video is
# new.
# Create Continuous recording.
video_suffix = 'mkv'
tstart = time.time()
fg.fg['/sim/video/container'] = video_suffix
fg.fg['/sim/video/codec'] = 'libx265'
fg.fg['/sim/video/quality'] = 0.75
fg.fg['/sim/video/speed'] = 1.0
fg.fg['/sim/replay/record-continuous-compression'] = 1
fg.fg['/sim/replay/record-continuous'] = 1
fg.fg['/sim/replay/record-main-view'] = 1
endtime = tstart + 10
while 1:
if time.time() > endtime:
break
time.sleep(1)
fg.run_command('run view-step step=1')
fg.fg['/sim/replay/record-continuous'] = 0
# Replay to create video.
tstart = time.time()
fg.fg['sim/replay/replay-main-view'] = 1
fg.fg['sim/replay/replay-windows-position'] = 0
fg.fg['sim/replay/replay-windows-size'] = 0
fg.fg['/sim/video/container'] = 'mkv'
fg.fg['/sim/video/codec'] = 'libx265'
fg.fg['/sim/video/quality'] = 0.75
fg.fg['/sim/video/speed'] = 1.0
fg.run_command( f'run load-tape tape={fg.aircraft}-continuous create-video=1 fixed-dt=0.04')
fg.waitfor('/sim/replay/replay-state', 1) # Wait for replay to start.
fg.waitfor('/sim/replay/replay-state-eof', 1) # Wait for replay eof.
# Check video looks ok.
video_path = f'fgvideo-harrier-gr3.{video_suffix}'
video_path2 = recordreplay.readlink( video_path)
print(f'*** video_path={video_path} video_path2={video_path2}')
t = os.path.getmtime(video_path2)
assert t > tstart, f'Video file too old: {video_path2}'
elif 1:
make_recording('libtheora', 'ogv', quality=1, speed=1, fixed_dt=0.02)
make_recording('libx265', 'mkv', quality=1, speed=1, fixed_dt=0.02)
else:
for codec in (
'libtheora',
'libx265',
'mpeg2video',
'libx264',
'libvpx',
):
fg.fg['/sim/video/codec'] = codec
for container in (
'mpeg',
'ogv',
'mkv',
):
# High quality can semi-freeze Flightgear.
for quality, speed in [
('0.4', '0.5'),
('0.4', '0.9'),
('0.1', '0.5'),
('0.1', '0.9'),
]:
# This ordering should give increasing frame rate.
make_recording(codec, container, quality, speed)
print('Results:')
for result in results:
print( f' {result}')
if __name__ == '__main__':
main()

62
scripts/syntax/ac3d.vim Normal file
View File

@@ -0,0 +1,62 @@
if !exists("main_syntax")
if version < 600
syntax clear
" elseif exists("b:current_syntax")
" finish
endif
let main_syntax = 'ac3d'
endif
"setlocal iskeyword=46,95,97-122
syn keyword ac3dIdentifier AC3Db
syn region ac3dMaterial start=+^MATERIAL+ end=+$+ contains=ac3dSTringS,ac3dStringD,ac3dMatKeyword
syn match ac3dError display +^OBJECT+
syn match ac3dObject display +^OBJECT\s\+\(world\|group\|poly\)\s*$+
syn match ac3dMaterial display +^SURF.*+
syn region ac3dStringS start=+'+ end=+'+
syn region ac3dStringD start=+"+ end=+"+
syn match ac3dFunction display +^\(crease\|mat\|texture\|texrep\|texoff\|url\|data\|refs\)+
syn match ac3dFunction display +^\(numvert\|numsurf\|kids\|name\|SURF\|loc\)+
syn keyword ac3dMatKeyword MATERIAL rgb amb emis spec shi trans
" Define the default highlighting.
" For version 5.7 and earlier: only when not done already
" For version 5.8 and later: only when an item doesn't have highlighting yet
if version >= 508 || !exists("did_ac3d_syn_inits")
if version < 508
let did_ac3d_syn_inits = 1
command -nargs=+ HiLink hi link <args>
else
command -nargs=+ HiLink hi def link <args>
endif
HiLink ac3dMatKeyword Statement
HiLink ac3dStringS String
HiLink ac3dStringD String
HiLink ac3dIdentifier Identifier
HiLink ac3dObject Identifier
HiLink ac3dFunction Function
HiLink ac3dComment Comment
HiLink ac3dSpecial Special
HiLink ac3dCharacter Character
HiLink ac3dNumber Number
HiLink ac3dFloat Float
HiLink ac3dIdentifier Identifier
HiLink ac3dConditional Conditional
HiLink ac3dRepeat Repeat
HiLink ac3dOperator Operator
HiLink ac3dType Type
HiLink ac3dError Error
HiLink ac3dBoolean Boolean
delcommand HiLink
endif
let b:current_syntax = "ac3d"
if main_syntax == 'ac3d'
unlet main_syntax
endif
" vim: ts=8

View File

@@ -0,0 +1,870 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
gedit syntax highlighter for the Nasal scripting language.
Just drop it in the gtksourceview-X.X/lanugage-specs folder:
Linux: /usr/share/gtksourceview-X.X/language-specs/
Mac: Applications/gedit/Contents/Resources/share/gtksourceview-X.X/language specs
Copyright (C) 2013 Philosopher
Author: Philosopher (Flightgear forums)
http://forum.flightgear.org/viewtopic.php?f=30&t=17265#p164133
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
-->
<language id="nasal" _name="Nasal" version="2.0" _section="Scripts">
<metadata>
<property name="globs">*.nas</property>
<property name="line-comment-start">#</property>
</metadata>
<styles>
<style id="module-handler" _name="Module Handler" map-to="def:preprocessor"/>
<style id="keyword" _name="Keyword" map-to="def:keyword"/>
<style id="blockoid" _name="Blockoid" map-to="def:keyword"/>
<style id="operator" _name="Operator" map-to="def:operator"/>
<style id="builtin-constant" _name="Builtin Constant" map-to="def:special-constant"/>
<style id="builtin-object" _name="Builtin Object" map-to="def:type"/>
<style id="node-object" _name="Flightgear Node" map-to="def:type"/>
<style id="builtin-function" _name="Builtin Function" map-to="def:builtin"/>
<style id="boolean" _name="Boolean" map-to="def:boolean"/>
<style id="floating-point" _name="Floating point number" map-to="def:floating-point"/>
<style id="decimal" _name="Decimal number" map-to="def:decimal"/>
<style id="base-n-integer" _name="Base-N number" map-to="def:base-n-integer"/>
<style id="special-variable" _name="Special Variable" map-to="def:preprocessor"/><!-- def:identifier -->
<style id="string-conversion" _name="String conversion numeric"/>
<style id="string" _name="String" map-to="def:string"/>
<style id="escaped-char" _name="Escaped Character" map-to="def:special-char"/>
<style id="format" _name="Format" map-to="def:special-char"/>
<style id="path" _name="Path" map-to="def:type"/>
<style id="types" _name="Type Object" map-to="def:type"/>
</styles>
<definitions>
<!-- Basic lexing constructs: -->
<define-regex id="identifier">[_a-zA-Z][_a-zA-Z0-9]*</define-regex>
<define-regex id="number">[1-9][0-9]*</define-regex>
<context id="number" style-ref="decimal">
<match>(?&lt;![0-9a-zA-Z])(0x[0-9a-fA-F]+|([0-9]*\.)?[0-9]+([eE][-+]?[0-9]+)?)</match>
</context>
<!-- Builtin Nasal keyword-operators -->
<context id="keyword" style-ref="keyword">
<keyword>and</keyword>
<keyword>or</keyword>
<keyword>var</keyword>
<keyword>return</keyword>
<keyword>break(\s+[_a-zA-Z]\w*)?</keyword>
<keyword>continue(\s+[_a-zA-Z]\w*)?</keyword>
</context>
<!-- Builtin Nasal blockoids -->
<context id="blockoid" style-ref="blockoid">
<keyword>while</keyword>
<keyword>for</keyword>
<keyword>foreach</keyword>
<keyword>forindex</keyword>
<keyword>if</keyword>
<keyword>elsif</keyword>
<keyword>else</keyword>
<keyword>func</keyword>
</context>
<context id="builtin-constants" style-ref="builtin-constant">
<prefix>(?&lt;![\w\.])</prefix> <!-- look behind for anything but a word or period as a prefix, e.g. a space or tab -->
<keyword>nil</keyword>
<keyword>math\.e</keyword>
<keyword>math\.pi</keyword>
<!-- Flightgear global constants from globals.nas -->
<keyword>D2R</keyword>
<keyword>R2D</keyword>
<keyword>FT2M</keyword>
<keyword>M2FT</keyword>
<keyword>IN2M</keyword>
<keyword>M2IN</keyword>
<keyword>NM2M</keyword>
<keyword>M2NM</keyword>
<keyword>KT2MPS</keyword>
<keyword>MPS2KT</keyword>
<keyword>LB2KG</keyword>
<keyword>KG2LB</keyword>
<keyword>GAL2L</keyword>
<keyword>L2GAL</keyword>
<!-- Non-official -->
<keyword>FG_ROOT</keyword>
<keyword>FG_HOME</keyword>
<keyword>MODEL_PATH</keyword>
</context>
<!-- Other operators -->
<context id="operators" style-ref="operator" extend-parent="false">
<match>[-~+*/!=?&lt;&gt;]</match>
</context>
<!-- Strings: -->
<context id="format" style-ref="format" extend-parent="false">
<match extended="true">
% # leading % sign
[#0\-\ \+]* # conversion flags
(\-?\%{number})? # minimum field width
(\.(\-?\%{number}))? # precision
[%sdicouxXeEfFgG] # conversion type
</match>
</context>
<context id="escaped-char" style-ref="escaped-char" extend-parent="true">
<match extended="true">
\\( # leading backslash
[\\"abfnrtv] | # single escaped char
N\{[A-Z\ ]+\} | # named unicode character
u[0-9A-Fa-f]{4} | # xxxx - character with 16-bit hex value xxxx
U[0-9A-Fa-f]{8} | # xxxxxxxx - character with 32-bit hex value xxxxxxxx
x[0-9A-Fa-f]{1,2} | # \xhh - character with hex value hh
[0-7]{1,3} # \ooo - character with octal value ooo
)
</match>
</context>
<context id="double-escaped-char" style-ref="escaped-char" extend-parent="true">
<match extended="true">
\\\\( # double leading backslash
\\" | # escaped quote (needs an extra backslash)
[\\abfnrtv] | # single escaped char
N\{[A-Z\ ]+\} | # named unicode character
u[0-9A-Fa-f]{4} | # xxxx - character with 16-bit hex value xxxx
U[0-9A-Fa-f]{8} | # xxxxxxxx - character with 32-bit hex value xxxxxxxx
x[0-9A-Fa-f]{1,2} | # \xhh - character with hex value hh
[0-7]{1,3} # \ooo - character with octal value ooo
)
</match>
</context>
<context id="object-types" style-ref="builtin-object">
<prefix>(?&lt;=['"])</prefix> <!-- look behind for ' or " as a prefix -->
<suffix>(?=['"])</suffix> <!-- look ahead for ' or " as a suffix -->
<keyword>func</keyword>
<keyword>code</keyword><!-- not official -->
<keyword>hash</keyword>
<keyword>scalar</keyword>
<keyword>vector</keyword>
<keyword>nil</keyword>
<keyword>ghost</keyword>
<!-- GitHub/AndyRoss/Nasal or Gitorious/nasal-standalone -->
<keyword>iofile</keyword>
<keyword>cairo</keyword>
<keyword>cairo_surface</keyword>
<keyword>GObject</keyword>
<keyword>regex</keyword>
<keyword>sqlite_db</keyword>
<keyword>sqlite_statement</keyword>
<keyword>dir</keyword>
<!-- SimGear -->
<keyword>prop</keyword>
<!-- FlightGear -->
<keyword>positioned</keyword>
<keyword>airport</keyword>
<keyword>aircraft</keyword>
<keyword>helipad</keyword>
</context>
<define-regex id="property-path" extended="true">
( # optional first (root) node
[a-zA-Z] # first character has to be an alpha
[-a-zA-Z0-9_]* # now it can be alpha, hyphen, or number, repeat this
(?:\[ # optional index specifier like this: [90]
(?:[0-9]+)
\])?
)?
/ # require a slash
( # middle node(s)
[a-zA-Z] # first character has to be an alpha
[-a-zA-Z0-9_]* # now it can be alpha, hyphen, or number, repeat this
(?:\[ # optional index specifier like this: [90]
(?:[0-9]+)
\])?
/ # slash to separate nodes
)* # repeat or none
( # last element, doesn't require a slash at the end
[a-zA-Z] # first character has to be an alpha
[-a-zA-Z0-9_]* # now it can be alpha, hyphen, or number, repeat this
(?:\[ # optional index specifier like this: [90]
(?:[0-9]+)
\])?
/?
)
</define-regex>
<define-regex id="system-path" extended="true">
[-a-zA-Z0-9_]* # valid characters, repeat this
/ # require a slash
[-a-zA-Z0-9/_]+ # more valid characters, repeat this once or more
(.[a-zA-Z0-9]+)? # optional extension
</define-regex>
<context id="string-path" style-ref="path" once-only="true">
<match extended="true">
(?&lt;=")
(?:
(\]/)?\%{property-path}\[? # a recognizeable property path
|((?:\]/)? # or a "simple" path that isn't covered by above
[a-zA-Z] # first character has to be an alpha
[-a-zA-Z0-9_]* # now it can be alpha, hyphen, or number, repeat this
(\[ # optional index specifier like this: [90]
(?:[0-9]+\]/?)? # this is the rest after the opening; optional, "node/foo[" is valid
|/ # or a slash if no opening: sim/
)
)
|\[|\]/? # or a single bracket (closing can have slash): "]", "[", or "]/"
|(?&lt;=prop\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[? # or a word like in setprop("foo-bar");
|(?&lt;=\.getNode\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[? # or a word like in props.globals.getNode("foo-bar["~index~"]");
|(?&lt;=\.alias\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[? # etc.
|(?&lt;=\.initNode\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.getValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.getBoolValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.setValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.setBoolValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.setIntValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.setDoubleValue\(")[-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|(?&lt;=\.getChild\(") [-a-zA-Z0-9_]+ # while these are simple names
|(?&lt;=\.getChildren\(") [-a-zA-Z0-9_]+
|(?&lt;=\.removeChild\(") [-a-zA-Z0-9_]+
|(?&lt;=\.removeChildren\(")[-a-zA-Z0-9_]+
)
(?=")
</match>
</context>
<context id="double-quoted-string" style-ref="string" class="string" class-disabled="no-spell-check">
<start>"</start>
<end>"</end>
<include>
<context ref="format"/>
<context ref="escaped-char"/>
<context ref="string-path"/>
</include>
</context>
<context id="single-quoted-string" style-ref="string" class="string" class-disabled="no-spell-check">
<start>'</start>
<end>'</end>
<include>
<context ref="format"/>
<context id="escaped-single-quote" style-ref="escaped-char" extend-parent="true">
<match>\\'</match>
</context>
<context ref="object-types"/>
</include>
</context>
<context id="escaped-double-quoted-string" style-ref="string" class="string" class-disabled="no-spell-check">
<start>\\"</start>
<end>\\"</end>
<include>
<context ref="format"/>
<context ref="double-escaped-char"/>
<context ref="string-path"/>
</include>
</context>
<context id="escaped-single-quoted-string" style-ref="string" class="string" class-disabled="no-spell-check">
<start>\\'</start>
<end>\\'</end>
<include>
<context ref="format"/>
<context id="double-escaped-single-quote" style-ref="escaped-char" extend-parent="true">
<match>\\\\'</match>
</context>
<context ref="object-types"/>
</include>
</context>
<context id="string-conversion" style-ref="string-conversion">
<match extended="true">
`
(
[^`\\] # any single character except for backqote or backslash
| # or:
\\( # leading backslash
[\\`abfnrtv] | # single escaped char
N\{[A-Z\ ]+\} | # named unicode character
u[0-9A-Fa-f]{4} | # xxxx - character with 16-bit hex value xxxx
U[0-9A-Fa-f]{8} | # xxxxxxxx - character with 32-bit hex value xxxxxxxx
x[0-9A-Fa-f]{1,2} | # \xhh - character with hex value hh
[0-7]{1,3} # \ooo - character with octal value ooo
)
)
`
</match>
</context>
<!-- Variables (special, chosen between FlightGear/Nasal-Standalone) -->
<context id="special-variables" style-ref="special-variable">
<prefix>(?&lt;![\w\.])</prefix>
<keyword>_?cmdarg</keyword>
<keyword>arg</keyword>
<keyword>me</keyword>
<keyword>parents</keyword>
<keyword>globals</keyword>
</context>
<context id="module-handler" style-ref="module-handler">
<prefix>(?&lt;![\w\.])</prefix>
<!-- Nasal-standalone/lib/driver.nas -->
<keyword>import</keyword>
<!-- $FG_ROOT/nasal_boostrap.nas -->
<keyword>require</keyword>
</context>
<context id="builtin-function" style-ref="builtin-function">
<prefix>((?&lt;![\w\.])|(?&lt;=globals\.))</prefix> <!-- look behind for anything but a word or period as a prefix, e.g. a space or tab, or lookbehind for globals. (since it will result in the same path) -->
<!-- Builtin to Nasal -->
<keyword>append</keyword>
<keyword>setsize</keyword>
<keyword>subvec</keyword>
<keyword>contains</keyword>
<keyword>delete</keyword>
<keyword>num</keyword>
<keyword>keys</keyword>
<keyword>pop</keyword>
<keyword>size</keyword>
<keyword>streq</keyword>
<keyword>cmp</keyword>
<keyword>abs</keyword>
<keyword>chr</keyword>
<keyword>sort</keyword>
<keyword>substr</keyword>
<keyword>sprintf</keyword>
<keyword>find</keyword>
<keyword>split</keyword>
<keyword>rand</keyword>
<keyword>typeof</keyword>
<keyword>die</keyword>
<keyword>call</keyword>
<keyword>compile</keyword>
<keyword>closure</keyword>
<keyword>caller</keyword>
<keyword>bind</keyword>
<keyword>print</keyword>
<keyword>bits\.fld</keyword>
<keyword>bits\.sfld</keyword>
<keyword>bits\.setfld</keyword>
<keyword>bits\.buf</keyword>
<keyword>math\.sin</keyword>
<keyword>math\.cos</keyword>
<keyword>math\.exp</keyword>
<keyword>math\.ln</keyword>
<keyword>math\.sqrt</keyword>
<keyword>math\.atan2</keyword>
<keyword>io\.open</keyword>
<keyword>io\.close</keyword>
<keyword>io\.read</keyword>
<keyword>io\.write</keyword>
<keyword>io\.seek</keyword>
<keyword>io\.tell</keyword>
<keyword>io\.readln</keyword>
<keyword>io\.stat</keyword>
<keyword>utf8\.chstr</keyword>
<keyword>utf8\.strc</keyword>
<keyword>utf8\.substr</keyword>
<keyword>utf8\.size</keyword>
<keyword>utf8\.validate</keyword>
<keyword>thread\.newthread</keyword>
<keyword>thread\.newlock</keyword>
<keyword>thread\.lock</keyword>
<keyword>thread\.unlock</keyword>
<keyword>thread\.newsem</keyword>
<keyword>thread\.semdown</keyword>
<keyword>thread\.semup</keyword>
<keyword>unix\.pipe</keyword>
<keyword>unix\.fork</keyword>
<keyword>unix\.dup2</keyword>
<keyword>unix\.exec</keyword>
<keyword>unix\.waitpid</keyword>
<keyword>unix\.opendir</keyword>
<keyword>unix\.readdir</keyword>
<keyword>unix\.closedir</keyword>
<keyword>unix\.time</keyword>
<keyword>unix\.chdir</keyword>
<keyword>unix\.environ</keyword>
<keyword>regex\.comp</keyword>
<keyword>regex\.exec</keyword>
<keyword>sqlite\.open</keyword>
<keyword>sqlite\.close</keyword>
<keyword>sqlite\.prepare</keyword>
<keyword>sqlite\.finalize</keyword>
<keyword>readline</keyword>
<!-- Flightgear-specific -->
<!-- $FG_SRC/NasalSys.cxx -->
<keyword>getprop</keyword>
<keyword>setprop</keyword>
<keyword>print</keyword>
<keyword>logprint</keyword>
<keyword>_fgcommand</keyword>
<keyword>settimer</keyword>
<keyword>maketimer</keyword>
<keyword>_setlistener</keyword>
<keyword>removelistener</keyword>
<keyword>addcommand</keyword>
<keyword>removecommand</keyword>
<keyword>_interpolate</keyword>
<keyword>rand</keyword>
<keyword>srand</keyword>
<keyword>abort</keyword>
<keyword>directory</keyword>
<keyword>resolvepath</keyword>
<keyword>parsexml</keyword>
<keyword>systime</keyword>
<!-- globals.nas -->
<keyword>setlistener</keyword>
<keyword>fgcommand</keyword>
<keyword>interpolate</keyword>
<keyword>isa</keyword>
<keyword>abs</keyword>
<keyword>defined</keyword>
<keyword>thisfunc</keyword>
<keyword>printf</keyword>
<keyword>sprintf</keyword>
<keyword>values</keyword>
<keyword>printlog</keyword>
<!-- io.nas -->
<keyword>io\.readfile</keyword>
<keyword>io\.load_nasal</keyword>
<keyword>io\.read_properties</keyword>
<keyword>io\.read_airport_properties</keyword>
<keyword>io\.write_properties</keyword>
<keyword>io\.readxml</keyword>
<keyword>io\.writexml</keyword>
<!-- string.nas -->
<keyword>string\.match</keyword>
<keyword>string\.normpath</keyword>
<keyword>string\.join</keyword>
<keyword>string\.replace</keyword>
<keyword>string\.iscntrl</keyword>
<keyword>string\.isascii</keyword>
<keyword>string\.isupper</keyword>
<keyword>string\.islower</keyword>
<keyword>string\.isdigit</keyword>
<keyword>string\.isblank</keyword>
<keyword>string\.ispunct</keyword>
<keyword>string\.isxdigit</keyword>
<keyword>string\.isspace</keyword>
<keyword>string\.isalpha</keyword>
<keyword>string\.isalnum</keyword>
<keyword>string\.isgraph</keyword>
<keyword>string\.isprint</keyword>
<keyword>string\.toupper</keyword>
<keyword>string\.tolower</keyword>
<keyword>string\.isxspace</keyword>
<keyword>string\.trim</keyword>
<keyword>string\.uc</keyword>
<keyword>string\.lc</keyword>
<keyword>string\.icmp</keyword>
<keyword>string\.imatch</keyword>
<keyword>string\.scanf</keyword>
<keyword>string\.setcolors</keyword>
<keyword>string\.color</keyword>
<!-- gui.nas -->
<keyword>gui\.Dialog\.new</keyword>
<keyword>gui\.OverlaySelector\.new</keyword>
<keyword>gui\.FileSelector\.new</keyword>
<keyword>gui\.DirSelector\.new</keyword>
<keyword>gui\.findElementByName</keyword>
<keyword>gui\.popupTip</keyword>
<keyword>gui\.showDialog</keyword>
<keyword>gui\.showHelpDialog</keyword>
<keyword>gui\.menuEnable</keyword>
<keyword>gui\.menuBind</keyword>
<keyword>gui\.setCursor</keyword>
<keyword>gui\.save_flight</keyword>
<keyword>gui\.load_flight</keyword>
<keyword>gui\.set_screenshotdir</keyword>
<keyword>gui\.property_browser</keyword>
<keyword>gui\.dialog_apply</keyword>
<keyword>gui\.dialog_update</keyword>
<keyword>gui\.enable_widgets</keyword>
<!-- props.nas -->
<keyword>props\.Node\.new</keyword>
<keyword>props\.Node</keyword>
<keyword>props\.globals</keyword>
<keyword>props\.setAll</keyword>
<keyword>props\.wrap</keyword>
<keyword>props\.wrapNode</keyword>
<keyword>props\.copy</keyword>
<keyword>props\.dump</keyword>
<keyword>props\.nodeList</keyword>
<keyword>props\.condition</keyword>
<keyword>props\.runBinding</keyword>
<!-- nasal-props.cxx -->
<keyword>props\._new</keyword>
<keyword>props\._globals</keyword>
<keyword>props\._getNode</keyword>
<keyword>props\._getParent</keyword>
<keyword>props\._getChild</keyword>
<keyword>props\._getChildren</keyword>
<keyword>props\._removeChild</keyword>
<keyword>props\._removeChildren</keyword>
<keyword>props\._getAliasTarget</keyword>
<keyword>props\._getName</keyword>
<keyword>props\._getIndex</keyword>
<keyword>props\._getType</keyword>
<keyword>props\._getAttribute</keyword>
<keyword>props\._setAttribute</keyword>
<keyword>props\._getValue</keyword>
<keyword>props\._setValue</keyword>
<keyword>props\._setIntValue</keyword>
<keyword>props\._setBoolValue</keyword>
<keyword>props\._setDoubleValue</keyword>
<keyword>props\._unalias</keyword>
<keyword>props\._alias</keyword>
<!-- debug.nas -->
<keyword>debug\.dump</keyword>
<keyword>debug\.local</keyword>
<keyword>debug\.backtrace</keyword>
<keyword>debug\.bt</keyword>
<keyword>debug\.proptrace</keyword>
<keyword>debug\.tree</keyword>
<keyword>debug\.string</keyword>
<keyword>debug\.attributes</keyword>
<keyword>debug\.isnan</keyword>
<keyword>debug\.benchmark</keyword>
<keyword>debug\.printerror</keyword>
<keyword>debug\.warn</keyword>
<keyword>debug\.propify</keyword>
<!-- math.nas -->
<keyword>math\.abs</keyword>
<keyword>math\.sgn</keyword>
<keyword>math\.max</keyword>
<keyword>math\.min</keyword>
<keyword>math\.avg</keyword>
<keyword>math\.pow</keyword>
<keyword>math\.mod</keyword>
<keyword>math\.asin</keyword>
<keyword>math\.acos</keyword>
<keyword>math\.tan</keyword>
<keyword>math\.log10</keyword>
<keyword>math\.to_base</keyword>
<keyword>math\.print_base</keyword>
<!-- view.nas -->
<keyword>view\.panViewDir</keyword>
<keyword>view\.panViewPitch</keyword>
<keyword>view\.resetView</keyword>
<keyword>view\.manager\.register</keyword>
<!-- screen.nas -->
<keyword>screen\.log\.write</keyword>
<!-- controls.nas -->
<keyword>controls\.startEngine</keyword>
<keyword>controls\.selectEngine</keyword>
<keyword>controls\.stepMagnetos</keyword>
<keyword>controls\.centerFlightControls</keyword>
<keyword>controls\.throttleAxis</keyword>
<keyword>controls\.propellerAxis</keyword>
<keyword>controls\.mixtureAxis</keyword>
<keyword>controls\.carbHeatAxis</keyword>
<keyword>controls\.flapsDown</keyword>
<keyword>controls\.wingSweep</keyword>
<keyword>controls\.wingsDown</keyword>
<keyword>controls\.stepSpoilers</keyword>
<keyword>controls\.stepSlats</keyword>
<keyword>controls\.elevatorTrim</keyword>
<keyword>controls\.aileronTrim</keyword>
<keyword>controls\.rudderTrim</keyword>
<keyword>controls\.adjThrottle</keyword>
<keyword>controls\.adjMixture</keyword>
<keyword>controls\.adjCondition</keyword>
<keyword>controls\.adjPropeller</keyword>
<keyword>controls\.incThrottle</keyword>
<keyword>controls\.incAileron</keyword>
<keyword>controls\.incElevator</keyword>
<keyword>controls\.elevatorTrimAxis</keyword>
<keyword>controls\.aileronTrimAxis</keyword>
<keyword>controls\.rudderTrimAxis</keyword>
<keyword>controls\.gearDown</keyword>
<keyword>controls\.gearToggle</keyword>
<keyword>controls\.applyBrakes</keyword>
<keyword>controls\.applyParkingBrake</keyword>
<keyword>controls\.deployChute</keyword>
<keyword>controls\.trigger</keyword>
<keyword>controls\.ptt</keyword>
<keyword>controls\.toggleLights</keyword>
<!-- aircraft.nas -->
<keyword>aircraft\.door\.new</keyword>
<keyword>aircraft\.light\.new</keyword>
<keyword>aircraft\.lowpass\.new</keyword>
<keyword>aircraft\.angular_lowpass\.new</keyword>
<keyword>aircraft\.data\.init</keyword>
<keyword>aircraft\.data\.load</keyword>
<keyword>aircraft\.data\.save</keyword>
<keyword>aircraft\.data\.add</keyword>
<keyword>aircraft\.timer\.new</keyword>
<keyword>aircraft\.livery\.new</keyword>
<keyword>aircraft\.livery_update\.new</keyword>
<keyword>aircraft\.overlay_update\.new</keyword>
<keyword>aircraft\.steering\.init</keyword>
<keyword>aircraft\.steering\.setbrakes</keyword>
<keyword>aircraft\.autotrim\.init</keyword>
<keyword>aircraft\.autotrim\.start</keyword>
<keyword>aircraft\.autotrim\.stop</keyword>
<keyword>aircraft\.tyresmoke\.new</keyword>
<keyword>aircraft\.tyresmoke_system\.new</keyword>
<keyword>aircraft\.rain\.init</keyword>
<keyword>aircraft\.rain\.update</keyword>
<keyword>aircraft\.teleport</keyword>
<keyword>aircraft\.wind_speed_from</keyword>
<keyword>aircraft\.kias_to_ktas</keyword>
<keyword>aircraft\.HUD\.init</keyword>
<keyword>aircraft\.HUD\.cycle_color</keyword>
<keyword>aircraft\.HUD\.cycle_brightness</keyword>
<keyword>aircraft\.HUD\.normal_type</keyword>
<keyword>aircraft\.HUD\.cycle_type</keyword>
<keyword>aircraft\.HUD\.is_active</keyword>
<keyword>aircraft\.crossfeed_valve\.new</keyword>
<!-- screen.nas -->
<keyword>screen\.window\.new</keyword>
<keyword>screen\.display\.new</keyword>
<keyword>screen\.msg_repeat</keyword>
<!-- geo.nas -->
<keyword>geo\.Coord\.new</keyword>
<keyword>geo\.normdeg</keyword>
<keyword>geo\.tile_index</keyword>
<keyword>geo\.format</keyword>
<keyword>geo\.tile_path</keyword>
<keyword>geo\.put_model</keyword>
<keyword>geo\.click_position</keyword>
<keyword>geo\.aircraft_position</keyword>
<keyword>geo\.viewer_position</keyword>
<!-- Mine (ignore) -->
<keyword>setlocalprop</keyword>
<keyword>getlocalprop</keyword>
</context>
<!-- Flightgear props.Node methods -->
<context id="node-object" style-ref="builtin-function">
<prefix>(?&lt;=\.)</prefix> <!-- look behind for . as a prefix-->
<keyword>initNode</keyword>
<keyword>getNode</keyword>
<keyword>getType</keyword>
<keyword>getName</keyword>
<keyword>getPath</keyword>
<keyword>getIndex</keyword>
<keyword>getValue</keyword>
<keyword>getBoolValue</keyword>
<keyword>getValues</keyword>
<keyword>getParent</keyword>
<keyword>getChildren</keyword>
<keyword>getChild</keyword>
<keyword>getAttribute</keyword>
<keyword>setIntValue</keyword>
<keyword>setBoolValue</keyword>
<keyword>setDoubleValue</keyword>
<keyword>setValues</keyword>
<keyword>setValue</keyword>
<keyword>setAttribute</keyword>
<keyword>addChildren</keyword>
<keyword>addChild</keyword>
<keyword>removeAllChildren</keyword>
<keyword>removeChildren</keyword>
<keyword>removeChild</keyword>
<keyword>remove</keyword>
<keyword>clearValue</keyword>
<keyword>unalias</keyword>
<keyword>alias</keyword>
</context>
<context id="nasal" class="no-spell-check">
<include>
<context ref="def:shebang"/>
<context ref="def:shell-like-comment"/>
<context ref="nasal-code-string-double"/>
<context ref="nasal-code-string-single"/>
<context ref="double-quoted-string"/>
<context ref="single-quoted-string"/>
<context ref="string-conversion"/>
<context ref="number"/>
<context ref="operators"/>
<context ref="keyword"/>
<context ref="blockoid"/>
<context ref="builtin-constants"/>
<context ref="builtin-function"/>
<context ref="node-object"/>
<context ref="special-variables"/>
<context ref="module-handler"/>
<context ref="escaped-char"/>
</include>
</context>
<context id="nasal-noextend" class="no-spell-check" extend-parent="false">
<start>.</start>
<end>.</end>
<include>
<context ref="def:shebang"/>
<context ref="def:shell-like-comment"/>
<context ref="nasal-code-string-double"/>
<context ref="nasal-code-string-single"/>
<context ref="double-quoted-string"/>
<context ref="single-quoted-string"/>
<context ref="string-conversion"/>
<context ref="number"/>
<context ref="operators"/>
<context ref="keyword"/>
<context ref="blockoid"/>
<context ref="builtin-constants"/>
<context ref="builtin-function"/>
<context ref="node-object"/>
<context ref="special-variables"/>
<context ref="module-handler"/>
<context ref="escaped-char"/>
</include>
</context>
<context id="nasal-code-string-double">
<start>"""</start>
<end>"""</end>
<include>
<!-- Highlight our delimiters -->
<context sub-pattern="0" where="start" style-ref="string"/>
<context sub-pattern="0" where="end" style-ref="string"/>
<context ref="def:shebang"/>
<context ref="def:shell-like-comment"/>
<context ref="escaped-double-quoted-string"/>
<context ref="single-quoted-string"/>
<context id="double-quote-error" style-ref="def:error">
<match>"(?! *~|"")</match>
</context>
<context id="non-string-double" extend-parent="false">
<start>"</start>
<end>"</end>
<include><context ref="nasal-noextend"/></include>
</context>
<context ref="string-conversion"/>
<context ref="number"/>
<context ref="operators"/>
<context ref="keyword"/>
<context ref="blockoid"/>
<context ref="builtin-constants"/>
<context ref="builtin-function"/>
<context ref="node-object"/>
<context ref="special-variables"/>
<context ref="module-handler"/>
</include>
</context>
<context id="nasal-code-string-single">
<start>'''</start>
<end>'''</end>
<include>
<!-- Highlight our delimiters -->
<context sub-pattern="0" where="start" style-ref="string"/>
<context sub-pattern="0" where="end" style-ref="string"/>
<context ref="def:shebang"/>
<context ref="def:shell-like-comment"/>
<context ref="double-quoted-string"/>
<context ref="escaped-single-quoted-string"/>
<context id="single-quote-error" style-ref="def:error">
<match>'(?! *~|'')</match>
</context>
<context id="non-string-single" extend-parent="false">
<start>'</start>
<end>'</end>
<include><context ref="nasal-noextend"/></include>
</context>
<context ref="string-conversion"/>
<context ref="number"/>
<context ref="operators"/>
<context ref="keyword"/>
<context ref="blockoid"/>
<context ref="builtin-constants"/>
<context ref="builtin-function"/>
<context ref="node-object"/>
<context ref="special-variables"/>
<context ref="module-handler"/>
</include>
</context>
<context id="xml-extras">
<include>
<context ref="xml:comment"/>
<context id="cdata">
<start>&lt;!\[CDATA\[</start>
<end>\]\]&gt;</end>
<include>
<!-- Highlight our delimiters -->
<context sub-pattern="0" where="start" style-ref="xml:cdata-delim" class="no-spell-check"/>
<context sub-pattern="0" where="end" style-ref="xml:cdata-delim"/>
<!-- And include regular Nasal (no XML extensions) -->
<context ref="nasal"/>
</include>
</context>
<context id="character-reference" style-ref="xml:entity">
<match>&amp;(#([0-9]+|x[a-fA-F0-9]+)|lt|gt|amp|quot|apos);</match>
</context>
<context id="unallowed-chars" style-ref="xml:error" extend-parent="false">
<match>[&amp;&lt;&gt;]</match>
</context>
</include>
</context>
<!-- For use when embedded in XML, e.g. in a <script> tag. -->
<context id="xml">
<include>
<context id="script">
<start>(?&lt;=&lt;script&gt;)</start><!-- look behind for <script> as a start -->
<end>(?=&lt;/script&gt;)</end><!-- look ahead for </script> as an end -->
<include>
<context ref="xml-extras"/>
<context ref="nasal"/>
</include>
</context>
<context id="dialog-open">
<start>(?&lt;=&lt;open&gt;)</start><!-- look behind for <open> as a start -->
<end>(?=&lt;/open&gt;)</end><!-- look ahead for </open> as an end -->
<include>
<context ref="xml-extras"/>
<context ref="nasal"/>
</include>
</context>
<context id="dialog-close">
<start>(?&lt;=&lt;close&gt;)</start><!-- look behind for <close> as a start -->
<end>(?=&lt;/close&gt;)</end><!-- look ahead for </close> as an end -->
<include>
<context ref="xml-extras"/>
<context ref="nasal"/>
</include>
</context>
<context id="model-load">
<start>(?&lt;=&lt;load&gt;)</start><!-- look behind for <load> as a start -->
<end>(?=&lt;/load&gt;)</end><!-- look ahead for </load> as an end -->
<include>
<context ref="xml-extras"/>
<context ref="nasal"/>
</include>
</context>
<context id="model-unload">
<start>(?&lt;=&lt;unload&gt;)</start><!-- look behind for <unload> as a start -->
<end>(?=&lt;/unload&gt;)</end><!-- look ahead for </unload> as an end -->
<include>
<context ref="xml-extras"/>
<context ref="nasal"/>
</include>
</context>
</include>
</context>
</definitions>
</language>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
gedit snippet-addon support for the Nasal scripting language.
http://wiki.flightgear.org/Howto:Syntax_highlighting_for_Nasal#gedit
Moved into /usr/share/gedit/plugins/snippets/ folder or install using the
gedit "import" feature.
Copyright (C) 2013 Philosopher
Author: Philosopher (Flightgear forums)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
-->
<snippets language="Nasal">
<snippet id="else">
<text><![CDATA[else {
${0:# body...}
}]]></text>
<description>else statement</description>
<tag>else</tag>
</snippet>
<snippet id="elsif">
<text><![CDATA[elsif (${0:condition}) {
${0:# body...}
}]]></text>
<description>conditional elsif statement</description>
<tag>elsif</tag>
</snippet>
<snippet id="}">
<text><![CDATA[} elsif (${1:variable} == ${2:constant}) {
${0:# body...}
}]]></text>
<description>equality elsif statement</description>
<tag>}</tag>
</snippet>
<snippet id="if">
<text><![CDATA[if (${0:condition}) {
${0:# body...}
}]]></text>
<description>conditional if statement</description>
<tag>if</tag>
</snippet>
<snippet id="=">
<text><![CDATA[if (${1:variable} == ${2:constant}) {
${0:# body...}
}]]></text>
<description>equality if statement</description>
<tag>=</tag>
</snippet>
<snippet id="typeif">
<text><![CDATA[if (typeof(${1:variable}) == "${2:type}") {
${0:# body...}
}]]></text>
<description>typeof if statement</description>
<tag>typeif</tag>
</snippet>
</snippets>

161
scripts/syntax/nasal.vim Normal file
View File

@@ -0,0 +1,161 @@
" Vim syntax file
" Language: Nasal (FlightGear)
" Maintainer: Melchior FRANZ <mfranz # aon : at>
" URL: http://members.aon.at/mfranz/nasal.vim
" Last Change: 2008 Sep 29
" ________________________________CUSTOMIZATION______________________________
"
" :let nasal_no_fgfs=1 " turn off FlightGear extensions
" :hi nasalStatement ctermfg=Green " change statement color
" ___________________________________________________________________________
" for use in ~/.vimrc drop the initial colon
" type ":help new-filetype" in vim for installation instructions
if !exists("main_syntax")
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
let main_syntax = 'nasal'
endif
syn keyword nasalCommentTodo TODO FIXME XXX contained
syn match nasalComment "#.*$" contains=nasalCommentTodo
syn region nasalStringS start=+'+ skip=+\\'+ end=+'+ contains=nasalSpecialS
syn region nasalStringD start=+"+ skip=+\\"+ end=+"+ contains=nasalSpecialD,nasalSpecial
syn match nasalSpecialS contained "\\'"
syn match nasalSpecialD contained "\\[\\rnt\"]"
syn match nasalSpecial contained "\\x[[:xdigit:]][[:xdigit:]]"
syn match nasalError "``\="
syn match nasalError "`\\[^`\\rnt]`"
syn match nasalError "`[^`][^`]\+`"
syn match nasalCharConstant "`[^`\\]`"
syn match nasalCharConstant "`\\[`\\rnt]`"
syn match nasalCharConstant "`\\x[[:xdigit:]][[:xdigit:]]`"
syn match nasalNumber "-\=\<0x\x\+\>"
syn match nasalNumber "-\=\<\d\+\>"
syn match nasalNumber "-\=\.\d\+\([eE][+-]\=\d\+\)\=\>"
syn match nasalNumber "-\=\<\d\+\.\=\([eE][+-]\=\d\+\)\=\>"
syn match nasalNumber "-\=\<\d\+\.\d\+\([eE][+-]\=\d\+\)\=\>"
syn keyword nasalStatement func return var
syn keyword nasalConditional if elsif else
syn keyword nasalRepeat while for foreach forindex
syn keyword nasalBranch break continue
syn keyword nasalVar me arg parents
syn keyword nasalType nil
syn keyword nasalOperator and or
syn match nasalFoo "\~"
syn match nasalFunction display "\<contains\>"
syn keyword nasalFunction size keys append pop setsize subvec delete int num streq substr
syn keyword nasalFunction chr typeof compile call die sprintf caller closure find cmp
syn keyword nasalFunction split rand bind sort ghosttype id
" math lib
syn match nasalFunction "\<math\.\(sin\|cos\|exp\|ln\|sqrt\|atan2\)\>"
syn match nasalConstant "\<math\.\(e\|pi\)\>"
" io lib
syn match nasalFunction "\<io\.\(close\|read\|write\|seek\|tell\|flush\|open\|readln\|stat\)\>"
syn match nasalVar "\<io\.\(SEEK_SET\|SEEK_CUR\|SEEK_END\|stdin\|stdout\|stderr\)\>"
" bits lib
syn match nasalFunction "\<bits\.\(sfld\|fld\|setfld\|buf\)\>"
syn sync fromstart
syn sync maxlines=100
syn match nasalParenError "[()]"
syn match nasalBraceError "[{}]"
syn match nasalBrackError "[\[\]]"
" FlightGear specific commands
if !exists("nasal_no_fgfs")
syn keyword nasalFGFSFunction getprop setprop print _fgcommand settimer _setlistener _cmdarg
syn keyword nasalFGFSFunction _interpolate rand srand directory removelistener systime
syn keyword nasalFGFSFunction geodtocart carttogeod geodinfo parsexml airportinfo abort
syn keyword nasalGlobalsFunction isa fgcommand cmdarg abs interpolate setlistener defined printlog
syn keyword nasalGlobalsFunction thisfunc printf values
syn keyword nasalPropsFunction getType getName getIndex getValue setValue setIntValue
syn keyword nasalPropsFunction setBoolValue setDoubleValue getParent getChild getChildren
syn keyword nasalPropsFunction getAttribute setAttribute alias unalias getAliasTarget clearValue
syn keyword nasalPropsFunction removeChild removeChildren getNode initNode
syn keyword nasalPropsFunction getPath getBoolValue setValues getValues
syn match nasalPropsFunction "\<props\.\(_\?globals\|Node\|nodeList\|condition\)\>\.\="
" XML embedded mode
if expand("%:e") == "xml"
syn region nasalComment start="<!--" end="-->" contains=nasalCommentTodo
syn region nasalComment start="<?" end="?>" contains=nasalCommentTodo
syn match nasalComment "^\s*</\?[[:alnum:]!].*[[:alnum:]\"-]/\?>\s*$"
syn match nasalComment "^\s*<script>"
syn match nasalComment "</script>.*"
syn match nasalCDATA "<!\[CDATA\["
syn match nasalCDATA "\]\]>"
endif
endif
syn region nasalEncl transparent matchgroup=nasalParenEncl start="(" end=")" contains=ALLBUT,nasalParenError
syn region nasalEncl transparent matchgroup=nasalBrackEncl start="\[" end="\]" contains=ALLBUT,nasalBrackError
syn region nasalEncl transparent matchgroup=nasalBraceEncl start="{" end="}" contains=ALLBUT,nasalBraceError
if version >= 508 || !exists("did_nasal_syn_inits")
if version < 508
let did_nasal_syn_inits = 1
command -nargs=+ HiLink hi link <args>
else
command -nargs=+ HiLink hi def link <args>
endif
HiLink nasalComment Comment
HiLink nasalCommentTodo Todo
HiLink nasalSpecial Special
HiLink nasalSpecialS Special
HiLink nasalSpecialD Special
HiLink nasalStringS String
HiLink nasalStringD String
HiLink nasalNumber Number
HiLink nasalConditional Conditional
HiLink nasalVar Macro
HiLink nasalType Type
HiLink nasalConstant Constant
HiLink nasalCharConstant Type
HiLink nasalFoo NonText
HiLink nasalCDATA Type
HiLink nasalRepeat Repeat
HiLink nasalBranch Conditional
HiLink nasalOperator Operator
HiLink nasalStatement Statement
HiLink nasalFunction Function
HiLink nasalFGFSFunction Function
HiLink nasalGlobalsFunction Function
HiLink nasalPropsFunction Function
HiLink nasalError Error
HiLink nasalParenError nasalError
HiLink nasalBrackError nasalError
HiLink nasalBraceError nasalError
delcommand HiLink
endif
let b:current_syntax = "nasal"
if main_syntax == 'nasal'
unlet main_syntax
endif
" vim: ts=8

113
scripts/tools/fg-check Executable file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
#
# Checks source code and data for potential problems.
# Meant to be executed before submitting/committing.
SELF=${0##*/}
# optional apps
RLE=$(which rle 2>/dev/null) # http://members.aon.at/mfranz/rle.tar.gz (depends on Qt lib)
AC3D_SCAN=$(which ac3d-scan 2>/dev/null) # http://members.aon.at/mfranz/ac3d-scan
function ERROR { echo -e "\e[31;1m$*\e[m"; }
function LOG { echo -e "\e[35m$*\e[m"; }
function RESULT { echo -e "\t$*"; }
TMP=$(mktemp -d -t $SELF.XXX) || (echo "$0: can't create temporary dir"; exit 1)
trap "rm -rf $TMP" 0 1 2 3 13 15
LOG "checking for spaces in filenames ..."
find .|grep " "|while read i; do RESULT "$i"; done
LOG "checking for upper-case extensions ..."
find .|while read i; do
case "$i" in .|..|CVS/*|*/CVS/*|*.Opt|*.README|*.Po|*.TXT) continue ;; esac
base=${i##*/}
ext=${base##*.}
[ "$base" == "$ext" ] && continue # has no extension
ext=${ext//[^a-zA-Z]/}
[ "$ext" ] || continue # only non-letters
lcext=$(echo $ext|sed -e 's,\(.*\),\L\1,')
[ "$ext" != "$lcext" ] && RESULT "$i"
done
LOG "checking for DOS line endings ..."
find . -type f|while read i; do
desc=$(file -b "$i")
case "$desc" in *text*)
grep "
$" "$i" >/dev/null && RESULT "$i"
;;
esac
done
LOG "checking for uncompressed textures ..."
find . -iname \*.rgb -o -iname \*.rgba|while read i; do
new=$TMP/sgi.rgb
if [ -x "$RLE" ]; then
cp "$i" $new && "$RLE" $new 2>&1|grep corrupt &>/dev/null && ERROR "\t$i ... FILE CORRUPTED"
else
convert "$i" -compress RLE sgi:$new
fi
perl -e '
my $file = shift;
my $old = -s $file;
my $new = -s shift;
if ($new < $old) {
printf "\t$file: could be %0.02f%% of current size (%d bytes less)\n",
100 * $new / $old, $old - $new;
}
' "$i" $new
done
if [ -x "$AC3D_SCAN" ]; then
LOG "checking for AC3D sanity ..."
find . -iname \*.ac|while read i; do
case "$i" in configure.ac|*/configure.ac) continue ;; esac
result=$($AC3D_SCAN <$i 2>&1)
[ "$result" ] && echo -e "$result\n\t... in file \e[36m$i\e[m";
done
fi
LOG "checking for thumbnail file size (expected JPEG 171x128)"
find . -name thumbnail.jpg|while read i; do
id=$(identify "$i")
if ! echo $id|grep "JPEG 171x128" >/dev/null; then
RESULT "$i ... $id"
fi
done
LOG "checking for 'userarchive' flags (not allowed in aircraft XML files) ..."
find . -name \*.xml|while read i; do
if grep "userarchive" $i >/dev/null; then
RESULT "$i"
fi
done
LOG "checking for XML syntax ..."
find . -name \*.xml|while read i; do
xmllint $i >/dev/null || RESULT "... in file \e[36m$i\e[m"
done
LOG "checking for 'if (foo) delete foo;' ..."
find . -iregex ".*\.\([ch]\(xx\|pp\)\|cc\|h\)$"|while read i; do perl -e '
my $i = 0;
my $name = $ARGV[0];
undef $/;
$_ = <>;
s/(if\s*\(([^\)]+)\)\s*delete(?:\s+|\s*\[\]\s*)\2\s*;)/print "$1\n" and $i++/ges;
print "\t... \033[36min file $name\033[m\n" if $i;
' "$i"; done

366
scripts/tools/fg-submit Executable file
View File

@@ -0,0 +1,366 @@
#!/bin/bash
#
# This script called in a CVS directory compares local files with
# the repository, and prepares an update package containing all
# changes and new files for submission to a CVS maintainer. If there
# are only changes in text files, then a compressed unified diff is
# made (foo.diff.bz2). If there are also changed binary or new files,
# then an archive is made instead (foo.tar.bz2). The base name ("foo")
# can be given as command line argument. Otherwise the directory name
# is used. The script also leaves a diff in uncompressed/unpackaged
# form. This is only for developer convenience -- for a quick check
# of the diff correctness. It is not to be submitted. The script will
# not overwrite any file, but rather rename conflicting files.
#
# Usage: fg-submit [-v] [<basename>]
#
# Options:
# -v ... verbose output
#
# Example:
# $ cd $FG_ROOT/Aircraft/bo105
# $ fg-submit # -> bo105.diff.bz2 or bo105.tar.bz2
#
# $ fg-submit update # -> update.diff.bz2 or update.tar.bz2
#
#
# Spaces in the basename are replaced with "%20". People who prefer
# to have the date in the archive name can conveniently achieve this
# by defining a shell alias in ~/.bashrc:
#
# alias submit='fg-submit "${PWD##*/}-$(date +%Y-%m-%d)"'
#
#
#
# If the script finds an application named "fg-upload", then it calls
# this at the end with two arguments:
#
# $1 ... archive or compressed diff for submission
# $2 ... accessory uncompressed diff, *NOT* for submission!
#
# $1 and $2 are guaranteed not to contain spaces, only $1 is guaranteed
# to actually exist. Such a script can be used to upload the file to an
# ftp-/webserver, and/or to remove one or both files. Example using
# KDE's kfmclient for upload (alternatives: ncftpput, gnomevfs-copy):
#
# $ cat ~/bin/fg-upload
# #!/bin/bash
# echo "uploading $1"
# if kfmclient copy $1 ftp://user:password@server.com; then
# echo "deleting $1 $2"
# rm -f $1 $2
#
# URL=ftp://server.com/$1
#
# # copy URL to KDE's clipboard, so that MMB-clicking pastes it
# dcop klipper klipper setClipboardContents $URL
#
# echo "Done. --> $URL"
# else
# echo "$0: uploading failed!"
# fi
#
#
#
# Whether a file should be included in the archive or not, is decided
# by pattern rules. There is a set of reasonable default rules predefined,
# but alternative settings can be defined in a hidden configuration file
# named ".fg-submit". Such a file is searched in the current directory,
# in its parent directory, in its grand-parent directory and so on,
# and finally in the $HOME directory. The first found file is taken.
#
# A file can use a list of three keywords with arguments, each on a
# separate line:
#
# ALLOW <pattern-list> ... accept & report matching file
# DENY <pattern-list> ... reject & report matching file
# IGNORE <pattern-list> ... silently reject matching file
#
# A <pattern-list> is a space-separated list of shell pattern.
# It may also be empty, in which case it has no effect. Examples:
#
# DENY test.blend
# ALLOW *.xcf *.blend
#
# The list of pattern is checked in the same order in which it was
# built. The first match causes a file to be accepted or rejected.
# Further matches are not considered. Comments using the hash
# character '#' are allowed and ignored.
#
# Some default rules are always added at the end. If you want to
# bypass them, then finish your configuration with an "ALLOW *"
# or "DENY *", and no file will ever reach the default rules.
#
#
# Example:
#
# DENY test.xcf # throw out the test image, but ...
# ALLOW *.xcf # ... allow all other GIMP images (the default
# # rules would otherwise throw them out)
#
# ALLOW not.old # add this file, but ...
# IGNORE *.old # throw out all other "old" files (and don't
# # report that to the terminal)
#
#
# .fg-submit configuration files are "sourced" bash scripts, the
# keywords are simple shell functions. That means that you can
# also use other bash commands in that file, such as "echo", or
# write several commands on one line, separated with semicolon.
# You can even put all special rules in your ~/.fg-submit file,
# with rules depending on the working directory:
#
# case "$PWD" in
# */bo105*) DENY *.osg; ALLOW livery.xcf ;;
# */ufo*) DENY *.tiff ;;
# esac
SELF=${0##*/}
DIR=${PWD##*/}
if [ "$1" == "-v" ]; then
DBG=1
shift
fi
BASE=${1:-$DIR}
BASE=${BASE// /%20}
CVS=/usr/bin/cvs # avoid colorcvs wrapper from
[ -x $CVS ] || CVS=cvs # http://www.hakubi.us/colorcvs/
UPLOAD=$(which fg-upload 2>/dev/null)
CONFIG_FILE=".fg-submit"
ARCHIVE=$BASE.tar.bz2
DIFF=$BASE.diff
CDIFF=$DIFF.bz2
# these rules are always prepended; the first letter decides if the
# rule accepts (+), rejects (-), or ignores (!) a matching file.
PREFIX_RULES="
!$DIFF* !$CDIFF* !$ARCHIVE*
!CVS/* !*/CVS/*
"
# these rules are always appended
DEFAULT_RULES="
+.cvsignore +*/.cvsignore
-*~ -*. -*.bak -*.orig
-*.RGB -*.RGBA -*.MDL
-*.XCF -*.tga -*.TGA -*.bmp -*.BMP -*.PNG
-*.blend -*.blend[0-9] -*blend[0-9][0-9] -*.blend[0-9][0-9][0-9]
-*.gz -*.tgz -*.bz2 -*.zip -*.tar.gz* -*.tar.bz2*
"
POSTFIX_RULES="
!.* !*/.*
+*
"
function ERROR { echo -e "\e[31;1m$*\e[m"; }
function LOG { echo -e "\e[35m$*\e[m"; }
function NEW { echo -e "\e[32m\t+ $*\e[m"; }
function CHANGED { echo -e "\e[36m\t+ $*\e[m"; }
function REJECT { echo -e "\e[31m\t- $*\e[m"; }
function DEBUG { [ $DBG ] && echo -e "$*"; }
function diffstat {
# output diff statistics, similar to the "diffstat" utility
awk '
function line(a, r, c, f) {
print "\t\033[32m"a"\033[m\t\033[31m"r"\033[m\t\033[34m"c"\033[m\t"f
}
function dofile() {
if (!file) {
return
}
if (bin) {
print "\t. . . . binary . . . . \033[36m"file"\033[m"
} else {
line(a, r, c, file)
at += a; rt += r; ct += c
}
a = r = c = 0
}
BEGIN {
print "\tadded---removed-changed----------------------------------------"
a = r = c = at = rt = ct = n = bin = 0
}
/^Index: / { dofile(); scan = bin = 0; file = $2; n++; next }
/^@@/ { scan = 1; next }
/^Binary/ { if (!scan) bin = 1; next }
/^\+/ { if (scan) a++; next }
/^-/ { if (scan) r++; next }
/^!/ { if (scan) c++; next }
END {
dofile()
print "\t----------------------------------------total------------------"
line(at, rt, ct, "\033[min "n" files")
}
' <$1
}
function backup_filename {
for ((i = 1; 1; i = i + 1)); do
name=$1.$i
if ! [ -a "$name" ]; then
touch $name
echo $name
return
fi
done
}
# set up accept/reject rules
function ALLOW { for i in $*; do RULES="$RULES +$i"; done }
function DENY { for i in $*; do RULES="$RULES -$i"; done }
function IGNORE { for i in $*; do RULES="$RULES !$i"; done }
function search_config {
file="$1/$CONFIG_FILE"
DEBUG "checking for config file $file"
if [ -f "$file" ]; then
CONFIG="$file"
return 0
elif [ "$1" ]; then
search_config ${1%/${1##*/}} # parent dir
return
fi
return 1
}
set -f
RULES=
if search_config "$PWD"; then
LOG "loading config file $CONFIG"
source "$CONFIG"
elif [ -f ~/$CONFIG_FILE ]; then
DEBUG "loading config file ~/$CONFIG_FILE"
source ~/$CONFIG_FILE
elif [ -f ~/${CONFIG_FILE}rc ]; then
DEBUG "loading config file ~/${CONFIG}rc"
source ~/${CONFIG_FILE}rc
fi
RULES="$PREFIX_RULES $RULES $DEFAULT_RULES $POSTFIX_RULES"
set +f
if [ $DBG ]; then
DEBUG "using these rules: "
for i in $RULES; do echo -n "$i "; done
echo
fi
# create temporary dir that's automatically removed on exit
TMP=$(mktemp -d /tmp/$SELF.$BASE.XXXXXX) || (echo "$0: can't create temporary dir"; exit 1)
trap "rm -rf $TMP" 0 1 2 3 13 15
# move old files out of the way adding sequential suffixes
for i in $DIFF $CDIFF $ARCHIVE; do
[ -f $i ] && mv $i $(backup_filename $i)
done
LOG "updating and checking for new files ..."
$CVS -q up -dP >$TMP/up || exit 1
if grep "^C " $TMP/up &>/dev/null; then
ERROR "there are conflicts with the following files:"
grep "^C " $TMP/up
exit 1
fi
LOG "making diff ..."
if ! $CVS -q diff -up >$DIFF; then
LOG "diff statistics:"
diffstat $DIFF
echo
# add diff file itself
echo $DIFF >>$TMP/files
# add changed binary files
awk '
/^Index: / { scan = 1; file = $2; next }
/^@@/ { scan = 0; next }
/^Binary/ { if (scan) { print file } }
' <$DIFF >>$TMP/files
else
rm -f $DIFF
fi
LOG "checking for files to submit ..."
if [ -f $TMP/files ]; then
cat $TMP/files|while read i; do
CHANGED "$i"
done
fi
grep "^? " $TMP/up|while read i; do
find ${i#? } -type f >>$TMP/check
done
# filter files according to the pattern rules
if [ -f $TMP/check ]; then
for i in $(cat $TMP/check); do
DEBUG "checking whether file '$i' matches"
for r in $RULES; do
DEBUG "\t\trule $r"
class=${r:0:1}
rule=${r:1}
case "$i" in $rule)
case $class in
!) DEBUG "$i\t\t\"silently\" rejected\t\t$rule" ;;
-) REJECT "$i\t\t$rule" ;;
+) NEW "$i\t\t$rule" && echo "$i" >>$TMP/files ;;
esac
break
;;
esac
done
done
fi
if ! [ -f $TMP/files ]; then
LOG "no changed or new files found"
exit 0
fi
echo
numfiles=$(awk '//{n++}END{print n}' <$TMP/files)
if [ -f $DIFF -a $numfiles == 1 ]; then
LOG "only changed non-binary files found"
LOG "creating compressed diff \e[1;37;40m$CDIFF\e[m\e[35m ..."
bzip2 --keep $DIFF
RESULT=$CDIFF
else
LOG "changed and/or new files found"
LOG "creating archive \e[1;37;40m$ARCHIVE\e[m\e[35m ..."
tar --create --bzip2 --file=$ARCHIVE --files-from $TMP/files
RESULT=$ARCHIVE
fi
[ -x "$UPLOAD" -a -f $RESULT ] && $UPLOAD $RESULT $DIFF

302
scripts/tools/freq Executable file
View File

@@ -0,0 +1,302 @@
#!/usr/bin/perl -w
# $Id$
# Melchior FRANZ <mfranz#aon:at> Public Domain
#
# Usage: $ freq [IACO:ksfo [RANGE:15]]
#
# Examples: $ freq
# $ freq ksjc
# $ freq ksjc 30
#
# The RANGE is in km and defines which NDB, VOR, VORTAC, ... to
# display. Default is 15 km.
#
# Note that the directions given for NDB, VOR, VORTAC, ... are
# always the heading from this radio facility to the airport!
use strict;
use POSIX qw(ceil floor);
my $ID = shift || "KSFO";
my $RANGE = shift || 15; # for NDB/VOR [km]
my $FG_ROOT = $ENV{'FG_ROOT'} || "/usr/local/share/FlightGear";
my $APTFILE = "$FG_ROOT/Airports/apt.dat.gz" || die "airport file not found";
my $NAVFILE = "$FG_ROOT/Navaids/nav.dat.gz" || die "nav file not found";
$ID = uc($ID);
my $PI = 3.1415926535897932384626433832795029;
my $D2R = $PI / 180;
my $R2D = 180 / $PI;
my $ERAD = 6378138.12;
my %COLOR = (
'NONE' => "\033[m",
'DME' => "\033[34;1",
'ILS' => "\033[33;1m",
'TWR' => "\033[31;1m",
'ATIS' => "\033[32;1m",
'NDB' => "\033[36;1m",
'VOR' => "\033[35;1m",
);
my $USECOLOR = 1;
my %FREQ;
my $aptdatacnt = 0;
my $aptlat = 0;
my $aptlon = 0;
open(F, "gzip -d -c $APTFILE|") or die "can't open airport file $APTFILE";
while (<F>) {
if (/^1\s+\S+\s+\S+\s+\S+\s+$ID\s+(.+)\s+/) {
my $title = "$ID - $1";
print "$title\n";
print "=" x length($title) . "\n";
foreach (<F>) {
chomp;
last if /^\s*$/;
if (/^1.\s+(\S+)\s+(\S+)\s+/) {
my ($lat, $lon) = ($1, $2);
map { s/^(-?)0+/$1/ } ($lat, $lon);
$aptlat += $lat;
$aptlon += $lon;
$aptdatacnt++;
} elsif (/^(5\d+)\s+(\d+)\s+(.*)\s*/) {
my ($id, $freq, $desc) = ($1, $2, $3);
$freq =~ s/(..)$/.$1/;
&addfreq($freq, $desc);
}
}
last;
}
}
close F or die "can't close airport file $APTFILE";
die "no data for $ID" unless $aptdatacnt;
# calculate mean location from all structures on the airport
$aptlat /= $aptdatacnt;
$aptlon /= $aptdatacnt;
my ($aptx, $apty, $aptz) = &ll2xyz($aptlat, $aptlon);
my @OM;
my @MM;
my @IM;
my @NDB;
my @VOR;
my @DME;
my @OTHERS;
open(F, "gzip -d -c $NAVFILE|") or die "can't open airport file $NAVFILE";
while (<F>) {
chomp;
if (/^2\s/) { # NDB
my @l = split /\s+/, $_, 9;
map { s/^(-?)0+/$1/ } @l[1,2];
my $dist = &coord_dist_sq(&ll2xyz($l[1], $l[2]), $aptx, $apty, $aptz);
push @NDB, [$dist, @l];
} elsif (/^3\s/) { # VOR/VOR-DME/DME/VORTAC/TACAN
my @l = split /\s+/, $_, 9;
map { s/^(-?)0+/$1/ } @l[1,2];
my $dist = &coord_dist_sq(&ll2xyz($l[1], $l[2]), $aptx, $apty, $aptz);
if ($l[8] =~ /\b(VOR|VOR-DME)$/) {
push @VOR, [$dist, @l];
} elsif ($l[8] =~ /\bDME\b/) {
push @DME, [$dist, @l];
} else {
push @OTHERS, [$dist, @l];
}
} elsif (/^(4|5)\s/) { # LLZ
my @l = split /\s+/, $_, 11;
next unless $l[8] eq $ID;
$l[4] =~ s/(..)$/.$1/;
&addfreq($l[4], "LLZ " . $l[9]);
} elsif (/^6\s/) { # GS
my @l = split /\s+/, $_, 11;
next unless $l[8] eq $ID;
$l[4] =~ s/(..)$/.$1/;
&addfreq($l[4], "GS " . $l[9]);
} elsif (/^7\s/) { # OM
my @l = split /\s+/, $_, 11;
next unless $l[8] eq $ID;
push @OM, $l[9];
} elsif (/^8\s/) { # MM
my @l = split /\s+/, $_, 11;
next unless $l[8] eq $ID;
push @MM, $l[9];
} elsif (/^9\s/) { # IM
my @l = split /\s+/, $_, 11;
next unless $l[8] eq $ID;
push @IM, $l[9];
} elsif (/^12\s/) { # DME (ILS)
my @l = split /\s+/, $_, 11;
next unless $l[8] eq $ID;
$l[4] =~ s/(..)$/.$1/;
&addfreq($l[4], "DME " . $l[9]);
}
}
close F or die "can't close airport file $NAVFILE";
foreach my $freq (sort { $a <=> $b } keys %FREQ) {
my %h;
map { $h{$_} = 1 } @{$FREQ{$freq}};
my @uniq = keys %h;
my @desc;
my %rwy;
foreach my $d (@uniq) {
if ($d =~ /(\S*)\s*(\d\d[LRC]?)\s*(\S*)/) {
push @{$rwy{$2}}, ($1 . $3);
} else {
push @desc, $d;
}
}
foreach my $r (keys %rwy) {
push @desc, ((join "/", sort @{$rwy{$r}}) . " $r");
}
my $s;
my $k = join ", ", @desc;
if ($k =~ /\bTWR\b/) {
$s = $COLOR{'TWR'};
} elsif ($k =~ /\bATIS\b/) {
$s = $COLOR{'ATIS'};
} elsif ($k =~ /\b(GZ|LLZ)\b/) {
$s = $COLOR{'ILS'};
}
$s .= sprintf "%-7s %s\033[m\n", $freq, join ", ", $k;
print $s;
}
&printfreq(0, $COLOR{'NDB'}, @NDB);
&printfreq(1, $COLOR{'VOR'}, @VOR);
&printfreq(1, $COLOR{'DME'}, @DME);
&printfreq(1, $COLOR{'NONE'}, @OTHERS);
print " OM " . (join ", ", sort @OM) . "\n" if @OM;
print " MM " . (join ", ", sort @MM) . "\n" if @MM;
print " IM " . (join ", ", sort @IM) . "\n" if @IM;
exit 0;
sub printfreq($$$)
{
my $divfreq = shift; # divide frequency by 100?
my $color = shift;
foreach (sort { @{$a}[0] <=> @{$b}[0] } @_) {
my @l = @{$_};
my $dist = &distance($l[0]);
my $dir = &llll2dir($l[2], $l[3], $aptlat, $aptlon);
my $freq = $l[5];
$freq =~ s/(..)$/.$1/ if $divfreq;
printf "$color%-7s %s (\"%s\")\t-->\t%s km/%s nm @ %s (%s)$COLOR{'NONE'}\n",
$freq, $l[9], $l[8],
&round($dist, 0.1), # km
&round($dist / 1.852, 0.1), # nm
int $dir, &symdir($dir);
next if $l[9] =~ /\b$ID\b/;
last if $dist > $RANGE;
}
}
sub addfreq($$)
{
my ($freq, $desc) = @_;
push @{$FREQ{$freq}}, $desc;
}
sub distance($) # km
{
my $t = shift;
return $ERAD * sqrt($t) / 1000;
}
sub round($)
{
my $i = shift;
my $m = (shift or 1);
$i /= $m;
$i = $i - &floor($i) >= 0.5 ? &ceil($i) : &floor($i);
$i *= $m;
return $i;
}
sub llll2dir($$$$)
{
my $latA = (shift) * $D2R;
my $lonA = (shift) * $D2R;
my $latB = (shift) * $D2R;
my $lonB = (shift) * $D2R;
my $xdist = sin($lonB - $lonA) * $ERAD * cos(($latA + $latB) / 2);
my $ydist = sin($latB - $latA) * $ERAD;
my $dir = atan2($xdist, $ydist) * $R2D;
$dir += 360 if $dir < 0;
return $dir;
}
sub ll2xyz($$)
{
my $lat = (shift) * $D2R;
my $lon = (shift) * $D2R;
my $cosphi = cos $lat;
my $di = $cosphi * cos $lon;
my $dj = $cosphi * sin $lon;
my $dk = sin $lat;
return ($di, $dj, $dk);
}
sub xyz2ll($$$)
{
my ($di, $dj, $dk) = @_;
my $aux = $di * $di + $dj * $dj;
my $lat = atan2($dk, sqrt $aux) * $R2D;
my $lon = atan2($dj, $di) * $R2D;
return ($lat, $lon);
}
sub coord_dist_sq($$$$$$)
{
my ($xa, $ya, $za, $xb, $yb, $zb) = @_;
my $x = $xb - $xa;
my $y = $yb - $ya;
my $z = $zb - $za;
return $x * $x + $y * $y + $z * $z;
}
sub symdir($)
{
my $dir = shift;
my @names = ("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
"S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW");
my $nnames = scalar @names;
my $idx = int($nnames * (($dir / 360) + (0.5 / $nnames)));
if ($idx >= $nnames) {
$idx = 0;
}
return $names[$idx];
}

289
scripts/tools/lsprop Executable file
View File

@@ -0,0 +1,289 @@
#!/usr/bin/python
import glob, os, sys, string, xml.sax, getopt
__doc__ = """\
List properties defined in FlightGear's <PropertyList> XML files.
Usage:
lsprop [-v] [-p] [-i|-I] [-f <format>] [<list-of-xml-files>]
lsprop -h
Options:
-h, --help print this help screen
-v, --verbose increase verbosity
-i, --all-indices also show null indices in properties
-I, --no-indices don't show any indices in properties
-p, --raw-paths don't use symbols "$FG_ROOT" and "$FG_HOME" as path prefix
-f, --format set output format (default: --format="%f +%l: %p = '%v'")
Format:
%f file path
%l line number
%c column number
%p property path
%t property type
%V raw value (unescaped)
%v cooked value (carriage return, non printable chars etc. escaped)
%q like %v, but single quotes escaped to \\'
%Q like %v, but double quotes escaped to \\"
%% percent sign
Environment:
FG_ROOT
FG_HOME
LSPROP_FORMAT overrides default format
Arguments:
If no file arguments are specified, then the following files are assumed:
$FG_ROOT/preferences.xml
$FG_ROOT/Aircraft/*/*-set.xml
Current settings:\
"""
class config:
root = "/usr/local/share/FlightGear"
home = os.environ["HOME"] + "/.fgfs"
raw_paths = 0
format = "%f +%l: %p = '%v'"
verbose = 1
indices = 1 # 0: no indices; 1: only indices != [0]; 2: all indices
def errmsg(msg, color = "31;1"):
if os.isatty(2):
print >>sys.stderr, "\033[%sm%s\033[m" % (color, msg)
else:
print >>sys.stderr, msg
def cook_path(path, force = 0):
path = os.path.normpath(os.path.abspath(path))
if config.raw_paths and not force:
return path
if path.startswith(config.root):
path = path.replace(config.root, "$FG_ROOT", 1)
elif path.startswith(config.home):
path = path.replace(config.home, "$FG_HOME", 1)
return path
class Error(Exception):
pass
class Abort(Exception):
pass
class XMLError(Exception):
def __init__(self, locator, msg):
msg = "%s in %s +%d:%d" \
% (msg.replace("\n", "\\n"), cook_path(locator.getSystemId()), \
locator.getLineNumber(), locator.getColumnNumber())
raise Error(msg)
class parse_xml_file(xml.sax.handler.ContentHandler):
def __init__(self, path, nesting = 0, stack = None):
self.level = 0
self.path = path
self.nesting = nesting
self.type = None
if stack:
self.stack = stack
else:
self.stack = [[None, None, {}, []]] # name, index, indices, data
self.pretty_path = cook_path(path)
if config.verbose > 1:
errmsg("FILE %s (%d)" % (path, nesting), "35")
if not os.path.exists(path):
raise Error("file doesn't exist: " + self.pretty_path)
try:
xml.sax.parse(path, self, self)
except ValueError:
pass # FIXME hack arount DTD error
def startElement(self, name, attrs):
self.level += 1
if self.level == 1:
if name != "PropertyList":
raise XMLError(self.locator, "XML file isn't a <PropertyList>")
else:
index = 0
if attrs.has_key("n"):
index = int(attrs["n"])
elif name in self.stack[-1][2]:
index = self.stack[-1][2][name] + 1
self.stack[-1][2][name] = index
self.type = "unspecified"
if attrs.has_key("type"):
self.type = attrs["type"]
if attrs.has_key("include"):
path = os.path.dirname(os.path.abspath(self.path)) + "/" + attrs["include"]
if attrs.has_key("omit-node") and attrs["omit-node"] == "y" or self.level == 1:
self.stack.append([None, None, self.stack[-1][2], []])
else:
self.stack.append([name, index, {}, []])
parse_xml_file(path, self.nesting + 1, self.stack)
elif self.level > 1:
self.stack.append([name, index, {}, []])
def endElement(self, name):
value = string.join(self.stack[-1][3], '')
if not len(self.stack[-1][2]) and self.level > 1:
path = self.pathname()
if path:
cooked_value = self.escape(value.encode("iso-8859-15", "backslashreplace"))
print config.cooked_format % {
"f": self.pretty_path,
"l": self.locator.getLineNumber(),
"c": self.locator.getColumnNumber(),
"p": path,
"t": self.type,
"V": value,
"v": cooked_value,
"q": cooked_value.replace("'", "\\'"),
'Q': cooked_value.replace('"', '\\"'),
}
elif len(string.strip(value)):
raise XMLError(self.locator, "garbage found '%s'" % string.strip(value))
self.level -= 1
if self.level:
self.stack.pop()
def characters(self, data):
self.stack[-1][3].append(data)
def setDocumentLocator(self, locator):
self.locator = locator
def pathname(self):
path = ""
for e in self.stack[1:]:
if e[0] == None: # omit-node
continue
path += "/" + e[0]
if e[1] and config.indices == 1 or config.indices == 2:
path += "[%d]" % e[1]
return path
def escape(self, string):
s = ""
for c in string:
if c == '\n':
s += '\\n'
elif c == '\r':
s += '\\r'
elif c == '\v':
s += '\\v'
elif c == '\\':
s += '\\\\'
elif not c.isalnum() and " \t!@#$%^&*()_+|~-=\`[]{};':\",./<>?".find(c) < 0:
s += "\\x%02x" % ord(c)
else:
s += c
return s
def warning(self, exception):
raise XMLError(self.locator, "WARNING: " + str(exception))
def error(self, exception):
raise XMLError(self.locator, "ERROR: " + str(exception))
def fatalError(self, exception):
raise XMLError(self.locator, "FATAL: " + str(exception))
def main():
if 'FG_ROOT' in os.environ:
config.root = os.environ['FG_ROOT'].lstrip().rstrip("/\\\t ")
if 'FG_HOME' in os.environ:
config.home = os.environ['FG_HOME'].lstrip().rstrip("/\\\t ")
if 'LSPROP_FORMAT' in os.environ:
config.format = os.environ['LSPROP_FORMAT']
# options
try:
opts, args = getopt.getopt(sys.argv[1:], \
"hviIpf:", \
["help", "verbose", "all-indices", "no-indices", "raw-paths", "format="])
except getopt.GetoptError, msg:
errmsg("Error: %s" % msg)
return -1
for o, a in opts:
if o in ("-h", "--help"):
print __doc__
print '\t--format="%s"' % config.format.replace('"', '\\"')
return 0
if o in ("-v", "--verbose"):
config.verbose += 1
if o in ("-i", "--all-indices"):
config.indices = 2
if o in ("-I", "--no-indices"):
config.indices = 0
if o in ("-p", "--raw-paths"):
config.raw_paths = 1
if o in ("-f", "--format"):
config.format = a
# format
f = config.format
f = f.replace("\\e", "\x1b")
f = f.replace("\\033", "\x1b")
f = f.replace("\\x1b", "\x1b")
f = f.replace("%%", "\x01\x01")
f = f.replace("%f", "\x01(f)s")
f = f.replace("%l", "\x01(l)d")
f = f.replace("%c", "\x01(c)d")
f = f.replace("%p", "\x01(p)s")
f = f.replace("%t", "\x01(t)s")
f = f.replace("%V", "\x01(V)s")
f = f.replace("%v", "\x01(v)s")
f = f.replace("%q", "\x01(q)s")
f = f.replace('%Q', '\x01(Q)s')
f = f.replace("%", "%%")
f = f.replace("\x01", "%")
config.cooked_format = f
if config.verbose > 2:
print >>sys.stderr, "internal format = [%s]" % config.cooked_format
# arguments
if not len(args):
args = [config.root + "/preferences.xml"]
if not os.path.exists(args[0]):
errmsg("Error: environment variable FG_ROOT not set or set wrongly?")
return -1
for f in glob.glob(config.root + '/Aircraft/*/*-set.xml'):
args.append(f)
for arg in args:
try:
parse_xml_file(arg)
except Abort, e:
errmsg("Abort: " + e.args[0])
return -1
except Error, e:
errmsg("Error: " + e.args[0])
except IOError, (errno, msg):
errmsg("Error: " + msg)
return errno
except KeyboardInterrupt:
print >>sys.stderr, "\033[m"
return 0
if __name__ == "__main__":
sys.exit(main())