1package Astro::Telescope; 2 3=head1 NAME 4 5Astro::Telescope - class for obtaining telescope information 6 7=head1 SYNOPSIS 8 9 use Astro::Telescope; 10 11 $tel = new Astro::Telescope( 'UKIRT' ); 12 13 $latitude = $tel->lat; 14 $longitude = $tel->long; 15 $altitude = $tel->alt; 16 17 %limits = $tel->limits; 18 19 @telescopes = Astro::Telescope->telNames(); 20 21=head1 DESCRIPTION 22 23A class for handling properties of individual telescopes such 24as longitude, latitude, height and observational limits. 25 26=cut 27 28use 5.006; 29use warnings; 30use warnings::register; 31use strict; 32 33our $ASTRO_PAL = 0; 34eval { require Astro::PAL; }; 35if( ! $@ ) { 36 $ASTRO_PAL = 1; 37} 38 39use Astro::Telescope::MPC; 40 41use File::Spec; 42use Carp; 43 44use vars qw/ $VERSION /; 45$VERSION = '0.71'; 46 47# separator to use for output sexagesimal notation 48our $Separator = " "; 49 50# Decimal degrees to radians conversion factor. 51use constant DD2R => 0.017453292519943295769236907684886127134428718885417; 52 53# Decimal hours to radians conversion factor. 54use constant DH2R => 0.26179938779914943653855361527329190701643078328126; 55 56# Radians to degrees conversion factor. 57use constant DR2D => 57.295779513082320876798154814105170332405472466564; 58 59# Earth's equatorial radius in metres. 60use constant EQU_RAD => 6378100; 61 62# Earth's flattening parameter (actually 1-f). 63use constant E => 0.996647186; 64 65# Related to flattening parameter (sqrt(1-(1-f)^2)). 66use constant EPS => 0.081819221; 67 68# Pi. 69use constant PI => 4 * atan2( 1, 1 ); 70 71# AU to metre conversion factor. 72use constant AU2METRE => 149598000000; 73 74# Hash table containing mapping from PAL telescope name to 75# MPC observatory code. 76our %pal2obs = ( 'AAT' => '260', 77 'LPO4.2' => '950', 78 'LPO2.5' => '950', 79 'LP01' => '950', 80 'LICK120' => '662', 81 'MMT' => '696', 82 'DAO72' => '658', 83 'DUPONT' => '304', 84 'MTHOP1.5' => '696', 85 'STROMLO74' => '414', 86 'ANU2.3' => '413', 87 'GBVA140' => '256', 88 'TOLOLO4M' => 'I02', 89 'TOLOLO1.5M' => 'I02', 90 'BLOEMF' => '074', 91 'BOSQALEGRE' => '821', 92 'FLAGSTF61' => '689', 93 'LOWELL72' => '688', 94 'OKAYAMA' => '371', 95 'KPNO158' => '691', 96 'KPNO90' => '691', 97 'KPNO84' => '691', 98 'KPNO36FT' => '697', 99 'KOTTAMIA' => '088', 100 'ESO3.6' => '809', 101 'MAUNAK88' => '568', 102 'UKIRT' => '568', 103 'QUEBEC1.6' => '301', 104 'MTEKAR' => '098', 105 'MTLEMMON60' => '686', 106 'MCDONLD2.7' => '711', 107 'MCDONLD2.1' => '711', 108 'PALOMAR200' => '261', 109 'PALOMAR60' => '644', 110 'DUNLAP74' => '779', 111 'HPROV1.93' => '511', 112 'HPROV1.52' => '511', 113 'SANPM83' => '679', 114 'SAAO74' => '079', 115 'TAUTNBG' => '033', 116 'CATALINA61' => '693', 117 'STEWARD90' => '691', 118 'USSR6' => '115', 119 'ARECIBO' => '251', 120 'CAMB5KM' => '503', 121 'CAMB1MILE' => '503', 122 'GBVA300' => '256', 123 'JCMT' => '568', 124 'ESONTT' => '809', 125 'ST.ANDREWS' => '482', 126 'APO3.5' => '645', 127 'KECK1' => '568', 128 'TAUTSCHM' => '033', 129 'PALOMAR48' => '644', 130 'UKST' => 'E12', 131 'KISO' => '381', 132 'ESOSCHM' => '809', 133 'SUBARU' => '568', 134 'CFHT' => '568', 135 'KECK2' => '568', 136 'GEMININ' => '568', 137 'IRTF' => '568', 138 'CSO' => '568', 139 'VLT1' => '309', 140 'VLT2' => '309', 141 'VLT3' => '309', 142 'VLT4' => '309', 143 'MAGELLAN1' => '304', 144 'MAGELLAN2' => '304', 145 ); 146 147 148=head1 METHODS 149 150=head2 Constructor 151 152=over 153 154=item B<new> 155 156Create a new telescope object. Takes the telescope abbreviation 157as the single argument. 158 159 $tel = new Astro::Telescope( 'VLA' ); 160 161An argument must be supplied. Returns C<undef> if the telescope 162is not recognized. 163 164If more than one argument is supplied the assumption 165is that the user is supplying telescope details. In that case, 166"Name" and "Long" must be supplied, and either the geodetic latitude and 167altitude ("Lat" and "Alt" -- but if "Alt" is not supplied it will 168default to zero and this class will issue a warning), the geocentric 169latitude and distance 170("GeocLat" and "GeocDist"), or the parallax coefficients ("Parallax") 171must be supplied. Latitudes and longitudes must be given in radians, 172altitude and distance in metres, and the parallax constants in units 173of Earth radii. 174 175 $tel = new Astro::Telescope('telescope'); 176 $tel = new Astro::Telescope(Name => 'JCMT', Long => $long, Lat => $lat ); 177 178 179=cut 180 181sub new { 182 my $proto = shift; 183 my $class = ref($proto) || $proto; 184 185 return undef unless @_; 186 187 # Create the new object 188 my $tel = bless {}, $class; 189 190 # Configure it with the supplied telescope name 191 # or other arguments 192 $tel->_configure( @_ ) or return undef; 193 194 return $tel; 195} 196 197=back 198 199=head2 Accessor Methods 200 201=over 4 202 203=item B<name> 204 205Returns the abbreviated name of the telescope. This is the same as 206that given to the constructor (although it will be upper-cased). 207 208The object can be reconfigured to a new telescope by supplying 209a new abbreviation to this method. 210 211 $tel->name('JCMT'); 212 213The object will not change state if the name is not known. 214 215=cut 216 217sub name { 218 my $self = shift; 219 if (@_) { 220 my $name = shift; 221 $self->_configure( $name ); 222 } 223 return $self->{Name}; 224} 225 226=item B<fullname> 227 228Returns the full name of the telescope. For example, if the abbreviated 229name is "JCMT" this will return "JCMT 15 metre". 230 231=cut 232 233sub fullname { 234 my $self = shift; 235 return $self->{FullName}; 236} 237 238=item B<obscode> 239 240Returns or sets the IAU observatory code as listed at 241http://cfa-www.harvard.edu/iau/lists/ObsCodes.html. The object will 242not change state if the observatory code is not known. 243 244=cut 245 246sub obscode { 247 my $self = shift; 248 if( @_ ) { 249 my $obscode = shift; 250 $self->_configure( $obscode ); 251 } 252 return $self->{ObsCode}; 253} 254 255=item B<long> 256 257Longitude of the telescope (east +ve). By default this is in radians. 258 259An argument of "d" or "s" can be supplied to retrieve the value 260in decimal degrees or sexagesimal string format respectively. 261 262 $string = $tel->long("s"); 263 264=cut 265 266sub long { 267 my $self = shift; 268 my $long = $self->{Long}; 269 $long = $self->_cvt_fromrad( $long, shift ) if @_; 270 return $long 271} 272 273=item B<lat> 274 275Geodetic latitude of the telescope. By default this is in radians. 276 277An argument of "d" or "s" can be supplied to retrieve the value 278in decimal degrees or sexagesimal string format respectively. 279 280 $deg = $tel->lat("d"); 281 282=cut 283 284sub lat { 285 my $self = shift; 286 my $lat = $self->{Lat}; 287 $lat = $self->_cvt_fromrad( $lat, shift ) if @_; 288 return $lat 289} 290 291=item B<alt> 292 293Altitude of the telescope in metres above mean sea level. 294 295=cut 296 297sub alt { 298 my $self = shift; 299 return $self->{Alt}; 300} 301 302=item B<parallax> 303 304Return the parallax constants, rho*sin(phi') and rho*cos(phi'), 305where rho is the geocentric radius in Earth radii and phi' is 306the geocentric latitude. Returned as a hash where 'Par_C' is 307rho*sin(phi') and 'Par_S' is rho*cos(phi'). 308 309 @parallax = $tel->parallax; 310 311=cut 312 313sub parallax { 314 my $self = shift; 315 return %{$self->{Parallax}}; 316} 317 318=item B<geoc_lat> 319 320Return the geocentric latitude. By default this is in radians. 321 322An argument of "d" or "s" can be supplied to retrieve the value 323in decimal degrees or sexagesimal string format respectively. 324 325 $deg = $tel->geoc_lat("d"); 326 327=cut 328 329sub geoc_lat { 330 my $self = shift; 331 my $lat = $self->{GeocLat}; 332 $lat = $self->_cvt_fromrad( $lat, shift ) if @_; 333 return $lat; 334} 335 336=item B<geoc_dist> 337 338Return the distance from the centre of the Earth. By default 339this is in metres. 340 341 $geoc_dist = $tel->geoc_dist; 342 343=cut 344 345sub geoc_dist { 346 my $self = shift; 347 return $self->{GeocDist}; 348} 349 350=item B<obsgeo> 351 352Return the cartesian coordinates of the observatory. These are the form required 353for specifying coordinates in the FITS OBSGEO-X, OBSGEO-Y and OBSGEO-Z header 354items. 355 356 ($x, $y, $z) = $tel->obsgeo; 357 358Values are returned in metres. 359 360=cut 361 362sub obsgeo { 363 my $self = shift; 364 my $long = $self->long; 365 my $gclat = $self->geoc_lat; 366 my $dist = $self->geoc_dist; 367 368# Could use the PAL versions but we have local copies of these routines. 369# Seem to give identical answers to PAL within about 50 m. 370# my $gdlat = $self->lat; 371# Astro::PAL::palGeoc( $gdlat, $self->alt, my $pal_r, my $pal_z); 372# $pal_r *= $AU2METRE; 373# $pal_z *= $AU2METRE; 374 375 # calculate distance from observatory to centre of Earth projected onto the equator 376 my $r = $dist * cos( $gclat ); 377 378 # calculate height above the equator 379 my $z = $dist * sin( $gclat ); 380 381# $z = $pal_z; $r = $pal_r; 382 383 # now calculate coordinates projected from the longitude 384 my $x = $r * cos( $long ); 385 my $y = $r * sin( $long ); 386 387 return ($x, $y, $z); 388} 389 390=item B<limits> 391 392Return the telescope limits. 393 394 %limits = $tel->limits; 395 396The limits are returned as a hash with the following keys: 397 398=over 4 399 400=item type 401 402Specifies the way in which the limits are specified. Effectively the 403telescope mount. Values of "AZEL" (for altaz telescopes) and "HADEC" 404(for equatorial telescopes) are currently supported. 405 406=item el 407 408Elevation limit of the telescope. Value is a hash with keys C<max> 409and C<min>. Units are in radians. Only used if C<type> is C<AZEL>. 410 411=item ha 412 413Hour angle limit of the telescope. Value is a hash with keys C<max> 414and C<min>. Units are in radians. Only used if C<type> is C<HADEC>. 415 416=item dec 417 418Declination limit of the telescope. Value is a hash with keys C<max> 419and C<min>. Units are in radians. Only used if C<type> is C<HADEC>. 420 421=back 422 423Only some telescopes have limits defined (please send patches with new 424limits if you know them). If limits are not available for this 425telescope limits corresponding to "above the horizon" are returned. 426 427If limits have been explicitly associated with this object using the 428C<setlimits> method then those limits will be returned. 429 430=cut 431 432sub limits { 433 my $self = shift; 434 croak "Limits() method does not (yet) accept any arguments!" if @_; 435 return %{$self->{LIMITS}} if defined $self->{LIMITS}; 436 437 # Just put them all in a big hash (this could come outside 438 # the method since it does not change) 439 my %limits = ( 440 JCMT => { 441 type => "AZEL", 442 el => { # 5 to 88 deg 443 max => 88 * DD2R, 444 min => 5 * DD2R, 445 }, 446 }, 447 UKIRT => { 448 type => "HADEC", 449 ha => { # +/- 4.5 hours 450 max => 4.5 * DH2R, 451 min => -4.5 * DH2R, 452 }, 453 dec=> { # -42 to +60 deg 454 max => 60 * DD2R, 455 min => -42 * DD2R, 456 }, 457 }, 458 459 ); 460 461 # Return the hash if it exists 462 if (exists $limits{ $self->name }) { 463 return %{ $limits{ $self->name } }; 464 } else { 465 # fudge something for simple observability 466 return ( type => 'AZEL', 467 el => { 468 max => 90 * DD2R, 469 min => 0, 470 } 471 ); 472 } 473 474} 475 476=item B<setlimits> 477 478This method allows limits for this telescope object to be set explicitly. 479The contents of the limits hash must be those described by the C<limits> method 480and will be returned by the C<limits> method). Limits set 481in this way will override built-in limits. 482 483 $tel->setlimits( %limits ); 484 485Limits will be cleared if the object is reconfigured (eg by setting the obscode). 486 487=cut 488 489sub setlimits { 490 my $self = shift; 491 my %limits = @_; 492 croak "Supplied limits do not seem to contain a type key" 493 unless exists $limits{type}; 494 $self->{LIMITS} = \%limits; 495 return; 496} 497 498=back 499 500=head2 Class Methods 501 502=over 4 503 504=item B<telNames> 505 506Obtain a sorted list of all supported telescope names. 507 508 @names = Astro::Telescope->telNames; 509 510Currently only returns the PAL names, and only if Astro::PAL is 511available. If it is not available, return an empty list. 512 513=cut 514 515sub telNames { 516 my @names; 517 if( $ASTRO_PAL ) { 518 my $i = 1; 519 my $ident = ''; 520 while (defined $ident) { 521 my ($ident, $name, $w, $p, $h) = Astro::PAL::palObs($i); 522 last unless defined $ident; 523 $i++; 524 push(@names, $ident); 525 } 526 } 527 return sort @names; 528} 529 530=back 531 532=begin __PRIVATE__ 533 534=head2 Private Methods 535 536=over 4 537 538=item B<_configure> 539 540Reconfigure the object for a new telescope. Called automatically 541by the constructor or if a new telescope name or observatory 542code is provided. 543 544Returns C<undef> if the telescope was not supported. 545 546If more than one argument is supplied the assumption 547is that the user is supplying telescope details. In that case, 548"Name" and "Long" must be supplied, and either the geodetic latitude and 549altitude ("Lat" and "Alt" -- but if "Alt" is not supplied it will 550default to zero and this class will issue a warning), the geocentric 551latitude and distance 552("GeocLat" and "GeocDist"), or the parallax coefficients ("Parallax") 553must be supplied. Latitudes and longitudes must be given in radians, 554altitude and distance in metres, and the parallax constants in units 555of Earth radii. 556 557 $t->_configure('telescope'); 558 $t->_configure( $obscode ); 559 $t->_configure(Name => 'JCMT', Long => $long, Lat => $lat ); 560 561Any user defined limits are cleared by this routine. 562 563=cut 564 565sub _configure { 566 my $self = shift; 567 $self->{LIMITS} = undef; # reset user-supplied limits 568 if (scalar(@_) == 1) { 569 570 my $name = uc(shift); 571 572 &Astro::Telescope::MPC::parse_table; 573 574 if( exists( $Astro::Telescope::MPC::obs_codes{$name} ) ) { 575 576 $self->{Name} = $Astro::Telescope::MPC::obs_codes{$name}->{Name}; 577 $self->{FullName} = $Astro::Telescope::MPC::obs_codes{$name}->{Name}; 578 $self->{ObsCode} = $name; 579 $self->{Long} = $Astro::Telescope::MPC::obs_codes{$name}->{Long}; 580 $self->{Parallax}->{Par_C} = $Astro::Telescope::MPC::obs_codes{$name}->{Par_C}; 581 $self->{Parallax}->{Par_S} = $Astro::Telescope::MPC::obs_codes{$name}->{Par_S}; 582 583 ( $self->{GeocLat}, $self->{GeocDist} ) = $self->_par2geoc(); 584 ( $self->{Lat}, $self->{Alt} ) = $self->_geoc2geod(); 585 586 } elsif( $ASTRO_PAL ) { 587 588 my ($ident, $fullname, $w, $p, $h) = Astro::PAL::palObs($name); 589 590 if( defined $fullname ) { 591 592 # Correct for East positive 593 $w *= -1; 594 595 $self->{Name} = $ident; 596 $self->{FullName} = $fullname; 597 $self->{Long} = $w; 598 $self->{Lat} = $p; 599 $self->{Alt} = $h; 600 601 ( $self->{GeocLat}, $self->{GeocDist} ) = $self->_geod2geoc(); 602 $self->{Parallax} = $self->_geoc2par(); 603 604 $self->{ObsCode} = $pal2obs{$name}; 605 606 } else { 607 return undef; 608 } 609 610 } else { 611 return undef; 612 } 613 614 return 1; 615 616 } else { 617 my %args = @_; 618 619 return undef unless exists $args{Name} && exists $args{Long}; 620 621 if( exists( $args{Lat} ) ) { 622 623 if( !exists( $args{Alt} ) ) { 624 warnings::warnif( "Warning: Altitude not given. Defaulting to zero." ); 625 $self->{Alt} = 0; 626 } else { 627 $self->{Alt} = $args{Alt}; 628 } 629 $self->{Lat} = $args{Lat}; 630 631 if( !exists( $args{GeocLat} ) || !exists( $args{GeocDist} ) ) { 632 ( $self->{GeocLat}, $self->{GeocDist} ) = $self->_geod2geoc(); 633 } 634 635 if( !exists( $args{Parallax} ) ) { 636 $self->{Parallax} = $self->_geoc2par(); 637 } 638 } elsif( exists( $args{Parallax} ) ) { 639 640 $self->{Parallax} = $args{Parallax}; 641 642 if( !exists( $args{GeocLat} ) || !exists( $args{GeocDist} ) ) { 643 ( $self->{GeocLat}, $self->{GeocDist} ) = $self->_par2geoc(); 644 } 645 646 if( !exists( $args{Lat} ) || !exists( $args{Alt} ) ) { 647 ( $self->{Lat}, $self->{Alt} ) = $self->_geoc2geod(); 648 } 649 } elsif( exists( $args{GeocLat} ) && exists( $args{GeocDist} ) ) { 650 651 $self->{GeocLat} = $args{GeocLat}; 652 $self->{GeocDist} = $args{GeocDist}; 653 654 if( !exists( $args{Lat} ) || !exists( $args{Alt} ) ) { 655 ( $self->{Lat}, $self->{Alt} ) = $self->_geoc2geod(); 656 } 657 if( !exists( $args{Parallax} ) ) { 658 $self->{Parallax} = $self->_geoc2par(); 659 } 660 } else { 661 return undef; 662 } 663 664 for my $key (qw/ Name Long FullName ObsCode / ) { 665 $self->{$key} = $args{$key} if exists $args{$key}; 666 } 667 return 1; 668 } 669} 670 671=item B<_cvt_fromrad> 672 673Convert radians to either degrees ("d") or sexagesimal string ("s"). 674 675 $converted = $self->_cvt_fromrad($rad, "s"); 676 677If the second argument is not supplied the string is returned 678unmodified. 679 680The string is space separated by default but this can be overridden 681by setting the variable $Astro::Telescope::Separator to a new value. 682 683=cut 684 685sub _cvt_fromrad { 686 my $self = shift; 687 my $rad = shift; 688 my $format = shift; 689 return $rad unless defined $format; 690 my $degrees = $rad * DR2D; 691 my $out; 692 if ($format =~ /^d/) { 693 $out = $degrees; 694 } elsif ($format =~ /^s/) { 695 696 my $deg = int( $degrees ); 697 my $rem = abs( $degrees - $deg ); 698 my $min = int( 60 * $rem ); 699 $rem = 60 * $rem - $min; 700 my $sec = int( 60 * $rem ); 701 $rem = 60 * $rem - $sec; 702 my $frac = int( $rem * 100 ); 703 704 $out = join($Separator,$deg,$min,$sec) . ".$frac"; 705 } 706 return $out; 707} 708 709=item B<_geod2geoc> 710 711Convert geodetic latitude and altitude to geocentric latitude and 712distance from centre of earth. 713 714 ( $geoc_lat, $geoc_dist ) = $self->_geod2geoc(); 715 716Returns latitude in radians and distance in metres. 717 718=cut 719 720sub _geod2geoc { 721 my $self = shift; 722 723 return undef unless ( defined $self->lat && 724 defined $self->alt ); 725 726 my $lat = $self->lat; 727 my $alt = $self->alt; 728 729 my $lambda_sl = atan2( E * E * sin( $lat ) / cos( $lat ), 1 ); 730 my $sin_lambda_sl = sin( $lambda_sl ); 731 my $cos_lambda_sl = cos( $lambda_sl ); 732 my $sin_mu = sin( $lat ); 733 my $cos_mu = cos( $lat ); 734 my $sl_radius = sqrt( EQU_RAD * EQU_RAD / ( 1 + ( ( 1 / ( E * E ) ) - 1 ) * $sin_lambda_sl * $sin_lambda_sl ) ); 735 736 my $py = $sl_radius * $sin_lambda_sl + $alt * $sin_mu; 737 my $px = $sl_radius * $cos_lambda_sl + $alt * $cos_mu; 738 my $geoc_lat = atan2( $py, $px ); 739 740 my $geoc_dist = sqrt( $py * $py + $px * $px ); 741 742 return( $geoc_lat, $geoc_dist ); 743} 744 745=item B<_geoc2geod> 746 747Convert geocentric latitude and distance from centre of Earth to 748geodetic latitude and altitude. 749 750 ( $geod_lat, $geod_alt ) = $self->_geoc2geod(); 751 752Returns latitude in radians and altitude in metres. 753 754=cut 755 756sub _geoc2geod { 757 my $self = shift; 758 759 return undef unless ( defined $self->{GeocLat} && 760 defined $self->{GeocDist} ); 761 762 my $geoc_lat = $self->{GeocLat}; 763 my $geoc_dist = $self->{GeocDist}; 764 765 my $t_lat = sin( $geoc_lat ) / cos( $geoc_lat ); 766 my $x_alpha = E * EQU_RAD / sqrt( $t_lat * $t_lat + E * E ); 767 my $mu_alpha = atan2( sqrt( EQU_RAD * EQU_RAD - $x_alpha * $x_alpha ), E * $x_alpha ); 768 if( $geoc_lat < 0 ) { 769 $mu_alpha = 0 - $mu_alpha; 770 } 771 my $sin_mu_a = sin( $mu_alpha ); 772 my $delt_lambda = $mu_alpha - $geoc_lat; 773 my $r_alpha = $x_alpha / cos( $geoc_lat ); 774 my $l_point = $geoc_dist - $r_alpha; 775 my $alt = $l_point * cos( $delt_lambda ); 776 my $denom = sqrt( 1 - EPS * EPS * $sin_mu_a * $sin_mu_a ); 777 my $rho_alpha = EQU_RAD * ( 1 - EPS ) / ( $denom * $denom * $denom ); 778 my $delt_mu = atan2( $l_point * sin( $delt_lambda ), $rho_alpha + $alt ); 779 my $geod_lat = $mu_alpha - $delt_mu; 780 my $lambda_sl = atan2( E * E * sin( $geod_lat ) / cos( $geod_lat ), 1 ); 781 my $sin_lambda_sl = sin( $lambda_sl ); 782 my $sea_level_r = sqrt( EQU_RAD * EQU_RAD / ( 1 + ( ( 1 / ( E * E ) ) - 1 ) * $sin_lambda_sl * $sin_lambda_sl ) ); 783 784 return ( $geod_lat, $alt ); 785} 786 787=item B<_geoc2par> 788 789Convert geocentric latitude and distance from centre of Earth to 790parallax constants. 791 792 $parallax = $self->_geoc2par(); 793 794Returns a hash reference, where keys are 'Par_C' and 'Par_S' for 795C and S constants, respectively. 796 797=cut 798 799sub _geoc2par { 800 my $self = shift; 801 802 return undef unless ( defined $self->{GeocLat} && 803 defined $self->{GeocDist} ); 804 805 my %return; 806 807 my $geoc_lat = $self->{GeocLat}; 808 my $geoc_dist = $self->{GeocDist}; 809 810 my $rho = $geoc_dist / EQU_RAD; 811 812 $return{Par_C} = $rho * sin( $geoc_lat ); 813 $return{Par_S} = $rho * cos( $geoc_lat ); 814 815 return \%return; 816 817} 818 819=item B<_par2geoc> 820 821Convert parallax constants to geocentric latitude and distance from 822centre of Earth. 823 824 ( $geoc_lat, $geoc_dist ) = $self->_par2geoc(); 825 826=cut 827 828sub _par2geoc { 829 my $self = shift; 830 831 return undef unless ( defined $self->{Parallax} ); 832 833 my $par_S = $self->{Parallax}->{Par_S}; 834 my $par_C = $self->{Parallax}->{Par_C}; 835 836 my $geoc_lat = atan2( $par_C, $par_S ); 837 my $geoc_dist = sqrt( $par_S * $par_S + $par_C * $par_C ) * EQU_RAD; 838 839 return( $geoc_lat, $geoc_dist ); 840 841} 842 843=back 844 845=head2 Backwards Compatibility 846 847These methods are provided for programs that used the original 848interface: 849 850 lat_by_rad, long_by_rad, lat_by_deg, long_by_deg, alt_by_deg, 851 alt_by_rad 852 853=cut 854 855sub lat_by_rad { 856 my $self = shift; 857 return $self->lat; 858} 859 860sub long_by_rad { 861 my $self = shift; 862 return $self->long; 863} 864 865sub alt_by_rad { 866 my $self = shift; 867 return $self->alt; 868} 869 870sub lat_by_deg { 871 my $self = shift; 872 return $self->lat('d'); 873} 874 875sub long_by_deg { 876 my $self = shift; 877 return $self->long('d'); 878} 879 880sub alt_by_deg { 881 my $self = shift; 882 return $self->alt('d'); 883} 884 885=end __PRIVATE__ 886 887=head1 REQUIREMENTS 888 889The list of telescope properties is currently obtained from those 890provided by PAL (C<Astro::PAL>) and also from the Minor Planet 891Center (http://www.cfa.harvard.edu/iau/lists/ObsCodes.html). 892 893=head1 AUTHORS 894 895Tim Jenness E<lt>t.jenness@jach.hawaii.eduE<gt>, 896Brad Cavanagh E<lt>b.cavanagh@jach.hawaii.eduE<gt> 897 898=head1 COPYRIGHT 899 900Copyright (C) 2007, 2008, 2010, 2012 Science and Technology Facilities Council. 901Copyright (C) 1998-2005 Particle Physics and Astronomy Research Council. 902All Rights Reserved. This program is free software; you can 903redistribute it and/or modify it under the same terms as Perl itself. 904 905=cut 906 9071; 908 909