Instrument Neutral Distributed Interface INDI  2.0.2
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, uint64_t timestamp)
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  if(timestamp)
257  frameStamps.push_back(timestamp * m_sepaseconds_per_microsecond);
258  else
259  frameStamps.push_back(getUTCTimeStamp());
260 
261  // Not technically pixel format, but let's use this for now.
262  if (m_PixelFormat == INDI_JPG)
263  {
264  int w = 0, h = 0, naxis = 1;
265  size_t memsize = 0;
266  if (decode_jpeg_rgb(const_cast<uint8_t *>(frame), nbytes, &jpegBuffer, &memsize, &naxis, &w, &h) < 0)
267  return false;
268 
269  serh.ImageWidth = w;
270  serh.ImageHeight = h;
271  serh.ColorID = (naxis == 3) ? SER_RGB : SER_MONO;
272  fwrite(jpegBuffer, 1, memsize, f);
273  }
274  else
275  fwrite(frame, 1, nbytes, f);
276  serh.FrameCount += 1;
277  return true;
278 }
279 
280 // Copyright (C) 2015 Chris Garry
281 //
282 
283 //
284 // Calculate if a year is a leap yer
285 //
286 bool SER_Recorder::is_leap_year(uint32_t year)
287 {
288  if ((year % 400) == 0)
289  {
290  // If year is divisible by 400 then is_leap_year
291  return true;
292  }
293  else if ((year % 100) == 0)
294  {
295  // Else if year is divisible by 100 then not_leap_year
296  return false;
297  }
298  else if ((year % 4) == 0)
299  {
300  // Else if year is divisible by 4 then is_leap_year
301  return true;
302  }
303  else
304  {
305  // Else not_leap_year
306  return false;
307  }
308 }
309 
310 uint64_t SER_Recorder::getUTCTimeStamp()
311 {
312  uint64_t utcTS;
313 
314  // Get starting time
315  timeval currentTime;
316  gettimeofday(&currentTime, nullptr);
317 
318  struct tm *tp;
319  time_t t = (time_t)currentTime.tv_sec;
320  uint32_t u = currentTime.tv_usec;
321 
322  // UTC Time
323  tp = gmtime(&t);
324 
325  dateTo64BitTS(tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, u, &utcTS);
326 
327  return utcTS;
328 }
329 
330 uint64_t SER_Recorder::getLocalTimeStamp()
331 {
332  uint64_t localTS;
333 
334  // Get starting time
335  timeval currentTime;
336  gettimeofday(&currentTime, nullptr);
337 
338  struct tm *tp;
339  time_t t = (time_t)currentTime.tv_sec;
340  uint32_t u = currentTime.tv_usec;
341 
342  // Local Time
343  tp = localtime(&t);
344 
345  dateTo64BitTS(tp->tm_year, tp->tm_mon, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, u, &localTS);
346 
347  return localTS;
348 }
349 
350 // Convert real time to timestamp
351 //
352 void SER_Recorder::dateTo64BitTS(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t minute, int32_t second,
353  int32_t microsec, uint64_t *p_ts)
354 {
355  uint64_t ts = 0;
356  int32_t yr;
357 
358  // Add 400 year blocks
359  for (yr = 1; yr < (year - 400); yr += 400)
360  {
361  ts += m_septaseconds_per_400_years;
362  }
363 
364  // Add 1 years
365  for (; yr < year; yr++)
366  {
367  uint32_t days_this_year = 365;
368  if (is_leap_year(yr))
369  {
370  days_this_year = 366;
371  }
372 
373  ts += (days_this_year * m_septaseconds_per_day);
374  }
375 
376  // Add months
377  for (int mon = 1; mon < month; mon++)
378  {
379  switch (mon)
380  {
381  case 4: // April
382  case 6: // June
383  case 9: // September
384  case 11: // Novenber
385  ts += (30 * m_septaseconds_per_day);
386  break;
387  case 2: // Feburary
388  if (is_leap_year(year))
389  {
390  ts += (29 * m_septaseconds_per_day);
391  }
392  else
393  {
394  ts += (28 * m_septaseconds_per_day);
395  }
396 
397  break;
398  default:
399  ts += (31 * m_septaseconds_per_day);
400  break;
401  }
402  }
403 
404  // Add days
405  ts += ((day - 1) * m_septaseconds_per_day);
406 
407  // Add hours
408  ts += (hour * m_septaseconds_per_hour);
409 
410  // Add minutes
411  ts += (minute * m_septaseconds_per_minute);
412 
413  // Add seconds
414  ts += (second * C_SEPASECONDS_PER_SECOND);
415 
416  // Micro seconds
417  ts += (microsec * m_sepaseconds_per_microsecond);
418 
419  // Output result
420  *p_ts = ts;
421 }
422 
423 }
void write_long_int_le(uint64_t *i)
Definition: serrecorder.cpp:87
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes, uint64_t timestamp)
virtual ~SER_Recorder()
Definition: serrecorder.cpp:61
virtual bool close()
virtual bool open(const char *filename, char *errmsg)
virtual bool setSize(uint16_t width, uint16_t height)
void write_header(ser_header *s)
void write_int_le(uint32_t *i)
Definition: serrecorder.cpp:73
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
std::vector< uint64_t > frameStamps
Definition: serrecorder.h:106
static const uint64_t C_SEPASECONDS_PER_SECOND
Definition: serrecorder.h:92
uint32_t number_of_planes
Definition: serrecorder.h:104
int errno
INDI_PIXEL_FORMAT
Definition: indibasetypes.h:70
@ INDI_BAYER_GRBG
Definition: indibasetypes.h:73
@ INDI_MONO
Definition: indibasetypes.h:71
@ INDI_BAYER_GBRG
Definition: indibasetypes.h:74
@ INDI_BAYER_BGGR
Definition: indibasetypes.h:75
@ INDI_BGR
Definition: indibasetypes.h:81
@ INDI_RGB
Definition: indibasetypes.h:80
@ INDI_JPG
Definition: indibasetypes.h:82
@ INDI_BAYER_RGGB
Definition: indibasetypes.h:72
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
Namespace to encapsulate INDI client, drivers, and mediator classes.
#define ERRMSGSIZ
Definition: serrecorder.cpp:37
#define SER_BIG_ENDIAN
Definition: serrecorder.h:61
@ SER_BGR
Definition: serrecorder.h:58
@ SER_BAYER_GRBG
Definition: serrecorder.h:50
@ SER_RGB
Definition: serrecorder.h:57
@ SER_BAYER_GBRG
Definition: serrecorder.h:51
@ SER_MONO
Definition: serrecorder.h:48
@ SER_BAYER_BGGR
Definition: serrecorder.h:52
@ SER_BAYER_RGGB
Definition: serrecorder.h:49
uint64_t DateTime
Definition: serrecorder.h:42
char FileID[14]
Definition: serrecorder.h:31
uint64_t DateTime_UTC
Definition: serrecorder.h:43
char Telescope[40]
Definition: serrecorder.h:41
char Observer[40]
Definition: serrecorder.h:39
uint32_t PixelDepth
Definition: serrecorder.h:37
uint32_t LittleEndian
Definition: serrecorder.h:34
char Instrume[40]
Definition: serrecorder.h:40
uint32_t LuID
Definition: serrecorder.h:32
uint32_t ColorID
Definition: serrecorder.h:33
uint32_t ImageWidth
Definition: serrecorder.h:35
uint32_t FrameCount
Definition: serrecorder.h:38
uint32_t ImageHeight
Definition: serrecorder.h:36