Example #1
0
def test_pedestal_calculator():
    """ test of PedestalIntegrator """
    from lstchain.calib.camera.pedestals import PedestalIntegrator

    tel_id = 0
    n_events = 10
    n_gain = 2
    n_pixels = 1855
    ped_level = 300

    subarray = SubarrayDescription(
        "test array",
        tel_positions={0: np.zeros(3) * u.m},
        tel_descriptions={
            0: TelescopeDescription.from_name(
                optics_name="SST-ASTRI", camera_name="CHEC"
            ),
        },
    )
    subarray.tel[0].camera.readout.reference_pulse_shape = np.ones((1, 2))
    subarray.tel[0].camera.readout.reference_pulse_sample_width = u.Quantity(1, u.ns)

    config = Config({
        "FixedWindowSum": {
          "apply_integration_correction": False,
        }
    })
    ped_calculator = PedestalIntegrator(charge_product="FixedWindowSum",
                                        config=config,
                                        sample_size=n_events,
                                        tel_id=tel_id,
                                        subarray=subarray)
    # create one event
    data = ArrayEventContainer()
    data.meta['origin'] = 'test'

    # fill the values necessary for the pedestal calculation
    data.mon.tel[tel_id].pixel_status.hardware_failing_pixels = np.zeros(
        (n_gain, n_pixels), dtype=bool
    )
    data.r1.tel[tel_id].waveform = np.full((2, n_pixels, 40), ped_level)
    data.trigger.time = Time(0, format='mjd', scale='tai')
    while ped_calculator.num_events_seen < n_events:
        if ped_calculator.calculate_pedestals(data):
            assert data.mon.tel[tel_id].pedestal
            assert np.mean(data.mon.tel[tel_id].pedestal.charge_median) == (
                ped_calculator.extractor.window_width.tel[tel_id] * ped_level
            )
            assert np.mean(data.mon.tel[tel_id].pedestal.charge_std) == 0
Example #2
0
def test_pedestal_calculator():
    """ test of PedestalIntegrator """

    tel_id = 0
    n_events = 10
    n_gain = 2
    n_pixels = 1855
    ped_level = 300

    subarray = SubarrayDescription(
        "test array",
        tel_positions={0: np.zeros(3) * u.m},
        tel_descriptions={
            0:
            TelescopeDescription.from_name(optics_name="SST-ASTRI",
                                           camera_name="CHEC")
        },
    )
    subarray.tel[0].camera.readout.reference_pulse_shape = np.ones((1, 2))
    subarray.tel[0].camera.readout.reference_pulse_sample_width = u.Quantity(
        1, u.ns)

    ped_calculator = PedestalIntegrator(
        subarray=subarray,
        charge_product="FixedWindowSum",
        sample_size=n_events,
        tel_id=tel_id,
    )
    # create one event
    data = ArrayEventContainer()
    data.meta["origin"] = "test"
    data.trigger.time = Time.now()

    # fill the values necessary for the pedestal calculation
    data.mon.tel[tel_id].pixel_status.hardware_failing_pixels = np.zeros(
        (n_gain, n_pixels), dtype=bool)
    data.r1.tel[tel_id].waveform = np.full((2, n_pixels, 40), ped_level)

    while ped_calculator.num_events_seen < n_events:
        if ped_calculator.calculate_pedestals(data):
            assert data.mon.tel[tel_id].pedestal
            assert np.mean(data.mon.tel[tel_id].pedestal.charge_median) == (
                ped_calculator.extractor.window_width.tel[0] * ped_level)
            assert np.mean(data.mon.tel[tel_id].pedestal.charge_std) == 0
def test_event_filter_config():
    from ctapipe.utils import EventTypeFilter

    config = Config({"EventTypeFilter": {"allowed_types": [EventType.SUBARRAY.value]}})
    event_filter = EventTypeFilter(config=config)

    e = ArrayEventContainer()
    e.trigger.event_type = EventType.DARK_PEDESTAL
    assert not event_filter(e)

    e.trigger.event_type = EventType.SUBARRAY
    assert event_filter(e)
Example #4
0
def test_rescale_dl1_charge():
    event = ArrayEventContainer()
    tel_ids = [1, 3]
    images = {}
    for tel_id in tel_ids:
        images[tel_id] = np.random.rand(1855)
        event.dl1.tel[tel_id].image = copy(images[tel_id])

    rescaling_factor = np.random.rand() * 10
    rescale_dl1_charge(event, rescaling_factor)

    for tel_id in tel_ids:
        np.testing.assert_allclose(event.dl1.tel[tel_id].image,
                                   images[tel_id] * rescaling_factor)
def test_event_filter():
    from ctapipe.utils import EventTypeFilter

    event_filter = EventTypeFilter(
        allowed_types={EventType.SUBARRAY, EventType.FLATFIELD}
    )

    e = ArrayEventContainer()
    e.trigger.event_type = EventType.SUBARRAY
    assert event_filter(e)
    e.trigger.event_type = EventType.FLATFIELD
    assert event_filter(e)
    e.trigger.event_type = EventType.DARK_PEDESTAL
    assert not event_filter(e)
Example #6
0
def test_check_dl0_empty(example_event, example_subarray):
    calibrator = CameraCalibrator(subarray=example_subarray)
    telid = list(example_event.r0.tel)[0]
    calibrator._calibrate_dl0(example_event, telid)
    waveform = example_event.dl0.tel[telid].waveform.copy()
    with pytest.warns(UserWarning):
        example_event.dl0.tel[telid].waveform = None
        calibrator._calibrate_dl1(example_event, telid)
        assert example_event.dl1.tel[telid].image is None

    assert calibrator._check_dl0_empty(None) is True
    assert calibrator._check_dl0_empty(waveform) is False

    calibrator = CameraCalibrator(subarray=example_subarray)
    event = ArrayEventContainer()
    event.dl1.tel[telid].image = np.full(2048, 2)
    with pytest.warns(UserWarning):
        calibrator(event)
    assert (event.dl1.tel[telid].image == 2).all()
Example #7
0
def test_check_r1_empty(example_event, example_subarray):
    calibrator = CameraCalibrator(subarray=example_subarray)
    telid = list(example_event.r0.tel)[0]
    waveform = example_event.r1.tel[telid].waveform.copy()
    with pytest.warns(UserWarning):
        example_event.r1.tel[telid].waveform = None
        calibrator._calibrate_dl0(example_event, telid)
        assert example_event.dl0.tel[telid].waveform is None

    assert calibrator._check_r1_empty(None) is True
    assert calibrator._check_r1_empty(waveform) is False

    calibrator = CameraCalibrator(
        subarray=example_subarray,
        image_extractor=FullWaveformSum(subarray=example_subarray),
    )
    event = ArrayEventContainer()
    event.dl0.tel[telid].waveform = np.full((2048, 128), 2)
    with pytest.warns(UserWarning):
        calibrator(event)
    assert (event.dl0.tel[telid].waveform == 2).all()
    assert (event.dl1.tel[telid].image == 2 * 128).all()
Example #8
0
def test_flasherflatfieldcalculator():
    """test of flasherFlatFieldCalculator"""
    tel_id = 0
    n_gain = 2
    n_events = 10
    n_pixels = 1855
    ff_level = 10000

    subarray = SubarrayDescription(
        "test array",
        tel_positions={0: np.zeros(3) * u.m},
        tel_descriptions={
            0:
            TelescopeDescription.from_name(optics_name="SST-ASTRI",
                                           camera_name="CHEC"),
        },
    )
    subarray.tel[0].camera.readout.reference_pulse_shape = np.ones((1, 2))
    subarray.tel[0].camera.readout.reference_pulse_sample_width = u.Quantity(
        1, u.ns)

    config = Config({
        "FixedWindowSum": {
            "window_shift": 5,
            "window_width": 10,
            "peak_index": 20,
            "apply_integration_correction": False,
        }
    })
    ff_calculator = FlasherFlatFieldCalculator(subarray=subarray,
                                               charge_product="FixedWindowSum",
                                               sample_size=n_events,
                                               tel_id=tel_id,
                                               config=config)
    # create one event
    data = ArrayEventContainer()
    data.meta['origin'] = 'test'
    data.trigger.time = Time(0, format='mjd', scale='tai')
    # initialize mon and r1 data
    data.mon.tel[tel_id].pixel_status.hardware_failing_pixels = np.zeros(
        (n_gain, n_pixels), dtype=bool)
    data.mon.tel[tel_id].pixel_status.pedestal_failing_pixels = np.zeros(
        (n_gain, n_pixels), dtype=bool)
    data.mon.tel[tel_id].pixel_status.flatfield_failing_pixels = np.zeros(
        (n_gain, n_pixels), dtype=bool)
    data.r1.tel[tel_id].waveform = np.zeros((n_gain, n_pixels, 40),
                                            dtype=np.float32)
    # data.r1.tel[tel_id].trigger_time = 1000

    # flat-field signal put == delta function of height ff_level at sample 20
    data.r1.tel[tel_id].waveform[:, :, 20] = ff_level
    print(data.r1.tel[tel_id].waveform[0, 0, 20])

    # First test: good event
    while ff_calculator.num_events_seen < n_events:
        if ff_calculator.calculate_relative_gain(data):
            assert data.mon.tel[tel_id].flatfield

            print(data.mon.tel[tel_id].flatfield)
            assert np.mean(
                data.mon.tel[tel_id].flatfield.charge_median) == ff_level
            assert np.mean(
                data.mon.tel[tel_id].flatfield.relative_gain_median) == 1
            assert np.mean(
                data.mon.tel[tel_id].flatfield.relative_gain_std) == 0

    # Second test: introduce some failing pixels
    failing_pixels_id = np.array([10, 20, 30, 40])
    data.r1.tel[tel_id].waveform[:, failing_pixels_id, :] = 0
    data.mon.tel[
        tel_id].pixel_status.pedestal_failing_pixels[:,
                                                     failing_pixels_id] = True

    while ff_calculator.num_events_seen < n_events:
        if ff_calculator.calculate_relative_gain(data):

            # working pixel have good gain
            assert (
                data.mon.tel[tel_id].flatfield.relative_gain_median[0, 0] == 1)

            # bad pixels do non influence the gain
            assert np.mean(
                data.mon.tel[tel_id].flatfield.relative_gain_std) == 0
Example #9
0
def test_dl1_charge_calib(example_subarray):
    # copy because we mutate the camera, should not affect other tests
    subarray = deepcopy(example_subarray)
    camera = subarray.tel[1].camera
    # test with a sampling_rate different than 1 to
    # test if we handle time vs. slices correctly
    sampling_rate = 2
    camera.readout.sampling_rate = sampling_rate * u.GHz

    n_pixels = camera.geometry.n_pixels
    n_samples = 96
    mid = n_samples // 2
    pulse_sigma = 6
    random = np.random.RandomState(1)
    x = np.arange(n_samples)

    # Randomize times and create pulses
    time_offset = random.uniform(-10, +10, n_pixels)
    y = norm.pdf(x, mid + time_offset[:, np.newaxis],
                 pulse_sigma).astype("float32")

    camera.readout.reference_pulse_shape = norm.pdf(x, mid,
                                                    pulse_sigma)[np.newaxis, :]
    camera.readout.reference_pulse_sample_width = 1 / camera.readout.sampling_rate

    # Define absolute calibration coefficients
    absolute = random.uniform(100, 1000, n_pixels).astype("float32")
    y *= absolute[:, np.newaxis]

    # Define relative coefficients
    relative = random.normal(1, 0.01, n_pixels)
    y /= relative[:, np.newaxis]

    # Define pedestal
    pedestal = random.uniform(-4, 4, n_pixels)
    y += pedestal[:, np.newaxis]

    event = ArrayEventContainer()
    telid = list(subarray.tel.keys())[0]
    event.dl0.tel[telid].waveform = y
    event.dl0.tel[telid].selected_gain_channel = np.zeros(len(y), dtype=int)
    event.r1.tel[telid].selected_gain_channel = np.zeros(len(y), dtype=int)

    # Test default
    calibrator = CameraCalibrator(
        subarray=subarray, image_extractor=FullWaveformSum(subarray=subarray))
    calibrator(event)
    np.testing.assert_allclose(event.dl1.tel[telid].image, y.sum(1), rtol=1e-4)

    event.calibration.tel[telid].dl1.pedestal_offset = pedestal
    event.calibration.tel[telid].dl1.absolute_factor = absolute
    event.calibration.tel[telid].dl1.relative_factor = relative

    # Test without timing corrections
    calibrator(event)
    dl1 = event.dl1.tel[telid]
    np.testing.assert_allclose(dl1.image, 1, rtol=1e-5)
    expected_peak_time = (mid + time_offset) / sampling_rate
    np.testing.assert_allclose(dl1.peak_time, expected_peak_time, rtol=1e-5)

    # test with timing corrections
    event.calibration.tel[telid].dl1.time_shift = time_offset / sampling_rate
    calibrator(event)

    # more rtol since shifting might lead to reduced integral
    np.testing.assert_allclose(event.dl1.tel[telid].image, 1, rtol=1e-5)
    np.testing.assert_allclose(event.dl1.tel[telid].peak_time,
                               mid / sampling_rate,
                               atol=1)

    # test not applying time shifts
    # now we should be back to the result without setting time shift
    calibrator.apply_peak_time_shift = False
    calibrator.apply_waveform_time_shift = False
    calibrator(event)

    np.testing.assert_allclose(event.dl1.tel[telid].image, 1, rtol=1e-4)
    np.testing.assert_allclose(event.dl1.tel[telid].peak_time,
                               expected_peak_time,
                               atol=1)

    # We now use GlobalPeakWindowSum to see the effect of missing charge
    # due to not correcting time offsets.
    calibrator = CameraCalibrator(
        subarray=subarray,
        image_extractor=GlobalPeakWindowSum(subarray=subarray))
    calibrator(event)
    # test with timing corrections, should work
    # higher rtol because we cannot shift perfectly
    np.testing.assert_allclose(event.dl1.tel[telid].image, 1, rtol=0.01)
    np.testing.assert_allclose(event.dl1.tel[telid].peak_time,
                               mid / sampling_rate,
                               atol=1)

    # test deactivating timing corrections
    calibrator.apply_waveform_time_shift = False
    calibrator(event)

    # make sure we chose an example where the time shifts matter
    # charges should be quite off due to summing around global shift
    assert not np.allclose(event.dl1.tel[telid].image, 1, rtol=0.1)
    assert not np.allclose(
        event.dl1.tel[telid].peak_time, mid / sampling_rate, atol=1)
Example #10
0
    def _generate_events(self):
        """
        Yield ArrayEventContainer to iterate through events.
        """
        data = ArrayEventContainer()
        # Maybe take some other metadata, but there are still some 'unknown'
        # written out by the stage1 tool
        data.meta["origin"] = self.file_.root._v_attrs["CTA PROCESS TYPE"]
        data.meta["input_url"] = self.input_url
        data.meta["max_events"] = self.max_events

        if DataLevel.DL1_IMAGES in self.datalevels:
            image_iterators = {
                tel.name: self.file_.root.dl1.event.telescope.images[
                    tel.name
                ].iterrows()
                for tel in self.file_.root.dl1.event.telescope.images
            }
            if self.has_simulated_dl1:
                simulated_image_iterators = {
                    tel.name: self.file_.root.simulation.event.telescope.images[
                        tel.name
                    ].iterrows()
                    for tel in self.file_.root.simulation.event.telescope.images
                }

        if DataLevel.DL1_PARAMETERS in self.datalevels:
            param_readers = {
                tel.name: HDF5TableReader(self.file_).read(
                    f"/dl1/event/telescope/parameters/{tel.name}",
                    containers=[
                        HillasParametersContainer(),
                        TimingParametersContainer(),
                        LeakageContainer(),
                        ConcentrationContainer(),
                        MorphologyContainer(),
                        IntensityStatisticsContainer(),
                        PeakTimeStatisticsContainer(),
                    ],
                    prefixes=True,
                )
                for tel in self.file_.root.dl1.event.telescope.parameters
            }
            if self.has_simulated_dl1:
                simulated_param_readers = {
                    tel.name: HDF5TableReader(self.file_).read(
                        f"/simulation/event/telescope/parameters/{tel.name}",
                        containers=[
                            HillasParametersContainer(),
                            LeakageContainer(),
                            ConcentrationContainer(),
                            MorphologyContainer(),
                            IntensityStatisticsContainer(),
                        ],
                        prefixes=True,
                    )
                    for tel in self.file_.root.dl1.event.telescope.parameters
                }

        if self.is_simulation:
            # simulated shower wide information
            mc_shower_reader = HDF5TableReader(self.file_).read(
                "/simulation/event/subarray/shower",
                SimulatedShowerContainer(),
                prefixes="true",
            )

        # Setup iterators for the array events
        events = HDF5TableReader(self.file_).read(
            "/dl1/event/subarray/trigger", [TriggerContainer(), EventIndexContainer()]
        )

        array_pointing_finder = IndexFinder(
            self.file_.root.dl1.monitoring.subarray.pointing.col("time")
        )

        tel_pointing_finder = {
            tel.name: IndexFinder(tel.col("time"))
            for tel in self.file_.root.dl1.monitoring.telescope.pointing
        }

        for counter, array_event in enumerate(events):
            data.dl1.tel.clear()
            data.simulation.tel.clear()
            data.pointing.tel.clear()
            data.trigger.tel.clear()

            data.count = counter
            data.trigger, data.index = next(events)
            data.trigger.tels_with_trigger = (
                np.where(data.trigger.tels_with_trigger)[0] + 1
            )  # +1 to match array index to telescope id

            # Maybe there is a simpler way  to do this
            # Beware: tels_with_trigger contains all triggered telescopes whereas
            # the telescope trigger table contains only the subset of
            # allowed_tels given during the creation of the dl1 file
            for i in self.file_.root.dl1.event.telescope.trigger.where(
                f"(obs_id=={data.index.obs_id}) & (event_id=={data.index.event_id})"
            ):
                if self.allowed_tels and i["tel_id"] not in self.allowed_tels:
                    continue
                if self.datamodel_version == "v1.0.0":
                    data.trigger.tel[i["tel_id"]].time = i["telescopetrigger_time"]
                else:
                    data.trigger.tel[i["tel_id"]].time = i["time"]

            self._fill_array_pointing(data, array_pointing_finder)
            self._fill_telescope_pointing(data, tel_pointing_finder)

            if self.is_simulation:
                data.simulation.shower = next(mc_shower_reader)

            for tel in data.trigger.tel.keys():
                if self.allowed_tels and tel not in self.allowed_tels:
                    continue
                if self.has_simulated_dl1:
                    simulated = data.simulation.tel[tel]
                dl1 = data.dl1.tel[tel]
                if DataLevel.DL1_IMAGES in self.datalevels:
                    if f"tel_{tel:03d}" not in image_iterators.keys():
                        logger.debug(
                            f"Triggered telescope {tel} is missing "
                            "from the image table."
                        )
                        continue
                    image_row = next(image_iterators[f"tel_{tel:03d}"])
                    dl1.image = image_row["image"]
                    dl1.peak_time = image_row["peak_time"]
                    dl1.image_mask = image_row["image_mask"]

                    if self.has_simulated_dl1:
                        if f"tel_{tel:03d}" not in simulated_image_iterators.keys():
                            logger.warning(
                                f"Triggered telescope {tel} is missing "
                                "from the simulated image table, but was present at the "
                                "reconstructed image table."
                            )
                            continue
                        simulated_image_row = next(
                            simulated_image_iterators[f"tel_{tel:03d}"]
                        )
                        simulated.true_image = simulated_image_row["true_image"]

                if DataLevel.DL1_PARAMETERS in self.datalevels:
                    if f"tel_{tel:03d}" not in param_readers.keys():
                        logger.debug(
                            f"Triggered telescope {tel} is missing "
                            "from the parameters table."
                        )
                        continue
                    # Is there a smarter way to unpack this?
                    # Best would probbaly be if we could directly read
                    # into the ImageParametersContainer
                    params = next(param_readers[f"tel_{tel:03d}"])
                    dl1.parameters.hillas = params[0]
                    dl1.parameters.timing = params[1]
                    dl1.parameters.leakage = params[2]
                    dl1.parameters.concentration = params[3]
                    dl1.parameters.morphology = params[4]
                    dl1.parameters.intensity_statistics = params[5]
                    dl1.parameters.peak_time_statistics = params[6]

                    if self.has_simulated_dl1:
                        if f"tel_{tel:03d}" not in param_readers.keys():
                            logger.debug(
                                f"Triggered telescope {tel} is missing "
                                "from the simulated parameters table, but was "
                                "present at the reconstructed parameters table."
                            )
                            continue
                        simulated_params = next(
                            simulated_param_readers[f"tel_{tel:03d}"]
                        )
                        simulated.true_parameters.hillas = simulated_params[0]
                        simulated.true_parameters.leakage = simulated_params[1]
                        simulated.true_parameters.concentration = simulated_params[2]
                        simulated.true_parameters.morphology = simulated_params[3]
                        simulated.true_parameters.intensity_statistics = simulated_params[
                            4
                        ]

            yield data
Example #11
0
def test_array_display():
    """ check that we can do basic array display functionality """
    from ctapipe.visualization.mpl_array import ArrayDisplay
    from ctapipe.image import timing_parameters

    from ctapipe.containers import (
        ArrayEventContainer,
        DL1Container,
        DL1CameraContainer,
        ImageParametersContainer,
        CoreParametersContainer,
    )

    # build a test subarray:
    tels = dict()
    tel_pos = dict()
    for ii, pos in enumerate([[0, 0, 0], [100, 0, 0], [-100, 0, 0]] * u.m):
        tels[ii + 1] = TelescopeDescription.from_name("MST", "NectarCam")
        tel_pos[ii + 1] = pos

    sub = SubarrayDescription(name="TestSubarray",
                              tel_positions=tel_pos,
                              tel_descriptions=tels)

    # Create a fake event containing telescope-wise information about
    # the image directions projected on the ground
    event = ArrayEventContainer()
    event.dl1 = DL1Container()
    event.dl1.tel = {1: DL1CameraContainer(), 2: DL1CameraContainer()}
    event.dl1.tel[1].parameters = ImageParametersContainer()
    event.dl1.tel[2].parameters = ImageParametersContainer()
    event.dl1.tel[2].parameters.core = CoreParametersContainer()
    event.dl1.tel[1].parameters.core = CoreParametersContainer()
    event.dl1.tel[1].parameters.core.psi = u.Quantity(2.0, unit=u.deg)
    event.dl1.tel[2].parameters.core.psi = u.Quantity(1.0, unit=u.deg)

    ad = ArrayDisplay(subarray=sub)
    ad.set_vector_rho_phi(1 * u.m, 90 * u.deg)

    # try setting a value
    vals = np.ones(sub.num_tels)
    ad.values = vals

    assert (vals == ad.values).all()

    # test UV field ...

    # ...with colors by telescope type
    ad.set_vector_uv(np.array([1, 2, 3]) * u.m, np.array([1, 2, 3]) * u.m)
    # ...with scalar color
    ad.set_vector_uv(np.array([1, 2, 3]) * u.m, np.array([1, 2, 3]) * u.m, c=3)

    geom = CameraGeometry.from_name("LSTCam")
    rot_angle = 20 * u.deg
    hillas = CameraHillasParametersContainer(x=0 * u.m,
                                             y=0 * u.m,
                                             psi=rot_angle)

    # test using hillas params CameraFrame:
    hillas_dict = {
        1: CameraHillasParametersContainer(length=100.0 * u.m, psi=90 * u.deg),
        2: CameraHillasParametersContainer(length=20000 * u.cm, psi="95deg"),
    }

    grad = 2
    intercept = 1

    timing_rot20 = timing_parameters(
        geom,
        image=np.ones(geom.n_pixels),
        peak_time=intercept + grad * geom.pix_x.value,
        hillas_parameters=hillas,
        cleaning_mask=np.ones(geom.n_pixels, dtype=bool),
    )
    gradient_dict = {1: timing_rot20.slope.value, 2: timing_rot20.slope.value}
    core_dict = {
        tel_id: dl1.parameters.core.psi
        for tel_id, dl1 in event.dl1.tel.items()
    }
    ad.set_vector_hillas(
        hillas_dict=hillas_dict,
        core_dict=core_dict,
        length=500,
        time_gradient=gradient_dict,
        angle_offset=0 * u.deg,
    )
    ad.set_line_hillas(hillas_dict=hillas_dict, core_dict=core_dict, range=300)

    # test using hillas params for divergent pointing in telescopeframe:
    hillas_dict = {
        1:
        HillasParametersContainer(fov_lon=1.0 * u.deg,
                                  fov_lat=1.0 * u.deg,
                                  length=1.0 * u.deg,
                                  psi=90 * u.deg),
        2:
        HillasParametersContainer(fov_lon=1.0 * u.deg,
                                  fov_lat=1.0 * u.deg,
                                  length=1.0 * u.deg,
                                  psi=95 * u.deg),
    }
    ad.set_vector_hillas(
        hillas_dict=hillas_dict,
        core_dict=core_dict,
        length=500,
        time_gradient=gradient_dict,
        angle_offset=0 * u.deg,
    )
    ad.set_line_hillas(hillas_dict=hillas_dict, core_dict=core_dict, range=300)

    # test using hillas params for parallel pointing in telescopeframe:
    hillas_dict = {
        1:
        HillasParametersContainer(fov_lon=1.0 * u.deg,
                                  fov_lat=1.0 * u.deg,
                                  length=1.0 * u.deg,
                                  psi=90 * u.deg),
        2:
        HillasParametersContainer(fov_lon=1.0 * u.deg,
                                  fov_lat=1.0 * u.deg,
                                  length=1.0 * u.deg,
                                  psi=95 * u.deg),
    }
    ad.set_vector_hillas(
        hillas_dict=hillas_dict,
        core_dict=core_dict,
        length=500,
        time_gradient=gradient_dict,
        angle_offset=0 * u.deg,
    )

    # test negative time_gradients
    gradient_dict = {1: -0.03, 2: -0.02}
    ad.set_vector_hillas(
        hillas_dict=hillas_dict,
        core_dict=core_dict,
        length=500,
        time_gradient=gradient_dict,
        angle_offset=0 * u.deg,
    )
    # and very small
    gradient_dict = {1: 0.003, 2: 0.002}
    ad.set_vector_hillas(
        hillas_dict=hillas_dict,
        core_dict=core_dict,
        length=500,
        time_gradient=gradient_dict,
        angle_offset=0 * u.deg,
    )

    # Test background contour
    ad.background_contour(
        x=np.array([0, 1, 2]),
        y=np.array([0, 1, 2]),
        background=np.array([[0, 1, 2], [0, 1, 2], [0, 1, 2]]),
    )

    ad.set_line_hillas(hillas_dict=hillas_dict, core_dict=core_dict, range=300)
    ad.add_labels()
    ad.remove_labels()