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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

# 

# LSST Data Management System 

# Copyright 2016 LSST Corporation. 

# 

# This product includes software developed by the 

# LSST Project (http://www.lsst.org/). 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the LSST License Statement and 

# the GNU General Public License along with this program. If not, 

# see <http://www.lsstcorp.org/LegalNotices/>. 

# 

__all__ = ["Ctio0m9Mapper"] 

 

import os 

import re 

 

from astropy.coordinates import Angle 

from astropy import units as u 

 

import lsst.afw.image as afwImage 

import lsst.afw.image.utils as afwImageUtils 

import lsst.afw.geom as afwGeom 

import lsst.afw.cameraGeom as cameraGeom 

from lsst.obs.base import CameraMapper, MakeRawVisitInfo, bboxFromIraf, exposureFromImage 

import lsst.daf.base as dafBase 

from lsst.daf.persistence import Policy 

from lsst.obs.ctio0m9 import makeCamera 

 

 

class Ctio0m9MakeRawVisitInfo(MakeRawVisitInfo): 

"""functor to make a VisitInfo from the FITS header of a raw image 

""" 

 

def setArgDict(self, md, argDict): 

"""Fill an argument dict with arguments for makeVisitInfo and pop 

associated metadata 

 

@param[in] md image metadata 

@param[in, out] md the argument dictionary for modification 

""" 

super(Ctio0m9MakeRawVisitInfo, self).setArgDict(md, argDict) 

argDict["darkTime"] = md.getScalar("DARKTIME") 

 

def getDateAvg(self, md, exposureTime): 

"""Return date at the middle of the exposure 

 

@param[in,out] md metadata, as an lsst.daf.base.PropertyList or 

PropertySet; items that are used are stripped from the metadata 

(except TIMESYS, because it may apply to more than one other 

keyword). 

@param[in] exposureTime exposure time (sec) 

""" 

dateObs = self.popIsoDate(md, "DATE-OBS") 

return self.offsetDate(dateObs, 0.5*exposureTime) 

 

 

class Ctio0m9Mapper(CameraMapper): 

"""Mapper class for the 0.9m telescope at CTIO 

""" 

packageName = 'obs_ctio0m9' 

MakeRawVisitInfoClass = Ctio0m9MakeRawVisitInfo 

 

def __init__(self, inputPolicy=None, **kwargs): 

policyFile = Policy.defaultPolicyFile(self.packageName, "ctio0m9Mapper.yaml", "policy") 

policy = Policy(policyFile) 

CameraMapper.__init__(self, policy, os.path.dirname(policyFile), **kwargs) 

filter_pairings = ['NONE+SEMROCK', # list of all filter pairings found in data 

'NONE+RONCHI200', 

'RONCHI200+SEMROCK', 

'NONE+NONE', 

'NONE+g', 

'NONE+r', 

'NONE+i', 

'NONE+z', 

'RONCHI200+z', 

'RONCHI200+g', 

'FGB37+RONCHI200', 

'NONE+RONCHI400', 

'FGC715S+RONCHI400', 

'FGC715S+RONCHI200'] 

 

# default no-filter name used for biases and darks - must appear 

afwImageUtils.defineFilter('NONE', 0.0, alias=[]) 

 

for pairing in filter_pairings: 

afwImageUtils.defineFilter(pairing, 0.0, alias=[]) 

 

def _makeCamera(self, policy, repositoryDir): 

"""Make a camera (instance of lsst.afw.cameraGeom.Camera) describing 

the camera geometry 

""" 

return makeCamera() 

 

def _extractDetectorName(self, dataId): 

return 'SITE2K' 

 

def _computeCcdExposureId(self, dataId): 

"""Compute the 64-bit (long) identifier for a CCD exposure. 

 

@param dataId (dict) Data identifier with visit 

""" 

visit = dataId['visit'] 

return int(visit) 

 

def bypass_ccdExposureId(self, datasetType, pythonType, location, dataId): 

"""Hook to retrieve identifier for CCD""" 

return self._computeCcdExposureId(dataId) 

 

def bypass_ccdExposureId_bits(self, datasetType, pythonType, location, dataId): 

"""Hook to retrieve number of bits in identifier for CCD""" 

return 32 

 

def std_raw_md(self, md, dataId): 

"""Method for performing any necessary sanitization of metadata. 

 

@param[in,out] md metadata, as an lsst.daf.base.PropertyList or 

PropertySet, to be sanitized 

@param[in] dataId unused 

""" 

md = sanitize_date(md) 

return md 

 

def std_raw(self, item, dataId): 

"""Method for performing any necessary manipulation of the raw files. 

 

@param[in,out] item afwImage exposure object with associated metadata 

and detector info 

@param[in] dataId 

""" 

md = item.getMetadata() 

 

# Note that setting these must be done before the call to super below 

md.set('CTYPE1', 'RA---TAN') # add missing keywords 

md.set('CTYPE2', 'DEC--TAN') # add missing keywords 

md.set('CRVAL2', Angle(md.getScalar('DEC'), unit=u.deg).degree) # translate RA/DEC from header 

md.set('CRVAL1', Angle(md.getScalar('RA'), unit=u.hour).degree) 

md.set('CRPIX1', 210.216) # set reference pixels 

md.set('CRPIX2', 344.751) 

md.set('CD1_1', -0.000111557869436) # set nominal CD matrix 

md.set('CD1_2', 1.09444409144E-07) 

md.set('CD2_1', 6.26180926869E-09) 

md.set('CD2_2', -0.000111259259893) 

 

item = super(Ctio0m9Mapper, self).std_raw(item, dataId) 

# 

# We may need to hack up the cameraGeom 

# 

# There doesn't seem to be a way to get the extended register, so I 

# don't update it. 

# We could do this by detecting extra overscan and adjusting things 

# cleverly; probably we need to so so. 

# 

ccd = item.getDetector() 

rawBBoxFromMetadata = bboxFromIraf(md.getScalar("ASEC11")) 

rawBBox = ccd[0].getRawBBox() 

 

if rawBBoxFromMetadata != rawBBox: 

extraSerialOverscan = rawBBoxFromMetadata.getWidth() - rawBBox.getWidth() # extra overscan pixels 

extraParallelOverscan = rawBBoxFromMetadata.getHeight() - rawBBox.getHeight() # vertical 

 

ccd = cameraGeom.copyDetector(ccd, ampInfoCatalog=ccd.getAmpInfoCatalog().copy(deep=True)) 

item.setDetector(ccd) 

 

for a in ccd: 

ix, iy = [int(_) for _ in a.getName()] 

irafName = "%d%d" % (iy, ix) 

a.setRawBBox(bboxFromIraf(md.getScalar("ASEC%s" % irafName))) 

a.setRawDataBBox(bboxFromIraf(md.getScalar("TSEC%s" % irafName))) 

 

if extraSerialOverscan != 0 or extraParallelOverscan != 0: 

# 

# the number of overscan pixels has been changed from camera.yaml 

# 

# First adjust the overscan 

# 

rawHorizontalOverscanBBox = a.getRawHorizontalOverscanBBox() 

 

rawHorizontalOverscanBBox.shift(afwGeom.ExtentI((ix - 1)*extraSerialOverscan, 

(iy - 1)*extraParallelOverscan)) 

 

xy0 = rawHorizontalOverscanBBox.getMin() 

xy1 = rawHorizontalOverscanBBox.getMax() 

 

xy1.shift(afwGeom.ExtentI(extraSerialOverscan, extraParallelOverscan)) 

 

a.setRawHorizontalOverscanBBox(afwGeom.BoxI(xy0, xy1)) 

# 

# And now move the extended register to allow for the extra 

# overscan pixels 

# 

rawPrescanBBox = a.getRawPrescanBBox() 

rawPrescanBBox.shift(afwGeom.ExtentI(2*(ix - 1)*extraSerialOverscan, 

(iy - 1)*extraParallelOverscan)) 

 

xy0 = rawPrescanBBox.getMin() 

xy1 = rawPrescanBBox.getMax() 

 

xy1.shift(afwGeom.ExtentI(0, extraParallelOverscan)) 

a.setRawPrescanBBox(afwGeom.BoxI(xy0, xy1)) 

 

return item 

 

def std_dark(self, item, dataId): 

"""Standardiation of master dark frame. Must only be called on master 

darks. 

 

@param[in,out] item the master dark, as an image-like object 

@param[in] dataId unused 

""" 

exp = exposureFromImage(item) 

if not exp.getInfo().hasVisitInfo(): 

# hard-coded, but pipe_drivers always(?) normalises darks to a 

# darktime of 1s so this is OK? 

exp.getInfo().setVisitInfo(afwImage.VisitInfo(darkTime=1.0)) 

return exp 

 

 

def sanitize_date(md): 

'''Take a metadata object, fix corrupted dates in DATE-OBS field, and 

return the fixed md object. 

 

We see corrupted dates like "2016-03-06T08:53:3.198" (should be 53:03.198); 

fix these when they make dafBase.DateTime unhappy 

 

@param md metadata in, to be fixed 

@return md metadata returned, with DATE-OBS fixed 

''' 

date_obs = md.getScalar('DATE-OBS') 

try: # see if compliant. Don't use, just a test with dafBase 

dafBase.DateTime(date_obs, dafBase.DateTime.TAI) 

except Exception: # if bad, sanitise 

year, month, day, h, m, s = re.split(r"[-:T]", date_obs) 

if re.search(r"[A-Z]$", s): 

s, TZ = s[:-1], s[-1] 

else: 

TZ = "" 

 

date_obs = "%4d-%02d-%02dT%02d:%02d:%06.3f%s" % (int(year), int(month), int(day), 

int(h), int(m), float(s), TZ) 

md.set('DATE-OBS', date_obs) # put santized version back 

return md