HAWKI Pipeline Reference Manual 1.8.12
|
00001 /* $Id: hawki_util_gendist.c,v 1.23 2013/03/11 11:03:00 cgarcia Exp $ 00002 * 00003 * This file is part of the HAWKI Pipeline 00004 * Copyright (C) 2002,2003 European Southern Observatory 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00019 */ 00020 00021 /* 00022 * $Author: cgarcia $ 00023 * $Date: 2013/03/11 11:03:00 $ 00024 * $Revision: 1.23 $ 00025 * $Name: hawki-1_8_12 $ 00026 */ 00027 00028 #ifdef HAVE_CONFIG_H 00029 #include <config.h> 00030 #endif 00031 00032 /*----------------------------------------------------------------------------- 00033 Includes 00034 -----------------------------------------------------------------------------*/ 00035 00036 #include <math.h> 00037 #include <cpl.h> 00038 00039 #include "irplib_utils.h" 00040 00041 #include "hawki_utils.h" 00042 #include "hawki_load.h" 00043 #include "hawki_pfits.h" 00044 #include "hawki_dfs.h" 00045 #include "hawki_distortion.h" 00046 #include "hawki_save.h" 00047 00048 /*----------------------------------------------------------------------------- 00049 Functions prototypes 00050 -----------------------------------------------------------------------------*/ 00051 00052 #ifdef __cplusplus 00053 extern "C" 00054 #endif 00055 int cpl_plugin_get_info(cpl_pluginlist * list); 00056 00057 static int hawki_util_gendist_create(cpl_plugin *) ; 00058 static int hawki_util_gendist_exec(cpl_plugin *) ; 00059 static int hawki_util_gendist_destroy(cpl_plugin *) ; 00060 static int hawki_util_gendist(cpl_parameterlist *, cpl_frameset *) ; 00061 static cpl_table * hawki_util_gendist_convert(const char *) ; 00062 static hawki_distortion * hawki_util_gendist_convert_to_images 00063 (const cpl_table * dist_tab, 00064 int detector_nx, 00065 int detector_ny); 00066 00067 /*----------------------------------------------------------------------------- 00068 Static variables 00069 -----------------------------------------------------------------------------*/ 00070 00071 static char hawki_util_gendist_description[] = 00072 "hawki_util_gendist -- HAWK-I distortion calibration file creation.\n" 00073 "The 4 files (chip 1 2 3 4) listed in the Set Of Frames (sof-file) \n" 00074 "must be tagged:\n" 00075 "raw-file_chip1.txt "HAWKI_UTIL_DISTMAP_RAW"\n" 00076 "raw-file_chip2.txt "HAWKI_UTIL_DISTMAP_RAW"\n" 00077 "raw-file_chip3.txt "HAWKI_UTIL_DISTMAP_RAW"\n" 00078 "raw-file_chip4.txt "HAWKI_UTIL_DISTMAP_RAW"\n" ; 00079 00080 /*----------------------------------------------------------------------------- 00081 Functions code 00082 -----------------------------------------------------------------------------*/ 00083 00084 /*----------------------------------------------------------------------------*/ 00092 /*----------------------------------------------------------------------------*/ 00093 int cpl_plugin_get_info(cpl_pluginlist * list) 00094 { 00095 cpl_recipe * recipe = cpl_calloc(1, sizeof(*recipe)) ; 00096 cpl_plugin * plugin = &recipe->interface ; 00097 00098 cpl_plugin_init(plugin, 00099 CPL_PLUGIN_API, 00100 HAWKI_BINARY_VERSION, 00101 CPL_PLUGIN_TYPE_RECIPE, 00102 "hawki_util_gendist", 00103 "Distortion map creation", 00104 hawki_util_gendist_description, 00105 "Yves Jung", 00106 PACKAGE_BUGREPORT, 00107 hawki_get_license(), 00108 hawki_util_gendist_create, 00109 hawki_util_gendist_exec, 00110 hawki_util_gendist_destroy) ; 00111 00112 cpl_pluginlist_append(list, plugin) ; 00113 00114 return 0; 00115 } 00116 00117 /*----------------------------------------------------------------------------*/ 00126 /*----------------------------------------------------------------------------*/ 00127 static int hawki_util_gendist_create(cpl_plugin * plugin) 00128 { 00129 cpl_recipe * recipe ; 00130 cpl_parameter * p ; 00131 00132 /* Get the recipe out of the plugin */ 00133 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 00134 recipe = (cpl_recipe *)plugin ; 00135 else return -1 ; 00136 00137 /* Create the parameters list in the cpl_recipe object */ 00138 recipe->parameters = cpl_parameterlist_new() ; 00139 if (recipe->parameters == NULL) 00140 return 1; 00141 00142 /* Fill the parameters list */ 00143 /* --dim_x */ 00144 p = cpl_parameter_new_value("hawki.hawki_util_gendist.dim_x", 00145 CPL_TYPE_INT, "Dimension of distortion image in X", 00146 "hawki.hawki_util_gendist", HAWKI_DET_NPIX_X); 00147 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dim_x"); 00148 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00149 cpl_parameterlist_append(recipe->parameters, p); 00150 00151 /* --dim_y */ 00152 p = cpl_parameter_new_value("hawki.hawki_util_gendist.dim_y", 00153 CPL_TYPE_INT, "Dimension of distortion image in Y", 00154 "hawki.hawki_util_gendist", HAWKI_DET_NPIX_Y); 00155 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dim_y"); 00156 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV); 00157 cpl_parameterlist_append(recipe->parameters, p); 00158 00159 00160 /* Return */ 00161 return 0; 00162 } 00163 00164 /*----------------------------------------------------------------------------*/ 00170 /*----------------------------------------------------------------------------*/ 00171 static int hawki_util_gendist_exec(cpl_plugin * plugin) 00172 { 00173 cpl_recipe * recipe ; 00174 00175 /* Get the recipe out of the plugin */ 00176 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 00177 recipe = (cpl_recipe *)plugin ; 00178 else return -1 ; 00179 00180 /* Issue a banner */ 00181 hawki_print_banner(); 00182 00183 return hawki_util_gendist(recipe->parameters, recipe->frames) ; 00184 } 00185 00186 /*----------------------------------------------------------------------------*/ 00192 /*----------------------------------------------------------------------------*/ 00193 static int hawki_util_gendist_destroy(cpl_plugin * plugin) 00194 { 00195 cpl_recipe * recipe ; 00196 00197 /* Get the recipe out of the plugin */ 00198 if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 00199 recipe = (cpl_recipe *)plugin ; 00200 else return -1 ; 00201 00202 cpl_parameterlist_delete(recipe->parameters) ; 00203 return 0 ; 00204 } 00205 00206 /*----------------------------------------------------------------------------*/ 00212 /*----------------------------------------------------------------------------*/ 00213 static int hawki_util_gendist 00214 (cpl_parameterlist * parlist, 00215 cpl_frameset * framelist) 00216 { 00217 cpl_frameset * rawframes; 00218 const cpl_frame * cur_frame; 00219 const char * dist_name; 00220 cpl_table ** distortion_table; 00221 hawki_distortion ** distortion_im; 00222 cpl_propertylist * plist; 00223 cpl_propertylist * plist_ext; 00224 char sval[64]; 00225 const char * recipe_name = "hawki_util_gendist"; 00226 int iext; 00227 int ichip; 00228 cpl_parameter * par; 00229 int dim_x; 00230 int dim_y; 00231 00232 /* Identify the RAW and CALIB frames in the input frameset */ 00233 if (hawki_dfs_set_groups(framelist)) { 00234 cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ; 00235 return -1 ; 00236 } 00237 00238 /* Retrieve raw frames */ 00239 if ((rawframes = hawki_extract_frameset(framelist, 00240 HAWKI_UTIL_DISTMAP_RAW)) == NULL) { 00241 cpl_msg_error(__func__, "Cannot find raw frames in the input list") ; 00242 return -1 ; 00243 } 00244 00245 /* There should be HAWKI_NB_DETECTORS input frames ordered */ 00246 if (cpl_frameset_get_size(rawframes) != HAWKI_NB_DETECTORS) { 00247 cpl_msg_error(__func__, "%d frames expected", HAWKI_NB_DETECTORS) ; 00248 cpl_frameset_delete(rawframes) ; 00249 return -1 ; 00250 } 00251 00252 /* Retrieve parameters */ 00253 par = cpl_parameterlist_find(parlist, "hawki.hawki_util_gendist.dim_x") ; 00254 dim_x = cpl_parameter_get_int(par); 00255 par = cpl_parameterlist_find(parlist, "hawki.hawki_util_gendist.dim_y") ; 00256 dim_y = cpl_parameter_get_int(par); 00257 00258 /* Allocate holder for the tables */ 00259 distortion_table = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_table*)) ; 00260 00261 /* Loop on the chips */ 00262 for (ichip=0 ; ichip<HAWKI_NB_DETECTORS ; ichip++) { 00263 00264 /* Get the file name */ 00265 cur_frame = cpl_frameset_get_frame_const(rawframes, ichip) ; 00266 dist_name = cpl_frame_get_filename(cur_frame) ; 00267 00268 /* Create the output table */ 00269 cpl_msg_info(__func__, "Create the output table for chip %d", ichip+1) ; 00270 if ((distortion_table[ichip] = hawki_util_gendist_convert(dist_name)) == NULL) { 00271 int j; 00272 cpl_msg_error(__func__, "Cannot create the output table") ; 00273 cpl_frameset_delete(rawframes) ; 00274 for (j=0 ; j<ichip ; j++) cpl_table_delete(distortion_table[j]) ; 00275 cpl_free(distortion_table) ; 00276 return -1 ; 00277 } 00278 } 00279 00280 /* Write the table */ 00281 plist = cpl_propertylist_new() ; 00282 cpl_propertylist_append_string(plist, "INSTRUME", "HAWKI") ; 00283 cpl_propertylist_append_string(plist, CPL_DFS_PRO_TYPE, 00284 HAWKI_PROTYPE_DISTORTION) ; 00285 cpl_propertylist_append_string(plist, CPL_DFS_PRO_CATG, 00286 HAWKI_CALPRO_DISTORTION) ; 00287 00288 plist_ext = cpl_propertylist_new() ; 00289 cpl_propertylist_prepend_string(plist_ext, "EXTNAME", "CHIP1.INT1") ; 00290 if (cpl_dfs_save_table(framelist, NULL, parlist, rawframes, NULL, 00291 distortion_table[0], plist_ext, recipe_name, 00292 plist, NULL, PACKAGE "/" PACKAGE_VERSION, 00293 "hawki_util_gendist.fits") != CPL_ERROR_NONE) { 00294 cpl_msg_error(__func__, "Cannot save the first extension table") ; 00295 cpl_propertylist_delete(plist_ext) ; 00296 cpl_propertylist_delete(plist) ; 00297 for (iext=0 ; iext<HAWKI_NB_DETECTORS ; iext++) 00298 cpl_table_delete(distortion_table[iext]) ; 00299 cpl_free(distortion_table) ; 00300 cpl_frameset_delete(rawframes) ; 00301 return -1 ; 00302 } 00303 cpl_propertylist_delete(plist) ; 00304 cpl_propertylist_delete(plist_ext) ; 00305 00306 /* Save the extensions */ 00307 for (iext=1 ; iext<HAWKI_NB_DETECTORS; iext++) 00308 { 00309 ichip = iext; 00310 //This is the actual layout of the chips in raw HAWK-I images. 00311 if(iext == 2) 00312 ichip = 3; 00313 if(iext == 3) 00314 ichip = 2; 00315 plist_ext = cpl_propertylist_new() ; 00316 sprintf(sval, "CHIP%d.INT1", ichip+1) ; 00317 cpl_propertylist_prepend_string(plist_ext, "EXTNAME", sval) ; 00318 cpl_table_save(distortion_table[ichip], NULL, plist_ext, 00319 "hawki_util_gendist.fits", CPL_IO_EXTEND); 00320 cpl_propertylist_delete(plist_ext) ; 00321 } 00322 00323 /* Allocate holder for the distortion images */ 00324 distortion_im = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(hawki_distortion*)); 00325 00326 /* Loop on the chips */ 00327 for (ichip=0 ; ichip<HAWKI_NB_DETECTORS ; ichip++) 00328 { 00329 /* Get the file name */ 00330 cur_frame = cpl_frameset_get_frame_const(rawframes, ichip) ; 00331 dist_name = cpl_frame_get_filename(cur_frame) ; 00332 00333 /* Create the output image */ 00334 cpl_msg_info(__func__, "Create the output images for extension %d", ichip+1) ; 00335 if ((distortion_im[ichip] = hawki_util_gendist_convert_to_images 00336 (distortion_table[ichip], dim_x, dim_y)) == NULL) 00337 { 00338 int j; 00339 cpl_msg_error(__func__,"Cannot create the output distortion images"); 00340 cpl_frameset_delete(rawframes); 00341 for (j=0 ;j < ichip; j++) 00342 hawki_distortion_delete(distortion_im[j]); 00343 cpl_free(distortion_im) ; 00344 return -1 ; 00345 } 00346 } 00347 00348 /* Write the distortion images */ 00349 plist = cpl_propertylist_new() ; 00350 cpl_propertylist_append_string(plist, "INSTRUME", "HAWKI"); 00351 //This two keywords are needed by QC. I do not why.. 00352 cpl_propertylist_append_string(plist, "MJD-OBS", "55128.5000000"); 00353 cpl_propertylist_append_string(plist, "FOR_QC", "dummy.fits"); 00354 cpl_propertylist_append_string(plist, "ORIGFILE", 00355 "hawki_util_gendist_distx.fits") ; 00356 hawki_main_header_save(framelist, parlist, rawframes, 00357 "hawki_util_gendist", 00358 HAWKI_CALPRO_DISTORTION_X, 00359 HAWKI_PROTYPE_DISTORTION_X, 00360 plist, "hawki_util_gendist_distx.fits"); 00361 cpl_propertylist_erase(plist, "ORIGFILE"); 00362 cpl_propertylist_append_string(plist, "ORIGFILE", 00363 "hawki_util_gendist_distx.fits") ; 00364 hawki_main_header_save(framelist, parlist, rawframes, 00365 "hawki_util_gendist", 00366 HAWKI_CALPRO_DISTORTION_Y, 00367 HAWKI_PROTYPE_DISTORTION_Y, 00368 plist, "hawki_util_gendist_disty.fits"); 00369 cpl_propertylist_delete(plist) ; 00370 00371 /* Save the extensions */ 00372 //There is kind of a hack here 00373 //We use the distortion table as a reference to save the distortion images 00374 //to have a proper layout of the detectors in the FITS file. 00375 //For that hawki_get_extref_file has been modified. 00376 for (iext=0 ; iext<HAWKI_NB_DETECTORS; iext++) { 00377 ichip = iext; 00378 //This is the actual layout of the chips in raw HAWK-I images. 00379 if(iext == 2) 00380 ichip = 3; 00381 if(iext == 3) 00382 ichip = 2; 00383 plist_ext = cpl_propertylist_new() ; 00384 cpl_propertylist_append_double(plist_ext, "CRVAL1", 00385 distortion_im[ichip]->x_crval); 00386 cpl_propertylist_append_double(plist_ext, "CRVAL2", 00387 distortion_im[ichip]->y_crval); 00388 cpl_propertylist_append_double(plist_ext, "CDELT1", 00389 distortion_im[ichip]->x_cdelt); 00390 cpl_propertylist_append_double(plist_ext, "CDELT2", 00391 distortion_im[ichip]->y_cdelt); 00392 cpl_propertylist_append_double(plist_ext, "CRPIX1", 1); 00393 cpl_propertylist_append_double(plist_ext, "CRPIX2", 1); 00394 cpl_propertylist_append_string(plist_ext, "CTYPE1", ""); 00395 cpl_propertylist_append_string(plist_ext, "CTYPE2", ""); 00396 cpl_propertylist_append_string(plist_ext, "CUNIT1", ""); 00397 cpl_propertylist_append_string(plist_ext, "CUNIT2", ""); 00398 hawki_image_ext_save(framelist, distortion_im[ichip]->dist_x, iext + 1, 00399 plist_ext, "hawki_util_gendist_distx.fits"); 00400 hawki_image_ext_save(framelist, distortion_im[ichip]->dist_y, iext + 1, 00401 plist_ext, "hawki_util_gendist_disty.fits"); 00402 cpl_propertylist_delete(plist_ext); 00403 } 00404 00405 for (iext=0 ; iext<HAWKI_NB_DETECTORS ; iext++) 00406 cpl_table_delete(distortion_table[iext]); 00407 cpl_free(distortion_table) ; 00408 00409 for (iext=0 ; iext<HAWKI_NB_DETECTORS ; iext++) 00410 hawki_distortion_delete(distortion_im[iext]); 00411 cpl_free(distortion_im) ; 00412 cpl_frameset_delete(rawframes) ; 00413 00414 00415 /* Return */ 00416 if (cpl_error_get_code()) 00417 { 00418 cpl_msg_error(__func__, 00419 "HAWK-I pipeline could not recover from previous errors"); 00420 return -1 ; 00421 } 00422 else return 0 ; 00423 } 00424 00425 /*----------------------------------------------------------------------------*/ 00450 /*----------------------------------------------------------------------------*/ 00451 static cpl_table * hawki_util_gendist_convert(const char * filename) 00452 { 00453 cpl_table * out ; 00454 int nbentries ; 00455 FILE * in ; 00456 double dxgc, dygc ; 00457 int i, j ; 00458 char line[1024]; 00459 00460 /* Check entries */ 00461 if (filename == NULL) return NULL ; 00462 00463 /* Get the number of lines */ 00464 nbentries = 0 ; 00465 if ((in = fopen(filename, "r")) == NULL) { 00466 return NULL ; 00467 } 00468 while (fgets(line, 1024, in) != NULL) { 00469 if (line[0] != '#') nbentries ++ ; 00470 } 00471 fclose(in) ; 00472 00473 /* Create the table */ 00474 out = cpl_table_new(nbentries); 00475 cpl_table_new_column(out, HAWKI_COL_DIST_DXGC, CPL_TYPE_DOUBLE); 00476 cpl_table_set_column_unit(out, HAWKI_COL_DIST_DXGC, "pixels"); 00477 cpl_table_new_column(out, HAWKI_COL_DIST_DYGC, CPL_TYPE_DOUBLE); 00478 cpl_table_set_column_unit(out, HAWKI_COL_DIST_DYGC, "pixels"); 00479 cpl_table_new_column(out, HAWKI_COL_DIST_I, CPL_TYPE_INT); 00480 cpl_table_set_column_unit(out, HAWKI_COL_DIST_I, "pixels"); 00481 cpl_table_new_column(out, HAWKI_COL_DIST_J, CPL_TYPE_INT); 00482 cpl_table_set_column_unit(out, HAWKI_COL_DIST_J, "pixels"); 00483 00484 /* Parse the file */ 00485 if ((in = fopen(filename, "r")) == NULL) { 00486 cpl_table_delete(out) ; 00487 return NULL ; 00488 } 00489 nbentries = 0 ; 00490 while (fgets(line, 1024, in) != NULL) { 00491 if (line[0] != '#') { 00492 if (sscanf(line, "%lg %lg %d %d", 00493 &dxgc, &dygc, &i, &j) != 4) { 00494 cpl_msg_error(__func__, "Bad line %d", nbentries+1) ; 00495 cpl_table_delete(out) ; 00496 return NULL ; 00497 } 00498 cpl_table_set_double(out, HAWKI_COL_DIST_DXGC, nbentries, dxgc); 00499 cpl_table_set_double(out, HAWKI_COL_DIST_DYGC, nbentries, dygc); 00500 cpl_table_set_int(out, HAWKI_COL_DIST_I, nbentries, i); 00501 cpl_table_set_int(out, HAWKI_COL_DIST_J, nbentries, j); 00502 nbentries ++ ; 00503 } 00504 } 00505 fclose(in) ; 00506 00507 return out ; 00508 } 00509 00510 /*----------------------------------------------------------------------------*/ 00528 /*----------------------------------------------------------------------------*/ 00529 static hawki_distortion * hawki_util_gendist_convert_to_images 00530 (const cpl_table * dist_tab, 00531 int detector_nx, 00532 int detector_ny) 00533 { 00534 hawki_distortion * out ; 00535 int nbentries ; 00536 int ngrid; 00537 const int * i_ptr; 00538 const int * j_ptr; 00539 cpl_array * i_vec; 00540 cpl_array * j_vec; 00541 int irow; 00542 00543 00544 /* Check entries */ 00545 if (dist_tab == NULL) return NULL ; 00546 00547 /* Create the table */ 00548 nbentries = cpl_table_get_nrow(dist_tab); 00549 ngrid = sqrt(nbentries); 00550 if(ngrid * ngrid != nbentries) 00551 { 00552 cpl_msg_error(__func__,"Only square grids are supported"); 00553 return NULL; 00554 } 00555 out = hawki_distortion_grid_new(detector_nx, detector_ny, ngrid); 00556 00557 /* Get the crval, cdelt */ 00558 i_ptr = cpl_table_get_data_int_const(dist_tab, HAWKI_COL_DIST_I); 00559 i_vec = cpl_array_wrap_int((int *)i_ptr, nbentries); 00560 out->x_crval = cpl_array_get_min(i_vec); 00561 out->x_cdelt = (cpl_array_get_max(i_vec) - cpl_array_get_min(i_vec)) / 00562 (ngrid - 1); 00563 cpl_array_unwrap(i_vec); 00564 j_ptr = cpl_table_get_data_int_const(dist_tab, HAWKI_COL_DIST_J); 00565 j_vec = cpl_array_wrap_int((int *)j_ptr, nbentries); 00566 out->y_crval = cpl_array_get_min(j_vec); 00567 out->y_cdelt = (cpl_array_get_max(j_vec) - cpl_array_get_min(j_vec)) / 00568 (ngrid - 1); 00569 cpl_array_unwrap(j_vec); 00570 00571 00572 /* Fill the image */ 00573 for(irow = 0; irow < nbentries; ++irow) 00574 { 00575 double i_ima; 00576 double j_ima; 00577 double dx; 00578 double dy; 00579 int null; 00580 00581 i_ima = (cpl_table_get_int(dist_tab, HAWKI_COL_DIST_I, irow, &null) - 00582 out->x_crval) / out->x_cdelt; 00583 if(floor(i_ima) != i_ima) 00584 { 00585 cpl_msg_error(__func__, " The distortion tables must be defined " 00586 "in a regular grid"); 00587 hawki_distortion_delete(out); 00588 return NULL; 00589 } 00590 j_ima = (cpl_table_get_int(dist_tab, HAWKI_COL_DIST_J, irow, &null) - 00591 out->y_crval) / out->y_cdelt; 00592 if(floor(j_ima) != j_ima) 00593 { 00594 cpl_msg_error(__func__, " The distortion tables must be defined " 00595 "in a regular grid"); 00596 hawki_distortion_delete(out); 00597 return NULL; 00598 } 00599 dx = cpl_table_get_double(dist_tab, HAWKI_COL_DIST_DXGC, irow, &null); 00600 dy = cpl_table_get_double(dist_tab, HAWKI_COL_DIST_DYGC, irow, &null); 00601 00602 cpl_image_set(out->dist_x, (int)i_ima + 1, (int)j_ima + 1, dx); 00603 cpl_image_set(out->dist_y, (int)i_ima + 1, (int)j_ima + 1, dy); 00604 } 00605 00606 return out ; 00607 }