You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SimpleSkyCam/ser.cc

330 lines
7.3 KiB

/*
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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#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(&current_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;
}