Example #1
0
def rotationWrtHorizon(platepar):
    """ Given the platepar, compute the rotation of the FOV with respect to the horizon.

    Arguments:
        pletepar: [Platepar object] Input platepar.
    Return:
        rot_angle: [float] Rotation w.r.t. horizon (degrees).
    """

    # Image coordiantes of the center
    img_mid_w = platepar.X_res / 2
    img_mid_h = platepar.Y_res / 2

    # Image coordinate slighty right of the center (horizontal)
    img_up_w = img_mid_w + 10
    img_up_h = img_mid_h

    # Compute apparent alt/az in the epoch of date from X,Y
    jd_arr, ra_arr, dec_arr, _ = xyToRaDecPP(2 * [jd2Date(platepar.JD)],
                                             [img_mid_w, img_up_w],
                                             [img_mid_h, img_up_h], [1, 1],
                                             platepar,
                                             extinction_correction=False)
    azim_mid, alt_mid = trueRaDec2ApparentAltAz(ra_arr[0], dec_arr[0],
                                                jd_arr[0], platepar.lat,
                                                platepar.lon,
                                                platepar.refraction)
    azim_up, alt_up = trueRaDec2ApparentAltAz(ra_arr[1], dec_arr[1], jd_arr[1],
                                              platepar.lat, platepar.lon,
                                              platepar.refraction)

    # Compute the rotation wrt horizon (deg)
    rot_angle = np.degrees(np.arctan2(alt_up - alt_mid, azim_up - azim_mid))

    # Wrap output to <-180, 180] range
    if rot_angle > 180:
        rot_angle -= 360

    return rot_angle
Example #2
0
def extinctionCorrectionApparentToTrue(mags, x_data, y_data, jd, platepar):
    """ Compute true magnitudes by applying extinction correction to apparent magnitudes.

    Arguments:
        mags: [list] A list of apparent magnitudes.
        x_data: [list] A list of pixel columns.
        y_data: [list] A list of pixel rows.
        jd: [float] Julian date.
        platepar: [Platepar object]
    Return:
        corrected_mags: [list] A list of extinction corrected mangitudes.
    """

    ### Compute star elevations above the horizon (epoch of date, true) ###

    # Compute RA/Dec in J2000
    _, ra_data, dec_data, _ = xyToRaDecPP(len(x_data)*[jd2Date(jd)], x_data, y_data, len(x_data)*[1], \
        platepar, extinction_correction=False)

    # Compute elevation above the horizon
    elevation_data = []
    for ra, dec in zip(ra_data, dec_data):

        # Compute elevation
        _, elev = trueRaDec2ApparentAltAz(ra, dec, jd, platepar.lat,
                                          platepar.lon)

        if elev < 0:
            elev = 0

        elevation_data.append(elev)

    ### ###

    # Correct catalog magnitudes for extinction
    extinction_correction = atmosphericExtinctionCorrection(np.array(elevation_data), platepar.elev) \
        - atmosphericExtinctionCorrection(90, platepar.elev)
    corrected_mags = np.array(
        mags) - platepar.extinction_scale * extinction_correction

    return corrected_mags
Example #3
0
def extinctionCorrectionTrueToApparent(catalog_mags, ra_data, dec_data, jd,
                                       platepar):
    """ Compute apparent magnitudes by applying extinction correction to catalog magnitudes.

    Arguments:
        catalog_mags: [list] A list of catalog magnitudes.
        ra_data: [list] A list of catalog right ascensions (J2000) in degrees.
        dec_data: [list] A list of catalog declinations (J2000) in degrees.
        jd: [float] Julian date.
        platepar: [Platepar object]
    Return:
        corrected_catalog_mags: [list] Extinction corrected catalog magnitudes.
    """

    ### Compute star elevations above the horizon (epoch of date, true) ###

    # Compute elevation above the horizon
    elevation_data = []
    for ra, dec in zip(ra_data, dec_data):

        # Compute elevation
        _, elev = trueRaDec2ApparentAltAz(ra, dec, jd, platepar.lat,
                                          platepar.lon)

        if elev < 0:
            elev = 0

        elevation_data.append(elev)

    ### ###

    # Correct catalog magnitudes for extinction
    extinction_correction = atmosphericExtinctionCorrection(np.array(elevation_data), platepar.elev) \
        - atmosphericExtinctionCorrection(90, platepar.elev)
    corrected_catalog_mags = np.array(
        catalog_mags) + platepar.extinction_scale * extinction_correction

    return corrected_catalog_mags
Example #4
0
def applyPlateparToCentroids(ff_name,
                             fps,
                             meteor_meas,
                             platepar,
                             add_calstatus=False):
    """ Given the meteor centroids and a platepar file, compute meteor astrometry and photometry (RA/Dec,
        alt/az, mag).
    Arguments:
        ff_name: [str] Name of the FF file with the meteor.
        fps: [float] Frames per second of the video.
        meteor_meas: [list] A list of [calib_status, frame_n, x, y, ra, dec, azim, elev, inten, mag].
        platepar: [Platepar instance] Platepar which will be used for astrometry and photometry.
    Keyword arguments:
        add_calstatus: [bool] Add a column with calibration status at the beginning. False by default.
    Return:
        meteor_picks: [ndarray] A numpy 2D array of: [frames, X_data, Y_data, RA_data, dec_data, az_data,
        alt_data, level_data, magnitudes]
    """

    meteor_meas = np.array(meteor_meas)

    # Add a line which is indicating the calibration status
    if add_calstatus:
        meteor_meas = np.c_[np.ones((meteor_meas.shape[0], 1)), meteor_meas]

    # Remove all entries where levels are equal to or smaller than 0, unless all are zero
    level_data = meteor_meas[:, 8]
    if np.any(level_data):
        meteor_meas = meteor_meas[level_data > 0, :]

    # Extract frame number, x, y, intensity
    frames = meteor_meas[:, 1]
    X_data = meteor_meas[:, 2]
    Y_data = meteor_meas[:, 3]
    level_data = meteor_meas[:, 8]

    # Get the beginning time of the FF file
    time_beg = filenameToDatetime(ff_name)

    # Calculate time data of every point
    time_data = []
    for frame_n in frames:
        t = time_beg + datetime.timedelta(seconds=frame_n / fps)
        time_data.append([
            t.year, t.month, t.day, t.hour, t.minute, t.second,
            int(t.microsecond / 1000)
        ])

    # Convert image cooredinates to RA and Dec, and do the photometry
    JD_data, RA_data, dec_data, magnitudes = xyToRaDecPP(np.array(time_data), X_data, Y_data, \
        level_data, platepar)

    # Compute azimuth and altitude of centroids
    az_data = np.zeros_like(RA_data)
    alt_data = np.zeros_like(RA_data)

    for i in range(len(az_data)):

        jd = JD_data[i]
        ra_tmp = RA_data[i]
        dec_tmp = dec_data[i]

        # Alt and az are kept in the J2000 epoch, which is the CAMS standard!
        az_tmp, alt_tmp = trueRaDec2ApparentAltAz(ra_tmp, dec_tmp, jd,
                                                  platepar.lat, platepar.lon)

        az_data[i] = az_tmp
        alt_data[i] = alt_tmp

    # print(ff_name, cam_code, meteor_No, fps)
    # print(X_data, Y_data)
    # print(RA_data, dec_data)
    # print('------------------------------------------')

    # Construct the meteor measurements array
    meteor_picks = np.c_[frames, X_data, Y_data, RA_data, dec_data, az_data, alt_data, level_data, \
        magnitudes]

    return meteor_picks
Example #5
0
    def fitAstrometry(self, jd, img_stars, catalog_stars, first_platepar_fit=False):
        """ Fit astrometric parameters to the list of star image and celectial catalog coordinates.
        At least 4 stars are needed to fit the rigid body parameters, and 12 to fit the distortion.
        New parameters are saved to the given object.
        Arguments:
            jd: [float] Julian date of the image.
            img_stars: [list] A list of (x, y, intensity_sum) entires for every star.
            catalog_stars: [list] A list of (ra, dec, mag) entries for every star (degrees).
        Keyword arguments:
            first_platepar_fit: [bool] Fit a platepar from scratch. False by default.
        """


        def _calcImageResidualsAstro(params, platepar, jd, catalog_stars, img_stars):
            """ Calculates the differences between the stars on the image and catalog stars in image
                coordinates with the given astrometrical solution.
            """

            # Extract fitting parameters
            ra_ref, dec_ref, pos_angle_ref, F_scale = params

            img_x, img_y, _ = img_stars.T

            pp_copy = copy.deepcopy(platepar)

            pp_copy.RA_d = ra_ref
            pp_copy.dec_d = dec_ref
            pp_copy.pos_angle_ref = pos_angle_ref
            pp_copy.F_scale = F_scale

            # Get image coordinates of catalog stars
            catalog_x, catalog_y, catalog_mag = getCatalogStarsImagePositions(catalog_stars, jd, pp_copy)

            # Calculate the sum of squared distances between image stars and catalog stars
            dist_sum = np.sum((catalog_x - img_x)**2 + (catalog_y - img_y)**2)

            return dist_sum


        def _calcSkyResidualsAstro(params, platepar, jd, catalog_stars, img_stars):
            """ Calculates the differences between the stars on the image and catalog stars in sky
                coordinates with the given astrometrical solution.
            """

            # Extract fitting parameters
            ra_ref, dec_ref, pos_angle_ref, F_scale = params

            pp_copy = copy.deepcopy(platepar)

            pp_copy.RA_d = ra_ref
            pp_copy.dec_d = dec_ref
            pp_copy.pos_angle_ref = pos_angle_ref
            pp_copy.F_scale = F_scale

            img_x, img_y, _ = img_stars.T

            # Get image coordinates of catalog stars
            ra_array, dec_array = getPairedStarsSkyPositions(img_x, img_y, jd, pp_copy)

            ra_catalog, dec_catalog, _ = catalog_stars.T

            # Compute the sum of the angular separation
            separation_sum = np.sum(angularSeparation(np.radians(ra_array), np.radians(dec_array), \
                np.radians(ra_catalog), np.radians(dec_catalog))**2)


            return separation_sum



        def _calcImageResidualsDistortion(params, platepar, jd, catalog_stars, img_stars, dimension):
            """ Calculates the differences between the stars on the image and catalog stars in image
                coordinates with the given astrometrical solution.
            Arguments:
                ...
                dimension: [str] 'x' for X polynomial fit, 'y' for Y polynomial fit
            """

            # Set distortion parameters
            pp_copy = copy.deepcopy(platepar)

            if (dimension == 'x') or (dimension == 'radial'):
                pp_copy.x_poly_rev = params
                pp_copy.y_poly_rev = np.zeros(12)

            else:
                pp_copy.x_poly_rev = np.zeros(12)
                pp_copy.y_poly_rev = params


            img_x, img_y, _ = img_stars.T


            # Get image coordinates of catalog stars
            catalog_x, catalog_y, catalog_mag = getCatalogStarsImagePositions(catalog_stars, jd, pp_copy)


            # Calculate the sum of squared distances between image stars and catalog stars, per every
            #   dimension
            if dimension == 'x':
                dist_sum = np.sum((catalog_x - img_x)**2)

            elif dimension == 'y':
                dist_sum = np.sum((catalog_y - img_y)**2)

            # Minimization for the radial distortion
            else:
                dist_sum = np.sum((catalog_x - img_x)**2 + (catalog_y - img_y)**2)



            return dist_sum


        def _calcSkyResidualsDistortion(params, platepar, jd, catalog_stars, img_stars, dimension):
            """ Calculates the differences between the stars on the image and catalog stars in sky
                coordinates with the given astrometrical solution.
            Arguments:
                ...
                dimension: [str] 'x' for X polynomial fit, 'y' for Y polynomial fit
            """

            pp_copy = copy.deepcopy(platepar)

            if (dimension == 'x') or (dimension == 'radial'):
                pp_copy.x_poly_fwd = params

            else:
                pp_copy.y_poly_fwd = params


            img_x, img_y, _ = img_stars.T

            # Get image coordinates of catalog stars
            ra_array, dec_array = getPairedStarsSkyPositions(img_x, img_y, jd, pp_copy)

            ra_catalog, dec_catalog, _ = catalog_stars.T

            # Compute the sum of the angular separation
            separation_sum = np.sum(angularSeparation(np.radians(ra_array), np.radians(dec_array), \
                np.radians(ra_catalog), np.radians(dec_catalog))**2)

            return separation_sum


        # print('ASTRO', _calcImageResidualsAstro([self.RA_d, self.dec_d,
        #     self.pos_angle_ref, self.F_scale],  catalog_stars, img_stars))

        # print('DIS_X', _calcImageResidualsDistortion(self.x_poly_rev,  catalog_stars, \
        #     img_stars, 'x'))

        # print('DIS_Y', _calcImageResidualsDistortion(self.y_poly_rev,  catalog_stars, \
        #     img_stars, 'y'))


        ### ASTROMETRIC PARAMETERS FIT ###

        # Initial parameters for the astrometric fit
        p0 = [self.RA_d, self.dec_d, self.pos_angle_ref, self.F_scale]

        # Fit the astrometric parameters using the reverse transform for reference
        res = scipy.optimize.minimize(_calcImageResidualsAstro, p0, \
            args=(self, jd, catalog_stars, img_stars), method='SLSQP')

        # # Fit the astrometric parameters using the forward transform for reference
        #   WARNING: USING THIS MAKES THE FIT UNSTABLE
        # res = scipy.optimize.minimize(_calcSkyResidualsAstro, p0, args=(self, jd, \
        #     catalog_stars, img_stars), method='Nelder-Mead')

        # Update fitted astrometric parameters
        self.RA_d, self.dec_d, self.pos_angle_ref, self.F_scale = res.x

        # Recalculate FOV centre
        self.az_centre, self.alt_centre = trueRaDec2ApparentAltAz(self.RA_d, self.dec_d, self.JD, self.lat, self.lon)
        ### ###


        ### DISTORTION FIT ###

        # fit the polynomial distortion parameters if there are more than 12 stars picked
        if self.distortion_type.startswith("poly"):
            min_fit_stars = 12

        # fit the radial distortion parameters if there are more than 7 stars picked
        else:
            min_fit_stars = 7


        if len(img_stars) >= min_fit_stars:

            ### REVERSE MAPPING FIT ###

            # Fit the polynomial distortion
            if self.distortion_type.startswith("poly"):

                # Fit distortion parameters in X direction, reverse mapping
                res = scipy.optimize.minimize(_calcImageResidualsDistortion, self.x_poly_rev, \
                    args=(self, jd, catalog_stars, img_stars, 'x'), method='Nelder-Mead', \
                    options={'maxiter': 10000, 'adaptive': True})

                # Exctact fitted X polynomial
                self.x_poly_rev = res.x

                # Fit distortion parameters in Y direction, reverse mapping
                res = scipy.optimize.minimize(_calcImageResidualsDistortion, self.y_poly_rev, \
                    args=(self, jd, catalog_stars, img_stars, 'y'), method='Nelder-Mead', \
                    options={'maxiter': 10000, 'adaptive': True})

                # Extract fitted Y polynomial
                self.y_poly_rev = res.x


            # Fit radial distortion
            else:

                # Fit the radial distortion - the X polynomial is used to store the fit paramters
                res = scipy.optimize.minimize(_calcImageResidualsDistortion, self.x_poly_rev, \
                    args=(self, jd, catalog_stars, img_stars, 'radial'), method='Nelder-Mead', \
                    options={'maxiter': 10000, 'adaptive': True})

                # IMPORTANT NOTE - the X polynomial is used to store the fit paramters
                self.x_poly_rev = res.x


                # Force distortion centre to image centre
                if self.force_distortion_centre:
                    self.x_poly_rev[0] = 0.5
                    self.x_poly_rev[1] = 0.5

                # Force aspect ratio to 0 if axes are set to be equal
                if self.equal_aspect:
                    self.x_poly_rev[2] = 0

                # Set all parameters not used by the radial fit to 0
                n_params = int(self.distortion_type[-1])
                self.x_poly_rev[(n_params + 2):] *= 0


            ### ###



            # If this is the first fit of the distortion, set the forward parametrs to be equal to the reverse
            if first_platepar_fit:

                self.x_poly_fwd = np.array(self.x_poly_rev)
                self.y_poly_fwd = np.array(self.y_poly_rev)


            ### FORWARD MAPPING FIT ###

            # Fit the polynomial distortion
            if self.distortion_type.startswith("poly"):

                # Fit distortion parameters in X direction, forward mapping
                res = scipy.optimize.minimize(_calcSkyResidualsDistortion, self.x_poly_fwd, \
                    args=(self, jd, catalog_stars, img_stars, 'x'), method='Nelder-Mead', \
                    options={'maxiter': 10000, 'adaptive': True})

                # Extract fitted X polynomial
                self.x_poly_fwd = res.x


                # Fit distortion parameters in Y direction, forward mapping
                res = scipy.optimize.minimize(_calcSkyResidualsDistortion, self.y_poly_fwd, \
                    args=(self, jd, catalog_stars, img_stars, 'y'), method='Nelder-Mead', \
                    options={'maxiter': 10000, 'adaptive': True})

                # IMPORTANT NOTE - the X polynomial is used to store the fit paramters
                self.y_poly_fwd = res.x


            # Fit the radial distortion
            else:

                # Fit the radial distortion - the X polynomial is used to store the fit paramters
                res = scipy.optimize.minimize(_calcSkyResidualsDistortion, self.x_poly_fwd, \
                    args=(self, jd, catalog_stars, img_stars, 'radial'), method='Nelder-Mead', \
                    options={'maxiter': 10000, 'adaptive': True})

                # Extract fitted X polynomial
                self.x_poly_fwd = res.x


                # Force distortion centre to image centre
                if self.force_distortion_centre:
                    self.x_poly_rev[0] = 0.5
                    self.x_poly_rev[1] = 0.5

                # Force aspect ratio to 0 if axes are set to be equal
                if self.equal_aspect:
                    self.x_poly_fwd[2] = 0

                # Set all parameters not used by the radial fit to 0
                n_params = int(self.distortion_type[-1])
                self.x_poly_fwd[(n_params + 2):] *= 0

            ### ###

        else:
            print('Too few stars to fit the distortion, only the astrometric parameters where fitted!')


        # Set the list of stars used for the fit to the platepar
        fit_star_list = []
        for img_coords, cat_coords in zip(img_stars, catalog_stars):

            # Store time, image coordinate x, y, intensity, catalog ra, dec, mag
            fit_star_list.append([jd] + img_coords.tolist() + cat_coords.tolist())


        self.star_list = fit_star_list


        # Set the flag to indicate that the platepar was manually fitted
        self.auto_check_fit_refined = False
        self.auto_recalibrated = False
Example #6
0
def updateRaDecGrid(grid, platepar):
    """
    Updates the values of grid to form a right ascension and declination grid

    Arguments:
        grid: [pg.PlotCurveItem]
        platepar: [Platepar object]

    """
    # Estimate RA,dec of the centre of the FOV
    _, RA_c, dec_c, _ = xyToRaDecPP([jd2Date(platepar.JD)],
                                    [platepar.X_res / 2], [platepar.Y_res / 2],
                                    [1],
                                    platepar,
                                    extinction_correction=False)

    # azim_centre, alt_centre = trueRaDec2ApparentAltAz(RA_c, dec_c, platepar.JD, platepar.lat, platepar.lon)

    # Compute FOV size
    fov_radius = np.hypot(*computeFOVSize(platepar))

    # Determine gridline frequency (double the gridlines if the number is < 4eN)
    grid_freq = 10**np.floor(np.log10(fov_radius))
    if 10**(np.log10(fov_radius) - np.floor(np.log10(fov_radius))) < 4:
        grid_freq /= 2

    # Set a maximum grid frequency of 15 deg
    if grid_freq > 15:
        grid_freq = 15

    # Grid plot density
    plot_dens = grid_freq / 100

    ra_grid_arr = np.arange(0, 360, grid_freq)
    dec_grid_arr = np.arange(-90, 90, grid_freq)

    x = []
    y = []
    cuts = []

    # Plot the celestial parallel grid (circles)
    for dec_grid in dec_grid_arr:
        ra_grid_plot = np.arange(0, 360, plot_dens)
        dec_grid_plot = np.zeros_like(ra_grid_plot) + dec_grid

        # Compute alt/az
        az_grid_plot, alt_grid_plot = trueRaDec2ApparentAltAz(
            ra_grid_plot, dec_grid_plot, platepar.JD, platepar.lat,
            platepar.lon, platepar.refraction)

        # Filter out points below the horizon  and outside the FOV
        filter_arr = (alt_grid_plot > 0)  # & (angularSeparation(alt_centre,
        # azim_centre,
        # alt_grid_plot,
        # az_grid_plot) < fov_radius)
        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Compute image coordinates for every grid celestial parallel
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, platepar.JD,
                                     platepar)

        filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (
            y_grid >= 0) & (y_grid <= platepar.Y_res)
        x_grid = x_grid[filter_arr]
        y_grid = y_grid[filter_arr]

        x.extend(x_grid)
        y.extend(y_grid)
        cuts.append(len(x) - 1)

    # Plot the celestial meridian grid (outward lines)
    for ra_grid in ra_grid_arr:
        dec_grid_plot = np.arange(-90, 90, plot_dens)  # how close to horizon
        ra_grid_plot = np.zeros_like(dec_grid_plot) + ra_grid

        # Compute alt/az
        az_grid_plot, alt_grid_plot = trueRaDec2ApparentAltAz(
            ra_grid_plot, dec_grid_plot, platepar.JD, platepar.lat,
            platepar.lon, platepar.refraction)

        # Filter out points below the horizon
        filter_arr = (alt_grid_plot > 0)  #& (angularSeparation(alt_centre,
        # azim_centre,
        # alt_grid_plot,
        # az_grid_plot) < fov_radius)
        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Compute image coordinates for every grid celestial parallel
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, platepar.JD,
                                     platepar)

        filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (
            y_grid >= 0) & (y_grid <= platepar.Y_res)
        x_grid = x_grid[filter_arr]
        y_grid = y_grid[filter_arr]

        x.extend(x_grid)
        y.extend(y_grid)
        cuts.append(len(x) - 1)

    # horizon
    az_horiz_arr = np.arange(0, 360, plot_dens)
    alt_horiz_arr = np.zeros_like(az_horiz_arr)
    ra_horiz_plot, dec_horiz_plot = apparentAltAz2TrueRADec(
        az_horiz_arr, alt_horiz_arr, platepar.JD, platepar.lat, platepar.lon,
        platepar.refraction)

    x_horiz, y_horiz = raDecToXYPP(ra_horiz_plot, dec_horiz_plot, platepar.JD,
                                   platepar)

    filter_arr = (x_horiz >= 0) & (x_horiz <= platepar.X_res) & (
        y_horiz >= 0) & (y_horiz <= platepar.Y_res)
    x_horiz = x_horiz[filter_arr]
    y_horiz = y_horiz[filter_arr]

    x.extend(x_horiz)
    y.extend(y_horiz)
    cuts.append(len(x) - 1)

    r = 15  # adjust this parameter if you see extraneous lines
    # disconnect lines that are distant (unfinished circles had straight lines completing them)
    for i in range(len(x) - 1):
        if (x[i] - x[i + 1])**2 + (y[i] - y[i + 1])**2 > r**2:
            cuts.append(i)

    # convert cuts into connect
    connect = np.full(len(x), 1)
    if len(connect) > 0:
        for i in cuts:
            connect[i] = 0

    grid.setData(x=x, y=y, connect=connect)
Example #7
0
def updateAzAltGrid(grid, platepar):
    """
    Updates the values of grid to form an azimuth and altitude grid on a pyqtgraph plot.

    Arguments:
        grid: [pg.PlotCurveItem]
        platepar: [Platepar object]

    """

    ### COMPUTE FOV CENTRE ###

    # Estimate RA,dec of the centre of the FOV
    _, RA_c, dec_c, _ = xyToRaDecPP([jd2Date(platepar.JD)], [platepar.X_res/2], [platepar.Y_res/2], [1], \
                                    platepar, extinction_correction=False)

    # Compute alt/az of FOV centre
    azim_centre, alt_centre = trueRaDec2ApparentAltAz(RA_c[0], dec_c[0], platepar.JD, platepar.lat, \
        platepar.lon)

    ### ###

    # Compute FOV size
    fov_radius = getFOVSelectionRadius(platepar)

    # Determine gridline frequency (double the gridlines if the number is < 4eN)
    grid_freq = 10**np.floor(np.log10(2 * fov_radius))
    if 10**(np.log10(2 * fov_radius) - np.floor(np.log10(2 * fov_radius))) < 4:
        grid_freq /= 2

    # Set a maximum grid frequency of 15 deg
    if grid_freq > 15:
        grid_freq = 15

    # Grid plot density
    plot_dens = grid_freq / 100

    # Generate a grid of all azimuths and altitudes
    alt_grid_arr = np.arange(0, 90, grid_freq)
    az_grid_arr = np.arange(0, 360, grid_freq)

    x = []
    y = []
    cuts = []

    # Altitude lines
    for alt_grid in alt_grid_arr:

        # Keep the altitude fixed and plot all azimuth lines
        az_grid_plot = np.arange(0, 360, plot_dens)
        alt_grid_plot = np.zeros_like(az_grid_plot) + alt_grid

        # Filter out all lines outside the FOV
        filter_arr = np.degrees(angularSeparation(np.radians(azim_centre), np.radians(alt_centre), \
            np.radians(az_grid_plot), np.radians(alt_grid_plot))) <= fov_radius

        az_grid_plot = az_grid_plot[filter_arr]
        alt_grid_plot = alt_grid_plot[filter_arr]

        # Compute image coordinates
        ra_grid_plot, dec_grid_plot = apparentAltAz2TrueRADec(az_grid_plot, alt_grid_plot, platepar.JD, \
            platepar.lat, platepar.lon, platepar.refraction)
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, platepar.JD,
                                     platepar)

        # Filter out all points outside the image
        filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (
            y_grid >= 0) & (y_grid <= platepar.Y_res)
        x_grid = x_grid[filter_arr]
        y_grid = y_grid[filter_arr]

        x.extend(x_grid)
        y.extend(y_grid)
        cuts.append(len(x) - 1)

    # Azimuth lines
    for az_grid in az_grid_arr:

        # Keep the azimuth fixed and plot all altitude lines
        alt_grid_plot = np.arange(0, 90 + plot_dens, plot_dens)
        az_grid_plot = np.zeros_like(alt_grid_plot) + az_grid

        # Filter out all lines outside the FOV
        filter_arr = np.degrees(angularSeparation(np.radians(azim_centre), np.radians(alt_centre), \
            np.radians(az_grid_plot), np.radians(alt_grid_plot))) <= fov_radius

        az_grid_plot = az_grid_plot[filter_arr]
        alt_grid_plot = alt_grid_plot[filter_arr]

        # Compute image coordinates
        ra_grid_plot, dec_grid_plot = apparentAltAz2TrueRADec(az_grid_plot, alt_grid_plot, platepar.JD, \
            platepar.lat, platepar.lon, platepar.refraction)
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, platepar.JD,
                                     platepar)

        # Filter out all points outside the image
        filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (
            y_grid >= 0) & (y_grid <= platepar.Y_res)
        x_grid = x_grid[filter_arr]
        y_grid = y_grid[filter_arr]

        x.extend(x_grid)
        y.extend(y_grid)
        cuts.append(len(x) - 1)

    r = 15  # adjust this parameter if you see extraneous lines
    # disconnect lines that are distant (unfinished circles had straight lines completing them)
    for i in range(len(x) - 1):
        if (x[i] - x[i + 1])**2 + (y[i] - y[i + 1])**2 > r**2:
            cuts.append(i)

    connect = np.full(len(x), 1)
    for i in cuts[:-1]:
        connect[i] = 0

    grid.setData(x=x, y=y, connect=connect)
Example #8
0
def updateRaDecGrid(grid, platepar):
    """
    Updates the values of grid to form a right ascension and declination grid on a pyqtgraph plot.

    Arguments:
        grid: [pg.PlotCurveItem]
        platepar: [Platepar object]

    """

    ### COMPUTE FOV CENTRE ###

    # Estimate RA,dec of the centre of the FOV
    _, RA_c, dec_c, _ = xyToRaDecPP([jd2Date(platepar.JD)], [platepar.X_res/2], [platepar.Y_res/2], [1], \
                                    platepar, extinction_correction=False)

    # Compute alt/az of FOV centre
    azim_centre, alt_centre = trueRaDec2ApparentAltAz(RA_c[0], dec_c[0], platepar.JD, platepar.lat, \
        platepar.lon)

    ### ###

    # Compute FOV size
    fov_radius = getFOVSelectionRadius(platepar)

    # Determine gridline frequency (double the gridlines if the number is < 4eN)
    grid_freq = 10**np.floor(np.log10(2 * fov_radius))
    if 10**(np.log10(2 * fov_radius) - np.floor(np.log10(2 * fov_radius))) < 4:
        grid_freq /= 2

    # Set a maximum grid frequency of 15 deg
    if grid_freq > 15:
        grid_freq = 15

    # Grid plot density
    plot_dens = grid_freq / 100

    # Make an array of RA and Dec
    ra_grid_arr = np.arange(0, 360, grid_freq)
    dec_grid_arr = np.arange(-90, 90, grid_freq)

    x = []
    y = []
    cuts = []

    # Generate points for the celestial parallels grid
    for dec_grid in dec_grid_arr:

        # Keep the declination fixed and evaluate all right ascensions
        ra_grid_plot = np.arange(0, 360, plot_dens)
        dec_grid_plot = np.zeros_like(ra_grid_plot) + dec_grid

        # Compute alt/az
        az_grid_plot, alt_grid_plot = trueRaDec2ApparentAltAz(ra_grid_plot, dec_grid_plot, platepar.JD, \
            platepar.lat, platepar.lon, platepar.refraction)

        # Filter out points below the horizon and outside the FOV
        filter_arr = (alt_grid_plot >= 0) & (np.degrees(angularSeparation(np.radians(azim_centre), \
            np.radians(alt_centre), np.radians(az_grid_plot), np.radians(alt_grid_plot))) <= fov_radius)

        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Compute image coordinates for every grid celestial parallel
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, platepar.JD,
                                     platepar)

        # Filter out all points outside the image
        filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (
            y_grid >= 0) & (y_grid <= platepar.Y_res)
        x_grid = x_grid[filter_arr]
        y_grid = y_grid[filter_arr]

        # Add points to the list
        x.extend(x_grid)
        y.extend(y_grid)
        cuts.append(len(x) - 1)

    # Generate points for the celestial meridian grid
    for ra_grid in ra_grid_arr:

        # Keep the RA fixed and evaluate all declinations
        dec_grid_plot = np.arange(-90, 90 + plot_dens, plot_dens)
        ra_grid_plot = np.zeros_like(dec_grid_plot) + ra_grid

        # Compute alt/az
        az_grid_plot, alt_grid_plot = trueRaDec2ApparentAltAz(ra_grid_plot, dec_grid_plot, platepar.JD, \
            platepar.lat, platepar.lon, platepar.refraction)

        # Filter out points below the horizon
        filter_arr = (alt_grid_plot >= 0) & (np.degrees(angularSeparation(np.radians(azim_centre), \
            np.radians(alt_centre), np.radians(az_grid_plot), np.radians(alt_grid_plot))) <= fov_radius)
        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Compute image coordinates for every grid celestial parallel
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, platepar.JD,
                                     platepar)

        # Filter out points outside the image
        filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (
            y_grid >= 0) & (y_grid <= platepar.Y_res)
        x_grid = x_grid[filter_arr]
        y_grid = y_grid[filter_arr]

        x.extend(x_grid)
        y.extend(y_grid)
        cuts.append(len(x) - 1)

    # Generate points for the horizon
    az_horiz_arr = np.arange(0, 360, plot_dens)
    alt_horiz_arr = np.zeros_like(az_horiz_arr)
    ra_horiz_plot, dec_horiz_plot = apparentAltAz2TrueRADec(az_horiz_arr, alt_horiz_arr, platepar.JD, \
        platepar.lat, platepar.lon, platepar.refraction)

    # Filter out all horizon points outside the FOV
    filter_arr = np.degrees(angularSeparation(np.radians(alt_centre), np.radians(azim_centre), \
        np.radians(alt_horiz_arr), np.radians(az_horiz_arr))) <= fov_radius

    ra_horiz_plot = ra_horiz_plot[filter_arr]
    dec_horiz_plot = dec_horiz_plot[filter_arr]

    # Compute image coordinates of the horizon
    x_horiz, y_horiz = raDecToXYPP(ra_horiz_plot, dec_horiz_plot, platepar.JD,
                                   platepar)

    # Filter out all horizon points outside the image
    filter_arr = (x_horiz >= 0) & (x_horiz <= platepar.X_res) & (
        y_horiz >= 0) & (y_horiz <= platepar.Y_res)
    x_horiz = x_horiz[filter_arr]
    y_horiz = y_horiz[filter_arr]

    x.extend(x_horiz)
    y.extend(y_horiz)
    cuts.append(len(x) - 1)

    r = 15  # adjust this parameter if you see extraneous lines
    # disconnect lines that are distant (unfinished circles had straight lines completing them)
    for i in range(len(x) - 1):
        if (x[i] - x[i + 1])**2 + (y[i] - y[i + 1])**2 > r**2:
            cuts.append(i)

    # convert cuts into connect
    connect = np.full(len(x), 1)
    if len(connect) > 0:
        for i in cuts:
            connect[i] = 0

    grid.setData(x=x, y=y, connect=connect)
Example #9
0
def addEquatorialGrid(plt_handle, platepar, jd):
    """ Given the plot handle containing the image, the function plots an equatorial grid.
        Arguments:
            plt_handle: [pyplot instance]
            platepar: [Platepar object]
            jd: [float] Julian date of the image. 
        Return:
            plt_handle: [pyplot instance] Pyplot instance with the added grid.
    """

    # Estimate RA,dec of the centre of the FOV
    _, RA_c, dec_c, _ = xyToRaDecPP([jd2Date(jd)], [platepar.X_res / 2],
                                    [platepar.Y_res / 2], [1],
                                    platepar,
                                    extinction_correction=False)

    RA_c = RA_c[0]
    dec_c = dec_c[0]

    # Compute FOV centre alt/az
    azim_centre, alt_centre = trueRaDec2ApparentAltAz(RA_c, dec_c, jd,
                                                      platepar.lat,
                                                      platepar.lon)

    # Compute FOV size
    fov_radius = getFOVSelectionRadius(platepar)

    # Determine gridline frequency (double the gridlines if the number is < 4eN)
    grid_freq = 10**np.floor(np.log10(2 * fov_radius))
    if 10**(np.log10(2 * fov_radius) - np.floor(np.log10(2 * fov_radius))) < 4:
        grid_freq /= 2

    # Set a maximum grid frequency of 15 deg
    if grid_freq > 15:
        grid_freq = 15

    # Grid plot density
    plot_dens = grid_freq / 100

    # Compute the range of declinations to consider
    dec_min = platepar.dec_d - fov_radius
    if dec_min < -90:
        dec_min = -90

    dec_max = platepar.dec_d + fov_radius
    if dec_max > 90:
        dec_max = 90

    ra_grid_arr = np.arange(0, 360, grid_freq)
    dec_grid_arr = np.arange(-90, 90, grid_freq)

    # Filter out the dec grid for min/max declination
    dec_grid_arr = dec_grid_arr[(dec_grid_arr >= dec_min)
                                & (dec_grid_arr <= dec_max)]

    # Plot the celestial parallel grid
    for dec_grid in dec_grid_arr:

        ra_grid_plot = np.arange(0, 360, plot_dens)
        dec_grid_plot = np.zeros_like(ra_grid_plot) + dec_grid

        # Compute alt/az
        az_grid_plot, alt_grid_plot = trueRaDec2ApparentAltAz(ra_grid_plot, dec_grid_plot, jd, platepar.lat, \
            platepar.lon)

        # Filter out points below the horizon  and outside the FOV
        filter_arr = (alt_grid_plot > 0) & (np.degrees(angularSeparation(np.radians(azim_centre), \
            np.radians(alt_centre), np.radians(az_grid_plot), np.radians(alt_grid_plot))) <= fov_radius)
        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Find gaps in continuity and break up plotting individual lines
        gap_indices = np.argwhere(
            np.abs(ra_grid_plot[1:] - ra_grid_plot[:-1]) > fov_radius)
        if len(gap_indices):

            ra_grid_plot_list = []
            dec_grid_plot_list = []

            # Separate gridlines with large gaps
            prev_gap_indx = 0
            for entry in gap_indices:

                gap_indx = entry[0]

                ra_grid_plot_list.append(ra_grid_plot[prev_gap_indx:gap_indx +
                                                      1])
                dec_grid_plot_list.append(
                    dec_grid_plot[prev_gap_indx:gap_indx + 1])

                prev_gap_indx = gap_indx

            # Add the last segment
            ra_grid_plot_list.append(ra_grid_plot[prev_gap_indx + 1:-1])
            dec_grid_plot_list.append(dec_grid_plot[prev_gap_indx + 1:-1])

        else:
            ra_grid_plot_list = [ra_grid_plot]
            dec_grid_plot_list = [dec_grid_plot]

        # Plot all grid segments
        for ra_grid_plot, dec_grid_plot in zip(ra_grid_plot_list,
                                               dec_grid_plot_list):

            # Compute image coordinates for every grid celestial parallel
            x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, jd,
                                         platepar)

            # Plot the grid
            plt_handle.plot(x_grid,
                            y_grid,
                            color='w',
                            alpha=0.2,
                            zorder=2,
                            linewidth=0.5,
                            linestyle='dotted')

    # Plot the celestial meridian grid
    for ra_grid in ra_grid_arr:

        dec_grid_plot = np.arange(-90, 90, plot_dens)
        ra_grid_plot = np.zeros_like(dec_grid_plot) + ra_grid

        # Filter out the dec grid
        filter_arr = (dec_grid_plot >= dec_min) & (dec_grid_plot <= dec_max)
        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Compute alt/az
        az_grid_plot, alt_grid_plot = trueRaDec2ApparentAltAz(ra_grid_plot, dec_grid_plot, jd, platepar.lat, \
            platepar.lon)

        # Filter out points below the horizon
        filter_arr = (alt_grid_plot > 0) & (np.degrees(angularSeparation(np.radians(azim_centre), \
            np.radians(alt_centre), np.radians(az_grid_plot), np.radians(alt_grid_plot))) <= fov_radius)
        ra_grid_plot = ra_grid_plot[filter_arr]
        dec_grid_plot = dec_grid_plot[filter_arr]

        # Compute image coordinates for every grid celestial parallel
        x_grid, y_grid = raDecToXYPP(ra_grid_plot, dec_grid_plot, jd, platepar)

        # # Filter out everything outside the FOV
        # filter_arr = (x_grid >= 0) & (x_grid <= platepar.X_res) & (y_grid >= 0) & (y_grid <= platepar.Y_res)
        # x_grid = x_grid[filter_arr]
        # y_grid = y_grid[filter_arr]

        # Plot the grid
        plt_handle.plot(x_grid,
                        y_grid,
                        color='w',
                        alpha=0.2,
                        zorder=2,
                        linewidth=0.5,
                        linestyle='dotted')

    return plt_handle