Instrument Neutral Distributed Interface INDI 2.2.5
serrecorder.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 by geehalel <[email protected]>
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
39namespace 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;
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
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));
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
118bool SER_Recorder::setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
119{
120 serh.PixelDepth = pixelDepth;
121 m_PixelFormat = pixelFormat;
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:
144 break;
145 case INDI_BGR:
148 break;
149 case INDI_JPG:
152 break;
153 default:
154 return false;
155 }
156
157 return true;
158}
159
160bool SER_Recorder::setSize(uint16_t width, uint16_t height)
161{
163 return false;
164
165 rawWidth = width;
166 rawHeight = height;
167
168 serh.ImageWidth = width;
169 serh.ImageHeight = height;
170 return true;
171}
172
173bool SER_Recorder::open(const char *filename, char *errmsg)
174{
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();
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);
207 fclose(f);
208 f = nullptr;
209 }
210
211 isRecordingActive = false;
212 return true;
213}
214
215bool SER_Recorder::writeFrame(const uint8_t *frame, uint32_t nbytes, uint64_t timestamp)
216{
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//
286bool 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
310uint64_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
330uint64_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//
352void 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: // February
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)
virtual bool writeFrame(const uint8_t *frame, uint32_t nbytes, uint64_t timestamp)
virtual ~SER_Recorder()
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)
virtual bool setPixelFormat(INDI_PIXEL_FORMAT pixelFormat, uint8_t pixelDepth)
std::vector< uint64_t > frameStamps
static const uint64_t C_SEPASECONDS_PER_SECOND
Definition serrecorder.h:92
uint32_t number_of_planes
int errno
INDI_PIXEL_FORMAT
@ INDI_BAYER_GRBG
@ INDI_MONO
@ INDI_BAYER_GBRG
@ INDI_BAYER_BGGR
@ INDI_BGR
@ INDI_RGB
@ INDI_JPG
@ INDI_BAYER_RGGB
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 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
#define ERRMSGSIZ
Definition v4l2driver.h:38