Astro-satpass
view release on metacpan or search on metacpan
lib/Astro/Coord/ECI.pm view on Meta::CPAN
The ellipsoid argument is the name of a L</Reference Ellipsoid> known
to the class, and is optional. If not specified, the most-recently-set
ellipsoid will be used.
The conversion from geocentric to geodetic comes from Kazimierz
Borkowski's "Accurate Algorithms to Transform Geocentric to Geodetic
Coordinates", at F<http://www.astro.uni.torun.pl/~kb/Papers/geod/Geod-BG.htm>.
This is best viewed with Internet Explorer because of its use of Microsoft's
Symbol font.
=cut
sub geodetic {
my ($self, @args) = @_;
# Detect and acquire the optional ellipsoid name argument. We do
# this before the check, since the check expects the extra
# argument to be a time.
my $elps = (@args == 1 || @args == 4) ? pop @args : undef;
$self = $self->_check_coord (geodetic => \@args, $elps ? $elps : ());
# The following is just a sleazy way to get a consistent
# error message if the ellipsoid name is unknown.
$elps && $self->reference_ellipsoid ($elps);
# If we're fetching the geodetic coordinates
unless (@args) {
# Return cached coordinates if they exist and we did not
# override the default ellipsoid.
return @{$self->{_ECI_cache}{fixed}{geodetic}}
if $self->{_ECI_cache}{fixed}{geodetic} && !$elps;
$self->{debug} and do {
require Data::Dumper;
local $Data::Dumper::Terse = 1;
print "Debug geodetic - explicit ellipsoid ",
Data::Dumper::Dumper( $elps );
};
# Get a reference to the ellipsoid data to use.
$elps = $elps ? $known_ellipsoid{$elps} : $self;
$self->{debug} and do {
require Data::Dumper;
local $Data::Dumper::Terse = 1;
print "Debug geodetic - ellipsoid ", Data::Dumper::Dumper( $elps );
};
# Calculate geodetic coordinates.
my ($phiprime, $lambda, $rho) = $self->geocentric;
my $r = $rho * cos ($phiprime);
my $b = $elps->{semimajor} * (1- $elps->{flattening});
my $a = $elps->{semimajor};
my $z = $rho * sin ($phiprime);
# The $b is _not_ a magic variable, since we are not inside
# any of the specialized blocks that make $b magic. Perl::Critic
# is simply confused.
$b = - $b ## no critic (RequireLocalizedPunctuationVars)
if $z < 0; # Per Borkowski, for southern hemisphere.
# The following algorithm is due to Kazimierz Borkowski's
# paper "Accurate Algorithms to Transform Geocentric to Geodetic
# Coordinates", from
# http://www.astro.uni.torun.pl/~kb/Papers/geod/Geod-BG.htm
my $bz = $b * $z;
my $asq_bsq = $a * $a - $b * $b;
my $ar = $a * $r;
my $E = ($bz - $asq_bsq) / $ar; # Borkowski (10)
my $F = ($bz + $asq_bsq) / $ar; # Borkowski (11)
my $Q = ($E * $E - $F * $F) * 2; # Borkowski (17)
my $P = ($E * $F + 1) * 4 / 3; # Borkowski (16)
my $D = $P * $P * $P + $Q * $Q; # Borkowski (15)
my $v = $D >= 0 ? do {
my $d = sqrt $D;
my $onethird = 1 / 3;
my $vp = ($d - $Q) ** $onethird - # Borkowski (14a)
($d + $Q) ** $onethird;
$vp * $vp >= abs ($P) ? $vp :
- ($vp * $vp * $vp + 2 * $Q) / # Borkowski (20)
(3 * $P);
} : do {
my $p = - $P;
sqrt (cos (acos ($Q / # Borkowski (14b)
sqrt ($p * $p * $p)) / 3) * $p) * 2;
};
my $G = (sqrt ($E * $E + $v) # Borkowski (13)
+ $E) / 2;
my $t = sqrt ($G * $G + ($F - $v * $G) # Borkowski (12)
/ (2 * $G - $E)) - $G;
# Borkowski (18)
# equivalent to atan (arg1)
my $phi = do {
my $Y = $a * ( 1 - $t * $t );
my $X = 2 * $b * $t;
( $Y, $X ) = ( -$Y, -$X ) if $X < 0;
atan2 $Y, $X;
};
my $h = ($r - $a * $t) * cos ($phi) + # Borkowski (19)
($z - $b) * sin ($phi);
# End of Borkowski's algorthm.
# Cache the results of the calculation if they were done using
# the default ellipsoid.
$self->{_ECI_cache}{fixed}{geodetic} = [$phi, $lambda, $h] unless $elps;
# Return the results in any event.
$self->get ('debug') and print <<eod;
Debug geodetic: geocentric to geodetic
inputs:
( run in 1.827 second using v1.01-cache-2.11-cpan-0d23b851a93 )