Astro-satpass

 view release on metacpan or  search on metacpan

script/satpass  view on Meta::CPAN

########################################################################
#
#	Initialization
#

our $VERSION = '0.135';

use constant LINFMT => <<eod;
%s %s %8.4f %9.4f %7.1f %-4s %s
eod

use constant LINFMT_MAG => <<eod;
%s %s %8.4f %9.4f %7.1f %4s %-4s %-4s
eod

use constant BGFMT => <<eod;
%s %s %8.4f %9.4f              %s
eod

use constant SUN_CLASS_DEFAULT	=> 'Astro::Coord::ECI::Sun';

my @bodies;
my @sky = (
    SUN_CLASS_DEFAULT->new (),
    Astro::Coord::ECI::Moon->new (),
);

my %opt;	# Options passed when we were invoked.
my %cmdconfig;	# How option parsing is to be comfigured for command.
my %cmdopt;	# Options for individual satpass commands.
my %cmdlgl;	# Legal options for a given command.
my %cmdquote;	# 1 if we keep quotes when parsing the command.

my %exported;	# True if the parameter is exported.
my %parm = (
##    almanac_horizon	=> 0, set below
    appulse => 0,
    autoheight => 1,
    background => 1,
    backdate => 1,
    country => 'us',
    date_format => '%a %d-%b-%Y',
    debug => 0,
    desired_equinox_dynamical => 0,
    echo => 0,
    edge_of_earths_shadow => 1,
    ellipsoid => Astro::Coord::ECI->get ('ellipsoid'),
    error_out => 0,
    exact_event => 1,
    explicit_macro_delete => 0,
    extinction => 1,
    flare_mag_day => -6,
    flare_mag_night => 0,
    geometric => 1,
    gmt => 0,
##    horizon => 20, set below
    illum	=> SUN_CLASS_DEFAULT,
    local_coord => 'azel_rng',
    model => 'model',
    pass_threshold => undef,
    prompt => 'satpass>',
##    simbad_url => 'simbad.harvard.edu',
##    simbad_version => 3,
    refraction => 1,
    simbad_url => 'simbad.u-strasbg.fr',
    simbad_version => 4,
    singleton => 0,
    sun		=> SUN_CLASS_DEFAULT,
    time_format => '%H:%M:%S',
##    timing => 0,
##    twilignt => 'civil', set below
    verbose => 0,
    visible => 1,
    webcmd => ''
    );
$parm{tz} = $ENV{TZ} if $ENV{TZ};

my %macro;

my %mutator = (
    almanac_horizon => \&_set_almanac_horizon,
    appulse => \&_set_angle,
    autoheight => \&_set_unmodified,
    backdate => \&_set_unmodified,
    background => \&_set_unmodified,
    country => \&_set_unmodified,
    date_format => \&_set_unmodified,
    desired_equinox_dynamical => \&_set_time,
    debug => \&_set_unmodified,
    echo => \&_set_unmodified,
    edge_of_earths_shadow => \&_set_unmodified,
    ellipsoid => \&_set_ellipsoid,
    error_out => \&_set_unmodified,
    exact_event => \&_set_unmodified,
    explicit_macro_delete => \&_set_unmodified,
    extinction => \&_set_unmodified,
    flare_mag_day => \&_set_unmodified,
    flare_mag_night => \&_set_unmodified,
    geometric => \&_set_unmodified,
    gmt => \&_set_unmodified,
    height => \&_set_unmodified,
    horizon => \&_set_angle,
    illum	=> \&_set_illum_class,
    latitude => \&_set_angle,
    local_coord => \&_set_local_coord,
    location => \&_set_unmodified,
    longitude => \&_set_angle,
    model => \&_set_unmodified,
    perltime => \&_set_perltime,
    prompt => \&_set_unmodified,
    refraction => \&_set_unmodified,
    simbad_url => \&_set_unmodified,
    simbad_version => \&_set_simbad_version,
    singleton => \&_set_unmodified,
    sun		=> \&_set_sun_class,
    pass_threshold => \&_set_angle_or_undef,
    time_format => \&_set_unmodified,
##    timing => \&_set_unmodified,
    twilight => \&_set_twilight,  # 'civil', 'nautical', 'astronomical'
				# (or a unique abbreviation thereof),
				# or degrees below the geometric
				# horizon.
    tz => \&_set_tz,
    verbose => \&_set_unmodified, # 0 = events only
				# 1 = whenever above horizon
				# 2 = anytime
    visible => \&_set_unmodified, # 1 = only if sun down & sat illuminated
    webcmd => \&_set_webcmd,	# Command to spawn for web pages
    );

my %accessor = (
    desired_equinox_dynamical => \&_get_time,
);
foreach (keys %mutator) {$accessor{$_} ||= \&_show_unmodified};

my @bearing = qw{N NE E SE S SW W NW};

my %twilight_def = (
    civil => deg2rad (-6),
    nautical => deg2rad (-12),
    astronomical => deg2rad (-18),
    );
my %twilight_abbr = abbrev (keys %twilight_def);

set( twilight	=> 'civil' );
set( horizon	=> 20 );
set( almanac_horizon => 0 );

my $inifn = $^O eq 'MSWin32' || $^O eq 'VMS' || $^O eq 'MacOS' ?
    'satpass.ini' : '.satpass';

my $inifil = $ENV{SATPASSINI} ? $ENV{SATPASSINI} :
    $^O eq 'VMS' ? "SYS\$LOGIN:$inifn" :
    $^O eq 'MacOS' ? $inifn :
    $ENV{HOME} ? "$ENV{HOME}/$inifn" :
    $ENV{LOGDIR} ? "$ENV{LOGDIR}/$inifn" :
    $ENV{USERPROFILE} ? "$ENV{USERPROFILE}/$inifn" : undef;

sub _format_location;	# Predeclare, so I do not need explicit STDOUT.
sub _time ();		# Predeclare, since I intend to redefine.

my $reader = eval {
    -t STDIN
	or return;
    require Term::ReadLine;
    my $rl = Term::ReadLine->new( 'Predict satellite passes' )
	or return;
    return sub {
	my ( $fh, @arg ) = @_;
	return -t $fh ?  $rl->readline( @arg ) : $fh->getline();

script/satpass  view on Meta::CPAN

    };

Getopt::Long::Configure (qw{pass_through});	# Need because of relative time format.

push @frame, {
	args => [],
	cntind => '',
	lines => [],
	parm => {},
	stdin => $fh,
	stdout => select (),
	};

$opt{version} and delete $opt{filter};

$opt{filter} or print <<eod;

satpass $VERSION - Satellite pass predictor
based on Astro::Coord::ECI @{[Astro::Coord::ECI->VERSION]}
Perl $Config{version} on $Config{osname}

Copyright 2005-2026 by Thomas R. Wyant, III.

Enter 'help' for help. See the help for terms of use.

As of release 0.057 this script is deprecated. See 'NOTES' in the help
for details.

eod
$opt{version} and exit;

########################################################################
#
#	Main program loop.
#

@ARGV and source (_memio ('<', \(join "\n", @ARGV)));

{	# Local symbol block
    my %inodes;
##    foreach my $fn ($inifn, $inifil) {	# Reverse of intended order
    foreach my $fn ($inifil) {	# Reverse of intended order
	next unless $fn && -e $fn;
	my $inode = (stat (_))[1];
	next if $inodes{$inode};
	$inodes{$inode} = $fn;
	source ($fn);
	}
    }	# End local symbol block

while (1) {

#	Select the default output

    select ($frame[$#frame]{stdout});

#	Get the next command, wherever it comes from.

    my $cntind = $frame[$#frame]{cntind} || '';
    my $buffer = @frame > 1 ? $fh->getline () :
	$reader->( $fh, "$cntind$parm{prompt} " );

#	If it was end-of-file, pop one level off the stack of input
#	files. If it's empty, we exit the execution loop. Otherwise
#	we select the proper STDOUT and redo the loop.

    defined $buffer or do {
	defined ($fh = _frame_pop ()) or last;
	redo;
	};

#	Canonicalize the input buffer.

    chomp $buffer;
    $buffer =~ s/^\s+//;
    $buffer =~ s/\s+$//;

#	If the input is empty or a comment, redo the loop.

    next unless $buffer;
    next if $buffer =~ m/^#/;

#	If we're a continuation line, save it in the line
#	buffer and redo the loop.

    $buffer =~ s/\\$// and do {
	push @{$frame[$#frame]{lines}}, $buffer;
	$frame[$#frame]{cntind} = '_';
	next
    };

#	Prepend all saved continuations (if any) to the line.

    @{$frame[$#frame]{lines}} and do {
	$buffer = join ' ', @{$frame[$#frame]{lines}}, $buffer;
	@{$frame[$#frame]{lines}} = ();
	$frame[$#frame]{cntind} = '';
	};

#	Interpolate positional parameters. This is not done if we
#	have the 'macro' command, because in that case we want to
#	defer the expansion until the macro is expanded. Note that
#	the way to pass a dollar sign is to double it.

    eval {$buffer =~ s/\$(?:(\w+)|\{(\w+)(?:\:(.*?))\}|(.))/
	    defined $4 ? $4 : _sub_arg ($1 || $2, $3 || '',
	    $frame[$#frame]->{args} || [])/mgex
	    unless $buffer =~ m/^\s*macro\b/};
    $@ and do {
	warn $@;
	defined ($fh = _frame_pop ()) or last;
	redo;
	};

#	Help the parser deal with things like '.' and '!'

    $buffer =~ s/^(\W)\s*/$1 /;
    $buffer =~ s/\s+$//;

#	Echo the line, if this is selected.

    $parm{echo} && (@frame > 1 || !-t $fh) and
	print "$cntind$parm{prompt} $buffer\n";

#	Pull the verb off by brute force, because we need to know what
#	it is before we know how to parse the rest of the line.

    my ($cmdspec, $rest) = split '\s+', $buffer, 2;
    $cmdspec = $alias{$cmdspec} if $alias{$cmdspec};

#	Pull the namespace off the command spec. We make use
#	of the $cmdspec argument later as a macro name, since
#	with the 'core.' on the front it will be invalid, and
#	force the use of the built-in.

    (my $verb = $cmdspec) =~ s/^core\.//;
    $verb = $alias{$verb} if $alias{$verb};

#	Parse the line, pulling off output redirection as we go.

    my $redout = '';
    my @args = parse_line ('\s+', $cmdquote{$verb} || 0, $rest);
    $rest && !@args and do {
	warn <<eod;
Error - Malformed command failed to parse:
        $rest
eod
	next;
    };
    {	# Local symbol block for hacking pipe off commands
	my @pipe;
	@args = map {
	    (@pipe && !$redout) ? do {push @pipe, $_; ()} :
	    m/^([>|])/ ?
		$1 eq '|' ? do {push @pipe, $_; ()} :
		    do {$redout = $_; ()} :
	    $redout =~ m/^>+$/ ? do {$redout .= $_; ()} :
	    $_} @args;
	@pipe and $redout = join ' ', @pipe;
    }	# End local symbol block.

#	Do pseudo tilde expansion.

    eval {
	$redout =~ s/^(>+)(~.*)/ $1 . _tilde_expand ($2) /e;
	1;
    } or do {
	warn $@;
	next;
    };

#	Special-case 'exit' to drop out of the input loop.

    $verb eq 'exit' and last;

#	Arbitrarily disallow syntactically invalid commands.

    $verb =~ m/^_/ || $verb =~ m/\W/ and do {
	warn <<eod;
Error - Verb '$verb' not recognized.
eod
	next;
	};

script/satpass  view on Meta::CPAN

 satpass> # Keep only the HST and the ISS
 satpass> choose hst iss

The one command-specific option is -epoch. It takes as an argument any
valid time, and retains the most recent set of elements for each object
which are before the given time. If there are none before the given
time for a given object, the earliest set of elements is retained.

For example:

 satpass> # Get some historical data
 satpass> st search_name zarya -start 2006/04/01 \
 _satpass> -end 2006/04/07
 satpass> # Keep the set relevant to noon the 6th
 satpass> choose -epoch 'Apr 7 2006 noon'

Most commands that operate on the observing list choose the correct
elements based on the time (or the start time) specified on the
command. So barring bugs, you may not need this option unless you are
trying to assemble and save a set of elements relevant to a given time
in the past.

=item clear

 satpass> clear

This command clears the observing list. It is not an error to issue it
with the list already clear.

=item drop

 satpass> drop name_or_id ...

This command removes the objects named in the command from the
observing list, retaining all others. Either names, NORAD ID numbers,
or a mixture may be given. Numeric items are matched against the NORAD
IDs of the items in the observing list; non-numeric items are made into
case-insensitive regular expressions and matched against the names of
the items if any.

=item echo

 satpass> echo ...

This command just prints its arguments to standard output. Environment
variable substitution and pseudo-redirection is done. You may not get
out exactly what you put in, because the output is reconstructed from
the tokens left after substitution and redirection.

This was added on a whim, to prevent having to shell out to get some
random text in the output.

=item exit

 satpass> exit

This command causes this script to terminate immediately. If issued
from a 'source' file, this is done without giving control back to the
user.

'bye' and 'quit' are synonyms. End-of-file at the command prompt will
also cause this script to terminate.

=item export

 satpass> export name value

This command exports the given value to an environment variable. This
value will be available to spawned commands, but will not persist after
we exit.

If the name is the name of a parameter, the value is optional, but if
supplied will be used to set the parameter. The environment variable
is set from the value of the parameter, and will track changes in it.

=item flare

 satpass> flare start_time end_time

This command predicts flares for any bodies in the observing list
capable of generating them. Currently, this means Iridium satellites.
The start_time defaults to 'today noon', and the end_time to +7.

In addition to the global options, the following options are legal
for the flare command:

-am ignores morning flares -- that is, those after midnight but before
morning twilight.

-choose chooses bodies from the observing list. It works the same way
as the L<choose|/choose> command, but does not alter the observing
list. You can specify multiple bodies by specifying -choose multiple
times, or by separating your choices with commas. If -choose is not
specified, the whole observing list is used.

-day ignores daytime flares -- that is, those between morning twilight
and evening twilight.

-pm ignores evening flares -- that is, those between evening twilight
and midnight.

-questionable requests that satellites whose status is questionable
(i.e. 'S') be included. Typically these are spares, or moving
between planes. You may use -spare as a synonym for this.

-quiet suppresses any errors generated by running the orbital model.
These are typically from obsolete data, and/or decayed satellites.
Bodies that produce errors will not be included in the output.

See the L</SPECIFYING TIMES> topic below for how to specify times.

For example, assuming the observers' location has already been set, you
can predict flares for the next two days as follows:

 satpass> # Get data for Iridium satellites
 satpass> st celestrak iridium
 satpass> # Use shorter twilight for Iridium flares
 satpass> set twilight 3
 satpass> # We are not interested in range to flare
 satpass> set local_coord azel
 satpass> # Supress date line in output, include in time

script/satpass  view on Meta::CPAN

release to release.

The default is 0.

=item desired_equinox_dynamical

This time parameter specifies the desired equinox for equatorial and ecliptic
coordinates. It is specified as a dynamical time, or as 0, '', or undef
if you want whatever the various models give (generally the current
equinox, or something reasonably close to this).

This parameter is used to precess equatorial coordinates to the correct
equinox for display. Any legal satpass time specification will parse,
but you probably want to specify an absolute time in the UT zone, e.g.
'1-Jan-2000 12:00 UT' for J2000.0. Yes, technically UT is universal, but
values specified for this setting are handled as dynamical. See
L<SPECIFYING TIMES|/SPECIFYING TIMES> for the full story on how to
specify times.

The only reason I can think of to set this is if you are displaying
equatorial coordinates for the 'flare', 'pass', or 'position' commands,
and want the coordinates for a given epoch.

The default is 0.

=item echo

This Boolean parameter causes commands that did not come from the keyboard to
be echoed. Set it to a non-zero value to watch your scripts run, or to
debug your macros, since the echo takes place B<after> parameter
substitution has occurred.

The default is 0.

=item edge_of_earths_shadow

This numeric parameter specifies the offset in elevation of the edge of the
Earth's shadow from the center of the illuminating body (typically the
Sun) as seen from a body in space. The offset is in units of the
apparent radius of the illuminating body, so that setting it to C<1>
specifies the edge of the umbra, <-1> specifies the edge of the
penumbra, and C<0> specifies the middle of the penumbra. This parameter
corresponds to the same-named L<Astro::Coord::ECI|Astro::Coord::ECI>
parameter.

The default is 1 (i.e. edge of umbra).

=item ellipsoid

This string parameter specifies the name of the reference ellipsoid to be used
to model the shape of the earth. Any reference ellipsoid supported by
Astro::Coord::ECI may be used. For details,
see L<Astro::Coord::ECI|Astro::Coord::ECI>.

The default is 'WGS84'.

=item error_out

This Boolean parameter specifies the behavior on encountering an error. If
true, all macros, source files, etc are aborted and control is returned
to the command prompt. If standard in is not a terminal, we exit. If
false, we ignore the error.

The default is 0 (i.e. false).

=item exact_event

This Boolean parameter specifies whether visibility events (rise, set, max, 
into or out of shadow, beginning or end of twilight) should be computed
to the nearest second. If false, such events are reported to the step
size specified when the 'pass' command was issued.

The default is 1 (i.e. true).

=item explicit_macro_delete

This Boolean parameter specifies whether an explicit -delete qualifier is needed
to delete a macro. If false, 'macro foo' deletes macro foo. If true,
'macro foo' lists macro foo.

The default is 0 (i.e. false). B<This will change>, as deletion without
an explicit -delete is deprecated.

=item extinction

This Boolean parameter specifies whether magnitude estimates take atmospheric
extinction into account. It should be set true if you are interested
in measured brightness, and false if you are interested in estimating
magnitudes versus nearby stars.

The default is 1 (i.e. true).

=item flare_mag_day

This numeric parameter specifies the limiting magnitude for the flare
calculation for flares that occur during the day. For this
purpose, it is considered to be day if the elevation of the
Sun is above the L<twilight|/twilight> parameter.

The default is -6.

=item flare_mag_night

This numeric parameter specifies the limiting magnitude for the flare
calculation for flares that occur during the night. For this
purpose, it is considered to be night if the elevation of the
Sun is below the L<twilight|/twilight> parameter.

The default is 0.

=item geometric

This Boolean parameter specifies whether satellite rise and set should be
computed versus the geometric horizon or the effective horizon
specified by the 'horizon' parameter. If true, the computation is
versus the geometric horizon (elevation 0 degrees). If false, it
is versus whatever the 'horizon' parameter specifies.

The default is 1 (i.e. true).

=item gmt

script/satpass  view on Meta::CPAN

Useful for testing, maybe.

The 'meta-models' implemented are:

model - Use the normal model appropriate to the object. Currently this
means sgp4r, but this will change if the preferred model changes (at
least, if I become aware of the fact).

model4 - Use either sgp4 or sdp4 as appropriate. Right now this is the
same as 'model', but 'model4' will still run sgp4 and sdp4, even if
they are no longer the preferred models.

model4r - Synonym for sgp4r, implemented to keep the 'meta-models'
consistent.

model8 - Use either sgp8 or sdp8 as appropriate.

The default is 'model'.

=item pass_threshold

This angle or undef parameter corresponds to the
L<Astro::Coord::ECI::TLE|Astro::Coord::ECI::TLE> C<pass_threshold>
attribute.  It is set in degrees. It can also be set to the C<undef>
value by specifying the string C<'undef'> (unquoted) as its value.

=item perltime

This Boolean parameter is a wart occasioned by the failure of
L<Date::Manip|Date::Manip> to understand summer time prior to version 6.
It is ignored if you are using L<Date::Manip|Date::Manip> 6.0 or
greater, or if you are using the internal ISO-8601 parser, since neither
needs this mechanism.

If you are using a L<Date::Manip|Date::Manip> prior to 6.0, you should
read on. This includes anyone not using Perl 5.010 or later, since Perl
5.010 is required for L<Date::Manip|Date::Manip> 6.0 and above.

This parameter specifies the time zone mechanism for date input. If
false (i.e. 0 or an empty string), L<Date::Manip|Date::Manip> does the
conversion.  If true (typically 1), L<Date::Manip|Date::Manip> is told
that the time zone is GMT, and the time zone conversion is done by
C<gmtime( timelocal( $time ) )>.

The problem this attempts to fix is that, in jurisdictions that do
summer time, Date::Manip gives the wrong time if the current time is not
summer time but the time converted is.  That is to say, with a time zone
of EST5EDT, in January, 'jan 1 noon' converts to 5:00 PM GMT. But 'jul 1
noon' does also, but should convert to 4:00 PM GMT.

If you turn this setting on, 'jul 1 noon' comes out 4:00 PM GMT even
if done in January. If you plan to parse times B<with zones> (e.g.
'jul 1 noon edt'), you should turn this setting off.

Note that at at some point this setting will be no-oped and its use
deprecated, though given the state of things this may well not happen
until this script itself starts requiring Perl 5.010.

The default is 0 (i.e. false).

=item prompt

This string parameter specifies the string used to prompt for commands.

The default is 'satpass>'.

=item refraction

This Boolean parameter specifies whether atmospheric refraction should be taken
into account in the azel() calculation.

The default is 1 (i.e. true).

=item simbad_url

This string parameter does not, strictly speaking, specify a URL, but does
specify the server to use to perform SIMBAD lookups (see the 'lookup'
subcommand of the L<sky|/sky> command). Currently-legal values are
'simbad.u-strasbg.fr' (the original site) and 'simbad.harvard.edu'
(Harvard University's mirror).

B<As of satpass 0.013_09,> the default is 'simbad.u-strasbg.fr', since
Harvard seems to be redirected to Strasbourg these days.

B<Please note that the command this parameter supports is
experimental,> and see the warnings on that command. Changes in the
command may result in this parameter becoming deprecated and/or
no-oped.

Also note that the version of SIMBAD used to access the site is
controlled by the L<simbad_version|/simbad_version> parameter.

=item simbad_version

This integer parameter specifies the version of the SIMBAD application being
used, the valid values being 3 or 4. If you set it to 3, the
Astro::SIMBAD package will be used for SIMBAD lookups. If you set it to
4, Astro::SIMBAD::Client will be used. As of early January 2007,
'simbad.u-strasbg.fr' supports both versions, though 3 is being phased
out. 'Simbad.harvard.edu' appears to me to be phasing out, and
redirected to simbad.u-strasbg.fr.

B<As of satpass 0.013_09,> the default is 4.

=item singleton

If this Boolean parameter is true, the script uses Astro::Coord::ECI::TLE::Set
objects to represent all bodies. If false, the set object is used only
if the observing list contains more than one instance of a given
NORAD ID. This is really only useful for testing purposes.

Use of the Astro::Coord::ECI::TLE::Set object causes calculations to
take about 15% longer.

The default is 0 (i.e. false).

=item sun

This string parameter specifies the name of the class to set as the 'sun'
attribute for any bodies that require it. The value must be the name of
a subclass of C<Astro::Coord::ECI::Sun>.

The default is C<'Astro::Coord::ECI::Sun>.



( run in 0.558 second using v1.01-cache-2.11-cpan-0b5f733616e )