Astro-SpaceTrack
view release on metacpan or search on metacpan
lib/Astro/SpaceTrack.pm view on Meta::CPAN
This package retrieves orbital data from the Space Track web site
L<https://www.space-track.org> and several others. You must register and
get a user name and password before you can get data from Space Track.
Other methods (C<celestrak()> ...) have
been added to access other repositories of orbital data, and in general
these do not require a Space Track username and password.
Nothing is exported by default, but the shell method/subroutine
and the BODY_STATUS constants (see C<iridium_status()>)
can be exported if you so desire.
Most methods return an HTTP::Response object. See the individual
method document for details. Methods which return orbital data on
success add a 'Pragma: spacetrack-type = orbit' header to the
HTTP::Response object if the request succeeds, and a 'Pragma:
spacetrack-source =' header to specify what source the data came from.
=head2 Methods
The following methods should be considered public:
=over 4
=cut
package Astro::SpaceTrack;
use 5.006002;
use strict;
use warnings;
use Exporter;
our @ISA = qw{ Exporter };
our $VERSION = '0.181';
our @EXPORT_OK = qw{
shell
BODY_STATUS_IS_OPERATIONAL
BODY_STATUS_IS_SPARE
BODY_STATUS_IS_TUMBLING
BODY_STATUS_IS_DECAYED
ARRAY_REF
CODE_REF
HASH_REF
};
our %EXPORT_TAGS = (
ref => [ grep { m/ _REF \z /smx } @EXPORT_OK ],
status => [ grep { m/ \A BODY_STATUS_IS_ /smx } @EXPORT_OK ],
);
use Carp ();
use Getopt::Long 2.39;
use HTTP::Date ();
use HTTP::Request;
use HTTP::Response;
use HTTP::Status qw{
HTTP_PAYMENT_REQUIRED
HTTP_BAD_REQUEST
HTTP_NOT_FOUND
HTTP_I_AM_A_TEAPOT
HTTP_INTERNAL_SERVER_ERROR
HTTP_NOT_ACCEPTABLE
HTTP_NOT_MODIFIED
HTTP_OK
HTTP_PRECONDITION_FAILED
HTTP_UNAUTHORIZED
HTTP_INTERNAL_SERVER_ERROR
};
use IO::File;
use IO::Uncompress::Unzip ();
use JSON qw{};
use List::Util ();
use LWP::UserAgent; # Not in the base.
use POSIX ();
use Scalar::Util 1.07 ();
use Text::ParseWords ();
use Time::Local ();
use URI qw{};
# use URI::Escape qw{};
# Number of OIDs to retrieve at once. This is a global variable so I can
# play with it, but it is neither documented nor supported, and I
# reserve the right to change it or delete it without notice.
our $RETRIEVAL_SIZE = $ENV{SPACETRACK_RETRIEVAL_SIZE};
defined $RETRIEVAL_SIZE or $RETRIEVAL_SIZE = 200;
use constant COPACETIC => 'OK';
use constant BAD_SPACETRACK_RESPONSE =>
'Unable to parse SpaceTrack response';
use constant INVALID_CATALOG =>
'Catalog name %s invalid. Legal names are %s.';
use constant LAPSED_FUNDING => 'Funding lapsed.';
use constant LOGIN_FAILED => 'Login failed';
use constant NO_CREDENTIALS => 'Username or password not specified.';
use constant NO_CAT_ID => 'No catalog IDs specified.';
use constant NO_OBJ_NAME => 'No object name specified.';
use constant NO_RECORDS => 'No records found.';
use constant SESSION_PATH => '/';
use constant DEFAULT_SPACE_TRACK_REST_SEARCH_CLASS => 'satcat';
use constant DEFAULT_SPACE_TRACK_VERSION => 2;
# dump_headers constants.
use constant DUMP_NONE => 0; # No dump
use constant DUMP_TRACE => 0x01; # Logic trace
use constant DUMP_REQUEST => 0x02; # Request content
use constant DUMP_DRY_RUN => 0x04; # Do not execute request
use constant DUMP_COOKIE => 0x08; # Dump cookies.
use constant DUMP_RESPONSE => 0x10; # Dump response.
use constant DUMP_TRUNCATED => 0x20; # Dump with truncated content
my @dump_options;
foreach my $key ( sort keys %Astro::SpaceTrack:: ) {
lib/Astro/SpaceTrack.pm view on Meta::CPAN
# modified since the modification date of the file. If the data
# have been modified, the cache file is refreshed; otherwise the
# response is loaded from the cache file.
# method => method_name
# If this is defined, it is the name of the method doing the
# catalog lookup. This is unused unless 'catalog' is defined, and
# defaults to the name of the calling method.
# post_process => code reference
# If the network operation succeeded and this is defined, it is
# called and passed the invocant, the HTTP::Response object, and
# a reference to the catalog information hash (or to an empty hash
# if 'url' was specified). The HTTP::Response object returned
# (which may or may not be the one passed in) is the basis for any
# further processing.
# spacetrack_source => spacetrack_source
# If this is defined, the corresponding-named pragma is set. The
# default comes from the same-named key in the catalog info if that
# is defined, or the 'method' argument (as defaulted).
# spacetrack_type => spacetrack_type
# If this is defined, the corresponding-named pragma is set.
# url => URL
# If this is defined, it is the URL of the data to retrieve.
#
# Either 'catalog' or 'url' MUST be specified. If 'url' is defined,
# 'catalog' is ignored.
sub _get_from_net {
my ( $self, %arg ) = @_;
delete $self->{_pragmata};
my $method = defined $arg{method} ? $arg{method} : ( caller 1)[3];
$method =~ s/ .* :: //smx;
my $url;
my $info;
if ( defined $arg{url} ) {
$url = $arg{url};
$info = {};
} elsif ( exists $arg{catalog} ) {
defined $arg{catalog}
and $catalogs{$method}
and $info = $catalogs{$method}{$arg{catalog}}
or return $self->_no_such_catalog( $method, $arg{catalog} );
$self->_deprecation_notice( $method => $arg{catalog} );
$url = $info->{url}
or Carp::confess "Bug - No url defined for $method( '$arg{catalog}' )";
} else {
Carp::confess q<Bug - neither 'url' nor 'catalog' specified>;
}
if ( my $resp = $self->_dump_request(
args => { map { $_ => CODE_REF eq ref $arg{$_} ? 'sub { ... }' : $arg{$_} } keys %arg },
method => 'GET',
url => $url,
version => 2,
) ) {
return $resp;
}
my $agent = $self->_get_agent();
my $rqst = HTTP::Request->new( GET => $url );
my $file_time;
if ( defined $arg{file} ) {
if ( my @stat = stat $arg{file} ) {
$file_time = HTTP::Date::time2str( $stat[9] );
$rqst->header( if_modified_since => $file_time );
}
}
my $resp;
$resp = $self->_dump_request(
arg => sub {
my %sanitary = %arg;
foreach my $key ( qw{ post_process } ) {
delete $sanitary{$key}
and $sanitary{$key} = CODE_REF;
}
return \%sanitary;
},
message => '_get_from_net() request object',
method => 'GET',
url => $url,
hdrs => sub {
my %rslt;
foreach my $name ( $rqst->header_field_names() ) {
my @v = $rqst->header( $name );
$rslt{$name} = @v == 1 ? $v[0] : \@v;
}
return \%rslt;
},
)
and return $resp;
$resp = $agent->request( $rqst );
$self->__dump_response(
$resp, '_get_from_net() initial response object' );
if ( $resp->code() == HTTP_NOT_MODIFIED ) {
defined $arg{file}
or Carp::confess q{Programming Error - argument 'file' not defined};
local $/ = undef;
open my $fh, '<', $arg{file}
or return HTTP::Response->new(
HTTP_INTERNAL_SERVER_ERROR,
"Unable to read $arg{file}: $!" );
$resp->content( scalar <$fh> );
close $fh;
$resp->code( HTTP_OK );
defined $file_time
and $resp->header( last_modified => $file_time );
$arg{spacetrack_cache_hit} = 1;
} else {
$resp->is_success()
and defined $arg{post_process}
and $resp = $arg{post_process}->( $self, $resp, $info );
$resp->is_success() # $resp may be a different object now.
or return $resp;
$self->_convert_content( $resp );
if ( defined $arg{file} ) {
open my $fh, '>', $arg{file}
or return HTTP::Response->new(
HTTP_INTERNAL_SERVER_ERROR,
( run in 0.700 second using v1.01-cache-2.11-cpan-39bf76dae61 )