Example #1
0
def _angularSeparation(long1, lat1, long2, lat2):
    """
    Angular separation between two points in radians

    Parameters
    ----------
    long1 is the first longitudinal coordinate in radians

    lat1 is the first latitudinal coordinate in radians

    long2 is the second longitudinal coordinate in radians

    lat2 is the second latitudinal coordinate in radians

    Returns
    -------
    The angular separation between the two points in radians

    Calculated based on the haversine formula
    From http://en.wikipedia.org/wiki/Haversine_formula
    """
    are_arrays_1 = _validate_inputs([long1, lat1],
                                    ['long1', 'lat1'],
                                    'angularSeparation')

    are_arrays_2 = _validate_inputs([long2, lat2],
                                    ['long2', 'lat2'],
                                    'angularSeparation')

    # The code below is necessary because the call to np.radians() in
    # angularSeparation() will automatically convert floats
    # into length 1 numpy arrays, confusing validate_inputs()
    if are_arrays_1 and len(long1) == 1:
        are_arrays_1 = False
        long1 = long1[0]
        lat1 = lat1[0]

    if are_arrays_2 and len(long2) == 1:
        are_arrays_2 = False
        long2 = long2[0]
        lat2 = lat2[0]

    if are_arrays_1 and are_arrays_2:
        if len(long1) != len(long2):
            raise RuntimeError("You need to pass arrays of the same length "
                               "as arguments to angularSeparation()")

    t1 = np.sin(lat2/2.0 - lat1/2.0)**2
    t2 = np.cos(lat1)*np.cos(lat2)*np.sin(long2/2.0 - long1/2.0)**2
    _sum = t1 + t2

    if isinstance(_sum, numbers.Number):
        if _sum<0.0:
            _sum = 0.0
    else:
        _sum = np.where(_sum<0.0, 0.0, _sum)

    return 2.0*np.arcsin(np.sqrt(_sum))
Example #2
0
    def test_validate_inputs(self):
        """
        test that _validate_inputs returns expected values
        """

        # test that it correctly identifies numpy array inputs
        self.assertTrue(
            _validate_inputs(
                [np.array([1, 2]), np.array([3, 4])], ['a', 'b'],
                'dummy_method'))

        # test that it correctly identifies inputs that are numbers
        self.assertFalse(_validate_inputs([1, 2, 3], ['a', 'b', 'c'], 'dummy'))

        # test that the correct exception is raised when you pass in
        # a number a numpy array
        with self.assertRaises(RuntimeError) as ee:
            _validate_inputs([1, np.array([2, 3])], ['a', 'b'], 'dummy')

        self.assertIn("and the same type", ee.exception.args[0])

        # test that the correct exception is raised when you pass in
        # numpy arrays of different length
        with self.assertRaises(RuntimeError) as ee:
            _validate_inputs(
                [np.array([1, 2]), np.array([1, 2, 3])], ['a', 'b'], 'dummy')

        self.assertIn("same length", ee.exception.args[0])

        # test that an exception is raised if lists (rather than numpy
        # arrays) are passed in
        with self.assertRaises(RuntimeError) as ee:
            _validate_inputs([[1, 2], [3, 4]], ['a', 'b'], 'dummy')

        self.assertIn("either a number or a numpy array", ee.exception.args[0])
Example #3
0
def _raDecFromPupilCoords(xPupil, yPupil, obs_metadata=None,
                          includeRefraction=True, epoch=2000.0):
    """
    @param [in] xPupil -- pupil coordinates in radians.
    Can be a numpy array or a number.

    @param [in] yPupil -- pupil coordinates in radians.
    Can be a numpy array or a number.

    @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
    the state of the telescope

    @param[in] includeRefraction -- a boolean which controls the effects of refraction
    (refraction is used when finding the observed coordinates of the boresite specified
    by obs_metadata)

    @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
    transformations (in years; defaults to 2000)

    @param [out] a 2-D numpy array in which the first row is RA and the second
    row is Dec (both in radians; both in the International Celestial Reference System)

    WARNING: This method does not account for apparent motion due to parallax.
    This method is only useful for mapping positions on a theoretical focal plane
    to positions on the celestial sphere.
    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'], "raDecFromPupilCoords")

    if obs_metadata is None:
        raise RuntimeError("Cannot call raDecFromPupilCoords without obs_metadata")

    if epoch is None:
        raise RuntimeError("Cannot call raDecFromPupilCoords; epoch is None")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("Cannot call raDecFromPupilCoords without rotSkyPos " +
                           "in obs_metadata")

    if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
        raise RuntimeError("Cannot call raDecFromPupilCoords " +
                           "without pointingRA, pointingDec in obs_metadata")

    if obs_metadata.mjd is None:
        raise RuntimeError("Cannot calculate RA, Dec without mjd " +
                           "in obs_metadata")

    raObs, decObs = _observedFromPupilCoords(xPupil, yPupil,
                                             obs_metadata=obs_metadata,
                                             epoch=epoch,
                                             includeRefraction=includeRefraction)

    ra_icrs, dec_icrs = _icrsFromObserved(raObs, decObs,
                                          obs_metadata=obs_metadata,
                                          epoch=epoch,
                                          includeRefraction=includeRefraction)

    return np.array([ra_icrs, dec_icrs])
Example #4
0
def _validate_inputs_and_chipname(input_list, input_names, method_name,
                                  chip_name, chipname_can_be_none = True):
    """
    This will wrap _validate_inputs, but also reformat chip_name if necessary.

    input_list is a list of the inputs passed to a method.

    input_name is a list of the variable names associated with
    input_list

    method_name is the name of the method whose input is being validated.

    chip_name is the chip_name variable passed into the calling method.

    chipname_can_be_none is a boolean that controls whether or not
    chip_name is allowed to be None.

    This method will raise a RuntimeError if:

    1) the contents of input_list are not all of the same type
    2) the contents of input_list are not all floats or numpy arrays
    3) the contents of input_list are different lengths (if numpy arrays)
    4) chip_name is None and chipname_can_be_none is False
    5) chip_name is a list or array of different length than input_list[0]
       (if input_list[0] is a list or array) and len(chip_name)>1

    This method returns a boolean indicating whether input_list[0]
    is a numpy array and a re-casting of chip_name as a list
    of length equal to input_list[0] (unless chip_name is None;
    then it will leave chip_name untouched)
    """

    are_arrays = _validate_inputs(input_list, input_names, method_name)

    if chip_name is None and not chipname_can_be_none:
        raise RuntimeError("You passed chipName=None to %s" % method_name)

    if are_arrays:
        n_pts = len(input_list[0])
    else:
        n_pts = 1

    if isinstance(chip_name, list) or isinstance(chip_name, np.ndarray):
        if len(chip_name) > 1 and len(chip_name) != n_pts:
            raise RuntimeError("You passed %d chipNames to %s.\n" % (len(chip_name), method_name) +
                               "You passed %d %s values." % (len(input_list[0]), input_names[0]))

        if len(chip_name) == 1 and n_pts > 1:
            chip_name_out = [chip_name[0]]*n_pts
        else:
            chip_name_out = chip_name

        return are_arrays, chip_name_out

    elif chip_name is None:
        return are_arrays, chip_name
    else:
        return are_arrays, [chip_name]*n_pts
Example #5
0
def _raDecFromAltAz(altRad, azRad, obs, includeRefraction=True):
    """
    Convert altitude and azimuth to RA and Dec

    @param [in] altRad is the altitude in radians.  Can be a numpy array or a single value.

    @param [in] azRad is the azimuth in radians.  Cant be a numpy array or a single value.

    @param [in] obs is an ObservationMetaData characterizing
    the site of the telescope and the MJD of the observation

    @param [in] includeRefraction is a boolean that turns refraction on and off
    (default True)

    @param [out] RA in radians (in the International Celestial Reference System)

    @param [out] Dec in radians (in the International Celestial Reference System)

    Note: This method is only accurate to within 0.01 arcsec near azimuth = 0 or pi
    """

    are_arrays = _validate_inputs([altRad, azRad], ['altRad', 'azRad'],
                                  "raDecFromAltAz")

    lst = calcLmstLast(obs.mjd.UT1, obs.site.longitude_rad)
    last = lst[1]
    sinAlt = np.sin(altRad)
    cosLat = np.cos(obs.site.latitude_rad)
    sinLat = np.sin(obs.site.latitude_rad)
    decObs = np.arcsin(sinLat * sinAlt +
                       cosLat * np.cos(altRad) * np.cos(azRad))
    costheta = (sinAlt - np.sin(decObs) * sinLat) / (np.cos(decObs) * cosLat)
    if are_arrays:
        haRad0 = np.arccos(costheta)
        # Make sure there were no NaNs
        nanSpots = np.where(np.isnan(haRad0))[0]
        if np.size(nanSpots) > 0:
            haRad0[nanSpots] = 0.5 * np.pi * \
                (1.0 - np.sign(costheta[nanSpots]))
    else:
        haRad0 = np.arccos(costheta)
        if np.isnan(haRad0):
            if np.sign(costheta) > 0.0:
                haRad0 = 0.0
            else:
                haRad0 = np.pi

    haRad = np.where(np.sin(azRad) >= 0.0, -1.0 * haRad0, haRad0)
    raObs = np.radians(last * 15.) - haRad

    raRad, decRad = _icrsFromObserved(raObs,
                                      decObs,
                                      obs_metadata=obs,
                                      epoch=2000.0,
                                      includeRefraction=includeRefraction)

    return raRad, decRad
def _raDecFromAltAz(altRad, azRad, obs, includeRefraction=True):
    """
    Convert altitude and azimuth to RA and Dec

    @param [in] altRad is the altitude in radians.  Can be a numpy array or a single value.

    @param [in] azRad is the azimuth in radians.  Cant be a numpy array or a single value.

    @param [in] obs is an ObservationMetaData characterizing
    the site of the telescope and the MJD of the observation

    @param [in] includeRefraction is a boolean that turns refraction on and off
    (default True)

    @param [out] RA in radians (in the International Celestial Reference System)

    @param [out] Dec in radians (in the International Celestial Reference System)

    Note: This method is only accurate to within 0.01 arcsec near azimuth = 0 or pi
    """

    with np.errstate(invalid='ignore', divide='ignore'):
        are_arrays = _validate_inputs(
            [altRad, azRad], ['altRad', 'azRad'], "raDecFromAltAz")

        lst = calcLmstLast(obs.mjd.UT1, obs.site.longitude_rad)
        last = lst[1]
        sinAlt = np.sin(altRad)
        cosLat = np.cos(obs.site.latitude_rad)
        sinLat = np.sin(obs.site.latitude_rad)
        decObs = np.arcsin(sinLat * sinAlt + cosLat *
                           np.cos(altRad) * np.cos(azRad))
        costheta = (sinAlt - np.sin(decObs) * sinLat) / (np.cos(decObs) * cosLat)
        if are_arrays:
            haRad0 = np.arccos(costheta)
            # Make sure there were no NaNs
            nanSpots = np.where(np.isnan(haRad0))[0]
            if np.size(nanSpots) > 0:
                haRad0[nanSpots] = 0.5 * np.pi * \
                    (1.0 - np.sign(costheta[nanSpots]))
        else:
            haRad0 = np.arccos(costheta)
            if np.isnan(haRad0):
                if np.sign(costheta) > 0.0:
                    haRad0 = 0.0
                else:
                    haRad0 = np.pi

        haRad = np.where(np.sin(azRad) >= 0.0, -1.0 * haRad0, haRad0)
        raObs = np.radians(last * 15.) - haRad

        raRad, decRad = _icrsFromObserved(raObs, decObs,
                                          obs_metadata=obs, epoch=2000.0,
                                          includeRefraction=includeRefraction)

    return raRad, decRad
Example #7
0
def _appGeoFromObserved(ra,
                        dec,
                        includeRefraction=True,
                        wavelength=0.5,
                        obs_metadata=None):
    """
    Convert observed (RA, Dec) to apparent geocentric (RA, Dec).
    More specifically: undo the effects of refraction and diurnal aberration.

    Note: This method is only accurate at zenith distances less than ~ 75 degrees.

    This method works in radians.

    @param [in] ra is observed RA (radians).  Can be a numpy array or a number.

    @param [in] dec is observed Dec (radians).  Can be a numpy array or a number.

    @param [in] includeRefraction is a boolean to turn refraction on and off

    @param [in] wavelength is effective wavelength in microns (default: 0.5)

    @param [in] obs_metadata is an ObservationMetaData characterizing the
    observation.

    @param [out] a 2-D numpy array in which the first row is the apparent
    geocentric RA and the second row is the apparentGeocentric Dec (both
    in radians)
    """

    are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'],
                                  "appGeoFromObserved")

    if obs_metadata is None:
        raise RuntimeError(
            "Cannot call appGeoFromObserved without an obs_metadata")

    if obs_metadata.site is None:
        raise RuntimeError(
            "Cannot call appGeoFromObserved: obs_metadata has no site info")

    if obs_metadata.mjd is None:
        raise RuntimeError(
            "Cannot call appGeoFromObserved: obs_metadata has no mjd")

    obsPrms = _calculateObservatoryParameters(obs_metadata, wavelength,
                                              includeRefraction)

    if are_arrays:
        raOut, decOut = palpy.oapqkVector('r', ra, dec, obsPrms)
    else:
        raOut, decOut = palpy.oapqk('r', ra, dec, obsPrms)

    return np.array([raOut, decOut])
Example #8
0
def _icrsFromAppGeo(ra, dec, epoch=2000.0, mjd=None):
    """
    Convert the apparent geocentric position in (RA, Dec) to
    the mean position in the International Celestial Reference
    System (ICRS)

    This method undoes the effects of precession, annual aberration,
    and nutation.  It is meant for mapping pointing RA and Dec (which
    presumably include the above effects) back to mean ICRS RA and Dec
    so that the user knows how to query a database of mean RA and Decs
    for objects observed at a given telescope pointing.

    WARNING: This method does not account for apparent motion due to parallax.
    This means it should not be used to invert the ICRS-to-apparent geocentric
    transformation for actual celestial objects.  This method is only useful
    for mapping positions on a theoretical celestial sphere.

    This method works in radians.

    @param [in] ra in radians (apparent geocentric).  Can be a numpy array or a number.

    @param [in] dec in radians (apparent geocentric).  Can be a numpy array or a number.

    @param [in] epoch is the julian epoch (in years) of the equinox against which to
    measure RA (default: 2000.0)

    @param [in] mjd is an instantiation of the ModifiedJulianDate class
    representing the date of the observation

    @param [out] a 2-D numpy array in which the first row is the mean ICRS RA and
    the second row is the mean ICRS Dec (both in radians)
    """

    are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromAppGeo")

    # Define star independent mean to apparent place parameters
    # palpy.mappa calculates the star-independent parameters
    # needed to correct RA and Dec
    # e.g the Earth barycentric and heliocentric position and velocity,
    # the precession-nutation matrix, etc.
    #
    # arguments of palpy.mappa are:
    # epoch of mean equinox to be used (Julian)
    #
    # date (MJD)
    params = palpy.mappa(epoch, mjd.TDB)

    if are_arrays:
        raOut, decOut = palpy.ampqkVector(ra, dec, params)
    else:
        raOut, decOut = palpy.ampqk(ra, dec, params)

    return np.array([raOut, decOut])
def _altAzPaFromRaDec(raRad, decRad, obs, includeRefraction=True):
    """
    Convert RA, Dec, longitude, latitude and MJD into altitude, azimuth
    and parallactic angle using PALPY

    @param [in] raRad is RA in radians.  Can be a numpy array or a single value.
    Assumed to be in the International Celestial Reference System.

    @param [in] decRad is Dec in radians.  Can be a numpy array or a single value.
    Assumed to be in the International Celestial Reference System.

    @param [in] obs is an ObservationMetaData characterizing
    the site of the telescope and the MJD of the observation

    @param [in] includeRefraction is a boolean that turns refraction on and off
    (default True)

    @param [out] altitude in radians

    @param [out] azimuth in radians

    @param [out] parallactic angle in radians

    WARNING: This method does not account for apparent motion due to parallax.
    This method is only useful for mapping positions on a theoretical focal plan
    to positions on the celestial sphere.
    """

    are_arrays = _validate_inputs([raRad, decRad], ['ra', 'dec'],
                                  "altAzPaFromRaDec")

    raObs, decObs = \
    _observedFromICRS(raRad, decRad, obs_metadata=obs,
                      epoch=2000.0, includeRefraction=includeRefraction)

    lst = calcLmstLast(obs.mjd.UT1, obs.site.longitude_rad)
    last = lst[1]
    haRad = np.radians(last * 15.0) - raObs

    if are_arrays:
        az, azd, azdd, \
            alt, altd, altdd, \
            pa, pad, padd = palpy.altazVector(
                haRad, decObs, obs.site.latitude_rad)
    else:
        az, azd, azdd, \
            alt, altd, altdd, \
            pa, pad, padd = palpy.altaz(haRad, decObs, obs.site.latitude_rad)

    return alt, az, pa
Example #10
0
def _altAzPaFromRaDec(raRad, decRad, obs, includeRefraction=True):
    """
    Convert RA, Dec, longitude, latitude and MJD into altitude, azimuth
    and parallactic angle using PALPY

    @param [in] raRad is RA in radians.  Can be a numpy array or a single value.
    Assumed to be in the International Celestial Reference System.

    @param [in] decRad is Dec in radians.  Can be a numpy array or a single value.
    Assumed to be in the International Celestial Reference System.

    @param [in] obs is an ObservationMetaData characterizing
    the site of the telescope and the MJD of the observation

    @param [in] includeRefraction is a boolean that turns refraction on and off
    (default True)

    @param [out] altitude in radians

    @param [out] azimuth in radians

    @param [out] parallactic angle in radians

    WARNING: This method does not account for apparent motion due to parallax.
    This method is only useful for mapping positions on a theoretical focal plan
    to positions on the celestial sphere.
    """

    are_arrays = _validate_inputs([raRad, decRad], ['ra', 'dec'],
                                  "altAzPaFromRaDec")

    raObs, decObs = \
    _observedFromICRS(raRad, decRad, obs_metadata=obs,
                      epoch=2000.0, includeRefraction=includeRefraction)

    lst = calcLmstLast(obs.mjd.UT1, obs.site.longitude_rad)
    last = lst[1]
    haRad = np.radians(last * 15.0) - raObs

    if are_arrays:
        az, azd, azdd, \
            alt, altd, altdd, \
            pa, pad, padd = palpy.altazVector(
                haRad, decObs, obs.site.latitude_rad)
    else:
        az, azd, azdd, \
            alt, altd, altdd, \
            pa, pad, padd = palpy.altaz(haRad, decObs, obs.site.latitude_rad)

    return alt, az, pa
def chipNameFromPupilCoordsLSST(xPupil, yPupil, allow_multiple_chips=False):
    """
    Return the names of LSST detectors that see the object specified by
    either (xPupil, yPupil).

    @param [in] xPupil is the x pupil coordinate in radians.
    Must be a numpy array.

    @param [in] yPupil is the y pupil coordinate in radians.
    Must be a numpy array.

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, only the first chip will appear in the list of
    chipNames and warning will be emitted.  If it is 'True' and an object falls on more than one
    chip, a list of chipNames will appear for that object.

    @param [out] a numpy array of chip names

    """

    global _lsst_pupil_coord_map
    if _lsst_pupil_coord_map is None:
        _build_lsst_pupil_coord_map()

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'], "chipNameFromPupilCoordsLSST")

    if not are_arrays:
        raise RuntimeError("Pupil coordinates passed to chipNameFromPupilCoordsLSST must be in numpy arrays")

    cameraPointList = [afwGeom.Point2D(x, y) for x, y in zip(xPupil, yPupil)]

    # Loop through every point being considered.  For each point, assemble a list of detectors
    # whose centers are within 1.1 detector radii of the point.  These are the detectors on which
    # the point could be located.  Store that list of possible detectors as a row in valid_detctors,
    # which will be passed to _findDetectorsListLSST()
    valid_detectors = []
    for xx, yy in zip(xPupil, yPupil):
        possible_dexes = np.where(np.sqrt(np.power(xx-_lsst_pupil_coord_map['xx'], 2) +
                                          np.power(yy-_lsst_pupil_coord_map['yy'], 2))/_lsst_pupil_coord_map['dp'] < 1.1)

        local_valid = [lsst_camera()[_lsst_pupil_coord_map['name'][ii]] for ii in possible_dexes[0]]
        valid_detectors.append(local_valid)

    nameList = _findDetectorsListLSST(cameraPointList, valid_detectors,
                                      allow_multiple_chips=allow_multiple_chips)

    return nameList
Example #12
0
def pupilCoordsFromFocalPlaneCoords(xFocal, yFocal, camera=None):
    """
    Get the pupil coordinates in radians from the focal plane
    coordinates in millimeters

    @param [in] xFocal the x focal plane coordinates in millimeters.
    Can be a float or a numpy array.

    @param [in] yFocal the y focal plane coordinates in millimeters.
    Can be a float or a numpy array.

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    pupil coordinate and the second row is the y pupil
    coordinate (both in radians)
    """

    are_arrays = _validate_inputs([xFocal, yFocal], ['xFocal', 'yFocal'],
                                  'pupilCoordsFromFocalPlaneCoords')

    if camera is None:
        raise RuntimeError(
            "You cannot calculate pupil coordinates without specifying a camera"
        )

    focal_to_field = camera.getTransformMap().getTransform(
        FOCAL_PLANE, FIELD_ANGLE)

    if are_arrays:
        focal_point_list = [geom.Point2D(x, y) for x, y in zip(xFocal, yFocal)]
        pupil_point_list = focal_to_field.applyForward(focal_point_list)
        pupil_arr = np.array([[pp.getX(), pp.getY()]
                              for pp in pupil_point_list]).transpose()
        is_nan = np.where(np.logical_or(np.isnan(xFocal), np.isnan(yFocal)))
        pupil_arr[0][is_nan] = np.NaN
        pupil_arr[1][is_nan] = np.NaN

        return pupil_arr

    # if not are_arrays
    if np.isfinite(xFocal) and np.isfinite(yFocal):
        pupPoint = focal_to_field.applyForward(geom.Point2D(xFocal, yFocal))
        return np.array([pupPoint.getX(), pupPoint.getY()])

    return np.array([np.NaN, np.NaN])
Example #13
0
def pupilCoordsFromFocalPlaneCoords(xFocal, yFocal, camera=None):
    """
    Get the pupil coordinates in radians from the focal plane
    coordinates in millimeters

    @param [in] xFocal the x focal plane coordinates in millimeters.
    Can be a float or a numpy array.

    @param [in] yFocal the y focal plane coordinates in millimeters.
    Can be a float or a numpy array.

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    pupil coordinate and the second row is the y pupil
    coordinate (both in radians)
    """

    are_arrays = _validate_inputs([xFocal, yFocal],
                                  ['xFocal', 'yFocal'],
                                  'pupilCoordsFromFocalPlaneCoords')

    if camera is None:
        raise RuntimeError("You cannot calculate pupil coordinates without specifying a camera")

    focal_to_field = camera.getTransformMap().getTransform(FOCAL_PLANE, FIELD_ANGLE)

    if are_arrays:
        focal_point_list = [geom.Point2D(x,y) for x,y in zip(xFocal, yFocal)]
        pupil_point_list = focal_to_field.applyForward(focal_point_list)
        pupil_arr = np.array([[pp.getX(), pp.getY()]
                              for pp in pupil_point_list]).transpose()
        is_nan = np.where(np.logical_or(np.isnan(xFocal), np.isnan(yFocal)))
        pupil_arr[0][is_nan] = np.NaN
        pupil_arr[1][is_nan] = np.NaN

        return pupil_arr

    # if not are_arrays
    if np.isfinite(xFocal) and np.isfinite(yFocal):
        pupPoint = focal_to_field.applyForward(geom.Point2D(xFocal, yFocal))
        return np.array([pupPoint.getX(), pupPoint.getY()])

    return np.array([np.NaN, np.NaN])
Example #14
0
def focalPlaneCoordsFromPupilCoords(xPupil, yPupil, camera=None):
    """
    Get the focal plane coordinates for all objects in the catalog.

    @param [in] xPupil the x pupil coordinates in radians.
    Can be a float or a numpy array.

    @param [in] yPupil the y pupil coordinates in radians.
    Can be a float or a numpy array.

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    focal plane coordinate and the second row is the y focal plane
    coordinate (both in millimeters)
    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'],
                                  'focalPlaneCoordsFromPupilCoords')

    if camera is None:
        raise RuntimeError(
            "You cannot calculate focal plane coordinates without specifying a camera"
        )

    if are_arrays:
        xPix = []
        yPix = []
        for x, y in zip(xPupil, yPupil):
            cp = camera.makeCameraPoint(afwGeom.Point2D(x, y), PUPIL)
            fpPoint = camera.transform(cp, FOCAL_PLANE).getPoint()
            xPix.append(fpPoint.getX())
            yPix.append(fpPoint.getY())

        return np.array([xPix, yPix])

    # if not are_arrays
    cp = camera.makeCameraPoint(afwGeom.Point2D(xPupil, yPupil), PUPIL)
    fpPoint = camera.transform(cp, FOCAL_PLANE).getPoint()
    return np.array([fpPoint.getX(), fpPoint.getY()])
Example #15
0
def focalPlaneCoordsFromPupilCoords(xPupil, yPupil, camera=None):
    """
    Get the focal plane coordinates for all objects in the catalog.

    @param [in] xPupil the x pupil coordinates in radians.
    Can be a float or a numpy array.

    @param [in] yPupil the y pupil coordinates in radians.
    Can be a float or a numpy array.

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    focal plane coordinate and the second row is the y focal plane
    coordinate (both in millimeters)
    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'],
                                  'focalPlaneCoordsFromPupilCoords')

    if camera is None:
        raise RuntimeError(
            "You cannot calculate focal plane coordinates without specifying a camera"
        )

    field_to_focal = camera.getTransformMap().getTransform(
        FIELD_ANGLE, FOCAL_PLANE)

    if are_arrays:
        pupil_point_list = [geom.Point2D(x, y) for x, y in zip(xPupil, yPupil)]
        focal_point_list = field_to_focal.applyForward(pupil_point_list)
        xFocal = np.array([pp.getX() for pp in focal_point_list])
        yFocal = np.array([pp.getY() for pp in focal_point_list])

        return np.array([xFocal, yFocal])

    # if not are_arrays
    fpPoint = field_to_focal.applyForward(geom.Point2D(xPupil, yPupil))
    return np.array([fpPoint.getX(), fpPoint.getY()])
Example #16
0
def focalPlaneCoordsFromPupilCoords(xPupil, yPupil, camera=None):
    """
    Get the focal plane coordinates for all objects in the catalog.

    @param [in] xPupil the x pupil coordinates in radians.
    Can be a float or a numpy array.

    @param [in] yPupil the y pupil coordinates in radians.
    Can be a float or a numpy array.

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    focal plane coordinate and the second row is the y focal plane
    coordinate (both in millimeters)
    """

    are_arrays = _validate_inputs([xPupil, yPupil],
                                  ['xPupil', 'yPupil'], 'focalPlaneCoordsFromPupilCoords')

    if camera is None:
        raise RuntimeError("You cannot calculate focal plane coordinates without specifying a camera")

    field_to_focal = camera.getTransformMap().getTransform(FIELD_ANGLE, FOCAL_PLANE)

    if are_arrays:
        pupil_point_list = [geom.Point2D(x,y) for x,y in zip(xPupil, yPupil)]
        focal_point_list = field_to_focal.applyForward(pupil_point_list)
        xFocal = np.array([pp.getX() for pp in focal_point_list])
        yFocal = np.array([pp.getY() for pp in focal_point_list])

        return np.array([xFocal, yFocal])

    # if not are_arrays
    fpPoint = field_to_focal.applyForward(geom.Point2D(xPupil, yPupil))
    return np.array([fpPoint.getX(), fpPoint.getY()])
Example #17
0
def _focalPlaneCoordsFromRaDec(ra,
                               dec,
                               pm_ra=None,
                               pm_dec=None,
                               parallax=None,
                               v_rad=None,
                               obs_metadata=None,
                               epoch=2000.0,
                               camera=None):
    """
    Get the focal plane coordinates for all objects in the catalog.

    @param [in] ra is in radians in the International Celestial Reference System.
    Can be either a float or a numpy array.

    @param [in] dec is in radians in the International Celestial Reference System.
    Can be either a float or a numpy array.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).


    @param [in] obs_metadata is an ObservationMetaData object describing the telescope
    pointing (only if specifying RA and Dec rather than pupil coordinates)

    @param [in] epoch is the julian epoch of the mean equinox used for coordinate transformations
    (in years; only if specifying RA and Dec rather than pupil coordinates; default is 2000)

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    focal plane coordinate and the second row is the y focal plane
    coordinate (both in millimeters)
    """

    _validate_inputs([ra, dec], ['ra', 'dec'], 'focalPlaneCoordsFromRaDec')

    if epoch is None:
        raise RuntimeError("You have to specify an epoch to run "
                           "focalPlaneCoordsFromRaDec")

    if obs_metadata is None:
        raise RuntimeError("You have to specify an ObservationMetaData to run "
                           "focalPlaneCoordsFromRaDec")

    if obs_metadata.mjd is None:
        raise RuntimeError("You need to pass an ObservationMetaData with an "
                           "mjd into focalPlaneCoordsFromRaDec")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("You need to pass an ObservationMetaData with a "
                           "rotSkyPos into focalPlaneCoordsFromRaDec")

    xPupil, yPupil = _pupilCoordsFromRaDec(ra,
                                           dec,
                                           pm_ra=pm_ra,
                                           pm_dec=pm_dec,
                                           parallax=parallax,
                                           v_rad=v_rad,
                                           obs_metadata=obs_metadata,
                                           epoch=epoch)

    return focalPlaneCoordsFromPupilCoords(xPupil, yPupil, camera=camera)
def _rawPupilCoordsFromObserved(ra_obs, dec_obs, ra0, dec0, rotSkyPos):
    """
    Convert Observed RA, Dec into pupil coordinates

    Parameters
    ----------
    ra_obs is the observed RA in radians

    dec_obs is the observed Dec in radians

    ra0 is the RA of the boresite in radians

    dec0 is the Dec of the boresite in radians

    rotSkyPos is in radians

    Returns
    --------
    A numpy array whose first row is the x coordinate on the pupil in
    radians and whose second row is the y coordinate in radians
    """

    are_arrays = _validate_inputs([ra_obs, dec_obs], ['ra_obs', 'dec_obs'],
                                  "pupilCoordsFromObserved")

    theta = -1.0 * rotSkyPos

    ra_pointing = ra0
    dec_pointing = dec0

    # palpy.ds2tp performs the gnomonic projection on ra_in and dec_in
    # with a tangent point at (pointingRA, pointingDec)
    #
    if not are_arrays:
        try:
            x, y = palpy.ds2tp(ra_obs, dec_obs, ra_pointing, dec_pointing)
        except:
            x = np.NaN
            y = np.NaN
    else:
        try:
            x, y = palpy.ds2tpVector(ra_obs, dec_obs, ra_pointing,
                                     dec_pointing)
        except:
            # apparently, one of your ra/dec values was improper; we will have to do this
            # element-wise, putting NaN in the place of the bad values
            x = []
            y = []
            for rr, dd in zip(ra_obs, dec_obs):
                try:
                    xx, yy = palpy.ds2tp(rr, dd, ra_pointing, dec_pointing)
                except:
                    xx = np.NaN
                    yy = np.NaN
                x.append(xx)
                y.append(yy)
            x = np.array(x)
            y = np.array(y)

    # rotate the result by rotskypos (rotskypos being "the angle of the sky relative to
    # camera coordinates" according to phoSim documentation) to account for
    # the rotation of the focal plane about the telescope pointing

    x_out = x * np.cos(theta) - y * np.sin(theta)
    y_out = x * np.sin(theta) + y * np.cos(theta)

    return np.array([x_out, y_out])
Example #19
0
def _pupilCoordsFromRaDec(ra_in, dec_in,
                          pm_ra=None, pm_dec=None,
                          parallax=None, v_rad=None,
                          includeRefraction=True,
                          obs_metadata=None, epoch=2000.0):
    """
    Take an input RA and dec from the sky and convert it to coordinates
    on the focal plane.

    This uses PAL's gnomonic projection routine which assumes that the focal
    plane is perfectly flat.  The output is in Cartesian coordinates, assuming
    that the Celestial Sphere is a unit sphere.

    The RA, Dec accepted by this method are in the International Celestial
    Reference System.  Before applying the gnomonic projection, this method
    transforms those RA, Dec into observed geocentric coordinates, applying
    the effects of precession, nutation, aberration, parallax and refraction.
    This is done, because the gnomonic projection ought to be applied to what
    observers actually see, rather than the idealized, above-the-atmosphere
    coordinates represented by the ICRS.

    @param [in] ra_in is in radians (ICRS).  Can be either a numpy array or a number.

    @param [in] dec_in is in radians (ICRS).  Can be either a numpy array or a number.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).

    @param [in] includeRefraction is a boolean controlling the application of refraction.

    @param [in] obs_metadata is an ObservationMetaData instantiation characterizing the
    telescope location and pointing.

    @param [in] epoch is the epoch of mean ra and dec in julian years (default=2000.0)

    @param [out] returns a numpy array whose first row is the x coordinate on the pupil in
    radians and whose second row is the y coordinate in radians
    """

    are_arrays = _validate_inputs([ra_in, dec_in], ['ra_in', 'dec_in'],
                                  "pupilCoordsFromRaDec")

    if obs_metadata is None:
        raise RuntimeError("Cannot call pupilCoordsFromRaDec without obs_metadata")

    if obs_metadata.mjd is None:
        raise RuntimeError("Cannot call pupilCoordsFromRaDec; obs_metadata.mjd is None")

    if epoch is None:
        raise RuntimeError("Cannot call pupilCoordsFromRaDec; epoch is None")

    if obs_metadata.rotSkyPos is None:
        # there is no observation meta data on which to base astrometry
        raise RuntimeError("Cannot calculate [x,y]_focal_nominal without obs_metadata.rotSkyPos")

    if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
        raise RuntimeError("Cannot calculate [x,y]_focal_nominal without pointingRA and Dec in obs_metadata")

    ra_obs, dec_obs = _observedFromICRS(ra_in, dec_in,
                                        pm_ra=pm_ra, pm_dec=pm_dec,
                                        parallax=parallax, v_rad=v_rad,
                                        obs_metadata=obs_metadata,
                                        epoch=epoch,
                                        includeRefraction=includeRefraction)

    return _pupilCoordsFromObserved(ra_obs, dec_obs, obs_metadata,
                                    epoch=epoch, includeRefraction=includeRefraction)
Example #20
0
def _focalPlaneCoordsFromRaDec(ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None,
                               obs_metadata=None, epoch=2000.0, camera=None):
    """
    Get the focal plane coordinates for all objects in the catalog.

    @param [in] ra is in radians in the International Celestial Reference System.
    Can be either a float or a numpy array.

    @param [in] dec is in radians in the International Celestial Reference System.
    Can be either a float or a numpy array.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).


    @param [in] obs_metadata is an ObservationMetaData object describing the telescope
    pointing (only if specifying RA and Dec rather than pupil coordinates)

    @param [in] epoch is the julian epoch of the mean equinox used for coordinate transformations
    (in years; only if specifying RA and Dec rather than pupil coordinates; default is 2000)

    @param [in] camera is an afw.cameraGeom camera object

    @param [out] a 2-D numpy array in which the first row is the x
    focal plane coordinate and the second row is the y focal plane
    coordinate (both in millimeters)
    """

    _validate_inputs([ra, dec], ['ra', 'dec'], 'focalPlaneCoordsFromRaDec')

    if epoch is None:
        raise RuntimeError("You have to specify an epoch to run "
                           "focalPlaneCoordsFromRaDec")

    if obs_metadata is None:
        raise RuntimeError("You have to specify an ObservationMetaData to run "
                           "focalPlaneCoordsFromRaDec")

    if obs_metadata.mjd is None:
        raise RuntimeError("You need to pass an ObservationMetaData with an "
                           "mjd into focalPlaneCoordsFromRaDec")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("You need to pass an ObservationMetaData with a "
                           "rotSkyPos into focalPlaneCoordsFromRaDec")

    xPupil, yPupil = _pupilCoordsFromRaDec(ra, dec,
                                           pm_ra=pm_ra, pm_dec=pm_dec,
                                           parallax=parallax, v_rad=v_rad,
                                           obs_metadata=obs_metadata,
                                           epoch=epoch)

    return focalPlaneCoordsFromPupilCoords(xPupil, yPupil, camera=camera)
Example #21
0
def _validate_inputs_and_chipname(input_list,
                                  input_names,
                                  method_name,
                                  chip_name,
                                  chipname_can_be_none=True):
    """
    This will wrap _validate_inputs, but also reformat chip_name if necessary.

    input_list is a list of the inputs passed to a method.

    input_name is a list of the variable names associated with
    input_list

    method_name is the name of the method whose input is being validated.

    chip_name is the chip_name variable passed into the calling method.

    chipname_can_be_none is a boolean that controls whether or not
    chip_name is allowed to be None.

    This method will raise a RuntimeError if:

    1) the contents of input_list are not all of the same type
    2) the contents of input_list are not all floats or numpy arrays
    3) the contents of input_list are different lengths (if numpy arrays)
    4) chip_name is None and chipname_can_be_none is False
    5) chip_name is a list or array of different length than input_list[0]
       (if input_list[0] is a list or array) and len(chip_name)>1

    This method returns a boolean indicating whether input_list[0]
    is a numpy array and a re-casting of chip_name as a list
    of length equal to input_list[0] (unless chip_name is None;
    then it will leave chip_name untouched)
    """

    are_arrays = _validate_inputs(input_list, input_names, method_name)

    if chip_name is None and not chipname_can_be_none:
        raise RuntimeError("You passed chipName=None to %s" % method_name)

    if are_arrays:
        n_pts = len(input_list[0])
    else:
        n_pts = 1

    if isinstance(chip_name, list) or isinstance(chip_name, np.ndarray):
        if len(chip_name) > 1 and len(chip_name) != n_pts:
            raise RuntimeError("You passed %d chipNames to %s.\n" %
                               (len(chip_name), method_name) +
                               "You passed %d %s values." %
                               (len(input_list[0]), input_names[0]))

        if len(chip_name) == 1 and n_pts > 1:
            chip_name_out = [chip_name[0]] * n_pts
        else:
            chip_name_out = chip_name

        return are_arrays, chip_name_out

    elif chip_name is None:
        return are_arrays, chip_name
    else:
        return are_arrays, [chip_name] * n_pts
Example #22
0
def _observedFromPupilCoords(xPupil, yPupil, obs_metadata=None,
                             includeRefraction=True,
                             epoch=2000.0):
    """
    Convert pupil coordinates into observed (RA, Dec)

    @param [in] xPupil -- pupil coordinates in radians.
    Can be a numpy array or a number.

    @param [in] yPupil -- pupil coordinates in radians.
    Can be a numpy array or a number.

    @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
    the state of the telescope

    @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
    transformations (in years; defaults to 2000)

    @param[in] includeRefraction -- a boolean which controls the effects of refraction
    (refraction is used when finding the observed coordinates of the boresite specified
    by obs_metadata)

    @param [out] a 2-D numpy array in which the first row is observed RA and the second
    row is observed Dec (both in radians).  Note: these are not ICRS coordinates.
    These are RA and Dec-like coordinates resulting from applying precession, nutation,
    diurnal aberration and annual aberration on top of ICRS coordinates.

    WARNING: This method does not account for apparent motion due to parallax.
    This method is only useful for mapping positions on a theoretical focal plane
    to positions on the celestial sphere.
    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'],
                                  "observedFromPupilCoords")

    if obs_metadata is None:
        raise RuntimeError("Cannot call observedFromPupilCoords without obs_metadata")

    if epoch is None:
        raise RuntimeError("Cannot call observedFromPupilCoords; epoch is None")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("Cannot call observedFromPupilCoords without rotSkyPos " +
                           "in obs_metadata")

    if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
        raise RuntimeError("Cannot call observedFromPupilCoords " +
                           "without pointingRA, pointingDec in obs_metadata")

    if obs_metadata.mjd is None:
        raise RuntimeError("Cannot calculate RA, Dec without mjd " +
                           "in obs_metadata")

    ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA,
                                                  obs_metadata._pointingDec,
                                                  obs_metadata=obs_metadata,
                                                  epoch=epoch,
                                                  includeRefraction=includeRefraction)

    # This is the same as theta in pupilCoordsFromRaDec, except without the minus sign.
    # This is because we will be reversing the rotation performed in that other method.
    theta = obs_metadata._rotSkyPos

    x_g = xPupil*np.cos(theta) - yPupil*np.sin(theta)
    y_g = xPupil*np.sin(theta) + yPupil*np.cos(theta)

    # x_g and y_g are now the x and y coordinates
    # can now use the PALPY method palDtp2s to convert to RA, Dec.

    if are_arrays:
        raObs, decObs = palpy.dtp2sVector(x_g, y_g, ra_pointing, dec_pointing)
    else:
        raObs, decObs = palpy.dtp2s(x_g, y_g, ra_pointing, dec_pointing)

    return raObs, decObs
Example #23
0
def _raDecFromPupilCoords(xPupil, yPupil, obs_metadata=None, epoch=2000.0):
    """
    @param [in] xPupil -- pupil coordinates in radians.
    Can be a numpy array or a number.

    @param [in] yPupil -- pupil coordinates in radians.
    Can be a numpy array or a number.

    @param [in] obs_metadata -- an instantiation of ObservationMetaData characterizing
    the state of the telescope

    @param [in] epoch -- julian epoch of the mean equinox used for the coordinate
    transformations (in years; defaults to 2000)

    @param [out] a 2-D numpy array in which the first row is RA and the second
    row is Dec (both in radians; both in the International Celestial Reference System)

    WARNING: This method does not account for apparent motion due to parallax.
    This method is only useful for mapping positions on a theoretical focal plane
    to positions on the celestial sphere.
    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'],
                                  "raDecFromPupilCoords")

    if obs_metadata is None:
        raise RuntimeError(
            "Cannot call raDecFromPupilCoords without obs_metadata")

    if epoch is None:
        raise RuntimeError("Cannot call raDecFromPupilCoords; epoch is None")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError(
            "Cannot call raDecFromPupilCoords without rotSkyPos " +
            "in obs_metadata")

    if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
        raise RuntimeError("Cannot call raDecFromPupilCoords " +
                           "without pointingRA, pointingDec in obs_metadata")

    if obs_metadata.mjd is None:
        raise RuntimeError("Cannot calculate x_pupil, y_pupil without mjd " +
                           "in obs_metadata")

    ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA,
                                                  obs_metadata._pointingDec,
                                                  obs_metadata=obs_metadata,
                                                  epoch=epoch,
                                                  includeRefraction=True)

    # This is the same as theta in pupilCoordsFromRaDec, except without the minus sign.
    # This is because we will be reversing the rotation performed in that other method.
    theta = -1.0 * obs_metadata._rotSkyPos

    x_g = xPupil * np.cos(theta) - yPupil * np.sin(theta)
    y_g = xPupil * np.sin(theta) + yPupil * np.cos(theta)

    x_g *= -1.0

    # x_g and y_g are now the x and y coordinates
    # can now use the PALPY method palDtp2s to convert to RA, Dec.

    if are_arrays:
        raObs, decObs = palpy.dtp2sVector(x_g, y_g, ra_pointing, dec_pointing)
    else:
        raObs, decObs = palpy.dtp2s(x_g, y_g, ra_pointing, dec_pointing)

    ra_icrs, dec_icrs = _icrsFromObserved(raObs,
                                          decObs,
                                          obs_metadata=obs_metadata,
                                          epoch=epoch,
                                          includeRefraction=True)

    return np.array([ra_icrs, dec_icrs])
Example #24
0
def _chipNameFromRaDecLSST(ra,
                           dec,
                           pm_ra=None,
                           pm_dec=None,
                           parallax=None,
                           v_rad=None,
                           obs_metadata=None,
                           epoch=2000.0,
                           allow_multiple_chips=False,
                           band='r'):
    """
    Return the names of detectors on the LSST camera that see the object specified by
    (RA, Dec) in radians.

    @param [in] ra in radians (a numpy array or a float).
    In the International Celestial Reference System.

    @param [in] dec in radians (a numpy array or a float).
    In the International Celestial Reference System.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).

    @param [in] obs_metadata is an ObservationMetaData characterizing the telescope pointing

    @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are
    measured.  Default is 2000.

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, only the first chip will appear in the list of
    chipNames but NO WARNING WILL BE EMITTED.  If it is 'True' and an object falls on more than one
    chip, a list of chipNames will appear for that object.

    @param [in] band is the filter we are simulating (Default=r)

    @param [out] the name(s) of the chips on which ra, dec fall (will be a numpy
    array if more than one)
    """

    are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'],
                                  "chipNameFromRaDecLSST")

    if epoch is None:
        raise RuntimeError("You need to pass an epoch into chipName")

    if obs_metadata is None:
        raise RuntimeError(
            "You need to pass an ObservationMetaData into chipName")

    if obs_metadata.mjd is None:
        raise RuntimeError(
            "You need to pass an ObservationMetaData with an mjd into chipName"
        )

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError(
            "You need to pass an ObservationMetaData with a rotSkyPos into chipName"
        )

    xp, yp = _pupilCoordsFromRaDec(ra,
                                   dec,
                                   pm_ra=pm_ra,
                                   pm_dec=pm_dec,
                                   parallax=parallax,
                                   v_rad=v_rad,
                                   obs_metadata=obs_metadata,
                                   epoch=epoch)

    return chipNameFromPupilCoordsLSST(
        xp, yp, allow_multiple_chips=allow_multiple_chips, band=band)
Example #25
0
def _rawPupilCoordsFromObserved(ra_obs, dec_obs, ra0, dec0, rotSkyPos):
    """
    Convert Observed RA, Dec into pupil coordinates

    Parameters
    ----------
    ra_obs is the observed RA in radians

    dec_obs is the observed Dec in radians

    ra0 is the RA of the boresite in radians

    dec0 is the Dec of the boresite in radians

    rotSkyPos is in radians

    Returns
    --------
    A numpy array whose first row is the x coordinate on the pupil in
    radians and whose second row is the y coordinate in radians
    """

    are_arrays = _validate_inputs([ra_obs, dec_obs], ['ra_obs', 'dec_obs'],
                                  "pupilCoordsFromObserved")

    theta = -1.0*rotSkyPos

    ra_pointing = ra0
    dec_pointing = dec0

    # palpy.ds2tp performs the gnomonic projection on ra_in and dec_in
    # with a tangent point at (pointingRA, pointingDec)
    #
    if not are_arrays:
        try:
            x, y = palpy.ds2tp(ra_obs, dec_obs, ra_pointing, dec_pointing)
        except:
            x = np.NaN
            y = np.NaN
    else:
        try:
            x, y = palpy.ds2tpVector(ra_obs, dec_obs, ra_pointing, dec_pointing)
        except:
            # apparently, one of your ra/dec values was improper; we will have to do this
            # element-wise, putting NaN in the place of the bad values
            x = []
            y = []
            for rr, dd in zip(ra_obs, dec_obs):
                try:
                    xx, yy = palpy.ds2tp(rr, dd, ra_pointing, dec_pointing)
                except:
                    xx = np.NaN
                    yy = np.NaN
                x.append(xx)
                y.append(yy)
            x = np.array(x)
            y = np.array(y)

    # rotate the result by rotskypos (rotskypos being "the angle of the sky relative to
    # camera coordinates" according to phoSim documentation) to account for
    # the rotation of the focal plane about the telescope pointing

    x_out = x*np.cos(theta) - y*np.sin(theta)
    y_out = x*np.sin(theta) + y*np.cos(theta)

    return np.array([x_out, y_out])
Example #26
0
def _chipNameFromRaDec(ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None,
                       obs_metadata=None, camera=None,
                       epoch=2000.0, allow_multiple_chips=False):
    """
    Return the names of detectors that see the object specified by
    (RA, Dec)  in radians.

    @param [in] ra in radians (a numpy array or a float).
    In the International Celestial Reference System.

    @param [in] dec in radians (a numpy array or a float).
    In the International Celestial Reference System.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).


    @param [in] obs_metadata is an ObservationMetaData characterizing the telescope pointing

    @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are
    measured.  Default is 2000.

    @param [in] camera is an afw.cameraGeom camera instance characterizing the camera

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, an exception will be raised.  If it is 'True'
    and an object falls on more than one chip, it will still only return the first chip in the
    list of chips returned. THIS BEHAVIOR SHOULD BE FIXED IN A FUTURE TICKET.

    @param [out] the name(s) of the chips on which ra, dec fall (will be a numpy
    array if more than one)
    """

    are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "chipNameFromRaDec")

    if epoch is None:
        raise RuntimeError("You need to pass an epoch into chipName")

    if obs_metadata is None:
        raise RuntimeError("You need to pass an ObservationMetaData into chipName")

    if obs_metadata.mjd is None:
        raise RuntimeError("You need to pass an ObservationMetaData with an mjd into chipName")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("You need to pass an ObservationMetaData with a rotSkyPos into chipName")

    if not are_arrays:
        ra = np.array([ra])
        dec = np.array([dec])

    xp, yp = _pupilCoordsFromRaDec(ra, dec,
                                   pm_ra=pm_ra, pm_dec=pm_dec, parallax=parallax, v_rad=v_rad,
                                   obs_metadata=obs_metadata, epoch=epoch)

    ans = chipNameFromPupilCoords(xp, yp, camera=camera, allow_multiple_chips=allow_multiple_chips)

    if not are_arrays:
        return ans[0]
    return ans
Example #27
0
def _chipNameFromRaDec(ra,
                       dec,
                       pm_ra=None,
                       pm_dec=None,
                       parallax=None,
                       v_rad=None,
                       obs_metadata=None,
                       camera=None,
                       epoch=2000.0,
                       allow_multiple_chips=False):
    """
    Return the names of detectors that see the object specified by
    (RA, Dec)  in radians.

    @param [in] ra in radians (a numpy array or a float).
    In the International Celestial Reference System.

    @param [in] dec in radians (a numpy array or a float).
    In the International Celestial Reference System.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).


    @param [in] obs_metadata is an ObservationMetaData characterizing the telescope pointing

    @param [in] epoch is the epoch in Julian years of the equinox against which RA and Dec are
    measured.  Default is 2000.

    @param [in] camera is an afw.cameraGeom camera instance characterizing the camera

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, an exception will be raised.  If it is 'True'
    and an object falls on more than one chip, it will still only return the first chip in the
    list of chips returned. THIS BEHAVIOR SHOULD BE FIXED IN A FUTURE TICKET.

    @param [out] the name(s) of the chips on which ra, dec fall (will be a numpy
    array if more than one)
    """

    are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'],
                                  "chipNameFromRaDec")

    if epoch is None:
        raise RuntimeError("You need to pass an epoch into chipName")

    if obs_metadata is None:
        raise RuntimeError(
            "You need to pass an ObservationMetaData into chipName")

    if obs_metadata.mjd is None:
        raise RuntimeError(
            "You need to pass an ObservationMetaData with an mjd into chipName"
        )

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError(
            "You need to pass an ObservationMetaData with a rotSkyPos into chipName"
        )

    if not are_arrays:
        ra = np.array([ra])
        dec = np.array([dec])

    xp, yp = _pupilCoordsFromRaDec(ra,
                                   dec,
                                   pm_ra=pm_ra,
                                   pm_dec=pm_dec,
                                   parallax=parallax,
                                   v_rad=v_rad,
                                   obs_metadata=obs_metadata,
                                   epoch=epoch)

    ans = chipNameFromPupilCoords(xp,
                                  yp,
                                  camera=camera,
                                  allow_multiple_chips=allow_multiple_chips)

    if not are_arrays:
        return ans[0]
    return ans
Example #28
0
def _observedFromAppGeo(ra,
                        dec,
                        includeRefraction=True,
                        altAzHr=False,
                        wavelength=0.5,
                        obs_metadata=None):
    """
    Convert apparent geocentric (RA, Dec) to observed (RA, Dec).  More specifically:
    apply refraction and diurnal aberration.

    This method works in radians.

    @param [in] ra is geocentric apparent RA (radians).  Can be a numpy array or a number.

    @param [in] dec is geocentric apparent Dec (radians).  Can be a numpy array or a number.

    @param [in] includeRefraction is a boolean to turn refraction on and off

    @param [in] altAzHr is a boolean indicating whether or not to return altitude
    and azimuth

    @param [in] wavelength is effective wavelength in microns (default: 0.5)

    @param [in] obs_metadata is an ObservationMetaData characterizing the
    observation.

    @param [out] a 2-D numpy array in which the first row is the observed RA
    and the second row is the observed Dec (both in radians)

    @param [out] a 2-D numpy array in which the first row is the altitude
    and the second row is the azimuth (both in radians).  Only returned
    if altAzHr == True.

    """

    are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'],
                                  "observedFromAppGeo")

    if obs_metadata is None:
        raise RuntimeError(
            "Cannot call observedFromAppGeo without an obs_metadata")

    if obs_metadata.site is None:
        raise RuntimeError(
            "Cannot call observedFromAppGeo: obs_metadata has no site info")

    if obs_metadata.mjd is None:
        raise RuntimeError(
            "Cannot call observedFromAppGeo: obs_metadata has no mjd")

    obsPrms = _calculateObservatoryParameters(obs_metadata, wavelength,
                                              includeRefraction)

    # palpy.aopqk does an apparent to observed place
    # correction
    #
    # it corrects for diurnal aberration and refraction
    # (using a fast algorithm for refraction in the case of
    # a small zenith distance and a more rigorous algorithm
    # for a large zenith distance)
    #

    if are_arrays:
        azimuth, zenith, hourAngle, decOut, raOut = palpy.aopqkVector(
            ra, dec, obsPrms)
    else:
        azimuth, zenith, hourAngle, decOut, raOut = palpy.aopqk(
            ra, dec, obsPrms)

    #
    # Note: this is a choke point.  Even the vectorized version of aopqk
    # is expensive (it takes about 0.006 seconds per call)
    #
    # Actually, this is only a choke point if you are dealing with zenith
    # distances of greater than about 70 degrees

    if altAzHr:
        #
        # palpy.de2h converts equatorial to horizon coordinates
        #
        if are_arrays:
            az, alt = palpy.de2hVector(hourAngle, decOut,
                                       obs_metadata.site.latitude_rad)
        else:
            az, alt = palpy.de2h(hourAngle, decOut,
                                 obs_metadata.site.latitude_rad)

        return np.array([raOut, decOut]), np.array([alt, az])
    return np.array([raOut, decOut])
Example #29
0
def chipNameFromPupilCoords(xPupil,
                            yPupil,
                            camera=None,
                            allow_multiple_chips=False):
    """
    Return the names of detectors that see the object specified by
    (xPupil, yPupil).

    @param [in] xPupil is the x pupil coordinate in radians.
    Can be either a float or a numpy array.

    @param [in] yPupil is the y pupil coordinate in radians.
    Can be either a float or a numpy array.

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, only the first chip will appear in the list of
    chipNames and warning will be emitted.  If it is 'True' and an object falls on more than one
    chip, the resulting chip name will be the string representation of the list of valid chip names.

    @param [in] camera is an afwCameraGeom object that specifies the attributes of the camera.

    @param [out] a numpy array of chip names

    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'],
                                  "chipNameFromPupilCoords")

    if camera is None:
        raise RuntimeError("No camera defined.  Cannot run chipName.")

    chipNames = []

    if are_arrays:
        cameraPointList = [
            afwGeom.Point2D(x, y) for x, y in zip(xPupil, yPupil)
        ]
    else:
        cameraPointList = [afwGeom.Point2D(xPupil, yPupil)]

    detList = camera.findDetectorsList(cameraPointList, PUPIL)

    for pt, det in zip(cameraPointList, detList):
        if len(det) == 0 or np.isnan(pt.getX()) or np.isnan(pt.getY()):
            chipNames.append(None)
        else:
            name_list = [dd.getName() for dd in det]
            if len(name_list) > 1:
                if allow_multiple_chips:
                    chipNames.append(str(name_list))
                else:
                    warnings.warn(
                        "An object has landed on multiple chips.  " +
                        "You asked for this not to happen.\n" +
                        "We will return only one of the chip names.  If you want both, "
                        + "try re-running with " +
                        "the kwarg allow_multiple_chips=True.\n" +
                        "Offending chip names were %s\n" % str(name_list) +
                        "Offending pupil coordinate point was %.12f %.12f\n" %
                        (pt[0], pt[1]),
                        category=MultipleChipWarning)

                    chipNames.append(name_list[0])

            elif len(name_list) == 0:
                chipNames.append(None)
            else:
                chipNames.append(name_list[0])

    if not are_arrays:
        return chipNames[0]

    return np.array(chipNames)
Example #30
0
def chipNameFromPupilCoords(xPupil, yPupil, camera=None, allow_multiple_chips=False):
    """
    Return the names of detectors that see the object specified by
    (xPupil, yPupil).

    @param [in] xPupil is the x pupil coordinate in radians.
    Can be either a float or a numpy array.

    @param [in] yPupil is the y pupil coordinate in radians.
    Can be either a float or a numpy array.

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, only the first chip will appear in the list of
    chipNames and warning will be emitted.  If it is 'True' and an object falls on more than one
    chip, the resulting chip name will be the string representation of the list of valid chip names.

    @param [in] camera is an afwCameraGeom object that specifies the attributes of the camera.

    @param [out] a numpy array of chip names

    """

    are_arrays = _validate_inputs([xPupil, yPupil], ['xPupil', 'yPupil'], "chipNameFromPupilCoords")

    if camera is None:
        raise RuntimeError("No camera defined.  Cannot run chipName.")

    chipNames = []

    if are_arrays:
        pupilPointList = [geom.Point2D(x, y) for x, y in zip(xPupil, yPupil)]
    else:
        pupilPointList = [geom.Point2D(xPupil, yPupil)]

    detList = camera.findDetectorsList(pupilPointList, FIELD_ANGLE)

    for pt, det in zip(pupilPointList, detList):
        if len(det) == 0 or np.isnan(pt.getX()) or np.isnan(pt.getY()):
            chipNames.append(None)
        else:
            name_list = [dd.getName() for dd in det]
            if len(name_list) > 1:
                if allow_multiple_chips:
                    chipNames.append(str(name_list))
                else:
                    warnings.warn("An object has landed on multiple chips.  " +
                                  "You asked for this not to happen.\n" +
                                  "We will return only one of the chip names.  If you want both, " +
                                  "try re-running with " +
                                  "the kwarg allow_multiple_chips=True.\n" +
                                  "Offending chip names were %s\n" % str(name_list) +
                                  "Offending pupil coordinate point was %.12f %.12f\n" % (pt[0], pt[1]),
                                  category=MultipleChipWarning)

                    chipNames.append(name_list[0])

            elif len(name_list) == 0:
                chipNames.append(None)
            else:
                chipNames.append(name_list[0])

    if not are_arrays:
        return chipNames[0]

    return np.array(chipNames)
Example #31
0
def chipNameFromPupilCoordsLSST(xPupil_in,
                                yPupil_in,
                                allow_multiple_chips=False,
                                band='r'):
    """
    Return the names of LSST detectors that see the object specified by
    either (xPupil, yPupil).

    @param [in] xPupil_in is the x pupil coordinate in radians.
    Must be a numpy array.

    @param [in] yPupil_in is the y pupil coordinate in radians.
    Must be a numpy array.

    @param [in] allow_multiple_chips is a boolean (default False) indicating whether or not
    this method will allow objects to be visible on more than one chip.  If it is 'False'
    and an object appears on more than one chip, only the first chip will appear in the list of
    chipNames and warning will be emitted.  If it is 'True' and an object falls on more than one
    chip, a list of chipNames will appear for that object.

    @param[in] band is the bandpass being simulated (default='r')

    @param [out] a numpy array of chip names

    """
    if (not hasattr(chipNameFromPupilCoordsLSST, '_focal_map')
            or not hasattr(chipNameFromPupilCoordsLSST, '_detector_arr')
            or len(chipNameFromPupilCoordsLSST._detector_arr) == 0):
        focal_map = _build_lsst_focal_coord_map()
        chipNameFromPupilCoordsLSST._focal_map = focal_map
        camera = lsst_camera()
        detector_arr = np.zeros(len(focal_map['name']), dtype=object)
        for ii in range(len(focal_map['name'])):
            detector_arr[ii] = camera[focal_map['name'][ii]]

        chipNameFromPupilCoordsLSST._detector_arr = detector_arr

        # build a Box2D that contains all of the detectors in the camera
        focal_to_field = camera.getTransformMap().getTransform(
            FOCAL_PLANE, FIELD_ANGLE)
        focal_bbox = camera.getFpBBox()
        focal_corners = focal_bbox.getCorners()
        camera_bbox = geom.Box2D()
        x_focal_max = None
        x_focal_min = None
        y_focal_max = None
        y_focal_min = None
        for cc in focal_corners:
            xx = cc.getX()
            yy = cc.getY()
            if x_focal_max is None or xx > x_focal_max:
                x_focal_max = xx
            if x_focal_min is None or xx < x_focal_min:
                x_focal_min = xx
            if y_focal_max is None or yy > y_focal_max:
                y_focal_max = yy
            if y_focal_min is None or yy < y_focal_min:
                y_focal_min = yy

        chipNameFromPupilCoordsLSST._x_focal_center = 0.5 * (x_focal_max +
                                                             x_focal_min)
        chipNameFromPupilCoordsLSST._y_focal_center = 0.5 * (y_focal_max +
                                                             y_focal_min)

        radius_sq_max = None
        for cc in focal_corners:
            xx = cc.getX()
            yy = cc.getY()
            radius_sq = (
                (xx - chipNameFromPupilCoordsLSST._x_focal_center)**2 +
                (yy - chipNameFromPupilCoordsLSST._y_focal_center)**2)
            if radius_sq_max is None or radius_sq > radius_sq_max:
                radius_sq_max = radius_sq

        chipNameFromPupilCoordsLSST._camera_focal_radius_sq = radius_sq_max * 1.1

    are_arrays = _validate_inputs([xPupil_in, yPupil_in],
                                  ['xPupil_in', 'yPupil_in'],
                                  "chipNameFromPupilCoordsLSST")

    if not are_arrays:
        xPupil_in = np.array([xPupil_in])
        yPupil_in = np.array([yPupil_in])

    xFocal, yFocal = focalPlaneCoordsFromPupilCoordsLSST(xPupil_in,
                                                         yPupil_in,
                                                         band=band)

    radius_sq_list = (
        (xFocal - chipNameFromPupilCoordsLSST._x_focal_center)**2 +
        (yFocal - chipNameFromPupilCoordsLSST._y_focal_center)**2)

    with np.errstate(invalid='ignore'):
        good_radii = np.where(radius_sq_list < chipNameFromPupilCoordsLSST.
                              _camera_focal_radius_sq)

    if len(good_radii[0]) == 0:
        return np.array([None] * len(xPupil_in))

    xFocal_good = xFocal[good_radii]
    yFocal_good = yFocal[good_radii]

    ############################################################
    # in the code below, we will only consider those points which
    # passed the 'good_radii' test above; the other points will
    # be added in with chipName == None at the end
    #
    focalPointList = [
        geom.Point2D(xFocal[i_pt], yFocal[i_pt]) for i_pt in good_radii[0]
    ]

    # Loop through every detector on the camera.  For each detector, assemble a list of points
    # whose centers are within 1.1 detector radii of the center of the detector.

    x_cam_list = chipNameFromPupilCoordsLSST._focal_map['xx']
    y_cam_list = chipNameFromPupilCoordsLSST._focal_map['yy']
    rrsq_lim_list = (1.1 * chipNameFromPupilCoordsLSST._focal_map['dp'])**2

    possible_points = []
    for i_chip, (x_cam, y_cam, rrsq_lim) in \
    enumerate(zip(x_cam_list, y_cam_list, rrsq_lim_list)):

        local_possible_pts = np.where(((xFocal_good - x_cam)**2 +
                                       (yFocal_good - y_cam)**2) < rrsq_lim)[0]

        possible_points.append(local_possible_pts)

    nameList_good = _findDetectorsListLSST(
        focalPointList,
        chipNameFromPupilCoordsLSST._detector_arr,
        possible_points,
        allow_multiple_chips=allow_multiple_chips)

    ####################################################################
    # initialize output as an array of Nones, effectively adding back in
    # the points which failed the initial radius cut
    nameList = np.array([None] * len(xPupil_in))

    nameList[good_radii] = nameList_good

    if not are_arrays:
        return nameList[0]

    return nameList
Example #32
0
def _icrsFromObserved(ra,
                      dec,
                      obs_metadata=None,
                      epoch=None,
                      includeRefraction=True):
    """
    Convert observed RA, Dec into mean International Celestial Reference Frame (ICRS)
    RA, Dec.  This method undoes the effects of precession, nutation, aberration (annual
    and diurnal), and refraction.  It is meant so that users can take pointing RA and Decs,
    which will be in the observed reference system, and transform them into ICRS for
    purposes of querying database tables (likely to contain mean ICRS RA, Dec) for objects
    visible from a given pointing.

    Note: This method is only accurate at angular distances from the sun of greater
    than 45 degrees and zenith distances of less than 75 degrees.

    WARNING: This method does not account for apparent motion due to parallax.
    This means it should not be used to invert the ICRS-to-observed coordinates
    transformation for actual celestial objects.  This method is only useful
    for mapping positions on a theoretical celestial sphere.

    This method works in radians.

    @param [in] ra is the observed RA in radians.  Can be a numpy array or a number.

    @param [in] dec is the observed Dec in radians.  Can be a numpy array or a number.

    @param [in] obs_metadata is an ObservationMetaData object describing the
    telescope pointing.

    @param [in] epoch is the julian epoch (in years) against which the mean
    equinoxes are measured.

    @param [in] includeRefraction toggles whether or not to correct for refraction

    @param [out] a 2-D numpy array in which the first row is the mean ICRS
    RA and the second row is the mean ICRS Dec (both in radians)
    """

    _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromObserved")

    if obs_metadata is None:
        raise RuntimeError(
            "Cannot call icrsFromObserved; obs_metadata is None")

    if obs_metadata.mjd is None:
        raise RuntimeError(
            "Cannot call icrsFromObserved; obs_metadata.mjd is None")

    if epoch is None:
        raise RuntimeError(
            "Cannot call icrsFromObserved; you have not specified an epoch")

    ra_app, dec_app = _appGeoFromObserved(ra,
                                          dec,
                                          obs_metadata=obs_metadata,
                                          includeRefraction=includeRefraction)

    ra_icrs, dec_icrs = _icrsFromAppGeo(ra_app,
                                        dec_app,
                                        epoch=epoch,
                                        mjd=obs_metadata.mjd)

    return np.array([ra_icrs, dec_icrs])
Example #33
0
def _pupilCoordsFromObserved(ra_obs,
                             dec_obs,
                             obs_metadata,
                             epoch=2000.0,
                             includeRefraction=True):
    """
    Convert Observed RA, Dec into pupil coordinates

    Parameters
    ----------
    ra_obs is the observed RA in radians

    dec_obs is the observed Dec in radians

    obs_metadata is an ObservationMetaData characterizing the telescope location and pointing

    epoch is the epoch of the mean RA and Dec in julian years (default=2000.0)

    includeRefraction is a boolean controlling the application of refraction.

    Returns
    --------
    A numpy array whose first row is the x coordinate on the pupil in
    radians and whose second row is the y coordinate in radians
    """

    are_arrays = _validate_inputs([ra_obs, dec_obs], ['ra_obs', 'dec_obs'],
                                  "pupilCoordsFromObserved")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("Cannot call pupilCoordsFromObserved; "
                           "rotSkyPos is None")

    theta = obs_metadata._rotSkyPos

    ra_pointing, dec_pointing = _observedFromICRS(
        obs_metadata._pointingRA,
        obs_metadata._pointingDec,
        obs_metadata=obs_metadata,
        epoch=epoch,
        includeRefraction=includeRefraction)

    # palpy.ds2tp performs the gnomonic projection on ra_in and dec_in
    # with a tangent point at (pointingRA, pointingDec)
    #
    if not are_arrays:
        try:
            x, y = palpy.ds2tp(ra_obs, dec_obs, ra_pointing, dec_pointing)
        except:
            x = np.NaN
            y = np.NaN
    else:
        try:
            x, y = palpy.ds2tpVector(ra_obs, dec_obs, ra_pointing,
                                     dec_pointing)
        except:
            # apparently, one of your ra/dec values was improper; we will have to do this
            # element-wise, putting NaN in the place of the bad values
            x = []
            y = []
            for rr, dd in zip(ra_obs, dec_obs):
                try:
                    xx, yy = palpy.ds2tp(rr, dd, ra_pointing, dec_pointing)
                except:
                    xx = np.NaN
                    yy = np.NaN
                x.append(xx)
                y.append(yy)
            x = np.array(x)
            y = np.array(y)

    # The extra negative sign on x_out comes from the following:
    # The Gnomonic projection as calculated by palpy is such that,
    # if north is in the +y direction, then west is in the -x direction,
    # which is the opposite of the behavior we want (I do not know how to
    # express this analytically; I have just confirmed it numerically)
    x *= -1.0

    # rotate the result by rotskypos (rotskypos being "the angle of the sky relative to
    # camera coordinates" according to phoSim documentation) to account for
    # the rotation of the focal plane about the telescope pointing

    x_out = x * np.cos(theta) - y * np.sin(theta)
    y_out = x * np.sin(theta) + y * np.cos(theta)

    return np.array([x_out, y_out])
Example #34
0
def _appGeoFromICRS(ra,
                    dec,
                    pm_ra=None,
                    pm_dec=None,
                    parallax=None,
                    v_rad=None,
                    epoch=2000.0,
                    mjd=None):
    """
    Convert the mean position (RA, Dec) in the International Celestial Reference
    System (ICRS) to the mean apparent geocentric position

    units:  ra (radians), dec (radians), pm_ra (radians/year), pm_dec
    (radians/year), parallax (radians), v_rad (km/sec; positive if receding),
    epoch (Julian years)

    @param [in] ra in radians (ICRS).  Can be a numpy array or a number.

    @param [in] dec in radians (ICRS).  Can be a numpy array or a number.

    @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in radians/year.
    Can be a numpy array or a number or None.

    @param [in] pm_dec is dec proper motion in radians/year.
    Can be a numpy array or a number or None.

    @param [in] parallax in radians.  Can be a numpy array or a number or None.

    @param [in] v_rad is radial velocity in km/sec (positive if the object is receding).
    Can be a numpy array or a number or None.

    @param [in] epoch is the julian epoch (in years) of the equinox against which to
    measure RA (default: 2000.0)

    @param [in] mjd is an instantiation of the ModifiedJulianDate class
    representing the date of the observation

    @param [out] a 2-D numpy array in which the first row is the apparent
    geocentric RAand the second row is the apparent geocentric Dec (both in radians)
    """

    if mjd is None:
        raise RuntimeError("cannot call appGeoFromICRS; mjd is None")

    include_px = False

    if (pm_ra is not None or pm_dec is not None or v_rad is not None
            or parallax is not None):

        include_px = True

        if isinstance(ra, np.ndarray):
            fill_value = np.zeros(len(ra), dtype=float)
        else:
            fill_value = 0.0

        if pm_ra is None:
            pm_ra = fill_value

        if pm_dec is None:
            pm_dec = fill_value

        if v_rad is None:
            v_rad = fill_value

        if parallax is None:
            parallax = fill_value

        are_arrays = _validate_inputs(
            [ra, dec, pm_ra, pm_dec, v_rad, parallax],
            ['ra', 'dec', 'pm_ra', 'pm_dec', 'v_rad', 'parallax'],
            "appGeoFromICRS")
    else:
        are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'],
                                      "appGeoFromICRS")

    # Define star independent mean to apparent place parameters
    # palpy.mappa calculates the star-independent parameters
    # needed to correct RA and Dec
    # e.g the Earth barycentric and heliocentric position and velocity,
    # the precession-nutation matrix, etc.
    #
    # arguments of palpy.mappa are:
    # epoch of mean equinox to be used (Julian)
    #
    # date (MJD)
    prms = palpy.mappa(epoch, mjd.TDB)

    # palpy.mapqk does a quick mean to apparent place calculation using
    # the output of palpy.mappa
    #
    # Taken from the palpy source code (palMap.c which calls both palMappa and palMapqk):
    # The accuracy is sub-milliarcsecond, limited by the
    # precession-nutation model (see palPrenut for details).

    if include_px:
        # because PAL and ERFA expect proper motion in terms of "coordinate
        # angle; not true angle" (as stated in erfa/starpm.c documentation)
        pm_ra_corrected = pm_ra / np.cos(dec)

    if are_arrays:
        if include_px:
            raOut, decOut = palpy.mapqkVector(ra, dec, pm_ra_corrected, pm_dec,
                                              arcsecFromRadians(parallax),
                                              v_rad, prms)
        else:
            raOut, decOut = palpy.mapqkzVector(ra, dec, prms)
    else:
        if include_px:
            raOut, decOut = palpy.mapqk(ra, dec, pm_ra_corrected, pm_dec,
                                        arcsecFromRadians(parallax), v_rad,
                                        prms)
        else:
            raOut, decOut = palpy.mapqkz(ra, dec, prms)

    return np.array([raOut, decOut])
Example #35
0
def _pupilCoordsFromRaDec(ra_in,
                          dec_in,
                          pm_ra=None,
                          pm_dec=None,
                          parallax=None,
                          v_rad=None,
                          includeRefraction=True,
                          obs_metadata=None,
                          epoch=2000.0):
    """
    Take an input RA and dec from the sky and convert it to coordinates
    on the focal plane.

    This uses PAL's gnomonic projection routine which assumes that the focal
    plane is perfectly flat.  The output is in Cartesian coordinates, assuming
    that the Celestial Sphere is a unit sphere.

    The RA, Dec accepted by this method are in the International Celestial
    Reference System.  Before applying the gnomonic projection, this method
    transforms those RA, Dec into observed geocentric coordinates, applying
    the effects of precession, nutation, aberration, parallax and refraction.
    This is done, because the gnomonic projection ought to be applied to what
    observers actually see, rather than the idealized, above-the-atmosphere
    coordinates represented by the ICRS.

    @param [in] ra_in is in radians (ICRS).  Can be either a numpy array or a number.

    @param [in] dec_in is in radians (ICRS).  Can be either a numpy array or a number.

    @param [in] pm_ra is proper motion in RA multiplied by cos(Dec) (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] pm_dec is proper motion in dec (radians/yr)
    Can be a numpy array or a number or None (default=None).

    @param [in] parallax is parallax in radians
    Can be a numpy array or a number or None (default=None).

    @param [in] v_rad is radial velocity (km/s)
    Can be a numpy array or a number or None (default=None).

    @param [in] includeRefraction is a boolean controlling the application of refraction.

    @param [in] obs_metadata is an ObservationMetaData instantiation characterizing the
    telescope location and pointing.

    @param [in] epoch is the epoch of mean ra and dec in julian years (default=2000.0)

    @param [out] returns a numpy array whose first row is the x coordinate on the pupil in
    radians and whose second row is the y coordinate in radians
    """

    are_arrays = _validate_inputs([ra_in, dec_in], ['ra_in', 'dec_in'],
                                  "pupilCoordsFromRaDec")

    if obs_metadata is None:
        raise RuntimeError(
            "Cannot call pupilCoordsFromRaDec without obs_metadata")

    if obs_metadata.mjd is None:
        raise RuntimeError(
            "Cannot call pupilCoordsFromRaDec; obs_metadata.mjd is None")

    if epoch is None:
        raise RuntimeError("Cannot call pupilCoordsFromRaDec; epoch is None")

    if obs_metadata.rotSkyPos is None:
        # there is no observation meta data on which to base astrometry
        raise RuntimeError(
            "Cannot calculate [x,y]_focal_nominal without obs_metadata.rotSkyPos"
        )

    if obs_metadata.pointingRA is None or obs_metadata.pointingDec is None:
        raise RuntimeError(
            "Cannot calculate [x,y]_focal_nominal without pointingRA and Dec in obs_metadata"
        )

    ra_obs, dec_obs = _observedFromICRS(ra_in,
                                        dec_in,
                                        pm_ra=pm_ra,
                                        pm_dec=pm_dec,
                                        parallax=parallax,
                                        v_rad=v_rad,
                                        obs_metadata=obs_metadata,
                                        epoch=epoch,
                                        includeRefraction=includeRefraction)

    return _pupilCoordsFromObserved(ra_obs,
                                    dec_obs,
                                    obs_metadata,
                                    epoch=epoch,
                                    includeRefraction=includeRefraction)
Example #36
0
def _pupilCoordsFromObserved(ra_obs, dec_obs, obs_metadata, epoch=2000.0, includeRefraction=True):
    """
    Convert Observed RA, Dec into pupil coordinates

    Parameters
    ----------
    ra_obs is the observed RA in radians

    dec_obs is the observed Dec in radians

    obs_metadata is an ObservationMetaData characterizing the telescope location and pointing

    epoch is the epoch of the mean RA and Dec in julian years (default=2000.0)

    includeRefraction is a boolean controlling the application of refraction.

    Returns
    --------
    A numpy array whose first row is the x coordinate on the pupil in
    radians and whose second row is the y coordinate in radians
    """

    are_arrays = _validate_inputs([ra_obs, dec_obs], ['ra_obs', 'dec_obs'],
                                  "pupilCoordsFromObserved")

    if obs_metadata.rotSkyPos is None:
        raise RuntimeError("Cannot call pupilCoordsFromObserved; "
                           "rotSkyPos is None")

    theta = -1.0*obs_metadata._rotSkyPos

    ra_pointing, dec_pointing = _observedFromICRS(obs_metadata._pointingRA,
                                                  obs_metadata._pointingDec,
                                                  obs_metadata=obs_metadata,
                                                  epoch=epoch,
                                                  includeRefraction=includeRefraction)

    # palpy.ds2tp performs the gnomonic projection on ra_in and dec_in
    # with a tangent point at (pointingRA, pointingDec)
    #
    if not are_arrays:
        try:
            x, y = palpy.ds2tp(ra_obs, dec_obs, ra_pointing, dec_pointing)
        except:
            x = np.NaN
            y = np.NaN
    else:
        try:
            x, y = palpy.ds2tpVector(ra_obs, dec_obs, ra_pointing, dec_pointing)
        except:
            # apparently, one of your ra/dec values was improper; we will have to do this
            # element-wise, putting NaN in the place of the bad values
            x = []
            y = []
            for rr, dd in zip(ra_obs, dec_obs):
                try:
                    xx, yy = palpy.ds2tp(rr, dd, ra_pointing, dec_pointing)
                except:
                    xx = np.NaN
                    yy = np.NaN
                x.append(xx)
                y.append(yy)
            x = np.array(x)
            y = np.array(y)

    # rotate the result by rotskypos (rotskypos being "the angle of the sky relative to
    # camera coordinates" according to phoSim documentation) to account for
    # the rotation of the focal plane about the telescope pointing

    x_out = x*np.cos(theta) - y*np.sin(theta)
    y_out = x*np.sin(theta) + y*np.cos(theta)

    return np.array([x_out, y_out])