Skip to content

Commit 2482e94

Browse files
committed
adding update_parallel_wcs.py
1 parent f4aaf85 commit 2482e94

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import os, sys
2+
import shutil
3+
import glob
4+
from astropy.io import ascii
5+
import astropy.io.fits as pyfits
6+
import numpy as np
7+
8+
def log_comment(LOGFILE, comment, verbose=False, show_date=False, mode='a'):
9+
"""
10+
Log a message to a file, optionally including a date tag
11+
"""
12+
import time
13+
14+
if show_date:
15+
msg = '# ({0})\n'.format(nowtime())
16+
else:
17+
msg = ''
18+
19+
msg += '{0}\n'.format(comment)
20+
21+
if LOGFILE is not None:
22+
fp = open(LOGFILE, mode)
23+
fp.write(msg)
24+
fp.close()
25+
26+
if verbose:
27+
print(msg[:-1])
28+
29+
return msg
30+
31+
def update_pure_parallel_wcs(file, logfile="pure_parallel_wcs_logfile",
32+
fix_vtype='PARALLEL_PURE', verbose=True):
33+
"""
34+
Update pointing-related keywords of pure parallel exposures using the
35+
pointing info from the FGS (and the prime exposures) from the MAST database
36+
and `pysiaf`
37+
38+
1. Find the FGS log from a MAST query that starts before the pure parallel
39+
exposure starts and ends after the exposure ends.
40+
2. Use the ``ra_v1, dec_v1, pa_v3`` values from the FGS log to set the
41+
pointing attitude with `pysiaf`.
42+
3. Compute the sky position of the ``CRPIX`` reference pixel of ``file``
43+
with `pysiaf` and put that position in the ``CRVAL`` keywords.
44+
45+
Parameters
46+
----------
47+
file : str
48+
Filename of a pure-parallel exposure (typically a rate.fits file)
49+
50+
fix_vtype : str
51+
Only run if ``file[0].header['VISITYPE'] == fix_vtype``
52+
53+
verbose : bool
54+
Status messaging
55+
56+
Returns
57+
-------
58+
status : None, True
59+
Returns None if some problem is found
60+
61+
"""
62+
import pysiaf
63+
import mastquery.jwst as jwstquery
64+
65+
if not os.path.exists(file):
66+
msg = "PureParallelUtils.update_pure_parallel_wcs: "
67+
msg += f" {file} not found"
68+
log_comment(logfile, msg, verbose=verbose)
69+
return None
70+
71+
with pyfits.open(file) as im:
72+
h0 = im[0].header.copy()
73+
h1 = im[1].header.copy()
74+
if 'VISITYPE' not in im[0].header:
75+
msg = "PureParallelUtils.update_pure_parallel_wcs: "
76+
msg += f" VISITYPE not found in header {file}"
77+
log_comment(logfile, msg, verbose=verbose)
78+
return None
79+
80+
# Only continue if this is a Pure Parallel exposure
81+
vtype = h0['VISITYPE']
82+
83+
if vtype != fix_vtype:
84+
msg = "PureParallelUtils.update_pure_parallel_wcs: "
85+
msg += f" VISITYPE ({vtype}) != {fix_vtype}, skip"
86+
log_comment(logfile, msg, verbose=verbose)
87+
return None
88+
89+
crval_init = h1['CRVAL1'], h1['CRVAL2']
90+
91+
# Get correct pointings from FGS logs. Allow the associated FGS exposure
92+
# to start up to 0.02 day ~ 30 min before the science exposure.
93+
dt = 0.02
94+
gs = jwstquery.query_guidestar_log(
95+
mjd=(h0['EXPSTART']-dt, h0['EXPEND']+dt),
96+
program=None,
97+
exp_type=['FGS_FINEGUIDE'],
98+
)
99+
100+
keep = (gs['expstart'] < h0['EXPSTART'])
101+
keep &= (gs['expend'] > h0['EXPEND'])
102+
103+
if keep.sum() == 0:
104+
msg = f"PureParallelUtils.update_pure_parallel_wcs: par_file='{file}'"
105+
msg += " couldn't find corresponding exposure in FGS logs"
106+
log_comment(logfile, msg, verbose=verbose)
107+
return None
108+
109+
gs = gs[keep][0]
110+
pos = (gs['ra_v1'], gs['dec_v1'], gs['pa_v3'])
111+
attmat = pysiaf.utils.rotations.attitude(0.0, 0.0, *pos)
112+
113+
# And apply the pointing to the parallel aperture and reference pixel
114+
par_aper = pysiaf.Siaf(h0['INSTRUME'])[h0['APERNAME']]
115+
par_aper.set_attitude_matrix(attmat)
116+
117+
crpix = h1['CRPIX1'], h1['CRPIX2']
118+
crpix_init = par_aper.sky_to_sci(*crval_init)
119+
120+
crval_fix = par_aper.sci_to_sky(*crpix)
121+
122+
msg = f"PureParallelUtils.update_pure_parallel_wcs: File: {file}"
123+
msg += '\n' + f"PureParallelUtils.update_pure_parallel_wcs: FGS: {gs['fileName']} "
124+
msg += '\n' + f"PureParallelUtils.update_pure_parallel_wcs: original crval "
125+
msg += f"{crval_init[0]:.7f} {crval_init[1]:.7f}"
126+
msg += '\n' + f"PureParallelUtils.update_pure_parallel_wcs: new crval "
127+
msg += f"{crval_fix[0]:.7f} {crval_fix[1]:.7f}"
128+
msg += '\n' + f"PureParallelUtils.update_pure_parallel_wcs: dpix "
129+
msg += f"{crpix[0] - crpix_init[0]:6.3f} {crpix[1] - crpix_init[1]:6.3f}"
130+
131+
_ = log_comment(logfile, msg, verbose=verbose)
132+
133+
with pyfits.open(file, mode='update') as im:
134+
im[1].header['RA_V1'] = gs['ra_v1']
135+
im[1].header['DEC_V1'] = gs['dec_v1']
136+
im[1].header['PA_V3'] = gs['pa_v3']
137+
im[1].header['CRVAL1'] = crval_fix[0]
138+
im[1].header['CRVAL2'] = crval_fix[1]
139+
im[1].header['PUREPWCS'] = True, 'WCS updated from query of FGS log file'
140+
im[1].header['PUREPEXP'] = gs['fileName'], 'FGS log file'
141+
142+
im.flush()
143+
144+
return True
145+
146+
if __name__ == "__main__":
147+
if len(sys.argv) < 2:
148+
print('Syntax: update_parallel_wcs.py fitsfile <verbose>')
149+
print(' where fitsfile is typically a _rate.fits or _rateints.fits file')
150+
print(' and <verbose> is a boolean to decide whether or not')
151+
print(' to print the results (the default is `True`)')
152+
sys.exit()
153+
fitsfile = str(sys.argv[1])
154+
verbose = True
155+
if len(sys.argv) > 2:
156+
if str(sys.argv[2]).upper() != 'TRUE':
157+
verbose = False
158+
update_pure_parallel_wcs(fitsfile, verbose=verbose)

0 commit comments

Comments
 (0)