diff --git a/ChangeLog b/ChangeLog index fa140ad..af5b8e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ +2022-11-09: +- added preliminary SER file implemenations, not yet tested - adding save of videodump files for debugging purposes - adding make target checkdumpfile to test and gain some basic information on videodumpfiles diff --git a/Makefile b/Makefile index b12ec91..cbf7f1c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ APP = simpleskycam -include Makefile.rules -OBJECTS := $(OBJECTS) gui.oo main.oo video.oo videoframe.oo videodev.oo videodev-v4l2.oo convert.oo filter.oo detect.oo json.oo configuration.oo +OBJECTS := $(OBJECTS) gui.oo main.oo video.oo videoframe.oo videodev.oo videodev-v4l2.oo convert.oo filter.oo detect.oo json.oo configuration.oo ser.oo DISTNAME=simpleskycam-$(VERSION) @@ -18,7 +18,8 @@ all: Makefile.rules $(TARGET) help: echo "set up configuration" echo " make configlinux to generate the linix build" - + echo " make checkdumpfile to create a test tool for videodumps." + checkdumpfile: checkdumpfile.cc c++ -o checkdumpfile checkdumpfile.cc diff --git a/checkdumpfile.cc b/checkdumpfile.cc index 25e8413..212ff90 100644 --- a/checkdumpfile.cc +++ b/checkdumpfile.cc @@ -1,3 +1,6 @@ +/* + * tool to check and gain some basic information about videodum-files + */ #include #include @@ -28,6 +31,8 @@ int main(int argc, char **argv) { if (argc != 2) { printf ("please give a file name as parameter.\n"); + printf ("checkdumpfile FILE\n"); + printf ("\n"); return -1; } diff --git a/ser.cc b/ser.cc new file mode 100644 index 0000000..ee338c8 --- /dev/null +++ b/ser.cc @@ -0,0 +1,268 @@ +/* + Video file format class according the specification of the + "SER format description version 3" found at + https://free-astro.org/images/5/51/SER_Doc_V3b.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ser.h" + +SER::SER() { + /* clear header */ + memset(&header, 0, sizeof(header)); + + /* fill some initial data */ + memcpy(header.FileID, "LUCAM-RECORDER", strlen("LUCAM-RECORDER")); + header.LuID = 0; + header.ColorID = SER_COLORID_RGB; + header.LittleEndian = 1; + header.ImageWidth = 0; + header.ImageHeight = 0; + header.PixelDepthPerPlane = 0; + header.FrameCount = 0; + header.DateTime = 0; + header.DateTimeUTC = 0; + + fd = NULL; + Frame = NULL; +} + + +SER::~SER() { + if(fd) { + fclose(fd); + } + if(Frame) { + free(Frame); + } +} + +/* + Sets the name of the observer. E.g. "Steffen Pohle" + 40 ASCII characters {32...126 dec.}, fill unused characters with 0 dec. +*/ +int SER::setObserver(char *name) { + strncpy(header.Observer, name, sizeof(header.Observer)); + return 0; +} + +/* + Sets the name of the used camera. E.g. "Svbony SV305" + 40 ASCII characters {32...126 dec.}, fill unused characters with 0 dec. +*/ +int SER::setInstrument(char *name) { + strncpy(header.Instrument, name, sizeof(header.Instrument)); + return 0; +} + +/* + Sets the name of the used telescope. E.g. "Sky-Watcher EVOSTAR 102" + 40 ASCII characters {32...126 dec.}, fill unused characters with 0 dec. +*/ +int SER::setTelescope(char *name) { + strncpy(header.Telescope, name, sizeof(header.Telescope)); + return 0; +} + +/* + Set the file name for input/output operations. If file exists, we open it + for reading, otherwise we try to create the file. +*/ +int SER::setFile(char *name) { + FILE *file; + + /* try opening file for reading */ + if((file = fopen(name, "rb")) == NULL) { + + /* file does probably not exist */ + if((file = fopen(name, "wb")) == NULL) { + + /* failed to create file */ + fprintf(stderr, "Error: failed to create file '%s' (%s)\n", name, strerror(errno)); + return -1; + } + else { + fprintf(stdout, "Debug: created file '%s' for writing SER file\n", name); + return 0; + } + } + + fprintf(stdout, "Debug: opened file '%s' for reading SER file\n", name); + return 0; +} + +/* + The function writes the file header of the SER file. + */ +int SER::writeHeader(void) { + + /* goto file beginning */ + if(fseek(fd, 0, SEEK_SET) == -1) { + fprintf(stderr, "Error: failed seek SER file beginning (%s)\n", strerror(errno)); + return -1; + } + + /* write header data */ + if(fwrite(&header, sizeof(header), 1, fd) != sizeof(header)) { + fprintf(stderr, "Error: failed writing SER header (%s)\n", strerror(errno)); + return -1; + } + return 0; +} + +/* + The function apppends a single frame at the current file position. + Frame size is determined by internal header data. + */ +int SER::appendFrame(void *data) { + + /* write frame data */ + if(fwrite(data, FrameSize, 1, fd) != FrameSize) { + fprintf(stderr, "Error: failed write SER frame (%s)\n", strerror(errno)); + return -1; + } + + return 0; +} + +/* + Updates the internal data according changes in the header. + */ +void SER::updateHeaderData(void) { + NumberOfPlanes = header.ColorID < SER_COLORID_RGB ? 1 : 3; + BytesPerPixel = NumberOfPlanes * (header.PixelDepthPerPlane <= 8 ? 1 : 2); + FrameSize = header.ImageWidth * header.ImageHeight * BytesPerPixel; +} + +/* + Sets the image width. + */ +int SER::setWidth(int width) { + header.ImageWidth = width; + updateHeaderData(); + return 0; +} + +/* + Sets the image height. + */ +int SER::setHeight(int height) { + header.ImageHeight = height; + updateHeaderData(); + return 0; +} + +/* + Sets the color ID. + */ +int SER::setColorID(int id) { + header.ColorID = id; + updateHeaderData(); + return 0; +} + +/* + Allocates memory for a single frame. Reading/writing could be done + on this memory block. + */ +void * SER::allocFrame(void) { + + /* Drop old frame data. */ + if(Frame) { + free(Frame); + Frame = NULL; + } + + /* Try allocating new frame data. */ + if((Frame = malloc(FrameSize)) == NULL) { + fprintf(stderr, "Error: failed to allocate %lu bytes for frame\n", FrameSize); + return NULL; + } + return Frame; +} + +/* + The function reads a single frame at the current file position. + Frame size is determined by internal header data. + */ +int SER::readFrame(void *data) { + + /* read frame data */ + if(fread(data, FrameSize, 1, fd) != FrameSize) { + fprintf(stderr, "Error: failed reading SER frame (%s)\n", strerror(errno)); + return -1; + } + + return 0; +} + +/* + Sets the number of frames in the header. +*/ +void SER::setNumberOfFrames(int32_t frames) { + header.FrameCount = frames; +} + +/* + Returns the number of frames in the header. +*/ +int32_t SER::getNumberOfFrames(void) { + return header.FrameCount; +} + +/* + Timestamps in SER + ----------------- + Holds IEEE 64-bit (8-byte) values that represent dates ranging from + January 1 of the year 0001 through December 31 of the year 9999, and + times from 12:00:00 AM (midnight) through 11:59:59.9999999 PM. Each + increment represents 100 nanoseconds of elapsed time since the + beginning of January 1 of the year 1 in the Gregorian calendar. The + maximum value represents 100 nanoseconds before the beginning of + January 1 of the year 10000. +*/ +int64_t SER::setDateTime() { + + int64_t hundred_nano_seconds; + struct timeval current_time; + + /* get time since 01.01.1970 00:00 */ + gettimeofday(¤t_time, NULL); + hundred_nano_seconds = (62135769600L + current_time.tv_sec) * 10000000L + + current_time.tv_usec * 10L; + header.DateTime = hundred_nano_seconds; + header.DateTimeUTC = hundred_nano_seconds + differenceLocalUTC(); + return header.DateTime; +} + +/* + Determines the difference between local time and UTC time. + */ +int64_t SER::differenceLocalUTC(void) { + + time_t abs_ts,loc_ts,gmt_ts; + struct tm loc_time_info,gmt_time_info; + + /* Absolute time stamp.*/ + time (&abs_ts); + + /* Now get once the local time for this time stamp, + and once the GMT (UTC without summer time) time stamp.*/ + localtime_r (&abs_ts,&loc_time_info); + gmtime_r (&abs_ts,&gmt_time_info); + + /* Convert them back.*/ + loc_ts = mktime (&loc_time_info); + gmt_ts = mktime (&gmt_time_info); + + /* Unfortunately, GMT still has summer time. Get rid of it:*/ + if (gmt_time_info.tm_isdst == 1) gmt_ts -= 3600; + + return (loc_ts-gmt_ts) * 10000000L; +} diff --git a/ser.h b/ser.h new file mode 100644 index 0000000..b07c257 --- /dev/null +++ b/ser.h @@ -0,0 +1,71 @@ +#ifndef _SER_H_ +#define _SER_H_ + +enum { + SER_COLORID_MONO = 0, + SER_COLORID_BAYER_RGGB = 8, + SER_COLORID_BAYER_GRBG = 9, + SER_COLORID_BAYER_GBRG = 10, + SER_COLORID_BAYER_BGGR = 11, + SER_COLORID_BAYER_CYYM = 16, + SER_COLORID_BAYER_YCMY = 17, + SER_COLORID_BAYER_YMCY = 18, + SER_COLORID_BAYER_MYYC = 19, + SER_COLORID_RGB = 100, + SER_COLORID_BGR = 101 +}; + +struct SER_Header { + char FileID[40]; /* "LUCAM-RECORDER" (fix) */ + int32_t LuID; /* Lumenera camera series ID (currently unused; default = 0) */ + int32_t ColorID; /* see SER_COLORID_* */ + int32_t LittleEndian; /* 0 (FALSE) for big-endian, 1 (TRUE) for little-endian */ + int32_t ImageWidth; /* Width of every image in pixel */ + int32_t ImageHeight; /* Height of every image in pixel */ + int32_t PixelDepthPerPlane; /* True bit depth per pixel per plane */ + int32_t FrameCount; /* Number of image frames in SER file */ + char Observer[40]; /* Name of observer */ + char Instrument[40]; /* Name of used camera */ + char Telescope[40]; /* Name of used telescope */ + int64_t DateTime; /* Start time of image stream (local time) */ + int64_t DateTimeUTC; /* Start time of image stream in UTC */ +}; + +class SER { + + private: + /* header data */ + struct SER_Header header; + + /* internal data */ + FILE *fd; + int NumberOfPlanes; + int BytesPerPixel; + size_t FrameSize; + void * Frame; + + void updateHeaderData(void); + + public: + SER(); + ~SER(); + + int setObserver(char *); + int setInstrument(char *); + int setTelescope(char *); + int setWidth(int); + int setHeight(int); + int setColorID(int); + int32_t getNumberOfFrames(void); + void setNumberOfFrames(int32_t); + int64_t setDateTime(void); + int64_t differenceLocalUTC(void); + + int setFile(char *); + int writeHeader(void); + int appendFrame(void *); + int readFrame(void *); + void * allocFrame(void); +}; + +#endif