/* 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 = 8; header.FrameCount = 0; header.DateTime = 0; header.DateTimeUTC = 0; fd = NULL; Frame = NULL; } /* Class destructor. Free resources. */ SER::~SER() { /* Close file if necessary */ if(fd) { fclose(fd); } /* Free frame memory if necessary */ 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); fd = file; return 0; } } fprintf(stdout, "Debug: opened file '%s' for reading SER file\n", name); fd = file; return 0; } /* The function writes the file header of the SER file. */ int SER::writeHeader(void) { int err; /* adjust time stamps for header */ setDateTime(); if(fd == NULL) { fprintf(stderr, "Error: writing header, SER file not yet specified\n"); return -1; } /* goto file beginning */ if((err = fseek(fd, 0, SEEK_SET)) == -1) { fprintf(stderr, "Error: failed seek SER file beginning (%d, %s)\n", err, strerror(errno)); return -1; } /* write header data */ if((err = fwrite(&header, sizeof(header), 1, fd)) != 1) { fprintf(stderr, "Error: failed writing SER header (%d, %s)\n", err, 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) { if(fd == NULL) { fprintf(stderr, "Error: appending frame, SER file not yet specified\n"); return -1; } /* write frame data */ if(fwrite(data, FrameSize, 1, fd) != 1) { 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 pixel depth in bits. */ int SER::setPixelDepth(int pixeldepth) { header.PixelDepthPerPlane = pixeldepth; updateHeaderData(); return 0; } /* Returns the pixel depth in bits. */ int SER::getPixelDepth(void) { return header.PixelDepthPerPlane; } /* Returns the number of bytes in an allocated frame. */ size_t SER::getFrameSize(void) { updateHeaderData(); return FrameSize; } /* Sets the image width. */ int SER::setWidth(int width) { header.ImageWidth = width; updateHeaderData(); return 0; } /* Gets the image width. */ int SER::getWidth(void) { return header.ImageWidth; } /* Sets the image height. */ int SER::setHeight(int height) { header.ImageHeight = height; updateHeaderData(); return 0; } /* Gets the image height. */ int SER::getHeight(void) { return header.ImageHeight; } /* 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 %d 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; }