Source code for scopesim.effects.obs_strategies

"""
Effects describing observing strategies.

- ChopNodCombiner: simulate chop-nod cycle
"""
import numpy as np

from scopesim.base_classes import DetectorBase
from scopesim.effects import Effect
from scopesim.utils import from_currsys, check_keys


[docs]class ChopNodCombiner(Effect): """ Creates and combines 4 images for each of the chop/nod positions. - AA : original position ``(dx, dy) = (0, 0)`` - AB : chop position ``(dx, dy) = chop_offsets`` - BA : nod position ``(dx, dy) = nod_offsets`` - BB : chop-nod position ``(dx, dy) = nod_offsets + chop_offsets`` Images are combined using:: im_combined = (AA - AB) - (BA - BB) If no ``nod_offset`` is given, it is set to the inverse of ``chop_offset``. ``ChopNodCombiner`` is a detector effect and should be placed last in the detector yaml (after the noise effects). Keyword Arguments ----------------- chop_offsets : tuple, optional [arcsec] (dx, dy) offset of chop position relative to AA nod_offsets : tuple, optional [arcsec] (dx, dy) offset of nod position relative to AA Example yaml entry ------------------ :: name: perpendicular_chop_nod_slanted_pattern description: chop throw to (+5, 0) and nod throw to (-7, +10) arcsec class: ChopNodCombiner include: True kwargs: pixel_scale : "!INST.pixel_scale" chop_offsets : (5, 0) nod_offsets : (-7, 10) """ def __init__(self, **kwargs): check_keys(kwargs, ["chop_offsets", "pixel_scale"]) super().__init__(**kwargs) params = { "chop_offsets": None, "nod_offsets": None, "pixel_scale": None, "include": True, "z_order": [863], } self.meta.update(params) self.meta.update(kwargs)
[docs] def apply_to(self, obj, **kwargs): if isinstance(obj, DetectorBase): chop_offsets = from_currsys(self.meta["chop_offsets"]) nod_offsets = from_currsys(self.meta["nod_offsets"]) if nod_offsets is None: nod_offsets = -np.array(chop_offsets) # these offsets are in pixels, not in arcsec or mm pixel_scale = float(from_currsys(self.meta["pixel_scale"])) chop_offsets_pixel = np.array(chop_offsets) / pixel_scale nod_offsets_pixel = np.array(nod_offsets) / pixel_scale image = obj.hdu.data obj.hdu.data = chop_nod_image(image, chop_offsets_pixel.astype(int), nod_offsets_pixel.astype(int)) return obj
[docs]def chop_nod_image(img, chop_offsets, nod_offsets=None): """Create four copies and combine in chop-nod pattern.""" if nod_offsets is None: nod_offsets = tuple(-np.array(chop_offsets)) im_aa = np.copy(img) im_ab = np.roll(im_aa, chop_offsets, (1, 0)) im_ba = np.roll(im_aa, nod_offsets, (1, 0)) im_bb = np.roll(im_ba, chop_offsets, (1, 0)) im_comb = (im_aa - im_ab) - (im_ba - im_bb) return im_comb