diff --git a/.gitignore b/.gitignore index c6127b3..b826f2d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ +# examples template: +examples/template_c + +# QT-creator files: +*.config +*.creator +*.creator.user +*.files +*.includes + # Prerequisites *.d diff --git a/CMake.readme b/CMake.readme new file mode 100644 index 0000000..e7c8ccd --- /dev/null +++ b/CMake.readme @@ -0,0 +1,4 @@ +cmake defines: +-DDEBUG=1 - debug mode +-DNOGETTEXT=1 - don't run xgettext +-DEXAMPLES=1 - to compile examples diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c74d40b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,156 @@ +cmake_minimum_required(VERSION 3.9) +set(PROJ FITSmanip) +set(MINOR_VERSION "1") +set(MID_VERSION "0") +set(MAJOR_VERSION "0") +set(PROJ_VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") + +project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C) + +# default flags +set(CMAKE_C_FLAGS "${CFLAGS} -O2") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") +set(CMAKE_COLOR_MAKEFILE ON) + +find_package(OpenMP) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") +endif() + +# cmake -DDEBUG=1 -> debugging +if(DEFINED DEBUG AND DEBUG EQUAL 1) + set(CMAKE_BUILD_TYPE "Debug") +else() + set(CMAKE_BUILD_TYPE "Release") +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DEBUG) + set(CMAKE_VERBOSE_MAKEFILE true) + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + message("install to ${CMAKE_CURRENT_SOURCE_DIR}/install ") + set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install) + endif() + set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_DEBUG}) +else() + set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS_RELEASE}) +endif() + +message("Build type: ${CMAKE_BUILD_TYPE}") + +# here is one of two variants: all .c in directory or .c files in list +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) + +# directory should contain dir locale/ru for gettext translations +set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) +if(NOT DEFINED LOCALEDIR) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(LOCALEDIR ${CMAKE_CURRENT_SOURCE_DIR}/locale) + else() + set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale) + endif() +endif() + +# find CFITSIO +SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) +FIND_PACKAGE(CFITSIO REQUIRED) + +###### pkgconfig ###### +# pkg-config modules (for pkg-check-modules) +#set(MODULES cfitsio fftw3) +# find packages: +find_package(PkgConfig REQUIRED) +pkg_check_modules(${PROJ} REQUIRED usefull_macros) + +# external modules like OpenMP: +include(FindOpenMP) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_definitions(-DOMP_FOUND) +endif() +###### additional flags ###### +#list(APPEND ${PROJ}_LIBRARIES "-lfftw3_threads") + +# gettext files +set(PO_FILE ${LCPATH}/messages.po) +set(MO_FILE ${LCPATH}/LC_MESSAGES/${PROJ}.mo) +set(RU_FILE ${LCPATH}/ru.po) + +# exe file +#add_executable(${PROJ} ${SOURCES}) +# library +add_library(${PROJ} SHARED ${SOURCES}) +# library header files +set(LIBHEADER "FITSmanip.h") +# -I +include_directories(${${PROJ}_INCLUDE_DIRS}) +# -L +link_directories(${${PROJ}_LIBRARY_DIRS}) +# -D +add_definitions(-DLOCALEDIR=\"${LOCALEDIR}\" + -DPACKAGE_VERSION=\"${PROJ_VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" + -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" + -DMAJOR_VERSION=\"${MAJOR_VESION}\") + +# -l +target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES}) + +set(PCFILE "${CMAKE_BINARY_DIR}/${PROJ}.pc") +configure_file("${PROJ}.pc.in" ${PCFILE} @ONLY) + +set_target_properties(${PROJ} PROPERTIES VERSION ${PROJ_VERSION}) +set_target_properties(${PROJ} PROPERTIES PUBLIC_HEADER ${LIBHEADER}) + +# Installation of the program +include(GNUInstallDirs) +#install(TARGETS ${PROJ} DESTINATION "bin") +install(TARGETS ${PROJ} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES ${PCFILE} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) + +# EXAMPLES +if(DEFINED EXAMPLES AND EXAMPLES EQUAL 1) + add_subdirectory(examples) +endif() + +###### gettext ###### +if(NOT DEFINED NOGETTEXT) + add_definitions(-DGETTEXT) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + message("Generate locale files @ make") + find_package(Gettext REQUIRED) + find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) + if(NOT GETTEXT_XGETTEXT_EXECUTABLE OR NOT GETTEXT_MSGFMT_EXECUTABLE) + message(FATAL_ERROR "xgettext not found") + endif() + file(MAKE_DIRECTORY ${LCPATH}) + file(MAKE_DIRECTORY ${LCPATH}/LC_MESSAGES) + + add_custom_command( + OUTPUT ${PO_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=utf-8 ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} + COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE} + COMMAND enconv ${PO_FILE} + DEPENDS ${SOURCES} + ) + # we need this to prevent ru.po & .mo from deleting by make clean + add_custom_target( + RU_FILE + COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} + DEPENDS ${PO_FILE} ${SOURCES} + ) + add_custom_target( + MO_FILE + COMMAND make RU_FILE && ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE} + DEPENDS ${RU_FILE} + ) + add_dependencies(${PROJ} MO_FILE) + else() # install .mo file + install(FILES ${MO_FILE} DESTINATION "${LOCALEDIR}/ru/LC_MESSAGES") + endif() +endif(NOT DEFINED NOGETTEXT) diff --git a/FITSmanip.c b/FITSmanip.c new file mode 100644 index 0000000..9d0aa7e --- /dev/null +++ b/FITSmanip.c @@ -0,0 +1,29 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ +#include "FITSmanip.h" +#include "local.h" +#include +#include +#include + +static inline void initomp(){ + int cpunumber = sysconf(_SC_NPROCESSORS_ONLN); + if(omp_get_max_threads() != cpunumber) + omp_set_num_threads(cpunumber); +} + diff --git a/FITSmanip.h b/FITSmanip.h new file mode 100644 index 0000000..7ba41ef --- /dev/null +++ b/FITSmanip.h @@ -0,0 +1,164 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include +#include +#include + +/************************************************************************************** + * fits.c * + **************************************************************************************/ + +//typedef double Item; + +#define FLEN_FORMAT (12) + +/* +cfitsio.h BITPIX code values for FITS image types: +#define BYTE_IMG 8 +#define SHORT_IMG 16 +#define LONG_IMG 32 +#define LONGLONG_IMG 64 +#define FLOAT_IMG -32 +#define DOUBLE_IMG -64 +*/ +// FilterType (not only convolution!) +typedef enum{ + FILTER_NONE = 0 // simple start + ,MEDIAN // median filter + ,ADPT_MEDIAN // simple adaptive median + ,LAPGAUSS // laplasian of gaussian + ,GAUSS // gaussian + ,SOBELH // Sobel horizontal + ,SOBELV // -//- vertical + ,SIMPLEGRAD // simple gradient (by Sobel) + ,PREWITTH // Prewitt (horizontal) - simple derivative + ,PREWITTV // -//- (vertical) + ,SCHARRH // Scharr (modified Sobel) + ,SCHARRV + ,STEP // "posterisation" +} FType; + +typedef struct{ + double *data; + size_t size; +}Itmarray; + +typedef struct klist_{ + char *record; + struct klist_ *next; + struct klist_ *last; +} KeyList; + +typedef struct{ + void *contents; // contents of table + int coltype; // type of columns + long width; // data width + long repeat; // amount of rows -> 'contents' size = width*repeat + char colname[FLEN_KEYWORD]; // column name (arg ttype of fits_create_tbl) + char format[FLEN_FORMAT]; // format codes (tform) + char unit[FLEN_CARD]; // units (tunit) +}table_column; + +typedef struct{ + int ncols; // amount of columns + long nrows; // max amount of rows + char tabname[FLEN_CARD]; // table name + table_column *columns; // array of structures 'table_column' +}FITStable; +/* +typedef struct{ + size_t amount; // amount of tables in file + FITStable **tables; // array of pointers to tables +} FITStables; +*/ +typedef struct{ + int width; // width + int height; // height + int dtype; // picture data type + void *data; // picture data +} FITSimage; + +typedef struct{ + fitsfile *fp; // cfitsio file structure + int Nimages; // amount of images in file + FITSimage **images; // image array + int Ntables; // amount of tables in file + FITStable **tables; // table array + KeyList *keylist; // list of options for each key +} FITS; + +typedef struct _Filter{ + char *name; // filter name + FType FilterType; // filter type + int w; // filter width + int h; // height + double sx; // x half-width + double sy; // y half-width (sx, sy - for Gaussian-type filters) + FITS* (*imfunc)(FITS *in, struct _Filter *f, Itmarray *i); // image function for given conversion type +} Filter; + +// mathematical operations when there's no '-i' parameter (for >1 FITS-files) +typedef enum{ + MATH_NONE = 0 + ,MATH_SUM // make sum of all files + ,MATH_MEDIAN // calculate median by all files + ,MATH_MEAN // calculate mean for all files + ,MATH_DIFF // difference of first and rest files +} MathOper; + +void keylist_free(KeyList **list); +KeyList *keylist_add_record(KeyList **list, char *rec); +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); +void keylist_remove_records(KeyList **list, char *sample); +KeyList *keylist_copy(KeyList *list); +KeyList *keylist_get_end(KeyList *list); +void keylist_print(KeyList *list); + +void table_free(FITStable **tbl); +FITStable *table_new(char *tabname); +FITStable *table_read(FITS *img); +FITStable *table_addcolumn(FITStable *tbl, table_column *column); +bool table_write(FITS *fits); +void table_print(FITStable *tbl); +void table_print_all(FITS *fits); + +void image_free(FITSimage **ima); +FITSimage *image_new(size_t h, size_t w, int dtype); +FITSimage *image_mksimilar(FITSimage *in, int dtype); +FITSimage *image_copy(FITSimage *in); +FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata); + +void fits_free(FITS **fits); +FITS *fits_read(char *filename); +bool fits_write(char *filename, FITS *fits); + +/************************************************************************************** + * fileops.c * + **************************************************************************************/ + +char* make_filename(char *buff, size_t buflen, char *prefix, char *suffix); +bool file_is_absent(char *name); + + + + +// pointer to image conversion function +typedef FITS* (*imfuncptr)(FITS *in, Filter *f, Itmarray *i); diff --git a/FITSmanip.pc.in b/FITSmanip.pc.in new file mode 100644 index 0000000..441b10e --- /dev/null +++ b/FITSmanip.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: @PROJ@ +Description: Library with a lot of usefull snippets +Version: @VERSION@ +Libs: -L${libdir} -l@PROJ@ +Cflags: -I${includedir} diff --git a/FindCFITSIO.cmake b/FindCFITSIO.cmake new file mode 100644 index 0000000..01dd612 --- /dev/null +++ b/FindCFITSIO.cmake @@ -0,0 +1,67 @@ +# - Try to find CFITSIO +# Once done this will define +# +# CFITSIO_FOUND - system has CFITSIO +# CFITSIO_INCLUDE_DIR - the CFITSIO include directory +# CFITSIO_LIBRARIES - Link these to use CFITSIO +# CFITSIO_VERSION_STRING - Human readable version number of cfitsio +# CFITSIO_VERSION_MAJOR - Major version number of cfitsio +# CFITSIO_VERSION_MINOR - Minor version number of cfitsio + +# Copyright (c) 2006, Jasem Mutlaq +# Based on FindLibfacile by Carsten Niehaus, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + # in cache already, be quiet + set(CFITSIO_FIND_QUIETLY TRUE) + +else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + # JM: Packages from different distributions have different suffixes + find_path(CFITSIO_INCLUDE_DIR fitsio.h + PATH_SUFFIXES libcfitsio3 libcfitsio0 cfitsio + PATHS + $ENV{CFITSIO} + ${_obIncDir} + ${GNUWIN32_DIR}/include + ) + + find_library(CFITSIO_LIBRARIES NAMES cfitsio + PATHS + $ENV{CFITSIO} + ${_obLinkDir} + ${GNUWIN32_DIR}/lib + ) + + if(CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + set(CFITSIO_FOUND TRUE) + else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + set(CFITSIO_FOUND FALSE) + endif(CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + + if (CFITSIO_FOUND) + + # Find the version of the cfitsio header + FILE(READ "${CFITSIO_INCLUDE_DIR}/fitsio.h" FITSIO_H) + STRING(REGEX REPLACE ".*#define CFITSIO_VERSION[^0-9]*([0-9]+)\\.([0-9]+).*" "\\1.\\2" CFITSIO_VERSION_STRING "${FITSIO_H}") + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\1" CFITSIO_VERSION_MAJOR ${CFITSIO_VERSION_STRING}) + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\2" CFITSIO_VERSION_MINOR ${CFITSIO_VERSION_STRING}) + message(STATUS "found version string ${CFITSIO_VERSION_STRING}") + + if (NOT CFITSIO_FIND_QUIETLY) + message(STATUS "Found CFITSIO ${CFITSIO_VERSION_MAJOR}.${CFITSIO_VERSION_MINOR}: ${CFITSIO_LIBRARIES}") + endif (NOT CFITSIO_FIND_QUIETLY) + else (CFITSIO_FOUND) + if (CFITSIO_FIND_REQUIRED) + message(STATUS "CFITSIO not found.") + endif (CFITSIO_FIND_REQUIRED) + endif (CFITSIO_FOUND) + + mark_as_advanced(CFITSIO_INCLUDE_DIR CFITSIO_LIBRARIES) + +endif (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) diff --git a/README.md b/README.md index 4267215..d991b7a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # fitsmaniplib FITS images manipulation library + +## Pre-pre-pre-alpha stage!!! + +I hope, in future this library would be written and allow to read/write FITS files with images and tables, +make simple image transformations and so on. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..ee0b966 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.9) +project(examples) +include_directories(../) +link_libraries(usefull_macros) + +#add_executable(fitsstat fitsstat.c) +add_executable(keylist keylist.c) diff --git a/examples/common.h b/examples/common.h new file mode 100644 index 0000000..b555808 --- /dev/null +++ b/examples/common.h @@ -0,0 +1,28 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ +#pragma once +#ifndef __COMMON_H +#define __COMMON_H + +#include +#include +#include +#include + + +#endif // __COMMON_H diff --git a/examples/fitsstat.c b/examples/fitsstat.c new file mode 100644 index 0000000..5f4e9a6 --- /dev/null +++ b/examples/fitsstat.c @@ -0,0 +1,30 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include "common.h" + +int main(){ + setlocale(LC_ALL, ""); + setlocale(LC_NUMERIC, "C"); + #if defined GETTEXT_PACKAGE && defined LOCALEDIR + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + textdomain(GETTEXT_PACKAGE); + #endif + + return 0; +} diff --git a/examples/keylist.c b/examples/keylist.c new file mode 100644 index 0000000..b05a070 --- /dev/null +++ b/examples/keylist.c @@ -0,0 +1,84 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include "common.h" + +typedef struct{ + char *fitsname; + int list; + char **addrec; +} glob_pars; + +/* + * here are global parameters initialisation + */ +int help; +glob_pars G; /* = { + ; +};*/ + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +myoption cmdlnopts[] = { +// common options + {"help", NO_ARGS, NULL, 'h', arg_int, APTR(&help), _("show this help")}, + {"fitsname",NEED_ARG, NULL, 'i', arg_string, APTR(&G.fitsname), _("name of input file")}, + {"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)")}, + end_option +}; + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + int i; + char *helpstring = "Usage: %%s [args]\n\n\tWhere args are:\n"; + change_helpstring(helpstring); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + for (i = 0; i < argc; i++) + printf("Ignore extra argument: %s\n", argv[i]); + } + return &G; +} + +int main(int argc, char *argv[]){ + initial_setup(); + parse_args(argc, argv); + if(!G.fitsname) ERRX(_("No input filename given!")); + printf("Name: %s\n", G.fitsname); + if(G.list) printf("List\n"); + if(G.addrec){ + char **ptr = G.addrec; + while(*ptr){ + printf("record: %s\n", *ptr); + ++ptr; + } + } + return 0; +} + diff --git a/fileops.c b/fileops.c new file mode 100644 index 0000000..fd953e0 --- /dev/null +++ b/fileops.c @@ -0,0 +1,43 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#include "FITSmanip.h" +#include "local.h" + +/** + * Return TRUE if file _name_ not exists + */ +bool file_is_absent(char *name){ + struct stat filestat; + if(!stat(name, &filestat)) return FALSE; + if(errno == ENOENT) return TRUE; + return FALSE; +} +/** + * find the first non-existing filename like prefixXXXX.suffix & put it into buff + */ +char* make_filename(char *buff, size_t buflen, char *prefix, char *suffix){ + int num; + for(num = 1; num < 10000; ++num){ + if(snprintf(buff, buflen, "%s_%04d.%s", prefix, num, suffix) < 1) + return NULL; + if(file_is_absent(buff)) // OK, file not exists + return buff; + } + return NULL; +} diff --git a/fits.c b/fits.c new file mode 100644 index 0000000..a4b3137 --- /dev/null +++ b/fits.c @@ -0,0 +1,879 @@ +/* + * fits.c - cfitsio routines + * + * Copyright 2015 Edward V. Emelianov + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include "local.h" +#include "FITSmanip.h" + +#include +#include +#include +#include + +/************************************************************************************** + * FITS keywords * + **************************************************************************************/ + +/* + * TODO: + * - int fits_parse_value(char *card, char *value, char *comment, int *status) + * will return value and comment of given record + * - int fits_get_keytype(char *value, char *dtype, int *status) + * dtype returns with a value of 'C', 'L', 'I', 'F' or 'X', for character string, + * logical, integer, floating point, or complex, respectively + * - int fits_get_keyclass(char *card) returns a classification code of keyword record + * - int fits_parse_template(char *template, char *card, int *keytype, int *status) + * makes record from template + */ + +/** + * @brief keylist_get_end - find last element in list + * @param list (i) - pointer to first element of list + * @return pointer to last element or NULL + */ +KeyList *keylist_get_end(KeyList *list){ + if(!list) return NULL; + if(list->last) return list->last; + KeyList *first = list; + while(list->next) list = list->next; + first->last = list; + return list; +} + +/** + * @brief keylist_add_record - add record to keylist + * @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 + */ +KeyList *keylist_add_record(KeyList **list, char *rec){ + KeyList *node, *last; + if((node = (KeyList*) MALLOC(KeyList, 1)) == 0) return NULL; // allocation error + node->record = strdup(rec); // insert data + if(!node->record){ + /// "Не могу скопировать данные" + WARNX(_("Can't copy data")); + return NULL; + } + if(list){ + if(*list){ // there was root node - search last + last = keylist_get_end(*list); + last->next = node; // insert pointer to new node into last element in list + (*list)->last = node; + // DBG("last node %s", (*list)->last->record); + }else *list = node; + } + return node; +} + +/** + * @brief keylist_find_key - find record with given key + * @param list (i) - pointer to first list element + * @param key (i) - key to find + * @return record with given key or NULL + */ +KeyList *keylist_find_key(KeyList *list, char *key){ + if(!list || !key) return NULL; + size_t L = strlen(key); + do{ + if(list->record){ + if(strncmp(list->record, key, L) == 0){ // key found + return list; + } + } + list = list->next; + }while(list); + return NULL; +} + +/** + * @brief keylist_modify_key - modify key value + * @param list (i) - pointer to first list element + * @param key (i) - key name + * @param newval (i) - new value of given key + * @return modified record or NULL if given key is absent + */ +KeyList *keylist_modify_key(KeyList *list, char *key, char *newval){ + // TODO: look modhead.c in cexamples for protected keys + char buf[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); + FREE(rec->record); + rec->record = strdup(buf); + return rec; +} + +/** + * @brief keylist_remove_key - remove record by key + * @param keylist (io) - address of pointer to first list element + * @param key (i) - key value + */ +void keylist_remove_key(KeyList **keylist, char *key){ + if(!keylist || !*keylist || !key) return; + size_t L = strlen(key); + KeyList *prev = NULL, *list = *keylist, *last = keylist_get_end(list); + do{ + if(list->record){ + if(strncmp(list->record, key, L) == 0){ // key found + if(prev){ // not first record + prev->next = list->next; + }else{ // first or only record + if(*keylist == last){ + *keylist = NULL; // the only record - erase it + }else{ // first record - modyfy heading record + *keylist = list->next; + (*keylist)->last = last; + } + } + DBG("remove record by key \"%s\":\n%s",key, list->record); + FREE(list->record); + FREE(list); + return; + } + } + prev = list; + list = list->next; + }while(list); +} + +/** + * @brief keylist_remove_records - remove records by any sample + * @param keylist (io) - address of pointer to first list element + * @param sample (i) - any (case sensitive) substring of record to be delete + */ +void keylist_remove_records(KeyList **keylist, char *sample){ + if(!keylist || !sample) return; + KeyList *prev = NULL, *list = *keylist, *last = keylist_get_end(list); + DBG("remove %s", sample); + do{ + if(list->record){ + if(strstr(list->record, sample)){ // key found + if(prev){ + prev->next = list->next; + }else{ + if(*keylist == last){ + *keylist = NULL; // the only record - erase it + }else{ // first record - modyfy heading record + *keylist = list->next; + (*keylist)->last = last; + } + } + KeyList *tmp = list->next; + FREE(list->record); + FREE(list); + list = tmp; + continue; + } + } + prev = list; + list = list->next; + }while(list); +} + +/** + * @brief keylist_free - free list memory & set it to NULL + * @param list (io) - address of pointer to first list element + */ +void keylist_free(KeyList **list){ + KeyList *node = *list, *next; + if(!list || !*list) return; + do{ + next = node->next; + FREE(node->record); + free(node); + node = next; + }while(node); + *list = NULL; +} + +/** + * @brief keylist_copy - make a full copy of given list + * @param list (i) - pointer to first list element + * @return copy of list + */ +KeyList *keylist_copy(KeyList *list){ + if(!list) return NULL; + KeyList *newlist = NULL; + #ifdef EBUG + int n = 0; + #endif + do{ + keylist_add_record(&newlist, list->record); + list = list->next; + #ifdef EBUG + ++n; + #endif + }while(list); + DBG("copy list of %d entries", n); + return newlist; +} + +/** + * @brief keylist_print - print out given list + * @param list (i) - pointer to first list element + */ +void keylist_print(KeyList *list){ + while(list){ + printf("%s\n", list->record); + list = list->next; + } +} + +/************************************************************************************** + * FITS tables * + **************************************************************************************/ + +/** + * @brief table_free - free memory of table + * @param tbl (io) - address of pointer to table + */ +void table_free(FITStable **tbl){ + if(!tbl || !*tbl) return; + FITStable *intab = *tbl; + size_t i, N = intab->ncols; + for(i = 0; i < N; ++i){ + table_column *col = &(intab->columns[i]); + if(col->coltype == TSTRING && col->width){ + size_t r, R = col->repeat; + void **cont = (void**) col->contents; + for(r = 0; r < R; ++r) free(*(cont++)); + } + FREE(col->contents); + FREE(col); + } + FREE(*tbl); +} + +/** + * @brief table_copy - make full copy of FITStable structure + * @param intab (i) - pointer to table + * @return pointer to copy of table + */ +FITStable *table_copy(FITStable *intab){ + if(!intab || intab->ncols == 0 || intab->nrows == 0) return NULL; + FITStable *tbl = MALLOC(FITStable, 1); + memcpy(tbl, intab, sizeof(FITStable)); + size_t ncols = intab->ncols, col; + tbl->columns = MALLOC(table_column, ncols); + memcpy(tbl->columns, intab->columns, sizeof(table_column)*ncols); + table_column *ocurcol = tbl->columns, *icurcol = intab->columns; + for(col = 0; col < ncols; ++col, ++ocurcol, ++icurcol){ + if(ocurcol->coltype == TSTRING && ocurcol->width){ // string array - copy all + size_t r, R = ocurcol->repeat; + char **oarr = (char**)ocurcol->contents, **iarr = (char**)icurcol->contents; + for(r = 0; r < R; ++r, ++oarr, ++iarr){ + *oarr = strdup(*iarr); + if(!oarr) ERR(_("strdup() failed!")); + } + }else memcpy(ocurcol->contents, icurcol->contents, icurcol->repeat * icurcol->width); + } + return tbl; +} + +/** + * @brief table_read - add FITS table to image structure + * @param fits (i) - pointer to FITS file structure + * @return + */ +FITStable *table_read(FITS *fits){ + int ncols, i, fst, ret; + long nrows; + char extname[FLEN_VALUE]; + fitsfile *fp = fits->fp; + fits_get_num_rows(fp, &nrows, &fst); + if(fst){fits_report_error(stderr, fst); return NULL;} + fits_get_num_cols(fp, &ncols, &fst); + if(fst){fits_report_error(stderr, fst); return NULL;} + fits_read_key(fp, TSTRING, "EXTNAME", extname, NULL, &fst); + if(fst){fits_report_error(stderr, fst); return NULL;} + DBG("Table named %s with %ld rows and %d columns", extname, nrows, ncols); + FITStable *tbl = table_new(extname); + if(!tbl) return NULL; + for(i = 1; i <= ncols; ++i){ + int typecode; + long repeat, width; + ret = fits_get_coltype(fp, i, &typecode, &repeat, &width, &fst); + if(fst){fits_report_error(stderr, fst); ret = fst;} + if(ret){ + WARNX(_("Can't read column %d!"), i); + continue; + } + DBG("typecode=%d, repeat=%ld, width=%ld", typecode, repeat, width); + table_column col = {.repeat = repeat, .width = width, .coltype = typecode}; + void *array = malloc(width*repeat); + if(!array) ERRX("malloc"); + int anynul; + int64_t nullval = 0; + int j; + for(j = 0; j < repeat; ++j){ + ret = fits_read_col(fp, typecode, i, j=1, 1, 1, (void*)nullval, array, &anynul, &fst); + if(fst){fits_report_error(stderr, fst); ret = fst;} + if(ret){ + WARNX(_("Can't read column %d row %d!"), i, j); + continue; + } + } + DBG("done"); + col.contents = array; + char keyword[FLEN_KEYWORD]; + int stat = 0; + fits_make_keyn("TTYPE", i, keyword, &stat); + if(stat){WARNX(_("Can't read table data type")); stat = 0;} + fits_read_key(fp, TSTRING, keyword, col.colname, NULL, &stat); + if(stat){ sprintf(col.colname, "noname"); stat = 0;} + fits_make_keyn("TUNIT", i, keyword, &stat); + if(stat){WARNX(_("Can't read table data unit")); stat = 0;} + fits_read_key(fp, TSTRING, keyword, col.unit, NULL, &stat); + if(stat) *col.unit = 0; + DBG("Column, cont[2]=%d, type=%d, w=%ld, r=%ld, nm=%s, u=%s", ((int*)col.contents)[2], col.coltype, + col.width, col.repeat, col.colname, col.unit); + //table_addcolumn(tbl, &col); + FREE(array); + } + int N = fits->Ntables + 1; + if(!(fits->tables = realloc(fits->tables, sizeof(FITStable**)*N))) + ERR("realloc()"); + fits->tables[fits->Ntables++] = tbl; + return tbl; +} + +/** + * @brief table_new - create empty FITS table + * @param tabname (i) - table name + * @return + */ +FITStable *table_new(char *tabname){ + FITStable *tab = MALLOC(FITStable, 1); + snprintf(tab->tabname, FLEN_CARD, "%s", tabname); + DBG("add new table: %s", tabname); + return tab; +} + +/** + * @brief table_addcolumn - add to table 'tbl' column 'column' + * Be carefull! All fields of 'column' exept of 'format' should be filled + * - if data is character array, 'width' should be equal 0 + * - all input data will be copied, so caller should run 'free' after this function! + * @param tbl (i) - pointer to table + * @param column (i) - column to add + * @return + */ +FITStable *table_addcolumn(FITStable *tbl, table_column *column){ + FNAME(); + if(!tbl || !column || !column->contents) return NULL; + long nrows = column->repeat; + int width = column->width; + if(tbl->nrows < nrows) tbl->nrows = nrows; + size_t datalen = nrows * width, cols = ++tbl->ncols; + char *curformat = column->format; + DBG("add column; width: %d, nrows: %ld, name: %s", width, nrows, column->colname); + /*void convchar(){ // count maximum length of strings in array + char **charr = (char**)column->contents, *dptr = charr; + size_t n, N = column->repeat; + for(n = 0; n < N; ++n){ + if(strlen( + if(*dptr++ == 0){ --processed; if(len > maxlen) maxlen = len; len = 0; } + else{ ++len; } + } + }*/ + #define CHKLEN(type) do{if(width != sizeof(type)) datalen = sizeof(type) * nrows;}while(0) + switch(column->coltype){ + case TBIT: + snprintf(curformat, FLEN_FORMAT, "%ldX", nrows); + CHKLEN(int8_t); + break; + case TBYTE: + snprintf(curformat, FLEN_FORMAT, "%ldB", nrows); + CHKLEN(int8_t); + break; + case TLOGICAL: + snprintf(curformat, FLEN_FORMAT, "%ldL", nrows); + CHKLEN(int8_t); + break; + case TSTRING: + if(width == 0){ + snprintf(curformat, FLEN_FORMAT, "%ldA", nrows); + datalen = nrows; + }else + snprintf(curformat, FLEN_FORMAT, "%ldA%d", nrows, width); + break; + case TSHORT: + snprintf(curformat, FLEN_FORMAT, "%ldI", nrows); + CHKLEN(int16_t); + break; + case TLONG: + snprintf(curformat, FLEN_FORMAT, "%ldJ", nrows); + CHKLEN(int32_t); + break; + case TLONGLONG: + snprintf(curformat, FLEN_FORMAT, "%ldK", nrows); + CHKLEN(int64_t); + break; + case TFLOAT: + snprintf(curformat, FLEN_FORMAT, "%ldE", nrows); + CHKLEN(float); + break; + case TDOUBLE: + snprintf(curformat, FLEN_FORMAT, "%ldD", nrows); + CHKLEN(double); + break; + case TCOMPLEX: + snprintf(curformat, FLEN_FORMAT, "%ldM", nrows); + if(width != sizeof(float)*2) datalen = sizeof(float) * nrows * 2; + break; + case TDBLCOMPLEX: + snprintf(curformat, FLEN_FORMAT, "%ldM", nrows); + if(width != sizeof(double)*2) datalen = sizeof(double) * nrows * 2; + break; + case TINT: + snprintf(curformat, FLEN_FORMAT, "%ldJ", nrows); + CHKLEN(int32_t); + break; + case TSBYTE: + snprintf(curformat, FLEN_FORMAT, "%ldS", nrows); + CHKLEN(int8_t); + break; + case TUINT: + snprintf(curformat, FLEN_FORMAT, "%ldV", nrows); + CHKLEN(int32_t); + break; + case TUSHORT: + snprintf(curformat, FLEN_FORMAT, "%ldU", nrows); + CHKLEN(int16_t); + break; + default: + WARNX(_("Unsupported column data type!")); + return NULL; + } + #undef CHKLEN + DBG("new size: %ld, old: %ld", sizeof(table_column)*cols, sizeof(table_column)*(cols-1)); + if(!(tbl->columns = realloc(tbl->columns, sizeof(table_column)*cols))) ERRX("malloc"); + table_column *newcol = &(tbl->columns[cols-1]); + memcpy(newcol, column, sizeof(table_column)); + newcol->contents = calloc(datalen, 1); + if(!newcol->contents) ERRX("malloc"); + DBG("copy %zd bytes", datalen); + if(column->coltype == TSTRING && width){ + long n; + char **optr = (char**)newcol->contents, **iptr = (char**)column->contents; + for(n = 0; n < nrows; ++n, ++optr, ++iptr) *optr = strdup(*iptr); + }else + memcpy(newcol->contents, column->contents, datalen); + return tbl; +} + +/** + * @brief table_print - print out contents of table + * @param tbl (i) - pointer to table to print + */ +void table_print(FITStable *tbl){ + printf("\nTable name: %s\n", tbl->tabname); + int c, cols = tbl->ncols; + long r, rows = tbl->nrows; + for(c = 0; c < cols; ++c){ + printf("%s", tbl->columns[c].colname); + if(*tbl->columns[c].unit) printf(" (%s)", tbl->columns[c].unit); + printf("\t"); + } + printf("\n"); + for(r = 0; r < rows; ++r){ + for(c = 0; c < cols; ++c){ + double *dpair; float *fpair; + table_column *col = &(tbl->columns[c]); + if(col->repeat < r){ // table with columns of different length + printf("(empty)\t"); + continue; + } + switch(col->coltype){ + case TBIT: + case TBYTE: + printf("%u\t", ((uint8_t*)col->contents)[r]); + break; + case TLOGICAL: + printf("%s\t", ((int8_t*)col->contents)[r] == 0 ? "FALSE" : "TRUE"); + break; + case TSTRING: + if(col->width == 0) printf("%c\t", ((char*)col->contents)[r]); + else printf("%s\t", ((char**)col->contents)[r]); + break; + case TSHORT: + printf("%d\t", ((int16_t*)col->contents)[r]); + break; + case TLONG: + case TINT: + printf("%d\t", ((int32_t*)col->contents)[r]); + break; + case TLONGLONG: + printf("%zd\t", ((int64_t*)col->contents)[r]); + break; + case TFLOAT: + printf("%g\t", ((float*)col->contents)[r]); + break; + case TDOUBLE: + printf("%g\t", ((double*)col->contents)[r]); + break; + case TCOMPLEX: + fpair = (float*)col->contents + 2*r; + printf("%g %s %g*i\t", fpair[0], fpair[1] > 0 ? "+" : "-", fpair[1]); + break; + case TDBLCOMPLEX: + dpair = (double*)col->contents + 2*r; + printf("%g %s %g*i\t", dpair[0], dpair[1] > 0 ? "+" : "-", dpair[1]); + break; + case TSBYTE: + printf("%d\t", ((int8_t*)col->contents)[r]); + break; + case TUINT: + printf("%d\t", ((uint32_t*)col->contents)[r]); + break; + case TUSHORT: + printf("%d\t", ((uint16_t*)col->contents)[r]); + break; + } + } + printf("\n"); + } +} + +/** + * @brief table_print_all - print out all tables in given FITS file + * @param fits - pointer to given file structure + */ +void table_print_all(FITS *fits){ + size_t i, N = fits->Ntables; + if(N == 0) return; + for(i = 0; i < N; ++i) + table_print(fits->tables[i]); +} + +/** + * @brief table_write - write tables to FITS file + * @param fits (i) - pointer to FITS file structure + */ +bool table_write(FITS *file){ + FNAME(); + size_t N = file->Ntables, i; + if(N == 0) return FALSE; + fitsfile *fp = file->fp; + for(i = 0; i < N; ++i){ + int fst; + FITStable *tbl = file->tables[i]; + size_t c, cols = tbl->ncols; + char **columns = MALLOC(char*, cols); + char **formats = MALLOC(char*, cols); + char **units = MALLOC(char*, cols); + table_column *col = tbl->columns; + for(c = 0; c < cols; ++c, ++col){ + columns[c] = col->colname; + formats[c] = col->format; + units[c] = col->unit; + DBG("col: %s, form: %s, unit: %s", columns[c], formats[c], units[c]); + } + //fits_movabs_hdu(fptr, 2, &hdutype, &status) + int ret = fits_create_tbl(fp, BINARY_TBL, tbl->nrows, cols, + columns, formats, units, tbl->tabname, &fst); + if(fst){fits_report_error(stderr, fst); ret = fst;} + FREE(columns); FREE(formats); FREE(units); + if(ret){ + WARNX(_("Can't write table %s!"), tbl->tabname); + return FALSE; + } + //col = tbl->columns; + for(c = 0; c < cols; ++c, ++col){ + DBG("write column %zd", c); + int fst; + fits_write_col(fp, col->coltype, c+1, 1, 1, col->repeat, col->contents, &fst); + if(fst){ + fits_report_error(stderr, fst); + WARNX(_("Can't write column %s!"), col->colname); + return FALSE; + } + } + } + return TRUE; +} + +/************************************************************************************** + * FITS files * + **************************************************************************************/ + +void fits_free(FITS **fits){ + FITS *f = *fits; + keylist_free(&f->keylist); + int n; + for(n = 0; n < f->Ntables; ++n) + table_free(&f->tables[n]); + for(n = 0; n < f->Nimages; ++n) + image_free(&f->images[n]); + FREE(*fits); +} + +/** + + TODO: these functions won't work cause on refactoring stage! + + * read FITS file and fill 'IMAGE' structure (with headers and tables) + * can't work with image stack - opens the first image met + * works only with binary tables + */ +FITS *fits_read(char *filename){ + FNAME(); + fitsfile *fp; + int hdunum = 0, fst, ret; + FITS *fits = MALLOC(FITS, 1); + fits_open_file(&fp, filename, READONLY, &fst); + if(fst){fits_report_error(stderr, fst); goto returning;} + ret = fits_get_num_hdus(fp, &hdunum, &fst); + if(fst){fits_report_error(stderr, fst);} + if(ret || hdunum < 1){ + WARNX(_("Can't read HDU")); + fst = 1; + goto returning; + } +#if 0 + // TODO: open not only images (liststruc.c from cexamples)! + // TODO: open not only 2-dimensional files! + // get image dimensions + int i, j, hdunum = 0, hdutype, nkeys, keypos, naxis; + long naxes[2]; + char card[FLEN_CARD]; + fits_get_img_param(fp, 2, &img->dtype, &naxis, naxes, &fst); + if(fst){fits_report_error(stderr, fst); goto returning;} + if(naxis > 2){ + WARNX(_("Images with > 2 dimensions are not supported")); + fst = 1; + goto returning; + } + img->width = naxes[0]; + img->height = naxes[1]; + DBG("got image %ldx%ld pix, bitpix=%d", naxes[0], naxes[1], img->dtype); + // loop through all HDUs + KeyList *list = img->keylist; + int imghdu = -1; + for(i = 1; !(fits_movabs_hdu(fp, i, &hdutype, &fst)); ++i){ + int hdutype; + fits_get_hdu_type(fp, &hdutype, &fst); + if(fst){fits_report_error(stderr, fst); continue;} + // types: IMAGE_HDU , ASCII_TBL, BINARY_TBL + DBG("HDU type %d", hdutype); + if(hdutype != IMAGE_HDU){ + //if(hdutype == BINARY_TBL){ + table_read(img, fp); + continue; + } + if(imghdu < 1) imghdu = i; + fits_get_hdrpos(fp, &nkeys, &keypos, &fst); + if(fst){fits_report_error(stderr, fst); continue;} + //DBG("HDU # %d of %d keys", i, nkeys); + for(j = 1; j <= nkeys; ++j){ + /* TODO check like this and mark all special records: + for (ii = 1; ii <= nkeys; ii++) { + fits_read_record(infptr, ii, card, &status); + if (fits_get_keyclass(card) > TYP_CMPRS_KEY) + list_add_rec... + }*/ + ret = fits_read_record(fp, j, card, &fst); + if(fst){fits_report_error(stderr, fst); ret = fst;} + if(!ret){ + if(!keylist_add_record(&list, card)){ + /// "Не могу добавить запись в список" + WARNX(_("Can't add record to list")); + } + DBG("add key %d: \"%s\"", j, card); + } + } + } + img->keylist = list; + if(fst == END_OF_FILE){ + fst = 0; + }else{ + fits_report_error(stderr, fst); + goto returning; + } + if(fits_movabs_hdu(fp, imghdu, &hdutype, &fst)){ + WARNX(_("Can't open image HDU #%d"), imghdu); + fst = 1; + goto returning; + } + size_t sz = naxes[0] * naxes[1]; + img->data = MALLOC(double, sz); + int stat = 0; + fits_read_img(fp, TDOUBLE, 1, sz, NULL, img->data, &stat, &fst); + if(fst){fits_report_error(stderr, fst);} + if(stat) WARNX(_("Found %d pixels with undefined value"), stat); + DBG("ready"); +#endif +returning: + /*if(fst){ + imfree(&img); + }*/ + ret = fits_close_file(fp, &fst); + if(fst){fits_report_error(stderr, fst);} + return fits; +} + +bool fits_write(char *filename, FITS *fits){ + if(!filename || !fits) return FALSE; + /*int w = fits->width, h = fits->height, fst; + long naxes[2] = {w, h}; + size_t sz = w * h; + fitsfile *fp; + fits_create_file(&fp, filename, &fst); + 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); + } + } + //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);}*/ + return TRUE; +} + + +/************************************************************************************** + * FITS images * + **************************************************************************************/ + +void image_free(FITSimage **img){ + FREE((*img)->data); + FREE(*img); +} + +/** + * create an empty image without headers, assign data type to "dtype" + */ +FITSimage *image_new(size_t h, size_t w, int dtype){ + size_t bufsiz = w*h; + FITSimage *out = MALLOC(FITSimage, 1); + // TODO: make allocation when reading! + // TODO: allocate input data type ? + out->data = MALLOC(double, bufsiz); + out->width = w; + out->height = h; + out->dtype = dtype; + return out; +} + +/** + * build IMAGE image from data array indata + */ +FITSimage *image_build(size_t h, size_t w, int dtype, uint8_t *indata){ + size_t stride = 0; + double (*fconv)(uint8_t *data) = NULL; + double ubyteconv(uint8_t *data){return (double)*data;} + double ushortconv(uint8_t *data){return (double)*(int16_t*)data;} + double ulongconv(uint8_t *data){return (double)*(uint32_t*)data;} + double ulonglongconv(uint8_t *data){return (double)*(uint64_t*)data;} + double floatconv(uint8_t *data){return (double)*(float*)data;} + FITSimage *out = image_new(h, w, dtype); + switch (dtype){ + case BYTE_IMG: + stride = 1; + fconv = ubyteconv; + break; + case SHORT_IMG: + stride = 2; + fconv = ushortconv; + break; + case LONG_IMG: + stride = 4; + fconv = ulongconv; + break; + case FLOAT_IMG: + stride = 4; + fconv = floatconv; + break; + case LONGLONG_IMG: + fconv = ulonglongconv; + stride = 8; + break; + case DOUBLE_IMG: + memcpy(out->data, indata, sizeof(double)*w*h); + return out; + break; + default: + /// Неправильный тип данных + ERRX(_("Wrong data type")); + } + size_t y, W = w*stride; + double *data = out->data; + OMP_FOR(shared(data)) + for(y = 0; y < h; ++y){ + double *dout = &data[y*w]; + uint8_t *din = &indata[y*W]; + size_t x; + for(x = 0; x < w; ++x, din += stride) + *dout++ = fconv(din); + } + return out; +} + +/** + * create an empty copy of image "in" without headers, assign data type to "dtype" + */ +FITSimage *image_mksimilar(FITSimage *img, int dtype){ + size_t w = img->width, h = img->height, bufsiz = w*h; + FITSimage *out = MALLOC(FITSimage, 1); + // TODO: allocate buffer as in original! + out->data = MALLOC(double, bufsiz); + out->width = w; + out->height = h; + out->dtype = dtype; + return out; +} + +/** + * make full copy of image 'in' + */ +FITSimage *image_copy(FITSimage *in){ + FITSimage *out = image_mksimilar(in, in->dtype); + // TODO: size of data as in original! + memcpy(out->data, in->data, sizeof(double)*in->width*in->height); + return out; +} + diff --git a/local.h b/local.h new file mode 100644 index 0000000..8205473 --- /dev/null +++ b/local.h @@ -0,0 +1,44 @@ +/* + * This file is part of the FITSmaniplib project. + * Copyright 2019 Edward V. Emelianov , . + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +#if defined GETTEXT +#include +#define _(String) gettext(String) +#define gettext_noop(String) String +#define N_(String) gettext_noop(String) +#else +#define _(String) (String) +#define N_(String) (String) +#endif + +#ifndef DBL_EPSILON +#define DBL_EPSILON (2.2204460492503131e-16) +#endif +#ifndef DBL_MAX +#define DBL_MAX (1.7976931348623157e+308) +#endif + +#define Stringify(x) #x +#define OMP_FOR(x) _Pragma(Stringify(omp parallel for x)) +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif + diff --git a/locale/ru/LC_MESSAGES/FITSmanip.mo b/locale/ru/LC_MESSAGES/FITSmanip.mo new file mode 100644 index 0000000..e9f1a55 Binary files /dev/null and b/locale/ru/LC_MESSAGES/FITSmanip.mo differ diff --git a/locale/ru/messages.po b/locale/ru/messages.po new file mode 100644 index 0000000..a57ba2a --- /dev/null +++ b/locale/ru/messages.po @@ -0,0 +1,87 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-14 22:15+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=koi8-r\n" +"Content-Transfer-Encoding: 8bit\n" + +#. / "Не могу скопировать данные" +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:72 +msgid "Can't copy data" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:287 +msgid "strdup() failed!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:319 +#, c-format +msgid "Can't read column %d!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:333 +#, c-format +msgid "Can't read column %d row %d!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:342 +msgid "Can't read table data type" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:346 +msgid "Can't read table data unit" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:466 +msgid "Unsupported column data type!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:599 +#, c-format +msgid "Can't write table %s!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:609 +#, c-format +msgid "Can't write column %s!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:650 +msgid "Can't read HDU" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:664 +msgid "Images with > 2 dimensions are not supported" +msgstr "" + +#. / "Не могу добавить запись в список" +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:701 +msgid "Can't add record to list" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:715 +#, c-format +msgid "Can't open image HDU #%d" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:724 +#, c-format +msgid "Found %d pixels with undefined value" +msgstr "" + +#. / Неправильный тип данных +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:841 +msgid "Wrong data type" +msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po new file mode 100644 index 0000000..dbb5559 --- /dev/null +++ b/locale/ru/ru.po @@ -0,0 +1,86 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: PACKAGE VERSION\n" + "Report-Msgid-Bugs-To: \n" + "POT-Creation-Date: 2019-02-14 22:15+0300\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: \n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=koi8-r\n" + "Content-Transfer-Encoding: 8bit\n" + +#. / "Не могу добавить запись в список" +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:701 +msgid "Can't add record to list" +msgstr "" + +#. / "Не могу скопировать данные" +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:72 +msgid "Can't copy data" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:715 +#, c-format +msgid "Can't open image HDU #%d" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:650 +msgid "Can't read HDU" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:333 +#, c-format +msgid "Can't read column %d row %d!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:319 +#, c-format +msgid "Can't read column %d!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:342 +msgid "Can't read table data type" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:346 +msgid "Can't read table data unit" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:609 +#, c-format +msgid "Can't write column %s!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:599 +#, c-format +msgid "Can't write table %s!" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:724 +#, c-format +msgid "Found %d pixels with undefined value" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:664 +msgid "Images with > 2 dimensions are not supported" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:466 +msgid "Unsupported column data type!" +msgstr "" + +#. / Неправильный тип данных +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:841 +msgid "Wrong data type" +msgstr "" + +#: /home/eddy/C-files/FITSmaniplib/sharedlib_template/fits.c:287 +msgid "strdup() failed!" +msgstr "" diff --git a/types.h b/types.h new file mode 100644 index 0000000..7fbecc9 --- /dev/null +++ b/types.h @@ -0,0 +1,95 @@ +/* + * types.h + * + * Copyright 2015 Edward V. Emelianov + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#pragma once +#ifndef __TYPES_H__ +#define __TYPES_H__ +#include "fits.h" + +#ifndef THREAD_NUMBER + #define THREAD_NUMBER 4 // default - 4 threads +#endif + +#ifndef DBL_EPSILON +#define DBL_EPSILON (2.2204460492503131e-16) +#endif +#ifndef DBL_MAX +#define DBL_MAX (1.7976931348623157e+308) +#endif + +// ITM_EPSILON is for data comparing, set it to zero for integer types +#define ITM_EPSILON DBL_EPSILON + +#define OMP_NUM_THREADS THREAD_NUMBER +#define Stringify(x) #x +#define OMP_FOR(x) _Pragma(Stringify(omp parallel for x)) +#ifndef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) +#endif +#ifndef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#endif + +// FilterType (not only convolution!) +typedef enum{ + FILTER_NONE = 0 // simple start + ,MEDIAN // median filter + ,ADPT_MEDIAN // simple adaptive median + ,LAPGAUSS // laplasian of gaussian + ,GAUSS // gaussian + ,SOBELH // Sobel horizontal + ,SOBELV // -//- vertical + ,SIMPLEGRAD // simple gradient (by Sobel) + ,PREWITTH // Prewitt (horizontal) - simple derivative + ,PREWITTV // -//- (vertical) + ,SCHARRH // Scharr (modified Sobel) + ,SCHARRV + ,STEP // "posterisation" +} FType; + +typedef struct{ + Item *data; + size_t size; +}Itmarray; + +typedef struct _Filter{ + char *name; // filter name + FType FilterType; // filter type + int w; // filter width + int h; // height + double sx; // x half-width + double sy; // y half-width (sx, sy - for Gaussian-type filters) + IMAGE* (*imfunc)(IMAGE *in, struct _Filter *f, Itmarray *i); // image function for given conversion type +} Filter; + +// mathematical operations when there's no '-i' parameter (for >1 FITS-files) +typedef enum{ + MATH_NONE = 0 + ,MATH_SUM // make sum of all files + ,MATH_MEDIAN // calculate median by all files + ,MATH_MEAN // calculate mean for all files + ,MATH_DIFF // difference of first and rest files +} MathOper; + +// pointer to image conversion function +typedef IMAGE* (*imfuncptr)(IMAGE *in, Filter *f, Itmarray *i); + +#endif // __TYPES_H__ +