Instrument Neutral Distributed Interface INDI  1.9.5
serrecorder.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 by geehalel <geehalel@gmail.com>
3 
4  SER File Format Recorder (see http://www.grischa-hahn.homepage.t-online.de/astro/ser/index.htm)
5  Specifications can be found in
6  - for V2: http://www.grischa-hahn.homepage.t-online.de/astro/ser/SER%20Doc%20V2.pdf
7  - for V3: http://www.grischa-hahn.homepage.t-online.de/astro/ser/SER%20Doc%20V3b.pdf
8 
9  SER Files may be used as input files for Registax 6 or astrostakkert
10  (which you can both run under Linux using wine), or also Siril, the linux iris version.
11 
12  This library is free software; you can redistribute it and/or
13  modify it under the terms of the GNU Lesser General Public
14  License as published by the Free Software Foundation; either
15  version 2.1 of the License, or (at your option) any later version.
16 
17  This library is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  Lesser General Public License for more details.
21 
22  You should have received a copy of the GNU Lesser General Public
23  License along with this library; if not, write to the Free Software
24  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 
26 */
27 
28 #include "serrecorder.h"
29 #include "jpegutils.h"
30 
31 #include <ctime>
32 #include <cerrno>
33 #include <cstring>
34 #include <sys/time.h>
35 
36 
37 #define ERRMSGSIZ 1024
38 
39 namespace INDI
40 {
41 
43 {
44  name = "SER";
45  strncpy(serh.FileID, "INDI-RECORDER", 14);
46  strncpy(serh.Observer, "Unknown Observer", 40);
47  strncpy(serh.Instrume, "Unknown Instrument", 40);
48  strncpy(serh.Telescope, "Unknown Telescope", 40);
49  serh.LuID = 0;
50  serh.PixelDepth = 8;
51  number_of_planes = 1;
52  // JM 2020-11-06: It appears we must always set it to BIG_ENDIAN since this is how astro-processing software
53  // always default to. LITTLE_ENDIAN appears to be ignored by them leading to garbled data.
55  isRecordingActive = false;
56  f = nullptr;
57 
58  jpegBuffer = static_cast<uint8_t*>(malloc(1));
59 }
60 
62 {
63  free(jpegBuffer);
64 }
65 
67 {
68  unsigned int magic = 0x00000001;
69  unsigned char black_magic = *(unsigned char *)&magic;
70  return black_magic == 0x01;
71 }
72 
73 void SER_Recorder::write_int_le(uint32_t *i)
74 {
75  if (is_little_endian())
76  fwrite((i), sizeof(uint32_t), 1, f);
77  else
78  {
79  unsigned char *c = (unsigned char *)i;
80  fwrite((c + 3), sizeof(char), 1, f);
81  fwrite((c + 2), sizeof(char), 1, f);
82  fwrite((c + 1), sizeof(char), 1, f);
83  fwrite((c), sizeof(char), 1, f);
84  }
85 }
86 
88 {
89  if (is_little_endian())
90  {
91  fwrite(((uint32_t *)i), sizeof(int), 1, f);
92  fwrite(((uint32_t *)(i) + 1), sizeof(int), 1, f);
93  }
94  else
95  {
96  write_int_le((uint32_t *)(i) + 1);
97  write_int_le((uint32_t *)(i));
98  }
99 }
100 
102 {
103  fwrite((s->FileID), sizeof(char), 14, f);
104  write_int_le(&(s->LuID));
105  write_int_le(&(s->ColorID));
106  write_int_le(&(s->LittleEndian));
107  write_int_le(&(s->ImageWidth));
108  write_int_le(&(s->ImageHeight));
109  write_int_le(&(s->PixelDepth));
110  write_int_le(&(s->FrameCount));
111  fwrite((s->Observer), sizeof(char), 40, f);
112  fwrite((s->Instrume), sizeof(char), 40, f);
113  fwrite((s->Telescope), sizeof(char), 40, f);
116 }
117 
118 bool SER_Recorder::setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
119 {
120  serh.PixelDepth = pixelDepth;
121  m_PixelFormat = pixelFormat;
122  number_of_planes = 1;
123  switch (pixelFormat)
124  {
125  case INDI_MONO:
127  break;
128 
129  case INDI_BAYER_BGGR:
131  break;
132  case INDI_BAYER_GBRG:
134  break;
135  case INDI_BAYER_GRBG:
137  break;
138  case INDI_BAYER_RGGB:
140  break;
141  case INDI_RGB:
142  number_of_planes = 3;
143  serh.ColorID = SER_RGB;
144  break;
145  case INDI_BGR:
146  number_of_planes = 3;
147  serh.ColorID = SER_BGR;
148  break;
149  case INDI_JPG:
150  number_of_planes = 3;
151  serh.ColorID = SER_RGB;
152  break;
153  default:
154  return false;
155  }
156 
157  return true;
158 }
159 
160 bool SER_Recorder::setSize(uint16_t width, uint16_t height)
161 {
162  if (isRecordingActive)
163  return false;
164 
165  rawWidth = width;
166  rawHeight = height;
167 
168  serh.ImageWidth = width;
169  serh.ImageHeight = height;
170  return true;
171 }
172 
173 bool SER_Recorder::open(const char *filename, char *errmsg)
174 {
175  if (isRecordingActive)
176  return false;
177  serh.FrameCount = 0;
178  if ((f = fopen(filename, "w")) == nullptr)
179  {
180  snprintf(errmsg, ERRMSGSIZ, "recorder open error %d, %s\n", errno, strerror(errno));
181  return false;
182  }
183 
184  serh.DateTime = getLocalTimeStamp();
185  serh.DateTime_UTC = getUTCTimeStamp();
186  write_header(&serh);
188  isRecordingActive = true;
189 
190  frameStamps.clear();
191 
192  return true;
193 }
194 
196 {
197  if (f)
198  {
199  // Write all timestamps
200  for (auto value : frameStamps)
201  write_long_int_le(&value);
202 
203  frameStamps.clear();
204 
205  fseek(f, 0L, SEEK_SET);
206  write_header(&serh);
207  fclose(f);
208  f = nullptr;
209  }
210 
211  isRecordingActive = false;
212  return true;
213 }
214 
215 bool SER_Recorder::writeFrame(const uint8_t *frame, uint32_t nbytes)
216 {
217  if (!isRecordingActive)
218  return false;
219 
220 #if 0
221  if (serh.ColorID == SER_MONO)
222  {
223  if (isStreamingActive == false &&
224  (offsetX > 0 || offsetY > 0 || serh.ImageWidth != rawWidth || serh.ImageHeight != rawHeight))
225  {
226  int offset = ((rawWidth * offsetY) + offsetX);
227 
228  uint8_t *srcBuffer = frame + offset;
229  uint8_t *destBuffer = frame;
230  int imageWidth = serh.ImageWidth;
231  int imageHeight = serh.ImageHeight;
232 
233  for (int i = 0; i < imageHeight; i++)
234  memcpy(destBuffer + i * imageWidth, srcBuffer + rawWidth * i, imageWidth);
235  }
236  }
237  else
238  {
239  if (isStreamingActive == false &&
240  (offsetX > 0 || offsetY > 0 || serh.ImageWidth != rawWidth || serh.ImageHeight != rawHeight))
241  {
242  int offset = ((rawWidth * offsetY) + offsetX);
243 
244  uint8_t *srcBuffer = frame + offset * 3;
245  uint8_t *destBuffer = frame;
246  int imageWidth = serh.ImageWidth;
247  int imageHeight = serh.ImageHeight;
248 
249  // RGB
250  for (int i = 0; i < imageHeight; i++)
251  memcpy(destBuffer + i * imageWidth * 3, srcBuffer + rawWidth * 3 * i, imageWidth * 3);
252  }
253  }
254 #endif
255 
256  frameStamps.push_back(getUTCTimeStamp());
257 
258  // Not technically pixel format, but let's use this for now.
259  if (m_PixelFormat == INDI_JPG)
260  {
261  int w = 0, h = 0, naxis = 1;
262  size_t memsize = 0;
263  if (decode_jpeg_rgb(const_cast<uint8_t *>(frame), nbytes, &jpegBuffer, &memsize, &naxis, &w, &h) < 0)
264  return false;
265 
266  serh.ImageWidth = w;
267  serh.ImageHeight = h;
268  serh.ColorID = (naxis == 3) ? SER_RGB : SER_MONO;
269  fwrite(jpegBuffer, 1, memsize, f);
270  }
271  else
272  fwrite(frame, 1, nbytes, f);
273  serh.FrameCount += 1;
274  return true;
275 }
276 
277 // Copyright (C) 2015 Chris Garry
278 //
279 
280 //
281 // Calculate if a year is a leap yer
282 //
283 bool SER_Recorder::is_leap_year(uint32_t year)
284 {
285  if ((year % 400) == 0)
286  {
287  // If year is divisible by 400 then is_leap_year
288  return true;
289  }
290  else if ((year % 100) == 0)
291  {
292  // Else if year is divisible by 100 then not_leap_year
293  return false;
294  }
295  else if ((year % 4) == 0)
296  {
297  // Else if year is divisible by 4 then is_leap_year
298  return true;
299  }
300  else
301  {
302  // Else not_leap_year
303  return false;
304  }
305 }
306 
307 uint64_t SER_Recorder::getUTCTimeStamp()
308 {
309  uint64_t utcTS;
310 
311  // Get starting time
312  timeval currentTime;
313  gettimeofday(&currentTime, nullptr);
314 
315  struct tm *tp;
316  time_t t = (time_t)currentTime.tv_sec;
317  uint32_t u = currentTime.tv_usec;
318 
319  // UTC Time
320  tp = gmtime(&t);
321 
322  dateTo64BitTS(tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, u, &utcTS);
323 
324  return utcTS;
325 }
326 
327 uint64_t SER_Recorder::getLocalTimeStamp()
328 {
329  uint64_t localTS;
330 
331  // Get starting time
332  timeval currentTime;
333  gettimeofday(&currentTime, nullptr);
334 
335  struct tm *tp;
336  time_t t = (time_t)currentTime.tv_sec;
337  uint32_t u = currentTime.tv_usec;
338 
339  // Local Time
340  tp = localtime(&t);
341 
342  dateTo64BitTS(tp->tm_year, tp->tm_mon, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, u, &localTS);
343 
344  return localTS;
345 }
346 
347 // Convert real time to timestamp
348 //
349 void SER_Recorder::dateTo64BitTS(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t minute, int32_t second,
350  int32_t microsec, uint64_t *p_ts)
351 {
352  uint64_t ts = 0;
353  int32_t yr;
354 
355  // Add 400 year blocks
356  for (yr = 1; yr < (year - 400); yr += 400)
357  {
358  ts += m_septaseconds_per_400_years;
359  }
360 
361  // Add 1 years
362  for (; yr < year; yr++)
363  {
364  uint32_t days_this_year = 365;
365  if (is_leap_year(yr))
366  {
367  days_this_year = 366;
368  }
369 
370  ts += (days_this_year * m_septaseconds_per_day);
371  }
372 
373  // Add months
374  for (int mon = 1; mon < month; mon++)
375  {
376  switch (mon)
377  {
378  case 4: // April
379  case 6: // June
380  case 9: // September
381  case 11: // Novenber
382  ts += (30 * m_septaseconds_per_day);
383  break;
384  case 2: // Feburary
385  if (is_leap_year(year))
386  {
387  ts += (29 * m_septaseconds_per_day);
388  }
389  else
390  {
391  ts += (28 * m_septaseconds_per_day);
392  }
393 
394  break;
395  default:
396  ts += (31 * m_septaseconds_per_day);
397  break;
398  }
399  }
400 
401  // Add days
402  ts += ((day - 1) * m_septaseconds_per_day);
403 
404  // Add hours
405  ts += (hour * m_septaseconds_per_hour);
406 
407  // Add minutes
408  ts += (minute * m_septaseconds_per_minute);
409 
410  // Add seconds
411  ts += (second * C_SEPASECONDS_PER_SECOND);
412 
413  // Micro seconds
414  ts += (microsec * m_sepaseconds_per_microsecond);
415 
416  // Output result
417  *p_ts = ts;
418 }
419 
420 }
ser_header::Telescope
char Telescope[40]
Definition: serrecorder.h:41
SER_BAYER_GBRG
@ SER_BAYER_GBRG
Definition: serrecorder.h:51
ser_header::ImageHeight
uint32_t ImageHeight
Definition: serrecorder.h:36
INDI_BAYER_GBRG
@ INDI_BAYER_GBRG
Definition: indibasetypes.h:68
ser_header::DateTime_UTC
uint64_t DateTime_UTC
Definition: serrecorder.h:43
ser_header::PixelDepth
uint32_t PixelDepth
Definition: serrecorder.h:37
INDI_BGR
@ INDI_BGR
Definition: indibasetypes.h:75
INDI::SER_Recorder::frameStamps
std::vector< uint64_t > frameStamps
Definition: serrecorder.h:99
SER_BGR
@ SER_BGR
Definition: serrecorder.h:58
INDI_MONO
@ INDI_MONO
Definition: indibasetypes.h:65
INDI::SER_Recorder::C_SEPASECONDS_PER_SECOND
static const uint64_t C_SEPASECONDS_PER_SECOND
Definition: serrecorder.h:85
INDI::SER_Recorder::frame_size
uint32_t frame_size
Definition: serrecorder.h:96
INDI::SER_Recorder::SER_Recorder
SER_Recorder()
Definition: serrecorder.cpp:42
INDI::SER_Recorder::writeFrame
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes)
Definition: serrecorder.cpp:215
INDI::SER_Recorder::isRecordingActive
bool isRecordingActive
Definition: serrecorder.h:94
INDI_BAYER_GRBG
@ INDI_BAYER_GRBG
Definition: indibasetypes.h:67
ser_header::Observer
char Observer[40]
Definition: serrecorder.h:39
serrecorder.h
INDI::SER_Recorder::isStreamingActive
bool isStreamingActive
Definition: serrecorder.h:94
INDI::RecorderInterface::name
const char * name
Definition: recorderinterface.h:80
ser_header::DateTime
uint64_t DateTime
Definition: serrecorder.h:42
INDI::SER_Recorder::f
FILE * f
Definition: serrecorder.h:95
INDI_RGB
@ INDI_RGB
Definition: indibasetypes.h:74
ERRMSGSIZ
#define ERRMSGSIZ
Definition: serrecorder.cpp:37
INDI::SER_Recorder::close
virtual bool close()
Definition: serrecorder.cpp:195
INDI::SER_Recorder::write_header
void write_header(ser_header *s)
Definition: serrecorder.cpp:101
ser_header::FileID
char FileID[14]
Definition: serrecorder.h:31
INDI::SER_Recorder::setSize
virtual bool setSize(uint16_t width, uint16_t height)
Definition: serrecorder.cpp:160
ser_header::ColorID
uint32_t ColorID
Definition: serrecorder.h:33
decode_jpeg_rgb
int decode_jpeg_rgb(unsigned char *inBuffer, unsigned long inSize, uint8_t **memptr, size_t *memsize, int *naxis, int *w, int *h)
decode_jpeg_rgb Read jpeg in memory buffer and produce RGB image
Definition: jpegutils.c:1309
SER_RGB
@ SER_RGB
Definition: serrecorder.h:57
INDI_JPG
@ INDI_JPG
Definition: indibasetypes.h:76
INDI_BAYER_RGGB
@ INDI_BAYER_RGGB
Definition: indibasetypes.h:66
INDI::SER_Recorder::write_int_le
void write_int_le(uint32_t *i)
Definition: serrecorder.cpp:73
SER_MONO
@ SER_MONO
Definition: serrecorder.h:48
INDI::SER_Recorder::~SER_Recorder
virtual ~SER_Recorder()
Definition: serrecorder.cpp:61
ser_header::FrameCount
uint32_t FrameCount
Definition: serrecorder.h:38
jpegutils.h
INDI::SER_Recorder::is_little_endian
bool is_little_endian()
Definition: serrecorder.cpp:66
ser_header::LittleEndian
uint32_t LittleEndian
Definition: serrecorder.h:34
INDI
Namespace to encapsulate INDI client, drivers, and mediator classes.
Definition: AlignmentSubsystemForClients.cpp:11
ser_header
Definition: serrecorder.h:29
INDI::SER_Recorder::serh
ser_header serh
Definition: serrecorder.h:93
INDI::SER_Recorder::setPixelFormat
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
Definition: serrecorder.cpp:118
INDI_BAYER_BGGR
@ INDI_BAYER_BGGR
Definition: indibasetypes.h:69
INDI::SER_Recorder::rawWidth
uint16_t rawWidth
Definition: serrecorder.h:98
ser_header::LuID
uint32_t LuID
Definition: serrecorder.h:32
INDI::SER_Recorder::write_long_int_le
void write_long_int_le(uint64_t *i)
Definition: serrecorder.cpp:87
SER_BAYER_RGGB
@ SER_BAYER_RGGB
Definition: serrecorder.h:49
ser_header::ImageWidth
uint32_t ImageWidth
Definition: serrecorder.h:35
SER_BAYER_GRBG
@ SER_BAYER_GRBG
Definition: serrecorder.h:50
INDI::SER_Recorder::number_of_planes
uint32_t number_of_planes
Definition: serrecorder.h:97
SER_BIG_ENDIAN
#define SER_BIG_ENDIAN
Definition: serrecorder.h:61
INDI::SER_Recorder::rawHeight
uint16_t rawHeight
Definition: serrecorder.h:98
SER_BAYER_BGGR
@ SER_BAYER_BGGR
Definition: serrecorder.h:52
errno
int errno
INDI::SER_Recorder::open
virtual bool open(const char *filename, char *errmsg)
Definition: serrecorder.cpp:173
ser_header::Instrume
char Instrume[40]
Definition: serrecorder.h:40
INDI_PIXEL_FORMAT
INDI_PIXEL_FORMAT
Definition: indibasetypes.h:63