Astro-SpaceTrack

 view release on metacpan or  search on metacpan

lib/Astro/SpaceTrack.pm  view on Meta::CPAN

	    visible => {
		favorite	=> 'Visible',
		name => 'Visible satellites',
		tle => {
		    EPOCH	=> '>now-30',
		},
#		number => 21,
	    },
	    special => {
		favorite	=> 'Special_interest',
		name => 'Special interest satellites',
		tle => {
		    EPOCH	=> '>now-30',
		},
#		number => 23,
	    },
	    bright_geosynchronous => {
		favorite	=> 'brightgeo',
		name => 'Bright Geosynchronous satellites',
		tle => {
		    EPOCH	=> '>now-30',
		},
	    },
	    human_spaceflight => {
		favorite	=> 'human_spaceflight',
		name => 'Human Spaceflight',
		tle => {
		    EPOCH	=> '>now-30',
		},
	    },

	    # Added by TRW
	    payloads	=> {
		name	=> 'All payloads',
#		satcat	=> {
#		    OBJECT_TYPE	=> 'PAYLOAD',
#		},
		tle	=> {
		    EPOCH	=> '>now-30',
		    OBJECT_TYPE	=> 'payload',
		},
	    },
	},
    ],
);

my %mutator = (	# Mutators for the various attributes.
    addendum => \&_mutate_attrib,		# Addendum to banner text.
    banner => \&_mutate_attrib,
    cookie_expires => \&_mutate_spacetrack_interface,
    cookie_name => \&_mutate_spacetrack_interface,
    domain_space_track => \&_mutate_spacetrack_interface,
    dump_headers => \&_mutate_dump_headers,	# Dump all HTTP headers. Undocumented and unsupported.
    fallback => \&_mutate_attrib,
    filter => \&_mutate_attrib,
    identity	=> \&_mutate_identity,
    iridium_status_format => \&_mutate_iridium_status_format,
    max_range => \&_mutate_number,
    password => \&_mutate_authen,
    pretty => \&_mutate_attrib,
    prompt	=> \&_mutate_attrib,
    scheme_space_track => \&_mutate_attrib,
    session_cookie => \&_mutate_spacetrack_interface,
    space_track_version => \&_mutate_space_track_version,
    url_iridium_status_kelso => \&_mutate_attrib,
    url_iridium_status_mccants => \&_mutate_attrib,
    url_iridium_status_sladen => \&_mutate_attrib,
    username => \&_mutate_authen,
    verbose => \&_mutate_attrib,
    verify_hostname => \&_mutate_verify_hostname,
    webcmd => \&_mutate_attrib,
    with_name => \&_mutate_attrib,
);

my %accessor = (
    cookie_expires	=> \&_access_spacetrack_interface,
    cookie_name		=> \&_access_spacetrack_interface,
    domain_space_track	=> \&_access_spacetrack_interface,
    session_cookie	=> \&_access_spacetrack_interface,
);
foreach my $key ( keys %mutator ) {
    exists $accessor{$key}
	or $accessor{$key} = sub {
	    $_[0]->_deprecation_notice( attribute => $_[1] );
	    return $_[0]->{$_[1]};
	};
}

# Maybe I really want a cookie_file attribute, which is used to do
# $self->{agent}->cookie_jar ({file => $self->{cookie_file}, autosave => 1}).
# We'll want to use a false attribute value to pass an empty hash. Going to
# this may imply modification of the new () method where the cookie_jar is
# defaulted and the session cookie's age is initialized.


=item $st = Astro::SpaceTrack->new ( ... )

=for html <a name="new"></a>

This method instantiates a new Space-Track accessor object. If any
arguments are passed, the C<set()> method is called on the new object,
and passed the arguments given.

For both historical and operational reasons, this method can get the
C<username> and C<password> values from multiple locations. It uses the
first defined value it finds in the following list:

=over

=item a value explicitly specified as an argument to C<new()>;

=item a value from the L<IDENTITY FILE|/IDENTITY FILE>, if the
C<identity> attribute is explicitly specified as true and
L<Config::Identity|Config::Identity> is installed;

=item a value from environment variable C<SPACETRACK_USER> if that has a
non-empty value;

=item a value from the L<IDENTITY FILE|/IDENTITY FILE>, if the
C<identity> attribute defaulted to true and
L<Config::Identity|Config::Identity> s installed;

=item a value from environment variable C<SPACETRACK_OPT>.

=back

The reason for preferring C<SPACETRACK_USER> over an identity file value
taken by default is that I have found that under Mac OS X an SSH session
does not have access to the system keyring, and
L<Config::Identity|Config::Identity> provides no other way to specify
the passphrase used to decrypt the private key. I concluded that if the
user explicitly requested an identity that it should be preferred to
anything from the environment, but that, for SSH access to be usable, I
needed to provide a source of username and password that would be taken
before the L<IDENTITY FILE|/IDENTITY FILE> was tried by default.

Proxies are taken from the environment if defined. See the ENVIRONMENT
section of the Perl LWP documentation for more information on how to
set these up.

=cut

sub new {
    my ( $class, %arg ) = @_;
    $class = ref $class if ref $class;

    my $self = {
	banner => 1,	# shell () displays banner if true.
	dump_headers => DUMP_NONE,	# No dumping.
	fallback => 0,	# Do not fall back if primary source offline
	filter => 0,	# Filter mode.
	iridium_status_format => 'kelso',
	max_range => 500,	# Sanity limit on range size.
	password => undef,	# Login password.
	pretty => 0,		# Pretty-format content
	prompt => 'SpaceTrack> ',
	scheme_space_track => 'https',
	_space_track_interface	=> [
	    undef,	# No such thing as version 0
	    undef,	# Interface version 1 retured.
	    {	# Interface version 2
		# This interface does not seem to put an expiration time
		# on the cookie. But the docs say it's only good for a
		# couple hours, so we need this so we can fudge
		# something in when the time comes.
		cookie_expires		=> 0,
		cookie_name		=> 'chocolatechip',
		domain_space_track	=> 'www.space-track.org',
		session_cookie		=> undef,
	    },
	],
	space_track_version	=> DEFAULT_SPACE_TRACK_VERSION,
	url_iridium_status_kelso =>
	    'https://celestrak.org/SpaceTrack/query/iridium.txt',
	url_iridium_status_sladen =>
	    'http://www.rod.sladen.org.uk/iridium.htm',
	username => undef,	# Login username.
	verbose => undef,	# Verbose error messages for catalogs.
	verify_hostname => 1,	# Don't verify host names by default.
	webcmd => undef,	# Command to get web help.
	with_name => undef,	# True to retrieve three-line element sets.
    };
    bless $self, $class;

    $self->set( identity	=> delete $arg{identity} );

    $ENV{SPACETRACK_OPT} and
	$self->set (grep {defined $_} split '\s+', $ENV{SPACETRACK_OPT});

    # TODO this makes no sense - the first branch of the if() can never
    # be executed because I already deleted $arg{identity}. But I do not
    # want to execute the SPACETRACK_USER code willy-nilly -- maybe warn
    # if identity is 1 and I don't have both a username and a password.
    if ( defined( my $id = delete $arg{identity} ) ) {
	$self->set( identity => $id );
    } elsif ( $ENV{SPACETRACK_USER} ) {
	my ($user, $pass) = split qr{ [:/] }smx, $ENV{SPACETRACK_USER}, 2;
	'' ne $user
	    and '' ne $pass
	    or $user = $pass = undef;
	$self->set (username => $user, password => $pass);
    } else {
	$self->set( identity => undef );
    }

    defined $ENV{SPACETRACK_VERIFY_HOSTNAME}
	and $self->set( verify_hostname =>
	$ENV{SPACETRACK_VERIFY_HOSTNAME} );

    keys %arg
	and $self->set( %arg );

    return $self;
}

=for html <a name="amsat"></a>

lib/Astro/SpaceTrack.pm  view on Meta::CPAN

	    {
		local $" = '';	# Make "@a" equivalent to join '', @a.
		$rslt->content( join '',
		    map { "@$_\n" }
		    sort { $a->[0] <=> $b->[0] }
		    @lines
		);
	    }
	    $self->__dump_response( $rslt );
	    return;
	},
    },
    time	=> {
	before	=> sub {
	    my ( undef, $context ) = @_;	# Invocant unused
	    eval {
		require Time::HiRes;
		$context->{start_time} = Time::HiRes::time();
		1;
	    } or warn 'No timings available. Can not load Time::HiRes';
	    return;
	},
	after	=> sub {
	    my ( undef, $context ) = @_;	# Invocant unused
	    $context->{start_time}
		and warn sprintf "Elapsed time: %.2f seconds\n",
		    Time::HiRes::time() - $context->{start_time};
	    return;
	}
    },
);

my $readline_word_break_re;

{
    my %alias = (
	show	=> 'get',
    );

    sub _verb_alias {
	my ( $verb ) = @_;
	return $alias{$verb} || $verb;
    }
}

sub shell {
    my @args = @_;
    my $self = _instance( $args[0], __PACKAGE__ ) ? shift @args :
	Astro::SpaceTrack->new (addendum => <<'EOD');

'help' gets you a list of valid commands.
EOD

    my $stdout = \*STDOUT;
    my $read;

    unshift @args, 'banner' if $self->{banner} && !$self->{filter};
    # Perl::Critic wants IO::Interactive::is_interactive() here. But
    # that assumes we're using the *ARGV input mechanism, which we're
    # not (command arguments are SpaceTrack commands.) Also, we would
    # like to be prompted even if output is to a pipe, but the
    # recommended module calls that non-interactive even if input is
    # from a terminal. So:
    my $interactive = -t STDIN;
    while (1) {
	my $buffer;
	if (@args) {
	    $buffer = shift @args;
	} else {
	    $read ||= $interactive ? ( eval {
		    $self->_get_readline( $stdout )
		} || sub { print { $stdout } $self->getv( 'prompt' ); return <STDIN> } ) :
		sub { return<STDIN> };
	    $buffer = $read->();
	}
	last unless defined $buffer;

	$buffer =~ s/ \A \s+ //smx;
	$buffer =~ s/ \s+ \z //smx;
	next unless $buffer;
	next if $buffer =~ m/ \A [#] /smx;

	# Break the buffer up into tokens, but leave quotes and escapes
	# in place, so that (e.g.) '\>foo' is seen as an argument, not a
	# redirection.

	my @cmdarg = Text::ParseWords::parse_line( '\s+', 1, $buffer );

	# Pull off any redirections.

	my $redir = '';
	@cmdarg = map {
	    m/ \A > /smx ? do {$redir = $_; ()} :
	    $redir =~ m/ \A >+ \z /smx ? do {$redir .= $_; ()} :
	    $_
	} @cmdarg;

	# Rerun everything through parse_line again, but with the $keep
	# argument false. This should not create any more tokens, it
	# should just un-quote and un-escape the data.

	@cmdarg = map { Text::ParseWords::parse_line( qr{ \s+ }, 0, $_ ) } @cmdarg;
	$redir ne ''
	    and ( $redir ) = Text::ParseWords::parse_line ( qr{ \s+ }, 0, $redir );

	$redir =~ s/ \A (>+) ~ /$1$ENV{HOME}/smx;
	my $verb = lc shift @cmdarg;

	my %meta_command = (
	    before	=> [],
	    after	=> [],
	);

	while ( my $def = $known_meta{$verb} ) {
	    my %context;
	    foreach my $key ( qw{ before after } ) {
		$def->{$key}
		    or next;
		push @{ $meta_command{$key} }, sub {
		    return $def->{$key}->( $self, \%context, @_ );
		};
	    }
	    $verb = shift @cmdarg;
	}

	last if $verb eq 'exit' || $verb eq 'bye';
	$verb = _verb_alias( $verb );
	$verb eq 'source' and do {
	    eval {
		splice @args, 0, 0, $self->_source (shift @cmdarg);
		1;
	    } or warn ( $@ || 'An unknown error occurred' );	## no critic (RequireCarping)

lib/Astro/SpaceTrack.pm  view on Meta::CPAN

	} else {
	    eval {
		$rslt = $self->$verb (@cmdarg);
		1;
	    } or do {
		warn $@;	## no critic (RequireCarping)
		next;
	    };
	}

	foreach my $pseudo ( reverse @{ $meta_command{after} } ) {
	    $pseudo->( $rslt );
	}

	if ( ARRAY_REF eq ref $rslt ) {
	    foreach (@$rslt) {print { $out } "$_\n"}
	} elsif ( ! ref $rslt ) {
	    print { $out } "$rslt\n";
	} elsif ($rslt->is_success) {
	    $self->content_type()
		or not $self->{filter}
		or next;
	    my $content = $rslt->content;
	    chomp $content;
	    print { $out } "$content\n";
	} else {
	    my $status = $rslt->status_line;
	    chomp $status;
	    warn $status, "\n";
	    $rslt->code() == HTTP_I_AM_A_TEAPOT
		and print { $out } $rslt->content(), "\n";
	}
    }
    $interactive
	and not $self->{filter}
	and print { $stdout } "\n";
    return;
}

sub _get_readline {	## no critic (Subroutines::RequireArgUnpacking)
    my ( $self ) = @_;
    require Term::ReadLine;
    $rdln ||= Term::ReadLine->new (
	'SpaceTrack orbital element access');
    @_ > 1
	and $_[1] = ( $rdln->OUT || \*STDOUT );	# $stdout
    if ( 'Term::ReadLine::Perl' eq $rdln->ReadLine() ) {
	require File::Glob;

	$readline_word_break_re ||= qr<
	    [\Q$readline::rl_completer_word_break_characters\E]+
	>smx;

	no warnings qw{ once };
	$readline::rl_completion_function = sub {
	    my ( $text, $line, $start ) = @_;
	    return $self->__readline_completer(
		$text, $line, $start );
	};
    }
    return sub { $rdln->readline ( $self->getv( 'prompt' ) ) };
}


=for html <a name="source"></a>

=item $st->source ($filename);

This convenience method reads the given file, and passes the individual
lines to the shell method. It croaks if the file is not provided or
cannot be read.

=cut

# We really just delegate to _source, which unpacks.
sub source {
    my $self = _instance( $_[0], __PACKAGE__ ) ? shift :
	Astro::SpaceTrack->new ();
    $self->shell ($self->_source (@_), 'exit');
    return;
}


=for html <a name="spacetrack"></a>

=item $resp = $st->spacetrack ($name);

This method returns predefined sets of data from the Space Track web
site, using either canned queries or global favorites.

The following catalogs are available:

    Name            Description
    full            Full catalog
    payloads        All payloads
  * navigation      Navigation satellites
  * weather         Weather satellites
    geosynchronous  Geosynchronous bodies
    iridium         Iridium satellites
    orbcomm         OrbComm satellites
    globalstar      Globalstar satellites
    intelsat        Intelsat satellites
    inmarsat        Inmarsat satellites
  * amateur         Amateur Radio satellites
  * visible         Visible satellites
  * special         Special satellites
  * bright_geosynchronous
                    Bright Geosynchronous satellites
  * human_spaceflight
                    Human Spaceflight
    well_tracked_objects
                    Well-Tracked Objects not associated
		    with a specific launch

The starred items are 404 as of 2026-01-19. They are deprecated and will
be removed.

The following option is supported:

 -json
   specifies the TLE be returned in JSON format

lib/Astro/SpaceTrack.pm  view on Meta::CPAN

identity file is encrypted C<gpg2> must be installed and properly
configured. See L<IDENTITY FILE|/IDENTITY FILE> below for details of the
identity file.

I have found that C<gpg> does not seem to work nicely, even though
L<Config::Identity|Config::Identity> prefers it to C<gpg2> if both are
present. The L<Config::Identity|Config::Identity> documentation says
that you can override this by setting environment variable C<CI_GPG>
to the executable you want used.

If this attribute is unspecified (to C<new()> or specified as C<undef>
(to C<new()> or C<set()>), the value of environment variable
C<SPACETRACK_IDENTITY> will be used as the new value.

When a new object is instantiated, the identity is processed first; in
this way attribute values that come from the environment or are
specified explicitly override those that come from the identity file. If
you explicitly set this on an already-instantiated object, the attribute
values from the identity file will replace those in the object.

When you instantiate an object, the identity from environment variable
C<SPACETRACK_USER> will be preferred over the value from the identity
file, if any, even if the C<identity> attribute is explicitly set true.

=item iridium_status_format (string)

This attribute specifies the default format of the data returned by the
C<iridium_status()> method. Valid values are 'kelso', 'sladen' or
'spacetrack'.  See that method for more information.

As of version 0.100_02, the default is C<'kelso'>. It used to be
C<'mccants'>, but Mike McCants no longer maintains his Iridium status
web page, and format C<'mccants'> was removed as of version 0.137.

This attribute is B<deprecated>.

=item max_range (number)

This attribute specifies the maximum size of a range of NORAD IDs to be
retrieved. Its purpose is to impose a sanity check on the use of the
range functionality.

The default is 500.

=item password (text)

This attribute specifies the Space-Track password.

The default is an empty string.

=item pretty (Boolean)

This attribute specifies whether the content of the returned
L<HTTP::Response|HTTP::Response> is to be pretty-formatted. Currently
this only applies to Space Track data returned in C<JSON> format.
Pretty-formatting the C<JSON> is extra overhead, so unless you intend to
read the C<JSON> yourself this should probably be false.

The default is C<0> (i.e. false).

=item prompt (string)

This attribute specifies the prompt issued by the C<shell()> method. The
default is C<< 'SpaceTrack> ' >>.

=item scheme_space_track (string)

This attribute specifies the URL scheme used to access the Space Track
web site. The user will not normally need to modify this, but if the web
site changes schemes for some reason, this attribute may provide a way
to get queries going again.

The default is C<'https'>.

=item session_cookie (text)

This attribute specifies the session cookie. You should only set it
with a previously-retrieved value.

The default is an empty string.

=item space_track_version (integer)

This attribute specifies the version of the Space Track interface to use
to retrieve data. The only valid value is C<2>.  If you set it to a
false value (i.e. C<undef>, C<0>, or C<''>) it will be set to the
default.

The default is C<2>.

=item url_iridium_status_kelso (text)

This attribute specifies the location of the celestrak.org Iridium
information. You should normally not change this, but it is provided
so you will not be dead in the water if Dr. Kelso needs to re-arrange
his web site.

The default is 'https://celestrak.org/SpaceTrack/query/iridium.txt'

This attribute is B<deprecated>.

=item url_iridium_status_mccants (text)

This attribute is B<deprecated>, and any access of it will be fatal.

=item url_iridium_status_sladen (text)

This attribute specifies the location of Rod Sladen's Iridium
Constellation Status page. You should normally not need to change this,
but it is provided so you will not be dead in the water if Mr. Sladen
needs to change his ISP or re-arrange his web site.

The default is 'http://www.rod.sladen.org.uk/iridium.htm'.

This attribute is B<deprecated>.

=item username (text)

This attribute specifies the Space-Track username.

The default is an empty string.

=item verbose (Boolean)



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