ERIS Pipeline Reference Manual 1.8.15
eris_ifu_skycorr.c
1#ifdef HAVE_CONFIG_H
2#include <config.h>
3#endif
4
5#include <math.h>
6
7#include "eris_ifu_skycorr.h"
8#include "eris_ifu_error.h"
9#include "eris_ifu_lambda_corr.h"
10#include "eris_ifu_error.h"
11#include "skycorr/sc_basic.h"
12
24cpl_table* eris_ifu_skycorr_extract_spectrum(const hdrl_imagelist *cube,
25 const cpl_propertylist *header,
26 cpl_size x,
27 cpl_size y)
28{
29 cpl_table *table = NULL;
30 cpl_vector *lambda = NULL;
31 cpl_image *img_data = NULL,
32 *img_err = NULL;
33 hdrl_image *hdrl_img = NULL;
34 double *plambda = NULL,
35 *pdata = NULL,
36 *perror = NULL,
37 *pweight = NULL,
38 val = 0;
39 cpl_size nz = 0;
40 int rej = 0,
41 *pmask = NULL,
42 *pmask_i = NULL;
43
44 cpl_ensure(cube != NULL, CPL_ERROR_NULL_INPUT, NULL);
45 cpl_ensure(header != NULL, CPL_ERROR_NULL_INPUT, NULL);
46 cpl_ensure(x > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
47 cpl_ensure(y > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
48
49 TRY {
53 plambda = cpl_vector_get_data(lambda));
54 nz = cpl_vector_get_size(lambda);
55
57 table = cpl_table_new(nz));
58
60 pdata = cpl_malloc(nz*sizeof(double)));
62 perror = cpl_malloc(nz*sizeof(double)));
64 pmask = cpl_malloc(nz*sizeof(int)));
66 pmask_i = cpl_malloc(nz*sizeof(int)));
68 pweight = cpl_malloc(nz*sizeof(double)));
69
70 // loop over all lambda slices
71 for (cpl_size z = 0; z < nz; z++) {
73 hdrl_img = hdrl_imagelist_get(cube, z));
75 img_data = hdrl_image_get_image(hdrl_img));
77 img_err = hdrl_image_get_error(hdrl_img));
78
79 val = cpl_image_get(img_data, x, y, &rej);
80
81 if (rej == 1) {
82 pdata[z] = 0.;
83 perror[z] = 0.;
84 pmask[z] = 0; // pixel is bad
85 pmask_i[z] = 0; // pixel is bad
86 pweight[z] = 0.;
87 } else {
88 pdata[z] = val;
89 perror[z] = cpl_image_get(img_err, x, y, &rej);
90 pmask[z] = 1; // pixel is good
91 pmask_i[z] = 1; // pixel is good
92 pweight[z] = 1./perror[z];
93 }
94 }
95
97 cpl_table_wrap_double(table, plambda, "lambda"));
99 cpl_table_wrap_double(table, pdata, "flux"));
101 cpl_table_wrap_double(table, perror, "dflux"));
103 cpl_table_wrap_int(table, pmask, "mask"));
105 cpl_table_wrap_double(table, pweight, "weight"));
107 cpl_table_wrap_int(table, pmask_i, "mask_I"));
108 } CATCH {
109 eris_ifu_free_table(&table);
110 }
111
112 return table;
113}
119cpl_table* eris_ifu_skycorr_create_nan_spectrum(const hdrl_imagelist *cube)
120{
121 cpl_size nz = 0;
122 cpl_table *table = NULL;
123 double *pscflux = NULL,
124 *pscdflux = NULL;
125 int *pscmask = NULL;
126
127 cpl_ensure(cube != NULL, CPL_ERROR_NULL_INPUT, NULL);
128
129 TRY {
130 nz = hdrl_imagelist_get_size(cube);
131
133 table = cpl_table_new(nz));
134
136 pscflux = cpl_malloc(nz*sizeof(double)));
138 pscdflux = cpl_malloc(nz*sizeof(double)));
140 pscmask = cpl_malloc(nz*sizeof(int)));
141
142 // loop over all lambda slices
143 for (cpl_size z = 0; z < nz; z++) {
144 pscflux[z] = NAN;
145 pscdflux[z] = NAN;
146 pscmask[z] = 0;
147 }
148
150 cpl_table_wrap_double(table, pscflux, "scflux"));
152 cpl_table_wrap_double(table, pscdflux, "scdflux"));
154 cpl_table_wrap_int(table, pscmask, "scmask"));
155 } CATCH {
156 eris_ifu_free_table(&table);
157 }
158
159 return table;
160}
172cpl_error_code eris_ifu_skycorr_insert_spectrum(hdrl_imagelist *cube,
173 cpl_table *scspec,
174 cpl_size x,
175 cpl_size y)
176{
177 cpl_image *img_data = NULL,
178 *img_err = NULL;
179 hdrl_image *hdrl_img = NULL;
180 const double *scflux = NULL,
181 *scdflux = NULL;
182 const int *scmask = NULL;
183 cpl_size nz = 0;
184 cpl_mask *img_mask = NULL;
185 cpl_error_code ret = CPL_ERROR_NONE;
186
187 cpl_ensure_code(cube != NULL, CPL_ERROR_NULL_INPUT);
188 cpl_ensure_code(scspec != NULL, CPL_ERROR_NULL_INPUT);
189 cpl_ensure_code(x > 0, CPL_ERROR_ILLEGAL_INPUT);
190 cpl_ensure_code(y > 0, CPL_ERROR_ILLEGAL_INPUT);
191
192 TRY {
193 nz = hdrl_imagelist_get_size(cube);
194
196 scflux = cpl_table_get_data_double_const(scspec, "scflux"));
198 scdflux = cpl_table_get_data_double_const(scspec, "scdflux"));
200 scmask = cpl_table_get_data_int_const(scspec, "scmask"));
201
202 // loop over all lambda slices
203 for (cpl_size z = 0; z < nz; z++) {
205 hdrl_img = hdrl_imagelist_get(cube, z));
206
208 img_data = hdrl_image_get_image(hdrl_img));
210 img_err = hdrl_image_get_error(hdrl_img));
212 img_mask = hdrl_image_get_mask(hdrl_img));
213
214 if (scmask[z] != 0) {
215 // value is valid
217 cpl_mask_set(img_mask, x, y, GOOD_PIX));
219 cpl_image_set(img_data, x, y, scflux[z]));
221 cpl_image_set(img_err, x, y, scdflux[z]));
222
224 cpl_mask_set(img_mask, x, y+1, GOOD_PIX));
226 cpl_image_set(img_data, x, y+1, scflux[z]));
228 cpl_image_set(img_err, x, y+1, scdflux[z]));
229 } else {
230 // assert that pixel is set invalid
231 // scmask[z] == 0 -> bad pixel
232 // img_err == BAD_PIX -> bad pixel
234 cpl_mask_set(img_mask, x, y, BAD_PIX));
236 cpl_image_set(img_data, x, y, NAN));
238 cpl_image_set(img_err, x, y, NAN));
239
241 cpl_mask_set(img_mask, x, y+1, BAD_PIX));
243 cpl_image_set(img_data, x, y+1, NAN));
245 cpl_image_set(img_err, x, y+1, NAN));
246 }
247 }
249 } CATCH {
250 ret = cpl_error_get_code();
251 }
252
253 return ret;
254}
255
265cpl_error_code eris_ifu_skycorr_flatten_outliers(cpl_table *objspec,
266 cpl_table *skyspec,
267 int f)
268{
269 double mean_of = 0.,
270 mean_sf = 0.,
271 mean_odf = 0.,
272 mean_sdf = 0.,
273 limit_of = 0.,
274 limit_sf = 0.,
275 limit_odf = 0.,
276 limit_sdf = 0.,
277 *data_of = NULL,
278 *data_sf = NULL,
279 *data_odf = NULL,
280 *data_sdf = NULL;
281 cpl_error_code ret = CPL_ERROR_NONE;
282
283 cpl_ensure_code(objspec != NULL, CPL_ERROR_NULL_INPUT);
284 cpl_ensure_code(skyspec != NULL, CPL_ERROR_NULL_INPUT);
285 cpl_ensure_code(f > 0, CPL_ERROR_ILLEGAL_INPUT);
286
287 TRY {
288 mean_of = cpl_table_get_column_mean(objspec, "flux");
289 mean_sf = cpl_table_get_column_mean(skyspec, "flux");
290 mean_odf = cpl_table_get_column_mean(objspec, "dflux");
291 mean_sdf = cpl_table_get_column_mean(skyspec, "dflux");
292
293 limit_of = mean_of * f;
294 limit_sf = mean_sf * f;
295 limit_odf = mean_odf * f;
296 limit_sdf = mean_sdf * f;
297
298 data_of = cpl_table_get_data_double(objspec, "flux");
299 data_sf = cpl_table_get_data_double(skyspec, "flux");
300 data_odf = cpl_table_get_data_double(objspec, "dflux");
301 data_sdf = cpl_table_get_data_double(skyspec, "dflux");
302
304
305 for (int i = 0; i < cpl_table_get_nrow(objspec); i++) {
306 if (data_of[i] > limit_of) {
307 data_of[i] = mean_of;
308 }
309 if (data_sf[i] > limit_sf) {
310 data_sf[i] = mean_sf;
311 }
312 if (data_odf[i] > limit_odf) {
313 data_odf[i] = mean_odf;
314 }
315 if (data_sdf[i] > limit_sdf) {
316 data_sdf[i] = mean_sdf;
317 }
318 }
319 } CATCH {
320 ret = cpl_error_get_code();
321 }
322
323 return ret;
324}
325
335cpl_error_code eris_ifu_skycorr_flatten_outliers2(cpl_table *objspec,
336 cpl_table *skyspec,
337 double min_frac)
338{
339 cpl_error_code ret = CPL_ERROR_NONE;
340 cpl_size nz = 0;
341 cpl_bivector *spectrum = NULL;
342// cpl_vector *outliers = NULL;
343 cpl_array *positions = NULL;
344 double *pobjlambda = NULL,
345 *pobjflux = NULL,
346 *pobjdflux = NULL,
347 *pobjweight = NULL,
348 *pskyflux = NULL,
349 *pskydflux = NULL,
350 *pskyweight = NULL,
351 *pbiveclambda = NULL,
352 *pbivecflux = NULL/*,
353 *poutliers = NULL*/;
354 int *pobjmask = NULL,
355 *pskymask = NULL,
356 *pobjmaskI = NULL,
357 *pskymaskI = NULL,
358 *ppositions = NULL;
359
360 cpl_ensure_code(objspec != NULL, CPL_ERROR_NULL_INPUT);
361 cpl_ensure_code(skyspec != NULL, CPL_ERROR_NULL_INPUT);
362 cpl_ensure_code((min_frac > 0) && (min_frac <= 1.0), CPL_ERROR_ILLEGAL_INPUT);
363
364 TRY {
365 // analyze outliers on obj spec
366 nz = cpl_table_get_nrow(objspec);
368 spectrum = cpl_bivector_new(nz));
369
371 pobjlambda = cpl_table_get_data_double(objspec, "lambda"));
372
374 pobjflux = cpl_table_get_data_double(objspec, "flux"));
376 pobjdflux = cpl_table_get_data_double(objspec, "dflux"));
378 pobjmask = cpl_table_get_data_int(objspec, "mask"));
380 pobjweight = cpl_table_get_data_double(objspec, "weight"));
382 pobjmaskI = cpl_table_get_data_int(objspec, "mask_I"));
383
385 pskyflux = cpl_table_get_data_double(skyspec, "flux"));
387 pskydflux = cpl_table_get_data_double(skyspec, "dflux"));
389 pskymask = cpl_table_get_data_int(skyspec, "mask"));
391 pskyweight = cpl_table_get_data_double(skyspec, "weight"));
393 pskymaskI = cpl_table_get_data_int(skyspec, "mask_I"));
394
396 pbiveclambda = cpl_bivector_get_x_data(spectrum));
398 pbivecflux = cpl_bivector_get_y_data(spectrum));
399
400 for (cpl_size z = 0; z < nz; z++) {
401 pbiveclambda[z] = pobjlambda[z];
402 pbivecflux[z] = pobjflux[z];
403 }
404
406 positions = eris_ifu_lcorr_get_peak_positions(spectrum, min_frac, NULL));
408 ppositions = cpl_array_get_data_int(positions));
409
410 // mask outliers on obj & sky spec
411 for (cpl_size o = 0; o < cpl_array_get_size(positions); o++) {
412 int pos = ppositions[o];
413 pobjflux[pos] = 0.;
414 pobjdflux[pos] = 0.;
415 pobjmask[pos] = 0;
416 pobjweight[pos] = 0.;
417 pobjmaskI[pos] = 0;
418
419 pskyflux[pos] = 0.;
420 pskydflux[pos] = 0.;
421 pskymask[pos] = 0;
422 pskyweight[pos] = 0.;
423 pskymaskI[pos] = 0;
424 }
425 } CATCH {
426 ret = cpl_error_get_code();
427
428 }
429 cpl_array_delete(positions);
430 eris_ifu_free_bivector(&spectrum);
431
432 return ret;
433}
441cpl_parameterlist* eris_ifu_skycorr_create_parlist(const cpl_propertylist *sky_header)
442{
443 cpl_parameterlist *parlist = NULL;
444 cpl_parameter *p = NULL;
445 const cpl_property *prop = NULL;
446
447 TRY {
449 parlist = cpl_parameterlist_new());
450
451 prop = cpl_propertylist_get_property_const(sky_header, "MJD-OBS");
452 double mjd = cpl_property_get_double(prop);
453 double val = sc_basic_mjd2fracyear(mjd);
454 prop = cpl_propertylist_get_property_const(sky_header, "UTC");
455 double utc = cpl_property_get_double(prop);
456 prop = cpl_propertylist_get_property_const(sky_header, "ESO TEL ALT");
457 double telalt = cpl_property_get_double(prop);
458
459 /* Directories and files */
460 // INST_DIR
461 p = cpl_parameter_new_value("inst_dir", CPL_TYPE_STRING, "", "", ".");
462 cpl_parameterlist_append(parlist, p);
463 //
464 p = cpl_parameter_new_value("data_dir", CPL_TYPE_STRING, "", "", "sysdata/");
465 cpl_parameterlist_append(parlist, p); /* not to be changed by user */
466 //
467 p = cpl_parameter_new_value("config_dir", CPL_TYPE_STRING, "", "", "config/");
468 cpl_parameterlist_append(parlist, p); /* not to be changed by user */
469 //
470 p = cpl_parameter_new_value("spectype", CPL_TYPE_STRING, "", "", "SKY");
471 cpl_parameterlist_append(parlist, p);
472 // INPUT_OBJECT_SPECTRUM
473 p = cpl_parameter_new_value("scispec", CPL_TYPE_STRING, "", "", "eris-obj-spec.fits");
474 cpl_parameterlist_append(parlist, p);
475 // INPUT_SKY_SPECTRUM
476 p = cpl_parameter_new_value("skyspec", CPL_TYPE_STRING, "", "", "eris-sky-spec.fits");
477 cpl_parameterlist_append(parlist, p);
478 // OUTPUT_DIR
479 p = cpl_parameter_new_value("output_dir", CPL_TYPE_STRING, "", "", ".");
480 cpl_parameterlist_append(parlist, p);
481 // OUTPUT_NAME
482 p = cpl_parameter_new_value("output_name", CPL_TYPE_STRING, "", "", "TEST-ERIS");
483 cpl_parameterlist_append(parlist, p);
484
485 /* Input structure */
486 // COL_NAMES
487 p = cpl_parameter_new_value("col_lam", CPL_TYPE_STRING, "", "", "lambda");
488 cpl_parameterlist_append(parlist, p);
489 p = cpl_parameter_new_value("col_flux", CPL_TYPE_STRING, "", "", "flux");
490 cpl_parameterlist_append(parlist, p);
491 p = cpl_parameter_new_value("col_dflux", CPL_TYPE_STRING, "", "", "dflux");
492 cpl_parameterlist_append(parlist, p);
493 p = cpl_parameter_new_value("col_mask", CPL_TYPE_STRING, "", "", "mask");
494 cpl_parameterlist_append(parlist, p);
495 // DEFAULT_ERROR
496 p = cpl_parameter_new_value("default_error", CPL_TYPE_DOUBLE, "", "", 0.01);
497 cpl_parameterlist_append(parlist, p);
498 // WLG_TO_MICRON
499 p = cpl_parameter_new_value("wlgtomicron", CPL_TYPE_DOUBLE, "", "", 1.);
500 cpl_parameterlist_append(parlist, p);
501 // VAC_AIR
502 p = cpl_parameter_new_value("vac_air", CPL_TYPE_STRING, "", "", "vac");
503 cpl_parameterlist_append(parlist, p);
504
505 /* FITS header keywords */
506 // DATE_KEY
507 p = cpl_parameter_new_value("date_key", CPL_TYPE_STRING, "", "", "MJD-OBS");
508 cpl_parameterlist_append(parlist, p);
509 // DATE_VAL
510 p = cpl_parameter_new_value("date_val", CPL_TYPE_DOUBLE, "", "", val);
511 cpl_parameterlist_append(parlist, p);
512 //
513 p = cpl_parameter_new_value("mjd", CPL_TYPE_DOUBLE, "", "", mjd);
514 cpl_parameterlist_append(parlist, p);
515 // TIME_KEY
516 p = cpl_parameter_new_value("time_key", CPL_TYPE_STRING, "", "", "UTC");
517 cpl_parameterlist_append(parlist, p);
518 // TIME_VAL
519 p = cpl_parameter_new_value("time_val", CPL_TYPE_DOUBLE, "", "", utc);
520 cpl_parameterlist_append(parlist, p);
521 // TELALT_KEY
522 p = cpl_parameter_new_value("telalt_key", CPL_TYPE_STRING, "", "", "ESO TEL ALT");
523 cpl_parameterlist_append(parlist, p);
524 // TELALT_VAL
525 p = cpl_parameter_new_value("telalt_val", CPL_TYPE_DOUBLE, "", "", telalt);
526 cpl_parameterlist_append(parlist, p);
527
528 /* Required input data */
529 // LINETABNAME
530 p = cpl_parameter_new_value("linetabname", CPL_TYPE_STRING, "", "", "airglow_groups.dat");
531 cpl_parameterlist_append(parlist, p);
532 // VARDATNAME
533 p = cpl_parameter_new_value("vardatname", CPL_TYPE_STRING, "", "", "airglow_var.dat");
534 cpl_parameterlist_append(parlist, p);
535 // SOLDATURL
536 p = cpl_parameter_new_value("soldaturl", CPL_TYPE_STRING, "", "", "ftp://ftp.seismo.nrcan.gc.ca/spaceweather/solar_flux/monthly_averages");
537 cpl_parameterlist_append(parlist, p);
538 // SOLDATNAME
539 p = cpl_parameter_new_value("soldatname", CPL_TYPE_STRING, "", "", "solflux_monthly_average.txt");
540 cpl_parameterlist_append(parlist, p);
541 //
542 p = cpl_parameter_new_value("soldatsource", CPL_TYPE_STRING, "", "", "NONE");
543 cpl_parameterlist_append(parlist, p);
544 // SOLFLUX
545 p = cpl_parameter_new_value("solflux", CPL_TYPE_DOUBLE, "", "", -1.);
546 cpl_parameterlist_append(parlist, p);
547
548 /* Line identification */
549 // FWHM
550 p = cpl_parameter_new_value("fwhm", CPL_TYPE_DOUBLE, "", "", 5.);
551 cpl_parameterlist_append(parlist, p);
552 // VARFWHM
553 p = cpl_parameter_new_range("varfwhm", CPL_TYPE_INT, "", "", 0, 0, 0);
554 cpl_parameterlist_append(parlist, p);
555 //
556 p = cpl_parameter_new_value("meanlam", CPL_TYPE_DOUBLE, "", "", 1.);
557 cpl_parameterlist_append(parlist, p);
558 // LTOL
559 p = cpl_parameter_new_value("ltol", CPL_TYPE_DOUBLE, "", "", 0.01);
560 cpl_parameterlist_append(parlist, p);
561 //
562 p = cpl_parameter_new_value("min_line_width_fac", CPL_TYPE_DOUBLE, "", "", 0.);
563 cpl_parameterlist_append(parlist, p);
564 // MIN_LINE_DIST
565 p = cpl_parameter_new_value("min_line_dist_fac", CPL_TYPE_DOUBLE, "", "", 2.5);
566 cpl_parameterlist_append(parlist, p);
567 // MIN_LINE_FLUX
568 p = cpl_parameter_new_value("min_line_flux_fac", CPL_TYPE_DOUBLE, "", "", 0.);
569 cpl_parameterlist_append(parlist, p);
570 // FLUXLIM
571 p = cpl_parameter_new_value("fluxlim", CPL_TYPE_DOUBLE, "", "", -1.);
572 cpl_parameterlist_append(parlist, p);
573 //
574 p = cpl_parameter_new_value("iteration",CPL_TYPE_INT, "", "", 0);
575 cpl_parameterlist_append(parlist, p);
576
577 /* Fitting of sky lines */
578 // FTOL
579 p = cpl_parameter_new_value("ftol", CPL_TYPE_DOUBLE, "", "", 1e-3);
580 cpl_parameterlist_append(parlist, p);
581 // XTOL
582 p = cpl_parameter_new_value("xtol", CPL_TYPE_DOUBLE, "", "", 1e-3);
583 cpl_parameterlist_append(parlist, p);
584 // WTOL
585 p = cpl_parameter_new_value("wtol", CPL_TYPE_DOUBLE, "", "", 1e-3);
586 cpl_parameterlist_append(parlist, p);
587 // CHEBY_MAX
588 p = cpl_parameter_new_value("cheby_max", CPL_TYPE_INT, "", "", 7);
589 cpl_parameterlist_append(parlist, p);
590 // CHEBY_MIN
591 p = cpl_parameter_new_value("cheby_min", CPL_TYPE_INT, "", "", 3);
592 cpl_parameterlist_append(parlist, p);
593 // CHEBY_CONST
594 p = cpl_parameter_new_value("cheby_const", CPL_TYPE_DOUBLE, "", "", 0.);
595 cpl_parameterlist_append(parlist, p);
596 // REBINTYPE
597 p = cpl_parameter_new_value("rebintype", CPL_TYPE_INT, "", "", 1);
598 cpl_parameterlist_append(parlist, p);
599 // WEIGHTLIM
600 p = cpl_parameter_new_value("weightlim", CPL_TYPE_DOUBLE, "", "", 0.67);
601 cpl_parameterlist_append(parlist, p);
602 // SIGLIM
603 p = cpl_parameter_new_value("siglim", CPL_TYPE_DOUBLE, "", "", 15.);
604 cpl_parameterlist_append(parlist, p);
605 // FITLIM
606 p = cpl_parameter_new_value("fitlim", CPL_TYPE_DOUBLE, "", "", 0.);
607 cpl_parameterlist_append(parlist, p);
608
610 } CATCH {
612 }
613 return parlist;
614}
#define BRK_IF_ERROR(function)
If function is or returns an error != CPL_ERROR_NONE, then the try-block is exited.
#define CHECK_ERROR_STATE(void)
Check the CPL error state, and exit the try-block if not CPL_ERROR_NONE.
#define TRY
Beginning of a TRY-block.
#define CATCH
End of a TRY-block, beginning of a CATCH-block.
#define BRK_IF_NULL(function)
If function is or returns a NULL pointer, then the try-block is exited.
cpl_vector * eris_ifu_lcorr_create_lambda_vector(const cpl_propertylist *header)
Create wavelength vector from FITS header WCS keywords.
cpl_array * eris_ifu_lcorr_get_peak_positions(const cpl_bivector *spectrum, double min_frac, cpl_vector *range)
Detect emission line peak positions in a spectrum.
void eris_ifu_free_table(cpl_table **item)
Free memory and set pointer to null.
void eris_ifu_free_parameterlist(cpl_parameterlist **item)
Free memory and set pointer to null.
void eris_ifu_free_bivector(cpl_bivector **item)
Free memory and set pointer to null.
cpl_mask * hdrl_image_get_mask(hdrl_image *himg)
get cpl bad pixel mask from image
Definition: hdrl_image.c:157
cpl_image * hdrl_image_get_error(hdrl_image *himg)
get error as cpl image
Definition: hdrl_image.c:131
cpl_image * hdrl_image_get_image(hdrl_image *himg)
get data as cpl image
Definition: hdrl_image.c:105
cpl_size hdrl_imagelist_get_size(const hdrl_imagelist *himlist)
Get the number of images in the imagelist.
hdrl_image * hdrl_imagelist_get(const hdrl_imagelist *himlist, cpl_size inum)
Get an image from a list of images.
double sc_basic_mjd2fracyear(double mjd)
Definition: sc_basic.c:179