00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031
00032 #include <math.h>
00033 #include <cpl.h>
00034 #include <moses.h>
00035 #include <fors_dfs.h>
00036
00037 static int fors_normalise_flat_create(cpl_plugin *);
00038 static int fors_normalise_flat_exec(cpl_plugin *);
00039 static int fors_normalise_flat_destroy(cpl_plugin *);
00040 static int fors_normalise_flat(cpl_parameterlist *, cpl_frameset *);
00041
00042 static char fors_normalise_flat_description[] =
00043 "This recipe is used to normalise a master flat field frame dividing it\n"
00044 "by its large scale illumination trend. This recipe can be applied both\n"
00045 "to generic multi-slit (MOS/MXU) and to long slit exposures (either LSS, or\n"
00046 "LSS-like MOS/MXU), even if different normalisation methods are applied in\n"
00047 "such different cases. The input master flat field image is the product\n"
00048 "of the recipe fors_flat. The input spectral curvature table, product of\n"
00049 "the recipe fors_detect_spectra, is only required in the case of multi-slit\n"
00050 "data.\n"
00051 "\n"
00052 "In the case of multi-slit data, the flat field spectra are spatially\n"
00053 "rectified, heavily smoothed, and then mapped back on the CCD. Then the\n"
00054 "master flat image is divided by its smoothed counterpart. The smoothing\n"
00055 "may be obtained either by applying a running median filter of specified\n"
00056 "sizes, or by polynomial fitting along the dispersion direction performed\n"
00057 "independently for each row of the spatially remapped spectra.\n"
00058 "\n"
00059 "In the case of long-slit data, the smoothing can still be obtained either\n"
00060 "by applying a running median filter or by polynomial fitting, but the\n"
00061 "polynomial fitting will be performed along the spatial direction, for\n"
00062 "each column of the spectrum.\n"
00063 "\n"
00064 "In the table below the MXU acronym can be alternatively read as MOS or\n"
00065 "LSS.\n\n"
00066 "Input files:\n\n"
00067 " DO category: Type: Explanation: Required:\n"
00068 " MASTER_SCREEN_FLAT_MXU Calib Master flat frame Y\n"
00069 " CURV_COEFF_MXU Calib Spectral curvature .\n"
00070 " GRISM_TABLE Calib Grism table .\n\n"
00071 "Output files:\n\n"
00072 " DO category: Data type: Explanation:\n"
00073 " MASTER_NORM_FLAT_MXU FITS image Normalised flat field\n\n";
00074
00075 #define fors_normalise_flat_exit(message) \
00076 { \
00077 if (message) cpl_msg_error(recipe, message); \
00078 cpl_image_delete(master_flat); \
00079 cpl_image_delete(spatial); \
00080 cpl_image_delete(coordinate); \
00081 cpl_image_delete(smo_flat); \
00082 cpl_table_delete(grism_table); \
00083 cpl_table_delete(maskslits); \
00084 cpl_table_delete(slits); \
00085 cpl_table_delete(polytraces); \
00086 cpl_propertylist_delete(header); \
00087 cpl_msg_indent_less(); \
00088 return -1; \
00089 }
00090
00091 #define fors_normalise_flat_exit_memcheck(message) \
00092 { \
00093 if (message) cpl_msg_info(recipe, message); \
00094 printf("free master_flat (%p)\n", master_flat); \
00095 cpl_image_delete(master_flat); \
00096 printf("free spatial (%p)\n", spatial); \
00097 cpl_image_delete(spatial); \
00098 printf("free coordinate (%p)\n", coordinate); \
00099 cpl_image_delete(coordinate); \
00100 printf("free smo_flat (%p)\n", smo_flat); \
00101 cpl_image_delete(smo_flat); \
00102 printf("free grism_table (%p)\n", grism_table); \
00103 cpl_table_delete(grism_table); \
00104 printf("free maskslits (%p)\n", maskslits); \
00105 cpl_table_delete(maskslits); \
00106 printf("free slits (%p)\n", slits); \
00107 cpl_table_delete(slits); \
00108 printf("free polytraces (%p)\n", polytraces); \
00109 cpl_table_delete(polytraces); \
00110 printf("free header (%p)\n", header); \
00111 cpl_propertylist_delete(header); \
00112 cpl_msg_indent_less(); \
00113 return 0; \
00114 }
00115
00116
00128 int cpl_plugin_get_info(cpl_pluginlist *list)
00129 {
00130 cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00131 cpl_plugin *plugin = &recipe->interface;
00132
00133 cpl_plugin_init(plugin,
00134 CPL_PLUGIN_API,
00135 FORS_BINARY_VERSION,
00136 CPL_PLUGIN_TYPE_RECIPE,
00137 "fors_normalise_flat",
00138 "Normalise master flat spectrum",
00139 fors_normalise_flat_description,
00140 "Carlo Izzo",
00141 PACKAGE_BUGREPORT,
00142 "This file is currently part of the FORS Instrument Pipeline\n"
00143 "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00144 "This program is free software; you can redistribute it and/or modify\n"
00145 "it under the terms of the GNU General Public License as published by\n"
00146 "the Free Software Foundation; either version 2 of the License, or\n"
00147 "(at your option) any later version.\n\n"
00148 "This program is distributed in the hope that it will be useful,\n"
00149 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00150 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00151 "GNU General Public License for more details.\n\n"
00152 "You should have received a copy of the GNU General Public License\n"
00153 "along with this program; if not, write to the Free Software Foundation,\n"
00154 "Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n",
00155 fors_normalise_flat_create,
00156 fors_normalise_flat_exec,
00157 fors_normalise_flat_destroy);
00158
00159 cpl_pluginlist_append(list, plugin);
00160
00161 return 0;
00162 }
00163
00164
00175 static int fors_normalise_flat_create(cpl_plugin *plugin)
00176 {
00177 cpl_recipe *recipe;
00178 cpl_parameter *p;
00179
00180
00181
00182
00183
00184 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
00185 recipe = (cpl_recipe *)plugin;
00186 else
00187 return -1;
00188
00189
00190
00191
00192
00193 recipe->parameters = cpl_parameterlist_new();
00194
00195
00196
00197
00198
00199 p = cpl_parameter_new_value("fors.fors_normalise_flat.dispersion",
00200 CPL_TYPE_DOUBLE,
00201 "Expected spectral dispersion (Angstrom/pixel)",
00202 "fors.fors_normalise_flat",
00203 0.0);
00204 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00205 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00206 cpl_parameterlist_append(recipe->parameters, p);
00207
00208
00209
00210
00211
00212 p = cpl_parameter_new_value("fors.fors_normalise_flat.startwavelength",
00213 CPL_TYPE_DOUBLE,
00214 "Start wavelength in spectral extraction",
00215 "fors.fors_normalise_flat",
00216 0.0);
00217 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00218 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00219 cpl_parameterlist_append(recipe->parameters, p);
00220
00221
00222
00223
00224
00225 p = cpl_parameter_new_value("fors.fors_normalise_flat.endwavelength",
00226 CPL_TYPE_DOUBLE,
00227 "End wavelength in spectral extraction",
00228 "fors.fors_normalise_flat",
00229 0.0);
00230 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00231 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00232 cpl_parameterlist_append(recipe->parameters, p);
00233
00234
00235
00236
00237
00238
00239 p = cpl_parameter_new_value("fors.fors_normalise_flat.sdegree",
00240 CPL_TYPE_INT,
00241 "Degree of flat field fitting polynomial "
00242 "along spatial direction (used for LSS "
00243 "data only)",
00244 "fors.fors_normalise_flat",
00245 4);
00246 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sdegree");
00247 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00248 cpl_parameterlist_append(recipe->parameters, p);
00249
00250
00251
00252
00253
00254
00255 p = cpl_parameter_new_value("fors.fors_normalise_flat.ddegree",
00256 CPL_TYPE_INT,
00257 "Degree of flat field fitting polynomial "
00258 "along dispersion direction (used for MOS "
00259 "and MXU data only)",
00260 "fors.fors_normalise_flat",
00261 -1);
00262 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ddegree");
00263 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00264 cpl_parameterlist_append(recipe->parameters, p);
00265
00266
00267
00268
00269
00270 p = cpl_parameter_new_value("fors.fors_normalise_flat.dradius",
00271 CPL_TYPE_INT,
00272 "Smooth box radius for flat field along "
00273 "dispersion direction",
00274 "fors.fors_normalise_flat",
00275 10);
00276 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dradius");
00277 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00278 cpl_parameterlist_append(recipe->parameters, p);
00279
00280
00281
00282
00283
00284
00285 p = cpl_parameter_new_value("fors.fors_normalise_flat.sradius",
00286 CPL_TYPE_INT,
00287 "Smooth box radius for flat field along "
00288 "spatial direction",
00289 "fors.fors_normalise_flat",
00290 10);
00291 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sradius");
00292 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00293 cpl_parameterlist_append(recipe->parameters, p);
00294
00295 return 0;
00296 }
00297
00298
00307 static int fors_normalise_flat_exec(cpl_plugin *plugin)
00308 {
00309 cpl_recipe *recipe;
00310
00311 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
00312 recipe = (cpl_recipe *)plugin;
00313 else
00314 return -1;
00315
00316 return fors_normalise_flat(recipe->parameters, recipe->frames);
00317 }
00318
00319
00328 static int fors_normalise_flat_destroy(cpl_plugin *plugin)
00329 {
00330 cpl_recipe *recipe;
00331
00332 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
00333 recipe = (cpl_recipe *)plugin;
00334 else
00335 return -1;
00336
00337 cpl_parameterlist_delete(recipe->parameters);
00338
00339 return 0;
00340 }
00341
00342
00352 static int fors_normalise_flat(cpl_parameterlist *parlist,
00353 cpl_frameset *frameset)
00354 {
00355
00356 const char *recipe = "fors_normalise_flat";
00357
00358
00359
00360
00361
00362
00363 double dispersion;
00364 double startwavelength;
00365 double endwavelength;
00366 int sdegree;
00367 int ddegree;
00368 int sradius;
00369 int dradius;
00370
00371
00372
00373
00374
00375 cpl_image *master_flat = NULL;
00376 cpl_image *smo_flat = NULL;
00377 cpl_image *coordinate = NULL;
00378 cpl_image *spatial = NULL;
00379 cpl_table *grism_table = NULL;
00380 cpl_table *slits = NULL;
00381 cpl_table *polytraces = NULL;
00382 cpl_table *maskslits = NULL;
00383 cpl_propertylist *header = NULL;
00384
00385
00386
00387
00388
00389 char version[80];
00390 const char *master_flat_tag;
00391 const char *master_norm_flat_tag;
00392 const char *slit_location_tag;
00393 const char *curv_coeff_tag;
00394 int mxu, mos, lss;
00395 int nflat;
00396 int rebin;
00397 int nx, ny;
00398 int nslits;
00399 int treat_as_lss;
00400 int i;
00401 double reference;
00402 double *xpos;
00403 double mxpos;
00404
00405 char *instrume = NULL;
00406
00407
00408 cpl_msg_set_indentation(2);
00409
00410 if (dfs_files_dont_exist(frameset))
00411 fors_normalise_flat_exit(NULL);
00412
00413
00414
00415
00416
00417
00418 cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00419 cpl_msg_indent_more();
00420
00421 if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00422 fors_normalise_flat_exit("Too many in input: GRISM_TABLE");
00423
00424 grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00425
00426 dispersion = dfs_get_parameter_double(parlist,
00427 "fors.fors_normalise_flat.dispersion", grism_table);
00428
00429 if (dispersion <= 0.0)
00430 fors_normalise_flat_exit("Invalid spectral dispersion value");
00431
00432 startwavelength = dfs_get_parameter_double(parlist,
00433 "fors.fors_normalise_flat.startwavelength", grism_table);
00434 if (startwavelength > 1.0)
00435 if (startwavelength < 3000.0 || startwavelength > 13000.0)
00436 fors_normalise_flat_exit("Invalid wavelength");
00437
00438 endwavelength = dfs_get_parameter_double(parlist,
00439 "fors.fors_normalise_flat.endwavelength", grism_table);
00440 if (endwavelength > 1.0) {
00441 if (endwavelength < 3000.0 || endwavelength > 13000.0)
00442 fors_normalise_flat_exit("Invalid wavelength");
00443 if (startwavelength < 1.0)
00444 fors_normalise_flat_exit("Invalid wavelength interval");
00445 }
00446
00447 if (startwavelength > 1.0)
00448 if (endwavelength - startwavelength <= 0.0)
00449 fors_normalise_flat_exit("Invalid wavelength interval");
00450
00451 sdegree = dfs_get_parameter_int(parlist,
00452 "fors.fors_normalise_flat.sdegree", NULL);
00453 ddegree = dfs_get_parameter_int(parlist,
00454 "fors.fors_normalise_flat.ddegree", NULL);
00455 sradius = dfs_get_parameter_int(parlist,
00456 "fors.fors_normalise_flat.sradius", NULL);
00457 dradius = dfs_get_parameter_int(parlist,
00458 "fors.fors_normalise_flat.dradius", NULL);
00459
00460 if (sradius < 1 || dradius < 1)
00461 fors_normalise_flat_exit("Invalid smoothing box radius");
00462
00463 cpl_table_delete(grism_table); grism_table = NULL;
00464
00465 if (cpl_error_get_code())
00466 fors_normalise_flat_exit("Failure reading the configuration "
00467 "parameters");
00468
00469
00470 cpl_msg_indent_less();
00471 cpl_msg_info(recipe, "Check input set-of-frames:");
00472 cpl_msg_indent_more();
00473
00474 nflat = mxu = cpl_frameset_count_tags(frameset, "MASTER_SCREEN_FLAT_MXU");
00475 nflat += mos = cpl_frameset_count_tags(frameset, "MASTER_SCREEN_FLAT_MOS");
00476 nflat += lss = cpl_frameset_count_tags(frameset, "MASTER_SCREEN_FLAT_LSS");
00477
00478 if (nflat == 0) {
00479 fors_normalise_flat_exit("Missing input master flat field frame");
00480 }
00481 if (nflat > 1) {
00482 cpl_msg_error(recipe, "Too many input flat frames (%d > 1)", nflat);
00483 fors_normalise_flat_exit(NULL);
00484 }
00485
00486 if (mxu) {
00487 master_flat_tag = "MASTER_SCREEN_FLAT_MXU";
00488 master_norm_flat_tag = "MASTER_NORM_FLAT_MXU";
00489 slit_location_tag = "SLIT_LOCATION_MXU";
00490 curv_coeff_tag = "CURV_COEFF_MXU";
00491 }
00492 else if (mos) {
00493 master_flat_tag = "MASTER_SCREEN_FLAT_MOS";
00494 master_norm_flat_tag = "MASTER_NORM_FLAT_MOS";
00495 slit_location_tag = "SLIT_LOCATION_MOS";
00496 curv_coeff_tag = "CURV_COEFF_MOS";
00497 }
00498 else if (lss) {
00499 master_flat_tag = "MASTER_SCREEN_FLAT_LSS";
00500 master_norm_flat_tag = "MASTER_NORM_FLAT_LSS";
00501 }
00502
00503 header = dfs_load_header(frameset, master_flat_tag, 0);
00504
00505 if (mos || mxu) {
00506 if (mos)
00507 maskslits = mos_load_slits_fors_mos(header);
00508 else
00509 maskslits = mos_load_slits_fors_mxu(header);
00510
00511
00512
00513
00514
00515
00516 mxpos = cpl_table_get_column_median(maskslits, "xtop");
00517 xpos = cpl_table_get_data_double(maskslits, "xtop");
00518 nslits = cpl_table_get_nrow(maskslits);
00519
00520 treat_as_lss = 1;
00521 for (i = 0; i < nslits; i++) {
00522 if (fabs(mxpos-xpos[i]) > 0.01) {
00523 treat_as_lss = 0;
00524 break;
00525 }
00526 }
00527
00528 cpl_table_delete(maskslits); maskslits = NULL;
00529
00530 if (treat_as_lss)
00531 cpl_msg_warning(recipe, "All MOS slits have the same offset: %.2f\n"
00532 "The LSS data reduction strategy is applied!",
00533 mxpos);
00534 }
00535
00536 if (!(lss || treat_as_lss)) {
00537 if (cpl_frameset_count_tags(frameset, curv_coeff_tag) == 0) {
00538 cpl_msg_error(recipe, "Missing input: %s", curv_coeff_tag);
00539 fors_normalise_flat_exit(NULL);
00540 }
00541
00542 if (cpl_frameset_count_tags(frameset, curv_coeff_tag) > 1) {
00543 cpl_msg_error(recipe, "Too many in input: %s", curv_coeff_tag);
00544 fors_normalise_flat_exit(NULL);
00545 }
00546
00547 if (cpl_frameset_count_tags(frameset, slit_location_tag) == 0) {
00548 cpl_msg_error(recipe, "Missing input: %s", slit_location_tag);
00549 fors_normalise_flat_exit(NULL);
00550 }
00551
00552 if (cpl_frameset_count_tags(frameset, slit_location_tag) > 1) {
00553 cpl_msg_error(recipe, "Too many in input: %s", slit_location_tag);
00554 fors_normalise_flat_exit(NULL);
00555 }
00556 }
00557
00558 if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00559 fors_normalise_flat_exit("Input frames are not from the same grism");
00560
00561 if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00562 fors_normalise_flat_exit("Input frames are not from the same filter");
00563
00564 if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00565 fors_normalise_flat_exit("Input frames are not from the same chip");
00566
00567
00568
00569
00570
00571
00572
00573 if (header == NULL)
00574 fors_normalise_flat_exit("Cannot load master flat frame header");
00575
00576 instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
00577 if (instrume == NULL)
00578 fors_normalise_flat_exit("Missing keyword INSTRUME in master "
00579 "flat header");
00580
00581 if (instrume[4] == '1')
00582 snprintf(version, 80, "%s/%s", "fors1", VERSION);
00583 if (instrume[4] == '2')
00584 snprintf(version, 80, "%s/%s", "fors2", VERSION);
00585
00586 reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
00587
00588 if (cpl_error_get_code() != CPL_ERROR_NONE)
00589 fors_normalise_flat_exit("Missing keyword ESO INS GRIS1 WLEN "
00590 "in master flat frame header");
00591
00592 if (reference < 3000.0)
00593 reference *= 10;
00594
00595 if (reference < 3000.0 || reference > 13000.0) {
00596 cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
00597 "keyword ESO INS GRIS1 WLEN in master flat header",
00598 reference);
00599 fors_normalise_flat_exit(NULL);
00600 }
00601
00602 cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
00603
00604 rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
00605
00606 if (cpl_error_get_code() != CPL_ERROR_NONE)
00607 fors_normalise_flat_exit("Missing keyword ESO DET WIN1 BINX "
00608 "in master flat header");
00609
00610 if (rebin != 1) {
00611 dispersion *= rebin;
00612 cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
00613 "working dispersion used is %f A/pixel", rebin,
00614 dispersion);
00615 }
00616
00617
00618 cpl_msg_indent_less();
00619 cpl_msg_info(recipe, "Load input frames...");
00620 cpl_msg_indent_more();
00621
00622 master_flat = dfs_load_image(frameset, master_flat_tag,
00623 CPL_TYPE_FLOAT, 0, 0);
00624 if (master_flat == NULL)
00625 fors_normalise_flat_exit("Cannot load master flat field frame");
00626
00627
00628 cpl_msg_indent_less();
00629 cpl_msg_info(recipe, "Perform flat field normalisation...");
00630 cpl_msg_indent_more();
00631
00632 if (lss || treat_as_lss) {
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648 smo_flat = mos_normalise_longflat(master_flat,
00649 sradius, dradius, sdegree);
00650
00651 cpl_image_delete(smo_flat); smo_flat = NULL;
00652
00653 if (dfs_save_image(frameset, master_flat, master_norm_flat_tag,
00654 header, parlist, recipe, version))
00655 fors_normalise_flat_exit(NULL);
00656
00657 cpl_propertylist_delete(header); header = NULL;
00658 cpl_image_delete(master_flat); master_flat = NULL;
00659
00660 return 0;
00661 }
00662
00663
00664
00665
00666
00667
00668 slits = dfs_load_table(frameset, slit_location_tag, 1);
00669 if (slits == NULL)
00670 fors_normalise_flat_exit("Cannot load slits location table");
00671
00672 polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
00673 if (slits == NULL)
00674 fors_normalise_flat_exit("Cannot load spectral curvature table");
00675
00676 nx = cpl_image_get_size_x(master_flat);
00677 ny = cpl_image_get_size_y(master_flat);
00678
00679 coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00680 spatial = mos_spatial_calibration(master_flat, slits, polytraces,
00681 reference,
00682 startwavelength, endwavelength,
00683 dispersion, 0, coordinate);
00684
00685 cpl_image_delete(spatial); spatial = NULL;
00686
00687 smo_flat = mos_normalise_flat(master_flat, coordinate, slits, polytraces,
00688 reference, startwavelength, endwavelength,
00689 dispersion, dradius, ddegree);
00690
00691 cpl_image_delete(smo_flat); smo_flat = NULL;
00692 cpl_image_delete(coordinate); coordinate = NULL;
00693 cpl_table_delete(polytraces); polytraces = NULL;
00694 cpl_table_delete(slits); slits = NULL;
00695
00696 if (dfs_save_image(frameset, master_flat, master_norm_flat_tag,
00697 header, parlist, recipe, version))
00698 fors_normalise_flat_exit(NULL);
00699
00700 cpl_propertylist_delete(header); header = NULL;
00701 cpl_image_delete(master_flat); master_flat = NULL;
00702
00703 return 0;
00704 }