Coverage for python/lsst/obs/subaru/strayLight/rotatorAngle.py : 17%

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# Copyright (C) 2017 HSC Software Team
2# Copyright (C) 2017 Satoshi Kawanomoto
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
17"""Module to calculate instrument rotator angle at start and end of observation"""
19__all__ = ["inrStartEnd"]
21import numpy as np
22from astropy.io import fits
23import lsst.geom
25# fixed parameters
26ltt_d = 19.82556 # dome latitude in degree
27lng_d = -155.47611 # dome longitude in degree
28mjd_J2000 = 51544.5 # mjd at J2000.0 (2000/01/01.5)
30# refraction index of air
31# T=273.15[K], P=600[hPa], Pw=1.5[hPa], lambda=0.55[um]
32air_idx = 1.0 + 1.7347e-04
34# scale height of air
35air_sh = 0.00130
38def _mjd2jc2000(mjd):
39 """convert mjd to Julian century (J2000.0 origin)"""
40 jc2000 = (mjd - mjd_J2000) / 36525.0
41 return jc2000
44def _precessionMatrix(jc2000):
45 """create precession matrix at the given time in Julian century"""
46 zeta_A = np.deg2rad((2306.2181*jc2000 + 0.30188*jc2000**2.0 + 0.017998*jc2000**3.0)/3600.0)
47 z_A = np.deg2rad((2306.2181*jc2000 + 1.09468*jc2000**2.0 + 0.018203*jc2000**3.0)/3600.0)
48 theta_A = np.deg2rad((2004.3109*jc2000 - 0.42665*jc2000**2.0 - 0.041833*jc2000**3.0)/3600.0)
49 precMat = np.matrix([[+np.cos(zeta_A)*np.cos(theta_A)*np.cos(z_A) - np.sin(zeta_A)*np.sin(z_A),
50 -np.sin(zeta_A)*np.cos(theta_A)*np.cos(z_A) - np.cos(zeta_A)*np.sin(z_A),
51 -np.sin(theta_A)*np.cos(z_A)],
52 [+np.cos(zeta_A)*np.cos(theta_A)*np.sin(z_A) + np.sin(zeta_A)*np.cos(z_A),
53 -np.sin(zeta_A)*np.cos(theta_A)*np.sin(z_A) + np.cos(zeta_A)*np.cos(z_A),
54 -np.sin(theta_A)*np.sin(z_A)],
55 [+np.cos(zeta_A)*np.sin(theta_A),
56 -np.sin(zeta_A)*np.sin(theta_A),
57 +np.cos(theta_A)]])
58 return precMat
61def _mjd2gmst(mjd):
62 """convert mjd to GMST(Greenwich mean sidereal time)"""
63 mjd_f = mjd % 1
64 jc2000 = _mjd2jc2000(mjd)
65 gmst_s = ((6.0*3600.0 + 41.0*60.0 + 50.54841) +
66 8640184.812866*jc2000 + 0.093104*jc2000**2.0 - 0.0000062*jc2000**3.0 +
67 mjd_f*86400.0)
68 gmst_d = (gmst_s % 86400)/240.0
69 return gmst_d
72def _gmst2lmst(gmst_d):
73 """convert GMST to LMST(mean local sidereal time)"""
74 lmst_d = (gmst_d + lng_d) % 360
75 return lmst_d
78def _sph2vec(ra_d, de_d):
79 """convert spherical coordinate to the Cartesian coordinates (vector)"""
80 ra_r = np.deg2rad(ra_d)
81 de_r = np.deg2rad(de_d)
82 vec = np.array([[np.cos(ra_r)*np.cos(de_r)],
83 [np.sin(ra_r)*np.cos(de_r)],
84 [np.sin(de_r)]])
85 return vec
88def _vec2sph(vec):
89 """convert the Cartesian coordinates vector to shperical coordinates"""
90 ra_r = np.arctan2(vec[1, 0], vec[0, 0])
91 de_r = np.arcsin(vec[2, 0])
92 ra_d = np.rad2deg(ra_r)
93 de_d = np.rad2deg(de_r)
94 return ra_d, de_d
97def _ra2ha(ra_d, lst_d):
98 """convert right ascension to hour angle at given LST"""
99 ha_d = (lst_d - ra_d)%360
100 return ha_d
103def _eq2hz(ha_d, de_d):
104 """convert equatorial coordinates to the horizontal coordinates"""
105 ltt_r = np.deg2rad(ltt_d)
106 ha_r = np.deg2rad(ha_d)
107 de_r = np.deg2rad(de_d)
108 zd_r = np.arccos(+np.sin(ltt_r)*np.sin(de_r) + np.cos(ltt_r)*np.cos(de_r)*np.cos(ha_r))
109 az_r = np.arctan2(+np.cos(de_r)*np.sin(ha_r),
110 -np.cos(ltt_r)*np.sin(de_r) + np.sin(ltt_r)*np.cos(de_r)*np.cos(ha_r))
111 zd_d = np.rad2deg(zd_r)
112 az_d = np.rad2deg(az_r)
113 al_d = 90.0 - zd_d
114 return al_d, az_d
117def _air_idx():
118 """return the air refraction index"""
119 return air_idx
122def _atm_ref(al_d):
123 """return the atmospheric refraction at given altitude"""
124 if al_d > 20.0:
125 zd_r = np.deg2rad(90.0 - al_d)
126 else:
127 zd_r = np.deg2rad(70.0)
128 r0 = _air_idx()-1.0
129 sh = air_sh
130 R0 = (1.0 - sh)*r0 - sh*r0**2/2.0 + sh**2*r0*2.0
131 R1 = r0**2/2.0 + r0**3/6.0 - sh*r0 - sh*r0**2*11.0/4.0 + sh**2*r0*5.0
132 R2 = r0**3 - sh*r0**2*9.0/4.0 + sh**2*r0*3.0
133 R = R0*np.tan(zd_r) + R1*(np.tan(zd_r))**3 + R2*(np.tan(zd_r))**5
134 return np.rad2deg(R)
137def _mal2aal(mal_d):
138 """convert mean altitude to apparent altitude"""
139 aal_d = mal_d + _atm_ref(mal_d)
140 return aal_d
143def _pos2adt(al_t_d, al_s_d, delta_az_d):
144 """convert altitudes of telescope and star and relative azimuth to angular distance and position angle"""
145 zd_t_r = np.deg2rad(90.0-al_t_d)
146 zd_s_r = np.deg2rad(90.0-al_s_d)
147 daz_r = np.deg2rad(delta_az_d)
149 ad_r = np.arccos(np.cos(zd_t_r)*np.cos(zd_s_r) + np.sin(zd_t_r)*np.sin(zd_s_r)*np.cos(daz_r))
151 if ad_r > 0.0:
152 pa_r = np.arcsin(np.sin(zd_s_r)*np.sin(daz_r)/np.sin(ad_r))
153 else:
154 pa_r = 0.0
155 ad_d = np.rad2deg(ad_r)
156 pa_d = np.rad2deg(pa_r)
157 if (zd_t_r < zd_s_r):
158 pa_d = 180.0 - pa_d
160 return ad_d, pa_d
163def _addpad2xy(ang_dist_d, p_ang_d, inr_d):
164 """convert angular distance, position angle, and instrument rotator angle to position on the cold plate"""
165 t = 90.0-(p_ang_d-inr_d)
166 x = np.cos(np.deg2rad(t))
167 y = np.sin(np.deg2rad(t))
168 return x, y
171def _gsCPposNorth(ra_t_d, de_t_d, inr_d, mjd):
172 jc2000 = _mjd2jc2000(mjd)
173 pm = _precessionMatrix(jc2000)
175 vt = _sph2vec(ra_t_d, de_t_d)
176 vt_mean = np.dot(pm, vt)
178 (mean_ra_t_d, mean_de_t_d) = _vec2sph(vt_mean)
179 mean_ra_s_d = mean_ra_t_d
180 mean_de_s_d = mean_de_t_d+0.75
182 gmst_d = _mjd2gmst(mjd)
183 lmst_d = _gmst2lmst(gmst_d)
185 mean_ha_t_d = _ra2ha(mean_ra_t_d, lmst_d)
186 mean_ha_s_d = _ra2ha(mean_ra_s_d, lmst_d)
188 (mean_al_t_d, mean_az_t_d) = _eq2hz(mean_ha_t_d, mean_de_t_d)
189 (mean_al_s_d, mean_az_s_d) = _eq2hz(mean_ha_s_d, mean_de_s_d)
191 apparent_al_t_d = _mal2aal(mean_al_t_d)
192 apparent_al_s_d = _mal2aal(mean_al_s_d)
194 delta_az_d = mean_az_s_d - mean_az_t_d
196 (ang_dist_d, p_ang_d) = _pos2adt(apparent_al_t_d, apparent_al_s_d, delta_az_d)
198 (x, y) = _addpad2xy(ang_dist_d, p_ang_d, inr_d)
199 return x, y
202def _getDataArrayFromFITSFileWithHeader(pathToFITSFile):
203 """return array of pixel data"""
204 fitsfile = fits.open(pathToFITSFile)
205 dataArray = fitsfile[0].data
206 fitsHeader = fitsfile[0].header
207 fitsfile.close()
208 return dataArray, fitsHeader
211def _minorArc(angle1, angle2):
212 """e.g. input (-179, 179) -> output (-179, -181)"""
214 angle1 = (angle1 + 180.0) % 360 - 180.0
215 angle2 = (angle2 + 180.0) % 360 - 180.0
217 if angle1 < angle2:
218 if angle2 - angle1 > 180.0:
219 angle2 -= 360.0
220 elif angle2 < angle1:
221 if angle1 - angle2 > 180.0:
222 angle1 -= 360.0
224 # Try to place [angle1, angle2] within [-270, +270]
226 if min(angle1, angle2) < -270.0:
227 angle1 += 360.0
228 angle2 += 360.0
229 if max(angle1, angle2) > 270.0:
230 angle1 -= 360.0
231 angle2 -= 360.0
233 return angle1, angle2
236def inrStartEnd(visitInfo):
237 """Calculate instrument rotator angle for start and end of exposure
239 Parameters
240 ----------
241 visitInfo : `lsst.afw.image.VisitInfo`
242 Visit info for the exposure to calculate correction.
244 Returns
245 -------
246 start : `float`
247 Instrument rotator angle at start of exposure, degrees.
248 end : `float`
249 Instrument rotator angle at end of exposure, degrees.
250 """
252 inst_pa = 270.0 - visitInfo.getBoresightRotAngle().asAngularUnits(lsst.geom.degrees)
253 ra_t_sp, de_t_sp = visitInfo.getBoresightRaDec()
255 ra_t_d = ra_t_sp.asAngularUnits(lsst.geom.degrees)
256 de_t_d = de_t_sp.asAngularUnits(lsst.geom.degrees)
258 mjd_str = visitInfo.getDate().get() - 0.5*visitInfo.getExposureTime()/86400.0
259 mjd_end = visitInfo.getDate().get() + 0.5*visitInfo.getExposureTime()/86400.0
261 inr_d = 0.00
263 (x, y) = _gsCPposNorth(ra_t_d, de_t_d, inr_d, mjd_str)
264 x_inr_str = 90.0 - np.rad2deg(np.arctan2(y, x)) + inst_pa
265 (x, y) = _gsCPposNorth(ra_t_d, de_t_d, inr_d, mjd_end)
266 x_inr_end = 90.0 - np.rad2deg(np.arctan2(y, x)) + inst_pa
268 return _minorArc(x_inr_str, x_inr_end)