Source code for rubin_scheduler.utils.camera_footprint
__all__ = ("LsstCameraFootprint",)
import os
import numpy as np
from rubin_scheduler.data import get_data_dir
from .projections import gnomonic_project_toxy
def rotate(x, y, rotation_angle_rad):
"""rotate 2d points around the origin (0,0)"""
cos_rad = np.cos(rotation_angle_rad)
sin_rad = np.sin(rotation_angle_rad)
qx = cos_rad * x - sin_rad * y
qy = sin_rad * x + cos_rad * y
return qx, qy
[docs]
class LsstCameraFootprint:
"""
Identify point on the sky within an LSST camera footprint.
Parameters
----------
units : `str`, opt
Units for the object RA/Dec and boresight RA/Dec/rotation values.
Default 'degrees'. If not degrees, assumes incoming values
are in radians.
footprint_file : `str` or None, opt
Location for the camera footprint map.
Default None loads the default map from
$RUBIN_SIM_DATA_DIR/maf/fov_map.npz
"""
def __init__(self, units="degrees", footprint_file=None):
if footprint_file is None:
footprint_file = os.path.join(get_data_dir(), "utils", "fov_map.npz")
_temp = np.load(footprint_file)
# Units refers to the incoming ra/dec values in the _call__ method
# Internally, radians are used to calculate the footprint
self.units = units
self.camera_fov = _temp["image"].copy()
self.x_camera = np.radians(_temp["x"].copy())
_temp.close()
self.plate_scale = self.x_camera[1] - self.x_camera[0]
self.indx_max = len(self.x_camera)
[docs]
def __call__(self, obj_ra, obj_dec, boresight_ra, boresight_dec, boresight_rot_sky_pos):
"""Determine which observations are within the actual camera
footprint for a series of observations.
Parameters
----------
obj_ra : `np.ndarray`
RA values for the objects (in the field of view?)
obj_dec : `np.ndarray`
Dec values for the objects
boresight_ra : `float`
RA value for the pointing
boresight_dec : `float`
Dec value for the pointing
boresight_rot_sky_pos : `float`
RotSkyPos value for the pointing.
Returns
-------
indices : `np.ndarray`
Returns the indexes of the numpy array of the object
observations which are inside the fov and land on a
science chip. Applying this to the input array
(e.g. obj_ra[indices]) indicates the positions of
the objects which fell onto active silicon.
"""
if self.units == "degrees":
x_proj, y_proj = gnomonic_project_toxy(
np.radians(obj_ra),
np.radians(obj_dec),
np.radians(boresight_ra),
np.radians(boresight_dec),
)
# rotate them by rotskypos
# TODO: look up whether this is a positive or
# negative rotation in the observatory documentation
x_proj, y_proj = rotate(x_proj, y_proj, np.radians(boresight_rot_sky_pos))
else:
x_proj, y_proj = gnomonic_project_toxy(obj_ra, obj_dec, boresight_ra, boresight_dec)
x_proj, y_proj = rotate(x_proj, y_proj, boresight_rot_sky_pos)
# look up which points are good
x_indx = np.round((x_proj - self.x_camera[0]) / self.plate_scale).astype(int)
y_indx = np.round((y_proj - self.x_camera[0]) / self.plate_scale).astype(int)
in_range = np.where(
(x_indx >= 0) & (x_indx < self.indx_max) & (y_indx >= 0) & (y_indx < self.indx_max)
)[0]
x_indx = x_indx[in_range]
y_indx = y_indx[in_range]
indices = in_range
# reduce the indices down to only the ones that fall on silicon.
# self.camera_fov is an array of `bool` values
map_val = self.camera_fov[x_indx, y_indx]
indices = indices[map_val]
return indices