/* * datatemplate.c * * Data template structure * * Copyright © 2019-2020 Deutsches Elektronen-Synchrotron DESY, * a research centre of the Helmholtz Association. * * Authors: * 2019-2020 Thomas White * * This file is part of CrystFEL. * * CrystFEL is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CrystFEL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with CrystFEL. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "utils.h" #include "datatemplate.h" #include "datatemplate_priv.h" /** * \file datatemplate.h */ struct rg_definition { char *name; char *pns; }; struct rgc_definition { char *name; char *rgs; }; static struct panel_template *new_panel(DataTemplate *det, const char *name, struct panel_template *defaults) { struct panel_template *new; det->n_panels++; det->panels = realloc(det->panels, det->n_panels*sizeof(struct panel_template)); new = &det->panels[det->n_panels-1]; memcpy(new, defaults, sizeof(struct panel_template)); /* Set name */ new->name = strdup(name); /* Copy strings */ new->cnz_from = safe_strdup(defaults->cnz_from); new->data = safe_strdup(defaults->data); new->mask = safe_strdup(defaults->mask); new->mask_file = safe_strdup(defaults->mask_file); new->satmap = safe_strdup(defaults->satmap); new->satmap_file = safe_strdup(defaults->satmap_file); return new; } static struct dt_badregion *new_bad_region(DataTemplate *det, const char *name) { struct dt_badregion *new; det->n_bad++; det->bad = realloc(det->bad, det->n_bad*sizeof(struct dt_badregion)); new = &det->bad[det->n_bad-1]; new->min_x = NAN; new->max_x = NAN; new->min_y = NAN; new->max_y = NAN; new->min_fs = 0; new->max_fs = 0; new->min_ss = 0; new->max_ss = 0; new->is_fsss = 99; /* Slightly nasty: means "unassigned" */ new->panel = NULL; strcpy(new->name, name); return new; } static struct panel_template *find_panel_by_name(DataTemplate *det, const char *name) { int i; for ( i=0; in_panels; i++ ) { if ( strcmp(det->panels[i].name, name) == 0 ) { return &det->panels[i]; } } return NULL; } static struct dt_badregion *find_bad_region_by_name(DataTemplate *det, const char *name) { int i; for ( i=0; in_bad; i++ ) { if ( strcmp(det->bad[i].name, name) == 0 ) { return &det->bad[i]; } } return NULL; } static struct dt_rigid_group *find_or_add_rg(DataTemplate *det, const char *name) { int i; struct dt_rigid_group **new; struct dt_rigid_group *rg; for ( i=0; in_rigid_groups; i++ ) { if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) { return det->rigid_groups[i]; } } new = realloc(det->rigid_groups, (1+det->n_rigid_groups)*sizeof(struct dt_rigid_group *)); if ( new == NULL ) return NULL; det->rigid_groups = new; rg = malloc(sizeof(struct dt_rigid_group)); if ( rg == NULL ) return NULL; det->rigid_groups[det->n_rigid_groups++] = rg; rg->name = strdup(name); rg->panels = NULL; rg->n_panels = 0; return rg; } static struct dt_rg_collection *find_or_add_rg_coll(DataTemplate *det, const char *name) { int i; struct dt_rg_collection **new; struct dt_rg_collection *rgc; for ( i=0; in_rg_collections; i++ ) { if ( strcmp(det->rigid_group_collections[i]->name, name) == 0 ) { return det->rigid_group_collections[i]; } } new = realloc(det->rigid_group_collections, (1+det->n_rg_collections)*sizeof(struct dt_rg_collection *)); if ( new == NULL ) return NULL; det->rigid_group_collections = new; rgc = malloc(sizeof(struct dt_rg_collection)); if ( rgc == NULL ) return NULL; det->rigid_group_collections[det->n_rg_collections++] = rgc; rgc->name = strdup(name); rgc->rigid_groups = NULL; rgc->n_rigid_groups = 0; return rgc; } static void add_to_rigid_group(struct dt_rigid_group *rg, struct panel_template *p) { struct panel_template **pn; pn = realloc(rg->panels, (1+rg->n_panels)*sizeof(struct panel_template *)); if ( pn == NULL ) { ERROR("Couldn't add panel to rigid group.\n"); return; } rg->panels = pn; rg->panels[rg->n_panels++] = p; } static void add_to_rigid_group_coll(struct dt_rg_collection *rgc, struct dt_rigid_group *rg) { struct dt_rigid_group **r; r = realloc(rgc->rigid_groups, (1+rgc->n_rigid_groups)* sizeof(struct dt_rigid_group *)); if ( r == NULL ) { ERROR("Couldn't add rigid group to collection.\n"); return; } rgc->rigid_groups = r; rgc->rigid_groups[rgc->n_rigid_groups++] = rg; } /* Free all rigid groups in detector */ static void free_all_rigid_groups(DataTemplate *det) { int i; if ( det->rigid_groups == NULL ) return; for ( i=0; in_rigid_groups; i++ ) { free(det->rigid_groups[i]->name); free(det->rigid_groups[i]->panels); free(det->rigid_groups[i]); } free(det->rigid_groups); } /* Free all rigid groups in detector */ static void free_all_rigid_group_collections(DataTemplate *det) { int i; if ( det->rigid_group_collections == NULL ) return; for ( i=0; in_rg_collections; i++ ) { free(det->rigid_group_collections[i]->name); free(det->rigid_group_collections[i]->rigid_groups); free(det->rigid_group_collections[i]); } free(det->rigid_group_collections); } static struct dt_rigid_group *find_rigid_group_by_name(DataTemplate *det, char *name) { int i; for ( i=0; in_rigid_groups; i++ ) { if ( strcmp(det->rigid_groups[i]->name, name) == 0 ) { return det->rigid_groups[i]; } } return NULL; } static int atob(const char *a) { if ( strcasecmp(a, "true") == 0 ) return 1; if ( strcasecmp(a, "false") == 0 ) return 0; return atoi(a); } static int assplode_algebraic(const char *a_orig, char ***pbits) { int len, i; int nexp; char **bits; char *a; int idx, istr; len = strlen(a_orig); /* Add plus at start if no sign there already */ if ( (a_orig[0] != '+') && (a_orig[0] != '-') ) { len += 1; a = malloc(len+1); snprintf(a, len+1, "+%s", a_orig); a[len] = '\0'; } else { a = strdup(a_orig); } /* Count the expressions */ nexp = 0; for ( i=0; i= 0 ) bits[idx][istr] = '\0'; idx++; bits[idx] = malloc(len+1); istr = 0; } if ( !isdigit(ch) && (ch != '.') && (ch != '+') && (ch != '-') && (ch != 'x') && (ch != 'y') && (ch != 'z') ) { ERROR("Invalid character '%c' found.\n", ch); return 0; } assert(idx >= 0); bits[idx][istr++] = ch; } if ( idx >= 0 ) bits[idx][istr] = '\0'; *pbits = bits; free(a); return nexp; } /* Parses the scan directions (accounting for possible rotation) * Assumes all white spaces have been already removed */ static int dir_conv(const char *a, double *sx, double *sy, double *sz) { int n; char **bits; int i; *sx = 0.0; *sy = 0.0; *sz = 0.0; n = assplode_algebraic(a, &bits); if ( n == 0 ) { ERROR("Invalid direction '%s'\n", a); return 1; } for ( i=0; i= MAX_DIMS ) { ERROR("Too many dimensions!\n"); return 1; } if ( strcmp(val, "fs") == 0 ) { panel->dims[dimension] = DIM_FS; } else if ( strcmp(val, "ss") == 0 ) { panel->dims[dimension] = DIM_SS; } else if ( strcmp(val, "%") == 0 ) { panel->dims[dimension] = DIM_PLACEHOLDER; } else { char *endptr; unsigned long int fix_val = strtoul(val, &endptr, 10); if ( endptr[0] != '\0' ) { ERROR("Invalid dimension value '%s'\n", val); return 1; } else { panel->dims[dimension] = fix_val; } } return 0; } static int parse_field_for_panel(struct panel_template *panel, const char *key, const char *val, DataTemplate *det) { int reject = 0; if ( strcmp(key, "min_fs") == 0 ) { panel->orig_min_fs = atof(val); } else if ( strcmp(key, "max_fs") == 0 ) { panel->orig_max_fs = atof(val); } else if ( strcmp(key, "min_ss") == 0 ) { panel->orig_min_ss = atof(val); } else if ( strcmp(key, "max_ss") == 0 ) { panel->orig_max_ss = atof(val); } else if ( strcmp(key, "corner_x") == 0 ) { panel->cnx = atof(val); } else if ( strcmp(key, "corner_y") == 0 ) { panel->cny = atof(val); } else if ( strcmp(key, "rail_direction") == 0 ) { if ( dir_conv(val, &panel->rail_x, &panel->rail_y, &panel->rail_z) ) { ERROR("Invalid rail direction '%s'\n", val); reject = 1; } } else if ( strcmp(key, "clen_for_centering") == 0 ) { panel->clen_for_centering = atof(val); } else if ( strcmp(key, "adu_per_eV") == 0 ) { panel->adu_scale = atof(val); panel->adu_scale_unit = ADU_PER_EV; } else if ( strcmp(key, "adu_per_photon") == 0 ) { panel->adu_scale = atof(val); panel->adu_scale_unit = ADU_PER_PHOTON; } else if ( strcmp(key, "rigid_group") == 0 ) { add_to_rigid_group(find_or_add_rg(det, val), panel); } else if ( strcmp(key, "clen") == 0 ) { /* Gets expanded when image is loaded */ panel->cnz_from = strdup(val); } else if ( strcmp(key, "data") == 0 ) { if ( strncmp(val,"/",1) != 0 ) { ERROR("Invalid data location '%s'\n", val); reject = -1; } panel->data = strdup(val); } else if ( strcmp(key, "mask") == 0 ) { if ( strncmp(val,"/",1) != 0 ) { ERROR("Invalid mask location '%s'\n", val); reject = -1; } panel->mask = strdup(val); } else if ( strcmp(key, "mask_file") == 0 ) { panel->mask_file = strdup(val); } else if ( strcmp(key, "saturation_map") == 0 ) { panel->satmap = strdup(val); } else if ( strcmp(key, "saturation_map_file") == 0 ) { panel->satmap_file = strdup(val); } else if ( strcmp(key, "coffset") == 0) { panel->cnz_offset = atof(val); } else if ( strcmp(key, "res") == 0 ) { panel->pixel_pitch = 1.0/atof(val); } else if ( strcmp(key, "max_adu") == 0 ) { panel->max_adu = atof(val); } else if ( strcmp(key, "badrow_direction") == 0 ) { ERROR("WARNING 'badrow_direction' is ignored in this version.\n"); } else if ( strcmp(key, "no_index") == 0 ) { panel->bad = atob(val); } else if ( strcmp(key, "fs") == 0 ) { if ( dir_conv(val, &panel->fsx, &panel->fsy, &panel->fsz) != 0 ) { ERROR("Invalid fast scan direction '%s'\n", val); reject = 1; } } else if ( strcmp(key, "ss") == 0 ) { if ( dir_conv(val, &panel->ssx, &panel->ssy, &panel->ssz) != 0 ) { ERROR("Invalid slow scan direction '%s'\n", val); reject = 1; } } else if ( strncmp(key, "dim", 3) == 0) { int dim_entry; char *endptr; if ( key[3] != '\0' ) { dim_entry = strtoul(key+3, &endptr, 10); if ( endptr[0] != '\0' ) { ERROR("Invalid dimension number %s\n", key+3); } else { if ( set_dim(panel, dim_entry, val) ) { ERROR("Failed to set dim structure entry\n"); } } } else { ERROR("'dim' must be followed by a number, e.g. 'dim0'\n"); } } else { ERROR("Unrecognised field '%s'\n", key); } return reject; } static int check_badr_fsss(struct dt_badregion *badr, int is_fsss) { /* First assignment? */ if ( badr->is_fsss == 99 ) { badr->is_fsss = is_fsss; return 0; } if ( is_fsss != badr->is_fsss ) { ERROR("You can't mix x/y and fs/ss in a bad region.\n"); return 1; } return 0; } static int parse_field_bad(struct dt_badregion *badr, const char *key, const char *val) { int reject = 0; if ( strcmp(key, "min_x") == 0 ) { badr->min_x = atof(val); reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "max_x") == 0 ) { badr->max_x = atof(val); reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "min_y") == 0 ) { badr->min_y = atof(val); reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "max_y") == 0 ) { badr->max_y = atof(val); reject = check_badr_fsss(badr, 0); } else if ( strcmp(key, "min_fs") == 0 ) { badr->min_fs = atof(val); reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "max_fs") == 0 ) { badr->max_fs = atof(val); reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "min_ss") == 0 ) { badr->min_ss = atof(val); reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "max_ss") == 0 ) { badr->max_ss = atof(val); reject = check_badr_fsss(badr, 1); } else if ( strcmp(key, "panel") == 0 ) { badr->panel = strdup(val); } else { ERROR("Unrecognised field '%s'\n", key); } return reject; } static void parse_toplevel(DataTemplate *dt, const char *key, const char *val, struct rg_definition ***rg_defl, struct rgc_definition ***rgc_defl, int *n_rg_defs, int *n_rgc_defs, struct panel_template *defaults) { if ( strcmp(key, "mask_bad") == 0 ) { char *end; double v = strtod(val, &end); if ( end != val ) { dt->mask_bad = v; } } else if ( strcmp(key, "mask_good") == 0 ) { char *end; double v = strtod(val, &end); if ( end != val ) { dt->mask_good = v; } } else if ( strcmp(key, "coffset") == 0 ) { defaults->cnz_offset = atof(val); } else if ( strcmp(key, "photon_energy") == 0 ) { /* Will be expanded when image is loaded */ dt->wavelength_from = strdup(val); } else if ( strcmp(key, "peak_list") == 0 ) { dt->peak_list = strdup(val); } else if ( strcmp(key, "photon_energy_bandwidth") == 0 ) { double v; char *end; v = strtod(val, &end); if ( (val[0] != '\0') && (end[0] == '\0') ) { dt->photon_energy_bandwidth = v; } else { ERROR("Invalid value for photon_energy_bandwidth\n"); } } else if ( strcmp(key, "photon_energy_scale") == 0 ) { dt->photon_energy_scale = atof(val); } else if (strncmp(key, "rigid_group", 11) == 0 && strncmp(key, "rigid_group_collection", 22) != 0 ) { struct rg_definition **new; new = realloc(*rg_defl, ((*n_rg_defs)+1)*sizeof(struct rg_definition*)); *rg_defl = new; (*rg_defl)[*n_rg_defs] = malloc(sizeof(struct rg_definition)); (*rg_defl)[*n_rg_defs]->name = strdup(key+12); (*rg_defl)[*n_rg_defs]->pns = strdup(val); *n_rg_defs = *n_rg_defs+1; } else if ( strncmp(key, "rigid_group_collection", 22) == 0 ) { struct rgc_definition **new; new = realloc(*rgc_defl, ((*n_rgc_defs)+1)* sizeof(struct rgc_definition*)); *rgc_defl = new; (*rgc_defl)[*n_rgc_defs] = malloc(sizeof(struct rgc_definition)); (*rgc_defl)[*n_rgc_defs]->name = strdup(key+23); (*rgc_defl)[*n_rgc_defs]->rgs = strdup(val); *n_rgc_defs = *n_rgc_defs+1; } else if ( parse_field_for_panel(defaults, key, val, dt) ) { ERROR("Unrecognised top level field '%s'\n", key); } } static int num_path_placeholders(const char *str) { size_t i, len; int n_pl; if ( str == NULL ) return 0; len = strlen(str); for ( i=0; in_panels = 0; dt->panels = NULL; dt->n_bad = 0; dt->bad = NULL; dt->mask_good = 0; dt->mask_bad = 0; dt->n_rigid_groups = 0; dt->rigid_groups = NULL; dt->n_rg_collections = 0; dt->rigid_group_collections = NULL; dt->photon_energy_bandwidth = -1.0; dt->photon_energy_scale = -1.0; dt->peak_list = NULL; /* The default defaults... */ defaults.orig_min_fs = -1; defaults.orig_min_ss = -1; defaults.orig_max_fs = -1; defaults.orig_max_ss = -1; defaults.cnx = NAN; defaults.cny = NAN; defaults.cnz_from = NULL; defaults.cnz_offset = 0.0; defaults.pixel_pitch = -1.0; defaults.bad = 0; defaults.fsx = 1.0; defaults.fsy = 0.0; defaults.fsz = 0.0; defaults.ssx = 0.0; defaults.ssy = 1.0; defaults.ssz = 0.0; defaults.rail_x = NAN; /* The actual default rail direction */ defaults.rail_y = NAN; /* is below */ defaults.rail_z = NAN; defaults.clen_for_centering = NAN; defaults.adu_scale = NAN; defaults.adu_scale_unit = ADU_PER_PHOTON; defaults.max_adu = +INFINITY; defaults.mask = NULL; defaults.mask_file = NULL; defaults.satmap = NULL; defaults.satmap_file = NULL; defaults.data = strdup("/data/data"); defaults.name = NULL; defaults.dims[0] = DIM_SS; defaults.dims[1] = DIM_FS; for ( i=2; in_panels == -1 ) { ERROR("No panel descriptions in geometry file.\n"); free(dt); return NULL; } num_data_pl = num_path_placeholders(dt->panels[i].data); num_mask_pl = num_path_placeholders(dt->panels[i].mask); num_satmap_pl = num_path_placeholders(dt->panels[i].satmap); /* This is because the "data" path will be used to expand * the path to generate the event list */ if ( (num_mask_pl > num_data_pl) || (num_satmap_pl > num_data_pl) ) { ERROR("Mask and saturation map paths must have fewer " "placeholders than image data path.\n"); reject = 1; } for ( i=0; in_panels; i++ ) { struct panel_template *p = &dt->panels[i]; if ( p->orig_min_fs < 0 ) { ERROR("Please specify the minimum FS coordinate for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( p->orig_max_fs < 0 ) { ERROR("Please specify the maximum FS coordinate for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( p->orig_min_ss < 0 ) { ERROR("Please specify the minimum SS coordinate for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( p->orig_max_ss < 0 ) { ERROR("Please specify the maximum SS coordinate for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( isnan(p->cnx) ) { ERROR("Please specify the corner X coordinate for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( isnan(p->cny) ) { ERROR("Please specify the corner Y coordinate for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( p->cnz_from == NULL ) { ERROR("Please specify the camera length for panel %s\n", dt->panels[i].name); reject = 1; } if ( p->pixel_pitch < 0 ) { ERROR("Please specify the pixel size for" " panel %s\n", dt->panels[i].name); reject = 1; } if ( p->data == NULL ) { ERROR("Please specify the data location for panel %s\n", p->name); reject = 1; } if ( isnan(p->adu_scale) ) { ERROR("Please specify either adu_per_eV or " "adu_per_photon for panel %s\n", dt->panels[i].name); reject = 1; } if ( isnan(p->clen_for_centering) && !isnan(p->rail_x) ) { ERROR("You must specify clen_for_centering if you " "specify the rail direction (panel %s)\n", p->name); reject = 1; } if ( (p->mask_file != NULL) && (p->mask == NULL) ) { ERROR("You have specified 'mask_file' but not 'mask'. " "'mask_file' will therefore have no effect. " "(panel %s)\n", p->name); reject = 1; } if ( num_path_placeholders(p->data) != num_data_pl ) { ERROR("Data locations for all panels must " "have the same number of placeholders\n"); reject = 1; } if ( num_path_placeholders(p->mask) != num_mask_pl ) { ERROR("Mask locations for all panels must " "have the same number of placeholders\n"); reject = 1; } if ( num_path_placeholders(p->satmap) != num_satmap_pl ) { ERROR("Satmap locations for all panels must " "have the same number of placeholders\n"); reject = 1; } /* The default rail direction */ if ( isnan(p->rail_x) ) { p->rail_x = 0.0; p->rail_y = 0.0; p->rail_z = 1.0; } if ( isnan(p->clen_for_centering) ) p->clen_for_centering = 0.0; } for ( i=0; in_bad; i++ ) { if ( dt->bad[i].is_fsss == 99 ) { ERROR("Please specify the coordinate ranges for" " bad region %s\n", dt->bad[i].name); reject = 1; } } free(defaults.cnz_from); free(defaults.data); free(defaults.mask); for ( rgi=0; rginame); n1 = assplode(rg_defl[rgi]->pns, ",", &bits, ASSPLODE_NONE); for ( pi=0; piname); free(rg_defl[rgi]->pns); free(rg_defl[rgi]); } free(rg_defl); for ( rgci=0; rgciname); n2 = assplode(rgc_defl[rgci]->rgs, ",", &bits, ASSPLODE_NONE); for ( rgi=0; rginame); free(rgc_defl[rgci]->rgs); free(rgc_defl[rgci]); } free(rgc_defl); if ( n_rg_definitions == 0 ) { int pi; for ( pi=0; pin_panels; pi++ ) { struct dt_rigid_group *rigidgroup = NULL; rigidgroup = find_or_add_rg(dt, dt->panels[pi].name); add_to_rigid_group(rigidgroup, &dt->panels[pi]); } } if ( n_rgc_definitions == 0 ) { int rgi; struct dt_rg_collection *rgcollection = NULL; rgcollection = find_or_add_rg_coll(dt, "default"); for ( rgi=0; rgin_rigid_groups; rgi++ ) { add_to_rigid_group_coll(rgcollection, dt->rigid_groups[rgi]); } } free(string_orig); if ( reject ) return NULL; return dt; } DataTemplate *data_template_new_from_file(const char *filename) { char *contents; DataTemplate *dt; contents = load_entire_file(filename); if ( contents == NULL ) { ERROR("Failed to load geometry file '%s'\n", filename); return NULL; } dt = data_template_new_from_string(contents); free(contents); return dt; } void data_template_free(DataTemplate *dt) { int i; if ( dt == NULL ) return; free_all_rigid_groups(dt); free_all_rigid_group_collections(dt); for ( i=0; in_panels; i++ ) { free(dt->panels[i].name); free(dt->panels[i].mask); free(dt->panels[i].mask_file); free(dt->panels[i].satmap); free(dt->panels[i].satmap_file); free(dt->panels[i].cnz_from); } free(dt->wavelength_from); free(dt->peak_list); free(dt->panels); free(dt->bad); free(dt); } static int data_template_find_panel(const DataTemplate *dt, int fs, int ss, int *ppn) { int p; for ( p=0; pn_panels; p++ ) { if ( (fs >= dt->panels[p].orig_min_fs) && (fs < dt->panels[p].orig_max_fs+1) && (ss >= dt->panels[p].orig_min_ss) && (ss < dt->panels[p].orig_max_ss+1) ) { *ppn = p; return 0; } } return 1; } int data_template_file_to_panel_coords(const DataTemplate *dt, float *pfs, float *pss, int *ppn) { int pn; if ( data_template_find_panel(dt, *pfs, *pss, &pn) ) { return 1; } *ppn = pn; *pfs = *pfs - dt->panels[pn].orig_min_fs; *pss = *pss - dt->panels[pn].orig_min_ss; return 0; } int data_template_panel_to_file_coords(const DataTemplate *dt, int pn, float *pfs, float *pss) { if ( pn >= dt->n_panels ) return 1; *pfs = *pfs + dt->panels[pn].orig_min_fs; *pss = *pss + dt->panels[pn].orig_min_ss; return 0; } const char *data_template_panel_number_to_name(const DataTemplate *dt, int pn) { if ( pn >= dt->n_panels ) return NULL; return dt->panels[pn].name; } int data_template_panel_name_to_number(const DataTemplate *dt, const char *panel_name, int *pn) { int i; if ( panel_name == NULL ) return 1; for ( i=0; in_panels; i++ ) { if ( strcmp(panel_name, dt->panels[i].name) == 0 ) { *pn = i; return 0; } } return 1; } void data_template_add_copy_header(DataTemplate *dt, const char *header) { /* FIXME: Add "header" to list of things to copy */ STATUS("Adding %s\n", header); } static double unit_string_to_unit(const char *str) { if ( strcmp(str, "mm") == 0 ) return 1e-3; if ( strcmp(str, "m") == 0 ) return 1.0; ERROR("Invalid length unit '%s'\n", str); return NAN; } static double get_length(const char *from) { double units; char *sp; char *fromcpy; double val; char *rval; if ( from == NULL ) return NAN; fromcpy = strdup(from); if ( fromcpy == NULL ) return NAN; sp = strchr(fromcpy, ' '); if ( sp == NULL ) { units = 1.0e-3; } else { units = unit_string_to_unit(sp+1); } sp[0] = '\0'; val = strtod(fromcpy, &rval); if ( !( (*rval == '\0') && (rval != fromcpy)) ) { ERROR("Invalid length value: %s\n", fromcpy); free(fromcpy); return NAN; } return val * units; } /* If possible, i.e. if there are no references to image headers in * 'dt', generate a detgeom structure from it. * * NB This is probably not the function you're looking for! * The detgeom structure should normally come from loading an image, * reading a stream or similar. This function should only be used * when there is really no data involved, e.g. in make_pixelmap. */ struct detgeom *data_template_to_detgeom(const DataTemplate *dt) { struct detgeom *detgeom; int i; if ( dt == NULL ) return NULL; detgeom = malloc(sizeof(struct detgeom)); if ( detgeom == NULL ) return NULL; detgeom->panels = malloc(dt->n_panels*sizeof(struct detgeom_panel)); if ( detgeom->panels == NULL ) return NULL; detgeom->n_panels = dt->n_panels; for ( i=0; in_panels; i++ ) { detgeom->panels[i].name = safe_strdup(dt->panels[i].name); detgeom->panels[i].pixel_pitch = dt->panels[i].pixel_pitch; /* NB cnx,cny are in pixels, cnz is in m */ detgeom->panels[i].cnx = dt->panels[i].cnx; detgeom->panels[i].cny = dt->panels[i].cny; detgeom->panels[i].cnz = get_length(dt->panels[i].cnz_from); /* Apply offset (in m) and then convert cnz from * m to pixels */ detgeom->panels[i].cnz += dt->panels[i].cnz_offset; detgeom->panels[i].cnz /= detgeom->panels[i].pixel_pitch; detgeom->panels[i].max_adu = dt->panels[i].max_adu; detgeom->panels[i].adu_per_photon = 1.0; /* FIXME ! */ detgeom->panels[i].w = dt->panels[i].orig_max_fs - dt->panels[i].orig_min_fs + 1; detgeom->panels[i].h = dt->panels[i].orig_max_ss - dt->panels[i].orig_min_ss + 1; detgeom->panels[i].fsx = dt->panels[i].fsx; detgeom->panels[i].fsy = dt->panels[i].fsy; detgeom->panels[i].fsz = dt->panels[i].fsz; detgeom->panels[i].ssx = dt->panels[i].ssx; detgeom->panels[i].ssy = dt->panels[i].ssy; detgeom->panels[i].ssz = dt->panels[i].ssz; } return detgeom; } static int num_placeholders(const struct panel_template *p) { int i; int n_pl = 0; for ( i=0; idims[i] == DIM_PLACEHOLDER ) n_pl++; } return n_pl; } int data_template_get_slab_extents(const DataTemplate *dt, int *pw, int *ph) { int w, h; char *data_from; int i; data_from = dt->panels[0].data; w = 0; h = 0; for ( i=0; in_panels; i++ ) { struct panel_template *p = &dt->panels[i]; if ( strcmp(data_from, p->data) != 0 ) { /* Not slabby */ return 1; } if ( num_placeholders(p) > 0 ) { /* Not slabby */ return 1; } if ( p->orig_max_fs > w ) { w = p->orig_max_fs; } if ( p->orig_max_ss > h ) { h = p->orig_max_ss; } } /* Inclusive -> exclusive */ *pw = w + 1; *ph = h + 1; return 0; } /* Return non-zero if pixel fs,ss on panel p is in a bad region * as specified in the geometry file (regions only, not including * masks, NaN/inf, no_index etc */ int data_template_in_bad_region(const DataTemplate *dtempl, int pn, double fs, double ss) { double rx, ry; double xs, ys; int i; struct panel_template *p; if ( pn >= dtempl->n_panels ) { ERROR("Panel index out of range\n"); return 0; } p = &dtempl->panels[pn]; /* Convert xs and ys, which are in fast scan/slow scan coordinates, * to x and y */ xs = fs*p->fsx + ss*p->ssx; ys = fs*p->fsy + ss*p->ssy; rx = xs + p->cnx; ry = ys + p->cny; for ( i=0; in_bad; i++ ) { struct dt_badregion *b = &dtempl->bad[i]; if ( (b->panel != NULL) && (strcmp(b->panel, p->name) != 0) ) continue; if ( b->is_fsss ) { int nfs, nss; /* fs/ss bad regions are specified according * to the original coordinates */ nfs = fs + p->orig_min_fs; nss = ss + p->orig_min_ss; if ( nfs < b->min_fs ) continue; if ( nfs > b->max_fs ) continue; if ( nss < b->min_ss ) continue; if ( nss > b->max_ss ) continue; } else { if ( rx < b->min_x ) continue; if ( rx > b->max_x ) continue; if ( ry < b->min_y ) continue; if ( ry > b->max_y ) continue; } return 1; } return 0; }