Example #1
0
def test_conversion():
    """
    Test conversion between CameraFrame and EngineeringCameraFrame
    """
    from ctapipe.coordinates import CameraFrame, EngineeringCameraFrame

    coords = SkyCoord(x=[3, 1] * u.m, y=[2, 4] * u.m, frame=CameraFrame())

    for coord in coords:
        eng_coord = coord.transform_to(EngineeringCameraFrame())

        assert eng_coord.x == -coord.y
        assert eng_coord.y == -coord.x

        back = eng_coord.transform_to(CameraFrame())
        assert back.x == coord.x
        assert back.y == coord.y

        eng_coord = coord.transform_to(EngineeringCameraFrame(n_mirrors=2))

        assert eng_coord.x == coord.y
        assert eng_coord.y == -coord.x

        back = eng_coord.transform_to(CameraFrame())
        assert back.x == coord.x
        assert back.y == coord.y
Example #2
0
def camera_to_horizontal(x, y, az_pointing, alt_pointing, focal_length):
    hf = AltAz()

    pointing = SkyCoord(
        alt=alt_pointing.values * u.rad,
        az=az_pointing.values * u.rad,
        frame=hf,
    )

    # cf = CameraFrame(
    #         focal_length=focal_length.values*u.m,
    #         telescope_pointing=pointing,
    #         )

    cf = EngineeringCameraFrame(
        n_mirrors=2,
        #location=LOCATION,
        #obstime=obstime,
        focal_length=focal_length.values * u.m,
        telescope_pointing=pointing,
    )

    altaz_coord = SkyCoord(x=x.values * u.m, y=y.values * u.m, frame=cf)
    altaz_coord = altaz_coord.transform_to(hf)

    return altaz_coord.alt.to(u.rad).value, altaz_coord.az.to(u.rad).value
Example #3
0
def horizontal_to_camera(az, alt, az_pointing, alt_pointing, focal_length):
    hf = AltAz()

    pointing = SkyCoord(
        alt=alt_pointing.values * u.rad,
        az=az_pointing.values * u.rad,
        frame=hf,
    )

    # cf = CameraFrame(
    #         focal_length=focal_length.values*u.m,
    #         telescope_pointing=pointing,
    #         )

    cf = EngineeringCameraFrame(
        n_mirrors=2,
        #location=LOCATION,
        #obstime=obstime,
        focal_length=focal_length.values * u.m,
        telescope_pointing=pointing,
    )

    xy_coord = SkyCoord(az=az.values * u.rad, alt=alt.values * u.rad, frame=hf)
    xy_coord = xy_coord.transform_to(cf)

    return xy_coord.x.to(u.m).value, xy_coord.y.to(u.m).value
Example #4
0
    def configure(self, config):
        config["pix_pos"] = self.pix_pos
        config["fov"] = self.fov

        td = config["time_step"]
        n_frames = config["n_frames"]

        # precomputing transformations
        # Will need to do this smarter (in chunks) when the number of frames reaches 10k-100k
        self.obstimes = np.array(
            [config["start_time"] + td * i for i in range(n_frames)])
        self.precomp_hf = AltAz(location=self.location, obstime=self.obstimes)
        if isinstance(self.target, SkyCoord):
            self.precomp_point = self.target.transform_to(self.precomp_hf)
        else:
            alt, az = self.target
            n = len(self.obstimes)
            self.precomp_point = SkyCoord(
                alt=alt * np.ones(n),
                az=az * np.ones(n),
                frame=self.precomp_hf,
            )

        self.precomp_camf = EngineeringCameraFrame(
            n_mirrors=2,
            telescope_pointing=self.precomp_point,
            focal_length=u.Quantity(self.focal_length, u.m),  # 28 for LST
            obstime=self.obstimes,
            location=self.location,
        )
        config["start_pointing"] = self.precomp_point[0]
Example #5
0
def test_camera_coordinate_transform(camera_name):
    """test conversion of the coordinates stored in a camera frame"""
    from ctapipe.coordinates import EngineeringCameraFrame, CameraFrame, TelescopeFrame

    geom = CameraGeometry.from_name(camera_name)
    trans_geom = geom.transform_to(EngineeringCameraFrame())

    unit = geom.pix_x.unit
    assert np.allclose(geom.pix_x.to_value(unit),
                       -trans_geom.pix_y.to_value(unit))
    assert np.allclose(geom.pix_y.to_value(unit),
                       -trans_geom.pix_x.to_value(unit))

    # also test converting into a spherical frame:
    focal_length = 1.2 * u.m
    geom.frame = CameraFrame(focal_length=focal_length)
    telescope_frame = TelescopeFrame()
    sky_geom = geom.transform_to(telescope_frame)

    x = sky_geom.pix_x.to_value(u.deg)
    assert len(x) == len(geom.pix_x)

    # and test going backward from spherical to cartesian:

    geom_cam = sky_geom.transform_to(CameraFrame(focal_length=focal_length))
    assert np.allclose(geom_cam.pix_x.to_value(unit),
                       geom.pix_x.to_value(unit))
Example #6
0
def test_camera_coordinate_transform(camera_name):
    '''test conversion of the coordinates stored in a camera frame'''
    from ctapipe.coordinates import EngineeringCameraFrame

    geom = CameraGeometry.from_name(camera_name)
    trans_geom = geom.transform_to(EngineeringCameraFrame())

    unit = geom.pix_x.unit
    assert np.allclose(geom.pix_x.to_value(unit), -trans_geom.pix_y.to_value(unit))
    assert np.allclose(geom.pix_y.to_value(unit), -trans_geom.pix_x.to_value(unit))
Example #7
0
def main():
    fig, axs = plt.subplots(1, 2, constrained_layout=True, figsize=(6, 3))

    model = Gaussian(0 * u.m, 0.1 * u.m, 0.3 * u.m, 0.05 * u.m, 25 * u.deg)
    cam = CameraGeometry.from_name('FlashCam')
    image, *_ = model.generate_image(cam, 2500)

    CameraDisplay(cam, ax=axs[0], image=image)
    CameraDisplay(
        cam.transform_to(EngineeringCameraFrame()),
        ax=axs[1],
        image=image,
    )

    axs[0].set_title('CameraFrame')
    axs[1].set_title('EngineeringCameraFrame')

    plt.show()
Example #8
0
def get_ctapipe_camera_geometry(mapping):
    """
    Obtain a ctapipe CameraGeometry object from the CHECLabPy Mapping object.

    Pixel coordinates are converted into degrees using the plate scale.

    Parameters
    ----------
    mapping : `pandas.DataFrame`
        The mapping for the pixels stored in a pandas DataFrame. Can be
        obtained from either of these options:

        CHECLabPy.io.TIOReader.mapping
        CHECLabPy.io.ReaderR0.mapping
        CHECLabPy.io.ReaderR1.mapping
        CHECLabPy.io.DL1Reader.mapping
        CHECLabPy.utils.mapping.get_clp_mapping_from_tc_mapping

    Returns
    -------
    geom : `ctapipe.instrument.camera.CameraGeometry`
    """
    from ctapipe.instrument import CameraGeometry
    from ctapipe.coordinates import EngineeringCameraFrame
    from astropy import units as u

    pix_x = mapping['xpix'].values
    pix_y = mapping['ypix'].values

    camera = CameraGeometry(
        "CHEC",
        pix_id=np.arange(mapping.metadata['n_pixels']),
        pix_x=pix_x * u.m,
        pix_y=pix_y * u.m,
        pix_area=None,
        pix_type='rectangular',
        frame=EngineeringCameraFrame(n_mirrors=2),
        sampling_rate=u.Quantity(1, u.GHz),
    )
    return camera
Example #9
0
def generate_hotspots(
    alt, az, altaz_frame, stars_pos, star_cat, pixsize, vmag_lim, pos
):

    telescope_pointing = SkyCoord(alt=alt, az=az, frame=altaz_frame)
    fov_mask = telescope_pointing.separation(stars_pos).deg < 5
    sind = np.argsort(star_cat.vmag.values[fov_mask])

    focal_length = u.Quantity(2.15191, u.m)

    engineering_frame = EngineeringCameraFrame(
        n_mirrors=2,
        location=telescope_pointing.location,
        obstime=telescope_pointing.obstime,
        focal_length=focal_length,
        telescope_pointing=telescope_pointing,
    )
    hotspots = []
    in_cat_stars = []
    hips = []
    for i, star in enumerate(stars_pos[fov_mask][sind]):
        star_en = star.transform_to(engineering_frame)
        p = np.array([star_en.x.value, star_en.y.value])
        if np.any(
            np.linalg.norm((pos - np.outer(p, np.ones(2048))).T, axis=1) < pixsize * 1.1
        ):
            for h in hotspots:
                if np.linalg.norm(np.array(h) - p) > pixsize * 4:
                    continue

            hotspots.append((star_en.x.value, star_en.y.value))
            hip = (i, star_cat.hip_number.values[fov_mask][sind][i])
            hips.append(hip)
            # print(hip, i)
            if star_cat.vmag.values[fov_mask][sind][i] < vmag_lim:
                in_cat_stars.append(hip)
    if len(in_cat_stars) == 0:
        print("no star in catalog for fov")
    # print("number of stars in catalog for fov", len(in_cat_stars))
    return hotspots, telescope_pointing, fov_mask, in_cat_stars, hips
Example #10
0
def add_camera_position(df_pointing, source_skycoord):
    obstime = df_pointing.index.values
    alt = df_pointing['altitude_raw'].values
    az = df_pointing['azimuth_raw'].values
    altaz_frame = AltAz(location=LOCATION, obstime=obstime)
    telescope_pointing = SkyCoord(
        alt=alt,
        az=az,
        unit='rad',
        frame=altaz_frame,
    )
    engineering_frame = EngineeringCameraFrame(
        n_mirrors=2,
        location=LOCATION,
        obstime=obstime,
        focal_length=FOCAL_LENGTH,
        telescope_pointing=telescope_pointing,
    )
    source_cam = source_skycoord.transform_to(engineering_frame)
    x_src = source_cam.x.value
    y_src = source_cam.y.value
    df_pointing['x_src'] = x_src
    df_pointing['y_src'] = y_src
Example #11
0
for pix, (p, noise) in zip((42, 314), pixels):
    ax.plot(x, p + 0.2 * noise, label=f'Pixel {pix}')

ax.legend(loc=(0.5, 0.6), frameon=False)

fig.savefig('build/calibrated.pdf')

hillas = dict(
    x=80 * u.mm,
    y=20 * u.mm,
    width=15 * u.mm,
    length=50 * u.mm,
    psi=35 * u.deg,
)

cam = CameraGeometry.from_name('FACT').transform_to(EngineeringCameraFrame())
longi, trans = camera_to_shower_coordinates(cam.pix_x, cam.pix_y, hillas['x'],
                                            hillas['y'], hillas['psi'])

m = SkewedGaussian(**hillas, skewness=0.3)
img, signal, noise = m.generate_image(cam, intensity=2500, nsb_level_pe=3)

time_noise = np.random.uniform(0, 60, cam.n_pixels)
time_image = 0.2 * longi.to_value(u.mm) + 25

time = np.average(np.column_stack([time_noise, time_image]),
                  weights=np.column_stack([noise, signal]) + 1,
                  axis=1)

inferno = plt.get_cmap('inferno')
inferno.set_bad('gray')
Example #12
0
def plot_pedestals(data_file, pedestal_file, run=0, plot_file=None, tel_id=1, offset_value=400, sample_size=1000):
    """
    plot pedestal quantities quantities

    Parameters
    ----------
    data_file:   pedestal run

    pedestal_file:   file with drs4 corrections

    run: run number of data to be corrected

    plot_file:  name of output pdf file

    tel_id: id of the telescope

    offset_value: baseline off_set
    """

    config = {
        "LSTEventSource": {
            "allowed_tels": [1],
            "LSTR0Corrections": {
                "drs4_pedestal_path": pedestal_file,
            },
        }
    }
    # event_reader
    reader = EventSource(data_file, config=Config(config), max_events=None)
    t = np.linspace(2, 37, 36)

    # configuration for the charge integrator
    charge_config = Config(
        {
            "FixedWindowSum": {
                "window_shift": 6,
                "window_width": 12,
                "peak_index": 18,
            }
        }
    )
    # declare the pedestal component
    pedestal = PedestalIntegrator(
        tel_id=tel_id,
        time_sampling_correction_path=None,
        sample_size=sample_size,
        sample_duration=1000000,
        charge_median_cut_outliers=[-10, 10],
        charge_std_cut_outliers=[-10, 10],
        charge_product="FixedWindowSum",
        config=charge_config,
        subarray=reader.subarray,
    )

    for i, event in enumerate(reader):
        if tel_id != event.trigger.tels_with_trigger[0]:
            raise Exception(
                f"Given wrong telescope id {tel_id}, files has id {event.trigger.tels_with_trigger[0]}"
            )

        are_pedestals_calculated = pedestal.calculate_pedestals(event)
        if are_pedestals_calculated:
            ped_data = event.mon.tel[tel_id].pedestal
            break

    camera_geometry = reader.subarray.tels[tel_id].camera.geometry
    camera_geometry = camera_geometry.transform_to(EngineeringCameraFrame())

    if are_pedestals_calculated and plot_file is not None:
        with PdfPages(plot_file) as pdf:

            plt.rc("font", size=15)

            # first figure
            fig = plt.figure(1, figsize=(12, 24))
            plt.tight_layout()
            n_samples = charge_config["FixedWindowSum"]["window_width"]
            fig.suptitle(f"Run {run}, integration on {n_samples} samples", fontsize=25)
            pad = 420

            image = ped_data.charge_median
            mask = ped_data.charge_median_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera_geometry)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} pedestal [ADC]', rotation=90)
                plt.title(f"{channel[chan]} pedestal [ADC]")
                disp.add_colorbar()

            image = ped_data.charge_std
            mask = ped_data.charge_std_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera_geometry)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} pedestal std [ADC]', rotation=90)
                plt.title(f"{channel[chan]} pedestal std [ADC]")
                disp.add_colorbar()

            #  histograms
            for chan in np.arange(2):
                mean_ped = ped_data.charge_mean[chan]
                ped_std = ped_data.charge_std[chan]

                # select good pixels
                select = np.logical_not(mask[chan])

                # fig.suptitle(f"Run {run} channel: {channel[chan]}", fontsize=25)
                pad += 1
                # pedestal charge
                plt.subplot(pad)
                plt.tight_layout()
                plt.ylabel("pixels")
                plt.xlabel(f"{channel[chan]} pedestal")
                median = np.median(mean_ped[select])
                rms = np.std(mean_ped[select])
                label = f"{channel[chan]} Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(mean_ped[select], bins=50, label=label)
                plt.legend()
                pad += 1
                # pedestal std
                plt.subplot(pad)
                plt.ylabel("pixels")
                plt.xlabel(f"{channel[chan]} pedestal std")
                median = np.median(ped_std[select])
                rms = np.std(ped_std[select])
                label = f" Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(ped_std[select], bins=50, label=label)
                plt.legend()

            plt.subplots_adjust(top=0.94, bottom=0.04, right=0.96)

            pdf.savefig()
            plt.close()

            # event_reader
            # reader = EventSource(data_file, config=Config(config), max_events=1000)

            pix = 0
            pad = 420
            offset_value = reader.r0_r1_calibrator.offset.tel[tel_id]

            # plot corrected waveforms of first 8 events
            for i, ev in enumerate(reader):
                for chan in np.arange(2):

                    if pad == 420:
                        # new figure

                        fig = plt.figure(ev.index.event_id * 1000, figsize=(12, 24))
                        fig.suptitle(f"Run {run}, pixel {pix}", fontsize=25)
                        plt.tight_layout()
                    pad += 1
                    plt.subplot(pad)

                    # remove samples at beginning / end of waveform
                    start = reader.r0_r1_calibrator.r1_sample_start.tel[tel_id]
                    end = reader.r0_r1_calibrator.r1_sample_end.tel[tel_id]

                    plt.subplots_adjust(top=0.92)
                    label = f"event {ev.index.event_id}, {channel[chan]}: R0"
                    plt.step(
                        t,
                        ev.r0.tel[tel_id].waveform[chan, pix, start:end],
                        color="blue",
                        label=label,
                    )

                    label = "baseline correction \n + dt corr + interp. spikes"

                    plt.step(
                        t,
                        ev.r1.tel[tel_id].waveform[chan, pix] + offset_value,
                        alpha=0.5,
                        color="green",
                        label=label,
                    )
                    plt.plot([0, 40], [offset_value, offset_value], "k--", label="offset")
                    plt.xlabel("time sample [ns]")
                    plt.ylabel("counts [ADC]")
                    plt.legend()
                    plt.ylim(200, 600)

                if pad == 428:
                    pad = 420
                    plt.subplots_adjust(top=0.92)
                    pdf.savefig()
                    plt.close()

                if i == 8:
                    break

    elif not are_pedestals_calculated:
        log.error("Not able to calculate pedestals or output pdf file not especified.")

    elif plot_file is None:
        log.warning("Not PDF outputfile specified.")
    def finish(self):
        """
        write fit results in h5 file and the check-plots in pdf file
        """

        gain = np.ma.array(self.fit_parameters.T[0], mask=self.fit_error.T)
        quadratic_term = np.ma.array(self.fit_parameters.T[1], mask=self.fit_error.T)

        # give to the badly fitted pixel a median value for the B term
        median_quadratic_term = np.ma.median(quadratic_term, axis=0)

        fill_array = np.ones((constants.N_PIXELS, constants.N_GAINS)) * median_quadratic_term

        quadratic_term_corrected = np.ma.filled(quadratic_term, fill_array)

        with h5py.File(self.output_path, 'w') as hf:
            hf.create_dataset('gain', data=gain.T)
            hf.create_dataset('B_term', data=quadratic_term_corrected.T)
            hf.create_dataset('covariance_matrix', data=self.fit_cov_matrix)
            hf.create_dataset('bad_fit_mask', data=self.fit_error)

            # remember the camera median and the variance per run
            channel = ["HG", "LG"]
            for chan in [0, 1]:
                if self.signal[chan] is not None:
                    hf.create_dataset(f'median_signal_{channel[chan]}', data=np.median(self.signal[chan], axis=0))
                    hf.create_dataset(f'median_variance_{channel[chan]}', data=np.median(self.variance[chan], axis=0))
                    hf.create_dataset(f'runs_{channel[chan]}', data=self.selected_runs[chan])

            hf.create_dataset('runs', data=self.run_list)
            hf.create_dataset('sub_run', data=self.sub_run)

            # plot open pdf
            with PdfPages(self.plot_path) as pdf:
                plt.rc("font", size=15)

                for chan in self.gain_channels:
                    # plot the used runs and their median camera charge
                    fig = plt.figure((chan + 1), figsize=(8, 20))
                    fig.suptitle(f"{channel[chan]} channel", fontsize=25)
                    ax = plt.subplot(2, 1, 1)
                    ax.grid(True)
                    ax.yaxis.set_major_locator(MaxNLocator(integer=True))
                    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
                    ax.yaxis.set_major_locator(plt.MultipleLocator(1))

                    plt.plot(np.median(self.signal[chan], axis=0), self.selected_runs[chan], "o")
                    plt.xlabel(r'$\mathrm{\overline{Q}-\overline{ped}}$ [ADC]')
                    plt.ylabel(r'Runs used in the fit')

                    plt.subplot(2, 1, 2)
                    camera = load_camera_geometry()
                    camera = camera.transform_to(EngineeringCameraFrame())
                    disp = CameraDisplay(camera)
                    image = self.fit_parameters.T[1].T * 100
                    mymin = np.median(image[chan]) - 3 * np.std(image[chan])
                    mymax = np.median(image[chan]) + 3 * np.std(image[chan])
                    disp.set_limits_minmax(mymin, mymax)
                    mask = np.where(self.fit_error[chan] == 1)[0]
                    disp.highlight_pixels(mask, linewidth=2.5, color="green")
                    disp.image = image[chan]
                    disp.cmap = plt.cm.coolwarm
                    plt.title(f"{channel[chan]} Fitted B values [%]")
                    disp.add_colorbar()
                    plt.tight_layout()
                    pdf.savefig()

                    # plot the fit results and residuals for four arbitrary  pixels
                    fig = plt.figure((chan + 1) * 10, figsize=(11, 22))
                    fig.suptitle(f"{channel[chan]} channel", fontsize=25)

                    pad = 0
                    for pix in [0, 600, 1200, 1800]:
                        pad += 1
                        plt.subplot(4, 2, pad)
                        plt.grid(which='minor')

                        mask = self.unusable_pixels[chan][pix]
                        sig = np.ma.array(self.signal[chan][pix], mask=mask).compressed()
                        var = np.ma.array(self.variance[chan][pix], mask=mask).compressed()
                        popt = self.fit_parameters[chan, pix]

                        # plot points
                        plt.plot(sig, var, 'o', color="C0")

                        # plot fit
                        min_x = min(1000, np.min(sig) * 0.9)
                        max_x = max(10000, np.max(sig) * 1.1)
                        x = np.arange(np.min(sig), np.max(sig))

                        plt.plot(x, quadratic_fit(x, *popt), '--', color="C1",
                                 label=f'Pixel {pix}:\ng={popt[0]:5.2f} [ADC/pe] , B={popt[1]:5.3f}')
                        plt.xlim(min_x, max_x)
                        plt.xlabel('Q-ped [ADC]')
                        plt.ylabel(r'$\mathrm{\sigma_Q^2-\sigma_{ped}^2}$ [$ADC^2$]')
                        plt.xscale('log')
                        plt.yscale('log')
                        plt.legend()

                        # plot residuals
                        pad += 1
                        plt.subplot(4, 2, pad)
                        plt.grid(which='both', axis='both')

                        popt = self.fit_parameters[chan, pix]
                        plt.plot(sig, (quadratic_fit(sig, *popt) - var) / var * 100, 'o', color="C0")
                        plt.xlim(min_x, max_x)
                        plt.xscale('log')
                        plt.ylabel('fit residuals %')
                        plt.xlabel('Q-ped [ADC]')
                        plt.hlines(0, 0, np.max(sig), linestyle='dashed', color="black")

                    plt.tight_layout()
                    pdf.savefig()
def plot_all(ped_data, ff_data, calib_data, run=0, plot_file="none"):
    """
     plot camera calibration quantities

     Parameters
     ----------
     ped_data:   pedestal container PedestalContainer()

     ff_data:    flat-field container FlatFieldContainer()

     calib_data: calibration container WaveformCalibrationContainer()

     """
    # read geometry
    camera = load_camera_geometry()
    camera = camera.transform_to(EngineeringCameraFrame())

    # plot open pdf
    if plot_file != "none":
        pp = PdfPages(plot_file)

    plt.rc('font', size=15)


    ### first figure
    fig = plt.figure(1, figsize=(12, 24))
    plt.tight_layout()
    fig.suptitle(f"Run {run}", fontsize=25)
    pad = 420
    image = ff_data.charge_median
    mask = ff_data.charge_median_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        #disp.axes.text(lposx, 0, f'{channel[chan]} signal charge (ADC)', rotation=90)
        plt.title(f'{channel[chan]} signal charge [ADC]')
        disp.add_colorbar()

    image = ff_data.charge_std
    mask = ff_data.charge_std_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        #disp.axes.text(lposx, 0, f'{channel[chan]} signal std [ADC]', rotation=90)
        plt.title(f'{channel[chan]} signal std [ADC]')
        disp.add_colorbar()

    image = ped_data.charge_median
    mask = ped_data.charge_median_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        #disp.axes.text(lposx, 0, f'{channel[chan]} pedestal [ADC]', rotation=90)
        plt.title(f'{channel[chan]} pedestal [ADC]')
        disp.add_colorbar()

    image = ped_data.charge_std
    mask =  ped_data.charge_std_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        #disp.axes.text(lposx, 0, f'{channel[chan]} pedestal std [ADC]', rotation=90)
        plt.title(f'{channel[chan]} pedestal std [ADC]')
        disp.add_colorbar()

    plt.subplots_adjust(top=0.92)

    if plot_file != "none":
        pp.savefig()

    ### second figure
    fig = plt.figure(2, figsize=(12, 24))
    plt.tight_layout()
    fig.suptitle(f"Run {run}", fontsize=25)
    pad=420

    # time
    image = ff_data.time_median
    mask = ff_data.time_median_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        #disp.axes.text(lposx, 0, f'{channel[chan]} time', rotation=90)
        plt.title(f'{channel[chan]} time')
        disp.add_colorbar()

    image = ff_data.relative_gain_median
    mask = calib_data.unusable_pixels
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        disp.highlight_pixels(mask[chan], linewidth=2)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        disp.set_limits_minmax(0.7, 1.3)
        plt.title(f'{channel[chan]} relative signal')
        #disp.axes.text(lposx, 0, f'{channel[chan]} relative gain', rotation=90)
        disp.add_colorbar()

    # pe
    image = calib_data.n_pe
    mask = calib_data.unusable_pixels
    image = np.where(np.isnan(image), 0, image)
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        mymin= np.median(image[chan])- 2*np.std(image[chan])
        mymax= np.median(image[chan])+ 2*np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.cmap = plt.cm.coolwarm
        plt.title(f'{channel[chan]} photon-electrons')
        #disp.axes.text(lposx, 0, f'{channel[chan]} photon-electrons', rotation=90)
        disp.add_colorbar()

    # pe histogram
    pad += 1
    plt.subplot(pad)
    plt.tight_layout()
    for chan in np.arange(2):
        n_pe = calib_data.n_pe[chan]
        # select good pixels
        select = np.logical_not(mask[chan])
        median = int(np.median(n_pe[select]))
        rms = np.std(n_pe[select])
        mymin= median - 4*rms
        mymax= median + 4*rms
        label = f"{channel[chan]} Median {median:3.2f}, std {rms:5.2f}"
        plt.hist(n_pe[select], label=label, histtype='step', range=(mymin, mymax), bins=50, stacked=True, alpha=0.5,
                 fill=True)
        plt.legend()
    plt.xlabel(f'pe', fontsize=20)
    plt.ylabel('pixels', fontsize=20)

    # pe scatter plot
    pad += 1
    plt.subplot(pad)
    plt.tight_layout()
    HG = calib_data.n_pe[0]
    LG = calib_data.n_pe[1]
    HG = np.where(np.isnan(HG), 0, HG)
    LG = np.where(np.isnan(LG), 0, LG)
    mymin = np.median(LG) - 2 * np.std(LG)
    mymax = np.median(LG) + 2 * np.std(LG)
    plt.hist2d(LG, HG, bins=[100, 100])
    plt.xlabel("LG", fontsize=20)
    plt.ylabel("HG", fontsize=20)

    x = np.arange(mymin,mymax)
    plt.plot(x, x)
    plt.ylim(mymin, mymax)
    plt.xlim(mymin, mymax)
    plt.subplots_adjust(top=0.92)
    if plot_file != "none":
        pp.savefig()


    ### figures 3 and 4 : histograms
    for chan in np.arange(2):
        n_pe = calib_data.n_pe[chan]

        gain_median = ff_data.relative_gain_median[chan]
        #charge_median = ff_data.charge_median[chan]
        charge_mean = ff_data.charge_mean[chan]
        charge_std = ff_data.charge_std[chan]
        #median_ped = ped_data.charge_median[chan]
        mean_ped = ped_data.charge_mean[chan]
        ped_std = ped_data.charge_std[chan]

        # select good pixels
        select = np.logical_not(mask[chan])
        fig = plt.figure(chan+10, figsize=(12, 18))
        fig.tight_layout(rect=[0, 0.0, 1, 0.95])

        fig.suptitle(f"Run {run} channel: {channel[chan]}", fontsize=25)

        # charge
        plt.subplot(321)
        plt.tight_layout()
        median = int(np.median(charge_mean[select]))
        rms = np.std(charge_mean[select])
        label=f"Median {median:3.2f}, std {rms:5.0f}"
        plt.xlabel('charge (ADC)', fontsize=20)
        plt.ylabel('pixels', fontsize=20)
        plt.hist(charge_mean[select], bins=50,label=label)
        plt.legend()

        plt.subplot(322)
        plt.tight_layout()
        plt.ylabel('pixels', fontsize=20)
        plt.xlabel('charge std', fontsize=20)
        median = np.median(charge_std[select])
        rms = np.std(charge_std[select])
        label=f"Median {median:3.2f}, std {rms:3.2f}"
        plt.hist(charge_std[select], bins=50,label=label)
        plt.legend()

        # pedestal charge
        plt.subplot(323)
        plt.tight_layout()
        plt.ylabel('pixels', fontsize=20)
        plt.xlabel('pedestal', fontsize=20)
        median = np.median(mean_ped[select])
        rms = np.std(mean_ped[select])
        label=f"Median {median:3.2f}, std {rms:3.2f}"
        plt.hist(mean_ped[select], bins=50,label=label)
        plt.legend()

        # pedestal std
        plt.subplot(324)
        plt.ylabel('pixels', fontsize=20)
        plt.xlabel('pedestal std', fontsize=20)
        median = np.median(ped_std[select])
        rms = np.std(ped_std[select])
        label=f"Median {median:3.2f}, std {rms:3.2f}"
        plt.hist(ped_std[select], bins=50,label=label)
        plt.legend()

        # relative gain
        plt.subplot(325)
        plt.tight_layout()
        plt.ylabel('pixels', fontsize=20)
        plt.xlabel('relative signal', fontsize=20)
        median = np.median(gain_median[select])
        rms = np.std(gain_median[select])
        label=f"Relative gain {median:3.2f}, std {rms:5.2f}"
        plt.hist(gain_median[select], bins=50,label=label)
        plt.legend()

        # photon electrons
        plt.subplot(326)
        plt.tight_layout()
        plt.ylabel('pixels', fontsize=20)
        plt.xlabel('pe', fontsize=20)
        median = np.median(n_pe[select])
        rms = np.std(n_pe[select])
        label=f"Median {median:3.2f}, std {rms:3.2f}"
        plt.hist(n_pe[select], bins=50,label=label)
        plt.legend()
        plt.subplots_adjust(top=0.92)
        if plot_file != "none":
            pp.savefig(plt.gcf())


    if plot_file != "none":
        pp.close()
Example #15
0
def plot_all(ped_data, ff_data, calib_data, run=0, plot_file=None):
    """
    plot camera calibration quantities

    Parameters
    ----------
    ped_data:   pedestal container PedestalContainer()

    ff_data:    flat-field container FlatFieldContainer()

    calib_data: calibration container WaveformCalibrationContainer()

    run: run number

    plot_file: name of the output PDF file. No file is produced if name is not provided

    """
    # read geometry
    camera = load_camera_geometry()
    camera = camera.transform_to(EngineeringCameraFrame())

    # plot open pdf
    if plot_file is not None:
        with PdfPages(plot_file) as pdf:

            plt.rc("font", size=15)

            # first figure
            fig = plt.figure(1, figsize=(12, 24))
            plt.tight_layout()
            fig.suptitle(f"Run {run}", fontsize=25)
            pad = 420
            image = ff_data.charge_median
            mask = ff_data.charge_median_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} signal charge (ADC)', rotation=90)
                plt.title(f"{channel[chan]} signal charge [ADC]")
                disp.add_colorbar()

            image = ff_data.charge_std
            mask = ff_data.charge_std_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} signal std [ADC]', rotation=90)
                plt.title(f"{channel[chan]} signal std [ADC]")
                disp.add_colorbar()

            image = ped_data.charge_median
            mask = ped_data.charge_median_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} pedestal [ADC]', rotation=90)
                plt.title(f"{channel[chan]} pedestal [ADC]")
                disp.add_colorbar()

            image = ped_data.charge_std
            mask = ped_data.charge_std_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} pedestal std [ADC]', rotation=90)
                plt.title(f"{channel[chan]} pedestal std [ADC]")
                disp.add_colorbar()

            plt.subplots_adjust(top=0.92)

            pdf.savefig()
            plt.close()

            # second figure
            fig = plt.figure(2, figsize=(12, 24))
            plt.tight_layout()
            fig.suptitle(f"Run {run}", fontsize=25)
            pad = 420

            # time
            image = ff_data.time_median
            mask = ff_data.time_median_outliers
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                # disp.axes.text(lposx, 0, f'{channel[chan]} time', rotation=90)
                plt.title(f"{channel[chan]} time")
                disp.add_colorbar()

            image = ff_data.relative_gain_median
            mask = calib_data.unusable_pixels
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                disp.highlight_pixels(mask[chan], linewidth=2)
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.image = image[chan]
                disp.cmap = plt.cm.coolwarm
                disp.set_limits_minmax(0.7, 1.3)
                plt.title(f"{channel[chan]} relative signal")
                # disp.axes.text(lposx, 0, f'{channel[chan]} relative gain', rotation=90)
                disp.add_colorbar()

            # pe
            image = calib_data.n_pe
            mask = calib_data.unusable_pixels
            image = np.where(np.isnan(image), 0, image)
            for chan in np.arange(2):
                pad += 1
                plt.subplot(pad)
                plt.tight_layout()
                disp = CameraDisplay(camera)
                disp.highlight_pixels(mask[chan], linewidth=2)
                disp.image = image[chan]
                mymin = np.median(image[chan]) - 2 * np.std(image[chan])
                mymax = np.median(image[chan]) + 2 * np.std(image[chan])
                disp.set_limits_minmax(mymin, mymax)
                disp.cmap = plt.cm.coolwarm
                plt.title(f"{channel[chan]} photon-electrons")
                # disp.axes.text(lposx, 0, f'{channel[chan]} photon-electrons', rotation=90)
                disp.add_colorbar()

            # pe histogram
            pad += 1
            plt.subplot(pad)
            plt.tight_layout()
            for chan in np.arange(2):
                n_pe = calib_data.n_pe[chan]
                # select good pixels
                select = np.logical_not(mask[chan])
                median = int(np.median(n_pe[select]))
                rms = np.std(n_pe[select])
                mymin = median - 4 * rms
                mymax = median + 4 * rms
                label = f"{channel[chan]} Median {median:3.2f}, std {rms:5.2f}"
                plt.hist(
                    n_pe[select],
                    label=label,
                    histtype="step",
                    range=(mymin, mymax),
                    bins=50,
                    stacked=True,
                    alpha=0.5,
                    fill=True,
                )
                plt.legend()
            plt.xlabel("pe", fontsize=20)
            plt.ylabel("pixels", fontsize=20)

            # pe scatter plot
            pad += 1
            plt.subplot(pad)
            plt.tight_layout()
            HG = calib_data.n_pe[0]
            LG = calib_data.n_pe[1]
            HG = np.where(np.isnan(HG), 0, HG)
            LG = np.where(np.isnan(LG), 0, LG)
            mymin = np.median(LG) - 2 * np.std(LG)
            mymax = np.median(LG) + 2 * np.std(LG)
            plt.hist2d(LG, HG, bins=[100, 100])
            plt.xlabel("LG", fontsize=20)
            plt.ylabel("HG", fontsize=20)

            x = np.arange(mymin, mymax)
            plt.plot(x, x)
            plt.ylim(mymin, mymax)
            plt.xlim(mymin, mymax)
            plt.subplots_adjust(top=0.92)

            pdf.savefig()
            plt.close()

            # figures 3 and 4: histograms
            for chan in np.arange(2):
                n_pe = calib_data.n_pe[chan]

                gain_median = ff_data.relative_gain_median[chan]
                charge_median = ff_data.charge_median[chan]
                #charge_mean = ff_data.charge_mean[chan]
                charge_std = ff_data.charge_std[chan]
                n_ff = ff_data.n_events
                median_ped = ped_data.charge_median[chan]
                #mean_ped = ped_data.charge_mean[chan]
                ped_std = ped_data.charge_std[chan]
                n_ped = ped_data.n_events

                dc_to_pe = calib_data.dc_to_pe[chan]
                time_correction = calib_data.time_correction[chan]

                # select good pixels
                select = np.logical_not(mask[chan])
                fig = plt.figure(chan + 10, figsize=(12, 24))
                fig.tight_layout(rect=[0, 0.0, 1, 0.95])

                fig.suptitle(f"Run {run} channel: {channel[chan]}", fontsize=25)

                # charge
                plt.subplot(421)
                plt.title(f"FF sample of {n_ff} events")
                plt.tight_layout()
                median = int(np.median(charge_median[select]))
                rms = np.std(charge_median[select])
                label = f"Median {median:3.2f}, std {rms:5.0f}"
                plt.xlabel("charge (ADC)", fontsize=20)
                plt.ylabel("pixels", fontsize=20)
                plt.hist(charge_median[select], bins=50, label=label)
                plt.legend()

                plt.subplot(422)
                plt.tight_layout()
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("charge std", fontsize=20)
                median = np.median(charge_std[select])
                rms = np.std(charge_std[select])
                label = f"Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(charge_std[select], bins=50, label=label)
                plt.legend()

                # pedestal charge
                plt.subplot(423)
                plt.tight_layout()
                plt.title(f"pedestal sample of {n_ped} events")
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("pedestal", fontsize=20)
                median = np.median(median_ped[select])
                rms = np.std(median_ped[select])
                label = f"Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(median_ped[select], bins=50, label=label)
                plt.legend()

                # pedestal std
                plt.subplot(424)
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("pedestal std", fontsize=20)
                median = np.median(ped_std[select])
                rms = np.std(ped_std[select])
                label = f"Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(ped_std[select], bins=50, label=label)
                plt.legend()

                # relative gain
                plt.subplot(425)
                plt.tight_layout()
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("relative signal", fontsize=20)
                median = np.median(gain_median[select])
                rms = np.std(gain_median[select])
                label = f"Relative gain {median:3.2f}, std {rms:5.2f}"
                plt.hist(gain_median[select], bins=50, label=label)
                plt.legend()

                # photon electrons
                plt.subplot(426)
                plt.tight_layout()
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("time corrections [ns]", fontsize=20)
                median = np.median(time_correction[select])
                rms = np.std(time_correction[select])
                label = f"Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(time_correction[select].value, bins=50, label=label)
                plt.legend()

                plt.subplots_adjust(top=0.92)
                # photon electrons
                plt.subplot(427)
                plt.tight_layout()
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("pe", fontsize=20)
                median = np.median(n_pe[select])
                rms = np.std(n_pe[select])
                label = f"Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(n_pe[select], bins=50, label=label)
                plt.legend()
                plt.subplots_adjust(top=0.92)

                # gain
                plt.subplot(428)
                plt.tight_layout()
                plt.ylabel("pixels", fontsize=20)
                plt.xlabel("flat-fielded gain [ADC/pe]", fontsize=20)
                denominator = dc_to_pe[select]
                numerator = 1.

                gain = np.divide(numerator, denominator, out=np.zeros_like(denominator), where=denominator != 0)
                median = np.median(gain)
                rms = np.std(gain)
                label = f"Median {median:3.2f}, std {rms:3.2f}"
                plt.hist(gain, bins=50, label=label)
                plt.legend()
                plt.subplots_adjust(top=0.92)

                pdf.savefig(plt.gcf())
                plt.close()
Example #16
0
    def __init__(self, path, max_events=None):
        """
        Reads simtelarray files utilising the SimTelEventSource from ctapipe

        Parameters
        ----------
        path : str
            Path to the simtel file
        max_events : int
            Maximum number of events to read from the file
        """
        super().__init__(path, max_events)

        try:
            from ctapipe.io import SimTelEventSource, EventSeeker
            from ctapipe.coordinates import EngineeringCameraFrame
        except ModuleNotFoundError:
            msg = "Cannot find ctapipe installation"
            raise ModuleNotFoundError(msg)

        try:
            from target_calib import CameraConfiguration
        except ModuleNotFoundError:
            msg = ("Cannot find TARGET libraries, please follow installation "
                   "instructions from https://forge.in2p3.fr/projects/gct/"
                   "wiki/Installing_CHEC_Software")
            raise ModuleNotFoundError(msg)

        self.path = path
        reader = SimTelEventSource(input_url=path,
                                   max_events=max_events,
                                   back_seekable=True)
        self.seeker = EventSeeker(reader)

        first_event = self.seeker[0]
        tels = list(first_event.r0.tels_with_data)
        self.tel = tels[0]
        shape = first_event.r0.tel[self.tel].waveform.shape
        _, self.n_pixels, self.n_samples = shape
        self.n_modules = self.n_pixels // 64

        n_modules = 32
        camera_version = "1.1.0"
        self._camera_config = CameraConfiguration(camera_version)
        tc_mapping = self._camera_config.GetMapping(n_modules == 1)
        self.mapping = get_clp_mapping_from_tc_mapping(tc_mapping)
        n_rows = self.mapping.metadata['n_rows']
        n_columns = self.mapping.metadata['n_columns']
        camera_geom = first_event.inst.subarray.tel[tels[0]].camera
        engineering_frame = EngineeringCameraFrame(n_mirrors=2)
        engineering_geom = camera_geom.transform_to(engineering_frame)
        pix_x = engineering_geom.pix_x.value
        pix_y = engineering_geom.pix_y.value
        row, col = get_row_column(pix_x, pix_y)
        camera_2d = np.zeros((n_rows, n_columns), dtype=np.int)
        camera_2d[row, col] = np.arange(self.n_pixels, dtype=np.int)
        self.pixel_order = camera_2d[self.mapping['row'], self.mapping['col']]

        self.reference_pulse_path = self._camera_config.GetReferencePulsePath()
        self.camera_version = self._camera_config.GetVersion()

        self._iev = None
        self._t_cpu = None
        self.mc = None
        self.pointing = None
        self.mcheader = None
Example #17
0
def plot(filename='longterm_dl1_check.h5'):

    # First read in the camera geometry:
    cam_description_table = \
        Table.read(filename, path='instrument/telescope/camera/LSTCam')
    camgeom = CameraGeometry.from_table(cam_description_table)
    engineering_geom = camgeom.transform_to(EngineeringCameraFrame())

    file = tables.open_file('longterm_dl1_check.h5')

    bokeh_output_file(Path(filename).with_suffix('.html'),
                      title='LST1 long-term DL1 data check')

    run_titles = []
    for i, run in enumerate(file.root.pixwise_runsummary.col('runnumber')):
        date = pd.to_datetime(file.root.pixwise_runsummary.col('time')[i],
                              origin='unix',
                              unit='s')
        run_titles.append('Run {0:05d}, {date}'.\
                          format(run,
                                 date = date.strftime("%b %d %Y %H:%M:%S")))

    runsummary = pd.read_hdf(filename, 'runsummary')
    page0 = Panel()
    fig_ped_rates = show_graph(
        x=pd.to_datetime(runsummary['time'], origin='unix', unit='s'),
        y=runsummary['num_pedestals'] / runsummary['elapsed_time'],
        xlabel='date',
        ylabel='Interleaved pedestals rate',
        ey=np.sqrt(runsummary['num_pedestals']) / runsummary['elapsed_time'],
        xtype='datetime',
        ytype='linear',
        point_labels=run_titles)
    fig_ff_rates = show_graph(
        x=pd.to_datetime(runsummary['time'], origin='unix', unit='s'),
        y=runsummary['num_flatfield'] / runsummary['elapsed_time'],
        xlabel='date',
        ylabel='Interleaved flat field rate',
        ey=np.sqrt(runsummary['num_flatfield']) / runsummary['elapsed_time'],
        xtype='datetime',
        ytype='linear',
        point_labels=run_titles)
    fig_cosmic_rates = show_graph(
        x=pd.to_datetime(runsummary['time'], origin='unix', unit='s'),
        y=runsummary['num_cosmics'] / runsummary['elapsed_time'],
        xlabel='date',
        ylabel='Cosmics rate',
        ey=np.sqrt(runsummary['num_cosmics']) / runsummary['elapsed_time'],
        xtype='datetime',
        ytype='linear',
        point_labels=run_titles)
    fig_muring_rates = show_graph(
        x=pd.to_datetime(runsummary['time'], origin='unix', unit='s'),
        y=runsummary['num_contained_mu_rings'] / runsummary['elapsed_time'],
        xlabel='date',
        ylabel='Contained mu-rings rate',
        ey=np.sqrt(runsummary['num_contained_mu_rings']) /
        runsummary['elapsed_time'],
        xtype='datetime',
        ytype='linear',
        point_labels=run_titles)

    pad_width = 550
    pad_height = 350
    row1 = [fig_ped_rates, fig_ff_rates]
    row2 = [fig_cosmic_rates, fig_muring_rates]
    grid0 = gridplot([row1, row2],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page0.child = grid0
    page0.title = 'Event rates'

    page0b = Panel()
    altmin = np.rad2deg(runsummary['min_altitude'])
    altmean = np.rad2deg(runsummary['mean_altitude'])
    altmax = np.rad2deg(runsummary['max_altitude'])
    fig_altitude = show_graph(x=pd.to_datetime(runsummary['time'],
                                               origin='unix',
                                               unit='s'),
                              y=altmean,
                              xlabel='date',
                              ylabel='Telescope altitude (mean, min, max)',
                              eylow=altmean - altmin,
                              eyhigh=altmax - altmean,
                              xtype='datetime',
                              ytype='linear',
                              point_labels=run_titles)
    fig_altitude.y_range = Range1d(altmin.min() * 0.95, altmax.max() * 1.05)
    row1 = [fig_altitude]
    grid0b = gridplot([row1],
                      sizing_mode=None,
                      plot_width=pad_width,
                      plot_height=pad_height)
    page0b.child = grid0b
    page0b.title = 'Pointing'

    page1 = Panel()
    pad_width = 350
    pad_height = 370
    mean = []
    stddev = []
    for item in file.root.pixwise_runsummary.col('ped_pix_charge_mean'):
        mean.append(item)
    for item in file.root.pixwise_runsummary.col('ped_pix_charge_stddev'):
        stddev.append(item)
    row1 = show_camera(np.array(mean), engineering_geom, pad_width, pad_height,
                       'Pedestals mean charge', run_titles)
    row2 = show_camera(np.array(stddev), engineering_geom, pad_width,
                       pad_height, 'Pedestals charge std dev', run_titles)
    grid1 = gridplot([row1, row2],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page1.child = grid1
    page1.title = 'Interleaved pedestals'

    page2 = Panel()
    mean = []
    stddev = []
    for item in file.root.pixwise_runsummary.col('ff_pix_charge_mean'):
        mean.append(item)
    for item in file.root.pixwise_runsummary.col('ff_pix_charge_stddev'):
        stddev.append(item)
    row1 = show_camera(np.array(mean), engineering_geom, pad_width, pad_height,
                       'Flat-Field mean charge (pe)', run_titles)
    row2 = show_camera(np.array(stddev), engineering_geom, pad_width,
                       pad_height, 'Flat-Field charge std dev (pe)',
                       run_titles)
    grid2 = gridplot([row1, row2],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page2.child = grid2
    page2.title = 'Interleaved flat field, charge'

    page3 = Panel()
    mean = []
    stddev = []
    for item in file.root.pixwise_runsummary.col('ff_pix_rel_time_mean'):
        mean.append(item)
    for item in file.root.pixwise_runsummary.col('ff_pix_rel_time_stddev'):
        stddev.append(item)
    row1 = show_camera(np.array(mean),
                       engineering_geom,
                       pad_width,
                       pad_height,
                       'Flat-Field mean relative time (ns)',
                       run_titles,
                       showlog=False)
    row2 = show_camera(np.array(stddev),
                       engineering_geom,
                       pad_width,
                       pad_height,
                       'Flat-Field rel. time std dev (ns)',
                       run_titles,
                       showlog=False)
    grid3 = gridplot([row1, row2],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page3.child = grid3
    page3.title = 'Interleaved flat field, time'

    page4 = Panel()
    pulse_fraction_above_10 = []
    pulse_fraction_above_30 = []
    for item in file.root.pixwise_runsummary.col(
            'cosmics_pix_fraction_pulses_above10'):
        pulse_fraction_above_10.append(item)
    for item in file.root.pixwise_runsummary.col(
            'cosmics_pix_fraction_pulses_above30'):
        pulse_fraction_above_30.append(item)

    row1 = show_camera(np.array(pulse_fraction_above_10), engineering_geom,
                       pad_width, pad_height,
                       'Cosmics, fraction of >10pe pulses', run_titles)
    row2 = show_camera(np.array(pulse_fraction_above_30), engineering_geom,
                       pad_width, pad_height,
                       'Cosmics, fraction of >30pe pulses', run_titles)

    grid4 = gridplot([row1, row2],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page4.child = grid4
    page4.title = 'Cosmics'

    file.close()

    page5 = Panel()
    pad_width = 550
    pad_height = 280
    fig_mu_effi = show_graph(x=pd.to_datetime(runsummary['time'],
                                              origin='unix',
                                              unit='s'),
                             y=runsummary['mu_effi_mean'],
                             xlabel='date',
                             ylabel='telescope efficiency from mu-rings',
                             ey=runsummary['mu_effi_stddev'] /
                             np.sqrt(runsummary['num_contained_mu_rings']),
                             xtype='datetime',
                             ytype='linear',
                             point_labels=run_titles)
    fig_mu_effi.y_range = Range1d(0., 1.1 * np.max(runsummary['mu_effi_mean']))

    fig_mu_width = show_graph(x=pd.to_datetime(runsummary['time'],
                                               origin='unix',
                                               unit='s'),
                              y=runsummary['mu_width_mean'],
                              xlabel='date',
                              ylabel='muon ring width (deg)',
                              ey=runsummary['mu_width_stddev'] /
                              np.sqrt(runsummary['num_contained_mu_rings']),
                              xtype='datetime',
                              ytype='linear',
                              point_labels=run_titles)
    fig_mu_width.y_range = Range1d(0.,
                                   1.1 * np.max(runsummary['mu_width_mean']))

    fig_mu_intensity = show_graph(x=pd.to_datetime(runsummary['time'],
                                                   origin='unix',
                                                   unit='s'),
                                  y=runsummary['mu_intensity_mean'],
                                  xlabel='date',
                                  ylabel='mean muon ring intensity (p.e.)',
                                  xtype='datetime',
                                  ytype='linear',
                                  point_labels=run_titles)
    fig_mu_intensity.y_range = \
        Range1d(0., 1.1 * np.max(runsummary['mu_intensity_mean']))

    fig_mu_hg_peak = show_graph(x=pd.to_datetime(runsummary['time'],
                                                 origin='unix',
                                                 unit='s'),
                                y=runsummary['mu_hg_peak_sample_mean'],
                                xlabel='date',
                                ey=runsummary['mu_hg_peak_sample_stddev'],
                                ylabel='HG global peak sample id (mean&RMS)',
                                xtype='datetime',
                                ytype='linear',
                                point_labels=run_titles)
    fig_mu_hg_peak.y_range = Range1d(0., 38.)
    fig_mu_lg_peak = show_graph(x=pd.to_datetime(runsummary['time'],
                                                 origin='unix',
                                                 unit='s'),
                                y=runsummary['mu_lg_peak_sample_mean'],
                                xlabel='date',
                                ey=runsummary['mu_lg_peak_sample_stddev'],
                                ylabel='LG global peak sample id (mean&RMS)',
                                xtype='datetime',
                                ytype='linear',
                                point_labels=run_titles)
    fig_mu_lg_peak.y_range = Range1d(0., 38.)
    row1 = [fig_mu_effi, fig_mu_width]
    row2 = [fig_mu_intensity]
    row3 = [fig_mu_hg_peak, fig_mu_lg_peak]

    grid5 = gridplot([row1, row2, row3],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page5.child = grid5
    page5.title = "Muons"

    page6 = Panel()
    pad_width = 550
    pad_height = 350
    fig_ped = show_graph(x=pd.to_datetime(runsummary['time'],
                                          origin='unix',
                                          unit='s'),
                         y=runsummary['ped_charge_mean'],
                         xlabel='date',
                         ylabel='Camera-averaged pedestal charge (pe/pixel)',
                         ey=runsummary['ped_charge_mean_err'],
                         xtype='datetime',
                         ytype='linear',
                         point_labels=run_titles)
    fig_ped.y_range = Range1d(0., 1.1 * np.max(runsummary['ped_charge_mean']))

    fig_ped_stddev = show_graph(x=pd.to_datetime(runsummary['time'],
                                                 origin='unix',
                                                 unit='s'),
                                y=runsummary['ped_charge_stddev'],
                                xlabel='date',
                                ylabel='Camera-averaged pedestal charge std '
                                'dev (pe/pixel)',
                                xtype='datetime',
                                ytype='linear',
                                point_labels=run_titles)
    fig_ped_stddev.y_range = \
        Range1d(0.,1.1*np.max(runsummary['ped_charge_stddev']))

    frac = runsummary['num_pedestals_after_cleaning'] / \
           runsummary['num_pedestals']
    err = np.sqrt(frac * (1 - frac) / runsummary['num_pedestals'])
    fig_ped_clean_fraction = show_graph(
        x=pd.to_datetime(runsummary['time'], origin='unix', unit='s'),
        y=frac,
        xlabel='date',
        ylabel='Fraction of pedestals surviving cleaning',
        ey=err,
        xtype='datetime',
        ytype='linear',
        point_labels=run_titles)

    row1 = [fig_ped, fig_ped_stddev]
    row2 = [fig_ped_clean_fraction]

    grid6 = gridplot([row1, row2],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page6.child = grid6
    page6.title = "Interleaved pedestals, averages"

    page7 = Panel()
    pad_width = 550
    pad_height = 280
    fig_flatfield = show_graph(x=pd.to_datetime(runsummary['time'],
                                                origin='unix',
                                                unit='s'),
                               y=runsummary['ff_charge_mean'],
                               xlabel='date',
                               ylabel='Cam-averaged FF Q (pe/pixel)',
                               ey=runsummary['ff_charge_mean_err'],
                               xtype='datetime',
                               ytype='linear',
                               point_labels=run_titles)
    fig_flatfield.y_range = Range1d(0.,
                                    1.1 * np.max(runsummary['ff_charge_mean']))

    fig_ff_stddev = show_graph(x=pd.to_datetime(runsummary['time'],
                                                origin='unix',
                                                unit='s'),
                               y=runsummary['ff_charge_stddev'],
                               xlabel='date',
                               ylabel='Cam-averaged FF Q std '
                               'dev (pe/pixel)',
                               xtype='datetime',
                               ytype='linear',
                               point_labels=run_titles)
    fig_ff_stddev.y_range = \
        Range1d(0.,1.1*np.max(runsummary['ff_charge_stddev']))

    fig_ff_time = show_graph(x=pd.to_datetime(runsummary['time'],
                                              origin='unix',
                                              unit='s'),
                             y=runsummary['ff_time_mean'],
                             xlabel='date',
                             ylabel='Cam-averaged FF time (ns)',
                             ey=runsummary['ff_time_mean_err'],
                             xtype='datetime',
                             ytype='linear',
                             point_labels=run_titles)

    fig_ff_time_std = show_graph(x=pd.to_datetime(runsummary['time'],
                                                  origin='unix',
                                                  unit='s'),
                                 y=runsummary['ff_time_stddev'],
                                 xlabel='date',
                                 ylabel='Cam-averaged FF t std '
                                 'dev (ns)',
                                 xtype='datetime',
                                 ytype='linear',
                                 point_labels=run_titles)
    fig_ff_rel_time_std = show_graph(x=pd.to_datetime(runsummary['time'],
                                                      origin='unix',
                                                      unit='s'),
                                     y=runsummary['ff_rel_time_stddev'],
                                     xlabel='date',
                                     ylabel='Cam-averaged FF '
                                     'rel. pix t std dev (ns)',
                                     xtype='datetime',
                                     ytype='linear',
                                     point_labels=run_titles)
    fig_ff_rel_time_std.y_range = \
        Range1d(0., np.max([1., runsummary['ff_rel_time_stddev'].max()]))

    row1 = [fig_flatfield, fig_ff_stddev]
    row2 = [fig_ff_time, fig_ff_time_std]
    row3 = [fig_ff_rel_time_std]

    grid7 = gridplot([row1, row2, row3],
                     sizing_mode=None,
                     plot_width=pad_width,
                     plot_height=pad_height)
    page7.child = grid7
    page7.title = "Interleaved FF, averages"

    tabs = Tabs(
        tabs=[page0, page0b, page1, page2, page3, page4, page5, page6, page7])
    show(column(Div(text='<h1> Long-term DL1 data check </h1>'), tabs))
Example #18
0
from ctapipe.visualization import CameraDisplay
from ctapipe.instrument import CameraGeometry
from ctapipe.coordinates import EngineeringCameraFrame
from ctapipe.image.toymodel import SkewedGaussian
import matplotlib.pyplot as plt
import astropy.units as u
import numpy as np

np.random.seed(1337)

geom = CameraGeometry.from_name('FACT')
geom = geom.transform_to(EngineeringCameraFrame())
model = SkewedGaussian(
    width=0.01 * u.m,
    length=0.03 * u.m,
    x=0.14 * u.m,
    y=0.07 * u.m,
    skewness=0.05,
    psi=30 * u.deg,
)
img, signal, noise = model.generate_image(geom, 1000, nsb_level_pe=2)

fig = plt.figure(figsize=(5, 5))
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xticks([])
ax.set_yticks([])
ax.set_xlabel('')
ax.set_ylabel('')
ax.set_axis_off()

disp = CameraDisplay(geom, image=img, ax=ax, cmap='inferno')
Example #19
0
def plot_pedestals(data_file,
                   pedestal_file,
                   run=0,
                   plot_file="none",
                   tel_id=1,
                   offset_value=400):
    """
     plot pedestal quantities quantities

     Parameters
     ----------
     data_file:   pedestal run

     pedestal_file:   file with drs4 corrections

     run: run number of data to be corrected

     plot_file:  name of output pdf file

     tel_id: id of the telescope

     offset_value: baseline off_set
     """

    # plot open pdf
    if plot_file != "none":
        pp = PdfPages(plot_file)

    plt.rc('font', size=15)

    # r0 calibrator
    r0_calib = LSTR0Corrections(pedestal_path=pedestal_file,
                                offset=offset_value,
                                tel_id=tel_id)

    # event_reader
    reader = event_source(data_file, max_events=1000)
    t = np.linspace(2, 37, 36)

    # configuration for the charge integrator
    charge_config = Config(
        {"FixedWindowSum": {
            "window_start": 12,
            "window_width": 12,
        }})
    # declare the pedestal component
    pedestal = PedestalIntegrator(tel_id=tel_id,
                                  sample_size=1000,
                                  sample_duration=1000000,
                                  charge_median_cut_outliers=[-10, 10],
                                  charge_std_cut_outliers=[-10, 10],
                                  charge_product="FixedWindowSum",
                                  config=charge_config,
                                  subarray=reader.subarray)

    for i, event in enumerate(reader):
        if tel_id != event.r0.tels_with_data[0]:
            raise Exception(
                f"Given wrong telescope id {tel_id}, files has id {event.r0.tels_with_data[0]}"
            )

        # move from R0 to R1
        r0_calib.calibrate(event)

        ok = pedestal.calculate_pedestals(event)
        if ok:
            ped_data = event.mon.tel[tel_id].pedestal
            break

    camera_geometry = reader.subarray.tels[tel_id].camera.geometry
    camera_geometry = camera_geometry.transform_to(EngineeringCameraFrame())
    # plot open pdf
    if plot_file != "none":
        pp = PdfPages(plot_file)

    plt.rc('font', size=15)

    ### first figure
    fig = plt.figure(1, figsize=(12, 24))
    plt.tight_layout()
    n_samples = charge_config["FixedWindowSum"]['window_width']
    fig.suptitle(f"Run {run}, integration on {n_samples} samples", fontsize=25)
    pad = 420

    image = ped_data.charge_median
    mask = ped_data.charge_median_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera_geometry)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        # disp.axes.text(lposx, 0, f'{channel[chan]} pedestal [ADC]', rotation=90)
        plt.title(f'{channel[chan]} pedestal [ADC]')
        disp.add_colorbar()

    image = ped_data.charge_std
    mask = ped_data.charge_std_outliers
    for chan in (np.arange(2)):
        pad += 1
        plt.subplot(pad)
        plt.tight_layout()
        disp = CameraDisplay(camera_geometry)
        mymin = np.median(image[chan]) - 2 * np.std(image[chan])
        mymax = np.median(image[chan]) + 2 * np.std(image[chan])
        disp.set_limits_minmax(mymin, mymax)
        disp.highlight_pixels(mask[chan], linewidth=2)
        disp.image = image[chan]
        disp.cmap = plt.cm.coolwarm
        # disp.axes.text(lposx, 0, f'{channel[chan]} pedestal std [ADC]', rotation=90)
        plt.title(f'{channel[chan]} pedestal std [ADC]')
        disp.add_colorbar()

    ###  histograms
    for chan in np.arange(2):
        mean_ped = ped_data.charge_mean[chan]
        ped_std = ped_data.charge_std[chan]

        # select good pixels
        select = np.logical_not(mask[chan])

        #fig.suptitle(f"Run {run} channel: {channel[chan]}", fontsize=25)
        pad += 1
        # pedestal charge
        plt.subplot(pad)
        plt.tight_layout()
        plt.ylabel('pixels')
        plt.xlabel(f'{channel[chan]} pedestal')
        median = np.median(mean_ped[select])
        rms = np.std(mean_ped[select])
        label = f"{channel[chan]} Median {median:3.2f}, std {rms:3.2f}"
        plt.hist(mean_ped[select], bins=50, label=label)
        plt.legend()
        pad += 1
        # pedestal std
        plt.subplot(pad)
        plt.ylabel('pixels')
        plt.xlabel(f'{channel[chan]} pedestal std')
        median = np.median(ped_std[select])
        rms = np.std(ped_std[select])
        label = f" Median {median:3.2f}, std {rms:3.2f}"
        plt.hist(ped_std[select], bins=50, label=label)
        plt.legend()

    plt.subplots_adjust(top=0.94)
    if plot_file != "none":

        pp.savefig()

    pix = 0
    pad = 420
    # plot corrected waveforms of first 8 events
    for i, ev in enumerate(reader):
        for chan in np.arange(2):

            if pad == 420:
                # new figure

                fig = plt.figure(ev.index.event_id, figsize=(12, 24))
                fig.suptitle(f"Run {run}, pixel {pix}", fontsize=25)
                plt.tight_layout()
            pad += 1
            plt.subplot(pad)

            plt.subplots_adjust(top=0.92)
            label = f"event {ev.index.event_id}, {channel[chan]}: R0"
            plt.step(t,
                     ev.r0.tel[tel_id].waveform[chan, pix, 2:38],
                     color="blue",
                     label=label)

            r0_calib.subtract_pedestal(ev, tel_id)
            label = "+ pedestal substraction"
            plt.step(t,
                     ev.r1.tel[tel_id].waveform[chan, pix, 2:38],
                     color="red",
                     alpha=0.5,
                     label=label)

            r0_calib.time_lapse_corr(ev, tel_id)
            r0_calib.interpolate_spikes(ev, tel_id)
            label = "+ dt corr + interp. spikes"
            plt.step(t,
                     ev.r1.tel[tel_id].waveform[chan, pix, 2:38],
                     alpha=0.5,
                     color="green",
                     label=label)
            plt.plot([0, 40], [offset_value, offset_value],
                     'k--',
                     label="offset")
            plt.xlabel("time sample [ns]")
            plt.ylabel("counts [ADC]")
            plt.legend()
            plt.ylim([-50, 500])

        if plot_file != "none" and pad == 428:
            pad = 420
            plt.subplots_adjust(top=0.92)
            pp.savefig()

        if i == 8:
            break

    if plot_file != "none":
        pp.close()
Example #20
0
    def from_location(
        clc,
        altaz_frame,
        stars,
        sdt,
        fov,
        focal_length,
        min_alt,
        pixsize,
        vmag_lim,
        minpixdist=5,
    ):
        log = daiquiri.getLogger(__name__)
        mask = sdt.vmag < vmag_lim
        sdt = sdt[mask]
        stars = stars[mask]

        path = os.path.join(
            root_dir,
            "caldata",
            "starcat_pixsize{:.3g}_fov{}_minpixdist{}_vmag{}.pkl".format(
                pixsize, fov, minpixdist, vmag_lim
            ),
        )
        if os.path.exists(path):
            with open(path, "rb") as f:
                try:
                    args = pickle.load(f)
                except Exception as e:
                    log.error(
                        "Error occured while loading star patterns from file.",
                        file=f,
                        error=e,
                    )
                    raise e
                return clc(**args)
        log.info(
            "No previously computed star pattern file found. Generating table now...."
        )
        star_patterns = {}

        for k, star in tqdm(enumerate(stars), total=len(stars)):

            telescope_pointing = SkyCoord(
                ra=star.ra, dec=star.dec, frame="icrs"  # ,altaz_frame,
            )
            tm_alt_az = telescope_pointing.transform_to(altaz_frame)
            engineering_frame = EngineeringCameraFrame(
                n_mirrors=2,
                location=altaz_frame.location,
                obstime=altaz_frame.obstime,
                focal_length=focal_length,
                telescope_pointing=tm_alt_az,
            )
            if tm_alt_az.alt.deg < min_alt:
                continue

            sep = telescope_pointing.separation(stars)
            ind = np.where(sep.deg < fov)[0]

            stars_eng = stars[ind].transform_to(engineering_frame)
            focus_star = stars_eng[np.where(ind == k)[0]]

            pf = np.array([focus_star.x.value[0], focus_star.y.value[0]])
            hipf = sdt.hip_number.values[k]
            vmagf = sdt.vmag.values[k]

            pattern = []
            for i, star1 in enumerate(stars_eng):
                if ind[i] == k:
                    continue
                hip = sdt.hip_number.values[ind][i]
                vmag = sdt.vmag.values[ind][i]
                pc = np.array([star1.x.value, star1.y.value])

                pattern.append((pf - pc, hip, vmag))

            pattern = sorted(pattern, key=lambda t: t[2])
            vmags = np.array([p[2] for p in pattern])
            hips = np.array([p[1] for p in pattern], dtype=np.uint32)
            pcs = np.array([p[0] for p in pattern])

            star_patterns[hipf] = StarPattern(
                hipf, vmagf, np.array([star.ra.rad, star.dec.rad]), pcs, vmags, hips, k
            )

        with open(path, "wb") as f:
            log.info("Generated star pattern table saved.", file=f)
            pickle.dump(
                dict(
                    star_coordinates=stars,
                    star_table=sdt,
                    patterns=star_patterns,
                    pixsize=pixsize,
                    minpixdist=minpixdist,
                    engineering_frame=engineering_frame,
                ),
                f,
            )

        return clc(stars, sdt, star_patterns, pixsize, minpixdist, engineering_frame)