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

1import numpy as np 

2from lsst.afw.cameraGeom import TAN_PIXELS, FOCAL_PLANE 

3import lsst.afw.geom as afwGeom 

4import lsst.geom as LsstGeom 

5import lsst.afw.image as afwImage 

6import lsst.afw.image.utils as afwImageUtils 

7import lsst.daf.base as dafBase 

8from lsst.sims.GalSimInterface.wcsUtils import approximateWcs 

9from lsst.sims.utils import _nativeLonLatFromPointing 

10 

11__all__ = ["tanWcsFromDetector", "tanSipWcsFromDetector"] 

12 

13 

14def tanWcsFromDetector(detector_name, camera_wrapper, obs_metadata, epoch): 

15 """ 

16 Take an afw.cameraGeom detector and return a WCS which approximates 

17 the focal plane as perfectly flat (i.e. it ignores optical distortions 

18 that the telescope may impose on the image) 

19 

20 @param [in] detector_name is the name of the detector as stored 

21 by afw 

22 

23 @param [in] camera_wrapper is an instantionat of a GalSimCameraWrapper 

24 

25 @param [in] obs_metadata is an instantiation of ObservationMetaData 

26 characterizing the telescope's current pointing 

27 

28 @param [in] epoch is the epoch in Julian years of the equinox against 

29 which RA and Dec are measured 

30 

31 @param [out] tanWcs is an instantiation of afw.image's TanWcs class 

32 representing the WCS of the detector as if there were no optical 

33 distortions imposed by the telescope. 

34 """ 

35 

36 xTanPixMin, xTanPixMax, \ 

37 yTanPixMin, yTanPixMax = camera_wrapper.getTanPixelBounds(detector_name) 

38 

39 x_center = 0.5*(xTanPixMax+xTanPixMin) 

40 y_center = 0.5*(yTanPixMax+yTanPixMin) 

41 

42 xPixList = [] 

43 yPixList = [] 

44 nameList = [] 

45 

46 # dx and dy are set somewhat heuristically 

47 # setting them equal to 0.1(max-min) lead to errors 

48 # on the order of 0.7 arcsec in the WCS 

49 

50 dx = 0.5*(xTanPixMax-xTanPixMin) 

51 dy = 0.5*(yTanPixMax-yTanPixMin) 

52 

53 for xx in np.arange(xTanPixMin, xTanPixMax+0.5*dx, dx): 

54 for yy in np.arange(yTanPixMin, yTanPixMax+0.5*dy, dy): 

55 xPixList.append(xx) 

56 yPixList.append(yy) 

57 nameList.append(detector_name) 

58 

59 xPixList = np.array(xPixList) 

60 yPixList = np.array(yPixList) 

61 

62 raList, decList = camera_wrapper._raDecFromPixelCoords(xPixList, 

63 yPixList, 

64 nameList, 

65 obs_metadata=obs_metadata, 

66 epoch=epoch, 

67 includeDistortion=False) 

68 

69 crPix1, crPix2 = camera_wrapper._pixelCoordsFromRaDec(obs_metadata._pointingRA, 

70 obs_metadata._pointingDec, 

71 chipName=detector_name, 

72 obs_metadata=obs_metadata, 

73 epoch=epoch, 

74 includeDistortion=False) 

75 

76 lonList, latList = _nativeLonLatFromPointing(raList, decList, 

77 obs_metadata._pointingRA, 

78 obs_metadata._pointingDec) 

79 

80 # convert from native longitude and latitude to intermediate world coordinates 

81 # according to equations (12), (13), (54) and (55) of 

82 # 

83 # Calabretta and Greisen (2002), A&A 395, p. 1077 

84 # 

85 radiusList = 180.0/(np.tan(latList)*np.pi) 

86 uList = radiusList*np.sin(lonList) 

87 vList = -radiusList*np.cos(lonList) 

88 

89 delta_xList = xPixList - crPix1 

90 delta_yList = yPixList - crPix2 

91 

92 bVector = np.array([ 

93 (delta_xList*uList).sum(), 

94 (delta_yList*uList).sum(), 

95 (delta_xList*vList).sum(), 

96 (delta_yList*vList).sum() 

97 ]) 

98 

99 offDiag = (delta_yList*delta_xList).sum() 

100 xsq = np.power(delta_xList, 2).sum() 

101 ysq = np.power(delta_yList, 2).sum() 

102 

103 aMatrix = np.array([ 

104 [xsq, offDiag, 0.0, 0.0], 

105 [offDiag, ysq, 0.0, 0.0], 

106 [0.0, 0.0, xsq, offDiag], 

107 [0.0, 0.0, offDiag, ysq] 

108 ]) 

109 

110 coeffs = np.linalg.solve(aMatrix, bVector) 

111 

112 fitsHeader = dafBase.PropertyList() 

113 fitsHeader.set("RADESYS", "ICRS") 

114 fitsHeader.set("EQUINOX", epoch) 

115 fitsHeader.set("CRVAL1", obs_metadata.pointingRA) 

116 fitsHeader.set("CRVAL2", obs_metadata.pointingDec) 

117 fitsHeader.set("CRPIX1", crPix1+1) # the +1 is because LSST uses 0-indexed images 

118 fitsHeader.set("CRPIX2", crPix2+1) # FITS files use 1-indexed images 

119 fitsHeader.set("CTYPE1", "RA---TAN") 

120 fitsHeader.set("CTYPE2", "DEC--TAN") 

121 fitsHeader.setDouble("CD1_1", coeffs[0]) 

122 fitsHeader.setDouble("CD1_2", coeffs[1]) 

123 fitsHeader.setDouble("CD2_1", coeffs[2]) 

124 fitsHeader.setDouble("CD2_2", coeffs[3]) 

125 

126 tanWcs = afwGeom.makeSkyWcs(fitsHeader) 

127 

128 return tanWcs 

129 

130 

131def tanSipWcsFromDetector(detector_name, camera_wrapper, obs_metadata, epoch, 

132 order=3, 

133 skyToleranceArcSec=0.001, 

134 pixelTolerance=0.01): 

135 """ 

136 Take an afw Detector and approximate its pixel-to-(Ra,Dec) transformation 

137 with a TAN-SIP WCs. 

138 

139 Definition of the TAN-SIP WCS can be found in Shupe and Hook (2008) 

140 http://fits.gsfc.nasa.gov/registry/sip/SIP_distortion_v1_0.pdf 

141 

142 @param [in] detector_name is the name of the detector as stored 

143 by afw 

144 

145 @param [in] camera_wrapper is an instantionat of a GalSimCameraWrapper 

146 

147 @param [in] obs_metadata is an instantiation of ObservationMetaData 

148 characterizing the telescope's current pointing 

149 

150 @param [in] epoch is the epoch in Julian years of the equinox against 

151 which RA and Dec are measured 

152 

153 @param [in] order is the order of the SIP polynomials to be fit to the 

154 optical distortions (default 3) 

155 

156 @param [in] skyToleranceArcSec is the maximum allowed error in the fitted 

157 world coordinates (in arcseconds). Default 0.001 

158 

159 @param [in] pixelTolerance is the maximum allowed error in the fitted 

160 pixel coordinates. Default 0.02 

161 

162 @param [out] tanSipWcs is an instantiation of afw.image's TanWcs class 

163 representing the WCS of the detector with optical distortions parametrized 

164 by the SIP polynomials. 

165 """ 

166 

167 bbox = camera_wrapper.getBBox(detector_name) 

168 

169 tanWcs = tanWcsFromDetector(detector_name, camera_wrapper, obs_metadata, epoch) 

170 

171 tanSipWcs = approximateWcs(tanWcs, 

172 order=order, 

173 skyTolerance=skyToleranceArcSec*LsstGeom.arcseconds, 

174 pixelTolerance=pixelTolerance, 

175 detector_name=detector_name, 

176 camera_wrapper=camera_wrapper, 

177 obs_metadata=obs_metadata) 

178 

179 return tanSipWcs 

180