parent
8d96a401fd
commit
d359c3e553
@ -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 <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", sizeof(header.FileID));
|
||||
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 %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;
|
||||
}
|
@ -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
|
Loading…
Reference in new issue