first commit
This commit is contained in:
11
scripts/CMakeLists.txt
Normal file
11
scripts/CMakeLists.txt
Normal 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
50
scripts/atis/README
Normal 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
80
scripts/atis/atis-lex.pl
Executable 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
21
scripts/atis/find_nonUTF8.pl
Executable 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
155
scripts/atis/list-airports.pl
Executable 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
BIN
scripts/atis/quiet0.500.wav
Normal file
Binary file not shown.
349
scripts/atis/synth.pl
Executable file
349
scripts/atis/synth.pl
Executable 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
102
scripts/atis/voice.pl
Executable 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
43
scripts/atis/words_per_line.sh
Executable 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
|
||||
397
scripts/completion/fg-completion.bash
Executable file
397
scripts/completion/fg-completion.bash
Executable 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
|
||||
303
scripts/completion/fg-completion.zsh
Executable file
303
scripts/completion/fg-completion.zsh
Executable 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
93
scripts/debug/debug-fgfs
Executable 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
|
||||
|
||||
100
scripts/debug/sample.fgfs.supp
Normal file
100
scripts/debug/sample.fgfs.supp
Normal 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
|
||||
}
|
||||
|
||||
153
scripts/example/fgfsclient.c
Normal file
153
scripts/example/fgfsclient.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
201
scripts/example/fgfsclient.cxx
Normal file
201
scripts/example/fgfsclient.cxx
Normal 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
|
||||
95
scripts/example/fgfsscript
Normal file
95
scripts/example/fgfsscript
Normal 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
124
scripts/example/remote.html
Normal 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<RETURN>". 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 [<dir>] list directory
|
||||
dump dump current state (in xml)
|
||||
cd <dir> cd to a directory, '..' to move back
|
||||
pwd display your current path
|
||||
get <var> show the value of a parameter
|
||||
show <var> synonym for get
|
||||
set <var> <val> set <var> to a new <val>
|
||||
data switch to raw data mode
|
||||
prompt switch to interactive mode (default)
|
||||
quit terminate connection
|
||||
|
||||
/>
|
||||
</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 minutes to connect) or afterwards. Then
|
||||
it picks a random AGL altitude and checks every 5 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&
|
||||
$ 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&
|
||||
</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>
|
||||
11
scripts/java/FGClient/README
Normal file
11
scripts/java/FGClient/README
Normal 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.
|
||||
|
||||
49
scripts/java/FGClient/build.xml
Normal file
49
scripts/java/FGClient/build.xml
Normal 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>
|
||||
25
scripts/java/FGClient/docs/javadoc/allclasses-frame.html
Normal file
25
scripts/java/FGClient/docs/javadoc/allclasses-frame.html
Normal 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>
|
||||
91
scripts/java/FGClient/docs/javadoc/deprecated-list.html
Normal file
91
scripts/java/FGClient/docs/javadoc/deprecated-list.html
Normal 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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Deprecated</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Deprecated</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="deprecated-list.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
144
scripts/java/FGClient/docs/javadoc/help-doc.html
Normal file
144
scripts/java/FGClient/docs/javadoc/help-doc.html
Normal 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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Help</B></FONT> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Help</B></FONT> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="help-doc.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
153
scripts/java/FGClient/docs/javadoc/index-all.html
Normal file
153
scripts/java/FGClient/docs/javadoc/index-all.html
Normal 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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Index</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Index</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="index-all.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
22
scripts/java/FGClient/docs/javadoc/index.html
Normal file
22
scripts/java/FGClient/docs/javadoc/index.html
Normal 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>
|
||||
@@ -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> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="class-use/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV CLASS
|
||||
NEXT CLASS</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
|
||||
SUMMARY: INNER | FIELD | <A HREF="#constructor_summary">CONSTR</A> | <A HREF="#method_summary">METHOD</A></FONT></TD>
|
||||
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
|
||||
DETAIL: FIELD | <A HREF="#constructor_detail">CONSTR</A> | <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> host,
|
||||
int port)</CODE>
|
||||
|
||||
<BR>
|
||||
Constructor.</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<!-- ========== 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> void</CODE></FONT></TD>
|
||||
<TD><CODE><B><A HREF="../../../org/flightgear/fgfsclient/FGFSConnection.html#close()">close</A></B>()</CODE>
|
||||
|
||||
<BR>
|
||||
Close the connection to FlightGear.</TD>
|
||||
</TR>
|
||||
<TR BGCOLOR="white" CLASS="TableRowColor">
|
||||
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
|
||||
<CODE> <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> name)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name,
|
||||
<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A> value)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name,
|
||||
boolean value)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name,
|
||||
double value)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name,
|
||||
float value)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name,
|
||||
int value)</CODE>
|
||||
|
||||
<BR>
|
||||
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> 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> name,
|
||||
long value)</CODE>
|
||||
|
||||
<BR>
|
||||
Set a property value from a long.</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<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>
|
||||
|
||||
<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> host,
|
||||
int 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=<port>
|
||||
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> 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> name,
|
||||
<A HREF="http://java.sun.com/products/jdk/1.3/docs/api/java/lang/String.html">String</A> 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> 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> 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> 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> 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> 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> name,
|
||||
boolean 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> name,
|
||||
int 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> name,
|
||||
long 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> name,
|
||||
float 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> name,
|
||||
double 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> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Class</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="class-use/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV CLASS
|
||||
NEXT CLASS</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
|
||||
SUMMARY: INNER | FIELD | <A HREF="#constructor_summary">CONSTR</A> | <A HREF="#method_summary">METHOD</A></FONT></TD>
|
||||
<TD VALIGN="top" CLASS="NavBarCell3"><FONT SIZE="-2">
|
||||
DETAIL: FIELD | <A HREF="#constructor_detail">CONSTR</A> | <A HREF="#method_detail">METHOD</A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../org/flightgear/fgfsclient/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Class</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../org/flightgear/fgfsclient/FGFSConnection.html"><FONT CLASS="NavBarFont1"><B>Class</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="FGFSConnection.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -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>
|
||||
<FONT CLASS="FrameItemFont">
|
||||
<BR>
|
||||
<A HREF="FGFSConnection.html" TARGET="classFrame">FGFSConnection</A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -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"> <FONT CLASS="NavBarFont1Rev"><B>Package</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-use.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV PACKAGE
|
||||
NEXT PACKAGE</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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>
|
||||
|
||||
|
||||
<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"> <FONT CLASS="NavBarFont1Rev"><B>Package</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-use.html"><FONT CLASS="NavBarFont1"><B>Use</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV PACKAGE
|
||||
NEXT PACKAGE</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="package-summary.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="package-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Use</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="../../../help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="../../../index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="package-use.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
101
scripts/java/FGClient/docs/javadoc/overview-tree.html
Normal file
101
scripts/java/FGClient/docs/javadoc/overview-tree.html
Normal 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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#FFFFFF" CLASS="NavBarCell1Rev"> <FONT CLASS="NavBarFont1Rev"><B>Tree</B></FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="overview-tree.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
1
scripts/java/FGClient/docs/javadoc/package-list
Normal file
1
scripts/java/FGClient/docs/javadoc/package-list
Normal file
@@ -0,0 +1 @@
|
||||
org.flightgear.fgfsclient
|
||||
26
scripts/java/FGClient/docs/javadoc/packages.html
Normal file
26
scripts/java/FGClient/docs/javadoc/packages.html
Normal 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>
|
||||
<A HREF="index.html">Frame version</A>
|
||||
<BR>
|
||||
<A HREF="org/flightgear/fgfsclient/package-summary.html">Non-frame version.</A></CENTER>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
91
scripts/java/FGClient/docs/javadoc/serialized-form.html
Normal file
91
scripts/java/FGClient/docs/javadoc/serialized-form.html
Normal 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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<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> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Class</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <FONT CLASS="NavBarFont1">Use</FONT> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="org/flightgear/fgfsclient/package-tree.html"><FONT CLASS="NavBarFont1"><B>Tree</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="deprecated-list.html"><FONT CLASS="NavBarFont1"><B>Deprecated</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="index-all.html"><FONT CLASS="NavBarFont1"><B>Index</B></FONT></A> </TD>
|
||||
<TD BGCOLOR="#EEEEFF" CLASS="NavBarCell1"> <A HREF="help-doc.html"><FONT CLASS="NavBarFont1"><B>Help</B></FONT></A> </TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</TD>
|
||||
<TD ALIGN="right" VALIGN="top" ROWSPAN=3><EM>
|
||||
</EM>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
PREV
|
||||
NEXT</FONT></TD>
|
||||
<TD BGCOLOR="white" CLASS="NavBarCell2"><FONT SIZE="-2">
|
||||
<A HREF="index.html" TARGET="_top"><B>FRAMES</B></A>
|
||||
<A HREF="serialized-form.html" TARGET="_top"><B>NO FRAMES</B></A></FONT></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<!-- =========== END OF NAVBAR =========== -->
|
||||
|
||||
<HR>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
29
scripts/java/FGClient/docs/javadoc/stylesheet.css
Normal file
29
scripts/java/FGClient/docs/javadoc/stylesheet.css
Normal 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;}
|
||||
|
||||
BIN
scripts/java/FGClient/fgfsclient.jar
Normal file
BIN
scripts/java/FGClient/fgfsclient.jar
Normal file
Binary file not shown.
2
scripts/java/FGClient/main-class.txt
Normal file
2
scripts/java/FGClient/main-class.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: FGFSDemo
|
||||
178
scripts/java/FGClient/src/FGFSDemo.java
Normal file
178
scripts/java/FGClient/src/FGFSDemo.java
Normal 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
|
||||
@@ -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=<port>
|
||||
* 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
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
765
scripts/perl/dafif/build_ils.pl
Executable 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";
|
||||
}
|
||||
21
scripts/perl/dafif/dafift2fix.pl
Executable file
21
scripts/perl/dafif/dafift2fix.pl
Executable 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
566
scripts/perl/dafif/dafift2ils.pl
Executable 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
160
scripts/perl/dafif/dafift2nav.pl
Executable 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
109
scripts/perl/examples/aircraft.pl
Executable 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 );
|
||||
}
|
||||
154
scripts/perl/examples/autopilot.pl
Executable file
154
scripts/perl/examples/autopilot.pl
Executable 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", "" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
138
scripts/perl/examples/environment.pl
Executable file
138
scripts/perl/examples/environment.pl
Executable 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" );
|
||||
}
|
||||
88
scripts/perl/examples/find_elevations.pl
Executable file
88
scripts/perl/examples/find_elevations.pl
Executable 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;
|
||||
91
scripts/perl/examples/flyplan.pl
Executable file
91
scripts/perl/examples/flyplan.pl
Executable 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
209
scripts/perl/examples/logging.pl
Executable 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
107
scripts/perl/examples/position.pl
Executable 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
79
scripts/perl/examples/reset.pl
Executable 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
84
scripts/perl/examples/telnet.pl
Normal file
84
scripts/perl/examples/telnet.pl
Normal 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
|
||||
159
scripts/perl/scenery/calc-tile.pl
Normal file
159
scripts/perl/scenery/calc-tile.pl
Normal 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
117
scripts/perl/traffic/conf2xml.pl
Executable 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";
|
||||
}
|
||||
99
scripts/perl/traffic/xml2conf.pl
Executable file
99
scripts/perl/traffic/xml2conf.pl
Executable 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
509
scripts/perl/web/mkindex.pl
Executable 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
195
scripts/python/FGFSDemo.py
Executable 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()
|
||||
|
||||
211
scripts/python/FlightGear.py
Normal file
211
scripts/python/FlightGear.py
Normal 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")
|
||||
|
||||
26
scripts/python/TerraSync/terrasync.py
Executable file
26
scripts/python/TerraSync/terrasync.py
Executable 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()
|
||||
0
scripts/python/TerraSync/terrasync/__init__.py
Normal file
0
scripts/python/TerraSync/terrasync/__init__.py
Normal file
105
scripts/python/TerraSync/terrasync/dirindex.py
Normal file
105
scripts/python/TerraSync/terrasync/dirindex.py
Normal 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))
|
||||
79
scripts/python/TerraSync/terrasync/exceptions.py
Normal file
79
scripts/python/TerraSync/terrasync/exceptions.py
Normal 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"
|
||||
743
scripts/python/TerraSync/terrasync/main.py
Executable file
743
scripts/python/TerraSync/terrasync/main.py
Executable 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)
|
||||
506
scripts/python/TerraSync/terrasync/virtual_path.py
Normal file
506
scripts/python/TerraSync/terrasync/virtual_path.py
Normal 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 path’s 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 path’s 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 doesn’t 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 doesn’t 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()
|
||||
0
scripts/python/TerraSync/tests/__init__.py
Normal file
0
scripts/python/TerraSync/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
d:some\illegal directory name with a backslash:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
f:some\illegal file name with a backslash:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
t:some\illegal tarball name with a backslash.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
d:..:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
||||
@@ -0,0 +1,2 @@
|
||||
version:1
|
||||
path:some/path/with/a/../component
|
||||
@@ -0,0 +1,2 @@
|
||||
version:1
|
||||
path:some/path/non-ASCII chars like é, ê, €, Œ, Ÿ, etc./foo/bar
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
f:..:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
|
||||
@@ -0,0 +1,2 @@
|
||||
version:1
|
||||
path:some/path/that/contains \ a/backslash
|
||||
@@ -0,0 +1,2 @@
|
||||
version:1
|
||||
path:/some/path/that/starts/with/a/slash
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
d:some/illegal directory name with a slash:378b3dd58ce3058f2992b70aa5ecf8947a4d7f9e
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
f:some/illegal file name with a slash:4cbf3d1746a1249bff7809e4b079dd80cfce594c:123
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
t:some/illegal tarball name with a slash.tgz:b63a067d82824f158d6bde66f9e76654274277fe:1234567
|
||||
@@ -0,0 +1,3 @@
|
||||
version:1
|
||||
path:some/path
|
||||
t:..:b63a067d82824f158d6bde66f9e76654274277fe:1234567
|
||||
@@ -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
|
||||
95
scripts/python/TerraSync/tests/test_dirindex.py
Normal file
95
scripts/python/TerraSync/tests/test_dirindex.py
Normal 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"))
|
||||
357
scripts/python/TerraSync/tests/test_virtual_path.py
Normal file
357
scripts/python/TerraSync/tests/test_virtual_path.py
Normal 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
49
scripts/python/demo.py
Executable 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
301
scripts/python/nasal_api_doc.py
Executable 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> \
|
||||
<a href="http://wiki.flightgear.org/Nasal_scripting_language">Flightgear Nasal documentation</a>\n<div style="float:right;"> '
|
||||
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> <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('<','<').replace('>','>')
|
||||
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()
|
||||
102
scripts/python/performance_replay.py
Executable file
102
scripts/python/performance_replay.py
Executable 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
847
scripts/python/recordreplay.py
Executable 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
165
scripts/python/video.py
Executable 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
62
scripts/syntax/ac3d.vim
Normal 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
|
||||
870
scripts/syntax/gedit/nasal.lang
Normal file
870
scripts/syntax/gedit/nasal.lang
Normal 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>(?<![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>(?<![\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>[-~+*/!=?<>]</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>(?<=['"])</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">
|
||||
(?<=")
|
||||
(?:
|
||||
(\]/)?\%{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 "]/"
|
||||
|(?<=prop\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[? # or a word like in setprop("foo-bar");
|
||||
|(?<=\.getNode\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[? # or a word like in props.globals.getNode("foo-bar["~index~"]");
|
||||
|(?<=\.alias\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[? # etc.
|
||||
|(?<=\.initNode\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.getValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.getBoolValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.setValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.setBoolValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.setIntValue\(") [-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.setDoubleValue\(")[-a-zA-Z0-9_]+(?:\[([0-9]+)\])?\[?
|
||||
|(?<=\.getChild\(") [-a-zA-Z0-9_]+ # while these are simple names
|
||||
|(?<=\.getChildren\(") [-a-zA-Z0-9_]+
|
||||
|(?<=\.removeChild\(") [-a-zA-Z0-9_]+
|
||||
|(?<=\.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>(?<![\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>(?<![\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>((?<![\w\.])|(?<=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>(?<=\.)</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><!\[CDATA\[</start>
|
||||
<end>\]\]></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>&(#([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>[&<>]</match>
|
||||
</context>
|
||||
</include>
|
||||
</context>
|
||||
|
||||
<!-- For use when embedded in XML, e.g. in a <script> tag. -->
|
||||
<context id="xml">
|
||||
<include>
|
||||
<context id="script">
|
||||
<start>(?<=<script>)</start><!-- look behind for <script> as a start -->
|
||||
<end>(?=</script>)</end><!-- look ahead for </script> as an end -->
|
||||
<include>
|
||||
<context ref="xml-extras"/>
|
||||
<context ref="nasal"/>
|
||||
</include>
|
||||
</context>
|
||||
<context id="dialog-open">
|
||||
<start>(?<=<open>)</start><!-- look behind for <open> as a start -->
|
||||
<end>(?=</open>)</end><!-- look ahead for </open> as an end -->
|
||||
<include>
|
||||
<context ref="xml-extras"/>
|
||||
<context ref="nasal"/>
|
||||
</include>
|
||||
</context>
|
||||
<context id="dialog-close">
|
||||
<start>(?<=<close>)</start><!-- look behind for <close> as a start -->
|
||||
<end>(?=</close>)</end><!-- look ahead for </close> as an end -->
|
||||
<include>
|
||||
<context ref="xml-extras"/>
|
||||
<context ref="nasal"/>
|
||||
</include>
|
||||
</context>
|
||||
<context id="model-load">
|
||||
<start>(?<=<load>)</start><!-- look behind for <load> as a start -->
|
||||
<end>(?=</load>)</end><!-- look ahead for </load> as an end -->
|
||||
<include>
|
||||
<context ref="xml-extras"/>
|
||||
<context ref="nasal"/>
|
||||
</include>
|
||||
</context>
|
||||
<context id="model-unload">
|
||||
<start>(?<=<unload>)</start><!-- look behind for <unload> as a start -->
|
||||
<end>(?=</unload>)</end><!-- look ahead for </unload> as an end -->
|
||||
<include>
|
||||
<context ref="xml-extras"/>
|
||||
<context ref="nasal"/>
|
||||
</include>
|
||||
</context>
|
||||
</include>
|
||||
</context>
|
||||
</definitions>
|
||||
</language>
|
||||
73
scripts/syntax/gedit/nasal.xml
Normal file
73
scripts/syntax/gedit/nasal.xml
Normal 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
161
scripts/syntax/nasal.vim
Normal 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
113
scripts/tools/fg-check
Executable 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
366
scripts/tools/fg-submit
Executable 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
302
scripts/tools/freq
Executable 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
289
scripts/tools/lsprop
Executable 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())
|
||||
|
||||
Reference in New Issue
Block a user