Commit 0861a483890b990836c40c53c2e06caafb70efcf
Exists in
dev
Merge branch 'dev' of https://gitlab.irap.omp.eu/pyros-irap/pyros into dev
Showing
2 changed files
with
625 additions
and
123 deletions
Show diff stats
config/pyros_observatory/general/schemas/schema_observatory-2.0.yml
... | ... | @@ -135,6 +135,34 @@ schema;schema_FN_CONTEXTS: |
135 | 135 | type: map |
136 | 136 | required: False |
137 | 137 | mapping: |
138 | + cal_L0: | |
139 | + type: map | |
140 | + required: False | |
141 | + mapping: | |
142 | + root_dir: | |
143 | + type: str | |
144 | + description: | |
145 | + type: str | |
146 | + extension: | |
147 | + type: str | |
148 | + naming: | |
149 | + type: str | |
150 | + pathnaming: | |
151 | + type: str | |
152 | + cal_L1: | |
153 | + type: map | |
154 | + required: False | |
155 | + mapping: | |
156 | + root_dir: | |
157 | + type: str | |
158 | + description: | |
159 | + type: str | |
160 | + extension: | |
161 | + type: str | |
162 | + naming: | |
163 | + type: str | |
164 | + pathnaming: | |
165 | + type: str | |
138 | 166 | img_L0: |
139 | 167 | type: map |
140 | 168 | required: False |
... | ... | @@ -162,7 +190,77 @@ schema;schema_FN_CONTEXTS: |
162 | 190 | naming: |
163 | 191 | type: str |
164 | 192 | pathnaming: |
193 | + type: str | |
194 | + img_L1a: | |
195 | + type: map | |
196 | + required: False | |
197 | + mapping: | |
198 | + root_dir: | |
199 | + type: str | |
200 | + description: | |
201 | + type: str | |
202 | + extension: | |
203 | + type: str | |
204 | + naming: | |
205 | + type: str | |
206 | + pathnaming: | |
207 | + type: str | |
208 | + img_L1b: | |
209 | + type: map | |
210 | + required: False | |
211 | + mapping: | |
212 | + root_dir: | |
213 | + type: str | |
214 | + description: | |
165 | 215 | type: str |
216 | + extension: | |
217 | + type: str | |
218 | + naming: | |
219 | + type: str | |
220 | + pathnaming: | |
221 | + type: str | |
222 | + img_L1c: | |
223 | + type: map | |
224 | + required: False | |
225 | + mapping: | |
226 | + root_dir: | |
227 | + type: str | |
228 | + description: | |
229 | + type: str | |
230 | + extension: | |
231 | + type: str | |
232 | + naming: | |
233 | + type: str | |
234 | + pathnaming: | |
235 | + type: str | |
236 | + img_L1d: | |
237 | + type: map | |
238 | + required: False | |
239 | + mapping: | |
240 | + root_dir: | |
241 | + type: str | |
242 | + description: | |
243 | + type: str | |
244 | + extension: | |
245 | + type: str | |
246 | + naming: | |
247 | + type: str | |
248 | + pathnaming: | |
249 | + type: str | |
250 | + img_L2: | |
251 | + type: map | |
252 | + required: False | |
253 | + mapping: | |
254 | + root_dir: | |
255 | + type: str | |
256 | + description: | |
257 | + type: str | |
258 | + extension: | |
259 | + type: str | |
260 | + naming: | |
261 | + type: str | |
262 | + pathnaming: | |
263 | + type: str | |
166 | 264 | img_tmp: |
167 | 265 | type: map |
168 | 266 | required: False | ... | ... |
src/core/pyros_django/img_process/A_ImgProcessor.py
... | ... | @@ -63,7 +63,7 @@ class A_ImgProcessor(Agent): |
63 | 63 | # AgentCmd.CMD_STATUS_CODE.CMD_EXECUTED |
64 | 64 | _TEST_COMMANDS_LIST = [ |
65 | 65 | # Format : ("self cmd_name cmd_args", timeout, "expected_result", expected_status), |
66 | - (True, "self do_create_test_images_1", 500, "", Agent.CMD_STATUS.CMD_EXECUTED), | |
66 | + (True, "self do_create_test_images_2", 500, "", Agent.CMD_STATUS.CMD_EXECUTED), | |
67 | 67 | (True, "self do_stop asap", 500, "STOPPING", Agent.CMD_STATUS.CMD_EXECUTED), |
68 | 68 | ] |
69 | 69 | |
... | ... | @@ -80,7 +80,7 @@ class A_ImgProcessor(Agent): |
80 | 80 | |
81 | 81 | def _init(self): |
82 | 82 | super()._init() |
83 | - log.debug("end super init()") | |
83 | + log.debug("End super init()") | |
84 | 84 | log.info(f"self.TEST_MODE = {self.TEST_MODE}") |
85 | 85 | |
86 | 86 | # === Get the config object |
... | ... | @@ -105,6 +105,7 @@ class A_ImgProcessor(Agent): |
105 | 105 | if channel == None: |
106 | 106 | raise Exception(f"{agent_alias} has no channel file contexts {channel=}") |
107 | 107 | #log.info(f"{channel=}") |
108 | + self._channel = channel | |
108 | 109 | |
109 | 110 | # === Get all file contexts from the channel config |
110 | 111 | self._fn = channel['fn_contexts'] |
... | ... | @@ -117,8 +118,7 @@ class A_ImgProcessor(Agent): |
117 | 118 | log.info(f"{longitude=}") |
118 | 119 | self._ima.longitude(longitude) |
119 | 120 | |
120 | - # === Copy the channel file context into the Ima object | |
121 | - # --- TODO make a Filenames function to do that | |
121 | + # === Copy the channel file contexts into the Ima object | |
122 | 122 | self._ima.fcontext_replace(self._fn) |
123 | 123 | log.info(f"=== List of file name contexts available for the channel {channel['name']} ({channel['symbol']})") |
124 | 124 | for fcname in self._ima.fcontexts: |
... | ... | @@ -174,6 +174,15 @@ class A_ImgProcessor(Agent): |
174 | 174 | """ |
175 | 175 | |
176 | 176 | def glob_images_to_process(self): |
177 | + """Get the list of L0A (and L1A or L1B) files compatibles with the unit and the channel. | |
178 | + | |
179 | + The list is sorted to have the oldest file at the first place. | |
180 | + Files are FITS format. | |
181 | + The context file is 'img_L0' | |
182 | + | |
183 | + Returns: | |
184 | + The list of files sorted from the oldest to the earlier. If L1A or L1B files exist the only the list of these files is returned. | |
185 | + """ | |
177 | 186 | |
178 | 187 | # - select the incoming directory file context |
179 | 188 | fcontext = "img_L0" |
... | ... | @@ -181,138 +190,308 @@ class A_ImgProcessor(Agent): |
181 | 190 | self._ima.fcontext = fcontext |
182 | 191 | except Exception as e: |
183 | 192 | raise Exception(f"glob_images_to_process: {e}") |
184 | - # - Compute the wildcard for glob | |
185 | - # TODO fix the bug of * in the file name | |
186 | - # wildcard = self.ima.join("*") | |
187 | - # The current solution is: | |
188 | - wildcard = os.path.join(self._ima.rootdir,"*"+self._ima.extension) | |
189 | - print(f"{wildcard}") | |
193 | + # - Get the fcontext naming wildcard | |
194 | + wildcard = self._ima.naming_wildcard() | |
195 | + # - Get param dict from the fcontext naming wildcard | |
196 | + param = self._ima.naming_get(wildcard) | |
197 | + # - Set the constraints of the wildcard | |
198 | + param['unit'] = self.config.unit_name | |
199 | + param['channel'] = self._channel['symbol'] | |
200 | + wildcard = self._ima.naming_set(param) | |
201 | + wildcard = self._ima.join(wildcard) | |
190 | 202 | # - glob the incoming directory contents: |
203 | + log.debug(f"glob_images_to_process: {wildcard=}") | |
191 | 204 | fitsfiles = glob.glob(wildcard) |
192 | 205 | # - Please sort list of files in increasing dates (TODO) |
193 | 206 | fitsfiles.sort() |
207 | + # - Get only L1? file types if exist | |
208 | + fics = [] | |
209 | + for fitsfile in fitsfiles: | |
210 | + param = self._ima.naming_get(fitsfile) | |
211 | + if param['ftype'][:1] == "L1": | |
212 | + fics.append(fitsfile) | |
213 | + if len(fics): | |
214 | + fitsfiles = fics | |
194 | 215 | return fitsfiles |
195 | 216 | |
196 | 217 | def bias_correction(self): |
197 | - | |
198 | - # - Search the bias | |
199 | - path_bias = os.path.join( self._paths['ima_bias'], self._date_night ) | |
200 | - fitsbiasfiles = glob.glob(f"{path_bias}/*.fit") | |
201 | - log.info(f"fitsbiasfiles = {fitsbiasfiles}") | |
202 | - if len(fitsbiasfiles) > 0: | |
203 | - | |
204 | - # - Select the bias | |
205 | - pass | |
218 | + """Process bias correction. | |
219 | + """ | |
220 | + log.info("No correction needed") | |
206 | 221 | |
207 | 222 | def dark_correction(self): |
223 | + """Process dark correction. | |
224 | + | |
225 | + This method is called by process_one_image(). | |
226 | + Consider the file contexts 'img_tmp' and 'cal_L1'. Consider the <night> and <filter>. | |
227 | + The processing is performed with all files lying only in the directory 'img_tmp'. | |
228 | + | |
229 | + Prerequesite: The method process_one_image() copied a science image from the directory 'img_L0' to a file named img_<channel>_<filter>.fit in the directory 'img_tmp'. | |
230 | + | |
231 | + Algorithm: | |
232 | + | |
233 | + * Check if the file dark_<channel>_<night> exists in the directory 'img_tmp'. | |
234 | + * If not, check if a DA1 superdark file exists in the directory 'cal_L1'/<night> and copy it in the directory 'img_tmp' as the name dark_<channel_<night>. | |
235 | + * Load the image img_<channel>_<filter> (= self._fimg) | |
236 | + * Sub the image dark_<channel>_<night> | |
237 | + """ | |
208 | 238 | |
209 | - # - Search the dark | |
210 | - path_darks = os.path.join( self._paths['ima_darks'], self._date_night ) | |
211 | - fitsdarkfiles = glob.glob(f"{path_darks}/*.fit") | |
212 | - log.info(f"fitsdarkfiles = {fitsdarkfiles}") | |
213 | - if len(fitsdarkfiles) > 0: | |
214 | - | |
215 | - # - Select two darks and compute the therm using exposure | |
216 | - # - Correction of dark | |
217 | - pass | |
239 | + # - Dark file name in tmp of this night | |
240 | + self._ima.fcontext = "img_tmp" | |
241 | + fdark = "dark_" + self._channel['symbol'] + "_" + self._datedict['night'] | |
242 | + fdark_out = self._ima.join(fdark) | |
243 | + | |
244 | + # - Delete dark files in tmp that are not of this night | |
245 | + # This action prevents orphan files in the tmp folder | |
246 | + wildcard = self._ima.join("dark_" + self._channel['symbol'] + "_*") | |
247 | + fdarkes = glob.glob(wildcard) | |
248 | + for fdarke in fdarkes: | |
249 | + if fdarke != fdark_out: | |
250 | + log.info(f"Clean temporary file {fdarke}") | |
251 | + os.remove(fdarke) | |
252 | + | |
253 | + # - Place the night dark in tmp if needed | |
254 | + fdark_in = None | |
255 | + if not os.path.exists(fdark_out): | |
256 | + # - The night dark does not exists in tmp | |
257 | + self._ima.fcontext = "cal_L1" | |
258 | + # - Get the fcontext naming wildcard | |
259 | + wildcard = self._ima.naming_wildcard() | |
260 | + # - Get param dict from the fcontext naming wildcard | |
261 | + param_dark = self._ima.naming_get(wildcard) | |
262 | + # - Set the constraints of the wildcard | |
263 | + param_dark['ftype'] = 'DA1' | |
264 | + param_dark['unit'] = self.config.unit_name | |
265 | + param_dark['channel'] = self._channel['symbol'] | |
266 | + wildcard = self._ima.naming_set(param_dark) | |
267 | + wildcard = os.path.join( self._ima.rootdir, self._datedict['night'], wildcard + self._ima.extension) | |
268 | + # - glob the incoming directory contents: | |
269 | + #log.info(f"dark glob : {wildcard=}") | |
270 | + fitsfiles = glob.glob(wildcard) | |
271 | + #log.info(f"dark files : {fitsfiles=}") | |
272 | + if len(fitsfiles) > 0: | |
273 | + fdark_in = fitsfiles[0] | |
274 | + # - Copy the night dark into tmp | |
275 | + #log.info(f"{fdark_in=}") | |
276 | + #log.info(f"{fdark_out=}") | |
277 | + shutil.copyfile(fdark_in, fdark_out) | |
278 | + | |
279 | + # - Dark correction if dark exists | |
280 | + if os.path.exists(fdark_out): | |
281 | + # - Load img and get infos | |
282 | + self._ima.fcontext = "img_tmp" | |
283 | + self._ima.load(self._fimg) | |
284 | + # - Dark correction | |
285 | + log.info(f"Dark used: {fdark}") | |
286 | + self._ima.sub(fdark) | |
287 | + self._ima.save(self._fimg) | |
288 | + log.info("Dark correction done") | |
289 | + else: | |
290 | + log.info("No dark correction") | |
218 | 291 | |
219 | 292 | def flat_correction(self): |
293 | + """Process flat correction. | |
294 | + | |
295 | + This method is called by process_one_image(). | |
296 | + Consider the file contexts 'img_tmp' and 'cal_L1'. Consider the <night> and <filter>. | |
297 | + The processing is performed with all files lying only in the directory 'img_tmp'. | |
298 | + | |
299 | + Prerequesite: The method process_one_image() copied a science image from the directory 'img_L0' to a file named img_<channel>_<filter>.fit in the directory 'img_tmp'. The image must be corrected by bias and dark. | |
300 | + | |
301 | + Algorithm: | |
302 | + | |
303 | + * Check if the file flat_<channel>_<night>_<filter> exists in the directory 'img_tmp'. | |
304 | + * If not, check if a FL1 superdark file exists in the directory 'cal_L1'/<night> and copy it in the directory 'img_tmp' as the name flat_<channel_<night>_<filter>. | |
305 | + * Load the image img_<channel>_<filter> (= self._fimg) | |
306 | + * Div the image flat_<channel>_<night>_<filter> | |
307 | + """ | |
220 | 308 | |
221 | - # - Search the flat | |
222 | - path_flats = os.path.join( self._paths['ima_flats'], self._date_night ) | |
223 | - fitsflatfiles = glob.glob(f"{path_flats}/*.fit") | |
224 | - log.info(f"fitsflatfiles = {fitsflatfiles}") | |
225 | - if len(fitsflatfiles) > 0: | |
226 | - | |
227 | - # - Select the flat (with the filter) | |
228 | - # - Correction of flat | |
229 | - pass | |
309 | + # - Flat file name in tmp of this night | |
310 | + self._ima.fcontext = "img_tmp" | |
311 | + fflat = "flat_" + self._channel['symbol'] + "_" + self._datedict['night'] + "_" + self._filter_symbol | |
312 | + | |
313 | + fflat_out = self._ima.join(fflat) | |
314 | + | |
315 | + # - Delete flat files in tmp that are not of this night | |
316 | + # This action prevents orphan files in the tmp folder | |
317 | + wildcard = self._ima.join("flat_" + self._channel['symbol'] + "_" + self._filter_symbol + "_*") | |
318 | + fflates = glob.glob(wildcard) | |
319 | + for fflate in fflates: | |
320 | + if fflate != fflat_out: | |
321 | + log.info(f"Clean temporary file {fflate}") | |
322 | + os.remove(fflate) | |
323 | + | |
324 | + # - Place the night flat in tmp if needed | |
325 | + fflat_in = None | |
326 | + if not os.path.exists(fflat_out): | |
327 | + # - The night flat does not exists in tmp | |
328 | + self._ima.fcontext = "cal_L1" | |
329 | + # - Get the fcontext naming wildcard | |
330 | + wildcard = self._ima.naming_wildcard() | |
331 | + # - Get param dict from the fcontext naming wildcard | |
332 | + param_flat = self._ima.naming_get(wildcard) | |
333 | + # - Set the constraints of the wildcard | |
334 | + param_flat['ftype'] = 'FL1' | |
335 | + param_flat['unit'] = self.config.unit_name | |
336 | + param_flat['channel'] = self._channel['symbol'] | |
337 | + param_flat['filter'] = self._filter_symbol | |
338 | + wildcard = self._ima.naming_set(param_flat) | |
339 | + wildcard = os.path.join( self._ima.rootdir, self._datedict['night'], wildcard + self._ima.extension) | |
340 | + # - glob the incoming directory contents: | |
341 | + #log.info(f"flat glob : {wildcard=}") | |
342 | + fitsfiles = glob.glob(wildcard) | |
343 | + #log.info(f"flat files : {fitsfiles=}") | |
344 | + if len(fitsfiles) > 0: | |
345 | + fflat_in = fitsfiles[0] | |
346 | + # - Copy the night flat into tmp | |
347 | + #log.info(f"{fflat_in=}") | |
348 | + #log.info(f"{fflat_out=}") | |
349 | + shutil.copyfile(fflat_in, fflat_out) | |
350 | + | |
351 | + # - Flat correction if flat exists | |
352 | + if os.path.exists(fflat_out): | |
353 | + # - Load img and get infos | |
354 | + self._ima.fcontext = "img_tmp" | |
355 | + #self._ima.load(self._fimg) | |
356 | + # - Flat correction | |
357 | + log.info(f"Flat used: {fflat}") | |
358 | + self._ima.div(fflat, 10000) | |
359 | + self._ima.save(self._fimg) | |
360 | + log.info("Flat correction done") | |
361 | + else: | |
362 | + log.info("No flat correction") | |
230 | 363 | |
231 | 364 | def inversion_correction(self): |
232 | - pass | |
365 | + """Process inversion corrections. | |
366 | + """ | |
367 | + log.info("No inversion correction needed") | |
233 | 368 | |
234 | 369 | def cosmetic_correction(self): |
235 | - pass | |
370 | + """Process cosmetic correction. | |
371 | + """ | |
372 | + log.info("No cosmetic correction needed") | |
236 | 373 | |
237 | 374 | def wcs_calibration(self): |
238 | - return 0 | |
375 | + """Process WCS calibration. | |
376 | + | |
377 | + This method is called by process_one_image(). | |
378 | + Consider the file context 'img_tmp'. Consider the <night> and <filter>. | |
379 | + The processing is performed with all files lying only in the directory 'img_tmp'. | |
380 | + | |
381 | + Prerequesite: The method process_one_image() copied a science image from the directory 'img_L0' to a file named img_<channel>_<filter>.fit in the directory 'img_tmp'. The image must be corrected by bias, dark and flat. | |
382 | + | |
383 | + Algorithm: | |
384 | + | |
385 | + * Load the image img_<channel>_<filter> (= self._fimg) | |
386 | + * Set the current working directory to the 'img_tmp' rootdir. | |
387 | + * Create the dictionary of settings to prepare the calibration call. | |
388 | + * Call the method of calibwcs. | |
389 | + """ | |
390 | + method = "" | |
391 | + #method = "upload" | |
392 | + if method == "upload": | |
393 | + self._ima.fcontext = "img_tmp" | |
394 | + os.chdir(self._ima.rootdir) | |
395 | + self._ima.config("astrometrynet", API_KEY = "amxbkytvjishmehq") | |
396 | + settings = {} | |
397 | + settings["center_ra"] = self._ima.getkwd('RA') | |
398 | + settings["center_dec"] = self._ima.getkwd('DEC') | |
399 | + settings["radius"] = 1.0 | |
400 | + success, comment, detail = self._ima.calibwcs("upload", **settings) | |
401 | + log.info(f"WCS {success=}") | |
402 | + log.info(f"WCS {comment=}") | |
403 | + log.info(f"WCS {detail=}") | |
404 | + log.info("WCS correction done") | |
405 | + else: | |
406 | + log.info("No WCS correction needed") | |
239 | 407 | |
240 | 408 | def process_one_image(self, fitsfile: str): |
241 | - """This is the general algorithm of processing | |
409 | + """This is the general algorithm of processing to transform L0 to L1b | |
410 | + | |
411 | + The processing consists to make corrections of dark, flat, inversions, cosmetic (L1a) and perform WCS calibration (L1b). | |
242 | 412 | |
243 | - The processing consists to make corrections of dark, flat, inversions, cosmetic | |
244 | - and perform WCS calibration. | |
413 | + This method is called by the method _routine_process_iter_end_body(). | |
414 | + The method _routine_process_iter_end_body() first call the method glob_images_to_process() to get the list of file names to process. | |
415 | + The list of file names can be L0A images of L1A, L1B, ... | |
416 | + The normal use of process_one_image() is to process L0A but it is possible to copy by hand L1A or L1B files in the incoming directory to force a new WCS calibration if needed. | |
245 | 417 | |
246 | 418 | Args: |
247 | - fitsfile: The file of the FITS file to process. | |
419 | + fitsfile: The file name of the FITS file to process. The file name must be contains path, name, extension. | |
248 | 420 | |
249 | 421 | """ |
250 | 422 | |
423 | + log.info("\n" + "="*70 + f"\n=== Start process an image for L0->L1\n" + "="*70 + "\n") | |
424 | + | |
251 | 425 | # - Get informations from the file name according the fcontext |
252 | - log.info(f"{self._ima.fcontext} : {self._ima.fdescription} {self._ima.rootdir}/[{self._ima.pathing()}]/[{self._ima.naming()}]{self._ima.extension}") | |
253 | - infos = self._ima.naming_get(fitsfile) | |
254 | - print(f"{infos=}") | |
426 | + self._ima.fcontext = "img_L0" | |
427 | + #log.info(f"{self._ima.fcontext} : {self._ima.fdescription} {self._ima.rootdir}/[{self._ima.pathing()}]/[{self._ima.naming()}]{self._ima.extension}") | |
428 | + param_img = self._ima.naming_get(fitsfile) | |
429 | + #log.info(f"{param_img=}") | |
255 | 430 | |
256 | 431 | # - Load file in memory |
257 | - log.info("Load the file in memory") | |
258 | - infos = self._ima.load(fitsfile) | |
259 | - print(f"{infos=}") | |
260 | - #f = self._ima.genename(self._ima.load(fitsfile)) | |
261 | - #log.info(f"{f=}") | |
432 | + log.info(f"Image to process {fitsfile}") | |
433 | + self._ima.load(fitsfile) | |
262 | 434 | |
263 | - # - Save as tmp | |
435 | + # - Get datedict from DATE-OBS of img | |
436 | + date_obs = self._ima.getkwd("DATE-OBS") | |
437 | + log.info(f"DATE-OBS: {date_obs}") | |
438 | + self._datedict = self._ima.naming_date(date_obs) | |
439 | + log.info(f"night: {self._datedict['night']}") | |
440 | + | |
441 | + # - Get filter | |
442 | + self._filter_symbol = self._ima.getkwd("FILTER") | |
443 | + | |
444 | + # - Save as img in tmp | |
445 | + self._fimg = "img_" + self._channel['symbol'] + "_" + self._filter_symbol | |
264 | 446 | self._ima.fcontext = "img_tmp" |
265 | - f = self._ima.join("tmp.fit") | |
266 | - log.info("Save the temporary file as tmp name") | |
447 | + #log.info(f"{self._ima.fcontext} : {self._ima.fdescription} {self._ima.rootdir}/[{self._ima.pathing()}]/[{self._ima.naming()}]{self._ima.extension}") | |
448 | + f = self._ima.join(self._fimg) | |
449 | + log.info(f"Save the temporary file as {f}") | |
267 | 450 | self._ima.save(f) |
268 | 451 | |
269 | - # - Load tmp and get infos | |
270 | - self._ima.load("tmp") | |
271 | - date_obs = self._ima.getkwd("DATE-OBS") | |
272 | - self._date_night = self._ima.get_night(date_obs) | |
273 | - log.info(f"Date_obs = {date_obs}") | |
274 | - log.info(f"Night = {self._date_night}") | |
275 | - exposure = self._ima.getkwd("EXPOSURE") | |
276 | - log.info(f"Exposure = {exposure}") | |
277 | - | |
278 | - # - Bias correction | |
279 | - self.bias_correction() | |
280 | - | |
281 | - # - Dark correction | |
282 | - self.dark_correction() | |
283 | - | |
284 | - # - Flat correction | |
285 | - self.flat_correction() | |
286 | - | |
287 | - # - Save tmp corrected by dark and flat | |
288 | - self._ima.path(self._paths['ima_tmp']) | |
289 | - self._ima.save("tmp") | |
290 | - | |
291 | - # - Inversion of mirrors or mirorxy | |
292 | - self.inversion_correction() | |
293 | - | |
294 | - # - Cosmetic correction | |
295 | - self.cosmetic_correction() | |
296 | - | |
297 | - # - WCS calibration | |
298 | - nmatched = self.wcs_calibration() | |
299 | - | |
300 | - # - Prepare the output file name | |
301 | - log.info("Decode the filename") | |
302 | - fgen_in = f['genename'] + f['sep'] + f['indexes'][0] + f['suffix'] | |
303 | - fext_in = f['file_extension'] | |
304 | - fext_out = ".fits" | |
305 | - | |
306 | - # - Save in processed | |
307 | - yyyy = self._date_night[0:4] | |
308 | - mm = self._date_night[4:6] | |
309 | - dd = self._date_night[6:8] | |
310 | - path_processed = os.path.join( self._paths['ima_processed'], yyyy, mm, dd ) | |
311 | - self._ima.path(path_processed) | |
312 | - fname_out = fgen_in + fext_out | |
313 | - fname = self._ima.save(fname_out) | |
314 | - log.info(f"Save the processed image {fname}") | |
315 | - | |
452 | + if param_img['ftype'] == "L0A": | |
453 | + # - Bias correction | |
454 | + log.info("\n" + "-"*60 + f"\n--- Bias correction\n" + "-"*60 + "\n") | |
455 | + self.bias_correction() | |
456 | + | |
457 | + # - Dark correction | |
458 | + log.info("\n" + "-"*60 + f"\n--- Dark correction\n" + "-"*60 + "\n") | |
459 | + self.dark_correction() | |
460 | + | |
461 | + # - Flat correction | |
462 | + log.info("\n" + "-"*60 + f"\n--- Flat correction\n" + "-"*60 + "\n") | |
463 | + self.flat_correction() | |
464 | + | |
465 | + # - Inversion of mirrors or mirorxy | |
466 | + log.info("\n" + "-"*60 + f"\n--- Mirror correction\n" + "-"*60 + "\n") | |
467 | + self.inversion_correction() | |
468 | + | |
469 | + # - Cosmetic correction | |
470 | + log.info("\n" + "-"*60 + f"\n--- Cosmetic correction\n" + "-"*60 + "\n") | |
471 | + self.cosmetic_correction() | |
472 | + | |
473 | + # - Save the L1a file name | |
474 | + self._ima.fcontext = "img_L1a" | |
475 | + param_img['ftype'] = 'L1A' | |
476 | + fname = self._ima.naming_set(param_img) | |
477 | + fname = self._ima.join(fname) | |
478 | + self._ima.save(fname) | |
479 | + | |
480 | + if param_img['ftype'] == "L1A" or param_img['ftype'] == "L1B": | |
481 | + | |
482 | + # - WCS calibration | |
483 | + log.info("\n" + "-"*60 + f"\n--- WCS calibration\n" + "-"*60 + "\n") | |
484 | + nmatched = self.wcs_calibration() | |
485 | + | |
486 | + # - Save the L1a file name | |
487 | + self._ima.fcontext = "img_L1b" | |
488 | + param_img['ftype'] = 'L1B' | |
489 | + fname = self._ima.naming_set(param_img) | |
490 | + fname = self._ima.join(fname) | |
491 | + self._ima.save(fname) | |
492 | + | |
493 | + # - TODO Resampling (img_L1c) | |
494 | + | |
316 | 495 | # - Delete the file in incoming directory |
317 | 496 | os.remove(fitsfile) |
318 | 497 | log.info(f"Delete the raw image {fitsfile}") |
... | ... | @@ -320,50 +499,276 @@ class A_ImgProcessor(Agent): |
320 | 499 | # - Update the running state |
321 | 500 | self._routine_running = self.RUNNING_NOTHING |
322 | 501 | |
323 | - time.sleep(5) | |
324 | - print("\n ...End of image calibration\n") | |
502 | + log.info("\n" + "="*70 + f"\n=== End process an image for L0->L1\n" + "="*70 + "\n") | |
325 | 503 | |
326 | 504 | """ |
327 | 505 | ================================================================= |
328 | - Internal methods | |
506 | + Protected methods | |
329 | 507 | ================================================================= |
330 | 508 | """ |
331 | 509 | |
332 | 510 | def _create_test_images_1(self): |
333 | - # === Define an image to test the processing and copy it in incoming directory | |
334 | - file_in = os.path.join(os.environ['PROJECT_ROOT_PATH'],"vendor","guitastro","tests","data","m57.fit") | |
511 | + """Copy the image m57.fit of Guitastro as a test image. | |
512 | + | |
513 | + A filter C is assumed. | |
514 | + Create bias, dark and flat calibration images. | |
515 | + | |
516 | + This method does not need external internal call. | |
517 | + """ | |
518 | + | |
519 | + try: | |
520 | + self._ima.fcontext = "default" | |
521 | + except Exception as e: | |
522 | + raise Exception(f"_create_test_images_1: {e}") | |
523 | + self._ima.rootdir = os.path.join(os.environ['PROJECT_ROOT_PATH'],"vendor","guitastro","tests","data") | |
524 | + self._ima.extension = ".fit" | |
525 | + file_in = self._ima.join("m57") | |
526 | + # --- Load the image | |
527 | + log.info(f"{file_in=}") | |
528 | + self._ima.load(file_in) | |
529 | + log.info("Load test image OK") | |
530 | + # --- Get image shape | |
531 | + naxis1, naxis2 = self._ima._array.shape | |
532 | + log.info(f"Test image shape is: {naxis1=} {naxis2=}") | |
533 | + # --- Get datedict | |
534 | + date_obs = self._ima.getkwd("DATE-OBS") | |
535 | + log.info(f"Test image DATE-OBS: {date_obs}") | |
536 | + datedict = self._ima.naming_date(date_obs) | |
537 | + log.info(f"Test image night: {datedict['night']}") | |
538 | + # --- Get other infos | |
539 | + filter_symbol = "C" | |
540 | + id_sequence = 123456789 | |
541 | + | |
542 | + # --- Rename the image file name when copying to be compatible with the img_L0 file context | |
335 | 543 | try: |
336 | 544 | self._ima.fcontext = "img_L0" |
337 | 545 | except Exception as e: |
338 | 546 | raise Exception(f"_create_test_images_1: {e}") |
339 | 547 | param = {} |
340 | 548 | param['ftype'] = "L0A" |
341 | - param['date'] = "20000713" | |
342 | - param['time'] = "224045000000" | |
343 | - param['unit'] = "TNC" | |
549 | + param['date'] = datedict['yyyymmdd'] | |
550 | + param['time'] = datedict['hhmmssssssss'] | |
551 | + param['unit'] = self.config.unit_name | |
344 | 552 | param['version'] = 1 |
345 | - param['channel'] = "CH1" | |
346 | - param['id_seq'] = 123456789 | |
553 | + param['channel'] = self._channel['symbol'] | |
554 | + param['id_seq'] = id_sequence | |
347 | 555 | param['plane'] = 1 |
348 | 556 | param['frame'] = 1 |
349 | 557 | fname = self._ima.naming_set(param) |
350 | 558 | file_out = self._ima.join(fname) |
351 | - print(f"{file_in=}") | |
352 | - print(f"{file_out=}") | |
353 | - shutil.copyfile(file_in, file_out) | |
559 | + log.info(f"{file_out=}") | |
560 | + self._ima.setkwd("FILTER", filter_symbol) | |
561 | + self._ima.save(file_out) | |
562 | + | |
563 | + # --- Build a bias image filled by zeros | |
564 | + log.info("Build a test bias") | |
565 | + try: | |
566 | + self._ima.fcontext = "cal_L1" | |
567 | + except Exception as e: | |
568 | + raise Exception(f"_create_test_images_1: {e}") | |
569 | + self._ima.mult(0) | |
570 | + param = {} | |
571 | + param['ftype'] = "BI1" | |
572 | + param['date'] = datedict['yyyymmdd'] | |
573 | + param['time'] = datedict['hhmmssssssss'] | |
574 | + param['unit'] = self.config.unit_name | |
575 | + param['version'] = 1 | |
576 | + param['channel'] = self._channel['symbol'] | |
577 | + param['id_seq'] = id_sequence - 3 | |
578 | + param['plane'] = 1 | |
579 | + param['filter'] = filter_symbol | |
580 | + fname = self._ima.naming_set(param) | |
581 | + file_out = self._ima.join(fname) | |
582 | + log.info(f"{file_out=}") | |
583 | + self._ima.setkwd("FILTER", filter_symbol) | |
584 | + self._ima.save(file_out) | |
585 | + | |
586 | + # --- Build a dark image filled by zeros | |
587 | + log.info("Build a test dark") | |
588 | + param = {} | |
589 | + param['ftype'] = "DA1" | |
590 | + param['date'] = datedict['yyyymmdd'] | |
591 | + param['time'] = datedict['hhmmssssssss'] | |
592 | + param['unit'] = self.config.unit_name | |
593 | + param['version'] = 1 | |
594 | + param['channel'] = self._channel['symbol'] | |
595 | + param['id_seq'] = id_sequence - 2 | |
596 | + param['plane'] = 1 | |
597 | + param['filter'] = filter_symbol | |
598 | + fname = self._ima.naming_set(param) | |
599 | + file_out = self._ima.join(fname) | |
600 | + log.info(f"{file_out=}") | |
601 | + self._ima.setkwd("FILTER", filter_symbol) | |
602 | + self._ima.save(file_out) | |
603 | + | |
604 | + # --- Build a flat image filled by cst=10000 | |
605 | + log.info("Build a test flat") | |
606 | + flatnorm = 10000 | |
607 | + self._ima.offset(flatnorm) | |
608 | + self._ima.setkwd("FLATNORM", flatnorm, "Normalisation of the superflat") | |
609 | + param = {} | |
610 | + param['ftype'] = "FL1" | |
611 | + param['date'] = datedict['yyyymmdd'] | |
612 | + param['time'] = datedict['hhmmssssssss'] | |
613 | + param['unit'] = self.config.unit_name | |
614 | + param['version'] = 1 | |
615 | + param['channel'] = self._channel['symbol'] | |
616 | + param['id_seq'] = id_sequence - 1 | |
617 | + param['plane'] = 1 | |
618 | + param['filter'] = filter_symbol | |
619 | + fname = self._ima.naming_set(param) | |
620 | + file_out = self._ima.join(fname) | |
621 | + log.info(f"{file_out=}") | |
622 | + self._ima.setkwd("FILTER", filter_symbol) | |
623 | + self._ima.save(file_out) | |
354 | 624 | |
355 | 625 | def _create_test_images_2(self): |
626 | + """Simulate an image centered on Messier 67 as a test image. | |
627 | + | |
628 | + A filter C is assumed. | |
629 | + Create bias, dark and flat calibration images. | |
630 | + | |
631 | + This method needs external internal call. | |
632 | + """ | |
633 | + | |
634 | + # --- Configure the simulator | |
356 | 635 | self._ima.etc.camera("Kepler 4040") |
357 | 636 | self._ima.etc.optics("Takahashi_180ED") |
358 | - self._ima.etc.params("msky",18) | |
637 | + bias_level = 1000 | |
638 | + | |
639 | + # --- Set the target coordinates | |
359 | 640 | ra = 132.84583 |
360 | 641 | dec = 11.81333 |
361 | - at = self._ima.simulation("GAIA", "PHOTOM", shutter_mode="closed", t=50, ra=ra, dec=dec) | |
362 | - file_out = os.path.join(self._paths['ima_tmp'], "m67.ecsv") | |
363 | - print(f"STEP TOTO 1 = {at}") | |
364 | - at.t.write(file_out, format='astrotable', overwrite=True) | |
365 | - print(f"STEP TOTO 2") | |
366 | - date_obs = self.getkwd("DATE-OBS") | |
642 | + | |
643 | + # --- Get datedict | |
644 | + date_obs = guitastro.Date("now").iso(nb_subdigit=3) | |
645 | + log.info(f"Test image DATE-OBS: {date_obs}") | |
646 | + datedict = self._ima.naming_date(date_obs) | |
647 | + log.info(f"Test image night: {datedict['night']}") | |
648 | + | |
649 | + # --- Get the table of stars (simu_<channel>.ecsv) | |
650 | + shutter_mode = "synchro" | |
651 | + self._ima.etc.params("msky",19) | |
652 | + t = 10 | |
653 | + log.info("Build a test image") | |
654 | + try: | |
655 | + self._ima.fcontext = "img_tmp" | |
656 | + except Exception as e: | |
657 | + raise Exception(f"_create_test_images_2: {e}") | |
658 | + ext = self._ima.extension | |
659 | + self._ima.extension = ".ecsv" | |
660 | + fsimu = "simu_" + self._channel['symbol'] | |
661 | + fsimu = self._ima.join(fsimu) | |
662 | + self._ima.extension = ext | |
663 | + if os.path.exists(fsimu): | |
664 | + att = guitastro.AstroTable() | |
665 | + att.read(fsimu, format="ascii.ecsv") | |
666 | + at = self._ima.simulation("ASTROTABLE", att, shutter_mode=shutter_mode, t=t, ra=ra, dec=dec) | |
667 | + else: | |
668 | + at = self._ima.simulation("GAIA", "PHOTOM", shutter_mode=shutter_mode, t=t, ra=ra, dec=dec, column_filters = {"Gmag": "<14"}) | |
669 | + at.t.write(fsimu, format='ascii.ecsv', overwrite=True) | |
670 | + att = at | |
671 | + # --- Get other infos | |
672 | + filter_symbol = "C" | |
673 | + id_sequence = 123452789 | |
674 | + | |
675 | + # --- Save the image file name to be compatible with the img_L0 file context | |
676 | + try: | |
677 | + self._ima.fcontext = "img_L0" | |
678 | + except Exception as e: | |
679 | + raise Exception(f"_create_test_images_2: {e}") | |
680 | + param = {} | |
681 | + param['ftype'] = "L0A" | |
682 | + param['date'] = datedict['yyyymmdd'] | |
683 | + param['time'] = datedict['hhmmssssssss'] | |
684 | + param['unit'] = self.config.unit_name | |
685 | + param['version'] = 1 | |
686 | + param['channel'] = self._channel['symbol'] | |
687 | + param['id_seq'] = id_sequence | |
688 | + param['plane'] = 1 | |
689 | + param['frame'] = 1 | |
690 | + fname = self._ima.naming_set(param) | |
691 | + file_out = self._ima.join(fname) | |
692 | + self._ima.setkwd("DATE-OBS", date_obs) | |
693 | + self._ima.setkwd("FILTER", filter_symbol) | |
694 | + self._ima.save(file_out) | |
695 | + | |
696 | + # --- Build a bias image | |
697 | + log.info("Build a test bias") | |
698 | + shutter_mode = "closed" | |
699 | + self._ima.simulation("ASTROTABLE", att, shutter_mode=shutter_mode, t=0.01, ra=ra, dec=dec) | |
700 | + try: | |
701 | + self._ima.fcontext = "cal_L1" | |
702 | + except Exception as e: | |
703 | + raise Exception(f"_create_test_images_2: {e}") | |
704 | + param = {} | |
705 | + param['ftype'] = "BI1" | |
706 | + param['date'] = datedict['yyyymmdd'] | |
707 | + param['time'] = datedict['hhmmssssssss'] | |
708 | + param['unit'] = self.config.unit_name | |
709 | + param['version'] = 1 | |
710 | + param['channel'] = self._channel['symbol'] | |
711 | + param['id_seq'] = id_sequence - 3 | |
712 | + param['plane'] = 1 | |
713 | + param['filter'] = filter_symbol | |
714 | + fname = self._ima.naming_set(param) | |
715 | + file_out = self._ima.join(fname) | |
716 | + import numpy as np | |
717 | + log.info(f"{file_out=}") | |
718 | + self._ima.setkwd("DATE-OBS", date_obs) | |
719 | + self._ima.setkwd("FILTER", filter_symbol) | |
720 | + self._ima.save(file_out) | |
721 | + | |
722 | + # --- Build a dark image | |
723 | + log.info("Build a test dark") | |
724 | + shutter_mode = "closed" | |
725 | + self._ima.simulation("ASTROTABLE", att, shutter_mode=shutter_mode, t=t, ra=ra, dec=dec) | |
726 | + param = {} | |
727 | + param['ftype'] = "DA1" | |
728 | + param['date'] = datedict['yyyymmdd'] | |
729 | + param['time'] = datedict['hhmmssssssss'] | |
730 | + param['unit'] = self.config.unit_name | |
731 | + param['version'] = 1 | |
732 | + param['channel'] = self._channel['symbol'] | |
733 | + param['id_seq'] = id_sequence - 2 | |
734 | + param['plane'] = 1 | |
735 | + param['filter'] = filter_symbol | |
736 | + fname = self._ima.naming_set(param) | |
737 | + file_out = self._ima.join(fname) | |
738 | + log.info(f"{file_out=}") | |
739 | + self._ima.setkwd("DATE-OBS", date_obs) | |
740 | + self._ima.setkwd("FILTER", filter_symbol) | |
741 | + self._ima.save(file_out) | |
742 | + | |
743 | + # --- Build a flat image normalized by cst=10000 | |
744 | + log.info("Build a test flat") | |
745 | + shutter_mode = "synchro" | |
746 | + self._ima.etc.params("msky",-1) | |
747 | + tf = t/1000000.0 | |
748 | + #tf = t | |
749 | + self._ima.simulation("ASTROTABLE", att, shutter_mode=shutter_mode, t=tf, ra=ra, dec=dec) | |
750 | + log.info(f"MEAN={np.mean(self._ima._array)}") | |
751 | + self._ima.offset(-bias_level) | |
752 | + flatnorm = 10000.0 | |
753 | + self._ima.ngain(flatnorm) | |
754 | + self._ima.setkwd("FLATNORM", flatnorm, "Normalisation of the superflat") | |
755 | + param = {} | |
756 | + param['ftype'] = "FL1" | |
757 | + param['date'] = datedict['yyyymmdd'] | |
758 | + param['time'] = datedict['hhmmssssssss'] | |
759 | + param['unit'] = self.config.unit_name | |
760 | + param['version'] = 1 | |
761 | + param['channel'] = self._channel['symbol'] | |
762 | + param['id_seq'] = id_sequence - 1 | |
763 | + param['plane'] = 1 | |
764 | + param['filter'] = filter_symbol | |
765 | + fname = self._ima.naming_set(param) | |
766 | + file_out = self._ima.join(fname) | |
767 | + log.info(f"{file_out=}") | |
768 | + self._ima.setkwd("DATE-OBS", date_obs) | |
769 | + self._ima.setkwd("FILTER", filter_symbol) | |
770 | + self._ima.save(file_out) | |
771 | + | |
367 | 772 | |
368 | 773 | def _plural(self, n: int) -> str: |
369 | 774 | """Return "s" if n>1 for plurals. |
... | ... | @@ -382,7 +787,6 @@ class A_ImgProcessor(Agent): |
382 | 787 | |
383 | 788 | if __name__ == "__main__": |
384 | 789 | args = parse_args(sys.argv[1:]) |
385 | - #args = vars(parser.parse_args()) | |
386 | 790 | agent = build_agent(A_ImgProcessor, param_constr=args) |
387 | 791 | print(agent) |
388 | 792 | agent.run() | ... | ... |