def _run(self, simData): ra_pi_amp = np.zeros(np.size(simData), dtype=[('ra_pi_amp', 'float')]) dec_pi_amp = np.zeros(np.size(simData), dtype=[('dec_pi_amp', 'float')]) ra_geo1 = np.zeros(np.size(simData), dtype='float') dec_geo1 = np.zeros(np.size(simData), dtype='float') ra_geo = np.zeros(np.size(simData), dtype='float') dec_geo = np.zeros(np.size(simData), dtype='float') ra = simData[self.raCol] dec = simData[self.decCol] if self.raDecDeg: ra = np.radians(ra) dec = np.radians(dec) for i, ack in enumerate(simData): mtoa_params = palpy.mappa(2000., simData[self.dateCol][i]) # Object with a 1 arcsec parallax ra_geo1[i], dec_geo1[i] = palpy.mapqk(ra[i], dec[i], 0., 0., 1., 0., mtoa_params) # Object with no parallax ra_geo[i], dec_geo[i] = palpy.mapqk(ra[i], dec[i], 0., 0., 0., 0., mtoa_params) x_geo1, y_geo1 = self._gnomonic_project_toxy(ra_geo1, dec_geo1, ra, dec) x_geo, y_geo = self._gnomonic_project_toxy(ra_geo, dec_geo, ra, dec) ra_pi_amp[:] = np.degrees(x_geo1 - x_geo) * 3600. dec_pi_amp[:] = np.degrees(y_geo1 - y_geo) * 3600. simData['ra_pi_amp'] = ra_pi_amp simData['dec_pi_amp'] = dec_pi_amp return simData
def _run(self, simData, cols_present=False): if cols_present: # Column already present in data; assume it is correct and does not need recalculating. return simData ra_pi_amp = np.zeros(np.size(simData), dtype=[('ra_pi_amp', 'float')]) dec_pi_amp = np.zeros(np.size(simData), dtype=[('dec_pi_amp', 'float')]) ra_geo1 = np.zeros(np.size(simData), dtype='float') dec_geo1 = np.zeros(np.size(simData), dtype='float') ra_geo = np.zeros(np.size(simData), dtype='float') dec_geo = np.zeros(np.size(simData), dtype='float') ra = simData[self.raCol] dec = simData[self.decCol] if self.degrees: ra = np.radians(ra) dec = np.radians(dec) for i, ack in enumerate(simData): mtoa_params = palpy.mappa(2000., simData[self.dateCol][i]) # Object with a 1 arcsec parallax ra_geo1[i], dec_geo1[i] = palpy.mapqk(ra[i], dec[i], 0., 0., 1., 0., mtoa_params) # Object with no parallax ra_geo[i], dec_geo[i] = palpy.mapqk(ra[i], dec[i], 0., 0., 0., 0., mtoa_params) x_geo1, y_geo1 = self._gnomonic_project_toxy(ra_geo1, dec_geo1, ra, dec) x_geo, y_geo = self._gnomonic_project_toxy(ra_geo, dec_geo, ra, dec) # Return ra_pi_amp and dec_pi_amp in arcseconds. ra_pi_amp[:] = np.degrees(x_geo1-x_geo)*3600. dec_pi_amp[:] = np.degrees(y_geo1-y_geo)*3600. simData['ra_pi_amp'] = ra_pi_amp simData['dec_pi_amp'] = dec_pi_amp return simData
def _icrsFromAppGeo(ra, dec, epoch=2000.0, mjd=None): """ Convert the apparent geocentric position in (RA, Dec) to the mean position in the International Celestial Reference System (ICRS) This method undoes the effects of precession, annual aberration, and nutation. It is meant for mapping pointing RA and Dec (which presumably include the above effects) back to mean ICRS RA and Dec so that the user knows how to query a database of mean RA and Decs for objects observed at a given telescope pointing. WARNING: This method does not account for apparent motion due to parallax. This means it should not be used to invert the ICRS-to-apparent geocentric transformation for actual celestial objects. This method is only useful for mapping positions on a theoretical celestial sphere. This method works in radians. @param [in] ra in radians (apparent geocentric). Can be a numpy array or a number. @param [in] dec in radians (apparent geocentric). Can be a numpy array or a number. @param [in] epoch is the julian epoch (in years) of the equinox against which to measure RA (default: 2000.0) @param [in] mjd is an instantiation of the ModifiedJulianDate class representing the date of the observation @param [out] a 2-D numpy array in which the first row is the mean ICRS RA and the second row is the mean ICRS Dec (both in radians) """ are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "icrsFromAppGeo") # Define star independent mean to apparent place parameters # palpy.mappa calculates the star-independent parameters # needed to correct RA and Dec # e.g the Earth barycentric and heliocentric position and velocity, # the precession-nutation matrix, etc. # # arguments of palpy.mappa are: # epoch of mean equinox to be used (Julian) # # date (MJD) params = palpy.mappa(epoch, mjd.TDB) if are_arrays: raOut, decOut = palpy.ampqkVector(ra, dec, params) else: raOut, decOut = palpy.ampqk(ra, dec, params) return np.array([raOut, decOut])
def testParallax(self): """ This test will output a catalog of ICRS and observed positions. It will also output the quantities (proper motion, radial velocity, and parallax) needed to apply the transformaiton between the two. It will then run the catalog through PALPY and verify that the catalog generating code correctly applied the transformations. """ # create and write a catalog that performs astrometric transformations # on a cartoon star database cat = parallaxTestCatalog(self.starDBObject, obs_metadata=self.obs_metadata) parallaxName = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'scratchSpace', 'parallaxCatalog.sav') if os.path.exists(parallaxName): os.unlink(parallaxName) cat.write_catalog(parallaxName) data = np.genfromtxt(parallaxName, delimiter=',') self.assertGreater(len(data), 0) epoch = cat.db_obj.epoch mjd = cat.obs_metadata.mjd prms = pal.mappa(epoch, mjd.TDB) for vv in data: # run the PALPY routines that actuall do astrometry `by hand' and compare # the results to the contents of the catalog ra0 = np.radians(vv[0]) dec0 = np.radians(vv[1]) pmra = np.radians(vv[4]) pmdec = np.radians(vv[5]) rv = vv[6] px = vv[7] ra_apparent, dec_apparent = pal.mapqk(ra0, dec0, pmra, pmdec, px, rv, prms) ra_apparent = np.array([ra_apparent]) dec_apparent = np.array([dec_apparent]) raObserved, decObserved = _observedFromAppGeo(ra_apparent, dec_apparent, obs_metadata=cat.obs_metadata) self.assertAlmostEqual(raObserved[0], np.radians(vv[2]), 7) self.assertAlmostEqual(decObserved[0], np.radians(vv[3]), 7) if os.path.exists(parallaxName): os.unlink(parallaxName)
def testParallax(self): """ This test will output a catalog of ICRS and observed positions. It will also output the quantities (proper motion, radial velocity, and parallax) needed to apply the transformaiton between the two. It will then run the catalog through PALPY and verify that the catalog generating code correctly applied the transformations. """ #create and write a catalog that performs astrometric transformations #on a cartoon star database cat = parallaxTestCatalog(self.starDBObject, obs_metadata=self.obs_metadata) parallaxName = os.path.join(getPackageDir('sims_catUtils'), 'tests', 'scratchSpace', 'parallaxCatalog.sav') if os.path.exists(parallaxName): os.unlink(parallaxName) cat.write_catalog(parallaxName) data = numpy.genfromtxt(parallaxName,delimiter=',') self.assertGreater(len(data), 0) epoch = cat.db_obj.epoch mjd = cat.obs_metadata.mjd prms = pal.mappa(epoch, mjd.TDB) for vv in data: #run the PALPY routines that actuall do astrometry `by hand' and compare #the results to the contents of the catalog ra0 = numpy.radians(vv[0]) dec0 = numpy.radians(vv[1]) pmra = numpy.radians(vv[4]) pmdec = numpy.radians(vv[5]) rv = vv[6] px = vv[7] ra_apparent, dec_apparent = pal.mapqk(ra0, dec0, pmra, pmdec, px, rv, prms) ra_apparent = numpy.array([ra_apparent]) dec_apparent = numpy.array([dec_apparent]) raObserved, decObserved = _observedFromAppGeo(ra_apparent, dec_apparent, obs_metadata=cat.obs_metadata) self.assertAlmostEqual(raObserved[0],numpy.radians(vv[2]),7) self.assertAlmostEqual(decObserved[0],numpy.radians(vv[3]),7) if os.path.exists(parallaxName): os.unlink(parallaxName)
def _icrsFromAppGeo(ra, dec, epoch=2000.0, mjd = None): """ Convert the apparent geocentric position in (RA, Dec) to the mean position in the International Celestial Reference System (ICRS) This method undoes the effects of precession, annual aberration, and nutation. It is meant for mapping pointing RA and Dec (which presumably include the above effects) back to mean ICRS RA and Dec so that the user knows how to query a database of mean RA and Decs for objects observed at a given telescope pointing. This method works in radians. @param [in] ra in radians (apparent geocentric). Must be a numpy array. @param [in] dec in radians (apparent geocentric). Must be a numpy array. @param [in] epoch is the julian epoch (in years) of the equinox against which to measure RA (default: 2000.0) @param [in] mjd is an instantiation of the ModifiedJulianDate class representing the date of the observation @param [out] a 2-D numpy array in which the first row is the mean ICRS RA and the second row is the mean ICRS Dec (both in radians) """ # Define star independent mean to apparent place parameters # palpy.mappa calculates the star-independent parameters # needed to correct RA and Dec # e.g the Earth barycentric and heliocentric position and velocity, # the precession-nutation matrix, etc. # # arguments of palpy.mappa are: # epoch of mean equinox to be used (Julian) # # date (MJD) params = palpy.mappa(epoch, mjd.TDB) raOut, decOut = palpy.ampqkVector(ra, dec, params) return numpy.array([raOut, decOut])
def _solarRaDec(mjd, epoch=2000.0): """ Return the RA and Dec of the Sun in radians @param [in] mjd is the date (TDB) in question as an MJD @param [in] epoch is the mean epoch of the coordinate system (default is 2000.0) @param [out] RA of Sun in radians @param [out] Dec of Sun in radians """ params = palpy.mappa(epoch, mjd) # params[4:7] is a unit vector pointing from the Sun # to the Earth (see the docstring for palpy.mappa) return palpy.dcc2s(-1.0*params[4:7])
def _run(self, simData): ra_pi_amp = np.zeros(np.size(simData), dtype=[('ra_pi_amp','float')]) dec_pi_amp = np.zeros(np.size(simData), dtype=[('dec_pi_amp','float')]) ra_geo1 = np.zeros(np.size(simData), dtype='float') dec_geo1 = np.zeros(np.size(simData), dtype='float') ra_geo = np.zeros(np.size(simData), dtype='float') dec_geo = np.zeros(np.size(simData), dtype='float') for i,ack in enumerate(simData): mtoa_params = palpy.mappa(2000., simData[self.dateCol][i]) ra_geo1[i],dec_geo1[i] = palpy.mapqk(simData[self.raCol][i],simData[self.decCol][i], 0.,0.,1.,0.,mtoa_params) ra_geo[i],dec_geo[i] = palpy.mapqk(simData[self.raCol][i],simData[self.decCol][i], 0.,0.,0.,0.,mtoa_params) x_geo1,y_geo1 = self._gnomonic_project_toxy(ra_geo1, dec_geo1, simData[self.raCol],simData[self.decCol]) x_geo, y_geo = self._gnomonic_project_toxy(ra_geo, dec_geo, simData[self.raCol], simData[self.decCol]) ra_pi_amp[:] = np.degrees(x_geo1-x_geo)*3600. dec_pi_amp[:] = np.degrees(y_geo1-y_geo)*3600. simData['ra_pi_amp'] = ra_pi_amp simData['dec_pi_amp'] = dec_pi_amp return simData
def _solarRaDec(mjd, epoch=2000.0): """ Return the RA and Dec of the Sun in radians @param [in] mjd is the date represented as a ModifiedJulianDate object. @param [in] epoch is the mean epoch of the coordinate system (default is 2000.0) @param [out] RA of Sun in radians @param [out] Dec of Sun in radians """ params = palpy.mappa(epoch, mjd.TDB) # params[4:7] is a unit vector pointing from the Sun # to the Earth (see the docstring for palpy.mappa) return palpy.dcc2s(-1.0 * params[4:7])
def test_applyProperMotion(self): """ Compare the output of _applyProperMotion to control outputs generated by recreating the 'space motion' section of code from palMapqk.c in palpy/cextern/pal """ VF=0.21094502 pal_das2r=4.8481368110953599358991410235794797595635330237270e-6; numpy.random.seed(18) nSamples = 1000 mjdList = numpy.random.random_sample(20)*20000.0 + 45000.0 for mjd in mjdList: raList_icrs = numpy.random.random_sample(nSamples)*2.0*numpy.pi decList_icrs = (numpy.random.random_sample(nSamples)-0.5)*numpy.pi # stars' original position in Cartesian space x_list_icrs = numpy.cos(decList_icrs)*numpy.cos(raList_icrs) y_list_icrs = numpy.cos(decList_icrs)*numpy.sin(raList_icrs) z_list_icrs = numpy.sin(decList_icrs) pm_ra = (numpy.random.random_sample(nSamples)-0.5)*radiansFromArcsec(1.0) pm_dec = (numpy.random.random_sample(nSamples)-0.5)*radiansFromArcsec(1.0) px = numpy.random.random_sample(nSamples)*radiansFromArcsec(1.0) v_rad = numpy.random.random_sample(nSamples)*200.0 ra_list_pm, dec_list_pm = _applyProperMotion(raList_icrs, decList_icrs, pm_ra*numpy.cos(decList_icrs), pm_dec, px, v_rad, mjd=ModifiedJulianDate(TAI=mjd)) # stars' Cartesian position after proper motion is applied x_list_pm = numpy.cos(dec_list_pm)*numpy.cos(ra_list_pm) y_list_pm = numpy.cos(dec_list_pm)*numpy.sin(ra_list_pm) z_list_pm = numpy.sin(dec_list_pm) ############################################################### # The code below is copied from palMapqk.c in palpy/cextern/pal params = pal.mappa(2000.0, mjd) pmt = params[0] eb = numpy.array([params[1], params[2], params[3]]) pxr = px*pal_das2r w = VF*v_rad*pxr motion_per_year = numpy.array([-1.0*pm_ra*y_list_icrs - pm_dec*numpy.cos(raList_icrs)*numpy.sin(decList_icrs) + w*x_list_icrs, pm_ra*x_list_icrs - pm_dec*numpy.sin(raList_icrs)*numpy.sin(decList_icrs) + w*y_list_icrs, pm_dec*numpy.cos(decList_icrs) + w*z_list_icrs]) xyz_control = numpy.array([ x_list_icrs + pmt*motion_per_year[0] - pxr*eb[0], y_list_icrs + pmt*motion_per_year[1] - pxr*eb[1], z_list_icrs + pmt*motion_per_year[2] - pxr*eb[2] ]) xyz_norm = numpy.sqrt(numpy.power(xyz_control[0],2) + numpy.power(xyz_control[1],2) + numpy.power(xyz_control[2],2)) # stars' Cartesian position after applying the control proper motion method xyz_control[0] = xyz_control[0]/xyz_norm xyz_control[1] = xyz_control[1]/xyz_norm xyz_control[2] = xyz_control[2]/xyz_norm # this is the Cartesian distance between the stars' positions as found by _applyProperMotion # and the distance as found by the control proper motion code above distance = numpy.sqrt(numpy.power(x_list_pm-xyz_control[0],2) + numpy.power(y_list_pm-xyz_control[1],2) + numpy.power(z_list_pm-xyz_control[2],2)) # this is the Cartesian distance between the stars' original positions on the celestial sphere # and their positions after the control proper motion was applied correction = numpy.sqrt(numpy.power(xyz_control[0]-x_list_icrs,2) + numpy.power(xyz_control[1]-y_list_icrs,2) + numpy.power(xyz_control[2]-z_list_icrs,2)) dex = numpy.argmax(distance) msg = 'pm %e %e vr %e px %e; time %e; err %e arcsec; corr %e' % \ (arcsecFromRadians(pm_ra[dex]), arcsecFromRadians(pm_dec[dex]), v_rad[dex], arcsecFromRadians(px[dex]), pmt, arcsecFromRadians(distance[dex]), arcsecFromRadians(correction[dex])) self.assertLess((distance/correction).max(), 0.01, msg=msg)
def _appGeoFromICRS(ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None, epoch=2000.0, mjd = None): """ Convert the mean position (RA, Dec) in the International Celestial Reference System (ICRS) to the mean apparent geocentric position units: ra (radians), dec (radians), pm_ra (radians/year), pm_dec (radians/year), parallax (radians), v_rad (km/sec; positive if receding), epoch (Julian years) @param [in] ra in radians (ICRS). Must be a numpy array. @param [in] dec in radians (ICRS). Must be a numpy array. @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in radians/year @param [in] pm_dec is dec proper motion in radians/year @param [in] parallax in radians @param [in] v_rad is radial velocity in km/sec (positive if the object is receding) @param [in] epoch is the julian epoch (in years) of the equinox against which to measure RA (default: 2000.0) @param [in] mjd is an instantiation of the ModifiedJulianDate class representing the date of the observation @param [out] a 2-D numpy array in which the first row is the apparent geocentric RAand the second row is the apparent geocentric Dec (both in radians) """ if mjd is None: raise RuntimeError("cannot call appGeoFromICRS; mjd is None") if len(ra) != len(dec): raise RuntimeError('appGeoFromICRS: len(ra) %d len(dec) %d ' % (len(ra),len(dec))) if pm_ra is None: pm_ra=numpy.zeros(len(ra)) if pm_dec is None: pm_dec=numpy.zeros(len(ra)) if v_rad is None: v_rad=numpy.zeros(len(ra)) if parallax is None: parallax=numpy.zeros(len(ra)) # Define star independent mean to apparent place parameters # palpy.mappa calculates the star-independent parameters # needed to correct RA and Dec # e.g the Earth barycentric and heliocentric position and velocity, # the precession-nutation matrix, etc. # # arguments of palpy.mappa are: # epoch of mean equinox to be used (Julian) # # date (MJD) prms=palpy.mappa(epoch, mjd.TDB) # palpy.mapqk does a quick mean to apparent place calculation using # the output of palpy.mappa # # Taken from the palpy source code (palMap.c which calls both palMappa and palMapqk): # The accuracy is sub-milliarcsecond, limited by the # precession-nutation model (see palPrenut for details). # because PAL and ERFA expect proper motion in terms of "coordinate # angle; not true angle" (as stated in erfa/starpm.c documentation) pm_ra_corrected = pm_ra/numpy.cos(dec) raOut,decOut = palpy.mapqkVector(ra,dec,pm_ra_corrected,pm_dec,arcsecFromRadians(parallax),v_rad,prms) return numpy.array([raOut,decOut])
def _appGeoFromICRS(ra, dec, pm_ra=None, pm_dec=None, parallax=None, v_rad=None, epoch=2000.0, mjd=None): """ Convert the mean position (RA, Dec) in the International Celestial Reference System (ICRS) to the mean apparent geocentric position units: ra (radians), dec (radians), pm_ra (radians/year), pm_dec (radians/year), parallax (radians), v_rad (km/sec; positive if receding), epoch (Julian years) @param [in] ra in radians (ICRS). Can be a numpy array or a number. @param [in] dec in radians (ICRS). Can be a numpy array or a number. @param [in] pm_ra is ra proper motion multiplied by cos(Dec) in radians/year. Can be a numpy array or a number or None. @param [in] pm_dec is dec proper motion in radians/year. Can be a numpy array or a number or None. @param [in] parallax in radians. Can be a numpy array or a number or None. @param [in] v_rad is radial velocity in km/sec (positive if the object is receding). Can be a numpy array or a number or None. @param [in] epoch is the julian epoch (in years) of the equinox against which to measure RA (default: 2000.0) @param [in] mjd is an instantiation of the ModifiedJulianDate class representing the date of the observation @param [out] a 2-D numpy array in which the first row is the apparent geocentric RAand the second row is the apparent geocentric Dec (both in radians) """ if mjd is None: raise RuntimeError("cannot call appGeoFromICRS; mjd is None") include_px = False if (pm_ra is not None or pm_dec is not None or v_rad is not None or parallax is not None): include_px = True if isinstance(ra, np.ndarray): fill_value = np.zeros(len(ra), dtype=float) else: fill_value = 0.0 if pm_ra is None: pm_ra = fill_value if pm_dec is None: pm_dec = fill_value if v_rad is None: v_rad = fill_value if parallax is None: parallax = fill_value are_arrays = _validate_inputs( [ra, dec, pm_ra, pm_dec, v_rad, parallax], ['ra', 'dec', 'pm_ra', 'pm_dec', 'v_rad', 'parallax'], "appGeoFromICRS") else: are_arrays = _validate_inputs([ra, dec], ['ra', 'dec'], "appGeoFromICRS") # Define star independent mean to apparent place parameters # palpy.mappa calculates the star-independent parameters # needed to correct RA and Dec # e.g the Earth barycentric and heliocentric position and velocity, # the precession-nutation matrix, etc. # # arguments of palpy.mappa are: # epoch of mean equinox to be used (Julian) # # date (MJD) prms = palpy.mappa(epoch, mjd.TDB) # palpy.mapqk does a quick mean to apparent place calculation using # the output of palpy.mappa # # Taken from the palpy source code (palMap.c which calls both palMappa and palMapqk): # The accuracy is sub-milliarcsecond, limited by the # precession-nutation model (see palPrenut for details). if include_px: # because PAL and ERFA expect proper motion in terms of "coordinate # angle; not true angle" (as stated in erfa/starpm.c documentation) pm_ra_corrected = pm_ra / np.cos(dec) if are_arrays: if include_px: raOut, decOut = palpy.mapqkVector(ra, dec, pm_ra_corrected, pm_dec, arcsecFromRadians(parallax), v_rad, prms) else: raOut, decOut = palpy.mapqkzVector(ra, dec, prms) else: if include_px: raOut, decOut = palpy.mapqk(ra, dec, pm_ra_corrected, pm_dec, arcsecFromRadians(parallax), v_rad, prms) else: raOut, decOut = palpy.mapqkz(ra, dec, prms) return np.array([raOut, decOut])