Astro-satpass

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

  Astro::Coord::ECI::TLE 0.012:

    * Correct behavior when Scalar::Util::dualvar not present.

  Astro::Coord::ECI::TLE::Iridium 0.005:

    * Use max() and min() from Astro::Coord::ECI::Utils.

  Astro::Coord::ECI::Utils 0.009:

    * Added looks_like_number(), max(), min(), defaulting to the
      Scalar::Util or List::Util implementations if possible, but
      providing our own if not.

    * Clarify load_module() documentation.

  bin/satpass 0.017:

    * Use looks_like_number() and max() from Astro::Coord::ECI::Utils.

    * Recode _load_module to use Astro::Coord::ECI::Utils::load_module.

    * Fix clipboard code to fail gracefully if Scalar::Util::weaken
      is unavailable.

  t/{moon,sun}.t:

    * Skip singleton test if Scalar::Util::refaddr not available.

inc/My/Module/Meta.pm  view on Meta::CPAN

    my ( undef, @extra ) = @_;		# Invocant unused
##  if ( ! $self->distribution() ) {
##  }
    return +{
	'Carp'		=> 0,
	'Clone'		=> 0,
	'Data::Dumper'	=> 0,
	'Exporter'	=> 5.64,
	'IO::File'	=> 0,
	'POSIX'		=> 0,
	'Scalar::Util'	=> 1.22,	# For looks_like_number
	'Time::Local'	=> 0,
	base		=> 0,
	constant	=> 0,
	if		=> 0,
	strict		=> 0,
	warnings	=> 0,
	@extra,
    };
}

lib/Astro/Coord/ECI.pm  view on Meta::CPAN


The algorithm is from W. S. Smart's "Text-Book on Spherical Astronomy",
as reported in Jean Meeus' "Astronomical Algorithms", 2nd Edition,
Chapter 28, page 185.

=cut

sub equation_of_time {
    my ( $self, $time ) = @_;

    if ( looks_like_number( $self ) ) {
	( $self, $time ) = ( __PACKAGE__, $self );
    }
    defined $time
	or $time = $self->dynamical();

    my $epsilon = $self->obliquity( $time );
    my $y = tan($epsilon / 2);
    $y *= $y;

#	The following algorithm is from Meeus, chapter 25, page, 163 ff.

lib/Astro/Coord/ECI.pm  view on Meta::CPAN


Note that precisions greater than 4 are not defined by the standard.
This method extends the system by alternating letters (base 24) with
digits (base 10), but this is unsupported since the results will change,
possibly without notice, if the standard is extended in a manner
incompatible with this implementation.

Conversion of latitudes and longitudes to Maidenhead Grid is subject to
truncation error, perhaps more so since latitude and longitude are
specified in radians. An attempt has been made to minimize this by using
Perl's stringification of numbers to ensure that something that looks
like C<42> is not handled as C<41.999999999385>. This probably amounts
to shifting some grid squares very slightly to the north-west, but in
practice it seems to give better results for points on the boundaries of
the grid squares.

=item $coord->maidenhead( $maidenhead_loc, $height );

This method sets the geodetic location in the Maidenhead Locator System.
Height above the reference ellipsoid is not part of the system, but is
accepted anyway, in kilometers, defaulting to 0.

lib/Astro/Coord/ECI.pm  view on Meta::CPAN

Edition, Chapter 22, pages 143ff. The conversion from universal to
dynamical time comes from chapter 10, equation 10.2  on page 78.

=cut

use constant E0BASE => (21.446 / 60 + 26) / 60 + 23;

sub obliquity {
    my ( $self, $time ) = @_;

    if ( looks_like_number( $self ) ) {
	( $self, $time ) = ( __PACKAGE__, $self );
    }
    defined $time
	or $time = $self->dynamical();

    my $T = jcent2000 ($time);	# Meeus (22.1)

    my ( undef, $delta_epsilon ) = $self->nutation( $time );

    my $epsilon0 = deg2rad( ( ( 0.001813 * $T - 0.00059 ) * $T - 46.8150 )

lib/Astro/Coord/ECI.pm  view on Meta::CPAN

##	horizon	=> sub { return $_[0]->get( 'horizon' ); },
	height	=> sub { return $_[0]->dip(); },
    );

    sub _set_almanac_horizon {
	my ( $hash, $attr, $value ) = @_;
	defined $value
	    or $value = 0;	# Default
	if ( $special{$value} ) {
	    $hash->{"_$attr"} = $special{$value};
	} elsif ( looks_like_number( $value )
	    && $value >= - PIOVER2 # Not -PIOVER2 to avoid warning under 5.10.1.
	    && $value <= PIOVER2
	) {
	    $hash->{"_$attr"} = sub { return $_[0]->get( $attr ) };
	} else {
	    croak "'$value' is an invalid value for '$attr'";
	}
	$hash->{$attr} = $value;
	return SET_ACTION_NONE;
    }

lib/Astro/Coord/ECI.pm  view on Meta::CPAN

{
    my %dflt = (
	twilight	=> CIVIL_TWILIGHT,
    );

    # Any attribute specified as angle above or below the horizon.
    sub _set_elevation {
	my ( $self, $name, $value ) = @_;
	defined $value
	    or $value = ( $dflt{$name} || 0 );	# Default
	looks_like_number( $value )
	    and $value >= - PIOVER2 # Not -PIOVER2 to avoid warning under 5.10.1.
	    and $value <= PIOVER2
	    or croak "'$value' is an invalid value for '$name'";
	$self->{$name} = $value;
	return SET_ACTION_NONE;
    }
}

sub _set_station {
    my ( $hash, $attr, $value ) = @_;

lib/Astro/Coord/ECI.pm  view on Meta::CPAN

	# r = sin Theta cos Phi x + sin Theta sin Phi y + cos Theta z
	# theta = cos Theta cos Phi x + cos Theta sin Phi y - sin Theta z
	# phi = - sin Phi x + cos phi y
	#
	# and
	#
	# x = sin Theta cos Phi r + cos Theta cos Phi theta - sin Phi phi
	# y = sin Theta sin Phi r + cos Theta sin Phi theta - cos Phi phi
	# z = cos Theta r - sin Theta theta
	#
	# It looks to me like I get the Theta convention I'm using by
	# replacing sin Theta with cos Theta and cos Theta by sin Theta
	# (because Dr. Cornelius takes 0 as the positive Z axis whereas
	# I take zero as the X-Y plane) and changing the sign of theta
	# (since Dr. Cornelius' Theta increases in the negative Z
	# direction, whereas mine increases in the positive Z
	# direction).
	#
	# The document was found at http://plaza.obu.edu/corneliusk/
	# which is the page for Dr. Kevin Cornelius' Mathematical
	# Physics (PHYS 4053) course at Ouachita Baptist University in

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

use warnings;

our $VERSION = '0.133';

use base qw{ Astro::Coord::ECI Exporter };

use Astro::Coord::ECI::Utils qw{ :params :ref :greg_time deg2rad distsq
    dynamical_delta embodies find_first_true fold_case
    __format_epoch_time_usec
    format_space_track_json_time gm_strftime load_module local_strftime
    looks_like_number max min
    mod2pi PI PIOVER2 rad2deg SECSPERDAY TWOPI thetag
    __default_station
    @CARP_NOT
    };

use Carp qw{carp croak confess};
use Data::Dumper;
use IO::File;
use POSIX qw{ ceil floor fmod modf };
use Scalar::Util ();

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

#		object unmodified, with the arguments
#		being the object, the name of the attribute,
#		and the new value of the attribute. The code
#		must make the needed changes to the attribute, and
#		return 0 or 1, interpreted as above.

my %attrib = (
    backdate => 0,
    effective => sub {
	my ($self, $name, $value) = @_;
	if ( defined $value && ! looks_like_number( $value ) ) {
	    if ( $value =~ m{ \A ([0-9]+) / ([0-9]+) / ([0-9]+) : ([0-9]+) :
		    ([0-9]+ (?: [.] [0-9]* )? ) \z }smx ) {
		$value = greg_time_gm( 0, 0, 0, 1, 0,
		    __tle_year_to_Gregorian_year( $1 + 0 ) ) + (
		    (($2 - 1) * 24 + $3) * 60 + $4) * 60 + $5;
	    } else {
		carp "Invalid effective date '$value'";
		$value = undef;
	    }
	}

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

    illum	=> \&_set_illum,
    launch_year => \&_set_intldes_part,
    launch_num	=> \&_set_intldes_part,
    launch_piece	=> \&_set_intldes_part,
    object_type	=> \&_set_object_type,
    ordinal	=> \&_set_optional_unsigned_integer_no_reinit,
    originator	=> 0,
    pass_threshold => sub {
	my ($self, $name, $value) = @_;
	not defined $value
	    or looks_like_number( $value )
	    or carp "Invalid $name '$value'";
	$self->{$name} = $value;
	return 0;
    },
    reblessable => sub {
	my $doit = !$_[0]{$_[1]} && $_[2] && $_[0]->get ('id');
	$_[0]{$_[1]} = $_[2];
	$doit and $_[0]->rebless ();
	return 0;
    },

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

Otherwise it is derived from the common name using an algorithm similar
to the one used by the Space Track web site. This algorithm will not
work if the common name is not available, or if it does not conform to
the Space Track naming conventions. Known or suspected differences from
the algorithm described at the bottom of the Satellite Box Score page
include:

* The C<Astro::Coord::ECI::TLE> algorithm is not case-sensitive. The
Space Track algorithm appears to assume all upper-case.

* The C<Astro::Coord::ECI::TLE> algorithm looks for words (that is,
alphanumeric strings delimited by non-alphanumeric characters), whereas
the Space Track documentation seems to say it just looks for substrings.
However, implementing the documented algorithm literally results in OID
20479 'DEBUT (ORIZURU)' being classified as debris, whereas Space Track
returns it in response to a query for name 'deb' that excludes debris.

The possible returns are:

C<< BODY_TYPE_UNKNOWN => dualvar( 0, 'unknown' ) >> if the value of the
C<name> attribute is C<undef>, or if it is empty or contains only
white space.

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

	local $_ = undef;	# while (<>) ... does not localize $_.
	while ( <$fh> ) {
	    chomp;
	    m/ \A \s* (?: \# | \z ) /smx
		and next;	# Extension to syntax.
	    $parse_info->{pad} > length
		and $_ = sprintf '%-*s', $parse_info->{pad}, $_;
	    # Perl 5.8 and below require an explicit buffer to unpack.
	    my ( $id, $mag ) = unpack $parse_info->{template}, $_;
	    $mag =~ s/ \s+ //smxg;
	    looks_like_number( $mag )
		or next;
	    $mag{ _normalize_oid( $id ) } = $mag + $parse_info->{mag_offset};
	}
	close $fh;
	%magnitude_table = %mag;
    };

    my %cmd_def = (
	add	=> sub {
	    my ( $id, $mag ) = @_;
	    defined $id
		and $id =~ m/ \A [0-9]+ \z /smx
		and defined $mag
		and looks_like_number( $mag )
		or croak 'magnitude_table add needs an OID and a magnitude';
	    $magnitude_table{ _normalize_oid( $id ) } = $mag;
	    return;
	},
	adjust	=> sub {
	    my ( $adj ) = @_;
	    if ( defined $adj ) {
		looks_like_number( $adj )
		    or croak 'magnitude_table adjust needs a floating point number';
		$magnitude_adjust = $adj;
		return;
	    } else {
		return $magnitude_adjust;
	    }
	},
	clear	=> sub {
	    %magnitude_table = ();
	    return;

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

	},
	magnitude	=> sub {
	    my ( $tbl ) = @_;
	    HASH_REF eq ref $tbl
		or croak 'magnitude_table magnitude needs a hash ref';
	    my %mag;
	    foreach my $key ( keys %{ $tbl } ) {
		my $val = $tbl->{$key};
		$key =~ m/ \A [0-9]+ \z /smx
		    or croak "OID '$key' must be numeric";
		looks_like_number( $val )
		    or croak "Magnitude '$val' must be numeric";
		$mag{ _normalize_oid( $key ) } = $val;
	    }
	    %magnitude_table = %mag;
	    return;
	},
	molczan	=> sub {
	    my ( $file_name, $mag_factor ) = @_;
	    $parse_file->( $file_name, $mag_factor, {
		    mag_offset	=> 0,

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN


# Unsupported, experimental, and subject to change or retraction without
# notice. The intent is to provide a way for the Astro::App::Satpass2
# 'list' command to pick an appropriate template to format each line of
# the listing based on the object being listed.
sub __list_type {
    my ( $self ) = @_;
    return $self->{inertial} ? 'inertial' : 'fixed';
}

# _looks_like_real
#
# This returns a boolean which is true if the input looks like a real
# number and is false otherwise. It is based on looks_like_number, but
# excludes things like NaN, and Inf.
sub _looks_like_real {
    my ( $number ) = @_;
    looks_like_number( $number )
	or return;
    $number =~ m/ \A nan \z /smxi
	and return;
    $number =~ m/ \A [+-]? inf (?: inity )? \z /smxi
	and return;
    return 1;
}

# *equinox_dynamical = \&Astro::Coord::ECI::equinox_dynamical;

lib/Astro/Coord/ECI/TLE.pm  view on Meta::CPAN

}

# _set_optional_float_no_reinit
#
# This acts as a mutator for any attribute whose value is either undef
# or a floating-point number, and which does not cause the model to be
# renitialized when its value changes. We disallow NaN.

sub _set_optional_float_no_reinit {
    my ( $self, $name, $value ) = @_;
    if ( defined $value && ! _looks_like_real( $value ) ) {
	carp "Invalid $name '$value'; must be a float or undef";
	$value = undef;
    }
    $self->{$name} = $value;
    return 0;
}

# _set_optional_unsigned_integer_no_reinit
#
# This acts as a mutator for any attribute whose value is either undef

lib/Astro/Coord/ECI/Utils.pm  view on Meta::CPAN

	SPEED_OF_LIGHT TWOPI
	ARRAY_REF CODE_REF HASH_REF SCALAR_REF
	acos add_magnitudes asin
	atmospheric_extinction date2epoch date2jd
	decode_space_track_json_time deg2rad distsq dynamical_delta
	embodies epoch2datetime find_first_true
	fold_case __format_epoch_time_usec
	format_space_track_json_time gm_strftime intensity_to_magnitude
	jcent2000 jd2date jd2datetime jday2000 julianday
	keplers_equation load_module local_strftime
	looks_like_number max min mod2pi mod360
	omega position_angle
	rad2deg rad2dms rad2hms tan theta0 thetag vector_cross_product
	vector_dot_product vector_magnitude vector_unitize __classisa
	__default_station __instance __subroutine_deprecation
	__sprintf
	},
	qw{ time_gm time_local }, @greg_time_routines );
our @EXPORT_OK = (
    qw{ @CARP_NOT },	# Package-private, undocumented
    @all_external,

lib/Astro/Coord/ECI/Utils.pm  view on Meta::CPAN

Examples include C<'%.3S'> or C<'%.6T'>. Such a specification overrides
the C<$places> argument, if any.

=cut

sub local_strftime {
    my ( $format, $epoch ) = _pre_strftime( @_ );
    return POSIX::strftime( $format, localtime $epoch );
}

=item $boolean = looks_like_number ($string);

This subroutine returns true if the input looks like a number. It uses
Scalar::Util::looks_like_number if that is available, otherwise it uses
its own code, which is lifted verbatim from Scalar::Util 1.19, which in
turn leans heavily on perlfaq4.

=cut

unless (eval {require Scalar::Util; Scalar::Util->import
	('looks_like_number'); 1}) {
    no warnings qw{once};
    *looks_like_number = sub {
	local $_ = shift;

	# checks from perlfaq4
	return 0 if !defined($_) || ref($_);
	return 1 if (/^[+-]?[0-9]+$/); # is a +/- integer
	return 1 if (/^([+-]?)(?=[0-9]|\.[0-9])[0-9]*(\.[0-9]*)?([Ee]([+-]?[0-9]+))?$/); # a C float
	return 1 if ($] >= 5.008 and /^(Inf(inity)?|NaN)$/i)
	    or ($] >= 5.006001 and /^Inf$/i);

	return 0;

script/satpass  view on Meta::CPAN

eod
	}
    }    
}

sub _set_almanac_horizon {
    my ( $name, $value ) = @_;
    $value = _parse_angle( $value );
    Astro::Coord::ECI->new( almanac_horizon => $value );
    $parm{$name} = $value;
    $parm{"_$name"} = looks_like_number( $value ) ?
	deg2rad( $value ) :
	$value;
    return;
}

sub _set_angle {
    my ( $name, $value ) = @_;
    $parm{$name} = _parse_angle( $value );
    $parm{"_$name"} = deg2rad( $value );
    return;

script/satpass  view on Meta::CPAN

        'astronomical', or a unique abbreviation thereof, or a number
        of degrees the geometric center of the sun is below the
        horizon.
eod
    $parm{$_[0]} = $_[1];
    $parm{_twilight} = - deg2rad (abs ($angle));
    }
}

sub _set_tz {
    if ($_[1] || looks_like_number ($_[1])) {
	$ENV{TZ} = $parm{$_[0]} = $_[1];
	$parm{perltime} and _parse_time_absolute_use_perltime()
	    or _parse_time_absolute_init( $_[1] );
    } else {
	$parm{$_[0]} = undef;
	delete $ENV{TZ};
	$parm{perltime} and _parse_time_absolute_use_perltime()
	    or _parse_time_absolute_init();
    }
}

script/satpass  view on Meta::CPAN

    pc => PARSEC,
    );

sub _parse_distance {
my ($string, $dfdist) = @_;
my $dfunits = $dfdist =~ s/([[:alpha:]]+)$// ? $1 : 'km';
my $units = lc ($string =~ s/([[:alpha:]]+)$// ? $1 : $dfunits);
$units{$units} or die <<eod;
Error - Units of '$units' are unknown.
eod
looks_like_number ($string) or die <<eod;
Error - '$string' is not a number.
eod
$string * $units{$units};
}
}	# end of BEGIN block

#	$time = _parse_time ($string, $default)

#	Parses a time string in any known format. Strings with a
#	leading "+" or "-" are assumed to be relative to the last

script/satpass  view on Meta::CPAN


    $psr->parse ($data);
}

#	$quoted = _quoter ($string)

#	Quotes and escapes the input string if and as necessary for parser.

sub _quoter {
my $string = shift;
return $string if looks_like_number ($string);
return "''" unless $string;
return $string unless $string =~ m/[\s'"]/;
$string =~ s/([\\'])/\\$1/g;
return "'$string'";
}

#	$string = _rad2hms ($angle)

#	Converts the given angle in radians to hours, minutes, and
#	seconds (of right ascension, presumably)

script/satpass  view on Meta::CPAN

sub _sub_arg {
my ($name, $dflt, $args) = @_;
$dflt = '' unless defined $dflt;
my $ctrl = $dflt =~ s/^(\W)// ? $1 : '-';
my $val = $name !~ m/\D/ ? $args->[$name - 1] :
    exists $mutator{$name} ? $parm{$name} : $ENV{$name};
my $rslt;
if ($ctrl eq '+') {
    $rslt = defined $val ? $dflt : '';
    }
  elsif ($val || looks_like_number $val) {
    $rslt = $val;
    }
  elsif ($ctrl eq '-') {
    $rslt = $dflt;
    }
  elsif ($ctrl eq '=') {
    if ($name !~ m/\D/) {
	$args->[$name - 1] = $dflt;
	$rslt = $dflt;
	}

script/satpass  view on Meta::CPAN

=item geocode_ca

 satpass> geocode_ca location

B<Notice:> This command is unsupported as of satpass 0.021, and
probably will not work anyway, since geocoder.ca has started
requiring registration to use its free port.

This command attempts to look up the given location (either street
address or street intersection) at L<http://geocoder.ca/>. The results
of the lookup are displayed. If no location is specified, it looks up
the value of the L<location|/location> parameter.

If exactly one valid result is returned, the latitude and longitude
of the observer are set to the returned values, and the name of
the location of the observer is set to the location passed to the
command.

If the location contains whitespace, it must be quoted. Example:

 satpass> geocode_ca '80 wellington st ottawa on'

script/satpass  view on Meta::CPAN

 satpass> geocode_pw location

Palau is handled by L<geocode_us|/geocode_us>.

=item geocode_us

 satpass> geocode_us location

This command attempts to look up the given location (either street
address or street intersection) in Open Street Maps. The results
of the lookup are displayed. If no location is specified, it looks up
the value of the L<location|/location> parameter.

If exactly one valid result is returned, the latitude and longitude
of the observer are set to the returned values, and the name of
the location of the observer is set to the canonical name of the
location as returned by Open Street Maps. Also, the height command is
implicitly invoked to attempt to acquire the height above sea level
provided the L<autoheight|/autoheight> parameter is true.

In addition to the usual qualifiers, this command supports the -height



( run in 1.052 second using v1.01-cache-2.11-cpan-64827b87656 )