Astro::FITS::HdrTrans::ACSIS - class for translation of JCMT ACSIS headers


Astro-FITS-HdrTrans documentation Contained in the Astro-FITS-HdrTrans distribution.

Index


Code Index:

NAME

Top

Astro::FITS::HdrTrans::ACSIS - class for translation of JCMT ACSIS headers

SYNOPSIS

Top

  use Astro::FITS::HdrTrans::ACSIS;

DESCRIPTION

Top

This class provides a set of translations for ACSIS at JCMT.

METHODS

Top

can_translate

Returns true if the supplied headers can be handled by this class.

  $cando = $class->can_translate( \%hdrs );

For this class, the method will return true if the BACKEND header exists and matches 'ACSIS'.

COMPLEX CONVERSIONS

Top

These methods are more complicated than a simple mapping. We have to provide both from- and to-FITS conversions All these routines are methods and the to_ routines all take a reference to a hash and return the translated value (a many-to-one mapping) The from_ methods take a reference to a generic hash and return a translated hash (sometimes these are many-to-many)

to_DR_RECIPE

Usually simply copies the "RECIPE" header. If the observation type is skydip and the RECIPE header is "REDUCE_SCIENCE" we actually use REDUCE_SKYDIP. If a skydip is not being done and the STANDARD header is true, then the recipe is set to REDUCE_STANDARD. If the INBEAM header is "POL", the recipe name has "_POL" appended if it is a science observation. "REDUCE_SCIENCE" is translated to "REDUCE_SCIENCE_GRADIENT".

from_DR_RECIPE

Returns DR_RECIPE unless we have a skydip.

to_POLARIMETER

If the polarimeter is in the beam, as denoted by the INBEAM header containing "POL", then this returns true. Otherwise, return false.

from_POLARIMETER

If the POLARIMETER header is true, then return "POL" for the INBEAM header. Otherwise, return undef.

to_SAMPLE_MODE

If the SAM_MODE value is either 'raster' or 'scan', return 'scan'. Otherwise, return the value in lowercase.

to_SURVEY

Checks the value of the SURVEY header and uses that. If it's undefined, then use the PROJECT header to determine an appropriate survey.

to_EXPOSURE_TIME

Uses the to_UTSTART and to_UTEND functions to calculate the exposure time. Returns the exposure time as a scalar, not as a Time::Seconds object.

to_INSTRUMENT

Converts the INSTRUME header into the INSTRUMENT header. If the INSTRUME header begins with "HARP" or "FE_HARP", then the INSTRUMENT header will be set to "HARP".

to_OBSERVATION_ID

Converts the OBSID header directly into the OBSERVATION_ID generic header, or if that header does not exist, converts the BACKEND, OBSNUM, and DATE-OBS headers into OBSERVATION_ID.

to_OBSERVATION_MODE

Concatenates the SAM_MODE, SW_MODE, and OBS_TYPE header keywords into the OBSERVATION_MODE generic header, with spaces removed and joined with underscores. For example, if SAM_MODE is 'jiggle ', SW_MODE is 'chop ', and OBS_TYPE is 'science ', then the OBSERVATION_MODE generic header will be 'jiggle_chop_science'.

to_OBSERVATION_TYPE

Returns the type of observation that was done. If the OBS_TYPE header matches /science/, the SAM_MODE header is used: if SAM_MODE matches /raster/, then return 'raster'. If SAM_MODE matches /grid/, then return 'grid'. If SAM_MODE matches /jiggle/, then return 'jiggle'.

If the OBS_TYPE header matches /focus/, then return 'focus'. If the OBS_TYPE header matches /pointing/, then return 'pointing'.

If none of the above options hold, then return undef.

to_REST_FREQUENCY

Uses an Starlink::AST::FrameSet object to determine the frequency. If such an object is not passed in, then the rest frequency is set to zero.

Returns the rest frequency in Hz.

to_SYSTEM_VELOCITY

Converts the DOPPLER and SPECSYS headers into one combined SYSTEM_VELOCITY header. The first three characters of each specific header are used and concatenated. For example, if DOPPLER is 'radio' and SPECSYS is 'LSR', then the resulting SYSTEM_VELOCITY generic header will be 'RADLSR'. The results are always returned in capital letters.

to_VELOCITY

Converts the ZSOURCE header into an appropriate system velocity, depending on the value of the DOPPLER header. If the DOPPLER header is 'redshift', then the VELOCITY generic header will be returned as a redshift. If the DOPPLER header is 'optical', then the VELOCITY generic header will be returned as an optical velocity. If the DOPPLER header is 'radio', then the VELOCITY generic header will be returned as a radio velocity. Note that calculating the radio velocity from the zeropoint (which is the ZSOURCE header) gives accurates results only if the radio velocity is a small fraction (~0.01) of the speed of light.

REVISION

Top

 $Id$

SEE ALSO

Top

Astro::FITS::HdrTrans, Astro::FITS::HdrTrans::Base

AUTHORS

Top

Tim Jenness <t.jenness@jach.hawaii.edu>, Brad Cavanagh <b.cavanagh@jach.hawaii.edu>.

COPYRIGHT

Top


Astro-FITS-HdrTrans documentation Contained in the Astro-FITS-HdrTrans distribution.
package Astro::FITS::HdrTrans::ACSIS;

use 5.006;
use warnings;
use strict;
use Carp;

use Astro::Coords;
use Astro::Telescope;
use DateTime;
use DateTime::TimeZone;

# inherit from the Base translation class and not HdrTrans
# itself (which is just a class-less wrapper)
use base qw/ Astro::FITS::HdrTrans::JCMT /;

# Use the FITS standard DATE-OBS handling
#use Astro::FITS::HdrTrans::FITS;

# Speed of light in km/s.
use constant CLIGHT => 2.99792458e5;

use vars qw/ $VERSION /;

$VERSION = "1.50";

# Cache UTC definition
our $UTC = DateTime::TimeZone->new( name => 'UTC' );

# in each class we have three sets of data.
#   - constant mappings
#   - unit mappings
#   - complex mappings

# for a constant mapping, there is no FITS header, just a generic
# header that is constant
my %CONST_MAP = (
                 INST_DHS          => 'ACSIS',
                 SUBSYSTEM_IDKEY   => 'SUBSYSNR',
                );

# unit mapping implies that the value propogates directly
# to the output with only a keyword name change

my %UNIT_MAP = (
                AIRMASS_END        => 'AMEND',
                AMBIENT_TEMPERATURE=> 'ATSTART',
                AZIMUTH_END        => 'AZEND',
                BACKEND            => 'BACKEND',
                BANDWIDTH_MODE     => 'BWMODE',
                CHOP_ANGLE         => 'CHOP_PA',
                CHOP_COORDINATE_SYSTEM => 'CHOP_CRD',
                CHOP_FREQUENCY     => 'CHOP_FRQ',
                CHOP_THROW         => 'CHOP_THR',
                ELEVATION_END      => 'ELEND',
                FRONTEND           => 'INSTRUME',
                NUMBER_OF_CYCLES   => 'NUM_CYC',
                SEEING             => 'SEEINGST',
                SWITCH_MODE        => 'SW_MODE',
                VELOCITY_TYPE      => 'DOPPLER',
               );

# Create the translation methods
__PACKAGE__->_generate_lookup_methods( \%CONST_MAP, \%UNIT_MAP );

sub can_translate {
  my $self = shift;
  my $headers = shift;

  if ( exists $headers->{BACKEND} &&
       defined $headers->{BACKEND} &&
       $headers->{BACKEND} =~ /^ACSIS/i
     ) {
    return 1;
  } else {
    return 0;
  }
}

sub to_DR_RECIPE {
  my $class = shift;
  my $FITS_headers = shift;

  my $dr = $FITS_headers->{RECIPE};

  my $obstype = lc( $class->to_OBSERVATION_TYPE( $FITS_headers ) );
  my $pol = $class->to_POLARIMETER( $FITS_headers );
  my $standard = $class->to_STANDARD( $FITS_headers );
  my $utdate = $class->to_UTDATE( $FITS_headers );

  if ($utdate < 20080701) {
    if ($obstype eq 'skydip' && $dr eq 'REDUCE_SCIENCE') {
      $dr = "REDUCE_SKYDIP";
    }
  }

  my $is_sci = ( $obstype =~ /science|raster|scan|grid|chop/ );

  if ( $standard && $is_sci ) {
    $dr = "REDUCE_STANDARD";
  }

  # Append unless we have already appended
  if ( $utdate > 20081115 && $pol && $is_sci ) {
    $dr .= "_POL" unless $dr =~ /_POL$/;
  }

  if( uc( $dr ) eq 'REDUCE_SCIENCE' ) {
    $dr = 'REDUCE_SCIENCE_GRADIENT';
  }

  return $dr;
}

sub from_DR_RECIPE {
  my $class = shift;
  my $generic_headers = shift;
  my $dr = $generic_headers->{DR_RECIPE};
  my $ut = $generic_headers->{UTDATE};
  if (defined $ut && $ut < 20080615) {
    if (defined $dr && $dr eq 'REDUCE_SKYDIP') {
      $dr = 'REDUCE_SCIENCE';
    }
  }
  return ("RECIPE" => $dr);
}

sub to_POLARIMETER {
  my $class = shift;
  my $FITS_headers = shift;

  my $inbeam = $FITS_headers->{INBEAM};
  my $utdate = $class->to_UTDATE( $FITS_headers );

  if ( $utdate > 20081115 &&
       defined( $inbeam ) &&
       $inbeam =~ /pol/i ) {
    return 1;
  }
  return 0;
}

sub from_POLARIMETER {
  my $class = shift;
  my $generic_headers = shift;

  my $pol = $generic_headers->{POLARIMETER};

  if ( $pol ) {
    return ( "INBEAM" => "POL" );
  }

  return ( "INBEAM" => undef );
}

sub to_SAMPLE_MODE {
  my $self = shift;
  my $FITS_headers = shift;

  my $sam_mode;
  if( defined( $FITS_headers->{'SAM_MODE'} ) &&
      uc( $FITS_headers->{'SAM_MODE'} ) eq 'RASTER' ) {
    $sam_mode = 'scan';
  } else {
    $sam_mode = lc( $FITS_headers->{'SAM_MODE'} );
  }
  return $sam_mode;
}

sub to_SURVEY {
  my $self = shift;
  my $FITS_headers = shift;

  my $survey;

  if( defined( $FITS_headers->{'SURVEY'} ) ) {
    $survey = $FITS_headers->{'SURVEY'};
  } else {

    my $project = $FITS_headers->{'PROJECT'};
    if( defined( $project ) ) {
      if( $project =~ /JLS([GNS])/ ) {
        if( $1 eq 'G' ) {
          $survey = 'GBS';
        } elsif( $1 eq 'N' ) {
          $survey = 'NGS';
        } elsif( $1 eq 'S' ) {
          $survey = 'SLS';
        }
      }
    }
  }

  return $survey;

}

sub to_EXPOSURE_TIME {
  my $self = shift;
  my $FITS_headers = shift;

  # force date headers to be standardized
  $self->_fix_dates( $FITS_headers );

  my $return;
  if ( exists( $FITS_headers->{'DATE-OBS'} ) &&
       exists( $FITS_headers->{'DATE-END'} ) ) {
    my $start = $self->to_UTSTART( $FITS_headers );
    my $end = $self->to_UTEND( $FITS_headers );
    my $duration = $end - $start;
    $return = $duration->seconds;
  }
  return $return;
}

sub to_INSTRUMENT {
  my $self = shift;
  my $FITS_headers = shift;
  my $return;
  if ( exists( $FITS_headers->{'INSTRUME'} ) ) {
    if ( $FITS_headers->{'INSTRUME'} =~ /^HARP/ ||
         $FITS_headers->{'INSTRUME'} =~ /^FE_HARP/ ) {
      $return = "HARP";
    } else {
      $return = $FITS_headers->{'INSTRUME'};
    }
  }
  return $return;
}

sub to_OBSERVATION_ID {
  my $self = shift;
  my $FITS_headers = shift;
  my $return;
  if ( exists( $FITS_headers->{'OBSID'} ) &&
       defined( $FITS_headers->{'OBSID'} ) ) {
    $return = $FITS_headers->{'OBSID'};
  } else {
    $self->_fix_dates( $FITS_headers );

    my $backend = lc( $self->to_BACKEND( $FITS_headers ) );
    my $obsnum = $self->to_OBSERVATION_NUMBER( $FITS_headers );
    my $dateobs = $self->to_UTSTART( $FITS_headers );

    if ( defined( $backend ) &&
         defined( $obsnum ) &&
         defined( $dateobs ) ) {
      my $datetime = $dateobs->datetime;
      $datetime =~ s/-//g;
      $datetime =~ s/://g;

      $return = join '_', $backend, $obsnum, $datetime;
    }
  }

  return $return;
}

sub to_OBSERVATION_MODE {
  my $self = shift;
  my $FITS_headers = shift;

  my $return;
  if ( exists( $FITS_headers->{'SAM_MODE'} ) &&
       exists( $FITS_headers->{'SW_MODE'} ) &&
       exists( $FITS_headers->{'OBS_TYPE'} ) ) {
    my $sam_mode = $FITS_headers->{'SAM_MODE'};
    $sam_mode =~ s/\s//g;
    $sam_mode = "raster" if $sam_mode eq "scan";
    my $sw_mode = $FITS_headers->{'SW_MODE'};
    $sw_mode =~ s/\s//g;

    # handle OBS_TYPE missing
    my $obs_type = $FITS_headers->{'OBS_TYPE'};
    $obs_type = "science" unless $obs_type;
    $obs_type =~ s/\s//g;

    $return = ( ( $obs_type =~ /science/i )
                ? join '_', $sam_mode, $sw_mode
                : join '_', $sam_mode, $sw_mode, $obs_type );
  }
  return $return;
}

sub to_OBSERVATION_TYPE {
  my $self = shift;
  my $FITS_headers = shift;

  my $return;
  my $ot = $FITS_headers->{OBS_TYPE};

  # Sometimes we lack OBS_TYPE. In that case we have to assume SCIENCE
  # even though the headers are broken. (eg 20080509#18 RxWD)
  $ot = "science" unless $ot;

  if ( $ot ) {
    my $obs_type = lc( $ot );

    if ( $obs_type =~ /science/ ) {

      if ( defined( $FITS_headers->{'SAM_MODE'} ) ) {

        my $sam_mode = $FITS_headers->{'SAM_MODE'};

        if ( $sam_mode =~ /raster|scan/ ) {
          $return = "raster";
        } elsif ( $sam_mode =~ /grid/ ) {
          $return = "grid";
        } elsif ( $sam_mode =~ /jiggle/ ) {
          $return = "jiggle";
        } else {
          croak "Unexpected sample mode: '$sam_mode'";
        }
      }
    } elsif ( $obs_type =~ /focus/ ) {
      $return = "focus";
    } elsif ( $obs_type =~ /pointing/ ) {
      $return = "pointing";
    } elsif ( $obs_type =~ /skydip/) {
      $return = "skydip";
    } else {
      croak "Unexpected OBS_TYPE of '$obs_type'\n";
    }
  }

  return $return;
}


sub to_REST_FREQUENCY {
  my $self = shift;
  my $FITS_headers = shift;
  my $frameset = shift;

  my $return;

  if ( defined( $frameset ) &&
       UNIVERSAL::isa( $frameset, "Starlink::AST::FrameSet" ) ) {
    # in some rare cases restfreq is not set in the frameset
    eval {
       my $frequency = $frameset->Get( "restfreq" );
       $return = $frequency * 1_000_000_000;
    };
  } elsif ( exists( $FITS_headers->{'RESTFREQ'} ) ||
            ( exists( $FITS_headers->{'SUBHEADERS'} ) &&
              exists( $FITS_headers->{'SUBHEADERS'}->[0]->{'RESTFREQ'} ) ) ) {

    $return = exists( $FITS_headers->{'RESTFREQ'} ) ?
      $FITS_headers->{'RESTFREQ'}           :
        $FITS_headers->{'SUBHEADERS'}->[0]->{'RESTFREQ'};
    $return *= 1_000_000_000;
  }

  return $return;
}

sub to_SYSTEM_VELOCITY {
  my $self = shift;
  my $FITS_headers = shift;
  my $frameset = shift;

  my $return;
  if ( exists( $FITS_headers->{'DOPPLER'} ) && defined $FITS_headers->{DOPPLER} ) {
    my $doppler = uc( $FITS_headers->{'DOPPLER'} );

    if ( defined( $frameset ) &&
         UNIVERSAL::isa( $frameset, "Starlink::AST::FrameSet" ) ) {
      # Sometimes we have frequency axis (rare)
      eval {
        my $sourcevrf = uc( $frameset->Get( "sourcevrf" ) );
        $return = substr( $doppler, 0, 3 ) . substr( $sourcevrf, 0, 3 );
      };
    }
    if (!defined $return) {
      if ( exists( $FITS_headers->{'SPECSYS'} ) ) {
        my $specsys = uc( $FITS_headers->{'SPECSYS'} );
        $return = substr( $doppler, 0, 3 ) . substr( $specsys, 0, 3 );
      } else {
        my $specsys = '';
        if ( $doppler eq 'RADIO' ) {
          $specsys = 'LSRK';
        } elsif ( $doppler eq 'OPTICAL' ) {
          $specsys = 'HELIOCENTRIC';
        }
        $return = substr( $doppler, 0, 3 ) . substr( $specsys, 0, 3 );
      }
    }
  }
  return $return;
}

sub to_VELOCITY {
  my $self = shift;
  my $FITS_headers = shift;
  my $frameset = shift;

  my $velocity = 0;
  if ( defined( $frameset ) &&
       UNIVERSAL::isa( $frameset, "Starlink::AST::FrameSet" ) ) {

    my $sourcesys = "VRAD";
    if ( defined( $FITS_headers->{'DOPPLER'} ) ) {
      if ( $FITS_headers->{'DOPPLER'} =~ /rad/i ) {
        $sourcesys = "VRAD";
      } elsif ( $FITS_headers->{'DOPPLER'} =~ /opt/i ) {
        $sourcesys = "VOPT";
      } elsif ( $FITS_headers->{'DOPPLER'} =~ /red/i ) {
        $sourcesys = "REDSHIFT";
      }
    }
    # Sometimes we do not have a spec frame (broken files)
    eval {
      $frameset->Set( sourcesys => $sourcesys );
      $velocity = $frameset->Get( "sourcevel" );
    };
  } else {

    # We weren't passed a frameset, so try using other headers.
    if ( exists( $FITS_headers->{'DOPPLER'} ) &&
         ( exists( $FITS_headers->{'ZSOURCE'} ) ||
           exists( $FITS_headers->{'SUBHEADERS'}->[0]->{'ZSOURCE'} ) ) ) {
      my $doppler = uc( $FITS_headers->{'DOPPLER'} );
      my $zsource = exists( $FITS_headers->{'ZSOURCE'} ) ?
        $FITS_headers->{'ZSOURCE'}           :
          $FITS_headers->{'SUBHEADERS'}->[0]->{'ZSOURCE'};

      if ( $doppler eq 'REDSHIFT' ) {
        $velocity = $zsource;
      } elsif ( $doppler eq 'OPTICAL' ) {
        $velocity = $zsource * CLIGHT;
      } elsif ( $doppler eq 'RADIO' ) {
        $velocity = ( CLIGHT * $zsource ) / ( 1 + $zsource );
      }
    }
  }

  return $velocity;
}

1;