ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/JSOC/proj/util/apps/render_image.c
Revision: 1.20
Committed: Thu Jul 16 20:15:20 2015 UTC (8 years, 2 months ago) by phil
Content type: text/plain
Branch: MAIN
CVS Tags: Ver_8-10
Changes since 1.19: +6 -1 lines
Log Message:
Allow T_REC if T_OBS not present.  Old change just now checked in.

File Contents

# Content
1 /*
2 * render_image - make .png or .ppm image from a segment image file in a series.
3 */
4
5 /**
6 @defgroup render_image render_image Make .png or .ppm image from a segment image file in a series.
7 @ingroup su_util
8
9 @brief Make .png or .ppm image from a segment image file in a series.
10
11 @par Synopsis:
12 @code
13 render_image --help
14 render_iamge in=<RecordSet> out=<target> | {outname=<ident_for_filename>} {scaling=<scaletype>}
15 {palette=<color_table>} {type=<image_protocol>} {outid=<filename_code>}
16 {tkey=<time_keyword>} {min=<scale_min>} {max=<scale_max>}
17 {scale=<image_size_ratio>} {-c} {-u} {-w} {-x}
18 @endcode
19
20 @code
21 in=Input data series.
22 n=limit limit number of records to n from beginning or -n from end of 'in'
23 out=Output data series or full path of directory or pipe via ppm program.
24 outname=Output quantity name for filenames.
25 scaling=Color table type, minmax, minmax99, minmaxgiven, histeq, ...,, default=MINMAX
26 palette=Color table, GREY or filename, default=GREY (bad spelling as pallette still allowed)
27 type=Image protocol, png, ppm, ..., default=png
28 tkey=keyword name for time, default T_REC
29 outid=output identifier, #=record counter, time=time as yyyymmdd_hhmmss, default=#
30 min=Min value for scaling, default nan
31 max=Max value for scaling, default nan
32 scale"Reduction factor list, default 1.
33 -c Crop flag, causes crop to RSUN_OBS
34 -u use unchanged for rotation and centering
35 -w use white for missing pixels, instead of black
36 -x High-quality flag, sets bytespercolor to 2 instead of 1,
37 @endcode
38
39 @par
40 Render_image can make one or many images per record in the input recordset. It will make one image for each value
41 of scale where the values are given as a comma delimited list of divisors for the dimensions of the input image array.
42 The image file will be written to a filename created from outid_outname_type where outid is either "time" or "#",
43 outname is any literal string, and type is "png" or "ppm" or if out specifies a pipe, then type may be any file type
44 that is supported as the output of a pipeline which accepts a PPM file.
45 @par
46 The file location is spcified with the out parameter. The target may be a seriesname in which case the image will
47 be written into a record directory of that series. If the target seriesname is the same as the series specified in in, then
48 each input record will be cloned. The image will be written into a segment named "image".
49 @par
50 If the target location begins with a "/" or "./" then the $target will be used as the path for a directory into which
51 the image file will be written. If the target location beginw with a "|" character, then the target string will be used
52 as a pipe into which a .ppm file will be written. The pipe string will have " > <filename>" appended to it.
53 In the case of a pipe, the string given in the 'out' parameter will be scanned
54 for replacement tokens delimited by '{...}'. Inside the '{}' pair the following
55 replacement instructions are available:
56 @code
57 The word 'ID' is replaced by the filename ID as built from outid.
58 The word '%' is replaced by that percent of the height of the image rounded
59 to the nearest pixel. If the floating point number after the '%' is itself
60 followed by a ':' and an integer, then that integer will be used as the
61 smallest number for the replacement token. So, for instance '{%1.2:5}'
62 with the image height of 256 pixels will be processed as: 1.2% of 256 is 3.072
63 which rounds to 3 which is smaller than 5 so the result will be 5.
64 @endcode
65 @par
66 The image filename is generated from the outid, outname, and type parameters. If outid is the string "time" then
67 the time of the image as specified by the tkey (defaults to T_REC) will be used with the "." and ":" and "_<zone>" componenets deleted. I.e.
68 the time will be in the form "yyyymmdd_hhmmss". The outid = time can have a suffix specifying the number of time characters to
69 include. the format of the suffix is ":nn" where nn is from 1 to 15. If outid is a literal "#" then the "%04d" layout will be used to generate
70 the image sequence number within the run of the program (i.e. the record number within the current recordset). the
71 type parameter must be "png" or "ppm" unless the out parameter specifies a ppm pipeline in which case type should
72 be the suffix for the file type geenrated by the pipe.
73 @par
74 The scaling of image values to color table indices is controlled by the scaling parameter. scaling must be one of
75 MINMAX, MAXMIN, MINMAXGIVEN, MINMAX99, MAXMIN99, HISTEQ, MAG at present. The default is MINMAX. If the scaling is
76 MINMAX and both min and max parameters are given, then the MINMAXGIVEN scaling will be used. MINMAX99 and
77 MAXMIN99 mean to eliminate the top and bottom 0.5% of the histogram of the values before computing a linear scaling.
78 MAXMIN and MAXMIN99 result in a reversed scaling with the max value mapped to the first color in the table and the
79 min value mapped to the max color in the table. String case is ignored for the scaling parameter.
80 @par
81 The MAG scaling uses a scaling of 400*M/pow((abs(M)+bias),0.75) bias=30, from user provided min and max (defaulting to +-1500).
82 This scaling emphasizes low absolute field strength but still shows detail up to a max of 3000 gauss. The default
83 of 1500 clips umbral fields with the tradeoff of more contrast for plage and network fields.
84 @par
85 The color table is specified by the palette parameter. The table may be a built in table, at persent the only
86 build-in is "grey". ("grey", "gray", "GREY", "GRAY" are all allowed). If not a build-in table name the palette
87 parameter is expected to be a file path to a table of .sao or .lut types. These table formats are described in the
88 ds9(1) documentation.
89 @par
90 The image will be centered to the nearest pixel, and will be rotated 180 degrees if CROTA2 is near 180.
91
92 @par Flags:
93 @c -c: Crop image. This flag will casue the image to be cropped at pixel radius RSUN_OBS/CDELT1 about CRPIX1,CRPIX2 center.
94
95 @c -w: White background, the -w flag will cause missing values to be white. Else black.
96
97 @c -x: the -x flag is used to indicate extra resolution in the color table, i.e. 16-bits per color.
98
99 @par Example to generate a directory full of simple grey scaled png images, perhaps for a movie
100 @code
101 render_image in='hmi.M_45s_nrt[2010.06.01_TAI/5d@30m]' out=./movie scaling=minmax99 outid=# outname=M
102 @endcode
103
104 @par Example to generate a set of 4096x4096 and 1024x1024, and 256x256 pixel images of magnetograms in the current directory
105 using a color table the adds dynamic range to small absolute values. The times will omit the seconds field in this example.
106 @code
107 render_image in='hmi.M_45s_nrt'$QRY \
108 outname=M \
109 palette=/home/phil/apps/mag.lut \
110 outid=time:13 \
111 -c \
112 min=-500 \
113 max=500 \
114 type=jpg \
115 -w \
116 scale=1,4,16 \
117 out='| ppmlabel -color black -size {%0.75:5} -x 15 -y {%98} -text "SDO/HMI Quick-Look Magnetogram: {ID}" | pnmtojpeg -quality=95'
118 @endcode
119
120 @bug
121 Using the MAG scaling with a list of scale sizes, any instance of scale=1 must appear last in the list. This is because the values of the
122 image data are replaced in the original array in the scale=1 case. This will affect any scaling type that modifies the values of the data.
123
124 @bug
125 Does not make 16-bit colors correctly, or I can not get ppm tools with correct flags...
126
127 */
128
129 #include "mypng.h"
130 #include "jsoc_main.h"
131
132 #define PNGDIE(msg) {fprintf(stderr,"%s\n",msg); \
133 if (png_ptr) png_destroy_write_struct(&png_ptr, &info_ptr); \
134 if (row_pointers) free(row_pointers); \
135 if (fp) fclose(fp); \
136 return(1);}
137
138 #define DIE(msg) {fflush(stdout);fprintf(stderr,"%s, status=%d\n",msg,status); return(status);}
139
140 #define MINMAX 1
141 #define MAXMIN 2
142 #define HISTEQ 3
143 #define MINMAX99 4
144 #define MAXMIN99 5
145 #define MINMAXGIVEN 6
146 #define MAG 7
147 #define MAGMINMAXGIVEN 8
148 #define LOG 9
149 #define SQRT 10
150
151 char *module_name = "render_image";
152
153 ModuleArgs_t module_args[] =
154 {
155 {ARG_STRING, "in", "NOT SPECIFIED", "Input data series."},
156 {ARG_INT, "n", "0", "Limit of number of records, 0 for no limin; >0 count from start; <0 count from end"},
157 {ARG_STRING, "out", "NOT SPECIFIED", "Output data series or full path of directory or pipe via ppm program."},
158 {ARG_STRING, "outname", "NOT SPECIFIED", "Output quantity name for filenames"},
159 {ARG_STRING, "scaling", "MINMAX", "Color table type, minmax, minmax99, minmaxgiven, histeq, ..."},
160 {ARG_STRING, "pallette", "none", "Color table, GREY or filename"},
161 {ARG_STRING, "palette", "GREY", "Color table, GREY or filename"},
162 {ARG_STRING, "type", "png", "Image protocol, png, ppm, ..."},
163 {ARG_STRING, "outid", "#", "output identifier, #=record counter, time=time as yyyymmdd_hhmmss"},
164 {ARG_STRING, "tkey", "T_REC", "Time keyword name used if outid==time, defaults to T_REC"},
165 {ARG_FLOAT, "min", "DRMS_MISSING_FLOAT", "Min value for scaling"},
166 {ARG_FLOAT, "max", "DRMS_MISSING_FLOAT", "Max value for scaling"},
167 {ARG_FLOAT, "bias", "30.0", "Max value for mag scaling"},
168 {ARG_INTS, "scale", "1", "Reduction factors"},
169 {ARG_FLAG, "c", "0", "Crop flag, causes crop to RSUN_OBS"},
170 {ARG_FLAG, "u", "0", "use unchanged, is-is for rotation and centering"},
171 {ARG_FLAG, "w", "0", "use white for missing pixels, instead of black"},
172 {ARG_FLAG, "x", "0", "High-quality flag, sets bytespercolor to 2 instead of 1"},
173 {ARG_END}
174 };
175
176 #define Deg2Rad (M_PI/180.0)
177 #define Rad2arcsec (3600.0/Deg2Rad)
178 #define arcsec2Rad (Deg2Rad/3600.0)
179 #define Rad2Deg (180.0/M_PI)
180
181 struct ObsInfo_struct
182 {
183 // from observation info
184 TIME t_obs;
185 double rsun_obs, obs_vr, obs_vw, obs_vn;
186 double crpix1, crpix2, cdelt1, cdelt2, crota2;
187 double crval1, crval2;
188 double cosa, sina;
189 double obs_b0;
190 // observed point
191 int i,j;
192 // parameters for observed point
193 double x,y,r;
194 double rho;
195 double lon;
196 double lat;
197 double sinlat, coslat;
198 double sig;
199 double mu;
200 double chi;
201 double obs_v;
202 };
203
204 typedef struct ObsInfo_struct ObsInfo_t;
205
206 void rebinArraySF(DRMS_Array_t *out, DRMS_Array_t *in);
207
208 int add_png(char *imgPath, DRMS_Array_t *imgArray, int scaletype, int usewhite, char *palette, int colors, int bytespercolor, double *minp, double *maxp);
209
210 int add_ppm(char *imgPath, DRMS_Array_t *imgArray, int scaletype, int usewhite, char *palette, int colors, int bytespercolor, double *minp, double *maxp);
211
212 int make_png(char *imgPath, unsigned char *data, int height, int width, char *palette, int bytepercolor, int colors);
213
214 char *set_scaling(DRMS_Array_t *in, double *minp, double *maxp, int *nmissp, char *palette, int colors, int bytepercolor, int scaling, int usewhite);
215
216 int upNcenter(DRMS_Array_t *arr, ObsInfo_t *ObsLoc);
217 int crop_image(DRMS_Array_t *arr, ObsInfo_t *ObsLoc);
218
219 ObsInfo_t *GetObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus);
220
221 #define PNG 1
222 #define PPM 2
223 #define PPMPIPE 3
224
225 int DoIt(void)
226 {
227 int status = DRMS_SUCCESS;
228 DRMS_RecordSet_t *inRS, *outRS = NULL;
229 DRMS_Record_t *inRec, *outRec;
230 int irec, nrecs;
231 int fileonly=0;
232 int imgtype;
233 int quality;
234
235 char *inQuery = (char *)params_get_str(&cmdparams, "in");
236 char *outQuery = (char *)params_get_str(&cmdparams, "out");
237 char *outName = (char *)params_get_str(&cmdparams, "outname");
238 char *outid = (char *)params_get_str(&cmdparams, "outid");
239 char *tkey = (char *)params_get_str(&cmdparams, "tkey");
240 char *palette = (char *)params_get_str(&cmdparams, "palette");
241 if (strcmp((char *)params_get_str(&cmdparams, "pallette"), "none"))
242 palette = (char *)params_get_str(&cmdparams, "pallette");
243 int limit = params_get_int(&cmdparams, "n");
244 int bytespercolor = (params_isflagset(&cmdparams, "x") ? 2 : 1);
245 int missingwhite = params_isflagset(&cmdparams, "w");
246 int crop = params_isflagset(&cmdparams, "c");
247 int asis = params_isflagset(&cmdparams, "u");
248 char *type = (char *)params_get_str(&cmdparams, "type");
249 char *scaling = (char *)params_get_str(&cmdparams, "scaling");
250 double called_min = params_get_double(&cmdparams, "min");
251 double called_max = params_get_double(&cmdparams, "max");
252 double min, max;
253 int *scales = NULL;
254 int iscale, nscales;
255 int colors;
256 int scaletype;
257 int srcNx = 0, srcNy = 0;
258
259 if (strcasecmp(palette, "grey") == 0 || strcasecmp(palette, "gray") == 0)
260 {
261 palette = "GREY";
262 colors = 1;
263 }
264 else
265 colors = 3;
266
267 if (strcasecmp(type, "png")==0) imgtype = PNG;
268 else if (strcasecmp(type, "ppm")==0) imgtype = PPM;
269 else imgtype = PPMPIPE;
270
271 nscales = cmdparams_get_intarr(&cmdparams, "scale", &scales, &status);
272 if (status)
273 DIE("scale parameter not found");
274
275 // for (iscale=0; iscale<nscales; iscale++)
276 // fprintf(stderr,"Scale #%d=%d ", iscale, scales[iscale]);
277 // fprintf(stderr," using min=%f, max=%f\n", min, max);
278
279 if (strcasecmp(scaling, "minmax") == 0) scaletype = MINMAX;
280 else if (strcasecmp(scaling, "maxmin") == 0) scaletype = MAXMIN;
281 else if (strcasecmp(scaling, "minmax") == 0) scaletype = MINMAX99;
282 else if (strcasecmp(scaling, "maxmin99") == 0) scaletype = MAXMIN99;
283 else if (strcasecmp(scaling, "histeq") == 0) scaletype = HISTEQ;
284 else if (strcasecmp(scaling, "minmaxgiven") == 0) scaletype = MINMAXGIVEN;
285 else if (strncasecmp(scaling, "mag", 3) == 0) scaletype = MAG;
286 else if (strncasecmp(scaling, "log", 3) == 0) scaletype = LOG;
287 else if (strncasecmp(scaling, "sqrt", 4) == 0) scaletype = SQRT;
288
289 else scaletype = 0;
290 fprintf(stdout, " using scaling %d\n", scaletype);
291 if (isnan(called_min) || isnan(called_max))
292 {
293 if (scaletype == MINMAXGIVEN)
294 DIE("min and max must be specified for type MINMAXGIVEN\n");
295 }
296 else
297 {
298 if (scaletype == MINMAX)
299 scaletype = MINMAXGIVEN;
300 else if (scaletype == MAG)
301 scaletype = MAGMINMAXGIVEN;
302 }
303
304 if (limit == 0)
305 inRS = drms_open_records(drms_env, inQuery, &status);
306 else
307 inRS = drms_open_nrecords(drms_env, inQuery, limit, &status);
308 if (status || inRS->n == 0)
309 DIE("No input data found");
310 drms_stage_records(inRS, 1, 1);
311 nrecs = inRS->n;
312 fprintf(stderr,"nrecs=%d\n",nrecs);
313
314 if (!outQuery[0])
315 DIE("No valid output place specified");
316 if (outQuery[0] == '/' || (outQuery[0] == '.' && outQuery[1] && outQuery[1] == '/') || outQuery[0] == '|')
317 {
318 // Output to a directory
319 fileonly = 1;
320 }
321 else
322 {
323 // output to a record segment
324 fileonly = 0;
325 if (strcmp(inRS->records[0]->seriesinfo->seriesname, outQuery) == 0)
326 outRS = drms_clone_records(inRS, DRMS_PERMANENT, DRMS_SHARE_SEGMENTS, &status);
327 else
328 outRS = drms_create_records(drms_env, nrecs, outQuery, DRMS_PERMANENT, &status);
329 if (status)
330 DIE("Output recordset not created");
331 }
332
333 for (irec=0; irec<nrecs; irec++)
334 {
335 int status;
336 DRMS_Array_t *srcArray;
337 DRMS_Segment_t *srcSeg;
338 ObsInfo_t *ObsLoc;
339
340 char imageID[100];
341 inRec = inRS->records[irec];
342 quality = drms_getkey_int(inRec, "QUALITY", &status);
343 if (!status && quality < 0)
344 {
345 fprintf(stderr,"Quality bad for rec %d\n", irec);
346 continue;
347 }
348 else
349 {
350 srcSeg = drms_segment_lookupnum(inRec, 0);
351 srcArray = drms_segment_read(srcSeg, DRMS_TYPE_FLOAT, &status);
352 if (!srcArray || status)
353 {
354 fprintf(stderr," No data file found rec %d, status=%d\n", irec,status);
355 if (srcArray)
356 {
357 drms_free_array(srcArray);
358 srcArray = NULL;
359 }
360 continue;
361 }
362 ObsLoc = GetObsInfo(srcSeg, NULL, &status);
363 if (!asis) upNcenter(srcArray, ObsLoc);
364 if (crop) crop_image(srcArray, ObsLoc);
365 srcNx = srcArray->axis[0];
366 srcNy = srcArray->axis[1];
367 }
368 if (strcmp(outid,"#") == 0)
369 sprintf(imageID, "%04d", irec);
370 else if (strncasecmp(outid,"time",4) == 0)
371 {
372 int timestatus;
373 char timebuf[100];
374 static int t[] = {0,1,2,3,5,6,8,9,10,11,12,14,15,17,18};
375 int i;
376 int maxtplace = 15;
377 char *tplace = index(outid,':');
378 TIME now = drms_getkey_time(inRec, tkey, &timestatus);
379 if (timestatus)
380 {
381 fprintf(stderr,"Time key given in 'tkey' param, %s, is not found in the input series.\n",tkey);
382 return(timestatus);
383 }
384 sprint_time(timebuf, now, "TAI", 0);
385 if (tplace)
386 sscanf(tplace+1,"%d",&maxtplace);
387 for (i=0; i<maxtplace; i++)
388 imageID[i] = timebuf[t[i]];
389 imageID[i] = '\0';
390 }
391 else
392 {
393 fprintf(stderr, "Invalid outid param: |%s|\n", outid);
394 return(1);
395 }
396 for (iscale=0; iscale<nscales; iscale++)
397 {
398 int dimxy;
399 char dimxytxt[50];
400 DRMS_Array_t *imageArray = NULL;
401 char imgPath[DRMS_MAXPATHLEN];
402 int scale = scales[iscale];
403 int imageDims[2];
404 int tmparray = 0;
405 char imageFileName[DRMS_MAXSEGFILENAME];
406 min = called_min;
407 max = called_max;
408 imageDims[0] = srcNx/scale;
409 imageDims[1] = srcNy/scale;
410 dimxy = imageDims[0];
411 sprintf(dimxytxt,"%d",dimxy);
412 // fprintf(stderr,"scale=%d, imageNy=%d, imageNx=%d\n", scale, imageDims[1], imageDims[0]);
413
414 if (scale != 1)
415 {
416 imageArray = drms_array_create(DRMS_TYPE_FLOAT, 2, imageDims, NULL, &status);
417 rebinArraySF(imageArray, srcArray);
418 tmparray = 1;
419 }
420 else
421 {
422 imageArray = srcArray;
423 tmparray = 0;
424 }
425
426 sprintf(imageFileName, "%s%s%s_%s.%s", imageID, (strcmp(outid,"#")==0 ? "." : "_"), outName,
427 (dimxy == 4096 ? "4k" :
428 (dimxy == 2048 ? "2k" :
429 (dimxy == 1024 ? "1k" :
430 (dimxy == 512 ? "512" :
431 (dimxy == 256 ? "256" :
432 (dimxy == 128 ? "128" : dimxytxt)))))), type);
433
434 if (fileonly)
435 {
436 if (outQuery[0] == '|')
437 { // process outQuery for '{...}' replacement tokens
438 char buf[4096];
439 char *pbuf = buf, *c = outQuery;
440 while (*c && (pbuf-buf) < sizeof(buf))
441 {
442 if (*c == '{')
443 {
444 c++;
445 if (*c == '%')
446 {
447 float x;
448 int ix, minx;
449 c++;
450 x = strtof(c, &c);
451 ix = round(0.01*x*imageDims[1]);
452 if (*c == ':')
453 {
454 c++;
455 minx = (int)strtol(c, &c, 10);
456 }
457 else minx = 0;
458 pbuf += sprintf(pbuf,"%d",(ix < minx ? minx : ix));
459 }
460 else if (strncmp(c, "ID", 2) == 0)
461 {
462 pbuf += sprintf(pbuf, "%s", imageID);
463 c += 2;
464 }
465 if (*c++ != '}')
466 DIE("Unrecognized token in pipe string");
467 }
468 else
469 *pbuf++ = *c++;
470 }
471 *pbuf = '\0';
472 sprintf(imgPath, "%s > %s", buf, imageFileName);
473 }
474 else
475 sprintf(imgPath, "%s/%s", outQuery, imageFileName);
476 }
477 else
478 {
479 DRMS_Segment_t *imageSeg;
480 char recdir[DRMS_MAXPATHLEN];
481 outRec = outRS->records[irec];
482 drms_copykeys(outRec, inRec, 0, kDRMS_KeyClass_Explicit);
483 drms_record_directory(outRec, recdir, 0);
484 imageSeg = drms_segment_lookup(outRec, "image");
485 strcpy(imageSeg->filename, imageFileName);
486 drms_keyword_setdate(outRec);
487 sprintf(imgPath, "%s/%s", recdir, imageFileName);
488 }
489
490 if (imgtype == PNG)
491 add_png(imgPath, imageArray, scaletype, missingwhite, palette, colors, bytespercolor, &min, &max);
492 else if (imgtype == PPM || imgtype == PPMPIPE)
493 add_ppm(imgPath, imageArray, scaletype, missingwhite, palette, colors, bytespercolor, &min, &max);
494
495 if (tmparray)
496 drms_free_array(imageArray);
497 }
498 drms_free_array(srcArray);
499 }
500 fprintf(stderr,"render_image done, irec=%d\n",irec);
501 drms_close_records(inRS, DRMS_FREE_RECORD);
502 fprintf(stderr,"render_image close in records done\n");
503 if (!fileonly && outRS)
504 drms_close_records(outRS, DRMS_INSERT_RECORD);
505 fprintf(stderr,"leaving module\n");
506 return (DRMS_SUCCESS);
507 } // end of DoIt
508
509 /* ----------------------------------------------------------------------
510 functions
511 ------------------------------------------------------------
512 */
513
514 int add_ppm(char *imgPath, DRMS_Array_t *imageArray, int scaletype, int usewhite, char *palette, int colors, int bytespercolor, double *minp, double *maxp)
515 {
516 int i, Nx, Ny;
517 unsigned char *imgData = NULL;
518 int nmissing;
519 int maxpixval = (bytespercolor == 1 ? 255 : 65535);
520 FILE *out;
521 char *ppmcode;
522
523 imgData = (unsigned char*)set_scaling(imageArray, minp, maxp, &nmissing, palette, colors, bytespercolor, scaletype, usewhite);
524 if (!imgData)
525 {
526 fprintf(stderr,"scaling failed\n");
527 return(1);
528 }
529
530 ppmcode = (colors == 1 ? "P5" : "P6");
531
532 Nx = imageArray->axis[0];
533 Ny = imageArray->axis[1];
534
535 if (imgPath[0] == '|')
536 out = popen(imgPath+1, "w");
537 else
538 out = fopen(imgPath, "w");
539
540 fprintf(out, "%s\n%d %d %d\n", ppmcode, Nx, Ny, maxpixval);
541 for (i=Ny-1; i >=0; i--)
542 fwrite(imgData + i*Nx*bytespercolor*colors, bytespercolor, colors*Nx, out);
543 if (imgPath[0] == '|')
544 pclose(out);
545 else
546 fclose(out);
547
548 free(imgData);
549 return(0);
550 }
551
552 int add_png(char *imgPath, DRMS_Array_t *imageArray, int scaletype, int usewhite, char *palette, int colors, int bytespercolor, double *minp, double *maxp)
553 {
554 int srcNx, srcNy;
555 int status=0;
556 unsigned char *imgData = NULL;
557 int nmissing;
558
559 imgData = (unsigned char*)set_scaling(imageArray, minp, maxp, &nmissing, palette, colors, bytespercolor, scaletype, usewhite);
560
561 if (!imgData)
562 {
563 fprintf(stderr,"scaling failed, status=%d\n",status);
564 return(status);
565 }
566
567 // should add text info to .png file with min, max, nmissing, etc.
568
569 if (make_png(imgPath, imgData, imageArray->axis[0], imageArray->axis[1], palette, bytespercolor, colors) != 0)
570 {
571 fprintf(stderr,"png failed, status=%d\n",status);
572 free(imgData);
573 return(status);
574 }
575 free(imgData);
576 return(0);
577 }
578 // ----------------------------------------------------------------------
579
580 void rebinArraySF(DRMS_Array_t *out, DRMS_Array_t *in)
581 {
582 float *inData = in->data;
583 float *outData = out->data;
584 float *inp, *outp;
585 int inNx=in->axis[0], inNy=in->axis[1];
586 int outNx=out->axis[0], outNy=out->axis[1];
587 int inI, inJ, outI, outJ, i;
588 int binsize = inNx/outNx;
589 double inRow[inNx], outRow[outNx];
590 int inRowN[inNx], outRowN[outNx];
591
592 for (outJ=0; outJ < outNy; outJ++)
593 {
594 int inRow0 = outJ * binsize;
595 for (outI=0; outI<outNx; outI++)
596 {
597 outRowN[outI] = 0;
598 outRow[outI] = 0.0;
599 }
600 for (inJ = inRow0; inJ < inRow0+binsize; inJ++)
601 {
602 inp = inData + inJ*inNx;
603 for (outI=0; outI<outNx; outI++)
604 for (i=0; i<binsize; i++, inp++)
605 if (*inp != DRMS_MISSING_FLOAT)
606 {
607 outRow[outI] += *inp;
608 outRowN[outI]++;
609 }
610 }
611 for (outI=0; outI < outNx; outI++)
612 outData[outJ*outNx + outI] = (outRowN[outI] ?
613 (outRow[outI]/outRowN[outI]) : DRMS_MISSING_FLOAT);
614 } // end outJ
615 }
616
617 // ---------------------------------------------------------------------
618
619 unsigned short *init_color_table(char * palette, int colors, int bytepercolor)
620 {
621 int c, i;
622 int tablesize = (bytepercolor == 1 ? 256 : 65536);
623 int maxcolor = tablesize - 2; // i.e. for 8-bit, max=254, leave top for missing
624 int lastcolor = tablesize - 1;
625 int mincolor = 1;
626 unsigned short *table = (unsigned short *)malloc(colors*tablesize*sizeof(unsigned short));
627
628 if (!table) return NULL;
629 if (strcmp(palette,"GREY") == 0)
630 for (i=0; i<tablesize; i++)
631 for (c = 0; c<colors; c++)
632 table[i*colors + c] = i;
633
634 else if (colors == 3 && access(palette, R_OK) == 0)
635 {
636 FILE *ct = fopen(palette,"r");
637 void proc_color(FILE *ct, unsigned short *t, int max);
638 char buf[256];
639 char *dot = rindex(palette, '.');
640 if (dot && strcmp(dot, ".lut")==0)
641 {
642 float fr, fg, fb;
643 table[0] = table[1] = table[2] = 0;
644 table[colors*lastcolor] = table[colors*lastcolor+1] = table[colors*lastcolor+2] = lastcolor;
645 for (i=1; i<lastcolor; i++)
646 {
647 fscanf(ct, "%f%f%f", &fr, &fg, &fb);
648 c = 0;
649 table[i*colors + c++] = maxcolor*fr;
650 table[i*colors + c++] = maxcolor*fg;
651 table[i*colors + c++] = maxcolor*fb;
652 }
653 }
654 else
655 {
656 // assume .sao type color table
657 unsigned short r[tablesize], g[tablesize], b[tablesize];
658 for (i=0; i<=tablesize; i++)
659 r[i] = g[i] = b[i] = 0;
660 while (fgets(buf,256,ct))
661 {
662 if (strncmp(buf,"RED:",4)==0)
663 proc_color(ct,r,maxcolor);
664 if (strncmp(buf,"GREEN:",6)==0)
665 proc_color(ct,g,maxcolor);
666 if (strncmp(buf,"BLUE:",5)==0)
667 proc_color(ct,b,maxcolor);
668 }
669 table[0] = table[1] = table[2] = 0;
670 table[colors*lastcolor] = table[colors*lastcolor+1] = table[colors*lastcolor+2] = lastcolor;
671 for (i=1; i<lastcolor; i++)
672 {
673 c = 0;
674 table[i*colors + c++] = r[i];
675 table[i*colors + c++] = g[i];
676 table[i*colors + c++] = b[i];
677 }
678 }
679 fclose(ct);
680 }
681 return(table);
682 }
683
684 void proc_color(FILE *ct, unsigned short *t, int max)
685 {
686 float cut, val;
687 int pos = -1;
688 double prev = 0.0;
689 int prevloc = 0;
690 double slope;
691 pos = -1;
692 prev = 0.0;
693 prevloc = 0;
694 while (fscanf(ct,"(%f,%f)", &cut, &val) == 2)
695 {
696 int loc = max*cut + 0.5;
697 val *= max;
698 // fprintf(stderr," got loc=%d, cut=%f %f\n",loc,cut,val);
699 if (pos < 0)
700 {
701 pos = 0;
702 prev = val;
703 prevloc = 0;
704 }
705 if (loc == prevloc)
706 slope = 0.0;
707 else
708 slope = (val - prev)/(loc - prevloc);
709 while (pos <= loc)
710 {
711 t[pos] = slope * (pos-prevloc) + prev;
712 pos++;
713 }
714 prev = t[loc];
715 prevloc = loc;
716 }
717 while (pos <= max)
718 t[pos++] = prev;
719 }
720
721
722 char *set_scaling(DRMS_Array_t *in, double *minp, double *maxp,
723 int *nmissp, char *palette, int colors, int bytepercolor, int scaling, int usewhite)
724 {
725 int idata, ndata, nmiss = 0;
726 float *inData = in->data;
727 ndata = in->axis[0] * in->axis[1];
728 int tablesize = (bytepercolor == 1 ? 256 : 65536);
729 int color;
730 int grey = strcmp(palette, "GREY") == 0;
731 static unsigned short *table = NULL;
732 static unsigned short *greytable = NULL;
733 static unsigned short *colortable = NULL;
734 char *out;
735 out = (char *)malloc(ndata * colors * bytepercolor * sizeof(char));
736 int maxcolor = (bytepercolor == 1 ? 254 : 65534);
737 int missingcolor = (usewhite ? maxcolor + 1 : 0);
738
739 fprintf(stderr,"set_scaling called, scaling=%d, palette=%s, min=%f, max=%f",scaling,palette,*minp,*maxp);
740 fprintf(stderr,"\n missingcolor %d maxcolor %d \n", missingcolor, maxcolor);
741 if (!out)
742 return(NULL);
743
744 // get color table unless palette is GREY and scaling is MINMAX, do grey inline for that case only
745 // since that combination is used to call set_scaling recursively for histogram generation
746 if (grey && scaling == MINMAX && colors == 1)
747 {
748 if (!greytable)
749 {
750 int i;
751 greytable = (unsigned short *)malloc(65536 * sizeof(unsigned short));
752 for (i=0; i<65536; i++)
753 greytable[i] = i;
754 colortable = greytable;
755 }
756 }
757 else
758 {
759 if (!table)
760 table = init_color_table(palette, colors, bytepercolor);
761 colortable = table;
762 }
763
764 switch(scaling)
765 {
766 case MAG:
767 case MAGMINMAXGIVEN:
768 {
769 float bias = params_get_double(&cmdparams, "bias");
770 for (idata=0; idata<ndata; idata++)
771 {
772 float val = inData[idata];
773 if (!isnan(val))
774 // inData[idata] = val/sqrt(fabs(val)+bias);
775 inData[idata] = 400.0*val/pow((fabs(val)+bias), 0.75);
776 }
777 if (isnan(*minp)) *minp = -1500.0;
778 if (isnan(*maxp)) *maxp = -*minp;
779 // no break;
780 }
781 case MINMAX:
782 case MAXMIN:
783 case MINMAXGIVEN:
784 case LOG:
785 case SQRT:
786 {
787 double min;
788 double max;
789 if (bytepercolor != 1 && bytepercolor != 2)
790 {fprintf(stderr,"wrong bytes per color\n"); return(NULL);}
791 if (scaling == MINMAXGIVEN || scaling == MAG || scaling == MAGMINMAXGIVEN)
792 {
793 min = *minp;
794 max = *maxp;
795 /* fprintf(stderr,"debug pt 1: min=%f max=%f\n scaling=%d",min,max,scaling ); */
796 }
797 else
798 {
799 if (isnan(*minp) || isnan(*maxp))
800 {
801 int n=0;
802 min = 1.0e20;
803 max = -min;
804 /* fprintf(stderr,"debug pt 2: min=%f max=%f\n",min,max); */
805 for (idata=0; idata<ndata; idata++)
806 {
807 float val = inData[idata];
808 if (!isnan(val))
809 {
810 n++;
811 if (val > max) max = val;
812 if (val < min) min = val;
813 }
814 }
815 if (n==0)
816 {
817 fprintf(stderr,"set_scaling, min or max is missing and no data values found.\n");
818 return(NULL);
819 }
820 }
821 if (isnan(*minp)) *minp = min;
822 if (isnan(*maxp)) *maxp = max;
823 min = *minp;
824 max = *maxp;
825 }
826 double use_min, use_max;
827 if (scaling == LOG)
828 {
829 if (min <= 0)
830 min = 1.0e-10;
831 if (max <= 0)
832 max = 1.0e-10;
833 use_min = log10(min);
834 use_max = log10(max);
835 }
836 else if (scaling == SQRT)
837 {
838 if (min < 0)
839 min = 0;
840 if (max < 0)
841 max = 0;
842 use_min = sqrt(min);
843 use_max = sqrt(max);
844 }
845 for (idata=0; idata<ndata; idata++)
846 {
847 /* fprintf(stderr,"debug pt 4: min=%f max=%f\n",min,max); */
848 float val = inData[idata];
849 int newval;
850 if (!isnan(val))
851 {
852 if (val <= min) val = min;
853 if (val >= max) val = max;
854 if (scaling == LOG)
855 newval = (maxcolor-1)*((log10(val)-use_min)/(use_max-use_min)) + 0.5;
856 else if (scaling == SQRT )
857 newval = (maxcolor-1)*((sqrt(val)-use_min)/(use_max-use_min)) + 0.5;
858 else
859 {
860 newval = (maxcolor-1)*(val-min)/(max-min) + 0.5;
861 if (scaling == MAXMIN)
862 newval = maxcolor - newval;
863 }
864 if (newval >= maxcolor) newval = maxcolor;
865 if (newval < 1) newval = 1;
866 }
867 else
868 {
869 newval = missingcolor;
870 nmiss += 1;
871 }
872 for (color=0; color<colors; color++)
873 {
874 if (bytepercolor == 1)
875 *((unsigned char *)out + colors*idata + color) = colortable[newval*colors + color];
876 else
877 *((unsigned short *)out + colors*idata + color) = colortable[newval*colors + color];
878 }
879 }
880 *maxp = max;
881 *minp = min;
882 *nmissp = nmiss;
883 fprintf(stderr," min %f max %f nmissp %d ndata %d\n", *minp, *maxp, nmiss, ndata);
884 break;
885 }
886
887 case HISTEQ:
888 {
889 int ihist, nhist=65536;
890 int *hist = (int *)malloc(nhist * sizeof(int));
891 int icolor, ncolor = (bytepercolor == 1 ? 255 : 65535);
892 int total, want;
893 unsigned int newval;
894 double valspercolor;
895 char *outa;
896 outa = set_scaling(in, minp, maxp, nmissp, "GREY", 1, 2, MINMAX, 0);
897 valspercolor = (ndata - *nmissp)/(double)ncolor;
898 for (ihist = 0; ihist < nhist; ihist++)
899 hist[ihist] = 0;
900 for (idata=0; idata<ndata; idata++)
901 hist[*((unsigned short *)outa+idata)] += 1;
902 for (icolor=0, total=0, ihist=0; icolor<=ncolor; icolor++)
903 {
904 want = icolor * valspercolor;
905 for ( ; ihist<nhist; ihist++)
906 {
907 if (total >= want)
908 break;
909 total += hist[ihist];
910 hist[ihist] = icolor;
911 }
912 }
913 for ( ; ihist<nhist; ihist++)
914 hist[ihist] = ncolor;
915 for (idata=0; idata<ndata; idata++)
916 {
917 if (!isnan(inData[idata]))
918 {
919 newval = hist[*((unsigned short *)outa+idata)];
920 if (newval > maxcolor) newval = maxcolor;
921 if (newval < 1) newval = 1;
922 }
923 else
924 newval = missingcolor;
925 for (color=0; color<colors; color++)
926 {
927 if (bytepercolor == 1)
928 *((unsigned char *)out + colors*idata + color) = colortable[newval*colors + color];
929 else
930 *((unsigned short *)out + colors*idata + color) = colortable[newval*colors + color];
931 }
932 }
933 // fprintf(stderr," HISTEQ scaling %d \n", scaling);
934 free(outa);
935 free(hist);
936 break;
937 }
938
939 case MINMAX99:
940 case MAXMIN99:
941 {
942 fprintf(stderr,"DOING set_scaling case MAXMIN99 \n");
943 int ihist, nhist=65536;
944 int *hist = (int *)malloc(nhist * sizeof(int));
945 int icolor, ncolor = (bytepercolor == 1 ? 255 : 65535);
946 int total, want;
947 unsigned int newval;
948 double newmin, newmax;
949 char *outa;
950 fprintf(stderr," CALLING set_scaling case MINMAX %d \n", MINMAX);
951 *minp = 0;
952 *maxp = 0;
953 outa = set_scaling(in, minp, maxp, nmissp, "GREY", 1, 2, MINMAX, 0);
954
955 fprintf(stderr,"CONTINUING: set_scaling, case MAXMIN99 %f %f \n", *minp, *maxp);
956
957 for (ihist=0; ihist<nhist; ihist++)
958 hist[ihist] = 0;
959 for (idata=0; idata<ndata; idata++)
960 {
961 hist[*((unsigned short *)outa+idata)] += 1;
962 }
963
964 want = ndata/600;
965 fprintf(stderr,"ndata %d want %d nhist %d \n", ndata, want, nhist);
966 total = 0;
967 for (ihist=0; ihist<nhist; total += hist[ihist++])
968 if (total >= want)
969 break;
970 newmin = *minp + ((double)ihist/(double)nhist) * (*maxp - *minp);
971
972 total = 0;
973 for (ihist=nhist - 1; ihist>=0; total += hist[ihist--])
974 if (total >= want)
975 break;
976 newmax = *minp + ((double)ihist/(double)nhist) * (*maxp - *minp);
977 fprintf(stderr,"ihist %d newmax=%f newmin=%f \n", ihist, newmax, newmin);
978 if (newmax <= newmin)
979 {
980 newmax = *maxp;
981 newmin = *minp;
982 }
983
984 fprintf(stderr,"newmax %f minp %f \n", newmax, *minp);
985 for (idata=0; idata<ndata; idata++)
986 {
987 float val = inData[idata];
988 unsigned short newval;
989 if (!isnan(val)){
990 if (val <= newmin) val=newmin;
991 if (val >= newmax) val=newmax;
992 newval = maxcolor*(val-newmin)/(newmax-newmin);
993 if (scaling == MAXMIN99)
994 newval = maxcolor - newval;
995 if (newval > maxcolor) newval = maxcolor;
996 if (newval < 1) newval = 1;
997 }
998
999 else
1000 newval = missingcolor;
1001 for (color=0; color<colors; color++)
1002 {
1003 if (bytepercolor == 1)
1004 *((unsigned char *)out + colors*idata + color) = colortable[newval*colors + color];
1005 else
1006 *((unsigned short *)out + colors*idata + color) = colortable[newval*colors + color];
1007 }
1008 }
1009 free(outa);
1010 free(hist);
1011 break;
1012 }
1013 }
1014 return(out);
1015 }
1016
1017 // ----------------------------------------------------------------------
1018 int make_png(char *filename, unsigned char *data,
1019 int height, int width, char *palette, int bytepercolor, int colors)
1020 {
1021 int row;
1022 FILE *fp; // = NULL;
1023 png_byte **row_pointers = NULL;
1024 png_structp png_ptr = NULL;
1025 png_infop info_ptr = NULL;
1026
1027 fp = fopen(filename, "w");
1028 if (!fp)
1029 PNGDIE("cant open output file");
1030
1031 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
1032 NULL, NULL);
1033 if (!png_ptr)
1034 PNGDIE("cant create png_struct");
1035
1036 info_ptr = png_create_info_struct(png_ptr);
1037 if (!info_ptr)
1038 PNGDIE("cant create png_infop");
1039
1040 if (setjmp(png_jmpbuf(png_ptr)))
1041 PNGDIE("png fatal error");
1042
1043 png_init_io(png_ptr, fp);
1044 // fprintf(stderr, "ready png, bitdepth=8*bytepercolor=%d, colors=%d \n",8*bytepercolor,colors);
1045 png_set_IHDR(png_ptr, info_ptr, width, height, 8*bytepercolor,
1046 (colors == 1 ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_ADAM7,
1047 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1048 png_write_info(png_ptr, info_ptr);
1049 if (bytepercolor > 1)
1050 png_set_swap(png_ptr);
1051 png_set_gAMA(png_ptr, info_ptr, 2.2);
1052 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
1053 row_pointers = (png_byte **)malloc(height * sizeof(png_byte *));
1054
1055 for (row=0; row<height ; row++)
1056 {
1057 if (bytepercolor == 1)
1058 row_pointers[height - row - 1] = (png_byte *)((char *)data + colors*row*width);
1059 else
1060 row_pointers[height - row - 1] = (png_byte *)((short *)data + colors*row*width);
1061 }
1062 png_set_rows(png_ptr, info_ptr, row_pointers);
1063 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_SWAP_ENDIAN, NULL);
1064 fclose(fp);
1065
1066 png_destroy_write_struct(&png_ptr, &info_ptr);
1067 free(row_pointers);
1068 return(0);
1069 }
1070
1071 // ----------------------------------------------------------------------
1072
1073 /* center whith whole pixel shifts and rotate by 180 if needed */
1074 /* Only apply center if it will not result in an image crop. I.e. not ever
1075 for AIA, and not for HMI or MDI or other if a shift of more than 20 arcsec
1076 is implied */
1077 int upNcenter(DRMS_Array_t *arr, ObsInfo_t *ObsLoc)
1078 {
1079 int nx, ny, ix, iy, i, j, xoff, yoff, max_off;
1080 double rot, x0, y0, midx, midy;
1081 float *data;
1082 float *data2;
1083 if (!arr || !ObsLoc)
1084 return(1);
1085 data = arr->data;
1086 nx = arr->axis[0];
1087 ny = arr->axis[1];
1088 x0 = ObsLoc->crpix1 - 1;
1089 y0 = ObsLoc->crpix2 - 1;
1090 midx = (nx-1.0)/2.0;
1091 midy = (ny-1.0)/2.0;
1092 if ((rot = fabs(ObsLoc->crota2)) > 179 && rot < 181)
1093 {
1094 // rotate image by 180 degrees by a flip flip
1095 float val;
1096 int half = ny / 2;
1097 int odd = ny & 1;
1098 if (odd) half++;
1099 for (iy=0; iy<half; iy++)
1100 {
1101 for (ix=0; ix<nx; ix++)
1102 {
1103 i = iy*nx + ix;
1104 j = (ny - 1 - iy)*nx + (nx - 1 - ix);
1105 val = data[i];
1106 data[i] = data[j];
1107 data[j] = val;
1108 }
1109 }
1110 x0 = nx - 1 - x0;
1111 y0 = ny - 1 - y0;
1112 rot = ObsLoc->crota2 - 180.0;
1113 if (rot < -90.0) rot += 360.0;
1114 ObsLoc->crota2 = rot;
1115 }
1116 // Center to nearest pixel - if OK to do so
1117 xoff = round(x0 - midx);
1118 yoff = round(y0 - midy);
1119 max_off = 20.0 / ObsLoc->cdelt1;
1120 if (arr->parent_segment &&
1121 arr->parent_segment->record &&
1122 arr->parent_segment->record->seriesinfo &&
1123 arr->parent_segment->record->seriesinfo->seriesname &&
1124 strncasecmp(arr->parent_segment->record->seriesinfo->seriesname, "aia", 3) &&
1125 abs(xoff) < max_off && abs(yoff) < max_off)
1126 {
1127 if (abs(xoff) >= 1 || abs(yoff) >= 1)
1128 {
1129 data2 = malloc(4*nx*ny);
1130 for (iy=0; iy<ny; iy++)
1131 {
1132 int jy = iy + yoff;
1133 for (ix=0; ix<nx; ix++)
1134 {
1135 int jx = ix + xoff;
1136 int idx = jy*nx + jx;
1137 int idx2 = iy*nx + ix;
1138 if (jx<0 || jx>=nx || jy<0 || jy>=ny)
1139 data2[idx2] = DRMS_MISSING_FLOAT;
1140 else
1141 data2[idx2] = data[idx];
1142 }
1143 }
1144 x0 -= xoff;
1145 y0 -= yoff;
1146 free(data);
1147 arr->data = data2;
1148 }
1149 }
1150 // update center location
1151 ObsLoc->crpix1 = x0 + 1;
1152 ObsLoc->crpix2 = y0 + 1;
1153 return(0);
1154 }
1155
1156 // ----------------------------------------------------------------------
1157
1158 int crop_image(DRMS_Array_t *arr, ObsInfo_t *ObsLoc)
1159 {
1160 int nx, ny, ix, iy, i, j, xoff, yoff;
1161 double x0, y0;
1162 double rsun = ObsLoc->rsun_obs/ObsLoc->cdelt1;
1163 double scale, crop_limit;
1164 float *data;
1165 if (!arr || !ObsLoc)
1166 return(1);
1167 data = arr->data;
1168 nx = arr->axis[0];
1169 ny = arr->axis[1];
1170 x0 = ObsLoc->crpix1 - 1;
1171 y0 = ObsLoc->crpix2 - 1;
1172 scale = 1.0/rsun;
1173 crop_limit = 0.99975; // 1 - 1/4000, 1/2 HMI pixel.
1174 for (iy=0; iy<ny; iy++)
1175 for (ix=0; ix<nx; ix++)
1176 {
1177 double x, y, R2;
1178 float *Ip = data + iy*nx + ix;
1179 if (drms_ismissing_float(*Ip))
1180 continue;
1181 x = ((double)ix - x0) * scale; /* x,y in pixel coords */
1182 y = ((double)iy - y0) * scale;
1183 R2 = x*x + y*y;
1184 if (R2 > crop_limit)
1185 *Ip = DRMS_MISSING_FLOAT;
1186 }
1187 return(0);
1188 }
1189
1190 #define CHECK(keyname) {if (status) {fprintf(stderr,"Keyword failure to find: %s, status=%d\n",keyname,status); *rstatus=status; return(NULL);}}
1191
1192 ObsInfo_t *GetObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus)
1193 {
1194 TIME t_prev;
1195 DRMS_Record_t *rec;
1196 TIME t_obs;
1197 double dv;
1198 ObsInfo_t *ObsLoc;
1199 int status;
1200 char t_key[100];
1201
1202 if (!seg || !(rec = seg->record))
1203 { *rstatus = 1; return(NULL); }
1204
1205 ObsLoc = (pObsLoc ? pObsLoc : (ObsInfo_t *)malloc(sizeof(ObsInfo_t)));
1206 if (!pObsLoc)
1207 memset(ObsLoc, 0, sizeof(ObsInfo_t));
1208
1209 t_prev = ObsLoc->t_obs;
1210 if (drms_keyword_lookup(rec, "T_OBS", 0))
1211 strcpy(t_key, "T_OBS");
1212 else
1213 strcpy(t_key, "T_REC");
1214 t_obs = drms_getkey_time(rec, t_key, &status); CHECK(t_key);
1215
1216 if (t_obs <= 0.0)
1217 { *rstatus = 2; return(NULL); }
1218
1219 if (t_obs != t_prev)
1220 {
1221 ObsLoc->crpix1 = drms_getkey_double(rec, "CRPIX1", &status); CHECK("CRPIX1");
1222 ObsLoc->crpix2 = drms_getkey_double(rec, "CRPIX2", &status); CHECK("CRPIX2");
1223 ObsLoc->crval1 = drms_getkey_double(rec, "CRVAL1", &status); CHECK("CRVAL1");
1224 ObsLoc->crval2 = drms_getkey_double(rec, "CRVAL2", &status); CHECK("CRVAL2");
1225 ObsLoc->cdelt1 = drms_getkey_double(rec, "CDELT1", &status); CHECK("CDELT1");
1226 ObsLoc->cdelt2 = drms_getkey_double(rec, "CDELT2", &status); CHECK("CDELT1");
1227 ObsLoc->crota2 = drms_getkey_double(rec, "CROTA2", &status); if (status) ObsLoc->crota2 = 0.0; // WCS default
1228 ObsLoc->sina = sin(ObsLoc->crota2*Deg2Rad);
1229 ObsLoc->cosa = sqrt (1.0 - ObsLoc->sina*ObsLoc->sina);
1230 ObsLoc->rsun_obs = drms_getkey_double(rec, "RSUN_OBS", &status);
1231 if (status)
1232 {
1233 double dsun_obs = drms_getkey_double(rec, "DSUN_OBS", &status); CHECK("DSUN_OBS");
1234 ObsLoc->rsun_obs = asin(696000000.0/dsun_obs)/arcsec2Rad;
1235 }
1236 ObsLoc->obs_vr = drms_getkey_double(rec, "OBS_VR", &status); CHECK("OBS_VR");
1237 ObsLoc->obs_vw = drms_getkey_double(rec, "OBS_VW", &status); CHECK("OBS_VW");
1238 ObsLoc->obs_vn = drms_getkey_double(rec, "OBS_VN", &status); CHECK("OBS_VN");
1239 ObsLoc->obs_b0 = drms_getkey_double(rec, "CRLT_OBS", &status); CHECK("CRLT_OBS");
1240 ObsLoc->t_obs = t_obs;
1241 }
1242 *rstatus = 0;
1243 return(ObsLoc);
1244 }
1245
1246
1247 // ----------------------------------------------------------------------