def LN_ICRS_to_AltAz(self,ra=None,dec=None,ln_pressure_qfe=None,ln_temperature=None,ln_humidity=None,obstime=None,mount_set_icrs=True): ln_pos_eq.ra=ra ln_pos_eq.dec=dec if mount_set_icrs: # libnova corrections for catalog data ... # ToDo missing see Jean Meeus, Astronomical Algorithms, chapter 23 # proper motion # annual paralax (0".8) # gravitational deflection of light (0".003) ln.ln_get_equ_prec(byref(ln_pos_eq), c_double(obstime.jd), byref(ln_pos_eq_pr)) ln_pos_eq_nut=self.LN_nutation_meeus(eq_pr=ln_pos_eq_pr,JD=obstime.jd) ln.ln_get_equ_aber(byref(ln_pos_eq_nut), c_double(obstime.jd), byref(ln_pos_eq_ab)) ln.ln_get_hrz_from_equ(byref(ln_pos_eq_ab), byref(self.ln_obs), c_double(obstime.jd), byref(ln_pos_aa_ab)) # here we use QFE not pressure at sea level! # E.g. at Dome-C this formula: # ln_pressure=ln_see_pres * pow(1. - (0.0065 * ln_alt) / 288.15, (9.80665 * 0.0289644) / (8.31447 * 0.0065)); # is not precise. if self.refraction_method is None: d_alt_deg=ln.ln_get_refraction_adj(c_double(ln_pos_aa_ab.alt),c_double(ln_pressure_qfe),c_double(ln_temperature)) else: d_alt_deg=180./np.pi* self.refraction_method(alt=ln_pos_aa_ab.alt,tem=ln_temperature,pre=ln_pressure_qfe,hum=ln_humidity) else: # ... but not for the star position as measured in mount frame ln.ln_get_hrz_from_equ(byref(ln_pos_eq), byref(self.ln_obs), c_double(obstime.jd), byref(ln_pos_aa_ab)); d_alt_deg=0. a_az=Longitude(ln_pos_aa_ab.az,u.deg) a_az.wrap_at(0.*u.degree) a_alt=Latitude(ln_pos_aa_ab.alt + d_alt_deg,u.deg) pos_aa=SkyCoord(az=a_az.radian,alt=a_alt.radian,unit=(u.radian,u.radian),frame='altaz',location=self.obs,obstime=obstime,obswl=0.5*u.micron, pressure=ln_pressure_qfe*u.hPa,temperature=ln_temperature*u.deg_C,relative_humidity=ln_humidity) return pos_aa
def coordinates(self, coord_type='world', origin=0, mode='center'): """ Sky coordinate images. Parameters ---------- coord_type : {'world', 'pix', 'skycoord'} Which type of coordinates to return. origin : {0, 1} Pixel coordinate origin. mode : {'center', 'edges'} Return coordinate values at the pixels edges or pixel centers. """ if mode == 'center': y, x = np.indices(self.data.shape) elif mode == 'edges': shape = self.data.shape[0] + 1, self.data.shape[1] + 1 y, x = np.indices(shape) y, x = y - 0.5, x - 0.5 else: raise ValueError('Invalid mode to compute coordinates.') if coord_type == 'pix': return x, y else: xsky, ysky = self.wcs.wcs_pix2world(x, y, origin) l, b = Longitude(xsky, unit='deg'), Latitude(ysky, unit='deg') l = l.wrap_at('180d') if coord_type == 'world': return l.degree, b.degree elif coord_type == 'skycoord': return l, b else: raise ValueError("Not a valid coordinate type. Choose either" " 'world', 'pix' or 'skycoord'.")
def calculate(self): ephem_location = ephem.Observer() ephem_location.lat = self.location.latitude.to(u.rad) / u.rad ephem_location.lon = self.location.longitude.to(u.rad) / u.rad ephem_location.elevation = self.location.height / u.meter ephem_location.date = ephem.Date(self.time.datetime) if self.data is None: self.alt = Latitude([], unit=u.deg) self.az = Longitude([], unit=u.deg) self.names = Column([], dtype=np.str) self.vmag = Column([]) else: ra = Longitude((self.data['RAh'], self.data['RAm'], self.data['RAs']), u.h) dec = Latitude((np.core.defchararray.add(self.data['DE-'], self.data['DEd'].astype(str)).astype(int), self.data['DEm'], self.data['DEs']), u.deg) c = SkyCoord(ra, dec, frame='icrs') altaz = c.transform_to(AltAz(obstime=self.time, location=self.location)) self.alt = altaz.alt self.az = altaz.az self.names = self.data['Name'] self.vmag = self.data['Vmag'] for ephemeris in self.ephemerides: ephemeris.compute(ephem_location) self.vmag = self.vmag.insert(0, ephemeris.mag) self.alt = self.alt.insert(0, (ephemeris.alt.znorm * u.rad).to(u.deg)) self.az = self.az.insert(0, (ephemeris.az * u.rad).to(u.deg)) self.names = self.names.insert(0, ephemeris.name) return self.names, self.vmag, self.alt, self.az
def gmst2time(gmst, time): """ Converts a Greenwich Mean Sidereal Time to UTC time, for a given date. Parameters ---------- gmst: ~float Greenwich Mean Siderial Time (hours) time : astropy.time.Time UT date+time Returns ------- astropy.time.Time object with closest UT day+time at which siderial time is correct. """ dgmst = Longitude(gmst - time2gmst(time)) return time+TimeDelta(dgmst.to(u.hourangle).value*0.9972695663/24., format='jd')
def pixel_to_data(self, x, y, origin=0): """ Convert a pixel coordinate to a data (world) coordinate by using `~astropy.wcs.WCS.wcs_pix2world`. Parameters ---------- x : float Pixel coordinate of the CTYPE1 axis. (Normally solar-x). y : float Pixel coordinate of the CTYPE2 axis. (Normally solar-y). origin : int Origin of the top-left corner. i.e. count from 0 or 1. Normally, origin should be 0 when passing numpy indices, or 1 if passing values from FITS header or map attributes. See `~astropy.wcs.WCS.wcs_pix2world` for more information. Returns ------- x : `~astropy.units.Quantity` Coordinate of the CTYPE1 axis. (Normally solar-x). y : `~astropy.units.Quantity` Coordinate of the CTYPE2 axis. (Normally solar-y). """ x, y = self.wcs.wcs_pix2world(x, y, origin) # If the wcs is celestial it is output in degress if self.wcs.is_celestial: x *= u.deg y *= u.deg else: x *= self.units.x y *= self.units.y x = Longitude(x, wrap_angle=180 * u.deg) y = Latitude(y) return x.to(self.units.x), y.to(self.units.y)
def diff_rot(duration, latitude, rot_type='howard', frame_time='sidereal'): """ This function computes the change in longitude over days in degrees. Parameters ----------- duration : `~astropy.units.Quantity` Number of seconds to rotate over. latitude : `~astropy.units.Quantity` heliographic coordinate latitude in Degrees. rot_type : `str` The differential rotation model to use. One of: | ``howard`` : Use values for small magnetic features from Howard et al. | ``snodgrass`` : Use Values from Snodgrass et. al | ``allen`` : Use values from Allen's Astrophysical Quantities, and simpler equation. frame_time : `str` One of : ``'sidereal'`` or ``'synodic'``. Choose 'type of day' time reference frame. Returns ------- longitude_delta : `~astropy.units.Quantity` The change in longitude over days (units=degrees) References ---------- * `IDL code equivalent <http://hesperia.gsfc.nasa.gov/ssw/gen/idl/solar/diff_rot.pro>`__ * `Howard rotation <http://adsabs.harvard.edu/abs/1990SoPh..130..295H>`__ * `A review of rotation parameters (including Snodgrass values) <http://link.springer.com/article/10.1023%2FA%3A1005226402796>`__ Examples -------- Default rotation calculation over two days at 30 degrees latitude: >>> import numpy as np >>> import astropy.units as u >>> from sunpy.physics.differential_rotation import diff_rot >>> rotation = diff_rot(2 * u.day, 30 * u.deg) Default rotation over two days for a number of latitudes: >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg) With rotation type 'allen': >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg, 'allen') """ latitude = latitude.to(u.deg) sin2l = (np.sin(latitude))**2 sin4l = sin2l**2 rot_params = { 'howard': [2.894, -0.428, -0.370] * u.urad / u.second, 'snodgrass': [2.851, -0.343, -0.474] * u.urad / u.second, 'allen': [14.44, -3.0, 0] * u.deg / u.day } if rot_type not in ['howard', 'allen', 'snodgrass']: raise ValueError(("rot_type must equal one of " "{{ {} }}".format(" | ".join(rot_params.keys())))) A, B, C = rot_params[rot_type] rotation = (A + B * sin2l + C * sin4l) * duration if frame_time == 'synodic': rotation -= 0.9856 * u.deg / u.day * duration return Longitude(rotation.to(u.deg))
def test_longitude(tmpdir): tree = {'angle': Longitude(-100, u.deg, wrap_angle=180*u.deg)} assert_roundtrip_tree(tree, tmpdir)
def get_altaz(obj_name, ipt_lon, ipt_lat, t=None): #for html scrapping #from lxml import html #from bs4 import BeautifulSoup #to place requests import requests import json import astropy.units as u from astropy.time import Time from astropy.coordinates import SkyCoord, EarthLocation, Angle, Latitude, Longitude from astroplan import FixedTarget, Observer from astroquery.simbad import Simbad as simbad import ephem if t == None: t = Time.now() ## Set up the observer obs_el = 100 * u.m loc = EarthLocation.from_geodetic(ipt_lon, ipt_lat, obs_el) my_site = Observer(name='My_Site', location=loc) obs_lat = my_site.location.lat obs_lon = my_site.location.lon #observer for pyephem ephem_site = ephem.Observer() ephem_site.lon, ephem_site.lat = str(obs_lon.deg), str(obs_lat.deg) ephem_site.date = ephem.Date(str(t.decimalyear)) ##Get the object #Check for planet-hood. #if planet: resolve the individual planet with pyephem. #else if satellite or ISS (or TIANGONG) scrap the appropriate websites and return info #else query simbad ############ # Put in an auto-correct for kids ############ #just make it lower case for now obj_name = obj_name.lower() if obj_name in [ "sun", "mercury", "venus", "moon", "mars", "jupiter", "saturn", "uranus", "neptune", "pluto" ]: if obj_name == "sun": my_planet = ephem.Sun() elif obj_name == "mercury": my_planet = ephem.Mercury() elif obj_name == "venus": my_planet = ephem.Venus() elif obj_name == "moon": my_planet = ephem.Moon() elif obj_name == "mars": my_planet = ephem.Mars() elif obj_name == "jupiter": my_planet = ephem.Jupiter() elif obj_name == "saturn": my_planet = ephem.Saturn() elif obj_name == "uranus": my_planet = ephem.Uranus() elif obj_name == "neptune": my_planet = ephem.Neptune() elif obj_name == "pluto": my_planet = ephem.Pluto() my_planet.compute(ephem_site) az = my_planet.az * 180 / 3.1415926535 alt = my_planet.alt * 180 / 3.1415926535 #here coded for just ISS but for all satellites we should have similar setups, probably poll site elif (obj_name == "iss"): #try a request for the iss from the open notify site. Gives current json data page = requests.get("http://api.open-notify.org/iss-now.json") issdata = page.json() tstamp = issdata['timestamp'] isslat = issdata['iss_position']['latitude'] isslon = issdata['iss_position']['longitude'] #there are issues with just this amount of data as you do not know the altitude of the object #here we fix it to 350 km issheight = 350 * u.km isslat = Latitude(isslat, unit=u.deg) isslon = Longitude(isslon, unit=u.deg) #there are issues however as this data does NOT contain the altitude so lets try scrapping the html #the issue with fullissdata is that it contains information in NASA style units (M50 Cartesian & M50 Keplerian) page = requests.get( "http://spaceflight.nasa.gov/realdata/sightings/SSapplications/Post/JavaSSOP/orbit/ISS/SVPOST.html" ) #fullissdata=html.fromstring(page.text) #there are also other satellites liseted in, issue is parsing the information as I do not know what each field contains #the issue here is that all sat data contains unknown units and uncertain which entries contain useful information page = requests.get( "http://www.celestrak.com/NORAD/elements/stations.txt") allsatdata = page.text c = SkyCoord(isslon, isslat, issheight) my_target = FixedTarget(name='ISS', coord=c) az = my_site.altaz(t, my_target).az.deg alt = my_site.altaz(t, my_target).alt.deg else: try: q = simbad.query_object(obj_name) c = SkyCoord(q["RA"][0], q["DEC"][0], unit=(u.hourangle, u.deg)) my_star = FixedTarget(name='my_star', coord=c) az = my_site.altaz(t, my_star).az.deg alt = my_site.altaz(t, my_star).alt.deg except: print("Couldn't find Object in Database") alt, az = 0, 0 return alt, az
def nai_detector_radecs(detectors, scx, scz, time): """ calculates the RA/DEC for each NaI detector given spacecraft z and x RA/DEC positions. NB: This routine is based on code found in GTBURST, originally written by Dr Giacamo Vianello for the Fermi Science Tools. Parameters ---------- detectors : `dict` A dictionary containing the Fermi/GBM detector pointing angles relative to the spacecraft axes. Obtained from the nai_detector_angles function. scx : array-like Two-element tuple containing the RA/DEC information of the Fermi spacecraft X-axis scz : array-like Two-element tuple containing the RA/DEC information of the Fermi spacecraft Z-axis time : `datetime.datetime` The time corresponding to the input scx and scz values, in a format understandable by parse_time. Returns ------- `dict` A dictionary containing the RA/DEC for each Fermi/GBM NaI detector at the given input time. """ scx_vector = (np.array([ np.cos(scx[0].to('rad').value) * np.cos(scx[1].to('rad').value), np.sin(scx[0].to('rad').value) * np.cos(scx[1].to('rad').value), np.sin(scx[1].to('rad').value) ])) scz_vector = (np.array([ np.cos(scz[0].to('rad').value) * np.cos(scz[1].to('rad').value), np.sin(scz[0].to('rad').value) * np.cos(scz[1].to('rad').value), np.sin(scz[1].to('rad').value) ])) # For each detector, do the rotation depending on the detector zenith and # azimuth angles. detector_radecs = copy.deepcopy(detectors) for l, d in detectors.items(): phi = d[0].value theta = d[1].value # rotate about spacecraft z-axis first vx_primed = rotate_vector(scx_vector, scz_vector, np.deg2rad(phi)) # now find spacecraft y-axis using cross product vy_primed = np.cross(scz_vector, vx_primed) # do the second part of the rotation around vy vz_primed = rotate_vector(scz_vector, vy_primed, np.deg2rad(theta)) # now we should be pointing at the new RA/DEC. ra = Longitude( np.degrees(np.arctan2(vz_primed[1], vz_primed[0])) * u.deg) dec = Latitude(np.degrees(np.arcsin(vz_primed[2])) * u.deg) # save the RA/DEC in a dictionary detector_radecs[l] = [ra, dec] detector_radecs['time'] = time return detector_radecs
def from_tree(cls, node, ctx): wrap_angle = node['wrap_angle'] return Longitude(super().from_tree(node, ctx), wrap_angle=wrap_angle)
def test_representations_api(): from astropy.coordinates.representation import SphericalRepresentation, \ UnitSphericalRepresentation, PhysicsSphericalRepresentation, \ CartesianRepresentation from astropy.coordinates import Angle, Longitude, Latitude, Distance # <-----------------Classes for representation of coordinate data--------------> # These classes inherit from a common base class and internally contain Quantity # objects, which are arrays (although they may act as scalars, like numpy's # length-0 "arrays") # They can be initialized with a variety of ways that make intuitive sense. # Distance is optional. UnitSphericalRepresentation(lon=8 * u.hour, lat=5 * u.deg) UnitSphericalRepresentation(lon=8 * u.hourangle, lat=5 * u.deg) SphericalRepresentation(lon=8 * u.hourangle, lat=5 * u.deg, distance=10 * u.kpc) # In the initial implementation, the lat/lon/distance arguments to the # initializer must be in order. A *possible* future change will be to allow # smarter guessing of the order. E.g. `Latitude` and `Longitude` objects can be # given in any order. UnitSphericalRepresentation(Longitude(8, u.hour), Latitude(5, u.deg)) SphericalRepresentation(Longitude(8, u.hour), Latitude(5, u.deg), Distance(10, u.kpc)) # Arrays of any of the inputs are fine UnitSphericalRepresentation(lon=[8, 9] * u.hourangle, lat=[5, 6] * u.deg) # Default is to copy arrays, but optionally, it can be a reference UnitSphericalRepresentation(lon=[8, 9] * u.hourangle, lat=[5, 6] * u.deg, copy=False) # strings are parsed by `Latitude` and `Longitude` constructors, so no need to # implement parsing in the Representation classes UnitSphericalRepresentation(lon=Angle('2h6m3.3s'), lat=Angle('0.1rad')) # Or, you can give `Quantity`s with keywords, and they will be internally # converted to Angle/Distance c1 = SphericalRepresentation(lon=8 * u.hourangle, lat=5 * u.deg, distance=10 * u.kpc) # Can also give another representation object with the `reprobj` keyword. c2 = SphericalRepresentation.from_representation(c1) # distance, lat, and lon typically will just match in shape SphericalRepresentation(lon=[8, 9] * u.hourangle, lat=[5, 6] * u.deg, distance=[10, 11] * u.kpc) # if the inputs are not the same, if possible they will be broadcast following # numpy's standard broadcasting rules. c2 = SphericalRepresentation(lon=[8, 9] * u.hourangle, lat=[5, 6] * u.deg, distance=10 * u.kpc) assert len(c2.distance) == 2 # when they can't be broadcast, it is a ValueError (same as Numpy) with pytest.raises(ValueError): c2 = UnitSphericalRepresentation(lon=[8, 9, 10] * u.hourangle, lat=[5, 6] * u.deg) # It's also possible to pass in scalar quantity lists with mixed units. These # are converted to array quantities following the same rule as `Quantity`: all # elements are converted to match the first element's units. c2 = UnitSphericalRepresentation( lon=Angle([8 * u.hourangle, 135 * u.deg]), lat=Angle([5 * u.deg, (6 * np.pi / 180) * u.rad])) assert c2.lat.unit == u.deg and c2.lon.unit == u.hourangle npt.assert_almost_equal(c2.lon[1].value, 9) # The Quantity initializer itself can also be used to force the unit even if the # first element doesn't have the right unit lon = u.Quantity([120 * u.deg, 135 * u.deg], u.hourangle) lat = u.Quantity([(5 * np.pi / 180) * u.rad, 0.4 * u.hourangle], u.deg) c2 = UnitSphericalRepresentation(lon, lat) # regardless of how input, the `lat` and `lon` come out as angle/distance assert isinstance(c1.lat, Angle) assert isinstance( c1.lat, Latitude) # `Latitude` is an `~astropy.coordinates.Angle` subclass assert isinstance(c1.distance, Distance) # but they are read-only, as representations are immutable once created with pytest.raises(AttributeError): c1.lat = Latitude(5, u.deg) # Note that it is still possible to modify the array in-place, but this is not # sanctioned by the API, as this would prevent things like caching. c2.lat[:] = [0] * u.deg # possible, but NOT SUPPORTED # To address the fact that there are various other conventions for how spherical # coordinates are defined, other conventions can be included as new classes. # Later there may be other conventions that we implement - for now just the # physics convention, as it is one of the most common cases. _ = PhysicsSphericalRepresentation(phi=120 * u.deg, theta=85 * u.deg, r=3 * u.kpc) # first dimension must be length-3 if a lone `Quantity` is passed in. c1 = CartesianRepresentation(np.random.randn(3, 100) * u.kpc) assert c1.xyz.shape[0] == 3 assert c1.xyz.unit == u.kpc assert c1.x.shape[0] == 100 assert c1.y.shape[0] == 100 assert c1.z.shape[0] == 100 # can also give each as separate keywords CartesianRepresentation(x=np.random.randn(100) * u.kpc, y=np.random.randn(100) * u.kpc, z=np.random.randn(100) * u.kpc) # if the units don't match but are all distances, they will automatically be # converted to match `x` xarr, yarr, zarr = np.random.randn(3, 100) c1 = CartesianRepresentation(x=xarr * u.kpc, y=yarr * u.kpc, z=zarr * u.kpc) c2 = CartesianRepresentation(x=xarr * u.kpc, y=yarr * u.kpc, z=zarr * u.pc) assert c1.xyz.unit == c2.xyz.unit == u.kpc assert_allclose((c1.z / 1000) - c2.z, 0 * u.kpc, atol=1e-10 * u.kpc) # representations convert into other representations via `represent_as` srep = SphericalRepresentation(lon=90 * u.deg, lat=0 * u.deg, distance=1 * u.pc) crep = srep.represent_as(CartesianRepresentation) assert_allclose(crep.x, 0 * u.pc, atol=1e-10 * u.pc) assert_allclose(crep.y, 1 * u.pc, atol=1e-10 * u.pc) assert_allclose(crep.z, 0 * u.pc, atol=1e-10 * u.pc)
def _sun_pos(date): """ Calculate solar ephemeris parameters. Allows for planetary and lunar perturbations in the calculation of solar longitude at date and various other solar positional parameters. This routine is a truncated version of Newcomb's Sun and is designed to give apparent angular coordinates (T.E.D) to a precision of one second of time. This function replicates the SSW/ IDL function "sun_pos.pro". This function is assigned to be internal at the moment as it should really be replaced by accurate ephemeris calculations in the part of SunPy that handles ephemeris. Parameters ----------- date : `sunpy.time.time` Time at which the solar ephemeris parameters are calculated. The input time can be in any acceptable time format. Returns ------- A dictionary with the following keys with the following meanings: longitude - Longitude of sun for mean equinox of date (degs) ra - Apparent RA for true equinox of date (degs) dec - Apparent declination for true equinox of date (degs) app_long - Apparent longitude (degs) obliq - True obliquity (degs) Notes ----- SSWIDL code equivalent: http://hesperia.gsfc.nasa.gov/ssw/gen/idl/solar/sun_pos.pro Examples -------- >>> sp = _sun_pos('2013-03-27') """ # Fractional Julian day with correct offset dd = julian_day(date) - 2415020.0 # form time in Julian centuries from 1900.0 t = dd / 36525.0 # form sun's mean longitude l = (279.6966780 + np.mod(36000.7689250 * t, 360.00)) * 3600.0 # allow for ellipticity of the orbit (equation of centre) using the Earth's # mean anomaly ME me = 358.4758440 + np.mod(35999.049750 * t, 360.0) ellcor = (6910.10 - 17.20 * t) * np.sin(np.deg2rad(me)) + \ 72.30 * np.sin(np.deg2rad(2.0 * me)) l = l + ellcor # allow for the Venus perturbations using the mean anomaly of Venus MV mv = 212.603219 + np.mod(58517.8038750 * t, 360.0) vencorr = 4.80 * np.cos(np.deg2rad(299.10170 + mv - me)) + \ 5.50 * np.cos(np.deg2rad(148.31330 + 2.0 * mv - 2.0 * me)) + \ 2.50 * np.cos(np.deg2rad(315.94330 + 2.0 * mv - 3.0 * me)) + \ 1.60 * np.cos(np.deg2rad(345.25330 + 3.0 * mv - 4.0 * me)) + \ 1.00 * np.cos(np.deg2rad(318.150 + 3.0 * mv - 5.0 * me)) l = l + vencorr # Allow for the Mars perturbations using the mean anomaly of Mars MM mm = 319.5294250 + np.mod(19139.858500 * t, 360.0) marscorr = 2.0 * np.cos(np.deg2rad(343.88830 - 2.0 * mm + 2.0 * me)) + \ 1.80 * np.cos(np.deg2rad(200.40170 - 2.0 * mm + me)) l = l + marscorr # Allow for the Jupiter perturbations using the mean anomaly of Jupiter MJ mj = 225.3283280 + np.mod(3034.69202390 * t, 360.00) jupcorr = 7.20 * np.cos(np.deg2rad(179.53170 - mj + me)) + \ 2.60 * np.cos(np.deg2rad(263.21670 - mj)) + \ 2.70 * np.cos(np.deg2rad(87.14500 - 2.0 * mj + 2.0 * me)) + \ 1.60 * np.cos(np.deg2rad(109.49330 - 2.0 * mj + me)) l = l + jupcorr # Allow for the Moons perturbations using the mean elongation of the Moon # from the Sun D d = 350.73768140 + np.mod(445267.114220 * t, 360.0) mooncorr = 6.50 * np.sin(np.deg2rad(d)) l = l + mooncorr # Note the original code is # longterm = + 6.4d0 * sin(( 231.19d0 + 20.20d0 * t )*!dtor) longterm = 6.40 * np.sin(np.deg2rad(231.190 + 20.20 * t)) l = l + longterm l = np.mod(l + 2592000.0, 1296000.0) longmed = l / 3600.0 # Allow for Aberration l = l - 20.5 # Allow for Nutation using the longitude of the Moons mean node OMEGA omega = 259.1832750 - np.mod(1934.1420080 * t, 360.0) l = l - 17.20 * np.sin(np.deg2rad(omega)) # Form the True Obliquity oblt = 23.4522940 - 0.01301250 * t + \ (9.20 * np.cos(np.deg2rad(omega))) / 3600.0 # Form Right Ascension and Declination l = l / 3600.0 ra = np.rad2deg(np.arctan2(np.sin(np.deg2rad(l)) * \ np.cos(np.deg2rad(oblt)), np.cos(np.deg2rad(l)))) if isinstance(ra, np.ndarray): ra[ra < 0.0] += 360.0 elif ra < 0.0: ra = ra + 360.0 dec = np.rad2deg(np.arcsin(np.sin(np.deg2rad(l)) * np.sin(np.deg2rad(oblt)))) # convert the internal variables to those listed in the top of the # comment section in this code and in the original IDL code. Quantities # are assigned following the advice in Astropy "Working with Angles" return {"longitude": Longitude(longmed, u.deg), "ra": Longitude(ra, u.deg), "dec": Latitude(dec, u.deg), "app_long": Longitude(l, u.deg), "obliq": Angle(oblt, u.deg)}
def _L0(time='now', light_travel_time_correction=True, nearest_point=True, aberration_correction=False): """ Return the L0 angle for the Sun at a specified time, which is the apparent Carrington longitude of the Sun-disk center as seen from Earth. Observer corrections can be disabled, and then this function will instead return the true Carrington longitude. Parameters ---------- time : {parse_time_types} Time to use in a parse_time-compatible format light_travel_time_correction : `bool` If True, apply the correction for light travel time from Sun to Earth. Defaults to True. nearest_point : `bool` If True, calculate the light travel time to the nearest point on the Sun's surface rather than the light travel time to the center of the Sun (i.e., a difference of the solar radius). Defaults to True. aberration_correction : `bool` If True, apply the stellar-aberration correction due to Earth's motion. Defaults to False. Returns ------- `~astropy.coordinates.Longitude` The Carrington longitude Notes ----- This longitude is calculated using current IAU values (Seidelmann et al. 2007 and later), which do not include the effects of light travel time and aberration due to Earth's motion (see that paper's Appendix). This function then, by default, applies the light-travel-time correction for the nearest point on the Sun's surface, but does not apply the stellar-aberration correction due to Earth's motion. We do not apply the stellar-abberation correction by default because it should not be applied for purposes such as co-aligning images that are each referenced to Sun-disk center. Stellar aberration does not shift the apparent positions of solar features relative to the Sun-disk center. The Astronomical Almanac applies the stellar-aberration correction in their printed published L0 values (see also Urban & Kaplan 2007). Applying the stellar-aberration correction due to Earth's motion decreases the apparent Carrington longitude by ~20.5 arcseconds. References ---------- * Seidelmann et al. (2007), "Report of the IAU/IAG Working Group on cartographic coordinates and rotational elements: 2006" `(link) <http://dx.doi.org/10.1007/s10569-007-9072-y>`__ * Urban & Kaplan (2007), "Investigation of Change in the Computational Technique of the Sun's Physical Ephemeris in The Astronomical Almanac" `(link) <http://asa.hmnao.com/static/files/sun_rotation_change.pdf>`__ """ obstime = parse_time(time) earth = get_earth(obstime) # Calculate the de-tilt longitude of the Earth dlon_earth = _detilt_lon(earth) # Calculate the distance to the nearest point on the Sun's surface distance = earth.radius - _RSUN if nearest_point else earth.radius # Apply a correction for aberration due to Earth motion # This expression is an approximation to reduce computations (e.g., it does not account for the # inclination of the Sun's rotation axis relative to the ecliptic), but the estimated error is # <0.2 arcseconds if aberration_correction: dlon_earth -= 20.496 * u.arcsec * 1 * u.AU / earth.radius # Antedate the observation time to account for light travel time for the Sun-Earth distance antetime = ( obstime - distance / speed_of_light) if light_travel_time_correction else obstime # Calculate the de-tilt longitude of the meridian due to the Sun's sidereal rotation dlon_meridian = Longitude(_DLON_MERIDIAN + (antetime - _J2000) * 14.1844 * u.deg / u.day) return Longitude(dlon_earth - dlon_meridian)
import cdshealpix from cdshealpix import elliptical_cone_search import astropy.units as u from astropy.coordinates import Angle, SkyCoord, Longitude, Latitude import numpy as np ipix, depth, fully_covered = elliptical_cone_search(lon=Longitude(0, u.deg), lat=Latitude(0, u.deg), a=Angle(50, unit="deg"), b=Angle(5, unit="deg"), pa=Angle(30, unit="deg"), depth=10) from mocpy import MOC, World2ScreenMPL moc = MOC.from_healpix_cells(ipix, depth, fully_covered) # Plot the MOC using matplotlib import matplotlib.pyplot as plt fig = plt.figure(111, figsize=(10, 10)) # Define a astropy WCS easily with World2ScreenMPL(fig, fov=100 * u.deg, center=SkyCoord(0, 0, unit="deg", frame="icrs"), coordsys="icrs", rotation=Angle(0, u.degree), projection="AIT") as wcs: ax = fig.add_subplot(1, 1, 1, projection=wcs) # Call fill with a matplotlib axe and the `~astropy.wcs.WCS` wcs object. moc.fill(ax=ax, wcs=wcs, alpha=0.5, fill=True, color="green") # Draw the perimeter of the MOC in black
def __init__(self, telemetry_file=None, save_file=None): if telemetry_file is not None: self.filename = telemetry_file self.systime = [] self.latitude = [] self.longitude = [] self.altitude = [] self.utc_time = [] pressure_low = [] pressure_mid = [] pressure_high = [] count = 0 print("Parsing {0}".format(telemetry_file)) with open(telemetry_file, 'rb') as f: pg = parser_generator(f, filter_systemid=0x05, filter_tmtype=0x02, verbose=True) for p in pg: count += 1 self.systime.append(p['systime']) self.latitude.append(p['user_latitude']) self.longitude.append(p['user_longitude']) self.altitude.append(p['user_altitude']) # Convert time to UTC self.utc_time.append(np.datetime64('1980-01-06T00:00:00Z') +\ np.timedelta64(p['user_week'], 'W') +\ np.timedelta64(int((p['user_timeofweek'] - p['user_offset']) * 1e6), 'us')) pressure_low.append( (p['sip1_pressure_low'] + p['sip2_pressure_low']) / 2) pressure_mid.append( (p['sip1_pressure_mid'] + p['sip2_pressure_mid']) / 2) pressure_high.append( (p['sip1_pressure_high'] + p['sip2_pressure_high']) / 2) if count > 0: self.systime = np.hstack(self.systime) self.latitude = Latitude(self.latitude, 'deg') self.longitude = Longitude(self.longitude, 'deg', wrap_angle=180 * u.deg) self.altitude = u.Quantity(self.altitude, 'm').to('km') self.utc_time = np.hstack(self.utc_time) pressure_low = np.hstack(pressure_low) pressure_mid = np.hstack(pressure_mid) pressure_high = np.hstack(pressure_high) # Convert pressure to millibars self.pressure = pressure_low * 0.327 - 10.76 use_mid = pressure_mid < 3165 self.pressure[use_mid] = pressure_mid[use_mid] * 0.032 - 1.29 use_high = pressure_high < 3383 self.pressure[ use_high] = pressure_high[use_high] * 0.003 - 0.149 self.pressure = u.Quantity(self.pressure, 'mbar') print("Total packets: {0}".format(count)) else: print("No packets found") elif save_file is not None: print("Restoring {0}".format(save_file)) with gzip.open(save_file, 'rb') as f: try: saved = pickle.load(f, encoding='latin1') except TypeError: saved = pickle.load(f) self.filename = saved['filename'] self.systime = saved['systime'] self.latitude = saved['latitude'] self.longitude = saved['longitude'] self.altitude = saved['altitude'] self.utc_time = saved['utc_time'] self.pressure = saved['pressure'] else: raise RuntimeError( "Either a telemetry file or a save file must be specified")
def do_target_observation(i, obstime_utc, telescope_position, csvfile, total_wait): #, closest_field): # Get objects that are close to current observing horizon: current_lst = Time(obstime_utc).sidereal_time('apparent', westerbork().lon) proposed_ra = (current_lst + Longitude('6h')).wrap_at(360 * u.deg) test_slew_seconds = calc_slewtime( [telescope_position.ra.radian, telescope_position.dec.radian], [proposed_ra.radian, telescope_position.dec.radian]) avail_fields = apertif_fields[apertif_fields['weights'] > 0] availability = SkyCoord( np.array(avail_fields['hmsdms'] )).ra.hour - proposed_ra.hour - test_slew_seconds / 3600. availability[availability < -12.] += 24. sun_position = get_sun(Time(obstime_utc, scale='utc')) sun_okay = np.array( sun_position.separation(SkyCoord(avail_fields['hmsdms'])).value) targ_wait = 0. # minutes ##### EDITABLE: Will control the maximum wait time for a target before it gives up and looks for a calibrator ##### # wait_limit = 5.0 # hours wait_limit = 10.0 # hours new_obstime_utc = obstime_utc # First check what is *already* up. If nothing, then wait for something to rise. ##### If target is passing it's HA limit, adjust availability numbers (especially the second one) ##### while not np.any((availability < -0.02) & (availability > -0.40) & (sun_okay > args.sun_distance)): targ_wait += dowait new_obstime_utc = wait_for_rise(new_obstime_utc, waittime=dowait) new_lst = Time(new_obstime_utc).sidereal_time('apparent', westerbork().lon) proposed_ra = (new_lst + Longitude('6h')).wrap_at(360 * u.deg) availability = SkyCoord(np.array( avail_fields['hmsdms'])).ra.hour - proposed_ra.hour availability[availability < -12.] += 24. # print(availability, sun_okay > args.sun_distance, sun_okay) if (targ_wait != 0.) and (targ_wait <= wait_limit * 60.): total_wait += targ_wait print( "\tTarget not up (or sun issues), waiting {} minutes until LST: {}" .format(targ_wait, str(new_lst))) if targ_wait <= wait_limit * 60.: # Choose M101 field first if available or within a 1 hour wait AND (before this function) if users had requested it in args.repeat_m101 if 'M1403+5324' in avail_fields[(availability < -0.02) & (availability > -0.40)]['name']: first_field = avail_fields[avail_fields['name'] == 'M1403+5324'][0] print( "*** M1403+5324 OBSERVED! WAIT A MONTH TO SCHEDULE AGAIN! ***" ) elif 'M1403+5324' in avail_fields[(availability < 1.00) & (availability > -0.02)]['name']: m101_field = avail_fields[avail_fields['name'] == 'M1403+5324'][0] m101_availability = SkyCoord( m101_field['hmsdms']).ra.hour - proposed_ra.hour while not (m101_availability < -0.02) & (m101_availability > -0.40): targ_wait += dowait new_obstime_utc = wait_for_rise(new_obstime_utc, waittime=dowait) new_lst = Time(new_obstime_utc).sidereal_time( 'apparent', westerbork().lon) proposed_ra = (new_lst + Longitude('6h')).wrap_at(360 * u.deg) m101_availability = SkyCoord( m101_field['hmsdms']).ra.hour - proposed_ra.hour # m101_availability[m101_availability < -12] += 24 first_field = m101_field print( "*** M1403+5324 OBSERVED! WAIT A MONTH TO SCHEDULE AGAIN! ***" ) else: first_field = avail_fields[(availability < -0.02) & (availability > -0.40) & (sun_okay > args.sun_distance)][0] check_sun = sun_position.separation(SkyCoord( first_field['hmsdms'])) if check_sun.value < 50.: print("\tField is THIS close to Sun: {}".format(check_sun)) # NOTE SLEW TIME IS CALCULATED TO THE *OBSERVING* HORIZON, NOT TO THE NEW RA! # TELESCOPE SHOULD MOVE TO HORIZON AND WAIT!!! slew_seconds = calc_slewtime( [telescope_position.ra.radian, telescope_position.dec.radian], [ SkyCoord(first_field['hmsdms']).ra.radian, SkyCoord(first_field['hmsdms']).dec.radian ]) if slew_seconds < targ_wait * 60.: new_obstime_utc = obstime_utc + datetime.timedelta( minutes=targ_wait) else: new_obstime_utc = obstime_utc + datetime.timedelta( seconds=slew_seconds) after_target = observe_target(apertif_fields, new_obstime_utc, first_field['name'], obstime=11.5) write_to_csv(csvfile, first_field['name'], SkyCoord(first_field['hmsdms']), new_obstime_utc, after_target) print("Scan {} observed {}.".format(i, first_field['hmsdms'])) return i, after_target, SkyCoord(first_field['hmsdms']), total_wait else: print("\tNo target for {} hours. Go to a calibrator instead.".format( targ_wait / 60.)) i -= 1 return i, obstime_utc, telescope_position, total_wait
def __init__(self, pix_values, lon0, lon1, lat0, lat1, dist0=None, dist1=None, axis_order=('lon', 'lat', 'dist'), frame='galactic', dist_interp='linear'): """ Args: pix_values (:obj:`np.ndarray`): An array containing the pixel values. For a 3D dust map (with distance), the array should be 3D, with the order of the axes corresponding to :obj:`axis_order`. For a 2D dust map, the array should be 2D. lon0 (float): The lower limiting longitude of the map, in deg. lon1 (float): The upper limiting longitude of the map, in deg. lat0 (float): The lower limiting latitude of the map, in deg. lat1 (float): The upper limiting latitude of the map, in deg. dist0 (Optional[:obj:`astropy.units.Quantity`]): The lower limiting distance of the map. If :obj:`dist0` has units of distance of the map. If :obj:`dist0` has units of :obj:`'mag'`, then it is assumed to be a distance modulus, and the distance bins are assumed to be spaced linearly in distance modulus, instead of distance. dist1 (Optional[:obj:`astropy.units.Quantity`]): The upper limiting distance of the map. If :obj:`dist1` has units of :obj:`'mag'`, then it is assumed to be a distance modulus, and the distance bins are assumed to be spaced linearly in distance modulus, instead of distance. axis_order (tuple of str): The order of the axes in :obj:`pix_values`. Defaults to :obj:`('lon','lat','dist')`. For 2D maps, do not include :obj:`'dist'`. frame (Optional[:obj:`str`]): The coordinate frame to which the longitudes and latitudes correspond. Must be a frame understood by :obj:`astropy.coordinates.SkyCoord`. Defaults to :obj:`'galactic'`. dist_interp (:obj:`str`): How to interpolate between distance slices in the map. Valid choices are :obj:`'step'` and :obj:`'linear'`. Defaults to :obj:`'linear'`. """ self._frame = frame # Read (lon, lat, dist) bounds self._wrap_angle = lon0 * units.deg self._lon_lim = ( Longitude(lon0, unit='deg', wrap_angle=self._wrap_angle), Longitude(lon1, unit='deg', wrap_angle=self._wrap_angle), ) self._lat_lim = (lat0, lat1) if dist0 is not None: if dist0.unit == 'mag': self._dm = True self._dist_lim = (dist0.value, dist1.value) else: self._dm = False self._dist_lim = (dist0.to('kpc').value, dist1.to('kpc').value) # Read mapping from axis -> (lon, lat, dist) self._axis_dist = None if len(axis_order) < 2: raise ValueError("axis_order must have at least " + "two entries ('lon' and 'lat').") for a in ('lon', 'lat'): if a not in axis_order: raise ValueError("'{}' must be in axis_order.".format(a)) for i, a in enumerate(axis_order): if a == 'lon': self._axis_lon = i elif a == 'lat': self._axis_lat = i elif a == 'dist': self._axis_dist = i else: raise ValueError("Unknown entry in axis_order: " + "'{}'".format(a)) # Get (lon,lat,dist) grid properties self._n_lon = pix_values.shape[self._axis_lon] self._n_lat = pix_values.shape[self._axis_lat] if self._lon_lim[1] == self._lon_lim[0]: self._dlon = 360. / self._n_lon else: self._dlon = (self._lon_lim[1] - self._lon_lim[0]) / self._n_lon self._dlon = self._dlon.to('deg').value self._dlat = (self._lat_lim[1] - self._lat_lim[0]) / self._n_lat if self._axis_dist is not None: self._n_dist = pix_values.shape[self._axis_dist] self._ddist = (self._dist_lim[1] - self._dist_lim[0]) / self._n_dist if self._dm: # Physical distance of each slice (in kpc) self._ddist_phys = 10.**(0.2 * np.linspace( self._dist_lim[0], self._dist_lim[1], self._n_dist) - 2.) # Get pixel values self._pix_values = pix_values self._n_axes = len(pix_values.shape) self._dist_interp = dist_interp
def _coords2idx(self, coords, diff=False): c = coords.transform_to(self._frame).represent_as('spherical') idx = np.empty(coords.shape + (self._n_axes, ), dtype='i4') lon = Longitude(c.lon, wrap_angle=self._wrap_angle) lon_idx = (lon - self._lon_lim[0]).to('deg').value / self._dlon lat_idx = (c.lat.deg - self._lat_lim[0]) / self._dlat lon_idx = np.floor(lon_idx).astype('i4') lat_idx = np.floor(lat_idx).astype('i4') mask = ((lon_idx < 0) | (lon_idx >= self._n_lon) | (lat_idx < 0) | (lat_idx >= self._n_lat)) if self._axis_dist is not None: dist = c.distance.to('kpc').value if self._dm: dist = 5. * (np.log10(dist) + 2.) dist_idx = (dist - self._dist_lim[0]) / self._ddist dist_idx_close = np.floor(dist_idx).astype('i4') # Differential extinction if diff: dist_idx_far = (dist_idx_close + 1).clip(0, self._n_dist - 1) close_mask = (dist_idx_close < 0) far_mask = (dist_idx_close >= self._n_dist) elif self._dist_interp == 'step': dist_idx_close = dist_idx_close.clip(-1, self._n_dist - 1) close_mask = (dist_idx_close == -1) elif self._dist_interp == 'linear': far_weight = dist_idx - dist_idx_close # Beyond farthest distance slice far_mask = (dist_idx_close >= self._n_dist - 1) if np.any(far_mask): dist_idx_close[far_mask] = self._n_dist - 2 far_weight[far_mask] = 1.0 # Closer than closest distance slice close_mask = (dist_idx_close < 0) dist_idx_close[close_mask] = -1 if np.any(close_mask): if self._dm: far_weight[close_mask] = (10.**( 0.2 * (dist[close_mask] - self._dist_lim[0]))) else: far_weight[close_mask] = (dist[close_mask] / self._dist_lim[0]) #mask |= (dist_idx < 0) | (dist_idx >= self._n_dist) if np.any(mask): lon_idx[mask] = -1 lat_idx[mask] = -1 if self._axis_dist is not None: dist_idx_close[mask] = -1 idx[:, self._axis_lon] = lon_idx idx[:, self._axis_lat] = lat_idx # Include distance indices if self._axis_dist is not None: if diff: # For differential reddening, need: # 1. Index of distance bin just beyond d # 2. Mask of out-of-bounds (lon, lat) # 3. Mask of which pixels are closer than closest slice # 3. Mask of which pixels are farther than farthest slice idx[:, self._axis_dist] = dist_idx_far return idx, mask, (close_mask, far_mask) else: idx[:, self._axis_dist] = dist_idx_close if self._dist_interp == 'step': # For step-function interpolation, need: # 1. Index of distance bin just inside d # 2. Mask of out-of-bounds (lon, lat) # 3. Mask of which pixels are closer than closest slice return idx, mask, close_mask elif self._dist_interp == 'linear': # For piecewise-linear interpolation, need: # 1. Index of distance bin just inside d # 2. Mask of out-of-bounds (lon, lat) # 3. For each pixel, weight to apply to next distance bin return idx, mask, far_weight return idx, mask, None
class SkyCatalog: def __init__(self, sun_only = False, moon_only = False): if sun_only: self.ephemerides = [ephem.Sun()] self.data = None elif moon_only: self.ephemerides = [ephem.Moon()] self.data = None else: self.ephemerides = [ephem.Venus(), ephem.Mars(), ephem.Jupiter(), ephem.Saturn(), ephem.Moon(), ephem.Sun()] self.data = ascii.read(Configuration.star_catalog_file, guess=False, format='fixed_width_no_header', names=('HR', 'Name', 'DM', 'HD', 'SAO', 'FK5', 'IRflag', 'r_IRflag', 'Multiple', 'ADS', 'ADScomp', 'VarID', 'RAh1900', 'RAm1900', 'RAs1900', 'DE-1900', 'DEd1900', 'DEm1900', 'DEs1900', 'RAh', 'RAm', 'RAs', 'DE-', 'DEd', 'DEm', 'DEs', 'GLON', 'GLAT', 'Vmag', 'n_Vmag', 'u_Vmag', 'B-V', 'u_B-V', 'U-B', 'u_U-B', 'R-I', 'n_R-I', 'SpType', 'n_SpType', 'pmRA', 'pmDE', 'n_Parallax', 'Parallax', 'RadVel', 'n_RadVel', 'l_RotVel', 'RotVel', 'u_RotVel', 'Dmag', 'Sep', 'MultID', 'MultCnt', 'NoteFlag'), col_starts=(0, 4, 14, 25, 31, 37, 41, 42, 43, 44, 49, 51, 60, 62, 64, 68, 69, 71, 73, 75, 77, 79, 83, 84, 86, 88, 90, 96, 102, 107, 108, 109, 114, 115, 120, 121, 126, 127, 147, 148, 154, 160, 161, 166, 170, 174, 176, 179, 180, 184, 190, 194, 196), col_ends=(3, 13, 24, 30, 36, 40, 41, 42, 43, 48, 50, 59, 61, 63, 67, 68, 70, 72, 74, 76, 78, 82, 83, 85, 87, 89, 95, 101, 106, 107, 108, 113, 114, 119, 120, 125, 126, 146, 147, 153, 159, 160, 165, 169, 173, 175, 178, 179, 183, 189, 193, 195, 196)) # removed masked rows self.data = self.data[:][~np.ma.getmaskarray(self.data['DE-'])] def setLocation(self, location): self.location = location def setTime(self, time): self.time = time def calculate(self): ephem_location = ephem.Observer() ephem_location.lat = self.location.latitude.to(u.rad) / u.rad ephem_location.lon = self.location.longitude.to(u.rad) / u.rad ephem_location.elevation = self.location.height / u.meter ephem_location.date = ephem.Date(self.time.datetime) if self.data is None: self.alt = Latitude([], unit=u.deg) self.az = Longitude([], unit=u.deg) self.names = Column([], dtype=np.str) self.vmag = Column([]) else: ra = Longitude((self.data['RAh'], self.data['RAm'], self.data['RAs']), u.h) dec = Latitude((np.core.defchararray.add(self.data['DE-'], self.data['DEd'].astype(str)).astype(int), self.data['DEm'], self.data['DEs']), u.deg) c = SkyCoord(ra, dec, frame='icrs') altaz = c.transform_to(AltAz(obstime=self.time, location=self.location)) self.alt = altaz.alt self.az = altaz.az self.names = self.data['Name'] self.vmag = self.data['Vmag'] for ephemeris in self.ephemerides: ephemeris.compute(ephem_location) self.vmag = self.vmag.insert(0, ephemeris.mag) self.alt = self.alt.insert(0, (ephemeris.alt.znorm * u.rad).to(u.deg)) self.az = self.az.insert(0, (ephemeris.az * u.rad).to(u.deg)) self.names = self.names.insert(0, ephemeris.name) return self.names, self.vmag, self.alt, self.az def filter(self, min_alt, max_mag): show = self.alt >= min_alt names = self.names[show] vmag = self.vmag[show] alt = self.alt[show] az = self.az[show] show_mags = vmag < max_mag names = names[show_mags] vmag = vmag[show_mags] alt = alt[show_mags] az = az[show_mags] return names, vmag, alt, az
coord_detilt = coord.hcrs.cartesian.transform(_SUN_DETILT_MATRIX) return coord_detilt.represent_as(SphericalRepresentation).lon.to('deg') # J2000.0 epoch _J2000 = Time('J2000.0', scale='tt') # One of the two nodes of intersection between the ICRF equator and Sun's equator in HCRS _NODE = SkyCoord(_SOLAR_NORTH_POLE_HCRS.lon + 90 * u.deg, 0 * u.deg, frame='hcrs') # The longitude in the de-tilted frame of the Sun's prime meridian. # The IAU (Seidelmann et al. 2007 and later) defines the true longitude of the meridian (i.e., # without light travel time to Earth and aberration effects) as 84.176 degrees eastward at J2000. _DLON_MERIDIAN = Longitude(_detilt_lon(_NODE) + 84.176 * u.deg) @add_common_docstring(**_variables_for_parse_time_docstring()) def _L0(time='now', light_travel_time_correction=True, nearest_point=True, aberration_correction=False): """ Return the L0 angle for the Sun at a specified time, which is the apparent Carrington longitude of the Sun-disk center as seen from Earth. Observer corrections can be disabled, and then this function will instead return the true Carrington longitude. Parameters
def nai_detector_radecs(detectors, scx, scz, time): """ calculates the "RA/DEC" for each NaI detector given spacecraft "z" and "x" "RA/DEC" positions. This routine is based on code found in `GTBURST <https://fermi.gsfc.nasa.gov/ssc/data/analysis/scitools/gtburst.html>`__, originally written by Dr Giacamo Vianello for the Fermi Science Tools. Parameters ---------- detectors : `dict` A dictionary containing the Fermi/GBM detector pointing angles relative to the spacecraft axes. Obtained from the `sunpy.instr.fermi.nai_detector_angles` function. scx : array-like Two-element tuple containing the "RA/DEC" information of the Fermi spacecraft X-axis scz : array-like Two-element tuple containing the "RA/DEC" information of the Fermi spacecraft Z-axis time : {parse_time_types} A time specified as a parse_time-compatible time string, number, or a datetime object. This will correspond to the input ``scx`` and ``scz`` values. Returns ------- `dict` A dictionary containing the "RA/DEC" for each Fermi/GBM NaI detector at the given input time. """ scx_vector = (np.array( [np.cos(scx[0].to('rad').value) * np.cos(scx[1].to('rad').value), np.sin(scx[0].to('rad').value) * np.cos(scx[1].to('rad').value), np.sin(scx[1].to('rad').value)])) scz_vector = (np.array( [np.cos(scz[0].to('rad').value) * np.cos(scz[1].to('rad').value), np.sin(scz[0].to('rad').value) * np.cos(scz[1].to('rad').value), np.sin(scz[1].to('rad').value)])) # For each detector, do the rotation depending on the detector zenith and # azimuth angles. detector_radecs = copy.deepcopy(detectors) for l, d in detectors.items(): phi = d[0].value theta = d[1].value # rotate about spacecraft z-axis first vx_primed = rotate_vector(scx_vector, scz_vector, np.deg2rad(phi)) # now find spacecraft y-axis using cross product vy_primed = np.cross(scz_vector, vx_primed) # do the second part of the rotation around vy vz_primed = rotate_vector(scz_vector, vy_primed, np.deg2rad(theta)) # now we should be pointing at the new RA/DEC. ra = Longitude( np.degrees(np.arctan2(vz_primed[1], vz_primed[0])) * u.deg) dec = Latitude(np.degrees(np.arcsin(vz_primed[2])) * u.deg) # save the RA/DEC in a dictionary detector_radecs[l] = [ra, dec] detector_radecs['time'] = time return detector_radecs
def diff_rot(duration: u.s, latitude: u.deg, rot_type='howard', frame_time='sidereal'): r""" This function computes the change in longitude over days in degrees. Parameters ----------- duration : `~astropy.units.Quantity` Number of seconds to rotate over. latitude : `~astropy.units.Quantity` heliographic coordinate latitude in Degrees. rot_type : `str` The differential rotation model to use. One of: | ``howard`` : Use values from Howard et al. (1990) | ``snodgrass`` : Use values from Snodgrass et. al. (1983) | ``allen`` : Use values from Allen's Astrophysical Quantities, and simpler equation. frame_time : `str` One of : ``'sidereal'`` or ``'synodic'``. Choose 'type of day' time reference frame. Returns ------- longitude_delta : `~astropy.units.Quantity` The change in longitude over days (units=degrees) Notes ----- The rotation rate at a heliographic latitude :math:`\theta` is given by .. math:: A + B \sin^{2} \left (\theta \right ) + C \sin^{4} \left ( \theta \right ) where :math:`A, B, C` are constants that depend on the model: ========= ===== ====== ====== ========== Model A B C Unit ========= ===== ====== ====== ========== howard 2.894 -0.428 -0.370 microrad/s snodgrass 2.851 -0.343 -0.474 microrad/s allen 14.44 -3.0 0 deg/day ========= ===== ====== ====== ========== 1 microrad/s is approximately 4.95 deg/day. References ---------- * `Solar surface velocity fields determined from small magnetic features (Howard et al. 1990) <https://doi.org/10.1007/BF00156795>`__ * `A comparison of differential rotation measurements (Beck 2000, includes Snodgrass values) <https://doi.org/10.1023/A:1005226402796>`__ Examples -------- Default rotation calculation over two days at 30 degrees latitude: >>> import numpy as np >>> import astropy.units as u >>> from sunpy.physics.differential_rotation import diff_rot >>> diff_rot(2 * u.day, 30 * u.deg) <Longitude 27.36432679 deg> Default rotation over two days for a number of latitudes: >>> diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg) <Longitude [22.05449682, 23.03214991, 24.12033958, 25.210281 , 26.21032832, 27.05716463, 27.71932645, 28.19299667, 28.49196765, 28.63509765, 28.63509765, 28.49196765, 28.19299667, 27.71932645, 27.05716463, 26.21032832, 25.210281 , 24.12033958, 23.03214991, 22.05449682] deg> With rotation type 'allen': >>> diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg, 'allen') <Longitude [23.58186667, 24.14800185, 24.82808733, 25.57737945, 26.34658134, 27.08508627, 27.74430709, 28.28087284, 28.6594822 , 28.85522599, 28.85522599, 28.6594822 , 28.28087284, 27.74430709, 27.08508627, 26.34658134, 25.57737945, 24.82808733, 24.14800185, 23.58186667] deg> """ latitude = latitude.to(u.deg) sin2l = (np.sin(latitude))**2 sin4l = sin2l**2 rot_params = {'howard': [2.894, -0.428, -0.370] * u.urad / u.second, 'snodgrass': [2.851, -0.343, -0.474] * u.urad / u.second, 'allen': [14.44, -3.0, 0] * u.deg / u.day } if rot_type not in ['howard', 'allen', 'snodgrass']: raise ValueError("rot_type must equal one of " "{{ {} }}".format(" | ".join(rot_params.keys()))) A, B, C = rot_params[rot_type] # This calculation of the rotation assumes a sidereal frame time. rotation = (A + B * sin2l + C * sin4l) * duration # Applying this correction assumes that the observer is on the Earth, # and that the Earth is at the same distance from the Sun at all times # during the year. if frame_time == 'synodic': rotation -= 0.9856 * u.deg / u.day * duration return Longitude(rotation.to(u.deg))
import datetime import math from astropy.coordinates import Longitude from skymap.geometry import SkyCoordDeg, ensure_angle_range from skymap.coordinates import PrecessionCalculator, REFERENCE_EPOCH GALACTIC_NORTH_POLE = SkyCoordDeg(Longitude("12h49m").degree, 27.4) GALACTIC_LONGITUDE_NORTH_CELESTIAL_POLE = 123 GALACTIC_NORTH_POLE_EPOCH = datetime.datetime(1950, 1, 1).date() # Ecliptic coordinate system def obliquity_of_the_ecliptic(epoch=REFERENCE_EPOCH): """Returns the obliquity of the ecliptic for the given epoch, in degrees. The calculation is taken from the Astronomical Almanac 2010, as given on https://en.wikipedia.org/wiki/Axial_tilt#Short_term. """ a = 23.0 + 26 / 60.0 + 21.406 / 3600.0 b = -46.836769 / 3600.0 c = -0.0001831 / 3600.0 d = 0.00200340 / 3600.0 e = -0.576e-6 / 3600.0 f = -0.0434e-6 / 3600.0 t = (epoch - REFERENCE_EPOCH).days / 36525.0 return a + b * t + c * t**2 + d * t**3 + e * t**4 + f * t**5 def ecliptic_to_equatorial(p, epoch=REFERENCE_EPOCH): """Returns the equatorial coordinates for the given point in ecliptic coordinates.
def main(): """See module docstring""" def gamepad_callback(_tracker: Tracker) -> bool: """Callback for gamepad control. Allows manual control of the slew rate via a gamepad when the 'B' button is held down, overriding tracking behavior. This callback is registered with the Tracker object which calls it on each control cycle. Defined inside main() so this has easy access to objects that are within that scope. Args: _tracker: A reference to an object of type Tracker. Not used internally. Returns: True when the 'B' button is depressed which disables the normal control path. False otherwise, which leaves the normal control path enabled. """ if game_pad.state.get('BTN_EAST', 0) == 1: gamepad_x, gamepad_y = game_pad.get_proportional() slew_rates = (mount.max_slew_rate * gamepad_x, mount.max_slew_rate * gamepad_y) for idx, axis_name in enumerate(mount.AxisName): mount.slew(axis_name, slew_rates[idx]) return True else: return False parser = track.ArgParser() targets.add_program_arguments(parser) laser.add_program_arguments(parser) mounts.add_program_arguments(parser, meridian_side_required=True) ntp.add_program_arguments(parser) telem.add_program_arguments(parser) control.add_program_arguments(parser) args = parser.parse_args() # Set priority of this thread to realtime. Do this before constructing objects since priority # is inherited and some critical threads are created by libraries we have no direct control # over. os.sched_setscheduler(0, os.SCHED_RR, os.sched_param(11)) # Check if system clock is synchronized to GPS if args.check_time_sync: try: ntp.check_ntp_status() except ntp.NTPCheckFailure as e: print('NTP check failed: ' + str(e)) if not click.confirm('Continue anyway?', default=True): print('Aborting') sys.exit(2) # Load a MountModel object try: mount_model = track.model.load_stored_model() except track.model.StaleParametersException: if click.confirm('Stored alignment parameters are stale. Use anyway?', default=True): mount_model = track.model.load_stored_model(max_age=None) elif args.target_type == 'camera': if click.confirm( 'Use a default set of alignment parameters instead?', default=True): guide_cam_orientation = click.prompt( 'Enter guide camera orientation in degrees, clockwise positive', type=float) mount_model = track.model.load_default_model( guide_cam_orientation=Longitude(guide_cam_orientation * u.deg)) if 'mount_model' not in locals(): print( 'Aborting: No model could be loaded. To refresh stored model run align program.' ) sys.exit(1) # Create object with base type TelescopeMount mount = mounts.make_mount_from_args(args) telem_logger = telem.make_telem_logger_from_args(args) target = targets.make_target_from_args( args, mount, mount_model, MeridianSide[args.meridian_side.upper()], telem_logger=telem_logger, ) tracker = Tracker( mount=mount, mount_model=mount_model, target=target, telem_logger=telem_logger, ) try: laser_pointer = laser.make_laser_from_args(args) except OSError: print('Could not connect to laser pointer FTDI device.') laser_pointer = None telem_sources = {} try: # Create gamepad object and register callback game_pad = Gamepad() if laser_pointer is not None: game_pad.register_callback('BTN_SOUTH', laser_pointer.set) tracker.register_callback(gamepad_callback) telem_sources['gamepad'] = game_pad print('Gamepad found and registered.') except RuntimeError: print('No gamepads found.') if telem_logger is not None: telem_logger.register_sources(telem_sources) telem_logger.start() stopping_conditions = control.make_stop_conditions_from_args(args) try: tracker.run(stopping_conditions) except KeyboardInterrupt: print('Got CTRL-C, shutting down...') finally: # don't rely on destructors to safe mount! print('Safing mount...', end='', flush=True) if mount.safe(): print('Mount safed successfully!') else: print('Warning: Mount may be in an unsafe state!') try: game_pad.stop() except UnboundLocalError: pass if telem_logger is not None: telem_logger.stop()
coord_detilt = coord.hcrs.cartesian.transform(_SUN_DETILT_MATRIX) return coord_detilt.represent_as(SphericalRepresentation).lon.to('deg') # J2000.0 epoch _J2000 = Time('J2000.0', scale='tt') # One of the two nodes of intersection between the ICRF equator and Sun's equator in HCRS _NODE = SkyCoord(_SOLAR_NORTH_POLE_HCRS.lon + 90 * u.deg, 0 * u.deg, frame='hcrs') # The longitude in the de-tilted frame of the Sun's prime meridian. # The IAU (Seidelmann et al. 2007 and later) defines the true longitude of the meridian (i.e., # without light travel time to Earth and aberration effects) as 84.176 degrees eastward at J2000. _DLON_MERIDIAN = Longitude(_detilt_lon(_NODE) + constants.get('W_0')) @add_common_docstring(**_variables_for_parse_time_docstring()) def L0(time='now', light_travel_time_correction=True, nearest_point=True, aberration_correction=False): """ Return the L0 angle for the Sun at a specified time, which is the apparent Carrington longitude of the Sun-disk center as seen from Earth. Observer corrections can be disabled, and then this function will instead return the true Carrington longitude. Parameters
brightness = str(row[14]) #print(row[12]) #account for brightness modifier, if '.' then make a little dimmer, if ':' then make a little brighter if row[15] == 'f' : brightnessFixed = float(brightness)+.3 elif row[15] == 'b' : brightnessFixed = float(brightness)-.3 else : brightnessFixed = float(brightness) #brightnessFixed = brightness.translate(None, string.punctuation) # Longitudes are measured as degrees from a zodic sign, which is in column [7] of the data ra = Longitude(((row[8])*30+row[9],row[10]), unit=u.deg) dec = Latitude((signLat*row[11],row[12]), unit=u.deg) #The Equinox of 1601 is chosen for the Tycho Data Set. object = SkyCoord(ra,dec,distance=10*u.pc, frame='geocentrictrueecliptic', equinox='+01437-01-01T12:00:00.0') astar = " "+str(object.galactic.cartesian.x.value)+" "+ \ str(object.galactic.cartesian.y.value)+" "+ \ str(object.galactic.cartesian.z.value)+" "+ \ str(.5)+" "+\ str(brightnessFixed)+" "+\ str(brightnessFixed)+" "+\ str(brightnessFixed)+" " astarLabel = " "+str(object.galactic.cartesian.x.value)+" "+ \
def prepare_plot(self, cats=None, mnts=None, imgs=None, nmls=None, selected=None, model=None): stars = list() for i, ct in enumerate(cats): if not i in selected: #self.lg.debug('star: {} dropped'.format(i)) continue mt = mnts[i] # readability mts = mt.represent_as(SphericalRepresentation) # ToDo may not the end of the story cts = ct.represent_as(SphericalRepresentation) df_lon = Longitude(cts.lon.radian - mts.lon.radian, u.radian, wrap_angle=Angle(np.pi, u.radian)) df_lat = Latitude(cts.lat.radian - mts.lat.radian, u.radian) #print(df_lat,df_lon) #if df_lat.radian < 0./60./180.*np.pi: # pass #elif df_lat.radian > 20./60./180.*np.pi: # pass #else: # continue # residuum: difference st.cats(fit corrected) - st.star # res_lon = Longitude(float( model.d_lon(cts.lon.radian, cts.lat.radian, cts.lon.radian - mts.lon.radian)), u.radian, wrap_angle=Angle(np.pi, u.radian)) res_lat = Latitude( float( model.d_lat(cts.lon.radian, cts.lat.radian, cts.lat.radian - mts.lat.radian)), u.radian) try: image_fn = imgs[i] except: image_fn = 'no image file' try: nml_id = nmls[i] except: nml_id = 'no nml_id' st = Point( cat_lon=cts.lon, cat_lat=cts.lat, mnt_lon=mts.lon, mnt_lat=mts.lat, df_lat=df_lat, df_lon=df_lon, res_lat=res_lat, res_lon=res_lon, image_fn=image_fn, nml_id=nml_id, ) stars.append(st) return stars
def rot_hpc(x, y, tstart, tend, frame_time='synodic', rot_type='howard', **kwargs): """Given a location on the Sun referred to using the Helioprojective Cartesian co-ordinate system (typically quoted in the units of arcseconds) use the solar rotation profile to find that location at some later or earlier time. Note that this function assumes that the data was observed from the Earth or near Earth vicinity. Specifically, data from SOHO and STEREO observatories are not supported. Note also that the function does NOT use solar B0 and L0 values provided in source FITS files - these quantities are calculated. Parameters ---------- x : `~astropy.units.Quantity` Helio-projective x-co-ordinate in arcseconds (can be an array). y : `~astropy.units.Quantity` Helio-projective y-co-ordinate in arcseconds (can be an array). tstart : `sunpy.time.time` date/time to which x and y are referred. tend : `sunpy.time.time` date/time at which x and y will be rotated to. rot_type : {'howard' | 'snodgrass' | 'allen'} | howard: Use values for small magnetic features from Howard et al. | snodgrass: Use Values from Snodgrass et. al | allen: Use values from Allen, Astrophysical Quantities, and simpler equation. frame_time: {'sidereal' | 'synodic'} Choose type of day time reference frame. Returns ------- x : `~astropy.units.Quantity` Rotated helio-projective x-co-ordinate in arcseconds (can be an array). y : `~astropy.units.Quantity` Rotated helio-projective y-co-ordinate in arcseconds (can be an array). Examples -------- >>> import astropy.units as u >>> from sunpy.physics.transforms.differential_rotation import rot_hpc >>> rot_hpc( -570 * u.arcsec, 120 * u.arcsec, '2010-09-10 12:34:56', '2010-09-10 13:34:56') (<Angle -562.9105822671319 arcsec>, <Angle 119.31920621992195 arcsec>) Notes ----- SSWIDL code equivalent: http://hesperia.gsfc.nasa.gov/ssw/gen/idl/solar/rot_xy.pro . The function rot_xy uses arcmin2hel.pro and hel2arcmin.pro to implement the same functionality as this function. These two functions seem to perform inverse operations of each other to a high accuracy. The corresponding equivalent functions here are convert_hpc_hg and convert_hg_hpc respectively. These two functions seem to perform inverse operations of each other to a high accuracy. However, the values returned by arcmin2hel.pro are slightly different from those provided by convert_hpc_hg. This leads to very slightly different results from rot_hpc compared to rot_xy. """ # must have pairs of co-ordinates if np.array(x).shape != np.array(y).shape: raise ValueError('Input co-ordinates must have the same shape.') # Make sure we have enough time information to perform a solar differential # rotation # Start time dstart = parse_time(tstart) dend = parse_time(tend) interval = (dend - dstart).total_seconds() * u.s # Get the Sun's position from the vantage point at the start time vstart = kwargs.get("vstart", _calc_P_B0_SD(dstart)) # Compute heliographic co-ordinates - returns (longitude, latitude). Points # off the limb are returned as nan longitude, latitude = convert_hpc_hg(x.to(u.arcsec).value, y.to(u.arcsec).value, b0_deg=vstart["b0"].to(u.deg).value, l0_deg=vstart["l0"].to(u.deg).value, dsun_meters=(constants.au * sun.sunearth_distance(t=dstart)).value, angle_units='arcsec') longitude = Longitude(longitude, u.deg) latitude = Angle(latitude, u.deg) # Compute the differential rotation drot = diff_rot(interval, latitude, frame_time=frame_time, rot_type=rot_type) # Convert back to heliocentric cartesian in units of arcseconds vend = kwargs.get("vend", _calc_P_B0_SD(dend)) # It appears that there is a difference in how the SSWIDL function # hel2arcmin and the sunpy function below performs this co-ordinate # transform. newx, newy = convert_hg_hpc(longitude.to(u.deg).value + drot.to(u.deg).value, latitude.to(u.deg).value, b0_deg=vend["b0"].to(u.deg).value, l0_deg=vend["l0"].to(u.deg).value, dsun_meters=(constants.au * sun.sunearth_distance(t=dend)).value, occultation=False) newx = Angle(newx, u.arcsec) newy = Angle(newy, u.arcsec) return newx.to(u.arcsec), newy.to(u.arcsec)
def __init__(self, lon_0, lat_0, sigma): self.parameters = Parameters([ Parameter("lon_0", Longitude(lon_0)), Parameter("lat_0", Latitude(lat_0)), Parameter("sigma", Angle(sigma), min=0), ])
def test_array(seconds_per_day): rot = diff_rot(10 * seconds_per_day, np.linspace(-70, 70, 2) * u.deg) assert_quantity_allclose(rot, Longitude(np.array([110.2725, 110.2725]) * u.deg), rtol=1e-3)
def __init__(self, lon_0, lat_0): self.parameters = Parameters([ Parameter("lon_0", Longitude(lon_0)), Parameter("lat_0", Latitude(lat_0)) ])
def diff_rot(duration: u.s, latitude: u.deg, rot_type='howard', frame_time='sidereal'): """ This function computes the change in longitude over days in degrees. Parameters ----------- duration : `~astropy.units.Quantity` Number of seconds to rotate over. latitude : `~astropy.units.Quantity` heliographic coordinate latitude in Degrees. rot_type : `str` The differential rotation model to use. One of: | ``howard`` : Use values from Howard et al. (1990) | ``snodgrass`` : Use values from Snodgrass et. al. (1983) | ``allen`` : Use values from Allen's Astrophysical Quantities, and simpler equation. frame_time : `str` One of : ``'sidereal'`` or ``'synodic'``. Choose 'type of day' time reference frame. Returns ------- longitude_delta : `~astropy.units.Quantity` The change in longitude over days (units=degrees) References ---------- * `IDL code equivalent <https://hesperia.gsfc.nasa.gov/ssw/gen/idl/solar/diff_rot.pro>`__ * `Solar surface velocity fields determined from small magnetic features (Howard et al. 1990) <https://doi.org/10.1007/BF00156795>`__ * `A comparison of differential rotation measurements (Beck 2000, includes Snodgrass values) <https://doi.org/10.1023/A:1005226402796>`__ Examples -------- Default rotation calculation over two days at 30 degrees latitude: >>> import numpy as np >>> import astropy.units as u >>> from sunpy.physics.differential_rotation import diff_rot >>> rotation = diff_rot(2 * u.day, 30 * u.deg) Default rotation over two days for a number of latitudes: >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg) With rotation type 'allen': >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg, 'allen') """ latitude = latitude.to(u.deg) sin2l = (np.sin(latitude))**2 sin4l = sin2l**2 rot_params = { 'howard': [2.894, -0.428, -0.370] * u.urad / u.second, 'snodgrass': [2.851, -0.343, -0.474] * u.urad / u.second, 'allen': [14.44, -3.0, 0] * u.deg / u.day } if rot_type not in ['howard', 'allen', 'snodgrass']: raise ValueError("rot_type must equal one of " "{{ {} }}".format(" | ".join(rot_params.keys()))) A, B, C = rot_params[rot_type] # This calculation of the rotation assumes a sidereal frame time. rotation = (A + B * sin2l + C * sin4l) * duration # Applying this correction assumes that the observer is on the Earth, # and that the Earth is at the same distance from the Sun at all times # during the year. if frame_time == 'synodic': rotation -= 0.9856 * u.deg / u.day * duration return Longitude(rotation.to(u.deg))
def healpix_to_lonlat(healpix_index, nside, dx=None, dy=None, order='ring'): """ Convert HEALPix indices (optionally with offsets) to longitudes/latitudes. If no offsets (``dx`` and ``dy``) are provided, the coordinates will default to those at the center of the HEALPix pixels. Parameters ---------- healpix_index : int or `~numpy.ndarray` HEALPix indices (as a scalar or array) nside : int Number of pixels along the side of each of the 12 top-level HEALPix tiles dx, dy : float or `~numpy.ndarray`, optional Offsets inside the HEALPix pixel, which must be in the range [0:1], where 0.5 is the center of the HEALPix pixels (as scalars or arrays) order : { 'nested' | 'ring' }, optional Order of HEALPix pixels Returns ------- lon : :class:`~astropy.coordinates.Longitude` The longitude values lat : :class:`~astropy.coordinates.Latitude` The latitude values """ if (dx is None and dy is not None) or (dx is not None and dy is None): raise ValueError('Either both or neither dx and dy must be specified') healpix_index = np.asarray(healpix_index, dtype=np.int64) if dx is None and dy is not None: dx = 0.5 elif dx is not None and dy is None: dy = 0.5 if dx is not None: dx = np.asarray(dx, dtype=np.float) dy = np.asarray(dy, dtype=np.float) _validate_offset('x', dx) _validate_offset('y', dy) healpix_index, dx, dy = np.broadcast_arrays(healpix_index, dx, dy) dx = dx.ravel() dy = dy.ravel() shape = healpix_index.shape healpix_index = healpix_index.ravel() nside = int(nside) _validate_healpix_index('healpix_index', healpix_index, nside) _validate_nside(nside) order = _validate_order(order) if dx is None: lon, lat = core_cython.healpix_to_lonlat(healpix_index, nside, order) else: lon, lat = core_cython.healpix_with_offset_to_lonlat(healpix_index, dx, dy, nside, order) lon = Longitude(lon, unit=u.rad, copy=False) lat = Latitude(lat, unit=u.rad, copy=False) return _restore_shape(lon, lat, shape=shape)
sc, 'scc': scc, 'scd': SkyCoord([1, 2], [3, 4], [5, 6], unit='deg,deg,m', frame='fk4', obstime=['J1990.5'] * 2), 'x': [1, 2] * u.m, 'qdb': [10, 20] * u.dB(u.mW), 'qdex': [4.5, 5.5] * u.dex(u.cm / u.s**2), 'qmag': [21, 22] * u.ABmag, 'lat': Latitude([1, 2] * u.deg), 'lon': Longitude([1, 2] * u.deg, wrap_angle=180. * u.deg), 'ang': Angle([1, 2] * u.deg), 'el': el, 'sr': sr, 'cr': cr, 'sd': sd, 'srd': srd, # 'nd': NdarrayMixin(el) # not supported yet }
def doTest(): print(colored('Testing Coordinates Module', 'magenta')) print(colored('Testing Ra', 'magenta')) print(colored('Testing Default Constructor', 'cyan')) ra = NumCpp.RaDouble() print(colored('\tPASS', 'green')) print(colored('Testing Degree Constructor', 'cyan')) randDegrees = np.random.rand(1).item() * 360 ra = NumCpp.RaDouble(randDegrees) raPy = Longitude(randDegrees, unit=u.deg) if (round(ra.degrees(), 9) == round(randDegrees, 9) and ra.hours() == raPy.hms.h and ra.minutes() == raPy.hms.m and round(ra.seconds(), 9) == round(raPy.hms.s, 9) and round(ra.radians(), 9) == round(np.deg2rad(randDegrees), 9)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing hms Constructor', 'cyan')) hours = np.random.randint(0, 24, [1,], dtype=np.uint8).item() minutes = np.random.randint(0, 60, [1, ], dtype=np.uint8).item() seconds = np.random.rand(1).astype(np.float32).item() * 60 ra = NumCpp.RaDouble(hours, minutes, seconds) degreesPy = (hours + minutes / 60 + seconds / 3600) * 15 if (round(ra.degrees(), 9) == round(degreesPy, 9) and ra.hours() == hours and ra.minutes() == minutes and round(ra.seconds(), 9) == round(seconds, 9) and round(ra.radians(), 9) == round(np.deg2rad(degreesPy), 9)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing astype', 'cyan')) raF = ra.asFloat() if (round(ra.degrees(), 4) == round(raF.degrees(), 4) and ra.hours() == raF.hours() and ra.minutes() == raF.minutes() and round(ra.seconds(), 4) == round(raF.seconds(), 4) and round(ra.radians(), 4) == round(raF.radians(), 4)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing equality operator', 'cyan')) ra2 = NumCpp.RaDouble(ra) if ra == ra2: print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing not equality operator', 'cyan')) randDegrees = np.random.rand(1).item() * 360 ra2 = NumCpp.RaDouble(randDegrees) if ra != ra2: print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing print', 'cyan')) ra.print() print(colored('\tPASS', 'green')) print(colored('Testing Dec', 'magenta')) print(colored('Testing Default Constructor', 'cyan')) dec = NumCpp.DecDouble() print(colored('\tPASS', 'green')) print(colored('Testing Degree Constructor', 'cyan')) randDegrees = np.random.rand(1).item() * 180 - 90 dec = NumCpp.DecDouble(randDegrees) decPy = Latitude(randDegrees, unit=u.deg) sign = NumCpp.Sign.NEGATIVE if randDegrees < 0 else NumCpp.Sign.POSITIVE if (round(dec.degrees(), 8) == round(randDegrees, 8) and dec.sign() == sign and dec.degreesWhole() == abs(decPy.dms.d) and dec.minutes() == abs(decPy.dms.m) and round(dec.seconds(), 8) == round(abs(decPy.dms.s), 8) and round(dec.radians(), 8) == round(np.deg2rad(randDegrees), 8)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing dms Constructor', 'cyan')) sign = NumCpp.Sign.POSITIVE if np.random.randint(-1, 1) == 0 else NumCpp.Sign.NEGATIVE degrees = np.random.randint(0, 91, [1, ], dtype=np.uint8).item() minutes = np.random.randint(0, 60, [1, ], dtype=np.uint8).item() seconds = np.random.rand(1).astype(np.float32).item() * 60 dec = NumCpp.DecDouble(sign, degrees, minutes, seconds) degreesPy = degrees + minutes / 60 + seconds / 3600 if sign == NumCpp.Sign.NEGATIVE: degreesPy *= -1 if (dec.sign() == sign and round(dec.degrees(), 9) == round(degreesPy, 9) and dec.degreesWhole() == degrees and dec.minutes() == minutes and round(dec.seconds(), 9) == round(seconds, 9) and round(dec.radians(), 8) == round(np.deg2rad(degreesPy), 8)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing astype', 'cyan')) decF = dec.asFloat() if (round(dec.degrees(), 4) == round(decF.degrees(), 4) and dec.sign() == decF.sign() and dec.degreesWhole() == decF.degreesWhole() and dec.minutes() == decF.minutes() and round(dec.seconds(), 4) == round(decF.seconds(), 4) and round(dec.radians(), 4) == round(decF.radians(), 4)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing equality operator', 'cyan')) dec2 = NumCpp.DecDouble(dec) if dec == dec2: print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing not equality operator', 'cyan')) randDegrees = np.random.rand(1).item() * 180 - 90 dec2 = NumCpp.DecDouble(randDegrees) if dec != dec2: print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing print', 'cyan')) dec.print() print(colored('\tPASS', 'green')) print(colored('Testing Coordinate', 'magenta')) print(colored('Testing Default Constructor', 'cyan')) coord = NumCpp.CoordinateDouble() print(colored('\tPASS', 'green')) print(colored('Testing Degree Constructor', 'cyan')) raDegrees = np.random.rand(1).item() * 360 ra = NumCpp.RaDouble(raDegrees) decDegrees = np.random.rand(1).item() * 180 - 90 dec = NumCpp.DecDouble(decDegrees) pyCoord = SkyCoord(raDegrees, decDegrees, unit=u.deg) cCoord = NumCpp.CoordinateDouble(raDegrees, decDegrees) if (cCoord.ra() == ra and cCoord.dec() == dec and round(cCoord.x(), 10) == round(pyCoord.cartesian.x.value, 10) and round(cCoord.y(), 10) == round(pyCoord.cartesian.y.value, 10) and round(cCoord.z(), 10) == round(pyCoord.cartesian.z.value, 10)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing Ra/Dec Constructor', 'cyan')) raDegrees = np.random.rand(1).item() * 360 ra = NumCpp.RaDouble(raDegrees) decDegrees = np.random.rand(1).item() * 180 - 90 dec = NumCpp.DecDouble(decDegrees) pyCoord = SkyCoord(raDegrees, decDegrees, unit=u.deg) cCoord = NumCpp.CoordinateDouble(ra, dec) if (cCoord.ra() == ra and cCoord.dec() == dec and round(cCoord.x(), 10) == round(pyCoord.cartesian.x.value, 10) and round(cCoord.y(), 10) == round(pyCoord.cartesian.y.value, 10) and round(cCoord.z(), 10) == round(pyCoord.cartesian.z.value, 10)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing x/y/z Constructor', 'cyan')) raDegrees = np.random.rand(1).item() * 360 ra = NumCpp.RaDouble(raDegrees) decDegrees = np.random.rand(1).item() * 180 - 90 dec = NumCpp.DecDouble(decDegrees) pyCoord = SkyCoord(raDegrees, decDegrees, unit=u.deg) cCoord = NumCpp.CoordinateDouble(pyCoord.cartesian.x.value, pyCoord.cartesian.y.value, pyCoord.cartesian.z.value) if (round(cCoord.ra().degrees(), 9) == round(ra.degrees(), 9) and round(cCoord.dec().degrees(), 9) == round(dec.degrees(), 9) and round(cCoord.x(), 9) == round(pyCoord.cartesian.x.value, 9) and round(cCoord.y(), 9) == round(pyCoord.cartesian.y.value, 9) and round(cCoord.z(), 9) == round(pyCoord.cartesian.z.value, 9)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing NdArray Constructor', 'cyan')) raDegrees = np.random.rand(1).item() * 360 ra = NumCpp.RaDouble(raDegrees) decDegrees = np.random.rand(1).item() * 180 - 90 dec = NumCpp.DecDouble(decDegrees) pyCoord = SkyCoord(raDegrees, decDegrees, unit=u.deg) vec = np.asarray([pyCoord.cartesian.x.value, pyCoord.cartesian.y.value, pyCoord.cartesian.z.value]) cVec = NumCpp.NdArray(1, 3) cVec.setArray(vec) cCoord = NumCpp.CoordinateDouble(cVec) if (round(cCoord.ra().degrees(), 9) == round(ra.degrees(), 9) and round(cCoord.dec().degrees(), 9) == round(dec.degrees(), 9) and round(cCoord.x(), 9) == round(pyCoord.cartesian.x.value, 9) and round(cCoord.y(), 9) == round(pyCoord.cartesian.y.value, 9) and round(cCoord.z(), 9) == round(pyCoord.cartesian.z.value, 9)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing hms/dms Constructor', 'cyan')) raHours = np.random.randint(0, 24, [1,], dtype=np.uint8).item() raMinutes = np.random.randint(0, 60, [1, ], dtype=np.uint8).item() raSeconds = np.random.rand(1).astype(np.float32).item() * 60 raDegreesPy = (raHours + raMinutes / 60 + raSeconds / 3600) * 15 decSign = NumCpp.Sign.POSITIVE if np.random.randint(-1, 1) == 0 else NumCpp.Sign.NEGATIVE decDegrees = np.random.randint(0, 91, [1, ], dtype=np.uint8).item() decMinutes = np.random.randint(0, 60, [1, ], dtype=np.uint8).item() decSeconds = np.random.rand(1).astype(np.float32).item() * 60 decDegreesPy = decDegrees + decMinutes / 60 + decSeconds / 3600 if decSign == NumCpp.Sign.NEGATIVE: decDegreesPy *= -1 cCoord = NumCpp.CoordinateDouble(raHours, raMinutes, raSeconds, decSign, decDegrees, decMinutes, decSeconds) cRa = cCoord.ra() cDec = cCoord.dec() pyCoord = SkyCoord(raDegreesPy, decDegreesPy, unit=u.deg) if (round(cRa.degrees(), 9) == round(raDegreesPy, 9) and cRa.hours() == raHours and cRa.minutes() == raMinutes and round(cRa.seconds(), 9) == round(raSeconds, 9) and cDec.sign() == decSign and round(cDec.degrees(), 9) == round(decDegreesPy, 9) and cDec.degreesWhole() == decDegrees and cDec.minutes() == decMinutes and round(cDec.seconds(), 9) == round(decSeconds, 9) and round(cCoord.x(), 9) == round(pyCoord.cartesian.x.value, 9) and round(cCoord.y(), 9) == round(pyCoord.cartesian.y.value, 9) and round(cCoord.z(), 9) == round(pyCoord.cartesian.z.value, 9)): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing equality operator', 'cyan')) cCoord2 = NumCpp.CoordinateDouble(cCoord) if cCoord2 == cCoord: print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing not equality operator', 'cyan')) raDegrees = np.random.rand(1).item() * 360 decDegrees = np.random.rand(1).item() * 180 - 90 cCoord2 = NumCpp.CoordinateDouble(raDegrees, decDegrees) if cCoord2 != cCoord: print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing xyz', 'cyan')) xyz = [cCoord.x(), cCoord.y(), cCoord.z()] if np.array_equal(cCoord.xyz().getNumpyArray().flatten(), xyz): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing degreeSeperation Coordinate', 'cyan')) pyCoord2 = SkyCoord(cCoord2.ra().degrees(), cCoord2.dec().degrees(), unit=u.deg) cDegSep = cCoord.degreeSeperation(cCoord2) pyDegSep = pyCoord.separation(pyCoord2).value if round(cDegSep, 9) == round(pyDegSep, 9): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing radianSeperation Coordinate', 'cyan')) cRadSep = cCoord.radianSeperation(cCoord2) pyRadSep = np.deg2rad(pyDegSep) if round(cRadSep, 9) == round(pyRadSep, 9): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing degreeSeperation Vector', 'cyan')) vec2 = np.asarray([pyCoord2.cartesian.x, pyCoord2.cartesian.y, pyCoord2.cartesian.z]) cArray = NumCpp.NdArray(1, 3) cArray.setArray(vec2) cDegSep = cCoord.degreeSeperation(cArray) if round(cDegSep, 9) == round(pyDegSep, 9): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing radianSeperation Vector', 'cyan')) cArray = NumCpp.NdArray(1, 3) cArray.setArray(vec2) cDegSep = cCoord.radianSeperation(cArray) if round(cDegSep, 9) == round(pyRadSep, 9): print(colored('\tPASS', 'green')) else: print(colored('\tFAIL', 'red')) print(colored('Testing print', 'cyan')) cCoord.print() print(colored('\tPASS', 'green'))
def add_wcs_coordinates(objects, catParNames, catParFormt, catParUnits, Parameters): try: hdulist = fits.open(Parameters["import"]["inFile"]) header = hdulist[0].header hdulist.close() # Fix headers where "per second" is written "/S" instead of "/s" # (assuming they mean "per second" and not "per Siemens"). if "cunit3" in header and "/S" in header["cunit3"]: err.warning("Converting '/S' to '/s' in CUNIT3.") header["cunit3"] = header["cunit3"].replace("/S","/s") # Check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [k for k in ["FREQ-OHEL", "FREQ-OLSR", "FREQ-RHEL", "FREQ-RLSR"] if (k in [header[key] for key in header if ("CTYPE" in key)])] if gipsyKey: err.message("GIPSY header found. Trying to convert to FITS standard.") from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr("VOPT-F2W") #if header["naxis"] == 4: # objects = np.concatenate((objects, wcsin.p2s(np.concatenate((objects[:, catParNames.index("x"):catParNames. index("x") + 3], np.zeros((objects.shape[0], 1))), axis=1), 0)["world"][:,:-1]), axis=1) #else: # objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.cunit]) catParNames = tuple(list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.ctype]) catParFormt = tuple(list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) else: # Constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound = 0 for kk in range(header["naxis"]): if header["ctype1"][:2] == "RA": rafound = 1 break if rafound: if header["crval%i" % (kk + 1)] < 0: err.warning("Adding 360 deg to RA reference value.") header["crval%i" % (kk + 1)] += 360 elif header["crval%i" % (kk + 1)] > 360: err.warning("Subtracting 360 deg from RA reference value.") header["crval%i" % (kk + 1)] -= 360 #if header["naxis"] == 4: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL, wcs.WCSSUB_STOKES]) #else: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) xyz = objects[:, catParNames.index("x"):catParNames.index("x") + 3].astype(float) if "cellscal" in header and header["cellscal"] == "1/F": err.warning( "CELLSCAL keyword with value of 1/F found.\n" "Will account for varying pixel scale in WCS coordinate calculation.") x0, y0 = header["crpix1"] - 1, header["crpix2"] - 1 # Will calculate the pixscale factor of each channel as: # pixscale = ref_frequency / frequency if header["ctype3"] == "VELO-HEL": pixscale = (1 - header["crval3"] / scipy.constants.c) / (1 - (((xyz[:, 2] + 1) - header["crpix3"]) * header["cdelt3"] + header["crval3"]) / scipy.constants.c) else: err.warning("Cannot convert 3rd axis coordinates to frequency. Ignoring the effect of CELLSCAL = 1/F.") pixscale = 1.0 xyz[:, 0] = (xyz[:, 0] - x0) * pixscale + x0 xyz[:, 1] = (xyz[:, 1] - y0) * pixscale + y0 #if header["naxis"] == 4: objects = np.concatenate((objects, wcsin.wcs_pix2world(np.concatenate((xyz, np.zeros((objects.shape[0], 1))), axis=1), 0)[:, :-1]), axis=1) #else: objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) catParUnits = tuple(list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.wcs.cunit]) catParNames = tuple(list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.wcs.ctype]) catParFormt = tuple(list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) #if header["naxis"] == 4: # catParUnits = catParUnits[:-1] # catParNames= catParNames[:-1] err.message("WCS coordinates added to catalogue.") # Create IAU-compliant source name: # WARNING: This currently assumes a regular, ≥ 2-dim. data cube where the first two axes are longitude and latitude. n_src = objects.shape[0] n_par = objects.shape[1] iau_names = np.empty([n_src, 1], dtype=object) if header["ctype1"][:4] == "RA--": # Equatorial coordinates; try to figure out equinox: iau_coord = "equ" if "equinox" in header: if int(header["equinox"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" elif "epoch" in header: # Assume that EPOCH has been abused to record the equinox: if int(header["epoch"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" else: # Equinox undefined: iau_equinox = "X" elif header["ctype1"][:4] == "GLON": # Galactic coordinates: iau_coord = "gal" iau_equinox = "G" else: # Unsupported coordinate system: iau_coord = "" iau_equinox = "" for src in xrange(n_src): lon = objects[src][n_par - 3] lat = objects[src][n_par - 2] if iau_coord == "equ": ra = Longitude(lon, unit=u.deg) dec = Latitude(lat, unit=u.deg) iau_pos = ra.to_string(unit=u.h, decimal=False, sep="", precision=2, alwayssign=False, pad=True, fields=3) iau_pos += dec.to_string(unit=u.deg, decimal=False, sep="", precision=1, alwayssign=True, pad=True, fields=3) else: iau_pos = "{0:08.4f}".format(lon) if lat < 0.0: iau_pos += "-" else: iau_pos += "+" iau_pos += "{0:07.4f}".format(abs(lat)) iau_names[src][0] = "SoFiA " + iau_equinox + iau_pos objects = np.concatenate((objects, iau_names), axis = 1) catParUnits = tuple(list(catParUnits) + ["-"]) catParNames = tuple(list(catParNames) + ["name"]) catParFormt = tuple(list(catParFormt) + ["%30s"]) except: err.warning("WCS conversion of parameters failed.") return (objects, catParNames, catParFormt, catParUnits)
def add_wcs_coordinates(objects, catParNames, catParFormt, catParUnits, Parameters): try: hdulist = fits.open(Parameters["import"]["inFile"]) header = hdulist[0].header hdulist.close() # Fix headers where "per second" is written "/S" instead of "/s" # (assuming they mean "per second" and not "per Siemens"). if "cunit3" in header and "/S" in header["cunit3"]: err.warning("Converting '/S' to '/s' in CUNIT3.") header["cunit3"] = header["cunit3"].replace("/S", "/s") # Check if there is a Nmap/GIPSY FITS header keyword value present gipsyKey = [ k for k in ["FREQ-OHEL", "FREQ-OLSR", "FREQ-RHEL", "FREQ-RLSR"] if (k in [header[key] for key in header if ("CTYPE" in key)]) ] if gipsyKey: err.message( "GIPSY header found. Trying to convert to FITS standard.") from astropy.wcs import Wcsprm header = fix_gipsy_header(header) wcsin = Wcsprm(str(header)) wcsin.sptr("VOPT-F2W") #if header["naxis"] == 4: # objects = np.concatenate((objects, wcsin.p2s(np.concatenate((objects[:, catParNames.index("x"):catParNames. index("x") + 3], np.zeros((objects.shape[0], 1))), axis=1), 0)["world"][:,:-1]), axis=1) #else: # objects = np.concatenate((objects, wcsin.p2s(objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) objects = np.concatenate( (objects, wcsin.p2s( objects[:, catParNames.index("x"):catParNames.index("x") + 3], 0)["world"]), axis=1) catParUnits = tuple( list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.cunit]) catParNames = tuple( list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.ctype]) catParFormt = tuple( list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) else: # Constrain the RA axis reference value CRVAL_ to be between 0 and 360 deg rafound = 0 for kk in range(header["naxis"]): if header["ctype1"][:2] == "RA": rafound = 1 break if rafound: if header["crval%i" % (kk + 1)] < 0: err.warning("Adding 360 deg to RA reference value.") header["crval%i" % (kk + 1)] += 360 elif header["crval%i" % (kk + 1)] > 360: err.warning("Subtracting 360 deg from RA reference value.") header["crval%i" % (kk + 1)] -= 360 #if header["naxis"] == 4: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL, wcs.WCSSUB_STOKES]) #else: wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) wcsin = wcs.WCS(header, naxis=[wcs.WCSSUB_CELESTIAL, wcs.WCSSUB_SPECTRAL]) xyz = objects[:, catParNames.index("x"):catParNames.index("x") + 3].astype(float) if "cellscal" in header and header["cellscal"] == "1/F": err.warning( "CELLSCAL keyword with value of 1/F found.\n" "Will account for varying pixel scale in WCS coordinate calculation." ) x0, y0 = header["crpix1"] - 1, header["crpix2"] - 1 # Will calculate the pixscale factor of each channel as: # pixscale = ref_frequency / frequency if header["ctype3"] == "VELO-HEL": pixscale = (1 - header["crval3"] / scipy.constants.c) / ( 1 - (((xyz[:, 2] + 1) - header["crpix3"]) * header["cdelt3"] + header["crval3"]) / scipy.constants.c) else: err.warning( "Cannot convert 3rd axis coordinates to frequency. Ignoring the effect of CELLSCAL = 1/F." ) pixscale = 1.0 xyz[:, 0] = (xyz[:, 0] - x0) * pixscale + x0 xyz[:, 1] = (xyz[:, 1] - y0) * pixscale + y0 #if header["naxis"] == 4: objects = np.concatenate((objects, wcsin.wcs_pix2world(np.concatenate((xyz, np.zeros((objects.shape[0], 1))), axis=1), 0)[:, :-1]), axis=1) #else: objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) objects = np.concatenate((objects, wcsin.wcs_pix2world(xyz, 0)), axis=1) catParUnits = tuple( list(catParUnits) + [str(cc).replace(" ", "") for cc in wcsin.wcs.cunit]) catParNames = tuple( list(catParNames) + [(cc.split("--")[0]).lower() for cc in wcsin.wcs.ctype]) catParFormt = tuple( list(catParFormt) + ["%15.7e", "%15.7e", "%15.7e"]) #if header["naxis"] == 4: # catParUnits = catParUnits[:-1] # catParNames= catParNames[:-1] err.message("WCS coordinates added to catalogue.") # Create IAU-compliant source name: # WARNING: This currently assumes a regular, ≥ 2-dim. data cube where the first two axes are longitude and latitude. n_src = objects.shape[0] n_par = objects.shape[1] iau_names = np.empty([n_src, 1], dtype=object) if header["ctype1"][:4] == "RA--": # Equatorial coordinates; try to figure out equinox: iau_coord = "equ" if "equinox" in header: if int(header["equinox"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" elif "epoch" in header: # Assume that EPOCH has been abused to record the equinox: if int(header["epoch"]) >= 2000: iau_equinox = "J" else: iau_equinox = "B" else: # Equinox undefined: iau_equinox = "X" elif header["ctype1"][:4] == "GLON": # Galactic coordinates: iau_coord = "gal" iau_equinox = "G" else: # Unsupported coordinate system: iau_coord = "" iau_equinox = "" for src in xrange(n_src): lon = objects[src][n_par - 3] lat = objects[src][n_par - 2] if iau_coord == "equ": ra = Longitude(lon, unit=u.deg) dec = Latitude(lat, unit=u.deg) iau_pos = ra.to_string(unit=u.h, decimal=False, sep="", precision=2, alwayssign=False, pad=True, fields=3) iau_pos += dec.to_string(unit=u.deg, decimal=False, sep="", precision=1, alwayssign=True, pad=True, fields=3) else: iau_pos = "{0:08.4f}".format(lon) if lat < 0.0: iau_pos += "-" else: iau_pos += "+" iau_pos += "{0:07.4f}".format(abs(lat)) iau_names[src][0] = "SoFiA " + iau_equinox + iau_pos objects = np.concatenate((objects, iau_names), axis=1) catParUnits = tuple(list(catParUnits) + ["-"]) catParNames = tuple(list(catParNames) + ["name"]) catParFormt = tuple(list(catParFormt) + ["%30s"]) except: err.warning("WCS conversion of parameters failed.") return (objects, catParNames, catParFormt, catParUnits)