Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of astro_metadata_translator. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the LICENSE file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# Use of this source code is governed by a 3-clause BSD-style 

10# license that can be found in the LICENSE file. 

11 

12"""Generically useful translation helpers which translation classes 

13can use. 

14 

15They are written as free functions. Some of them are written 

16as if they are methods of `MetadataTranslator`, allowing them to be attached 

17to translator classes that need them. These methods have full access to 

18the translator methods. 

19 

20Other functions are pure helpers that can be imported and used to help 

21translation classes without using `MetadataTranslator` properties. 

22 

23""" 

24 

25__all__ = ("to_location_via_telescope_name", 

26 "is_non_science", 

27 "tracking_from_degree_headers", 

28 "altitude_from_zenith_distance") 

29 

30import logging 

31from astropy.coordinates import EarthLocation, SkyCoord, AltAz 

32import astropy.units as u 

33 

34log = logging.getLogger(__name__) 

35 

36 

37def to_location_via_telescope_name(self): 

38 """Calculate the observatory location via the telescope name. 

39 

40 Returns 

41 ------- 

42 loc : `astropy.coordinates.EarthLocation` 

43 Location of the observatory. 

44 """ 

45 return EarthLocation.of_site(self.to_telescope()) 

46 

47 

48def is_non_science(self): 

49 """Raise an exception if this is a science observation. 

50 

51 Raises 

52 ------ 

53 KeyError 

54 Is a science observation. 

55 """ 

56 if self.to_observation_type() == "science": 

57 raise KeyError("Header represents science observation and can not default") 

58 return 

59 

60 

61def altitude_from_zenith_distance(zd): 

62 """Convert zenith distance to altitude 

63 

64 Parameters 

65 ---------- 

66 zd : `astropy.units.Quantity` 

67 Zenith distance as an angle. 

68 

69 Returns 

70 ------- 

71 alt : `astropy.units.Quantity` 

72 Altitude. 

73 """ 

74 return 90.*u.deg - zd 

75 

76 

77def tracking_from_degree_headers(self, radecsys, radecpairs, unit=u.deg): 

78 """Calculate the tracking coordinates from lists of headers. 

79 

80 Parameters 

81 ---------- 

82 radecsys : `list` or `tuple` 

83 Header keywords to try corresponding to the tracking system. If none 

84 match ICRS will be assumed. 

85 radecpairs : `tuple` of `tuple` of pairs of `str` 

86 Pairs of keywords specifying the RA/Dec in units of ``unit``. 

87 unit : `astropy.unit.BaseUnit` or `tuple` 

88 Unit definition suitable for the `~astropy.coordinate.SkyCoord` 

89 constructor. 

90 

91 Returns 

92 ------- 

93 radec = `astropy.coordinates.SkyCoord` 

94 The RA/Dec coordinates. None if this is a moving target or a 

95 non-science observation without any RA/Dec definition. 

96 

97 Raises 

98 ------ 

99 KeyError 

100 No RA/Dec keywords were found and this observation is a science 

101 observation. 

102 """ 

103 used = [] 

104 for k in radecsys: 

105 if self.is_key_ok(k): 

106 frame = self._header[k].strip().lower() 

107 used.append(k) 

108 if frame == "gappt": 

109 self._used_these_cards(*used) 

110 # Moving target 

111 return None 

112 break 

113 else: 

114 frame = "icrs" 

115 for ra_key, dec_key in radecpairs: 

116 if self.are_keys_ok([ra_key, dec_key]): 

117 radec = SkyCoord(self._header[ra_key], self._header[dec_key], 

118 frame=frame, unit=unit, obstime=self.to_datetime_begin(), 

119 location=self.to_location()) 

120 self._used_these_cards(ra_key, dec_key, *used) 

121 return radec 

122 if self.to_observation_type() == "science": 

123 raise KeyError("Unable to determine tracking RA/Dec of science observation") 

124 return None 

125 

126 

127def altaz_from_degree_headers(self, altazpairs, obstime, is_zd=None): 

128 """Calculate the altitude/azimuth coordinates from lists of headers. 

129 

130 If the altitude is found but is greater than 90 deg, it will be returned 

131 fixed at 90 deg. 

132 If the altitude or azimuth are negative and this is a calibration 

133 observation, `None` will be returned. 

134 

135 Parameters 

136 ---------- 

137 altazpairs : `tuple` of `str` 

138 Pairs of keywords specifying Alt/Az in degrees. Each pair is tried 

139 in turn. 

140 obstime : `astropy.time.Time` 

141 Reference time to use for these coordinates. 

142 is_zd : `set`, optional 

143 Contains keywords that correspond to zenith distances rather than 

144 altitude. 

145 

146 Returns 

147 ------- 

148 altaz = `astropy.coordinates.AltAz` 

149 The AltAz coordinates associated with the telescope location 

150 and provided time. Returns `None` if this observation is not 

151 a science observation and no AltAz keys were located. 

152 

153 Raises 

154 ------ 

155 KeyError 

156 No AltAz keywords were found and this observation is a science 

157 observation. 

158 """ 

159 for alt_key, az_key in altazpairs: 

160 if self.are_keys_ok([az_key, alt_key]): 

161 az = self._header[az_key] 

162 alt = self._header[alt_key] 

163 

164 # Check for zenith distance 

165 if is_zd and alt_key in is_zd: 

166 alt = altitude_from_zenith_distance(alt * u.deg).value 

167 

168 if az < -360.0 or alt < 0.0: 

169 # Break out of loop since we have found values but 

170 # they are bad. 

171 break 

172 if alt > 90.0: 

173 log.warning("Clipping altitude (%f) at 90 degrees", alt) 

174 alt = 90.0 

175 

176 altaz = AltAz(az * u.deg, alt * u.deg, 

177 obstime=obstime, location=self.to_location()) 

178 self._used_these_cards(az_key, alt_key) 

179 return altaz 

180 if self.to_observation_type() == "science": 

181 raise KeyError("Unable to determine AltAz of science observation") 

182 return None