diff --git a/cmdlnopts.c b/cmdlnopts.c index 52a4235..b5deb43 100644 --- a/cmdlnopts.c +++ b/cmdlnopts.c @@ -52,10 +52,10 @@ glob_pars const Gdefault = { .exptime = 1., .binning = 0, .takeimg = 0, - .imtype = "a", + .imtype = "l", + .imformat = NULL, .imstoretype = NULL, .outpfname = "output.tiff", - .dumpbin = 0 }; /* @@ -73,16 +73,15 @@ myoption cmdlnopts[] = { {"spd-list",NO_ARGS, NULL, 'l', arg_int, APTR(&G.splist), _("list speeds available")}, {"baudrate",NEED_ARG, NULL, 'b', arg_int, APTR(&G.speed), _("connect at given baudrate without autocheck")}, {"spd-set", NEED_ARG, NULL, 's', arg_int, APTR(&G.newspeed), _("set terminal speed")}, - // only long variants {"shutter", NEED_ARG, NULL, 0, arg_string, APTR(&G.shutter_cmd),_("shutter command: 'o' for open, 'c' for close, 'k' for de-energize")}, {"subframe",NEED_ARG, NULL, 0, arg_string, APTR(&G.subframe), _("select subframe: x,y,size")}, - {"exptime", NEED_ARG, NULL, 'x', arg_double, APTR(&G.exptime), _("exposition time (1s by default)")}, - {"binning", NEED_ARG, NULL, 'B', arg_int, APTR(&G.binning), _("binning (1 by default)")}, + {"exptime", NEED_ARG, NULL, 'x', arg_double, APTR(&G.exptime), _("exposition time in seconds (default: 1s)")}, + {"binning", NEED_ARG, NULL, 'B', arg_int, APTR(&G.binning), _("binning (default 0: full size)")}, {"start-exp",NO_ARGS, NULL, 'X', arg_int, APTR(&G.takeimg), _("start exposition")}, - {"imtype", NEED_ARG, NULL, 'T', arg_string, APTR(&G.imtype), _("image type: light (l, L), autodark (a, A), dark (d, D)")}, + {"imtype", NEED_ARG, NULL, 'T', arg_string, APTR(&G.imtype), _("image type: light (l, L), autodark (a, A), dark (d, D); default: light")}, {"storetype",NEED_ARG, NULL, 'S', arg_string, APTR(&G.imstoretype),_("'overwrite'/'rewrite' to rewrite existing image, 'enumerate'/'numerate' to use given filename as base for series")}, {"output", NEED_ARG, NULL, 'o', arg_string, APTR(&G.outpfname), _("output file name (default: output.tiff)")}, - {"dump", NO_ARGS, NULL, 0, arg_none, APTR(&G.dumpbin), _("dump binary data into file `dump.bin`")}, + {"imformat",NEED_ARG, NULL, 'f', arg_string, APTR(&G.imformat), _("image format: FITS (f), TIFF (t), raw dump with histogram storage (r,d), may be OR'ed; default: FITS or based on output image name")}, // simple integer parameter with obligatory arg: end_option }; diff --git a/cmdlnopts.h b/cmdlnopts.h index 55ca722..65a916c 100644 --- a/cmdlnopts.h +++ b/cmdlnopts.h @@ -46,7 +46,7 @@ typedef struct{ char *imtype; // image type (light, autodark, dark) char *imstoretype; // "overwrite" (or "rewrite"), "normal" (or NULL), "enumerate" (or "numerate") char *outpfname; // output filename for image storing - int dumpbin; // dump raw binary data + char *imformat; // output file format char** rest_pars; // the rest parameters: array of char* } glob_pars; diff --git a/imfunctions.c b/imfunctions.c index dd7bbec..4b33f94 100644 --- a/imfunctions.c +++ b/imfunctions.c @@ -28,16 +28,40 @@ #include // save tiff #include -// find the first non-exists filename -char *make_filename(char *outfile, char *ext){ +// image type suffixes +#define SUFFIX_FITS "fits" +#define SUFFIX_RAW "bin" +#define SUFFIX_TIFF "tiff" + +/** + * NON THREAD-SAFE! + * make filename for given name, suffix and storage type + * @return filename or NULL if can't create it + */ +static char *make_filename(const char *outfile, const char *suff, store_type st){ struct stat filestat; static char buff[FILENAME_MAX]; + if(st == STORE_NORMAL || st == STORE_REWRITE){ + snprintf(buff, FILENAME_MAX, "%s.%s", outfile, suff); + if(stat(buff, &filestat)){ + if(ENOENT != errno){ + WARN(_("Error access file %s"), buff); + return NULL; // some error + } + else return buff; + }else{ // file exists + if(st == STORE_REWRITE) return buff; + else return NULL; // file exists on option STORE_NORMAL + } + } + // STORE_NEXTNUM int num; for(num = 1; num < 10000; num++){ - if(snprintf(buff, FILENAME_MAX, "%s_%04d.%s", outfile, num, ext) < 1) + if(snprintf(buff, FILENAME_MAX, "%s_%04d.%s", outfile, num, suff) < 1) return NULL; - if(stat(buff, &filestat) && ENOENT == errno) // OK, file not exists + if(stat(buff, &filestat) && ENOENT == errno){ // OK, file not exists return buff; + } } return NULL; } @@ -47,8 +71,10 @@ char *make_filename(char *outfile, char *ext){ * @param filename (i) - output file name (or prefix with suffix) * @param store (i) - "overwrite" (or "rewrite"), "normal" (or NULL), "enumerate" (or "numerate") */ -imstorage *chk_storeimg(char *filename, char* store){ +imstorage *chk_storeimg(char *filename, char* store, char *format){ + FNAME(); store_type st = STORE_NORMAL; + image_format fmt = FORMAT_FITS; if(store){ // rewrite or enumerate int L = strlen(store); if(0 == strncasecmp(store, "overwrite", L) || 0 == strncasecmp(store, "rewrite", L)) st = STORE_REWRITE; @@ -58,53 +84,67 @@ imstorage *chk_storeimg(char *filename, char* store){ return NULL; } } - char *f2store = filename; char *nm = strdup(filename); if(!nm) ERRX("strdup"); char *pt = strrchr(nm, '.'); - if(!pt){ - WARNX(_("Wrong image name pattern: this should be a filename with suffix .tiff or .jpg")); - FREE(nm); - return NULL; - } - *pt++ = 0; - if(strcmp(pt, "tiff") && strcmp(pt, "jpg")){ - WARNX("Can save only into jpg or tiff files!"); - return NULL; - } - if(st == STORE_NORMAL){ - struct stat filestat; - if(stat(filename, &filestat)){ - if(ENOENT != errno){ - WARN(_("Error access file %s"), filename); + if(pt){ + char *suff = pt + 1; + DBG("got suffix: %s", suff); + // check if name's suffix is filetype + image_format fbysuff = FORMAT_NONE; + if(0 == strcasecmp(suff, "tiff") || 0 == strcasecmp(suff, "tif")) fbysuff = FORMAT_TIFF; + else if(0 == strcasecmp(suff, "fits") || 0 == strcasecmp(suff, "fit")) fbysuff = FORMAT_FITS; + else if(0 == strcasecmp(suff, "raw") || 0 == strcasecmp(suff, "bin") || 0 == strcasecmp(suff, "dump")) + fbysuff = FORMAT_RAW; + if(fbysuff != FORMAT_NONE) *pt = 0; // truncate filename if suffix found + if(format){ // check if user gave format + fbysuff = FORMAT_NONE; + if(strchr(format, 'f') || strchr(format, 'F')) fbysuff |= FORMAT_FITS; + if(strchr(format, 't') || strchr(format, 'T')) fbysuff |= FORMAT_TIFF; + if(strchr(format, 'r') || strchr(format, 'R') || + strchr(format, 'd') || strchr(format, 'D')) fbysuff |= FORMAT_RAW; + if(fbysuff == FORMAT_NONE){ + WARNX(_("Wrong format string: %s"), format); + free(nm); return NULL; } - }else{ - WARNX(_("The file %s exists, use '-Srew' option to rewrite")); + fmt = fbysuff; + }else{// if user gave image with suffix, change format; else leave default: FITS + if(fbysuff != FORMAT_NONE) fmt = fbysuff; + DBG("fmt: %d", fmt); + } + } + // now check all names + #define FMTSZ (3) + image_format formats[FMTSZ] = {FORMAT_FITS, FORMAT_TIFF, FORMAT_RAW}; + const char *suffixes[FMTSZ] = {SUFFIX_FITS, SUFFIX_TIFF, SUFFIX_RAW}; + for(size_t i = 0; i < FMTSZ; ++i){ + if(!(formats[i] & fmt)) continue; + if(!make_filename(nm, suffixes[i], st)){ + WARNX(_("Can't create output file")); + free(nm); return NULL; } - }else if(st == STORE_NEXTNUM){ - f2store = make_filename(nm, pt); } - FREE(nm); imstorage *ist = MALLOC(imstorage, 1); ist->st = st; - ist->imname = strdup(f2store); - if(!ist->imname)ERR("strdup"); + ist->imformat = fmt; + ist->imname = strdup(nm); return ist; } /** * Try to write tiff file + * @return 1 if all OK */ int writetiff(imstorage *img){ - int ret = 1, H = img->H, W = img->W, y; + int H = img->H, W = img->W, y; uint16_t *data = img->imdata; - TIFF *image = TIFFOpen(img->imname, "w"); - if(!image){ - WARN("Can't open tiff file"); - ret = 0; - goto done; + char *name = make_filename(img->imname, SUFFIX_TIFF, img->st); + TIFF *image = NULL; + if(!name || !(image = TIFFOpen(name, "w"))){ + WARN("Can't save tiff file"); + return 0; } TIFFSetField(image, TIFFTAG_IMAGEWIDTH, W); TIFFSetField(image, TIFFTAG_IMAGELENGTH, H); @@ -116,13 +156,18 @@ int writetiff(imstorage *img){ TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); + size_t S = W*sizeof(uint16_t); for (y = 0; y < H; ++y, data += W){ - TIFFWriteScanline(image, data, y, 0); + if(S != (size_t)TIFFWriteEncodedStrip(image, y, data, S)){ + WARNX(_("Error writing %s"), name); + TIFFClose(image); + return 0; + } + //TIFFWriteScanline(image, data, y, 0); } - -done: - if(image) TIFFClose(image); - return ret; + TIFFClose(image); + green(_("Image %s saved\n"), name); + return 1; } void print_stat(imstorage *img){ @@ -184,29 +229,76 @@ uint16_t *get_imdata(imstorage *img){ return imdata; } +/** + * save truncated to 256 levels histogram of `img` into file `f` + * @return 0 if all OK + */ +int save_histo(FILE *f, imstorage *img){ + uint8_t histogram[256]; + if(!img->imdata) return 1000; + size_t l, S = img->W*img->H; + uint16_t *ptr = img->imdata; + for(l = 0; l < S; ++l, ++ptr){ + ++histogram[(*ptr>>8)&0xff]; + } + for(l = 0; l < 256; ++l){ + int status = fprintf(f, "%zd\t%u\n", l, histogram[l]); + if(status < 0) + return status; + } + return 0; +} + +int writedump(imstorage *img){ + char *name = make_filename(img->imname, SUFFIX_RAW, img->st); + if(!name) return 1; + int f = open(name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(f){ + size_t S = img->W*img->H*2; + if(S != (size_t)write(f, img->imdata, S)){ + WARN(_("Error writting `%s`"), name); + return 2; + } + green(_("Image dump stored in `%s`\n"), name); + close(f); + }else{ + WARN(_("Can't make dump")); + return 3; + } + name = make_filename(img->imname, "_histogram.txt", img->st); + if(!name) return 4; + FILE *h = fopen(name, "w"); + if(!h) return 5; + int i = -1; + if(f){ + i = save_histo(h, img); + fclose(h); + } + if(i < 0){ + WARN(_("Can't save histogram")); + return 6; + }else{ + green(_("Truncated to 256 levels histogram stored in file `%s`\n"), name); + } + return 0; +} + /** * Save image * @param filename (i) - output file name * @return 0 if all OK */ int store_image(imstorage *img){ + int status = 0; if(!img->imdata && !get_imdata(img)) return 1; - green("Save image into %s\n", img->imname); - if(!writetiff(img)) return 3; - if(img->dump){ - int f = open("dump.bin", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if(f){ - size_t S = img->W*img->H*2; - if(S != (size_t)write(f, img->imdata, S)){ - WARN(_("Error writting `dump.bin`")); - return 4; - } - green(_("Image dump stored in `dump.bin`\n")); - close(f); - }else{ - WARN(_("Can't make dump")); - return 5; - } + if(img->imformat & FORMAT_TIFF){ // save tiff file + if(!writetiff(img)) status |= 1; } - return 0; + if(img->imformat & FORMAT_RAW){ + if(!writedump(img)) status |= 2; + } + if(img->imformat & FORMAT_FITS){ // not supported yet + status |= 4; + } + return status; } diff --git a/imfunctions.h b/imfunctions.h index 9f27669..7c071ef 100644 --- a/imfunctions.h +++ b/imfunctions.h @@ -25,37 +25,49 @@ #define __IMFUNCTIONS_H__ #include +// how to save files: rewrite, check existance or add number typedef enum{ STORE_REWRITE, STORE_NORMAL, STORE_NEXTNUM } store_type; +// which format should be used when image stored (OR`ed) +typedef enum{ + FORMAT_NONE = 0, + FORMAT_FITS = 1, + FORMAT_TIFF = 2, + FORMAT_RAW = 4 +} image_format; + +// exposed image type typedef enum{ IMTYPE_AUTODARK, IMTYPE_LIGHT, IMTYPE_DARK } image_type; +// subframe parameters typedef struct{ uint16_t Xstart; uint16_t Ystart; uint8_t size; } imsubframe; +// all data of image stored typedef struct{ store_type st; // how would files be stored - char *imname; - int binning; + char *imname; // image basename + image_format imformat; image_type imtype; double exptime; - int dump; + int binning; imsubframe *subframe; size_t W, H; // image size uint16_t *imdata; // image data itself } imstorage; -imstorage *chk_storeimg(char *filename, char* store); +imstorage *chk_storeimg(char *filename, char* store, char *format); int store_image(imstorage *filename); void print_stat(imstorage *img); uint16_t *get_imdata(imstorage *img); diff --git a/main.c b/main.c index 0be9199..e47185b 100644 --- a/main.c +++ b/main.c @@ -63,9 +63,9 @@ int main(int argc, char **argv){ ERRX(_("Error defining subframe")); G->binning = 0xff; // take subframe } - imstorage *img = chk_storeimg(G->outpfname, G->imstoretype); + imstorage *img = chk_storeimg(G->outpfname, G->imstoretype, G->imformat); if(img){ - if(G->dumpbin) img->dump = 1; + DBG("OK"); img->subframe = F; img->exptime = G->exptime; img->binning = G->binning; @@ -77,7 +77,7 @@ int main(int argc, char **argv){ }else{ print_stat(img); if(store_image(img)) - WARNX(_("Error storing image %s"), img->imname); + WARNX(_("Error storing image")); } } FREE(img->imname); diff --git a/term.c b/term.c index 3e564aa..4f38ddd 100644 --- a/term.c +++ b/term.c @@ -462,6 +462,7 @@ imsubframe *define_subframe(char *parm){ * @return 0 if all OK */ int start_exposition(imstorage *im, char *imtype){ + FNAME(); double exptime = im->exptime; uint64_t exp100us = exptime * 10000.; uint8_t cmd[6] = {CMD_TAKE_IMAGE};