ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/JSOC/proj/util/apps/im_patch.c
Revision: 1.30
Committed: Mon Nov 30 23:27:24 2020 UTC (2 years, 9 months ago) by phil
Content type: text/plain
Branch: MAIN
CVS Tags: Ver_LATEST, Ver_9-41, Ver_9-5
Changes since 1.29: +1 -1 lines
Log Message:
Change 1<<63 to (long long)1 << 63

File Contents

# Content
1 /**
2 @defgroup im_patch im_patch (image_patch) extracts a rectangular
3 region or patch from an input series. The patch is tracked across
4 the solar disk from time t_start to t_stop. The patch size in pixels is defined by either its
5 Carrington rotation, longitude, latitude and size when it crosses central meridian or
6 by its location in arcsec at a reference time. If defined by Carrington coordinates the
7 bounding box size is specified in degrees of longitude and latitude at disk center. Else the box is
8 specified by its reference image time and location in locunits. No reprojection is done, the patch is
9 a simple cutout of the original image.
10
11 @ingroup a_programs
12
13 @brief im_patch extracts a rectangular
14 image from an input series. The patch is tracked across
15 the solar disk from t_start to t_stop times. The patch may be defined by Carrington or Syonyhurst coordinates or
16 in arcsec or pixels. The extracted patch is rectangular in original image pixels.
17
18
19 @par Synopsis:
20
21 @code
22 im_patch in=<recordset> out=<series_out> \
23 log=<logfile> \
24 t_start=<start time> \
25 t_stop=<end time> \
26 cadence=<step time> \
27 car_rot=<Carrington Rotation> \
28 t_ref=<ref_time> \
29 locunits=<location units> \
30 x=<ew location> \
31 y=<ns location> \
32 boxunits= <patch spec units> \
33 height=<height> \
34 width=<width> \
35 requestid=<RequestID> \
36 [-t] \
37
38 @endcode
39
40 Cadence is the desired time between extracted frames. It will be modified to be a multiple of the dataset's step size.
41 The extracted region location may be specified by position at a reference time or by Carrington coordinates.
42 If by position at a reference time, the position may be specified in ipixels, arcsec, or Stonyhurst coordinates.
43 If the car_rot parameter is required if the Carrington location method is used.
44 If the t_ref parameter is present, location is specified by a method given in <locunits>.
45 The location is given in the "x" and "y" arguments where the units of x and y are determined by locunits.
46 The choices for locunits are: "arcsec", "pixels", "stony", "carrlong".
47 For "stony" (Stonyhurst) x is the CM distance in degrees, for "carrlong" x is Carrington longitude.
48 The y value is in degrees of latitude for either "stony" or "carrlong".
49 Arcsec locations are measured from disk center, pixel locations are measured from the lower left corner of
50 the image, with the first pixel labeled (1,1).
51
52 The size of the patch, given in the width and height parameters can be expressed in "pixels", "arcsec", or "degrees"
53 as determined by the "boxunits" parameter.
54
55 If the logfile parameter is present, a RecordSet query will be written to logfile for each image created containing a query
56 that will return that image.
57
58 RequestID, if present, is the ID of an on-requst processing export request.
59
60 If neither a limiting recordset specification nor t_start or t_stop are present, those values will be inferred from
61 the on-disk time span for the center of the patch -90 degrees to +90 degrees from CM.
62 If cadence is not specified explicitly on the command line or in the recordset spec, the full cadence of the dataset will be used.
63 This module computes the beginning and ending time for tracking the
64 region identified at time = t_ref, x and y or by the Carrington coordinates of the box center.
65 In the Carrington case the module extracts a rectangular region
66 with user defined size. the height and width of the box are specified in degrees
67 of latitude and longitude with the pixel size of the box is computed from the
68 projection of the box at CM. The pixel size of the rectangular box remains constant
69 during tracking. The box height defaults to the width. The width defaults to 10 degrees.
70
71 If the input recordset spec is exactly "[$]" that spec will be discarded and the time limits will be taken from t_start, t_stop, car_rot, and/or t_ref as appropriate. This is to allow exports via jsoc_fetch to not require a bounding recordset to be specified.
72
73 If t_start and/or t_stop are specified the referecne time or disk center location need not be
74 included.
75
76 If the input is only a seriesname with [$], it must have a prime key of type time.
77
78 If the t_ref parameter is specified, there must be a non-missing image within +- 2 hours of t_ref.
79
80 The output seriesname defaults to the input seriesname with a suffix of "_mod".
81
82 The -t, 'no tracking' flag casues the extracted region to remain fixed with respect to disc center. I.e. the Carrington
83 tracking is disabled. If locunits are "stony" or "carrlong" the patch center must be on the disk.
84
85 The -c, 'crop' flag causes the off-limb data to be replaced with MISSING (NaNs for floating types).
86
87 The -h "header keywords" flag causes the metadata keywords to be included in all of the output FITS files.
88
89 If -r or -R are specified, the images will be sub-pixel registered to the target location. If -r or -R are given
90 along with -t (no tracking) then -r means register to the first frame and -R means register to Sun center.
91
92 -f, -F flags and FDS are special rarely used parameters that allow tracking to a location given in
93 a SDO flight dynamics system generated trajectory of e.g. a transitting moon or planet. The -f or -F flags
94 in this case will generate a fake object with value either 0 or datamax. For example, the Venus transit
95 locations are found in sdo.fds[2012.06.05_00:00:00_UTC][SOLAR_TRANSIT][S][2]
96
97 If the input series is AIA lev1 or HMI lev1 where the time slots are not fully populated then special
98 code is used to produce the requested cadence. In these cases the DRMS "time@cadence" recordset notation
99 will fail so use the cadence parameter instead.
100
101 @par Flags:
102 -A Generate output for all segments.
103 -t Disable tracking.
104 -h Include header keywords
105 -r register to first frame
106 -R register to Sun center
107 -f make black fake tracked object
108 -F make light fake tracked object
109
110
111 @par GEN_FLAGS:
112 Ubiquitous flags present in every module.
113 @ref jsoc_main
114
115 @param in The input data series.
116 @param out The output series.
117
118 @par Exit_Status:
119
120 @par Example:
121 Extracts a rectangular region of size 20 x 10 degrees and tracks it from
122 when the region is just appearing at the E-limb to t_stop.
123
124 @code
125 im_patch in='hmi.M_45s' locunits=carrlong boxunits=pixels car_rot=2009 x=295 y=5 width=30 height=20
126
127 @endcode
128
129 @par Example:
130
131 @code
132
133 @endcode
134
135 @bug
136 No doubt some bugs.
137
138
139 */
140 #include "jsoc.h"
141 #include "jsoc_main.h"
142 #include "astro.h"
143 #include "fstats.h"
144 #include "atoinc.h"
145 #include <math.h>
146
147 int get_tracking_xy(char *FDSfile, TIME want, double *xp, double *yp, double *radius);
148 void HeliographicLocation(TIME t, int *crot, double *L, double *B);
149 TIME HeliographicTime(int crot, double L);
150
151 char *module_name = "im_patch";
152
153 #define DIE(msg) {fprintf(stderr,"%s Status=%d\n",msg, status); return(status?status:1);}
154 #define DIE2(msg,val) {fprintf(stderr,"%s %s, Status=%d\n",msg, val, status); return(status?status:1);}
155 #define TEST_PARAM(param) {if (status) DIE2("Required keyword missing: ", param);}
156
157 #define Deg2Rad (M_PI/180.0)
158 #define Rad2arcsec (3600.0/Deg2Rad)
159 #define Rad2Deg (180.0/M_PI)
160 #define VERBOSE 0
161
162 ModuleArgs_t module_args[] =
163 {
164 {ARG_STRING, "in", "NOTSPECIFIED", "input series. e.g 'hmi.M_720s'"},
165 {ARG_STRING, "out", "NOTSPECIFIED", "output seies. e.g. 'su_phil.hmi_cutout'"},
166 {ARG_STRING, "log", "NOTSPECIFIED", "output log file of records made"},
167 {ARG_STRING, "requestid", "NOTSPECIFIED", "RequestID if im_patch call originated in an export request."},
168 {ARG_INT, "car_rot", "-1", "Carrington Rotation when the region crosses CM"},
169 {ARG_FLOAT, "width", "0", "width of box in boxunits (conversion to pixels made when at CM)"},
170 {ARG_FLOAT, "height", "0", "height of box in boxunits (conversion to pixels made when at CM)"},
171 {ARG_STRING, "boxunits", "NOTSPECIFIED", "units of patch, 'pixels', 'arcsecs', or 'degrees'"},
172 {ARG_TIME, "t_start", "JD_0", "Start time, defaults to time at 90E"},
173 {ARG_TIME, "t_stop", "JD_0", "End time, defauolts to 90W"},
174 {ARG_TIME, "cadence", "NOTSPECIFIED", "Cadence of product, defaults to input cadence."},
175 {ARG_TIME, "t_ref", "JD_0", "Time for which x and y apply, selects reference image for tracking."},
176 {ARG_STRING, "locunits", "NOTSPECIFIED", "Location units in 'pixels', 'arcsec', 'stony', or 'carrlong'"},
177 {ARG_STRING, "where", "NOTSPECIFIED", "Additional 'where' clause if needed"},
178 {ARG_FLOAT, "x", "0", "Location of extract box center in pixels in locunits: arcsec from Sun center, degrees from Sun center, or Carrington longitude"},
179 {ARG_FLOAT, "y", "0", "Location of extract box center, same units as x"},
180 {ARG_FLAG, "A", NULL, "Generate output for all input segments"},
181 {ARG_FLAG, "t", "0", "Disable Carrington rate tracking"},
182 {ARG_FLAG, "h", "0", "Export keywords, i.e. include full metadata in FITS files"},
183 {ARG_FLAG, "c", "0", "Crop at limb, useful for HMI M data."},
184 {ARG_FLAG, "f", "0", "FAKE tracking target added for transits, val=0"},
185 {ARG_FLAG, "F", "0", "FAKE tracking target added for transits, val=datamax"},
186 {ARG_FLAG, "r", "0", "Register to fractional CRPIXi, all frames to first frame round(crpix)"},
187 {ARG_STRING, "FDS", "NOTSPECIFIED", "FDS file for tracking transit objects"},
188 {ARG_END}
189 };
190
191 int img2sphere (double x, double y, double ang_r, double latc, double lonc,
192 double pa, double *rho, double *lat, double *lon, double *sinlat,
193 double *coslat, double *sig, double *mu, double *chi);
194 int sphere2img (double lat, double lon, double latc, double lonc,
195 double *x, double *y, double xcenter, double ycenter,
196 double rsun, double peff, double ecc, double chi,
197 int xinvrt, int yinvrt);
198 int image_magrotate(void *, int nin, int min, int data_type_input, float theta, float mag,
199 float dx, float dy, void **outarray, int *nx, int *ny, int regridtype_input, int stretch_lines);
200
201 // experimental make recordset list at cadence
202 // OLD VERSION char *get_input_recset(DRMS_Env_t *env, char *in, TIME cadence, char *timekeyname);
203 char *get_input_recset(DRMS_Env_t *drms_env, char *inQuery);
204
205 // Macros for WCS transformations. assume crpix1, crpix2 = CRPIX1, CRPIX2, sina,cosa = sin and cos of CROTA2 resp.
206 // and crvalx and crvaly are CRVAL1 and CRVAL2, cdelt = CDELT1 == CDELT2, then
207 // PIX_X and PIX_Y are CCD pixel addresses, WX and WY are arc-sec W and N on the Sun from disk center.
208 // These are in units where the first pixel is 1 not 0.
209 #define PIX_X(wx,wy) ((((wx-crvalx)*cosa + (wy-crvaly)*sina)/cdelt)+crpix1)
210 #define PIX_Y(wx,wy) ((((wy-crvaly)*cosa - (wx-crvalx)*sina)/cdelt)+crpix2)
211 #define WX(pix_x,pix_y) (((pix_x-crpix1)*cosa - (pix_y-crpix2)*sina)*cdelt+crvalx)
212 #define WY(pix_x,pix_y) (((pix_y-crpix2)*cosa + (pix_x-crpix1)*sina)*cdelt+crvaly)
213
214 #define BOXBAD 0
215 #define BOXARCSEC 1
216 #define BOXPIXELS 2
217 #define BOXDEGREE 3
218
219 #define LOCBAD 0
220 #define LOCARCSEC 1
221 #define LOCPIXELS 2
222 #define LOCSTONY 3
223 #define LOCCARR 4
224
225 #define TIMES_RECSET 0 // recordset spec contains time range
226 #define TIMES_GIVEN 1 // explicit t_start and t_stop provided
227 #define TIMES_IMPLICIT 2 // times to be deduced for disk transit of specified box
228
229 // Two functions to get CRLN_OBS corrected and to store new CALVERxx value.
230 // Use update_CALVERxx to fetch CALVERxx and or it with e.g. 1<<28 to indicate new CRLN_OBS
231 // CALVERxx may be either of CALVER64 or CALVER32.
232 int update_CALVERxx(DRMS_Record_t *inrec, DRMS_Record_t *outrec, int calver)
233 {
234 int status;
235 DRMS_Keyword_t *calver_kw;
236 if (calver_kw = drms_keyword_lookup(inrec, "CALVER64", 1))
237 {
238 long long calver64 = calver_kw->value.longlong_val;
239 if (((long long)1<<63) & calver64) calver64 = 0;
240 drms_setkey_longlong(outrec, "CALVER64", calver64 | calver);
241 }
242 else
243 {
244 if (calver_kw = drms_keyword_lookup(inrec, "CALVER32", 1))
245 {
246 int calver32 = calver_kw->value.int_val;
247 if ((1<<31) & calver32) calver32 = 0;
248 drms_setkey_int(outrec, "CALVER32", calver32 | calver);
249 }
250 }
251 }
252
253 // Use get_new_CRLN_OBS as a direct replacement for drms_getkey_double or drms_getkey_float.
254 // If there is a CALVERxx keyword present, it will check the bit #28 and if not set then
255 // the correction to CRLN_OBS will be made.
256 double get_new_CRLN_OBS(DRMS_Record_t *inrec, int *status)
257 {
258 int this_status;
259 double CRLN_CORRECTION = -0.0818941;
260 long long calver64;
261 int calver32;
262 DRMS_Keyword_t *calver_kw;
263 calver32 = 1<<31;
264 double crln_obs = drms_getkey_double(inrec, "CRLN_OBS", &this_status);
265 if (this_status || isnan(crln_obs))
266 *status = this_status;
267 else // CRLN_OBS exists and is not missing, check for one of CALVER keywords.
268 {
269 if (calver_kw = drms_keyword_lookup(inrec, "CALVER64", 1))
270 {
271 calver64 = calver_kw->value.longlong_val;
272 if (calver64 >= 0)
273 calver32 = calver64 & 0xFFFFFFFF;
274 }
275 else
276 {
277 if (calver_kw = drms_keyword_lookup(inrec, "CALVER32", 1))
278 calver32 = calver_kw->value.int_val;
279 }
280 if (!((calver32>>31) & 1) && !((calver32>>28) & 1)) // good CALVER but new CRLN_OBS is not present
281 {
282 crln_obs += CRLN_CORRECTION;
283 }
284 }
285 return(crln_obs);
286 }
287
288
289 int DoIt(void)
290 {
291 CmdParams_t *params = &cmdparams;
292 DRMS_RecordSet_t *inRS = NULL;
293 DRMS_RecordSet_t *outRS = NULL;
294 DRMS_Record_t *inRec = NULL;
295 DRMS_Record_t *outRec = NULL;
296 DRMS_Record_t *inTemplate = NULL;
297 DRMS_Record_t *outTemplate = NULL;
298 DRMS_Segment_t *inSeg = NULL;
299 DRMS_Segment_t *outSeg = NULL;
300 DRMS_Array_t *inArray = NULL;
301 DRMS_Array_t *outArray = NULL;
302 int i, ii, status = DRMS_SUCCESS, nrecs;
303 int irec;
304 int OK_recs = 0;
305 double center_x, center_y, crpix1, crpix2, x0, y0;
306 double rsun_ref, dsun_obs, rsun, rsun_rad, rsunpix;
307 double r2;
308 double this_x0;
309 double this_y0;
310 double crln_obs, crlt_obs;
311 double crln_obs_rad, crlt_obs_rad;
312 double crln_rad, crlt_rad;
313 double pa_rad;
314 double cdelt;
315 double llx, lly;
316 int inAxis[2];
317 int this_car_rot;
318 char *ctype1, *ctype2;
319 TIME t_rec;
320 double box_x, box_y;
321 double crln, crlt;
322 char outseries[DRMS_MAXSERIESNAMELEN];
323 char inseries[DRMS_MAXSERIESNAMELEN];
324 char extractedSeries[DRMS_MAXSERIESNAMELEN];
325 int is_mod;
326 char testseries[DRMS_MAXSERIESNAMELEN];
327 char inQuery[DRMS_MAXQUERYLEN];
328 char in[DRMS_MAXQUERYLEN];
329
330 const char *ingiven = params_get_str(params, "in");
331 char *inparam;
332 char *lbracket, *rbracket;
333 char *moreQuery = NULL;
334 const char *outparam = params_get_str(params, "out");
335 const char *logfile = params_get_str(params, "log");
336 const char *requestid = params_get_str(params, "requestid");
337 const char *where = params_get_str(params, "where");
338 const char *FDSfile = params_get_str(params, "FDS");
339 TIME t_start = params_get_time(params, "t_start");
340 TIME t_stop = params_get_time(params, "t_stop");
341 const char *cadence_str = params_get_str(params, "cadence");
342 double cadence;
343 TIME t_ref = params_get_time(params, "t_ref");
344 double width = params_get_double(params, "width");
345 double height = params_get_double(params, "height");
346 int pixwidth, pixheight;
347 double midx, midy;
348 int car_rot = params_get_int(params, "car_rot");
349 double x = params_get_double(params, "x");
350 double y = params_get_double(params, "y");
351 const char *boxunits = params_get_str(params, "boxunits");
352 const char *locunits = params_get_str(params, "locunits");
353 int NoTrack = params_isflagset(params, "t");
354 int wantFAKEwhite = params_isflagset(params, "F");
355 int wantFAKE = params_isflagset(params, "f") || wantFAKEwhite;
356 int do_register = params_isflagset(params, "r");
357 int export_keys = params_isflagset(params, "h");
358 int do_crop = params_isflagset(params, "c");
359 TIME tNotSpecified = sscan_time("JD_0");
360 int do_reftime = 0; // Target specified by reftime vs Carr rotation mode.
361 double crvalx = 0.0;
362 double crvaly = 0.0;
363 double crota, sina, cosa;
364 double pa, deltlong;
365 double target_x, target_y; // used for NoTrack option
366 int firstimage = 1;
367 int boxtype, loctype;
368 char *timekeyname;
369 TIME t_step, t_epoch;
370 int npkeys;
371 int times_source = TIMES_RECSET;
372 char timebuf[20];
373 char *in_filename = NULL;
374 double trackx, tracky;
375 double track_radius;
376 double crpix1_0, crpix2_0;
377 int register_padding = 60; // later this should be based on crota2 and box size.
378 char *tmpstr = NULL;
379 int hasSegList = 0;
380 int doingAllSegs = params_isflagset(params, "A");
381 int calver;
382
383 FILE *log = NULL;
384
385 if (strcmp(FDSfile,"NOTSPECIFIED")==0)
386 FDSfile = NULL;
387
388 if (strncasecmp(locunits, "arcsec", 6) == 0) loctype = LOCARCSEC;
389 else if (strncasecmp(locunits, "pixels", 3) == 0) loctype = LOCPIXELS;
390 else if (strncasecmp(locunits, "stony", 5) == 0) loctype = LOCSTONY;
391 else if (strncasecmp(locunits, "carrlong", 4) == 0) loctype = LOCCARR;
392 else loctype = LOCBAD;
393
394 if (strncasecmp(boxunits, "arcsec", 6) == 0) boxtype = BOXARCSEC;
395 else if (strncasecmp(boxunits, "pixels", 3) == 0) boxtype = BOXPIXELS;
396 else if (strncasecmp(boxunits, "degrees", 3) == 0) boxtype = BOXDEGREE;
397 else loctype = BOXBAD;
398
399 if (loctype == LOCCARR)
400 {
401 if (car_rot < 0) DIE("Carrington rotation number must be provided for locunits=carrlong");
402 // if (NoTrack) DIE("Can not use locunits=carrlong for no-tracking mode");
403 do_reftime = 0;
404 }
405 else
406 {
407 if (t_ref < 0)
408 DIE("t_ref must be provided for locunits!=carrlong");
409 do_reftime = 1;
410 }
411
412 if (loctype==LOCBAD)
413 DIE2("Location units not detected, locunits", locunits)
414 if (boxtype==BOXBAD)
415 DIE2("patch dimensions not understood,", boxunits);
416
417 if (strcasecmp(logfile, "NOTSPECIFIED") != 0)
418 {
419 log = fopen(logfile, "w");
420 if (!log) DIE2("Can not create log file.",logfile);
421 }
422
423 if (strcasecmp(where, "NOTSPECIFIED") == 0 || *where == '\0')
424 {
425 where = "";
426 }
427 else
428 {
429 char wherework[4096];
430 sprintf(wherework,"[? %s ?]", where);
431 where = strdup(wherework);
432 }
433
434 /* We need to extract the series name from the input record-set specification. There */
435
436 /* Determine if there is a segment list specifier. I believe this is true if the last non-ws char of the specification is a '}'.
437 * Of course, the specification could be invalid, but in that event the drms_open_records() call will fail and catch the error and
438 * the module run will abort. */
439 char *testSegList = NULL;
440 char *pCh = NULL;
441
442 testSegList = rindex(ingiven, '}');
443 if (testSegList)
444 {
445 for (pCh = testSegList + 1, hasSegList = 1; *pCh; pCh++)
446 {
447 if (!isspace(*pCh))
448 {
449 /* There is a non-ws char after '}' - this is not a valid seglist. */
450 hasSegList = 0;
451 break;
452 }
453 }
454 }
455
456 inparam = strdup(ingiven);
457 if (strcasecmp(inparam, "NOTSPECIFIED") == 0) DIE("Input series must be specified.");
458
459 /* This program works only if the series' first prime-key constituent is a time keyword. Then, we want to
460 * ignore the first prime key and use the t_start/t_stop argument to select records instead.
461 * I believe the program will not work with record-set specifications that contain more than one
462 * subset either. So...
463 * 1. Extract the series from the record-set specification.
464 * 2. Bail if there is more than one record-set subset.
465 * 3. Obtain the first keyword from the prime-key set.
466 * 4. Bail if the first keyword is not a time keyword.
467 * 5. Remove the time filter provided by the user in the record-set specification.
468 */
469
470 /* Use drms_record_parserecsetspec() to extract the series and filter strings. The previous
471 * code wasn't quite working 100% . */
472 char *allvers = NULL;
473 char **sets = NULL;
474 DRMS_RecordSetType_t *settypes = NULL; /* a maximum doesn't make sense */
475 char **snames = NULL;
476 char **filts = NULL;
477 int nsets = 0;
478 DRMS_RecQueryInfo_t rsinfo; /* Filled in by parser as it encounters elements. */
479 DRMS_Keyword_t *firstPKey = NULL;
480
481 if (drms_record_parserecsetspec(inparam, &allvers, &sets, &settypes, &snames, &filts, &nsets, &rsinfo) == DRMS_SUCCESS)
482 {
483 if (nsets > 1)
484 {
485 status = DRMS_ERROR_INVALIDDATA;
486 DIE("im_patch supports single-set record-set specifications only.");
487 }
488 }
489 else
490 {
491 status = DRMS_ERROR_INVALIDDATA;
492 DIE("Invalid record-set specification provided.");
493 }
494
495 snprintf(inseries, sizeof(inseries), "%s", snames[0]);
496 lbracket = index(inparam, '[');
497
498 inTemplate = drms_template_record(drms_env, inseries, &status);
499 if (status || !inTemplate) DIE2("Input series can not be found: ", inseries);
500
501 if (drms_keyword_isindex(inTemplate->seriesinfo->pidx_keywords[0]))
502 {
503 firstPKey = drms_keyword_slotfromindex(inTemplate->seriesinfo->pidx_keywords[0]);
504 }
505 else
506 {
507 firstPKey = inTemplate->seriesinfo->pidx_keywords[0];
508 }
509
510 if (firstPKey->info->type != DRMS_TYPE_TIME)
511 {
512 status = DRMS_ERROR_INVALIDDATA;
513 DIE("The first prime-key constituent keyword must be of type TIME.");
514 }
515
516 if (filts && filts[0])
517 {
518 if (strlen(filts[0]) == 3 && filts[0][1] == '$')
519 {
520 /* The user specified the last (according to time) record. Remove that filter. */
521 moreQuery = lbracket + 3;
522 *lbracket = '\0';
523 lbracket = NULL;
524
525 if (t_start == tNotSpecified || t_stop == tNotSpecified)
526 times_source = TIMES_IMPLICIT;
527 else
528 times_source = TIMES_GIVEN;
529 }
530 else
531 {
532 rbracket = index(lbracket, ']');
533 moreQuery = rbracket ? rbracket + 1 : lbracket;
534 }
535 }
536 else
537 {
538 /* No filter provided at all. */
539 if (t_start == tNotSpecified || t_stop == tNotSpecified)
540 times_source = TIMES_IMPLICIT;
541 else
542 times_source = TIMES_GIVEN;
543 }
544
545 drms_record_freerecsetspecarr(&allvers, &sets, &settypes, &snames, &filts, nsets);
546
547 // XXXXX Get box location in Carrington Coords for all location types, also initial center_X, center_y in NoTrack case XXXXX
548 if (do_reftime) // Arc-sec, pixel, or Stonyhurst specification, get ref image information
549 { // the image for ref_time is supposed to be present in the series.
550 if (VERBOSE) fprintf(stderr,"doing reftime\n");
551 int okrec = -1;
552 char t_ref_pre[100];
553 char t_ref_post[100];
554 TIME search_width = 15;
555 while (search_width < 10000)
556 {
557 sprint_at(t_ref_pre, t_ref-search_width);
558 sprint_at(t_ref_post, t_ref+search_width);
559 sprintf(in, "%s[%s-%s][? QUALITY >= 0 ?]%s%s", inseries, t_ref_pre, t_ref_post, moreQuery, where);
560 if (VERBOSE) fprintf(stderr,"searchwidth=%f, t_ref query is: %s\n",search_width,in);
561 inRS = drms_open_records(drms_env, in, &status);
562 if (!status && inRS->n > 0)
563 {
564 int irec, nrecs = inRS->n;
565 TIME tdiff = 100000;
566 for (irec=0, okrec=-1; irec<nrecs; irec++) // find record close to t_ref
567 {
568 TIME newdiff;
569 inRec = inRS->records[irec];
570 if (VERBOSE) fprintf(stderr,"irec=%d, tdiff=%lf\n",irec,tdiff);
571 if ((newdiff=(fabs(drms_getkey_time(inRec,"T_OBS",NULL) - t_ref))) > tdiff)
572 break;
573 okrec = irec;
574 tdiff = newdiff;
575 }
576 }
577 if (okrec >= 0)
578 break;
579 search_width *= 2;
580 drms_close_records(inRS, DRMS_FREE_RECORD);
581 }
582 if (search_width >= 10000)
583 DIE2("No input data found within 2-hours of t_ref",in);
584 inRec = inRS->records[okrec];
585 this_car_rot = drms_getkey_int(inRec, "CAR_ROT", &status); TEST_PARAM("CAR_ROT");
586 crlt_obs = drms_getkey_double(inRec, "CRLT_OBS", &status); TEST_PARAM("CRLT_OBS");
587 // crln_obs = drms_getkey_double(inRec, "CRLN_OBS", &status); TEST_PARAM("CRLN_OBS");
588 crln_obs = get_new_CRLN_OBS(inRec, &status); TEST_PARAM("CRLN_OBS");
589 crpix1 = drms_getkey_double(inRec, "CRPIX1", &status); TEST_PARAM("CRPIX1");
590 x0 = crpix1 - 1;
591 crpix2 = drms_getkey_double(inRec, "CRPIX2", &status); TEST_PARAM("CRPIX2");
592 y0 = crpix2 - 1;
593 crvalx = drms_getkey_double(inRec, "CRVAL1", &status); TEST_PARAM("CRVAL1");
594 crvaly = drms_getkey_double(inRec, "CRVAL2", &status); TEST_PARAM("CRVAL2");
595 crota = drms_getkey_double(inRec, "CROTA2", &status); if (status) crota = 0.0; // WCS default
596 pa = -crota;
597 crlt_obs_rad = Deg2Rad * crlt_obs;
598 crln_obs_rad = Deg2Rad * crln_obs;
599 pa_rad = Deg2Rad * pa;
600 sina = sin(crota*Deg2Rad);
601 cosa = cos(crota*Deg2Rad);
602 tmpstr = drms_getkey_string(inRec, "CTYPE1", &status);
603 ctype1 = strdup(tmpstr); TEST_PARAM("CTYPE1");
604 if (tmpstr)
605 {
606 free(tmpstr);
607 }
608 if (strcmp(ctype1, "HPLN-TAN") != 0)
609 DIE2("CTYPE1 not HPLN-TAN as required, is: ", ctype1);
610 if (ctype1)
611 {
612 free(ctype1);
613 ctype1 = NULL;
614 }
615 tmpstr = drms_getkey_string(inRec, "CTYPE2", &status);
616 ctype2 = strdup(tmpstr); TEST_PARAM("CTYPE2");
617 if (tmpstr)
618 {
619 free(tmpstr);
620 }
621 if (strcmp(ctype2, "HPLT-TAN") != 0)
622 DIE2("CTYPE2 not HPLT-TAN as required, is: ", ctype2);
623 if (ctype2)
624 {
625 free(ctype2);
626 ctype2 = NULL;
627 }
628 rsun_ref = drms_getkey_double(inRec, "RSUN_REF", &status);
629 if (status)
630 rsun_ref = 6.96e8;
631 dsun_obs = drms_getkey_double(inRec, "DSUN_OBS", &status); TEST_PARAM("DSUN_OBS");
632 rsun_rad = asin(rsun_ref/dsun_obs);
633 rsun = rsun_rad*Rad2arcsec;
634 cdelt = drms_getkey_double(inRec, "CDELT1", &status); TEST_PARAM("CDELT1");
635 rsunpix = rsun/cdelt;
636 if (loctype == LOCPIXELS || loctype==LOCARCSEC)
637 { /* box ref location is in arcsec - find Carrington equivalent */
638 // Note x and y and PIX_X and PIX_Y have first pixel at (1,1)
639 // center_x and center_y are center of target box where first pixel is (0,0)
640 if (loctype==LOCARCSEC)
641 {
642 center_x = PIX_X(x,y) - 1;
643 center_y = PIX_Y(x,y) - 1;
644 }
645 else
646 {
647 center_x = x - 1;
648 center_y = y - 1;
649 }
650 // center_x and center_y are box center in pixels, first pixel at (0,0)
651 inSeg = drms_segment_lookupnum(inRec, 0);
652 inAxis[0] = inSeg->axis[0];
653 inAxis[1] = inSeg->axis[1];
654 if ( img2sphere((center_x - x0)/rsunpix, (center_y -y0)/rsunpix, rsun_rad, crlt_obs_rad, crln_obs_rad, pa_rad,
655 NULL, &crlt_rad, &crln_rad, NULL, NULL, NULL, NULL, NULL) < 0)
656 fprintf(stderr, "Starting location is off the solar disk.");
657 crln = crln_rad * Rad2Deg;
658 crlt = crlt_rad * Rad2Deg;
659 fprintf(stderr,"at do reftime, center_x=%f\n", center_x);
660 }
661 else /* loctype==LOCSTONY */
662 {
663 crlt = y;
664 crln = crln_obs + x;
665 sphere2img(crlt/Rad2Deg, crln/Rad2Deg, crlt_obs_rad, crln_obs_rad, &center_x, &center_y, x0, y0, rsunpix, pa_rad, 0, 0, 0, 0);
666 }
667 deltlong = crln - crln_obs;
668 car_rot = this_car_rot;
669 if (crln >= 360.0)
670 { car_rot--; crln -= 360.0; }
671 else if (crln < 0)
672 { car_rot++; crln += 360.0; }
673 drms_close_records(inRS, DRMS_FREE_RECORD);
674 }
675 else // Carrington specification
676 {
677 crln = x;
678 crlt = y;
679 if (crln < 0) DIE("Box longitude must be specified.");
680 if (crlt < -990) DIE("box latitude must be specified.");
681 sphere2img(crlt/Rad2Deg, crln/Rad2Deg, crlt_obs_rad, crln_obs_rad, &center_x, &center_y, x0, y0, rsunpix, pa_rad, 0, 0, 0, 0);
682 }
683 crln_rad = crln * Deg2Rad;
684 crlt_rad = crlt * Deg2Rad;
685
686 // Now we have crln, crlt, crln_rad, crlt_rad, car_rot for the target box.
687 // unless the box center is off the disk.
688 // For NoTrack case we have center_x and center_y in pixels
689
690 // Get implied time limits from car_rot and crln
691 t_ref = HeliographicTime(car_rot, crln);
692 if (times_source == TIMES_IMPLICIT)
693 {
694 if (t_start == tNotSpecified)
695 {
696 if (NoTrack) DIE("Must have t_start or explicit times for NoTrack");
697 t_start = HeliographicTime(car_rot, crln + 90);
698 }
699 if (t_stop == tNotSpecified)
700 {
701 if (NoTrack) DIE("Must have t_stop or explicit times for NoTrack");
702 t_stop = HeliographicTime(car_rot, crln - 90);
703 }
704 }
705 // XXXXXXXXXXXXXXXXX End of get target box location
706
707 // XXXXXXXXXXXXXXXX get input seriesname and output seriesname
708 // first, get input and output series names.
709 is_mod = 0;
710 if (strcasecmp(outparam, "NOTSPECIFIED") == 0)
711 {
712 strncpy(outseries, inseries, DRMS_MAXSERIESNAMELEN);
713 strncat(outseries, "_mod", DRMS_MAXSERIESNAMELEN);
714 is_mod = 1;
715 }
716 else
717 {
718 strncpy(outseries, outparam, DRMS_MAXSERIESNAMELEN);
719 strncpy(testseries, inseries, DRMS_MAXSERIESNAMELEN);
720 strcat(testseries, "_mod");
721 if (strcmp(testseries, outseries) == 0)
722 is_mod = 1;
723 }
724
725 // Now, make sure output series exists and get template record.
726 outTemplate = drms_template_record(drms_env, outseries, &status);
727 if (status || !outTemplate) DIE2("Output series can not be found: ", outseries);
728
729 // Now find the prime time keyword name
730 npkeys = inTemplate->seriesinfo->pidx_num;
731 timekeyname = NULL;
732 if (npkeys > 0)
733 {
734 int i;
735 for (i=0; i<npkeys; i++)
736 {
737 DRMS_Keyword_t *pkey, *skey;
738 pkey = inTemplate->seriesinfo->pidx_keywords[i];
739 if (pkey->info->recscope > 1)
740 pkey = drms_keyword_slotfromindex(pkey);
741 if (pkey->info->type != DRMS_TYPE_TIME)
742 continue;
743 if(i > 0) DIE("Input series must have TIME keyword first, for now...");
744 timekeyname = pkey->info->name;
745 t_step = drms_keyword_getdouble(drms_keyword_stepfromslot(pkey), &status);
746 if (status) DIE("problem getting t_step");
747 t_epoch = drms_keyword_getdouble(drms_keyword_epochfromslot(pkey), &status);
748 if (status) DIE("problem getting t_epoch");
749 }
750 }
751 else
752 DIE("Must have time prime key");
753
754 // Get cadence for output data. The output series should be slotted on a multiple of
755 // the input series, multiple may be 1.
756
757 if (strcasecmp(cadence_str, "NOTSPECIFIED") == 0)
758 cadence = t_step;
759 else
760 {
761 int ratio;
762 double initial_cadence;
763 initial_cadence = atoinc((char *)cadence_str);
764 ratio = round(initial_cadence/t_step);
765 cadence = ratio * t_step;
766 if (cadence != initial_cadence)
767 fprintf(stderr,"Cadence rounded from %f to %f, since t_step == %f\n", initial_cadence, cadence, t_step);
768 }
769
770 // Finally, get input recordset spec
771 if (lbracket && times_source == TIMES_RECSET) // RecordSet query specified
772 strncpy(in, inparam, DRMS_MAXQUERYLEN);
773 else // need to generate query from limit times
774 {
775 char t_start_text[100], t_stop_text[100], cadence_text[100];
776 if (cadence > t_step)
777 {
778 sprintf(cadence_text,"@%0.0fs", cadence);
779 if (times_source == TIMES_IMPLICIT) // round start and stop to cadence slots for auto limits
780 {
781 int nsteps;
782 nsteps = round((t_start - t_epoch)/cadence);
783 t_start = t_epoch + nsteps * cadence;
784 nsteps = round((t_stop - t_epoch)/cadence);
785 t_stop = t_epoch + nsteps * cadence;
786 }
787 }
788 else
789 cadence_text[0] = '\0';
790 sprint_at(t_start_text, t_start);
791 sprint_at(t_stop_text, t_stop);
792 sprintf(in, "%s[%s-%s%s]%s%s", inseries, t_start_text, t_stop_text, cadence_text, where, (moreQuery ? moreQuery : ""));
793 }
794
795 if (inparam)
796 {
797 free(inparam);
798 inparam = NULL;
799 }
800
801 // Check for specified epoch or cadence specified and not compact slotted series, replace query if needed
802 if (cmdparams_exists(&cmdparams, "epoch") || (cadence > 1.0 &&
803 (strncmp(inseries, "aia.lev1", 8)==0 || strncmp(inseries, "hmi.lev1", 8)==0 )))
804 { // special case for AIA and HMI lev1 slots
805 char *new_in;
806 new_in = get_input_recset(drms_env, in);
807 if (new_in != in)
808 {
809 strcpy(in, new_in);
810 in_filename = new_in+1;
811 }
812 }
813
814 // XXXXXXXXXXXXX End of get input and output series information
815
816 // XXXXXXXXXXXXX Now read data and extract boxes.
817
818 inRS = drms_open_records(drms_env, in, &status);
819 if (status || inRS->n == 0)
820 {
821 fprintf(stderr,"Query is: %s\n",in);
822 DIE("No input data found");
823 }
824 nrecs = inRS->n;
825
826 // extract patches from each record
827 int nextRec = 0;
828 for (irec = 0; irec < nrecs; irec ++)
829 {
830 double use_bzero, use_bscale;
831 char history[4096];
832
833 nextRec = 0;
834
835 *history = '\0';
836 inRec = inRS->records[irec];
837 if (status || !inRec) DIE("Record read failed.");
838
839
840 TIME trec = drms_getkey_time(inRec, timekeyname, &status); TEST_PARAM(timekeyname);
841 if (t_start != tNotSpecified && trec < t_start)
842 {
843 continue;
844 }
845 if (t_stop != tNotSpecified && trec > t_stop)
846 break;
847 // skip records without images.
848 if (drms_keyword_lookup(inRec, "QUALITY", 1))
849 {
850 int quality = drms_getkey_int(inRec, "QUALITY", &status);
851 if (quality < 0)
852 {
853 continue;
854 }
855 }
856
857 // get venus or other transit object location
858 if (FDSfile)
859 {
860 TIME t_obs = drms_getkey_time(inRec, "T_OBS", NULL);
861 if(get_tracking_xy((char *)FDSfile, t_obs, &trackx, &tracky, &track_radius))
862 DIE("Failed to open FDS file");
863 if (VERBOSE) fprintf(stderr,"xxxx called get_tracking, trackx=%f, tracky=%f, radius=%f\n",trackx,tracky,track_radius);
864 }
865
866 // get coordinate information for this image
867 this_car_rot = drms_getkey_int(inRec, "CAR_ROT", &status); TEST_PARAM("CAR_ROT");
868 crlt_obs = drms_getkey_double(inRec, "CRLT_OBS", &status); TEST_PARAM("CRLT_OBS");
869 // crln_obs = drms_getkey_double(inRec, "CRLN_OBS", &status); TEST_PARAM("CRLN_OBS");
870 crln_obs = get_new_CRLN_OBS(inRec, &status); TEST_PARAM("CRLN_OBS");
871 crpix1 = drms_getkey_double(inRec, "CRPIX1", &status); TEST_PARAM("CRPIX1");
872 x0 = crpix1 - 1;
873 crpix2 = drms_getkey_double(inRec, "CRPIX2", &status); TEST_PARAM("CRPIX2");
874 y0 = crpix2 - 1;
875 crota = drms_getkey_double(inRec, "CROTA2", &status); TEST_PARAM("CROTA2");
876 cdelt = drms_getkey_double(inRec, "CDELT1", &status); TEST_PARAM("CDELT1");
877 pa = -crota;
878
879 int paIs180 = 0;
880 if (fabs(fabs(pa)-180.0) < 1.0)
881 {
882 paIs180 = 1;
883 }
884
885 crlt_obs_rad = Deg2Rad * crlt_obs;
886 crln_obs_rad = Deg2Rad * crln_obs;
887 pa_rad = Deg2Rad * pa;
888 sina = sin(crota*Deg2Rad);
889 cosa = cos(crota*Deg2Rad);
890
891 if (VERBOSE) fprintf(stderr,"T_OBS=%s, crpix1=%f, crpix2=%f\n", drms_getkey_string(inRec,"T_OBS", NULL), crpix1, crpix2);
892
893 // XXXXXXXXXXXXXXXXX get coordinate mapping info using keywords from first record
894 if (firstimage)
895 {
896 firstimage = 0;
897 // firstimage values for register to first image option.
898 int idx, idy;
899 idx = crpix1 + 0.5;
900 idy = crpix2 + 0.5;
901 crpix1_0 = idx;
902 crpix2_0 = idy;
903
904 if (loctype==LOCCARR)
905 {
906 tmpstr = drms_getkey_string(inRec, "CTYPE1", &status);
907 ctype1 = strdup(tmpstr); TEST_PARAM("CTYPE1");
908 if (tmpstr)
909 {
910 free(tmpstr);
911 }
912
913 tmpstr = drms_getkey_string(inRec, "CTYPE2", &status);
914 if (strcmp(ctype1, "HPLN-TAN") != 0) DIE2("CTYPE1 not HPLN-TAN as required, is: ", ctype1);
915 if (ctype1)
916 {
917 free(ctype1);
918 ctype1 = NULL;
919 }
920 ctype2 = strdup(tmpstr); TEST_PARAM("CTYPE2");
921 if (tmpstr)
922 {
923 free(tmpstr);
924 }
925
926 if (strcmp(ctype2, "HPLT-TAN") != 0) DIE2("CTYPE2 not HPLT-TAN as required, is: ", ctype2);
927 if (ctype2)
928 {
929 free(ctype2);
930 ctype2 = NULL;
931 }
932 rsun_ref = drms_getkey_double(inRec, "RSUN_REF", &status);
933 if (status) rsun_ref = 6.96e8;
934 dsun_obs = drms_getkey_double(inRec, "DSUN_OBS", &status); TEST_PARAM("DSUN_OBS");
935 rsun_rad = asin(rsun_ref/dsun_obs);
936 rsun = rsun_rad*Rad2arcsec;
937 cdelt = drms_getkey_double(inRec, "CDELT1", &status); TEST_PARAM("CDELT1");
938 // in principle use deltlong to get time to use for correcting crlt_obs, ignore for now
939 // get ur, ll coords of box at CM.
940 rsunpix = rsun/cdelt;
941 }
942
943 if (boxtype == BOXDEGREE)
944 {
945 int nx, ny;
946 double urx, ury;
947 // get corners of sample box at zero longitude wrt center.
948 sphere2img(Deg2Rad*(crlt+height/2), Deg2Rad*(width/2), crlt_obs_rad, 0.0,
949 &urx, &ury, 0.0, 0.0, rsunpix, pa_rad, 0, 0, 0, 0);
950 sphere2img(Deg2Rad*(crlt-height/2), -Deg2Rad*(width/2), crlt_obs_rad, 0.0,
951 &llx, &lly, 0.0, 0.0, rsunpix, pa_rad, 0, 0, 0, 0);
952 // llx, urx, lly, ury are image coordinates for Sun, want llx and lly
953 // on plate so need to swap if image rotated.
954 nx = urx - llx ;
955 ny = ury - lly ;
956 if (nx < 0) { nx = -nx; llx = urx; }
957 if (ny < 0) { ny = -ny; lly = ury; }
958 pixwidth = nx + 1;
959 pixheight = ny + 1;
960 }
961 else if (boxtype==BOXARCSEC)
962 {
963 int nx, ny;
964 llx = -width/(2.0*cdelt);
965 lly = -height/(2.0*cdelt);
966 pixwidth = 1 - 2 * llx;
967 pixheight = 1 - 2 * lly;
968 }
969 else /* boxtype == BOXPIXELS */
970 {
971 pixwidth = round(width);
972 pixheight = round(height);
973 llx = -pixwidth / 2.0;
974 lly = -pixheight / 2.0;
975 }
976 }
977
978 if (!NoTrack)
979 {
980 if (FDSfile)
981 {
982 center_x = PIX_X(trackx,tracky) - 1;
983 center_y = PIX_Y(trackx,tracky) - 1;
984 sprintf(history+strlen(history), "Image center tracked as per %s\n", FDSfile);
985 fprintf(stderr,"center_x=%f,center_y=%f\n",center_x,center_y);
986 }
987 else
988 sphere2img(crlt_rad, crln_rad, crlt_obs_rad, crln_obs_rad, &center_x, &center_y, x0, y0, rsunpix, pa_rad, 0, 0, 0, 0);
989 }
990 // center_x and center_y are target locations for the center of the desired patch, pixels from 0 of as-is image.
991 // note HMI has not been flipped at this point
992
993 target_x = center_x;
994 target_y = center_y;
995 int x1Orig;
996 int y1Orig;
997
998 int x1 = round(target_x + llx);
999 int y1 = round(target_y + lly);
1000 int x2 = x1 + pixwidth - 1;
1001 int y2 = y1 + pixheight - 1;
1002 if (do_register)
1003 {
1004 x1 -= register_padding;
1005 y1 -= register_padding;
1006 x2 += register_padding;
1007 y2 += register_padding;
1008 }
1009 crpix1 = 1 + x0 - x1;
1010 crpix2 = 1 + y0 - y1;
1011 if (VERBOSE) fprintf(stderr,"at box define, crpix1=%f, x0=%f, x1=%d\n",crpix1,x0,x1);
1012
1013 int start1[2] = {x1, y1};
1014 int end1[2] = {x2, y2};
1015
1016 outRS = drms_create_records(drms_env, 1, outseries, DRMS_PERMANENT, &status);
1017 if (status) {fprintf(stderr,"Output series is %s, ",outseries); DIE("Cant make outout record");}
1018 outRec = outRS->records[0];
1019
1020
1021 if (do_crop)
1022 {
1023 /* BEFORE SEG LOOP */
1024 int retStat;
1025 dsun_obs = drms_getkey_double(inRec, "DSUN_OBS", &retStat);
1026 rsun_rad = asin(rsun_ref/dsun_obs);
1027 rsun = rsun_rad*Rad2arcsec/cdelt;
1028 r2 = rsun*rsun;
1029 r2 *= 0.9995;
1030
1031 /* WE HAVE TO CALCULATE this_x0 and this_y0 now, before paIs180 modifies crpix1 and crpix2. */
1032 this_x0 = crpix1 - 1;
1033 this_y0 = crpix2 - 1;
1034 }
1035
1036 /* We need to adjust pa and crota if |pa| == 180. Originally, this was done in code that was mixed with
1037 * per-segment code. But now that we made a per-segment loop, we do not want to execute this code more than
1038 * once. So I moved it here, right before the segment loop. */
1039
1040 /* These might be modified in the paIs180 block of code, but we need to use the original values
1041 * later. */
1042 x1Orig = x1;
1043 y1Orig = y1;
1044
1045 if (paIs180)
1046 {
1047 pa -= 180.0; /* This actually does not get used again during the rest of the record loop, and is not used by the segment loop. */
1048 crota -= 180.0;
1049
1050 crpix1 = 1 + x2 - x0;
1051 crpix2 = 1 + y2 - y0;
1052 if (VERBOSE) fprintf(stderr,"after rotate, crpix1=%f\n",crpix1);
1053
1054 // adjust internal quantities for after the flip, may be needed by do_register.
1055 /* But we will need the original values of x1 and y1 in the code that handles the patch-image intersection.
1056 * These quantities are saved in x1orig and y1orig.
1057 */
1058 x1 = inAxis[0] - 1 - x2;
1059 y1 = inAxis[1] - 1 - y2;
1060 target_x = inAxis[0] - 1 - center_x;
1061 target_y = inAxis[1] - 1 - center_y;
1062 // cosa = 1.0; sina = 0.0;
1063 strcat(history, "Image rotated 180 degrees.");
1064 if (VERBOSE) fprintf(stderr,"after flip x1=%d, target_x=%lf, y1=%d, target_y=%lf\n",x1,target_x,y1,target_y);
1065 }
1066
1067 /* At this point, all the modifications to x1, y1, target_x, and target_y have been performed,
1068 * so it is safe to calculate dx and dy. target_x, target_y are the pixel address of the part of
1069 * the image that we want to be in the center of the output array. */
1070
1071 /*
1072 * To simplify things, error-out if the input segment's patches dimensions are not equivalent.
1073 * Also, we need to calculate dx and dy, which require that we know the intersection of the
1074 * patch box with the image.
1075 */
1076 DRMS_Segment_t *firstSeg = NULL;
1077 HIterator_t *segIter = NULL;
1078 DRMS_Segment_t *orig = NULL;
1079
1080 float regShiftX = 0;
1081 float regShiftY = 0;
1082 char patchIntersection;
1083
1084 while ((inSeg = drms_record_nextseg2(inRec, &segIter, 1, &orig)) != NULL)
1085 {
1086 if (!firstSeg)
1087 {
1088 firstSeg = inSeg;
1089
1090 /* Calculate the ultimate patch dimensions. */
1091 /* We need to use the original x1 and y1, not the ones modified by paIs180. */
1092 if (x1Orig >= inSeg->axis[0] || y1Orig >= inSeg->axis[1] || x2 < 0 || y2 < 0)
1093 {
1094 // patch completely outside image
1095 patchIntersection = 'N';
1096 /* The record needs to be skipped. Downstream code will handle this. */
1097 }
1098 else if (x1Orig >= 0 && y1Orig >= 0 && x2 < inSeg->axis[0] && y2 < inSeg->axis[1])
1099 {
1100 // patch entirely in image.
1101 patchIntersection = 'F';
1102 }
1103 else
1104 {
1105 // patch partly outside image.
1106 patchIntersection = 'P';
1107 }
1108 }
1109 else
1110 {
1111 if (!hasSegList && !doingAllSegs)
1112 {
1113 /* No need to ensure post-first segment matches the first segment since we are processing only the first segment. */
1114 break;
1115 }
1116
1117 /* Ensure that both segments are of the same dimensions (both in number and size of each dimension). */
1118 int iSeg;
1119 int mismatch;
1120
1121 mismatch = 0;
1122 if (firstSeg->info->naxis == inSeg->info->naxis)
1123 {
1124 for (iSeg = 0; iSeg < firstSeg->info->naxis; iSeg++)
1125 {
1126 if (firstSeg->axis[iSeg] != inSeg->axis[iSeg])
1127 {
1128 mismatch = 1;
1129 break;
1130 }
1131 }
1132 }
1133 else
1134 {
1135 mismatch = 1;
1136 }
1137
1138 if (!mismatch)
1139 {
1140 if (firstSeg->info->protocol == DRMS_TAS && inSeg->info->protocol == DRMS_TAS)
1141 {
1142 for (iSeg = 0; iSeg < firstSeg->info->protocol; iSeg++)
1143 {
1144 if (firstSeg->blocksize[iSeg] != inSeg->blocksize[iSeg])
1145 {
1146 mismatch = 1;
1147 break;
1148 }
1149 }
1150 }
1151 }
1152
1153 if (mismatch)
1154 {
1155 DIE("When processing more than one segment, all segments' dimensions must match.");
1156 }
1157 }
1158 } /* Not the real segment loop. */
1159
1160 if (segIter)
1161 {
1162 hiter_destroy(&segIter);
1163 }
1164
1165 drms_copykeys(outRec, inRec, 0, kDRMS_KeyClass_Explicit);
1166 drms_sprint_rec_query(inQuery, inRec);
1167 drms_setkey_string(outRec, "SOURCE", inQuery);
1168 drms_appendhistory(outRec, inQuery, 1);
1169 drms_setkey_time(outRec, "T_REC", trec);
1170 drms_setkey_double(outRec, "CRVAL1", 0.0);
1171 drms_setkey_double(outRec, "CRVAL2", 0.0);
1172 drms_setkey_double(outRec, "CRDELT1", cdelt);
1173 drms_setkey_double(outRec, "CRDELT2", cdelt);
1174
1175 if (do_register)
1176 {
1177 /* It used to be the case that in the per-segment code, if do_register was set, then
1178 * crota got set to 0.0, and they the crota2 keyword got assigned that value.
1179 * But now that we have a segment loop, we cannot set crota to 0.0 in the per-segment
1180 * code, otherwise we lose the value of crota needed by the second and greater
1181 * segment. */
1182 drms_setkey_double(outRec, "CROTA2", 0.0);
1183 }
1184 else
1185 {
1186 drms_setkey_double(outRec, "CROTA2", crota);
1187 }
1188
1189 drms_setkey_string(outRec, "CONTENT", "Tracked Extracted Patches, made by im_patch");
1190 drms_appendcomment(outRec, "Patches", 1);
1191 drms_setkey_string(outRec, "RequestID", requestid);
1192 drms_setkey_string(outRec, "HGBOXUNITS", boxunits);
1193 drms_setkey_string(outRec, "HGLOCUNITS", locunits);
1194 drms_setkey_float(outRec, "HGWIDE", width);
1195 drms_setkey_float(outRec, "HGHIGH", height);
1196 drms_setkey_float(outRec, "HGCRLN", crln);
1197 drms_setkey_float(outRec, "HGCRLT", crlt);
1198 drms_setkey_int(outRec, "HGCARROT", car_rot);
1199 drms_setkey_time(outRec, "HGTSTART", t_start);
1200 drms_setkey_time(outRec, "HGTSTOP", t_stop);
1201 drms_setkey_time(outRec, "DATE", time(0) + UNIX_EPOCH);
1202 drms_setkey_string(outRec, "HGQUERY", in);
1203 drms_setkey_double(outRec, "CRLN_OBS", crln_obs);
1204 update_CALVERxx(inRec, outRec, 1<<28);
1205 if (VERBOSE) fprintf(stderr,"DATAMIN=%f, DATAMAX=%f\n",drms_getkey_float(outRec,"DATAMIN",NULL),drms_getkey_float(outRec,"DATAMAX",NULL));
1206
1207 /*
1208 * writing the extracted region data file
1209 */
1210 /* Make sure the output series is compatible with the input series.
1211 * The input series and output series must have the same-named segments in
1212 * the same order, with one exception. If there is only a single segment in
1213 * the input series and only a single segment in the output segment, then
1214 * the names can differ. In that case, this code will assume that the output
1215 * patch will go in the single output series.
1216 */
1217 int numInSegs = hcon_size(&(inTemplate->segments));
1218 int numOutSegs = hcon_size(&(outTemplate->segments));
1219
1220 if (numInSegs != numOutSegs)
1221 {
1222 DIE("Input and output series are incompatible (the number of segments differs).\n");
1223 }
1224
1225 if (numInSegs < 1)
1226 {
1227 DIE("The input series must have at least one segment.\n");
1228 }
1229
1230 /* inSeg -
1231 * The third argument to drms_record_nextseg2() indicates that a link should be followed.
1232 *
1233 */
1234 int iSeg = 0;
1235 char msgBuf[512];
1236
1237 while ((inSeg = drms_record_nextseg2(inRec, &segIter, 1, &orig)) != NULL)
1238 {
1239 /* THIS IS THE REAL SEGMENT LOOP. */
1240 if (!hasSegList && !doingAllSegs && iSeg > 0)
1241 {
1242 /* We've already processed the first segment, and there is no seglist specifier, so we are not processing segments other
1243 * than the first one. */
1244 break;
1245 }
1246
1247 /* Pin the first segment that meets these conditions:
1248 * + The segment protocol is either FITS or TAS.
1249 * + NAXIS == 2.
1250 * + CTYPE1 == CTYPE2 == 'HPLN-TAN' (but the keyword-reading code
1251 * already rejects records for which this isn't true).
1252 * + The existence of these keywords for the segment:
1253 * CAR_ROT
1254 * CRLT_OBS
1255 * CRLN_OBS
1256 * CRPIX1
1257 * CRPIX2
1258 * CRVAL1
1259 * CRVAL2
1260 * CROTA2
1261 * CTYPE1
1262 * CTYPE2
1263 * DSUN_OBS
1264 * CDELT1
1265 * T_REC or T_OBS
1266 * (but the keyword-reading code already rejects records for which
1267 * this isn't true).
1268 *
1269 * Once a segment is pinned, then all following segments must have WCS
1270 * keyword values that match the WCS keyword values of the pinned segment.
1271 * However, since this module rejects series with per-segment WCS keywords,
1272 * this will also already be true.
1273 *
1274 * So as long as the current segment has NAXIS == 2 and is a FITS or TAS segment,
1275 * then we can process it, even if it is a linked segment.
1276 */
1277
1278 /* The input series and output series must have the same-named segments in
1279 * the same order, with one exception. If there is only a single segment in
1280 * the input series and only a single segment in the output segment, then
1281 * the names can differ. In that case, this code will assume that the output
1282 * patch will go in the single output series.
1283 *
1284 * A check for the two series having the same number of segments was already performed,
1285 * before the segment loop.
1286 *
1287 * Must use orig, not inSeg, since inSeg could be a followed link.
1288 */
1289 outSeg = drms_segment_lookupnum(outRec, orig->info->segnum);
1290
1291 if (outSeg == NULL || (numInSegs != 1 && strncasecmp(orig->info->name, outSeg->info->name, DRMS_MAXSEGNAMELEN - 1) != 0))
1292 {
1293 /* Incompatible output series. */
1294 snprintf(msgBuf, sizeof(msgBuf), "Input and output series are incompatible (there is no segment named %s in the output series).\n", orig->info->name);
1295 DIE(msgBuf);
1296 }
1297
1298 if (inSeg->info->naxis != 2 || (inSeg->info->protocol != DRMS_FITZ && inSeg->info->protocol != DRMS_FITS && inSeg->info->protocol != DRMS_TAS))
1299 {
1300 /* The input series' segment must be a 2D FITS-type of segment. */
1301 snprintf(msgBuf, sizeof(msgBuf), "Input segment %s is not a 2D FITS segment.\n", orig->info->name);
1302 }
1303
1304 inAxis[0] = inSeg->axis[0];
1305 inAxis[1] = inSeg->axis[1];
1306
1307 /* We need to use the original x1 and y1, not the ones modified in paIs180. */
1308 if (patchIntersection == 'N')
1309 { // patch completely outside image
1310 fprintf(stderr, "patch completely outside image, x1=%d, y1=%d, x2=%d, y2=%d\n", x1Orig, y1Orig, x2, y2);
1311 /* If the patch is completely outside of one image, it is completely outside all images
1312 * since all images must be of the same dimensions. So break out of segment loop. */
1313 nextRec = 1;
1314 break;
1315 }
1316 else if (patchIntersection == 'F')
1317 { // patch entirely in image.
1318 status = 0;
1319 outArray = drms_segment_readslice(inSeg, DRMS_TYPE_FLOAT, start1, end1, &status);
1320 if (status) DIE("Cant read input record");
1321 if (VERBOSE) fprintf(stderr,"$$$$$$$ outArray bzero, bscale are %f, %f\n", outArray->bzero, outArray->bscale);
1322 }
1323 else
1324 { // patch partly outside image.
1325 int dims[2] = {x2-x1Orig+1,y2-y1Orig+1};
1326 int start2[2], end2[2];
1327
1328 int x,y;
1329 outArray = drms_array_create(DRMS_TYPE_FLOAT, 2, dims, NULL, &status);
1330 drms_array2missing(outArray);
1331
1332 start2[0] = x1Orig < 0 ? 0 : x1Orig;
1333 start2[1] = y1Orig < 0 ? 0 : y1Orig;
1334 end2[0] = x2 >= inAxis[0] ? inAxis[0]-1 : x2;
1335 end2[1] = y2 >= inAxis[1] ? inAxis[1]-1 : y2;
1336
1337 status = 0;
1338 inArray = drms_segment_readslice(inSeg, DRMS_TYPE_FLOAT, start2, end2, &status);
1339 if (status || !inArray) DIE("Cant read input record");
1340
1341 //fprintf(stderr,"$$$$$$$ inArray bzero, bscale are %f, %f\n",inArray->bzero, inArray->bscale);
1342 outArray->bzero = inArray->bzero;
1343 outArray->bscale = inArray->bscale;
1344
1345 int start3[2] = {start2[0]-start1[0],start2[1]-start1[1]}; // fetched slice offset in outarray
1346 int end3[2] = {end2[0]-end1[0],end2[1]-end1[1]}; // fetched slice offset in outarray
1347 int n3x = end2[0] - start2[0] + 1;
1348 int n3y = end2[1] - start2[1] + 1;
1349
1350 for (y=0; y< n3y; y++)
1351 {
1352 for (x=0; x < n3x; x++)
1353 {
1354 *((float *)outArray->data + ((start3[1]+y)*dims[0] + x + start3[0])) =
1355 *((float *)inArray->data + (y*n3x + x));
1356 }
1357 }
1358
1359 drms_free_array(inArray);
1360 }
1361
1362 use_bzero = outArray->bzero;
1363 use_bscale = outArray->bscale;
1364
1365 // Handle crop if wanted
1366 if (do_crop)
1367 {
1368 int stat;
1369 // rsun_ref and cdelt defined above
1370 float *data = (float *)outArray->data;
1371 int i,j;
1372 int nx = outArray->axis[0];
1373 int ny = outArray->axis[1];
1374
1375 for (j=0; j<ny; j++)
1376 {
1377 for (i=0; i<nx; i++)
1378 {
1379 double x = i - this_x0;
1380 double y = j - this_y0;
1381
1382 if ((x*x + y*y) >= r2)
1383 {
1384 data[j*nx + i] = DRMS_MISSING_FLOAT;
1385 }
1386 }
1387 }
1388 }
1389
1390 // Always handle special case where |pa| == 180
1391 if (paIs180)
1392 {
1393 // Tracking should be OK for center, but patch rotated. Flip on both axes.
1394 float *data = (float *)outArray->data;
1395 int i,j;
1396 int nx = outArray->axis[0];
1397 int ny = outArray->axis[1];
1398 int midrow = (ny)/2;
1399 int midcol = (nx)/2;
1400 float val;
1401
1402 for (j=0; j<midrow; j++)
1403 {
1404 for (i=0; i<nx; i++)
1405 {
1406 val = data[j*nx + i];
1407 data[j*nx + i] = data[(ny - 1 - j)*nx + nx - 1 - i];
1408 data[(ny - 1 - j)*nx + nx - 1 - i] = val;
1409 }
1410 }
1411
1412 if ((ny & 1) != 0)
1413 {
1414 for (i=0; i<midcol; i++)
1415 {
1416 val = data[midrow*nx + i];
1417 data[midrow*nx + i] = data[midrow*nx + nx - 1 - i];
1418 data[midrow*nx + nx - 1 - i] = val;
1419 }
1420 }
1421 }
1422
1423 /*
1424 * Register fraction of pixel to target location, if desired
1425 */
1426 if (do_register)
1427 {
1428 /* IN SEG LOOP */
1429 DRMS_Array_t *tmpArray;
1430 int status;
1431 int i,j;
1432 float *data = (float *)outArray->data;
1433 float *newdata = NULL;
1434 int newnx, newny;
1435 int nx = outArray->axis[0];
1436 int ny = outArray->axis[1];
1437 float midx = (nx-1)/2.0;
1438 float midy = (ny-1)/2.0;
1439 // x1+midx is the data pixel that was just placed at the center of the output array.
1440 // target_x is the pixel that contains the data we want to have in the center of the output array.
1441 float dx = (x1 + midx) - target_x;
1442 float dy = (y1 + midy) - target_y;
1443
1444 /* We do not want to adjust crpix1 and crpix2 more than once per record. */
1445 if (iSeg == 0)
1446 {
1447 sprintf(history+strlen(history), "\nImage registered by shift of (%0.3f,%0.3f) pixels.", dx, dy);
1448 crpix1 += dx - register_padding;
1449 crpix2 += dy - register_padding;
1450 }
1451
1452 regShiftX = dx;
1453 regShiftY = dy;
1454
1455 int dtyp = 3;
1456
1457 if (status=image_magrotate((void *)data, nx, ny, dtyp, crota, 1.0, regShiftX, regShiftY, &(void *)newdata, &newnx, &newny, 1, 0))
1458 {
1459 DIE("XXXX Failure in call to image_magrotate. Either malloc error or nx or ny too large. Must quit.\n");
1460 }
1461
1462 if (newnx != nx || newny != ny)
1463 {
1464 fprintf(stderr,"image_magrotate changed dimensions: nx want %d got %d, ny want %d got %d\n",nx,newnx,ny,newny);
1465 }
1466
1467 drms_free_array(outArray);
1468 int outdims[2] = {pixwidth, pixheight};
1469 outArray = drms_array_create(DRMS_TYPE_FLOAT, 2, outdims, NULL, &status);
1470 data = (float *)outArray->data;
1471 for (j=0; j<pixheight; j++)
1472 {
1473 for (i=0; i< pixwidth; i++)
1474 {
1475 data[j*pixwidth + i] = newdata[(j+register_padding)*nx + i+register_padding];
1476 }
1477 }
1478
1479 /* This was being leaked - over 4MB per call. */
1480 if (newdata)
1481 {
1482 free(newdata);
1483 newdata = NULL;
1484 }
1485
1486 outArray->bzero = use_bzero;
1487 outArray->bscale = use_bscale;
1488 }
1489
1490 set_statistics(outSeg, outArray, 1); // do this before adding possible FAKE image
1491
1492 if (wantFAKE && FDSfile)
1493 {
1494 // fprintf(stderr,"XXXX in wantFAKE and FDSfile\n");
1495 double r;
1496 int ix0, iy0;
1497 int nx = outArray->axis[0];
1498 int ny = outArray->axis[1];
1499 double x0, y0, r2;
1500 float *data = (float *)outArray->data;
1501 float datamax = drms_getkey_float(outRec, "DATAMAX", NULL);
1502 int ix,iy,ir,ir2;
1503
1504 if (NoTrack)
1505 {
1506 x0 = trackx/cdelt + crpix1;
1507 y0 = tracky/cdelt + crpix2;
1508 }
1509 else
1510 {
1511 x0 = nx/2.0;
1512 y0 = ny/2.0;
1513 }
1514
1515 if (VERBOSE) fprintf(stderr,"XXXXx x0=%f, yo=%f\n",x0,y0);
1516 ix0 = x0+0.5;
1517 iy0 = y0+0.5;
1518 // r = 28.9/cdelt; // venus radius in pixels
1519 r = track_radius/cdelt; // venus radius in pixels
1520 ir = r + 0.5;
1521 r2 = r*r;
1522
1523 for (ix = -ir; ix <= ir; ix++)
1524 {
1525 for (iy = -ir; iy <= ir; iy++)
1526 {
1527 x = x0 - ix0 + ix;
1528 y = y0 - iy0 + iy;
1529 if (x*x + y*y < r2 && ix0+ix >=0 && ix0+ix < nx && iy0+iy >=0 && iy0+iy < ny)
1530 {
1531 if (wantFAKEwhite)
1532 {
1533 data[(iy+iy0)*nx + (ix+ix0)] = datamax;
1534 }
1535 else
1536 {
1537 if (ix==0 || iy==0)
1538 {
1539 data[(iy+iy0)*nx + (ix+ix0)] = datamax;
1540 }
1541 else
1542 {
1543 data[(iy+iy0)*nx + (ix+ix0)] = 0.0;
1544 }
1545 }
1546 }
1547 }
1548 }
1549 }
1550
1551 if (iSeg == 0)
1552 {
1553 drms_setkey_double(outRec, "CRPIX1", crpix1);
1554 drms_setkey_double(outRec, "CRPIX2", crpix2);
1555 }
1556
1557 if (export_keys)
1558 {
1559 status = drms_segment_writewithkeys(outSeg, outArray, 0);
1560 }
1561 else
1562 {
1563 status = drms_segment_write(outSeg, outArray, 0);
1564 }
1565
1566 if (status)
1567 {
1568 DIE("problem writing file");
1569 }
1570
1571 if (outArray)
1572 {
1573 drms_free_array(outArray);
1574 outArray = NULL;
1575 }
1576
1577 iSeg++;
1578 } /* end segment loop */
1579
1580 if (segIter)
1581 {
1582 hiter_destroy(&segIter);
1583 }
1584
1585 if (log)
1586 {
1587 drms_fprint_rec_query(log, outRec);
1588 fprintf(log, "\n");
1589 }
1590
1591 if (*history)
1592 drms_appendhistory(outRec, history, 1);
1593
1594 if (nextRec)
1595 {
1596 drms_close_records(outRS, DRMS_FREE_RECORD);
1597 continue;
1598 }
1599 else
1600 {
1601 drms_close_records(outRS, DRMS_INSERT_RECORD);
1602 drms_free_array(outArray);
1603 }
1604
1605 OK_recs += 1;
1606 } /* end record loop */
1607
1608 drms_close_records(inRS, DRMS_FREE_RECORD);
1609 if (in_filename)
1610 {
1611 unlink(in_filename);
1612 }
1613
1614 if (OK_recs)
1615 printf(" DONE, %d records made! \n \n",OK_recs);
1616 else
1617 printf(" DONE but no useful records created! \n \n");
1618
1619 if (log)
1620 fclose(log);
1621 return DRMS_SUCCESS;
1622 } //
1623
1624 /*
1625 *
1626 * FUNCTIONS
1627 *
1628 * img2sphere()
1629 *
1630 * Written: Rick Bogart
1631 *
1632 */
1633 int img2sphere (double x, double y, double ang_r, double latc, double lonc,
1634 double pa, double *p_rho, double *p_lat, double *p_lon, double *p_sinlat,
1635 double *p_coslat, double *p_sig, double *p_mu, double *p_chi)
1636 {
1637 /*
1638 * Taken from cartography.c, written by Rick Bogart
1639 * Map projected coordinates (x, y) to (lon, lat) and (rho | sig, chi)
1640 *
1641 * Arguments:
1642 * x } Plate locations, in same units as the image radius, relative
1643 * y } to the image center
1644 * ang_r Apparent semi-diameter of sun (angular radius of sun at
1645 * the observer's tangent line)
1646 * latc Latitude of disc center, uncorrected for light travel time
1647 * lonc Longitude of disc center
1648 * pa Position angle of solar north on image, measured eastward
1649 * from north (sky coordinates)
1650 * Return values:
1651 * rho Angle point:sun_center:observer
1652 * lon Heliographic longitude
1653 * lat Heliographic latitude
1654 * sinlat sine of heliographic latitude
1655 * coslat cosine of heliographic latitude
1656 * sig Angle point:observer:sun_center
1657 * mu cosine of angle between the point:observer line and the
1658 * local normal
1659 * chi Position angle on image measured westward from solar
1660 * north
1661 *
1662 * All angles are in radians.
1663 * Return value is 1 if point is outside solar radius (in which case the
1664 * heliographic coordinates and mu are meaningless), 0 otherwise.
1665 * It is assumed that the image is direct; the x or y coordinates require a
1666 * sign change if the image is inverted.
1667 *
1668 * modified to ignore setting return values for NULL pointers.
1669 *
1670 */
1671
1672
1673 double w_rho, w_lat, w_lon, w_sinlat, w_coslat, w_sig, w_mu, w_chi;
1674 double *rho= &w_rho, *lat= &w_lat, *lon= &w_lon, *sinlat= &w_sinlat, *coslat= &w_coslat, *sig= &w_sig, *mu= &w_mu, *chi= &w_chi;
1675 static double ang_r0 = 0.0, sinang_r = 0.0, tanang_r = 0.0;
1676 static double latc0 = 0.0, coslatc = 1.0, sinlatc = 0.0;
1677 double cosr, sinr, sinlon, sinsig;
1678
1679 if (p_rho) rho=p_rho;
1680 if (p_lat) lat=p_lat;
1681 if (p_lon) lon=p_lon;
1682 if (p_sinlat) sinlat=p_sinlat;
1683 if (p_coslat) coslat=p_coslat;
1684 if (p_sig) sig=p_sig;
1685 if (p_mu) mu=p_mu;
1686 if (p_chi) chi=p_chi;
1687
1688 if (ang_r != ang_r0) {
1689 sinang_r = sin(ang_r);
1690 tanang_r = tan(ang_r);
1691 ang_r0 = ang_r;
1692 }
1693
1694 if (latc != latc0) {
1695 sinlatc = sin(latc);
1696 coslatc = cos(latc);
1697 latc0 = latc;
1698 }
1699
1700 // position angle
1701 *chi = atan2 (x, y) + pa;
1702 while (*chi > 2 * M_PI) *chi -= 2 * M_PI;
1703 while (*chi < 0.0) *chi += 2 * M_PI;
1704 /* Camera curvature correction, no small angle approximations */
1705 *sig = atan (hypot (x, y) * tanang_r);
1706 sinsig = sin (*sig);
1707 *rho = asin (sinsig / sinang_r) - *sig;
1708 if (*sig > ang_r) return (-1);
1709 *mu = cos (*rho + *sig);
1710 sinr = sin (*rho);
1711 cosr = cos (*rho);
1712 *sinlat = sinlatc * cosr + coslatc * sinr * cos (*chi);
1713 *coslat = sqrt (1.0 - *sinlat * *sinlat);
1714 *lat = asin (*sinlat);
1715
1716 sinlon = sinr * sin (*chi) / *coslat;
1717 *lon = asin (sinlon);
1718 if (cosr < (*sinlat * sinlatc)) *lon = M_PI - *lon;
1719 *lon += lonc;
1720 while (*lon < 0.0) *lon += 2 * M_PI;
1721 while (*lon >= 2 * M_PI) *lon -= 2 * M_PI;
1722 return (0);
1723 }
1724 /*
1725 *
1726 * sphere2img()
1727 *
1728 * Written: Rick Bogart
1729 *
1730 */
1731
1732 int sphere2img (double lat, double lon, double latc, double lonc,
1733 double *x, double *y, double xcenter, double ycenter,
1734 double rsun, double peff, double ecc, double chi,
1735 int xinvrt, int yinvrt) {
1736 /*
1737 * Taken from cartography.c, written by Rick Bogart
1738 * Perform a mapping from heliographic coordinates latitude and longitude
1739 * (in radians) to plate location on an image of the sun. The plate
1740 * location is in units of the image radius and is given relative to
1741 * the image center. The function returns 1 if the requested point is
1742 * on the far side (>90 deg from disc center), 0 otherwise.
1743 *
1744 * Arguments:
1745 * lat Latitude (in radians)
1746 * lon Longitude (in radians)
1747 * latc Heliographic latitude of the disc center (in radians)
1748 * lonc Heliographic longitude of the disc center (in radians)
1749 * *x } Plate locations, in same units as the image radius, NOT relative
1750 * *y } to the image center
1751 * xcenter } Plate locations of the image center, in units of the
1752 * ycenter } image radius, and measured from an arbitrary origin
1753 * (presumably the plate center or a corner)
1754 * rsun Apparent semi-diameter of the solar disc, in plate
1755 * coordinates
1756 * peff Position angle of the heliographic pole, measured
1757 * eastward from north, relative to the north direction
1758 * on the plate, in radians
1759 * ecc Eccentricity of the fit ellipse presumed due to image
1760 * distortion (no distortion in direction of major axis)
1761 * chi Position angle of the major axis of the fit ellipse,
1762 * measure eastward from north, relative to the north
1763 * direction on the plate, in radians (ignored if ecc = 0)
1764 * xinvrt} Flag parameters: if not equal to 0, the respective
1765 * yinvrt} coordinates on the image x and y are inverted
1766 *
1767 * The heliographic coordinates are first mapped into the polar coordinates
1768 * in an orthographic projection centered at the appropriate location and
1769 * oriented with north in direction of increasing y and west in direction
1770 * of increasing x. The radial coordinate is corrected for foreshortening
1771 * due to the finite distance to the Sun. If the eccentricity of the fit
1772 * ellipse is non-zero the coordinate of the mapped point is proportionately
1773 * reduced in the direction parallel to the minor axis.
1774 *
1775 * Bugs:
1776 * The finite distance correction uses a fixed apparent semi-diameter
1777 * of 16'01'' appropriate to 1.0 AU. In principle the plate radius could
1778 * be used, but this would require the plate scale to be supplied and the
1779 * correction would probably be erroneous and in any case negligible.
1780 *
1781 * The ellipsoidal correction has not been tested very thoroughly.
1782 *
1783 * The return value is based on a test which does not take foreshortening
1784 * into account.
1785 */
1786 static double sin_asd = 0.004660, cos_asd = 0.99998914;
1787 /* appropriate to 1 AU */
1788 static double last_latc = 0.0, cos_latc = 1.0, sin_latc = 0.0;
1789 double r, cos_cang, xr, yr;
1790 double sin_lat, cos_lat, cos_lat_lon, cospa, sinpa;
1791 double squash, cchi, schi, c2chi, s2chi, xp, yp;
1792 int hemisphere;
1793
1794 if (latc != last_latc) {
1795 sin_latc = sin (latc);
1796 cos_latc = cos (latc);
1797 last_latc = latc;
1798 }
1799 sin_lat = sin (lat);
1800 cos_lat = cos (lat);
1801 cos_lat_lon = cos_lat * cos (lon - lonc);
1802
1803 cos_cang = sin_lat * sin_latc + cos_latc * cos_lat_lon;
1804 hemisphere = (cos_cang < 0.0) ? 1 : 0;
1805 r = rsun * cos_asd / (1.0 - cos_cang * sin_asd);
1806 xr = r * cos_lat * sin (lon - lonc);
1807 yr = r * (sin_lat * cos_latc - sin_latc * cos_lat_lon);
1808 /* Change sign for inverted images */
1809 if (xinvrt) xr *= -1.0;
1810 if (yinvrt) yr *= -1.0;
1811 /* Correction for ellipsoidal squashing of image */
1812 if (ecc > 0.0 && ecc < 1.0) {
1813 squash = sqrt (1.0 - ecc * ecc);
1814 cchi = cos (chi);
1815 schi = sin (chi);
1816 s2chi = schi * schi;
1817 c2chi = 1.0 - s2chi;
1818 xp = xr * (s2chi + squash * c2chi) - yr * (1.0 - squash) * schi * cchi;
1819 yp = yr * (c2chi + squash * s2chi) - xr * (1.0 - squash) * schi * cchi;
1820 xr = xp;
1821 yr = yp;
1822 }
1823
1824 cospa = cos (peff);
1825 sinpa = sin (peff);
1826 *x = xr * cospa - yr * sinpa;
1827 *y = xr * sinpa + yr * cospa;
1828
1829 *y += ycenter;
1830 *x += xcenter;
1831
1832 return (hemisphere);
1833 }
1834
1835 // In cases known to not have compact slotted series and cadence is specified
1836 // generate explicit recordset list of closest good record to desired grid
1837 // First get vector of times and quality
1838 // Then if vector is not OK, quit.
1839 // then: make temp file to hold recordset list
1840 // start with first time to define desired grid,
1841 // make array of desired times.
1842 // make empty array of recnums
1843 // search vector for good images nearest desired times
1844 // for each found time, write record query
1845
1846
1847 #define DIE_get_recset(msg) {fprintf(stderr,"$$$$ %s: %s\n", module_name, msg); return NULL;}
1848 char *get_input_recset(DRMS_Env_t *drms_env, char *inQuery)
1849 {
1850 static char newInQuery[DRMS_MAXSERIESNAMELEN+2];
1851 int epoch_given = cmdparams_exists(&cmdparams, "epoch");
1852 TIME epoch, t_epoch;
1853 DRMS_Array_t *data;
1854 DRMS_Record_t *inTemplate;
1855 TIME t_start, t_stop, t_now, t_want, t_diff, this_t_diff;
1856 int status = 1;
1857 int nrecs, irec;
1858 int nslots, islot;
1859 long long *recnums;
1860 TIME *t_this, half;
1861 TIME cadence;
1862 double *drecnum, *dquality;
1863 int quality;
1864 long long recnum;
1865 char keylist[DRMS_MAXQUERYLEN];
1866 char filename[DRMS_MAXSERIESNAMELEN];
1867 char *tmpdir;
1868 FILE *tmpfile;
1869 char newIn[DRMS_MAXQUERYLEN];
1870 char seriesname[DRMS_MAXQUERYLEN];
1871 char *lbracket;
1872 char *at = index(inQuery, '@');
1873 int npkeys;
1874 char *timekeyname;
1875 double t_step;
1876
1877 strcpy(seriesname, inQuery);
1878 lbracket = index(seriesname,'[');
1879 if (lbracket) *lbracket = '\0';
1880 inTemplate = drms_template_record(drms_env, seriesname, &status);
1881 if (status || !inTemplate) DIE_get_recset("Input series can not be found");
1882
1883 // Now find the prime time keyword name
1884 npkeys = inTemplate->seriesinfo->pidx_num;
1885 timekeyname = NULL;
1886 if (npkeys > 0)
1887 {
1888 int i;
1889 for (i=0; i<npkeys; i++)
1890 {
1891 DRMS_Keyword_t *pkey, *skey;
1892 pkey = inTemplate->seriesinfo->pidx_keywords[i];
1893 if (pkey->info->recscope > 1)
1894 pkey = drms_keyword_slotfromindex(pkey);
1895 if (pkey->info->type != DRMS_TYPE_TIME)
1896 continue;
1897 if(i > 0) DIE_get_recset("Input series must have TIME keyword first, for now...");
1898 timekeyname = pkey->info->name;
1899 t_step = drms_keyword_getdouble(drms_keyword_stepfromslot(pkey), &status);
1900 if (status) DIE_get_recset("problem getting t_step");
1901 t_epoch = drms_keyword_getdouble(drms_keyword_epochfromslot(pkey), &status);
1902 if (status) DIE_get_recset("problem getting t_epoch");
1903 }
1904 }
1905 else
1906 DIE_get_recset("Must have time prime key");
1907 epoch = epoch_given ? params_get_time(&cmdparams, "epoch") : t_epoch;
1908
1909 if (at && *at && ((strncmp(inQuery,"aia.lev1[", 9)==0 ||
1910 strncmp(inQuery,"hmi.lev1[", 9)==0 ||
1911 strncmp(inQuery,"aia.lev1_nrt2[",14)==0 ||
1912 strncmp(inQuery,"hmi.lev1_nrt[", 13)==0 ) ||
1913 epoch_given))
1914 {
1915 char *ip=(char *)inQuery, *op=newIn, *p;
1916 long n, mul;
1917 while ( *ip && ip<at )
1918 *op++ = *ip++;
1919 ip++; // skip the '@'
1920 n = strtol(ip, &p, 10); // get digits only
1921 if (*p == 's') mul = 1;
1922 else if (*p == 'm') mul = 60;
1923 else if (*p == 'h') mul = 3600;
1924 else if (*p == 'd') mul = 86400;
1925 else
1926 DIE_get_recset("cant make sense of @xx cadence spec");
1927 cadence = n * mul;
1928 ip = ++p; // skip cadence multiplier
1929 while ( *ip )
1930 *op++ = *ip++;
1931 *op = '\0';
1932 half = cadence/2.0;
1933 sprintf(keylist, "%s,QUALITY,recnum", timekeyname);
1934 data = drms_record_getvector(drms_env, newIn, keylist, DRMS_TYPE_DOUBLE, 0, &status);
1935 if (!data || status)
1936 {
1937 fprintf(stderr, "status=%d\n", status);
1938 DIE_get_recset("getkey_vector failed\n");
1939 }
1940 nrecs = data->axis[1];
1941 irec = 0;
1942 t_this = (TIME *)data->data;
1943 dquality = (double *)data->data + 1*nrecs;
1944 drecnum = (double *)data->data + 2*nrecs;
1945 if (epoch_given)
1946 {
1947 int s0 = (t_this[0] - epoch)/cadence;
1948 TIME t0 = s0*cadence + epoch;
1949 t_start = (t0 < t_this[0] ? t0 + cadence : t0);
1950 }
1951 else
1952 t_start = t_this[0];
1953 t_stop = t_this[nrecs-1];
1954 nslots = (t_stop - t_start + cadence/2)/cadence;
1955 recnums = (long long *)malloc(nslots*sizeof(long long));
1956 for (islot=0; islot<nslots; islot++)
1957 recnums[islot] = 0;
1958 islot = 0;
1959 t_want = t_start;
1960 t_diff = 1.0e9;
1961 for (irec = 0; irec<nrecs; irec++)
1962 {
1963 t_now = t_this[irec];
1964 quality = (int)dquality[irec] & 0xFFFFFFFF;
1965 recnum = (long long)drecnum[irec];
1966 this_t_diff = fabs(t_now - t_want);
1967 if (quality < 0)
1968 continue;
1969 if (t_now <= (t_want-half))
1970 continue;
1971 while (t_now > (t_want+half))
1972 {
1973 islot++;
1974 if (islot >= nslots)
1975 break;
1976 t_want = t_start + cadence * islot;
1977 this_t_diff = fabs(t_now - t_want);
1978 t_diff = 1.0e8;
1979 }
1980 if (islot < nslots && this_t_diff <= t_diff)
1981 recnums[islot] = recnum;
1982 t_diff = fabs(t_now - t_want);
1983 }
1984 if (islot+1 < nslots)
1985 nslots = islot+1; // take what we got.
1986 tmpdir = getenv("TMPDIR");
1987 if (!tmpdir) tmpdir = "/tmp";
1988 sprintf(filename, "%s/%sXXXXXX", tmpdir, module_name);
1989 mkstemp(filename);
1990 tmpfile = fopen(filename,"w");
1991 for (islot=0; islot<nslots; islot++)
1992 if (recnums[islot])
1993 fprintf(tmpfile, "%s[:#%lld]\n", seriesname, recnums[islot]);
1994 fclose(tmpfile);
1995 free(recnums);
1996 drms_free_array(data);
1997 sprintf(newInQuery,"@%s", filename);
1998 return(newInQuery);
1999 }
2000 else
2001 return(inQuery);
2002 }
2003
2004 // FDS file is in format of SDO FDS "transit" files.
2005 // returns xp, yp, and radius of target all in arcsec
2006 int get_tracking_xy(char *FDSfile, TIME want, double *xp, double *yp, double *radius)
2007 {
2008 int iline;
2009 char buf[200];
2010 TIME now0;
2011 FILE *data = fopen(FDSfile,"r");
2012 double lambda, phi, x, y, dist, obj_radius;
2013 double frac, x0, y0, obj_radius0;
2014 char object[100], now_txt[100];
2015 if (!data)
2016 return(1);
2017
2018 // skip first 13 lines, for lunar transit data, was 14 lines
2019 for (iline=0; iline<13; iline++)
2020 fgets(buf, 200, data);
2021 // fprintf(stderr,"line before using: %s\n",buf);
2022 // read info until end, stop at first time after want
2023 while (fscanf(data, "%s %s %lf %lf %lf %lf %lf %lf", object, now_txt, &lambda, &phi, &x, &y, &dist, &obj_radius) == 8)
2024 {
2025 char tmp_txt[100];
2026 int yyyy, doy, hh, mm, ss;
2027 int m,d;
2028 int dim[] = {31,28,31,30,31,30,31,31,30,31,30,31};
2029 double distance;
2030 TIME now;
2031 // fix coordinate system to match HMI view
2032 // not for ISON
2033 x = -x;
2034 y = -y;
2035 obj_radius *= 3600;
2036 // extract time
2037 sscanf(tmp_txt,"%4d%3d.%2d%2d%2d", &yyyy, &doy, &hh, &mm, &ss);
2038 if (yyyy % 4 == 0) dim[1] = 29; else dim[1] = 28;
2039 for (m=1; m<=12; m++)
2040 {
2041 if (doy > dim[m-1])
2042 doy -= dim[m-1];
2043 else
2044 break;
2045 }
2046 d = doy;
2047
2048 sprintf(tmp_txt, "%4d.%02d.%02d_%02d:%02d:%02d_UTC", yyyy, m, d, hh, mm, ss);
2049 now = sscan_time(tmp_txt);
2050 // report closest x,y for time want
2051 if (now < want)
2052 {
2053 now0 = now;
2054 x0 = x;
2055 y0 = y;
2056 obj_radius0 = obj_radius;
2057 }
2058 else
2059 {
2060 if (fabs(now-want) >= fabs(now0-want))
2061 {
2062 obj_radius = obj_radius0;
2063 if (now == want)
2064 {
2065 x = x0;
2066 y = y0;
2067 }
2068 else
2069 {
2070 frac = (want-now0)/(now-now0);
2071 x = x0 + (x-x0)*frac;
2072 y = y0 + (y-y0)*frac;
2073 }
2074 }
2075 break;
2076 }
2077 }
2078 fclose(data);
2079 fprintf(stderr,"Found %s, x=%f, y=%f, radius=%f (all arcsecs)\n",now_txt,x,y,obj_radius);
2080 *xp = x;
2081 *yp = y;
2082 *radius = obj_radius;
2083 return(0);
2084 }