diff --git a/CMakeLists.txt b/CMakeLists.txt
index c74d40b..54cc254 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -94,7 +94,7 @@ link_directories(${${PROJ}_LIBRARY_DIRS})
add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\"
-DPACKAGE_VERSION=\"${PROJ_VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
- -DMAJOR_VERSION=\"${MAJOR_VESION}\")
+ -DMAJOR_VERSION=\"${MAJOR_VESION}\" -DVERSION=\"${PROJ_VERSION}\")
# -l
target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES})
diff --git a/FITSmanip.h b/FITSmanip.h
index e6e3f67..b322b18 100644
--- a/FITSmanip.h
+++ b/FITSmanip.h
@@ -105,7 +105,9 @@ typedef struct{
typedef struct{
int width; // width
int height; // height
- int dtype; // picture data type
+ int bitpix; // original bitpix
+ int dtype; // type of stored data
+ int pxsz; // number of bytes for one pixel data
void *data; // picture data
} FITSimage;
@@ -153,7 +155,7 @@ typedef enum{
*/
void keylist_free(KeyList **list);
-KeyList *keylist_add_record(KeyList **list, char *rec);
+KeyList *keylist_add_record(KeyList **list, char *rec, int check);
KeyList *keylist_find_key(KeyList *list, char *key);
void keylist_remove_key(KeyList **list, char *key);
KeyList *keylist_modify_key(KeyList *list, char *key, char *newval);
@@ -173,9 +175,9 @@ void table_print_all(FITS *fits);
void image_free(FITSimage **ima);
FITSimage *image_read(FITS *fits);
-#define image_datatype_size(d) (abs(d)/8)
-void *image_data_malloc(size_t w, size_t h, int dtype);
-FITSimage *image_new(size_t w, size_t h, int dtype);
+int image_datatype_size(int bitpix, int *dtype);
+void *image_data_malloc(size_t w, size_t h, int pxbytes);
+FITSimage *image_new(size_t w, size_t h, int bitpix);
FITSimage *image_mksimilar(FITSimage *in);
FITSimage *image_copy(FITSimage *in);
//FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata);
@@ -184,6 +186,7 @@ void FITS_free(FITS **fits);
FITS *FITS_read(char *filename);
FITS *FITS_open(char *filename);
bool FITS_write(char *filename, FITS *fits);
+bool FITS_rewrite(FITS *fits);
/**************************************************************************************
* fileops.c *
diff --git a/examples/keylist.c b/examples/keylist.c
index 2dd1c7b..9ecdd03 100644
--- a/examples/keylist.c
+++ b/examples/keylist.c
@@ -16,6 +16,12 @@
* along with this program. If not, see .
*/
+/*
+ This example allows to list all keywords in given FITS-file, list amount and types of HDUs in file,
+ add new keywords, modify old keywords.
+ New data could be saved to new file or in place into opened file.
+ */
+
#include "common.h"
#include
@@ -24,6 +30,8 @@ typedef struct{
int list; // print keyword list
int contents; // print short description of file contents
char **addrec; // add some records to keywords in first HDU
+ char *outfile; // output file name
+ char **modify; // keys which values should be modified
} glob_pars;
/*
@@ -43,7 +51,9 @@ myoption cmdlnopts[] = {
{"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")},
{"contents",NO_ARGS, NULL, 'c', arg_none, APTR(&G.contents), _("show short file contents")},
{"list", NO_ARGS, NULL, 'l', arg_none, APTR(&G.list), _("list all keywords")},
- {"addrec", MULT_PAR, NULL, 'a', arg_string, APTR(&G.addrec), _("add record to file (you can add more than one record in once, point more -a)")},
+ {"addrec", MULT_PAR, NULL, 'a', arg_string, APTR(&G.addrec), _("add record to first HDU (you can add more than one record in once, point more -a)")},
+ {"output", NEED_ARG, NULL, 'o', arg_string, APTR(&G.outfile), _("save result to file (else save to same file)")},
+ {"modify", MULT_PAR, NULL, 'm', arg_string, APTR(&G.modify), _("modify values values of given keys (each param should be \"key = new_value\")")},
end_option
};
@@ -86,7 +96,7 @@ int main(int argc, char *argv[]){
}
if(G.contents){
green("\n\nFile consists of %d HDUs:\n", N);
- for(int i = 0; i <= N; ++i){
+ for(int i = 1; i <= N; ++i){
printf("\tHDU #%d - ", i);
switch(f->HDUs[i].hdutype){
case IMAGE_HDU:
@@ -108,9 +118,30 @@ int main(int argc, char *argv[]){
char **ptr = G.addrec;
while(*ptr){
printf("record: %s\n", *ptr);
+ keylist_add_record(&(f->HDUs[1].keylist), *ptr++, 1);
+ }
+ }
+ if(G.modify){
+ char **ptr = G.modify;
+ while(*ptr){
+ printf("modify: %s\n", *ptr);
+ char *val = strchr(*ptr, '=');
+ if(!val){
+ WARNX("should be: 'parameter = value / comment'");
+ continue;
+ }
+ *val++ = 0; // now `val` is value + comment; ptr is key
+ if(!keylist_modify_key(f->HDUs[1].keylist, *ptr, val)){
+ WARNX("key %s not found", *ptr);
+ }
++ptr;
}
}
+ if(G.outfile){ // save result to new file
+ FITS_write(G.outfile, f);
+ }else{
+ FITS_rewrite(f);
+ }
return 0;
}
diff --git a/fits.c b/fits.c
index e17edd7..a7b6bd5 100644
--- a/fits.c
+++ b/fits.c
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
@@ -59,16 +60,29 @@ KeyList *keylist_get_end(KeyList *list){
}
/**
- * @brief keylist_add_record - add record to keylist
+ * @brief keylist_add_record - add record to keylist with optional check
* @param list (io) - pointer to root of list or NULL
* if *root == NULL, created node will be placed there
* @param rec (i) - data inserted
- * @return pointer to created node
+ * @param check - !=0 to check `rec` with fits_parse_template
+ * @return pointer to created node (if list == NULL - don't add created record to any list)
*/
-KeyList *keylist_add_record(KeyList **list, char *rec){
+KeyList *keylist_add_record(KeyList **list, char *rec, int check){
+ if(!rec) return NULL;
KeyList *node, *last;
if((node = (KeyList*) MALLOC(KeyList, 1)) == 0) return NULL; // allocation error
- node->record = strdup(rec); // insert data
+ int tp = 0, st = 0;
+ if(check){
+ char card[FLEN_CARD];
+ fits_parse_template(rec, card, &tp, &st);
+ if(st){
+ fits_report_error(stderr, st);
+ return NULL;
+ }
+ DBG("\n WAS: %s\nBECOME: %s\ntp=%d", rec, card, tp);
+ rec = card;
+ }
+ node->record = strdup(rec);
if(!node->record){
/// "Не могу скопировать данные"
WARNX(_("Can't copy data"));
@@ -95,14 +109,17 @@ KeyList *keylist_add_record(KeyList **list, char *rec){
KeyList *keylist_find_key(KeyList *list, char *key){
if(!list || !key) return NULL;
size_t L = strlen(key);
+ DBG("try to find %s", key);
do{
if(list->record){
- if(strncmp(list->record, key, L) == 0){ // key found
+ if(strncasecmp(list->record, key, L) == 0){ // key found
+ DBG("found:\n%s", list->record);
return list;
}
}
list = list->next;
}while(list);
+ DBG("not found");
return NULL;
}
@@ -115,13 +132,17 @@ KeyList *keylist_find_key(KeyList *list, char *key){
*/
KeyList *keylist_modify_key(KeyList *list, char *key, char *newval){
// TODO: look modhead.c in cexamples for protected keys
- char buf[FLEN_CARD];
+ char buf[FLEN_CARD], test[2*FLEN_CARD];
KeyList *rec = keylist_find_key(list, key);
if(!rec) return NULL;
- char *comm = strchr(rec->record, '/');
- if(!comm) comm = "";
- // TODO: use fits_parse_template
- snprintf(buf, FLEN_CARD, "%-8s=%21s %s", key, newval, comm);
+ snprintf(test, 2*FLEN_CARD, "%s = %s", key, newval);
+ int tp, st = 0;
+ fits_parse_template(test, buf, &tp, &st);
+ if(st){
+ fits_report_error(stderr, st);
+ return NULL;
+ }
+ DBG("new record:\n%s", buf);
FREE(rec->record);
rec->record = strdup(buf);
return rec;
@@ -222,7 +243,7 @@ KeyList *keylist_copy(KeyList *list){
int n = 0;
#endif
do{
- keylist_add_record(&newlist, list->record);
+ keylist_add_record(&newlist, list->record, 0);
list = list->next;
#ifdef EBUG
++n;
@@ -269,7 +290,7 @@ KeyList *keylist_read(FITS *fits){
fits_read_record(fits->fp, j, card, &fst);
if(fst) fits_report_error(stderr, fst);
else{
- KeyList *kl = keylist_add_record(&list, card);
+ KeyList *kl = keylist_add_record(&list, card, 0);
if(!kl){
/// "Не могу добавить запись в список"
WARNX(_("Can't add record to list"));
@@ -767,47 +788,102 @@ returning:
return fits;
}
+static bool keylist_write(KeyList *kl, fitsfile *fp){
+ int st = 0;
+ bool ret = TRUE;
+ if(!fp || !kl) return FALSE;
+ while(kl){
+ if(kl->keyclass > TYP_CMPRS_KEY){ // this record should be written
+ fits_write_record(fp, kl->record, &st);
+ DBG("Write %s, st = %d", kl->record, st);
+ if(st){fits_report_error(stderr, st); st = 0; ret = FALSE;}
+ }
+ kl = kl->next;
+ }
+ return ret;
+}
+
+/**
+ * @brief FITS_write - write FITS file to disk
+ * @param filename - new filename
+ * @param fits - structure to write
+ * @return TRUE if all OK
+ */
bool FITS_write(char *filename, FITS *fits){
if(!filename || !fits) return FALSE;
- /*int w = fits->width, h = fits->height, fst = 0;
- long naxes[2] = {w, h};
- size_t sz = w * h;
fitsfile *fp;
+ int fst = 0;
fits_create_diskfile(&fp, filename, &fst);
+ DBG("create file %s", filename);
if(fst){fits_report_error(stderr, fst); return FALSE;}
- // TODO: save FITS files in original (or given by user) data format!
- // check fits->dtype - does all data fits it
- fits_create_img(fp, fits->dtype, 2, naxes, &fst);
- if(fst){fits_report_error(stderr, fst); return FALSE;}
- if(fits->keylist){ // there's keys
- KeyList *records = fits->keylist;
- while(records){
- char *rec = records->record;
- records = records->next;
- // TODO: check types of headers from each record!
- if(strncmp(rec, "SIMPLE", 6) == 0 || strncmp(rec, "EXTEND", 6) == 0) // key "file does conform ..."
- continue;
- // comment of obligatory key in FITS head
- else if(strncmp(rec, "COMMENT FITS", 14) == 0 || strncmp(rec, "COMMENT and Astrophysics", 26) == 0)
- continue;
- else if(strncmp(rec, "NAXIS", 5) == 0 || strncmp(rec, "BITPIX", 6) == 0) // NAXIS, NAXISxxx, BITPIX
- continue;
- int ret = fits_write_record(fp, rec, &fst);
- if(fst){fits_report_error(stderr, fst);}
- else if(ret) WARNX(_("Can't write record %s"), rec);
- // DBG("write key: %s", rec);
+ int N = fits->NHDUs;
+ for(int i = 1; i <= N; ++i){
+ FITSHDU *hdu = &fits->HDUs[i];
+ if(!hdu) continue;
+ FITSimage *img;
+ long naxes[2] = {0,0};
+ KeyList *records = hdu->keylist;
+ DBG("HDU #%d (type %d)", i, hdu->hdutype);
+ switch(hdu->hdutype){
+ case IMAGE_HDU:
+ img = hdu->contents.image;
+ if(!img && records){ // something wrong - just write keylist
+ DBG("create empty image with records");
+ fits_create_img(fp, SHORT_IMG, 0, naxes, &fst);
+ if(fst){fits_report_error(stderr, fst); fst = 0; continue;}
+ keylist_write(records, fp);
+ DBG("OK");
+ continue;
+ }
+ naxes[0] = img->width, naxes[1] = img->height;
+ DBG("create, bitpix: %d, naxes = {%zd, %zd}", img->bitpix, naxes[0], naxes[1]);
+ //fits_create_img(fp, img->bitpix, 2, naxes, &fst);
+ fits_create_img(fp, SHORT_IMG, 2, naxes, &fst);
+ if(fst){fits_report_error(stderr, fst); fst = 0; continue;}
+ keylist_write(records, fp);
+ DBG("OK, now write image");
+ // TODO: change to original data type according to bitpix or more whide according to min/max
+ DBG("bitpix: %d, dtype: %d", SHORT_IMG, img->dtype);
+ //int bscale = 1, bzero = 32768, status = 0;
+ //fits_set_bscale(fp, bscale, bzero, &status);
+ if(img && img->data){
+ fits_write_img(fp, TUSHORT, 1, img->width * img->height, img->data, &fst);
+ //fits_write_img(fp, img->dtype, 1, img->width * img->height, img->data, &fst);
+ DBG("status: %d", fst);
+ if(fst){fits_report_error(stderr, fst); fst = 0; continue;}
+ }
+ break;
+ case BINARY_TBL:
+ case ASCII_TBL:
+ // TODO: save table
+ break;
}
}
- //fits->lasthdu = 1;
- //FITSFUN(fits_write_record, fp, "COMMENT modified by simple test routine");
- fits_write_img(fp, TDOUBLE, 1, sz, fits->data, &fst);
- if(fst){fits_report_error(stderr, fst); return FALSE;}
- if(fits->tables) table_write(fits, fp);
+
fits_close_file(fp, &fst);
- if(fst){fits_report_error(stderr, fst);}*/
+ if(fst){fits_report_error(stderr, fst);}
return TRUE;
}
+/**
+ * @brief FITS_rewrite - rewrite file in place
+ * @param fits - pointer to FITS structure
+ * @return TRUE if all OK
+ */
+bool FITS_rewrite(FITS *fits){
+ FNAME();
+ char *nm = tmpnam(NULL);
+ if(!nm){WARN("tmpnam()"); return FALSE;}
+ char *fnm = strrchr(nm, '/');
+ if(!fnm){WARN("strrchr()"); return FALSE;}
+ ++fnm;
+ DBG("make link: %s -> %s", fits->filename, fnm);
+ if(link(fits->filename, fnm)){
+ WARN("link()");
+ return FALSE;
+ }
+ return TRUE;
+}
/**************************************************************************************
* FITS images *
@@ -819,30 +895,74 @@ void image_free(FITSimage **img){
}
/**
- * @brief image_malloc - allocate memory for given data type
- * @param w - image width
- * @param h - image height
- * @param dtype - data type
+ * @brief image_datatype_size - calculate size of one data element for given bitpix
+ * @param bitpix - value of BITPIX
+ * @param dtype (o) - nearest type of data to fit input type
+ * @return amount of space need to store one pixel data
+ */
+int image_datatype_size(int bitpix, int *dtype){
+ int s = bitpix/8;
+ if(dtype){
+ switch(s){
+ case 1:
+ case 2:
+ case 4:
+ *dtype = TINT;
+ s = 4;
+ break;
+ case 8:
+ *dtype = TLONG;
+ break;
+ case -4:
+ case -8:
+ *dtype = TDOUBLE;
+ s = 8;
+ break;
+ default:
+ return 0; // wrong bitpix
+ }
+ }
+ DBG("bitpix: %d, dtype=%d, imgs=%d", bitpix, *dtype, s);
+ return s;
+}
+
+/**
+ * @brief image_malloc - allocate memory for given bitpix
+ * @param w - image width
+ * @param h - image height
+ * @param pxbytes- amount of bytes to store data from one pixel
* @return allocated memory
*/
-void *image_data_malloc(size_t w, size_t h, int dtype){
- void *data = calloc(w*h, image_datatype_size(dtype));
+void *image_data_malloc(size_t w, size_t h, int pxbytes){
+ if(!pxbytes || !w || !h) return NULL;
+ void *data = calloc(w*h, pxbytes);
+ DBG("Allocate %zd members of size %d", w*h, pxbytes);
if(!data) ERR(_("calloc()"));
return data;
}
/**
- * @brief image_new - create an empty image without headers, assign data type to "dtype"
+ * @brief image_new - create an empty image without headers, assign BITPIX to "bitpix"
* @param w - image width
* @param h - image height
* @param dtype - image data type
* @return
*/
-FITSimage *image_new(size_t w, size_t h, int dtype){
+FITSimage *image_new(size_t w, size_t h, int bitpix){
FITSimage *out = MALLOC(FITSimage, 1);
- out->data = image_data_malloc(w, h, dtype);
+ int dtype, pxsz = image_datatype_size(bitpix, &dtype);
+ if(w && h){
+ out->data = image_data_malloc(w, h, pxsz);
+ if(!out->data){
+ WARNX(_("Bad w, h or pxsz"));
+ FREE(out);
+ return NULL;
+ }
+ }
out->width = w;
out->height = h;
+ out->pxsz = pxsz;
+ out->bitpix = bitpix;
out->dtype = dtype;
return out;
}
@@ -907,7 +1027,7 @@ FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata){
*/
FITSimage *image_mksimilar(FITSimage *img){
if(!img || img->height < 1 || img->width < 1) return NULL;
- return image_new(img->width, img->height, img->dtype);
+ return image_new(img->width, img->height, img->bitpix);
}
/**
@@ -917,7 +1037,7 @@ FITSimage *image_copy(FITSimage *in){
FITSimage *out = image_mksimilar(in);
if(!out) return NULL;
// TODO: size of data as in original!
- memcpy(out->data, in->data, sizeof(double)*in->width*in->height);
+ memcpy(out->data, in->data, (in->pxsz)*(in->width)*(in->height));
return out;
}
@@ -929,20 +1049,30 @@ FITSimage *image_copy(FITSimage *in){
FITSimage *image_read(FITS *fits){
// TODO: open not only 2-dimensional files!
// get image dimensions
- int naxis, fst = 0, dtype;
+ int naxis, fst = 0, bitpix;
long naxes[2] = {0,0};
- fits_get_img_param(fits->fp, 2, &dtype, &naxis, naxes, &fst);
+ fits_get_img_param(fits->fp, 2, &bitpix, &naxis, naxes, &fst);
if(fst){fits_report_error(stderr, fst); return NULL;}
if(naxis > 2){
WARNX(_("Images with > 2 dimensions are not supported"));
return NULL;
- }
- DBG("got image %ldx%ld pix, bitpix=%d", naxes[0], naxes[1], dtype);
+ }/*
+ if(naxis < 2){
+ WARNX(_("Not an image: NAXIS = %d"), naxis);
+ return NULL;
+ }*/
+ DBG("got image %ldx%ld pix, bitpix=%d", naxes[0], naxes[1], bitpix);
- FITSimage *img = image_new(naxes[0], naxes[1], dtype);
+ FITSimage *img = image_new(naxes[0], naxes[1], bitpix);
int stat = 0;
- fits_read_img(fits->fp, dtype, 1, naxes[0]*naxes[1], NULL, img->data, &stat, &fst);
+ if(!img) return NULL;
+ if(!img->data) return img; // empty "image" - no data inside
+ DBG("try to read, dt=%d, sz=%ld", img->dtype, naxes[0]*naxes[1]);
+ //int bscale = 1, bzero = 32768, status = 0;
+ //fits_set_bscale(fits->fp, bscale, bzero, &status);
+ //fits_read_img(fits->fp, img->dtype, 1, naxes[0]*naxes[1], NULL, img->data, &stat, &fst);
+ fits_read_img(fits->fp, TUSHORT, 1, naxes[0]*naxes[1], NULL, img->data, &stat, &fst);
if(fst){
fits_report_error(stderr, fst);
image_free(&img);