diff --git a/src/lightkurve/missions/prf.py b/src/lightkurve/missions/prf.py index 1cce567..8398454 100644 --- a/src/lightkurve/missions/prf.py +++ b/src/lightkurve/missions/prf.py @@ -11,12 +11,13 @@ class PRF(ABC): """Abstract PRF class for working with Point Spread Functions""" - @abstractmethod def __init__( self, + supersamp_prf: npt.ArrayLike target_locations: Union[List[Tuple], Tuple] = (5.5, 5.5), origin: Tuple = (0, 0), shape: Tuple = (11, 11), + ): """ A generic base class object for PRFs. Not to be used directly. @@ -37,6 +38,8 @@ def __init__( self.origin_row, self.origin_column = origin self.shape = shape self.target_row, self.target_column = np.atleast_2d(target_locations) + self.col_coord, self.row_coord, self.interpolate = _prepare_prf(supersamp_prf) + def __repr__(self): return "PRF Base Class" @@ -195,13 +198,13 @@ def evaluate( Parameters ---------- center_col, center_row : float - Column and row coordinates of the center + Column and row coordinates of the center scale : float - Pixel scale stretch parameter, can be used to account for focus changes. - Values > 1 stretch the image, Values < 1 make the PRF more compact. - E.g. a scale value of 2 will double the PRF footprint. + Pixel scale stretch parameter, can be used to account for focus changes. + Values > 1 stretch the image, Values < 1 make the PRF more compact. + E.g. a scale value of 2 will double the PRF footprint. rotation_angle : float - Rotation angle in radians + Rotation angle in radians Returns ------- @@ -424,13 +427,44 @@ def _prf_model( ) return np.expand_dims(prf_model, axis=0) - def interpolate(self, row_grid, column_grid, dx=None, dy=None, grid=True): - raise NotImplementedError + #def interpolate(self, row_grid, column_grid, dx=None, dy=None, grid=True): + # raise NotImplementedError - @abstractmethod - def _prepare_prf(self): - """Method to open PRF files for given mission""" - pass + def _prepare_prf(self, supersamp_prf: npt.ArrayLike, + crval1p: float, + crval2p: float, + cdelt1p: float, + cdelt2p: float), + pixel_offset: float = 0.5: + """ + + pixel_offset: 0.5 offset necessary for TESS and Kepler + """ + + PRFcol = np.arange(pixel_offset, np.shape(supersampled_prf)[1] + pixel_offset) + PRFrow = np.arange(pixel_offset, np.shape(supersampled_prf)[0] + pixel_offset) + PRFcol = (PRFcol - np.size(PRFcol) / 2) * cdelt1p + PRFrow = (PRFrow - np.size(PRFrow) / 2) * cdelt2p + + # interpolate the calibrated PRF shape to the target position + rowdim, coldim = self.shape[0], self.shape[1] + ref_column = self.column + pixel_offset * coldim + ref_row = self.row + pixel_offset * rowdim + + + supersamp_prf /= np.nansum(supersamp_prf) * cdelt1p * cdelt2p + + # location of the data image centered on the PRF image (in PRF pixel units) + col_coord = np.arange(self.column + pixel_offset, self.column + coldim + pixel_offset) + row_coord = np.arange(self.row + pixel_offset, self.row + rowdim + pixel_offset) + # x-axis correspond to row-axis in scipy.RectBivariate + # not to be confused with our convention, in which the + # x-axis correspond to the column-axis + interpolate = scipy.interpolate.RectBivariateSpline( + PRFrow, PRFcol, supersamp_prf + ) + + return col_coord, row_coord, interpolate @abstractmethod def _read_prf_calibration_file(self): diff --git a/tests/test_prf.py b/tests/test_prf.py new file mode 100644 index 0000000..52ff32a --- /dev/null +++ b/tests/test_prf.py @@ -0,0 +1,51 @@ +""" +Tests for the functionality of the generic PRF module +""" +from lightkurve import PRF +# Create a grid that is super-sampled relative to true PRF pixels - to do this, creae a 2D Gaussian/exponential funcition + +def make2DGaussian(size, fwhm = 3, center=None): + """ Make a square gaussian kernel. + + size is the length of a side of the square + fwhm is full-width-half-maximum, which + can be thought of as an effective radius. + """ + + x = np.arange(0, size, 1, float) + y = x[:,np.newaxis] + + if center is None: + x0 = y0 = size // 2 + else: + x0 = center[0] + y0 = center[1] + + return np.exp(-4*np.log(2) * ((x-x0)**2 + (y-y0)**2) / fwhm**2) + +generic_prf = PRF() #initialize model here +supersample_factor=10 +supersampled_prf = make2DGaussian(prf_model.shape[0]*supersample_factor, fwhm=prf_model.shape[0]*supersample_factor/3) + +# Need to flesh out _prepare_prf() +prf_model = generic_prf.evaluate() + + +class PRF(object): + + def __init__(self, prf_image:np.NDArray, row, column): + + + +class KeplerPRF(PRF): + """Grabs the PRF files from the right place online + Intializes them from files correctly""" + def __init__(self, ..., quarter, channel): + # open file + # initialize super class + + + +# Add check for completeness and contamination +# check the aperture matches expectation +