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

# -*- coding: utf-8 -*- 

""" 

tools 

===== 

This module contanins assorted helper functions for the calibration of specMACS data. 

""" 

 

import logging 

 

logger = logging.getLogger(__name__) 

 

import datetime 

 

try: 

import dateutil.parser as dateparser 

import dateutil.tz as datetz 

except ImportError: 

dateparser = None 

datetz = None 

 

def parseIsoTimeSlow(time): 

""" 

Parses iso time format 

 

:param time: Time to parse as string. 

 

:returns: Time as datetime 

 

:note: This should cope all valid ISO time formats, but is intended only as fallback for :py:func:`parseIsoTime`. 

""" 

dt = dateparser.parse(time) 

try: 

dt = dt.astimezone(datetz.tzutc()) 

except ValueError: 

dt = dt.replace(tzinfo = datetz.tzutc()) 

return dt 

 

def parseIsoTime(time): 

""" 

Parses iso time format 

 

:param time: Time to parse as string. 

 

:returns: Time as datetime 

""" 

try: 

return datetime.datetime(int(time[0:4]), 

int(time[5:7]), 

int(time[8:10]), 

int(time[11:13]), 

int(time[14:16]), 

int(time[17:19]), 

int(time[20:26]), 

tzinfo=datetz.tzutc()) 

except: 

return parseIsoTimeSlow(time) 

 

def parseActionLog(loglist): 

""" 

Parses action log from a action log list. 

 

:param loglist: The action log as list of tuples. 

 

:returns: The action log as list of tuples with time as datetime objects. 

""" 

66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true if dateparser is None: 

raise ImportError('Could not import dateutil.parser') 

for action, time in loglist: 

yield action, parseIsoTime(time) 

 

def normalizeEnviHeader(header): 

""" 

Normalizes an ENVI header across various versions of the specMACS ENVI header formats. 

 

:param header: ENVI header to be normalized as :py:class:`dict` 

 

:returns: The (same,) normalized header 

 

:note: This function operates in-place 

""" 

for name in ('lines', 'samples', 'bands', 'autodarkstartline'): 

try: 

header[name] = int(header[name]) 

except KeyError: 

pass 

86 ↛ 87line 86 didn't jump to line 87, because the condition on line 86 was never true if not header.get('lines', 0) > 0: 

raise ValueError('cannot normalize header with 0 lines') 

for name in ('tint','fps'): 

try: 

header[name] = float(header[name]) 

except KeyError: 

pass 

for name in ('Wavelength',): 

try: 

header[name] = map(float, header[name]) 

except KeyError: 

pass 

try: 

header['wallTimes'] = map(parseIsoTime, header['wallTimes']) 

header['start time'] = header['wallTimes'][0] 

header['stop time'] = header['wallTimes'][-1] 

except KeyError: 

try: 

date = header['acquisition date'].rsplit(':',1)[1].strip() 

start = header['GPS Start Time'].split(':',1)[1].strip() 

stop = header['GPS Stop Time'].split(':',1)[1].strip() 

header['start time'] = datetime.datetime(*map(int, (date[6:10], date[3:5], date[0:2], start[0:2], start[3:5], start[6:8])), microsecond=int(1e6*float('0.'+start[9:])), tzinfo=datetz.tzutc()) 

header['stop time'] = datetime.datetime(*map(int, (date[6:10], date[3:5], date[0:2], stop[0:2], stop[3:5], stop[6:8])), microsecond=int(1e6*float('0.'+stop[9:])), tzinfo=datetz.tzutc()) 

try: 

calcFps = (float(header['lines']) - 1) / (header['stop time'] - header['start time']).total_seconds() 

except ZeroDivisionError: 

pass 

else: 

if 'fps' not in header: 

header['fps'] = calcFps 

header['wallTimes'] = [header['start time'] + datetime.timedelta(seconds = i / calcFps) for i in xrange(header['lines'])] 

except KeyError: 

pass 

pass 

else: 

try: 

header['fps'] = (float(header['lines']) - 1) / (header['stop time'] - header['start time']).total_seconds() 

except ZeroDivisionError: 

pass 

try: 

header['highResTimes'] = map(parseIsoTime, header['highResTimes']) 

except KeyError: 

try: 

header['highResTimes'] = header['wallTimes'] 

except KeyError: 

pass 

pass 

 

actionLog = {} 

for k,v in header.items(): 

try: 

n, a = k.rsplit(' ', 1) 

except ValueError: 

continue 

if a == 'actionLog': 

actionLog[n] = list(parseActionLog(v)) 

 

143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true if 'shutter' not in actionLog: 

actionLog['shutter'] = [('open', header['wallTimes'][0])] 

try: 

actionLog['shutter'].append(('close', header['wallTimes'][header['autodarkstartline']])) 

except (IndexError, KeyError): 

pass 

 

150 ↛ 151line 150 didn't jump to line 151, because the condition on line 150 was never true if 'fps' not in actionLog: 

actionLog['fps'] = [(header['fps'], header['wallTimes'][0])] 

 

153 ↛ 154line 153 didn't jump to line 154, because the condition on line 153 was never true if 'exposureTime' not in actionLog: 

actionLog['exposureTime'] = [(header['tint'], header['wallTimes'][0])] 

 

header['actionLog'] = actionLog 

header['isDarkfile'] = False 

return header 

 

 

def shutterStateToFrameTag(state): 

""" 

Converts shutter states into frame classification tags. 

 

:param state: shutter state 

 

:returns: frame classification tag 

""" 

if state in ('open', 'opened'): 

return 'good' 

if state in ('close', 'closed'): 

return 'dark' 

raise ValueError('invalid shutter state') 

 

def classifyFrames(header): 

""" 

Performs simple frame classification. 

 

This method operates solely by generating slices at the points im time 

as marked by the entries in the action log. 

 

:param header: ENVI header to be classified as :py:class:`dict` 

 

:returns: The (same,) header with added frame classificatoin data. 

 

:note: This function operates in-place 

 

:note: The :py:mod:`runmacs.spec.classification.frameclassification` module offers a more sophisticated analysis. 

""" 

if not header.get('isDarkfile', False): 

try: 

shutterLog = header['actionLog']['shutter'] 

wallTimes = header['wallTimes'] 

except (KeyError, AttributeError): 

shutterLog = [] 

if len(shutterLog) == 0: 

frameState = [(0, 'good')] 

logger.warning('no shutter information found in "%s", assuming full time exposure!', header['sourceId']) 

else: 

shutterLogIter = iter(shutterLog) 

if shutterLog[0][1] > wallTimes[0]: 

currentState = 'good' 

logger.warning('no shutter information found in start of "%s", assuming shutter open at the beginning!', header['sourceId']) 

else: 

currentState = shutterStateToFrameTag(shutterLog[0][0]) 

shutterLogIter.next() 

frameState = [(0, currentState)] 

try: 

nextState, nextTime = shutterLogIter.next() 

except StopIteration: 

nextState = None 

for i, frameTime in enumerate(wallTimes): 

if nextState is not None and frameTime >= nextTime: 

n = shutterStateToFrameTag(nextState) 

if n != currentState: 

currentState = n 

frameState.append((i, currentState)) 

try: 

nextState, nextTime = shutterLogIter.next() 

except StopIteration: 

nextState = None 

else: 

frameState = [(0, 'dark')] 

frameState.append((header['lines'], 'end')) 

header['frameState'] = frameState 

header['darkranges'] = [(a[0], b[0]) for a,b in zip(frameState[:-1], frameState[1:]) if a[1] == 'dark'] 

header['goodranges'] = [(a[0], b[0]) for a,b in zip(frameState[:-1], frameState[1:]) if a[1] == 'good'] 

header['darkindices'] = [i for s in header['darkranges'] for i in xrange(*s)] 

header['goodindices'] = [i for s in header['goodranges'] for i in xrange(*s)] 

return header