1 #if 0
2 V4L INDI Driver
3 INDI Interface for V4L devices
4 Copyright (C) 2003 - 2013 Jasem Mutlaq (mutlaqja@ikarustech.com)
5
6 This library is free software;
7 you can redistribute it and / or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation;
10 either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY;
15 without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with this library;
21 if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
23
24 #endif
25
26 #include <cmath>
27 #include <algorithm>
28 #include "v4l2driver.h"
29 #include "indistandardproperty.h"
30 #include "lx/Lx.h"
31
32 // Pixel size info for different cameras
33 typedef struct
34 {
35 const char * deviceName; // device name reported by V4L
36 const char * commonName; // if null, use device name
37 float pixelSizeX;
38 float pixelSizeY; // if negative, use pixelSizeX also for Y
39 bool tested; //if False print please report message
40 } PixelSizeInfo;
41
42 static const PixelSizeInfo pixelSizeInfo[] =
43 {
44 { "NexImage 5", nullptr, 2.2f, -1, true },
45 { "UVC Camera (046d:0809)", "Logitech Webcam Pro 9000", 3.3f, -1, true },
46 { "SVBONY SV105: SVBONY SV105", "SVBONY SV105", 3.0f, -1, true },
47 { "SVBONY SV205: SVBONY SV205", "SVBONY SV205", 4.0f, -1, true },
48 { "NexImage 10", nullptr, 1.67f, -1, true },
49 { "NexImage Burst Color", nullptr, 3.75f, -1, false },
50 { "NexImage Burst Mono", nullptr, 3.75f, -1, false },
51 { "Skyris 132C", nullptr, 3.75f, -1, false },
52 { "Skyris 132M", nullptr, 3.75f, -1, false },
53 { "Skyris 236C", nullptr, 2.8f, -1, false },
54 { "Skyris 236M", nullptr, 2.8f, -1, false },
55 { "iOptron iPolar: iOptron iPolar", nullptr, 3.75f, -1, true },
56 { "iOptron iGuider: iOptron iGuide", nullptr, 3.75f, -1, true },
57 { "mmal service 16.1", "Raspberry Pi High Quality Camera", 1.55f, -1, true },
58 { "UVC Camera (046d:0825)", "Logitech HD C270", 2.8f, -1, true },
59 { "0c45:6366 Microdia", "Spinel 2MP Full HD Low Light WDR H264 USB Camera Module IMX290", 2.9f, -1, true },
60 { "Microsoft® LifeCam Cinema(TM):", "Microsoft® LifeCam Cinema(TM)", 3.0f, -1, false },
61 { nullptr, nullptr, 5.6f, -1, false} // sentinel and default pixel size, needs to be last
62 };
63
V4L2_Driver()64 V4L2_Driver::V4L2_Driver()
65 {
66 setVersion(1, 0);
67
68 allocateBuffers();
69
70 divider = 128.;
71
72 is_capturing = false;
73 non_capture_frames = 0;
74
75 Options = nullptr;
76 v4loptions = 0;
77 AbsExposureN = nullptr;
78 ManualExposureSP = nullptr;
79 CaptureSizesSP.sp = nullptr;
80 CaptureSizesNP.np = nullptr;
81 FrameRatesSP.sp = nullptr;
82
83 frame_received.tv_sec = 0;
84 frame_received.tv_usec = 0;
85
86 v4l_capture_started = false;
87
88 stackMode = STACK_NONE;
89
90 lx = new Lx();
91 lxtimer = -1;
92 stdtimer = -1;
93 }
94
~V4L2_Driver()95 V4L2_Driver::~V4L2_Driver()
96 {
97 releaseBuffers();
98 }
99
updateFrameSize()100 void V4L2_Driver::updateFrameSize()
101 {
102 if (ISS_ON == ImageColorS[IMAGE_GRAYSCALE].s)
103 frameBytes =
104 PrimaryCCD.getSubW() * PrimaryCCD.getSubH() * (PrimaryCCD.getBPP() / 8 + (PrimaryCCD.getBPP() % 8 ? 1 : 0));
105 else
106 frameBytes = PrimaryCCD.getSubW() * PrimaryCCD.getSubH() *
107 (PrimaryCCD.getBPP() / 8 + (PrimaryCCD.getBPP() % 8 ? 1 : 0)) * 3;
108
109 PrimaryCCD.setFrameBufferSize(frameBytes);
110 LOGF_DEBUG("%s: frame bytes %d", __FUNCTION__, PrimaryCCD.getFrameBufferSize());
111 }
112
initProperties()113 bool V4L2_Driver::initProperties()
114 {
115 INDI::CCD::initProperties();
116 addDebugControl();
117
118 /* Port */
119 char configPort[256] = {0};
120 if (IUGetConfigText(getDeviceName(), PortTP.name, PortT[0].name, configPort, 256) == 0)
121 IUFillText(&PortT[0], "PORT", "Port", configPort);
122 else
123 IUFillText(&PortT[0], "PORT", "Port", "/dev/video0");
124 IUFillTextVector(&PortTP, PortT, NARRAY(PortT), getDeviceName(), INDI::SP::DEVICE_PORT, "Ports", OPTIONS_TAB, IP_RW, 0,
125 IPS_IDLE);
126
127 /* Color space */
128 IUFillSwitch(&ImageColorS[IMAGE_GRAYSCALE], "CCD_COLOR_GRAY", "Gray", ISS_ON);
129 IUFillSwitch(&ImageColorS[1], "CCD_COLOR_RGB", "Color", ISS_OFF);
130 IUFillSwitchVector(&ImageColorSP, ImageColorS, NARRAY(ImageColorS), getDeviceName(), "CCD_COLOR_SPACE",
131 "Image Type", IMAGE_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
132
133 /* Image depth */
134 IUFillSwitch(&ImageDepthS[0], "8 bits", "", ISS_ON);
135 IUFillSwitch(&ImageDepthS[1], "16 bits", "", ISS_OFF);
136 IUFillSwitchVector(&ImageDepthSP, ImageDepthS, NARRAY(ImageDepthS), getDeviceName(), "Image Depth", "",
137 IMAGE_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
138
139 /* Camera Name */
140 IUFillText(&camNameT[0], "Model", "", nullptr);
141 IUFillTextVector(&camNameTP, camNameT, NARRAY(camNameT), getDeviceName(), "Camera", "", IMAGE_INFO_TAB, IP_RO, 0,
142 IPS_IDLE);
143
144 /* Stacking Mode */
145 IUFillSwitch(&StackModeS[STACK_NONE], "None", "", ISS_ON);
146 IUFillSwitch(&StackModeS[STACK_MEAN], "Mean", "", ISS_OFF);
147 IUFillSwitch(&StackModeS[STACK_ADDITIVE], "Additive", "", ISS_OFF);
148 IUFillSwitch(&StackModeS[STACK_TAKE_DARK], "Take Dark", "", ISS_OFF);
149 IUFillSwitch(&StackModeS[STACK_RESET_DARK], "Reset Dark", "", ISS_OFF);
150 IUFillSwitchVector(&StackModeSP, StackModeS, NARRAY(StackModeS), getDeviceName(), "Stack", "", MAIN_CONTROL_TAB,
151 IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
152
153 stackMode = STACK_NONE;
154
155 /* Inputs */
156 IUFillSwitchVector(&InputsSP, nullptr, 0, getDeviceName(), "V4L2_INPUT", "Inputs", CAPTURE_FORMAT, IP_RW,
157 ISR_1OFMANY, 0, IPS_IDLE);
158 /* Capture Formats */
159 IUFillSwitchVector(&CaptureFormatsSP, nullptr, 0, getDeviceName(), "V4L2_FORMAT", "Capture Format", CAPTURE_FORMAT,
160 IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
161 /* Capture Sizes */
162 IUFillSwitchVector(&CaptureSizesSP, nullptr, 0, getDeviceName(), "V4L2_SIZE_DISCRETE", "Capture Size",
163 CAPTURE_FORMAT, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
164 IUFillNumberVector(&CaptureSizesNP, nullptr, 0, getDeviceName(), "V4L2_SIZE_STEP", "Capture Size", CAPTURE_FORMAT,
165 IP_RW, 0, IPS_IDLE);
166 /* Frame Rate */
167 IUFillSwitchVector(&FrameRatesSP, nullptr, 0, getDeviceName(), "V4L2_FRAMEINT_DISCRETE", "Frame Interval",
168 CAPTURE_FORMAT, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
169 IUFillNumberVector(&FrameRateNP, nullptr, 0, getDeviceName(), "V4L2_FRAMEINT_STEP", "Frame Interval",
170 CAPTURE_FORMAT, IP_RW, 60, IPS_IDLE);
171 /* Capture Colorspace */
172 IUFillText(&CaptureColorSpaceT[0], "Name", "", nullptr);
173 IUFillText(&CaptureColorSpaceT[1], "YCbCr Encoding", "", nullptr);
174 IUFillText(&CaptureColorSpaceT[2], "Quantization", "", nullptr);
175 IUFillTextVector(&CaptureColorSpaceTP, CaptureColorSpaceT, NARRAY(CaptureColorSpaceT), getDeviceName(),
176 "V4L2_COLORSPACE", "ColorSpace", IMAGE_INFO_TAB, IP_RO, 0, IPS_IDLE);
177
178 /* Color Processing */
179 IUFillSwitch(&ColorProcessingS[0], "Quantization", "", ISS_ON);
180 IUFillSwitch(&ColorProcessingS[1], "Color Conversion", "", ISS_OFF);
181 IUFillSwitch(&ColorProcessingS[2], "Linearization", "", ISS_OFF);
182 IUFillSwitchVector(&ColorProcessingSP, ColorProcessingS, NARRAY(ColorProcessingS), getDeviceName(),
183 "V4L2_COLOR_PROCESSING", "Color Process", CAPTURE_FORMAT, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
184
185 /* V4L2 Settings */
186 IUFillNumberVector(&ImageAdjustNP, nullptr, 0, getDeviceName(), "Image Adjustments", "", IMAGE_GROUP, IP_RW, 60,
187 IPS_IDLE);
188
189 PrimaryCCD.getCCDInfo()->p = IP_RW;
190
191 PrimaryCCD.setMinMaxStep("CCD_EXPOSURE", "CCD_EXPOSURE_VALUE", 0.001, 3600, 1, false);
192
193 if (!lx->initProperties(this))
194 LOG_WARN("Can not init Long Exposure");
195
196 #ifdef HAVE_WEBSOCKET
197 SetCCDCapability(CCD_CAN_BIN | CCD_CAN_SUBFRAME | CCD_HAS_STREAMING | CCD_CAN_ABORT | CCD_HAS_WEB_SOCKET);
198 #else
199 SetCCDCapability(CCD_CAN_BIN | CCD_CAN_SUBFRAME | CCD_HAS_STREAMING | CCD_CAN_ABORT);
200 #endif
201
202 v4l_base->setDeviceName(getDeviceName());
203 return true;
204 }
205
initCamBase()206 void V4L2_Driver::initCamBase()
207 {
208 v4l_base = new INDI::V4L2_Base();
209 }
210
ISGetProperties(const char * dev)211 void V4L2_Driver::ISGetProperties(const char * dev)
212 {
213 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
214 return;
215
216 INDI::CCD::ISGetProperties(dev);
217
218 defineProperty(&PortTP);
219 loadConfig(true, INDI::SP::DEVICE_PORT);
220
221 if (isConnected())
222 {
223 defineProperty(&camNameTP);
224
225 defineProperty(&ImageColorSP);
226 defineProperty(&InputsSP);
227 defineProperty(&CaptureFormatsSP);
228
229 if (CaptureSizesSP.sp != nullptr)
230 defineProperty(&CaptureSizesSP);
231 else if (CaptureSizesNP.np != nullptr)
232 defineProperty(&CaptureSizesNP);
233 if (FrameRatesSP.sp != nullptr)
234 defineProperty(&FrameRatesSP);
235 else if (FrameRateNP.np != nullptr)
236 defineProperty(&FrameRateNP);
237
238 defineProperty(&StackModeSP);
239
240 #ifdef WITH_V4L2_EXPERIMENTS
241 defineProperty(&ImageDepthSP);
242 defineProperty(&ColorProcessingSP);
243 defineProperty(&CaptureColorSpaceTP);
244 #endif
245 }
246 }
247
updateProperties()248 bool V4L2_Driver::updateProperties()
249 {
250 INDI::CCD::updateProperties();
251
252 if (isConnected())
253 {
254 //ExposeTimeNP=getNumber("CCD_EXPOSURE");
255 //ExposeTimeN=ExposeTimeNP->np;
256
257 CompressSP = getSwitch("CCD_COMPRESSION");
258 CompressS = CompressSP->sp;
259
260 FrameNP = getNumber("CCD_FRAME");
261 FrameN = FrameNP->np;
262
263 defineProperty(&camNameTP);
264 getBasicData();
265
266 defineProperty(&ImageColorSP);
267 defineProperty(&InputsSP);
268 defineProperty(&CaptureFormatsSP);
269
270 if (CaptureSizesSP.sp != nullptr)
271 defineProperty(&CaptureSizesSP);
272 else if (CaptureSizesNP.np != nullptr)
273 defineProperty(&CaptureSizesNP);
274 if (FrameRatesSP.sp != nullptr)
275 defineProperty(&FrameRatesSP);
276 else if (FrameRateNP.np != nullptr)
277 defineProperty(&FrameRateNP);
278
279 defineProperty(&StackModeSP);
280
281 #ifdef WITH_V4L2_EXPERIMENTS
282 defineProperty(&ImageDepthSP);
283 defineProperty(&ColorProcessingSP);
284 defineProperty(&CaptureColorSpaceTP);
285 #endif
286
287 // Check if we have pixel size info
288 const PixelSizeInfo * info = pixelSizeInfo;
289 std::string deviceName = std::string(v4l_base->getDeviceName());
290 // to lower case.
291 std::transform(deviceName.begin(), deviceName.end(), deviceName.begin(), ::tolower);
292 while (info->deviceName)
293 {
294 std::string infoDeviceName = std::string(info->deviceName);
295 std::transform(infoDeviceName.begin(), infoDeviceName.end(), infoDeviceName.begin(), ::tolower);
296
297 // Case insensitive comparision
298 if (infoDeviceName == deviceName)
299 break;
300 ++info;
301 }
302
303 const char * commonName = info->commonName;
304 float pixX = info->pixelSizeX;
305 float pixY = info->pixelSizeY;
306
307 if (!commonName)
308 commonName = info->deviceName;
309 if (pixY < 0)
310 pixY = pixX;
311
312 if (info->deviceName)
313 {
314 LOGF_INFO("Setting pixel size correctly for %s", commonName);
315 if (info->tested == false)
316 {
317 LOGF_INFO("Please report that the camera worked: Name: %s/%s Detected and working, to https://bit.ly/2S1Vxjq",
318 v4l_base->getDeviceName(), commonName);
319 }
320 }
321 else
322 {
323 LOGF_INFO("Setting pixel size to default of %5.2f", pixX);
324 LOGF_INFO("For future autodetection of pixel size, please report the following: Reported Name: %s, "
325 "Common Name (Eg: NexImage 10), Pixel Size to the following thread: https://bit.ly/2S1Vxjq",
326 v4l_base->getDeviceName());
327 }
328 SetCCDParams(V4LFrame->width, V4LFrame->height, V4LFrame->bpp, pixX, pixY);
329 PrimaryCCD.setImageExtension("fits");
330
331 if (v4l_base->isLXmodCapable())
332 lx->updateProperties();
333 return true;
334 }
335 else
336 {
337 unsigned int i;
338
339 if (v4l_base->isLXmodCapable())
340 lx->updateProperties();
341
342 deleteProperty(camNameTP.name);
343
344 deleteProperty(ImageColorSP.name);
345 deleteProperty(InputsSP.name);
346 deleteProperty(CaptureFormatsSP.name);
347
348 if (CaptureSizesSP.sp != nullptr)
349 deleteProperty(CaptureSizesSP.name);
350 else if (CaptureSizesNP.np != nullptr)
351 deleteProperty(CaptureSizesNP.name);
352 if (FrameRatesSP.sp != nullptr)
353 deleteProperty(FrameRatesSP.name);
354 else if (FrameRateNP.np != nullptr)
355 deleteProperty(FrameRateNP.name);
356
357 deleteProperty(ImageAdjustNP.name);
358 for (i = 0; i < v4loptions; i++)
359 deleteProperty(Options[i].name);
360 if (Options)
361 free(Options);
362 Options = nullptr;
363 v4loptions = 0;
364
365 deleteProperty(StackModeSP.name);
366
367 #ifdef WITH_V4L2_EXPERIMENTS
368 deleteProperty(ImageDepthSP.name);
369 deleteProperty(ColorProcessingSP.name);
370 deleteProperty(CaptureColorSpaceTP.name);
371 #endif
372
373 return true;
374 }
375 }
376
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)377 bool V4L2_Driver::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
378 {
379 char errmsg[ERRMSGSIZ];
380 unsigned int iopt;
381
382 /* ignore if not ours */
383 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
384 return true;
385
386 /* Input */
387 if (strcmp(name, InputsSP.name) == 0)
388 {
389 //if ((StreamSP.s == IPS_BUSY) || (ExposeTimeNP->s == IPS_BUSY) || (RecordStreamSP.s == IPS_BUSY)) {
390 if (PrimaryCCD.isExposing() || Streamer->isBusy())
391 {
392 LOG_ERROR("Can not set input while capturing.");
393 InputsSP.s = IPS_ALERT;
394 IDSetSwitch(&InputsSP, nullptr);
395 return false;
396 }
397 else
398 {
399 unsigned int inputindex, oldindex;
400 oldindex = IUFindOnSwitchIndex(&InputsSP);
401 IUResetSwitch(&InputsSP);
402 IUUpdateSwitch(&InputsSP, states, names, n);
403 inputindex = IUFindOnSwitchIndex(&InputsSP);
404
405 if (v4l_base->setinput(inputindex, errmsg) == -1)
406 {
407 LOGF_INFO("ERROR (setinput): %s", errmsg);
408 IUResetSwitch(&InputsSP);
409 InputsSP.sp[oldindex].s = ISS_ON;
410 InputsSP.s = IPS_ALERT;
411 IDSetSwitch(&InputsSP, nullptr);
412 return false;
413 }
414
415 deleteProperty(CaptureFormatsSP.name);
416 v4l_base->getcaptureformats(&CaptureFormatsSP);
417 defineProperty(&CaptureFormatsSP);
418 if (CaptureSizesSP.sp != nullptr)
419 deleteProperty(CaptureSizesSP.name);
420 else if (CaptureSizesNP.np != nullptr)
421 deleteProperty(CaptureSizesNP.name);
422
423 v4l_base->getcapturesizes(&CaptureSizesSP, &CaptureSizesNP);
424
425 if (CaptureSizesSP.sp != nullptr)
426 defineProperty(&CaptureSizesSP);
427 else if (CaptureSizesNP.np != nullptr)
428 defineProperty(&CaptureSizesNP);
429 InputsSP.s = IPS_OK;
430 IDSetSwitch(&InputsSP, nullptr);
431 LOGF_INFO("Capture input: %d. %s", inputindex, InputsSP.sp[inputindex].name);
432 return true;
433 }
434 }
435
436 /* Capture Format */
437 if (strcmp(name, CaptureFormatsSP.name) == 0)
438 {
439 //if ((StreamSP.s == IPS_BUSY) || (ExposeTimeNP->s == IPS_BUSY) || (RecordStreamSP.s == IPS_BUSY)) {
440 if (PrimaryCCD.isExposing() || Streamer->isBusy())
441 {
442 LOG_ERROR("Can not set format while capturing.");
443 CaptureFormatsSP.s = IPS_ALERT;
444 IDSetSwitch(&CaptureFormatsSP, nullptr);
445 return false;
446 }
447 else
448 {
449 unsigned int index, oldindex;
450 oldindex = IUFindOnSwitchIndex(&CaptureFormatsSP);
451 IUResetSwitch(&CaptureFormatsSP);
452 IUUpdateSwitch(&CaptureFormatsSP, states, names, n);
453 index = IUFindOnSwitchIndex(&CaptureFormatsSP);
454
455 if (v4l_base->setcaptureformat(*((unsigned int *)CaptureFormatsSP.sp[index].aux), errmsg) == -1)
456 {
457 LOGF_INFO("ERROR (setformat): %s", errmsg);
458 IUResetSwitch(&CaptureFormatsSP);
459 CaptureFormatsSP.sp[oldindex].s = ISS_ON;
460 CaptureFormatsSP.s = IPS_ALERT;
461 IDSetSwitch(&CaptureFormatsSP, nullptr);
462 return false;
463 }
464
465 V4LFrame->bpp = v4l_base->getBpp();
466 PrimaryCCD.setBPP(V4LFrame->bpp);
467
468 if (CaptureSizesSP.sp != nullptr)
469 deleteProperty(CaptureSizesSP.name);
470 else if (CaptureSizesNP.np != nullptr)
471 deleteProperty(CaptureSizesNP.name);
472 v4l_base->getcapturesizes(&CaptureSizesSP, &CaptureSizesNP);
473
474 if (CaptureSizesSP.sp != nullptr)
475 defineProperty(&CaptureSizesSP);
476 else if (CaptureSizesNP.np != nullptr)
477 defineProperty(&CaptureSizesNP);
478 CaptureFormatsSP.s = IPS_OK;
479
480 #ifdef WITH_V4L2_EXPERIMENTS
481 IUSaveText(&CaptureColorSpaceT[0], getColorSpaceName(&v4l_base->fmt));
482 IUSaveText(&CaptureColorSpaceT[1], getYCbCrEncodingName(&v4l_base->fmt));
483 IUSaveText(&CaptureColorSpaceT[2], getQuantizationName(&v4l_base->fmt));
484 IDSetText(&CaptureColorSpaceTP, nullptr);
485 #endif
486 //direct_record=recorder->setpixelformat(v4l_base->fmt.fmt.pix.pixelformat);
487 INDI_PIXEL_FORMAT pixelFormat;
488 uint8_t pixelDepth = 8;
489 if (getPixelFormat(v4l_base->fmt.fmt.pix.pixelformat, pixelFormat, pixelDepth))
490 Streamer->setPixelFormat(pixelFormat, pixelDepth);
491
492 saveConfig(true, CaptureFormatsSP.name);
493 IDSetSwitch(&CaptureFormatsSP, "Capture format: %d. %s", index, CaptureFormatsSP.sp[index].name);
494 return true;
495 }
496 }
497
498 /* Capture Size (Discrete) */
499 if (strcmp(name, CaptureSizesSP.name) == 0)
500 {
501 //if ((StreamSP.s == IPS_BUSY) || (ExposeTimeNP->s == IPS_BUSY) || (RecordStreamSP.s == IPS_BUSY)) {
502 if (PrimaryCCD.isExposing() || Streamer->isBusy())
503 {
504 LOG_ERROR("Can not set capture size while capturing.");
505 CaptureSizesSP.s = IPS_ALERT;
506 IDSetSwitch(&CaptureSizesSP, nullptr);
507 return false;
508 }
509 else
510 {
511 unsigned int index, w, h;
512 IUUpdateSwitch(&CaptureSizesSP, states, names, n);
513 index = IUFindOnSwitchIndex(&CaptureSizesSP);
514 sscanf(CaptureSizesSP.sp[index].name, "%dx%d", &w, &h);
515 if (v4l_base->setcapturesize(w, h, errmsg) == -1)
516 {
517 LOGF_INFO("ERROR (setsize): %s", errmsg);
518 CaptureSizesSP.s = IPS_ALERT;
519 IDSetSwitch(&CaptureSizesSP, nullptr);
520 return false;
521 }
522
523 if (FrameRatesSP.sp != nullptr)
524 deleteProperty(FrameRatesSP.name);
525 else if (FrameRateNP.np != nullptr)
526 deleteProperty(FrameRateNP.name);
527 v4l_base->getframerates(&FrameRatesSP, &FrameRateNP);
528 if (FrameRatesSP.sp != nullptr)
529 defineProperty(&FrameRatesSP);
530 else if (FrameRateNP.np != nullptr)
531 defineProperty(&FrameRateNP);
532
533 PrimaryCCD.setFrame(0, 0, w, h);
534 V4LFrame->width = w;
535 V4LFrame->height = h;
536 PrimaryCCD.setResolution(w, h);
537 updateFrameSize();
538 Streamer->setSize(w, h);
539
540 CaptureSizesSP.s = IPS_OK;
541 IDSetSwitch(&CaptureSizesSP, "Capture size (discrete): %d. %s", index, CaptureSizesSP.sp[index].name);
542
543 saveConfig(true, CaptureSizesSP.name);
544 return true;
545 }
546 }
547
548 /* Frame Rate (Discrete) */
549 if (strcmp(name, FrameRatesSP.name) == 0)
550 {
551 if (PrimaryCCD.isExposing() || Streamer->isBusy())
552 {
553 LOG_ERROR("Can not change frame rate while capturing.");
554 FrameRatesSP.s = IPS_ALERT;
555 IDSetSwitch(&FrameRatesSP, nullptr);
556 return false;
557 }
558 unsigned int index;
559 struct v4l2_fract frate;
560 IUUpdateSwitch(&FrameRatesSP, states, names, n);
561 index = IUFindOnSwitchIndex(&FrameRatesSP);
562 sscanf(FrameRatesSP.sp[index].name, "%d/%d", &frate.numerator, &frate.denominator);
563 if ((v4l_base->*(v4l_base->setframerate))(frate, errmsg) == -1)
564 {
565 LOGF_INFO("ERROR (setframerate): %s", errmsg);
566 FrameRatesSP.s = IPS_ALERT;
567 IDSetSwitch(&FrameRatesSP, nullptr);
568 return false;
569 }
570
571 FrameRatesSP.s = IPS_OK;
572 IDSetSwitch(&FrameRatesSP, "Frame Period (discrete): %d. %s", index, FrameRatesSP.sp[index].name);
573 return true;
574 }
575
576 /* Image Type */
577 if (strcmp(name, ImageColorSP.name) == 0)
578 {
579 if (Streamer->isRecording())
580 {
581 LOG_WARN("Can not set Image type (GRAY/COLOR) while recording.");
582 return false;
583 }
584
585 IUResetSwitch(&ImageColorSP);
586 IUUpdateSwitch(&ImageColorSP, states, names, n);
587 ImageColorSP.s = IPS_OK;
588 if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON)
589 {
590 //PrimaryCCD.setBPP(8);
591 PrimaryCCD.setNAxis(2);
592 }
593 else
594 {
595 //PrimaryCCD.setBPP(32);
596 //PrimaryCCD.setBPP(8);
597 PrimaryCCD.setNAxis(3);
598 }
599
600 updateFrameSize();
601 #if 0
602 INDI_PIXEL_FORMAT pixelFormat;
603 uint8_t pixelDepth = 8;
604 if (getPixelFormat(v4l_base->fmt.fmt.pix.pixelformat, pixelFormat, pixelDepth))
605 Streamer->setPixelFormat(pixelFormat, pixelDepth);
606 #endif
607 Streamer->setPixelFormat((ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON) ? INDI_MONO : INDI_RGB, 8);
608 IDSetSwitch(&ImageColorSP, nullptr);
609
610 saveConfig(true, ImageColorSP.name);
611 return true;
612 }
613
614 /* Image Depth */
615 if (strcmp(name, ImageDepthSP.name) == 0)
616 {
617 if (Streamer->isRecording())
618 {
619 LOG_WARN("Can not set Image depth (8/16bits) while recording.");
620 return false;
621 }
622
623 IUResetSwitch(&ImageDepthSP);
624 IUUpdateSwitch(&ImageDepthSP, states, names, n);
625 ImageDepthSP.s = IPS_OK;
626 if (ImageDepthS[0].s == ISS_ON)
627 {
628 PrimaryCCD.setBPP(8);
629 }
630 else
631 {
632 PrimaryCCD.setBPP(16);
633 }
634 IDSetSwitch(&ImageDepthSP, nullptr);
635 return true;
636 }
637
638 /* Stacking Mode */
639 if (strcmp(name, StackModeSP.name) == 0)
640 {
641 IUResetSwitch(&StackModeSP);
642 IUUpdateSwitch(&StackModeSP, states, names, n);
643 StackModeSP.s = IPS_OK;
644 stackMode = IUFindOnSwitchIndex(&StackModeSP);
645 if (stackMode == STACK_RESET_DARK)
646 {
647 if (V4LFrame->darkFrame != nullptr)
648 {
649 free(V4LFrame->darkFrame);
650 V4LFrame->darkFrame = nullptr;
651 }
652 }
653
654 IDSetSwitch(&StackModeSP, "Setting Stacking Mode: %s", StackModeS[stackMode].name);
655 return true;
656 }
657
658 /* V4L2 Options/Menus */
659 for (iopt = 0; iopt < v4loptions; iopt++)
660 if (strcmp(Options[iopt].name, name) == 0)
661 break;
662 if (iopt < v4loptions)
663 {
664 unsigned int ctrl_id, optindex, ctrlindex;
665
666 LOGF_DEBUG("Toggle switch %s=%s", Options[iopt].name, Options[iopt].label);
667
668 Options[iopt].s = IPS_IDLE;
669 IUResetSwitch(&Options[iopt]);
670 if (IUUpdateSwitch(&Options[iopt], states, names, n) < 0)
671 return false;
672
673 optindex = IUFindOnSwitchIndex(&Options[iopt]);
674 if (Options[iopt].sp[optindex].aux != nullptr)
675 ctrlindex = *(unsigned int *)(Options[iopt].sp[optindex].aux);
676 else
677 ctrlindex = optindex;
678 ctrl_id = (*((unsigned int *)Options[iopt].aux));
679 LOGF_DEBUG(" On switch is (%d) %s=\"%s\", ctrl_id = 0x%X ctrl_index=%d", optindex,
680 Options[iopt].sp[optindex].name, Options[iopt].sp[optindex].label, ctrl_id, ctrlindex);
681 if (v4l_base->setOPTControl(ctrl_id, ctrlindex, errmsg) < 0)
682 {
683 if (Options[iopt].nsp == 1) // button
684 {
685 Options[iopt].sp[optindex].s = ISS_OFF;
686 }
687 Options[iopt].s = IPS_ALERT;
688 IDSetSwitch(&Options[iopt], nullptr);
689 LOGF_ERROR("Unable to adjust setting. %s", errmsg);
690 return false;
691 }
692 if (Options[iopt].nsp == 1) // button
693 {
694 Options[iopt].sp[optindex].s = ISS_OFF;
695 }
696 Options[iopt].s = IPS_OK;
697 IDSetSwitch(&Options[iopt], nullptr);
698 return true;
699 }
700
701 /* ColorProcessing */
702 if (strcmp(name, ColorProcessingSP.name) == 0)
703 {
704 if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON)
705 {
706 IUUpdateSwitch(&ColorProcessingSP, states, names, n);
707 v4l_base->setColorProcessing(ColorProcessingS[0].s == ISS_ON, ColorProcessingS[1].s == ISS_ON,
708 ColorProcessingS[2].s == ISS_ON);
709 ColorProcessingSP.s = IPS_OK;
710 IDSetSwitch(&ColorProcessingSP, nullptr);
711 V4LFrame->bpp = v4l_base->getBpp();
712 PrimaryCCD.setBPP(V4LFrame->bpp);
713 PrimaryCCD.setBPP(V4LFrame->bpp);
714 updateFrameSize();
715 return true;
716 }
717 else
718 {
719 LOG_WARN("No color processing in color mode ");
720 return false;
721 }
722 }
723 lx->ISNewSwitch(dev, name, states, names, n);
724 return INDI::CCD::ISNewSwitch(dev, name, states, names, n);
725 }
726
ISNewText(const char * dev,const char * name,char * texts[],char * names[],int n)727 bool V4L2_Driver::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
728 {
729 IText * tp;
730
731 /* ignore if not ours */
732 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
733 return true;
734
735 if (strcmp(name, PortTP.name) == 0)
736 {
737 PortTP.s = IPS_OK;
738 tp = IUFindText(&PortTP, names[0]);
739 if (!tp)
740 return false;
741 IUSaveText(tp, texts[0]);
742 IDSetText(&PortTP, nullptr);
743
744 saveConfig(true, PortTP.name);
745 return true;
746 }
747
748 lx->ISNewText(dev, name, texts, names, n);
749 return INDI::CCD::ISNewText(dev, name, texts, names, n);
750 }
751
ISNewNumber(const char * dev,const char * name,double values[],char * names[],int n)752 bool V4L2_Driver::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
753 {
754 char errmsg[ERRMSGSIZ];
755
756 /* ignore if not ours */
757 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
758 return true;
759
760 /* Capture Size (Step/Continuous) */
761 if (strcmp(name, CaptureSizesNP.name) == 0)
762 {
763 if (PrimaryCCD.isExposing() || Streamer->isBusy())
764 {
765 LOG_ERROR("Can not set capture size while capturing.");
766 CaptureSizesNP.s = IPS_BUSY;
767 IDSetNumber(&CaptureSizesNP, nullptr);
768 return false;
769 }
770 else
771 {
772 unsigned int sizes[2], w = 0, h = 0;
773 double rsizes[2];
774
775 if (strcmp(names[0], "Width") == 0)
776 {
777 sizes[0] = values[0];
778 sizes[1] = values[1];
779 }
780 else
781 {
782 sizes[0] = values[1];
783 sizes[1] = values[0];
784 }
785 if (v4l_base->setcapturesize(sizes[0], sizes[1], errmsg) == -1)
786 {
787 LOGF_INFO("ERROR (setsize): %s", errmsg);
788 CaptureSizesNP.s = IPS_ALERT;
789 IDSetNumber(&CaptureSizesNP, nullptr);
790 return false;
791 }
792 if (strcmp(names[0], "Width") == 0)
793 {
794 w = v4l_base->getWidth();
795 rsizes[0] = (double)w;
796 h = v4l_base->getHeight();
797 rsizes[1] = (double)h;
798 }
799 else
800 {
801 w = v4l_base->getWidth();
802 rsizes[1] = (double)w;
803 h = v4l_base->getHeight();
804 rsizes[0] = (double)h;
805 }
806
807 PrimaryCCD.setFrame(0, 0, w, h);
808 IUUpdateNumber(&CaptureSizesNP, rsizes, names, n);
809 V4LFrame->width = w;
810 V4LFrame->height = h;
811 PrimaryCCD.setResolution(w, h);
812 CaptureSizesNP.s = IPS_OK;
813 updateFrameSize();
814 Streamer->setSize(w, h);
815
816 IDSetNumber(&CaptureSizesNP, "Capture size (step/cont): %dx%d", w, h);
817 return true;
818 }
819 }
820
821 if (strcmp(ImageAdjustNP.name, name) == 0)
822 {
823 ImageAdjustNP.s = IPS_IDLE;
824
825 if (IUUpdateNumber(&ImageAdjustNP, values, names, n) < 0)
826 return false;
827
828 for (int i = 0; i < ImageAdjustNP.nnp; i++)
829 {
830 unsigned int const ctrl_id = *((unsigned int *)ImageAdjustNP.np[i].aux0);
831 double const value = ImageAdjustNP.np[i].value;
832
833 LOGF_DEBUG(" Setting %s (%s) to %f, ctrl_id = 0x%X", ImageAdjustNP.np[i].name,
834 ImageAdjustNP.np[i].label, value, ctrl_id);
835
836 if (v4l_base->setINTControl(ctrl_id, ImageAdjustNP.np[i].value, errmsg) < 0)
837 {
838 /* Some controls may become read-only depending on selected options */
839 LOGF_WARN("Unable to adjust %s (ctrl_id = 0x%X)", ImageAdjustNP.np[i].label,
840 ctrl_id);
841 }
842 /* Some controls may have been ajusted by the driver */
843 /* a read is mandatory as VIDIOC_S_CTRL is write only and does not return the actual new value */
844 v4l_base->getControl(ctrl_id, &(ImageAdjustNP.np[i].value), errmsg);
845
846 /* Warn the client if the control returned another value than what was set */
847 if(value != ImageAdjustNP.np[i].value)
848 {
849 LOGF_WARN("Control %s set to %f returned %f (ctrl_id = 0x%X)",
850 ImageAdjustNP.np[i].label, value, ImageAdjustNP.np[i].value, ctrl_id);
851 }
852 }
853 ImageAdjustNP.s = IPS_OK;
854 IDSetNumber(&ImageAdjustNP, nullptr);
855 return true;
856 }
857
858 return INDI::CCD::ISNewNumber(dev, name, values, names, n);
859 }
860
StartExposure(float duration)861 bool V4L2_Driver::StartExposure(float duration)
862 {
863 /* Clicking the "Expose" set button while an exposure is running arrives here.
864 * Now that V4L2 CCD has the option to abort, this will properly abort the exposure.
865 * If CAN_ABORT is not set, we have to tell the caller we're busy until the end of this exposure.
866 * If we don't, PrimaryCCD will stop exposing nonetheless and we won't be able to restart an exposure.
867 */
868 {
869 if (Streamer->isBusy())
870 {
871 LOG_ERROR("Cannot start new exposure while streamer is busy, stop streaming first");
872 return !(GetCCDCapability() & CCD_CAN_ABORT);
873 }
874
875 if (is_capturing)
876 {
877 LOGF_ERROR(
878 "Cannot start new exposure until the current one completes (%.3f seconds left).",
879 getRemainingExposure());
880 return !(GetCCDCapability() & CCD_CAN_ABORT);
881
882 return true;
883 }
884 }
885
886 if (setShutter(duration))
887 {
888 V4LFrame->expose = duration;
889 PrimaryCCD.setExposureDuration(duration);
890
891 if (!lx->isEnabled() || lx->getLxmode() == LXSERIAL)
892 start_capturing(false);
893
894 /* Update exposure duration in client */
895 /* FIXME: exposure update timer has period hardcoded 1 second */
896 if (is_capturing && 1.0f < duration)
897 {
898 //if (-1 != stdtimer)
899 // IERmTimer(stdtimer);
900 //stdtimer = IEAddTimer(1000, (IE_TCF *)stdtimerCallback, this);
901 stdtimer = -1;
902 }
903 else
904 stdtimer = -1;
905 }
906
907 return is_capturing;
908 }
909
setShutter(double duration)910 bool V4L2_Driver::setShutter(double duration)
911 {
912 if (lx->isEnabled())
913 {
914 LOGF_INFO("Using long exposure mode for %.3f sec frame.", duration);
915 if (startlongexposure(duration))
916 {
917 LOGF_INFO("Started %.3f-second long exposure.", duration);
918 return true;
919 }
920 else
921 {
922 DEBUGF(INDI::Logger::DBG_WARNING,
923 "Unable to start %.3f-second long exposure, falling back to auto exposure", duration);
924 return false;
925 }
926 }
927 else if (setManualExposure(duration))
928 {
929 exposure_duration.tv_sec = (long) duration;
930 exposure_duration.tv_usec = (long) ((duration - (double) exposure_duration.tv_sec) * 1000000.0f);
931
932 elapsed_exposure.tv_sec = 0;
933 elapsed_exposure.tv_usec = 0;
934
935 gettimeofday(&capture_start, nullptr);
936
937 frameCount = 0;
938 subframeCount = 0;
939
940 LOGF_INFO("Started %.3f-second manual exposure.", duration);
941 return true;
942 }
943 else
944 {
945 LOGF_WARN("Failed %.3f-second manual exposure, no adequate control is registered.",
946 duration);
947 return false;
948 }
949 }
950
setManualExposure(double duration)951 bool V4L2_Driver::setManualExposure(double duration)
952 {
953 /* N.B. Check how this differs from one camera to another. This is just a proof of concept for now */
954 /* With DMx 21AU04.AS, exposing twice with the same duration causes an incomplete frame to pop in the buffer list
955 * This can be worked around by verifying the buffer size, but it won't work for anything else than Y8/Y16, so set
956 * exposure unconditionally */
957 /*if (duration * 10000 != AbsExposureN->value)*/
958
959 // INT control for manual exposure duration is an integer in 1/10000 seconds
960 long ticks = lround(duration * 10000.0f);
961
962 /* First check the presence of an absolute exposure control */
963 if (nullptr == AbsExposureN)
964 {
965 /* We don't have an absolute exposure control but we can stack gray frames until the exposure elapses */
966 if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON && stackMode != STACK_NONE && stackMode != STACK_RESET_DARK)
967 {
968 LOGF_WARN("Absolute exposure duration control is undefined, stacking up to %.3f seconds using %.16s.",
969 duration, StackModeS[stackMode].name);
970 return true;
971 }
972 /* We don't have an absolute exposure control and stacking is not configured, bail out */
973 else
974 {
975 LOGF_ERROR("Failed exposing, the absolute exposure duration control is undefined, and stacking is not ready.", "");
976 LOGF_ERROR("Configure grayscale and stacking in order to stack streamed frames up to %.3f seconds.", duration);
977 return false;
978 }
979 }
980 /* Then if we have an exposure control, check the requested exposure duration */
981 else if (AbsExposureN->max < ticks)
982 {
983 if( ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON && stackMode == STACK_NONE )
984 {
985 LOG_WARN("Requested manual exposure is out of device bounds auto set stackMode to ADDITIVE" );
986 stackMode = STACK_ADDITIVE;
987 StackModeSP.sp[ STACK_NONE ].s = ISS_OFF;
988 StackModeSP.sp[ STACK_ADDITIVE ].s = ISS_ON;
989 if(ManualExposureSP) IDSetSwitch(ManualExposureSP, nullptr);
990 }
991
992 /* We can't expose as long as requested but we can stack gray frames until the exposure elapses */
993 if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON && stackMode != STACK_NONE && stackMode != STACK_RESET_DARK)
994 {
995 if( AbsExposureN->value != AbsExposureN->max )
996 {
997 LOGF_WARN("Requested manual exposure is out of device bounds [%.3f,%.3f], stacking up to %.3f seconds using %.16s.",
998 (double) AbsExposureN->min / 10000.0f, (double) AbsExposureN->max / 10000.0f,
999 duration, StackModeS[stackMode].name);
1000 }
1001 ticks = AbsExposureN->max;
1002 }
1003 /* We can't expose as long as requested and stacking is not configured, bail out */
1004 else
1005 {
1006 LOGF_ERROR("Failed %.3f-second manual exposure, out of device bounds [%.3f,%.3f], and stacking is not ready.",
1007 duration, (double) AbsExposureN->min / 10000.0f, (double) AbsExposureN->max / 10000.0f);
1008 LOGF_ERROR("Configure grayscale and stacking in order to stack streamed frames up to %.3f seconds.", duration);
1009 return false;
1010 }
1011 }
1012 /* Lower-than-minimal exposure duration is left managed below */
1013
1014
1015 frame_duration.tv_sec = ticks / 10000;
1016 frame_duration.tv_usec = (ticks % 10000) * 100;
1017
1018 if( v4l_capture_started )
1019 {
1020 if( AbsExposureN->value != ticks )
1021 {
1022 stop_capturing();
1023 }
1024 else
1025 {
1026 return true;
1027 }
1028 }
1029
1030
1031 /* At this point we do have an absolute exposure control and a valid exposure duration, so start exposing. */
1032
1033 /* Manual mode should be set before changing Exposure (Auto), if possible.
1034 * In some cases there might be no control available, so don't fail and try to continue.
1035 */
1036 if (ManualExposureSP)
1037 {
1038 if (ManualExposureSP->sp[0].s == ISS_OFF)
1039 {
1040 ManualExposureSP->sp[0].s = ISS_ON;
1041 ManualExposureSP->sp[1].s = ISS_OFF;
1042 ManualExposureSP->s = IPS_IDLE;
1043
1044 unsigned int const ctrlindex = ManualExposureSP->sp[0].aux ? *(unsigned int *)(ManualExposureSP->sp[0].aux) : 0;
1045 unsigned int const ctrl_id = (*((unsigned int *)ManualExposureSP->aux));
1046
1047 char errmsg[MAXRBUF];
1048 if (v4l_base->setOPTControl(ctrl_id, ctrlindex, errmsg) < 0)
1049 {
1050 ManualExposureSP->sp[0].s = ISS_OFF;
1051 ManualExposureSP->sp[1].s = ISS_ON;
1052 ManualExposureSP->s = IPS_ALERT;
1053 IDSetSwitch(ManualExposureSP, nullptr);
1054
1055 LOGF_ERROR("Unable to adjust manual/auto exposure control. %s", errmsg);
1056 return false;
1057 }
1058
1059 ManualExposureSP->s = IPS_OK;
1060 IDSetSwitch(ManualExposureSP, nullptr);
1061 }
1062 }
1063 else
1064 {
1065 LOGF_WARN("Failed switching to manual exposure, control is unavailable", "");
1066 /* Don't fail, let the driver try to set the absolute duration, we'll see what happens */
1067 /* return false; */
1068 }
1069
1070 /* Configure absolute exposure */
1071 if (AbsExposureN->min <= ticks && ticks <= AbsExposureN->max)
1072 {
1073 double const restoredValue = AbsExposureN->value;
1074 AbsExposureN->value = ticks;
1075
1076 LOGF_DEBUG("%.3f-second exposure translates to %ld 1/10,000th-second device ticks.",
1077 duration, ticks);
1078
1079 unsigned int const ctrl_id = *((unsigned int *)AbsExposureN->aux0);
1080
1081 char errmsg[MAXRBUF];
1082 if (v4l_base->setINTControl(ctrl_id, AbsExposureN->value, errmsg) < 0)
1083 {
1084 ImageAdjustNP.s = IPS_ALERT;
1085 AbsExposureN->value = restoredValue;
1086 IDSetNumber(&ImageAdjustNP, "Failed requesting %.3f-second exposure to the driver (%s).", duration, errmsg);
1087 return false;
1088 }
1089
1090 ImageAdjustNP.s = IPS_OK;
1091 IDSetNumber(&ImageAdjustNP, nullptr);
1092 }
1093 else
1094 {
1095 LOGF_ERROR("Failed %.3f-second manual exposure, out of device bounds [%.3f,%.3f].",
1096 duration, (double) AbsExposureN->min / 10000.0f, (double) AbsExposureN->max / 10000.0f);
1097 return false;
1098 }
1099
1100 return true;
1101 }
1102
1103 /** \internal Timer callback.
1104 *
1105 * This provides a very rough estimation of the remaining exposure to the client.
1106 */
stdtimerCallback(void * userpointer)1107 void V4L2_Driver::stdtimerCallback(void * userpointer)
1108 {
1109 V4L2_Driver * p = (V4L2_Driver *)userpointer;
1110 float remaining = p->getRemainingExposure();
1111 //DEBUGF(INDI::Logger::DBG_SESSION,"Exposure running, %f seconds left...", remaining);
1112 if (1.0f < remaining)
1113 p->stdtimer = IEAddTimer(1000, (IE_TCF *)stdtimerCallback, userpointer);
1114 else
1115 p->stdtimer = -1;
1116 p->PrimaryCCD.setExposureLeft(remaining);
1117 }
1118
start_capturing(bool do_stream)1119 bool V4L2_Driver::start_capturing(bool do_stream)
1120 {
1121 // FIXME Must migrate completely to Stream
1122 // The class shouldn't be making calls to encoder/recorder directly
1123 // Stream? Yes or No
1124 // Direct Record?
1125 INDI_UNUSED(do_stream);
1126 if (Streamer->isBusy())
1127 {
1128 LOG_WARN("Cannot start exposure while streaming is in progress");
1129 return false;
1130 }
1131
1132 if (is_capturing)
1133 {
1134 LOGF_WARN("Cannot start exposure while another is in progress (%.3f seconds left)",
1135 getRemainingExposure());
1136 return false;
1137 }
1138
1139 if( !v4l_capture_started )
1140 {
1141 char errmsg[ERRMSGSIZ];
1142 if (v4l_base->start_capturing(errmsg))
1143 {
1144 LOGF_WARN("V4L2 base failed starting capture (%s)", errmsg);
1145 return false;
1146 }
1147 else
1148 {
1149 gettimeofday(&frame_received, nullptr);
1150 v4l_capture_started = true;
1151 }
1152 }
1153
1154 //if (do_stream)
1155 //v4l_base->doRecord(Streamer->isDirectRecording());
1156
1157 is_capturing = true;
1158 return true;
1159 }
1160
stop_capturing()1161 bool V4L2_Driver::stop_capturing()
1162 {
1163 if (!is_capturing && !v4l_capture_started)
1164 {
1165 LOG_WARN("No exposure or streaming in progress");
1166 return true;
1167 }
1168
1169 if (!Streamer->isBusy() && 0.0f < getRemainingExposure())
1170 {
1171 LOGF_WARN("Stopping running exposure %.3f seconds before completion",
1172 getRemainingExposure());
1173 }
1174
1175 // FIXME what to do with doRecord?
1176 //if(Streamer->isDirectRecording())
1177 //v4l_base->doRecord(false);
1178 char errmsg[ERRMSGSIZ];
1179 if (v4l_base->stop_capturing(errmsg))
1180 {
1181 LOGF_WARN("V4L2 base failed stopping capture (%s)", errmsg);
1182 }
1183
1184 is_capturing = false;
1185 v4l_capture_started = false;
1186 return true;
1187 }
1188
startlongexposure(double timeinsec)1189 bool V4L2_Driver::startlongexposure(double timeinsec)
1190 {
1191 lxtimer = IEAddTimer((int)(timeinsec * 1000.0), (IE_TCF *)lxtimerCallback, this);
1192 v4l_base->setlxstate(LX_ACCUMULATING);
1193 return (lx->startLx());
1194 }
1195
lxtimerCallback(void * userpointer)1196 void V4L2_Driver::lxtimerCallback(void * userpointer)
1197 {
1198 V4L2_Driver * p = (V4L2_Driver *)userpointer;
1199
1200 p->lx->stopLx();
1201 if (p->lx->getLxmode() == LXSERIAL)
1202 {
1203 p->v4l_base->setlxstate(LX_TRIGGERED);
1204 }
1205 else
1206 {
1207 p->v4l_base->setlxstate(LX_ACTIVE);
1208 }
1209 IERmTimer(p->lxtimer);
1210 if (!p->v4l_base->isstreamactive())
1211 p->is_capturing = p->start_capturing(false); // jump to new/updateFrame
1212 //p->v4l_base->start_capturing(errmsg); // jump to new/updateFrame
1213 }
1214
UpdateCCDBin(int hor,int ver)1215 bool V4L2_Driver::UpdateCCDBin(int hor, int ver)
1216 {
1217 if (ImageColorS[IMAGE_COLOR].s == ISS_ON)
1218 {
1219 if (hor == 1 && ver == 1)
1220 {
1221 PrimaryCCD.setBin(hor, ver);
1222 Streamer->setSize(PrimaryCCD.getSubW(), PrimaryCCD.getSubH());
1223 return true;
1224 }
1225
1226 LOG_WARN("Binning color frames is currently not supported.");
1227 return false;
1228 }
1229
1230 if (hor != ver)
1231 {
1232 LOGF_WARN("Cannot accept asymmetrical binning %dx%d.", hor, ver);
1233 return false;
1234 }
1235
1236 if (hor != 1 && hor != 2 && hor != 4)
1237 {
1238 LOG_WARN("Can only accept 1x1, 2x2, and 4x4 binning.");
1239 return false;
1240 }
1241
1242 if (Streamer->isBusy())
1243 {
1244 LOG_WARN("Cannot change binning while streaming/recording.");
1245 return false;
1246 }
1247
1248 PrimaryCCD.setBin(hor, ver);
1249 Streamer->setSize(PrimaryCCD.getSubW() / hor, PrimaryCCD.getSubH() / ver);
1250
1251 return true;
1252 }
1253
UpdateCCDFrame(int x,int y,int w,int h)1254 bool V4L2_Driver::UpdateCCDFrame(int x, int y, int w, int h)
1255 {
1256 char errmsg[ERRMSGSIZ];
1257
1258 //LOGF_INFO("calling updateCCDFrame: %d %d %d %d", x, y, w, h);
1259 //IDLog("calling updateCCDFrame: %d %d %d %d\n", x, y, w, h);
1260 if (v4l_base->setcroprect(x, y, w, h, errmsg) != -1)
1261 {
1262 struct v4l2_rect crect;
1263 crect = v4l_base->getcroprect();
1264
1265 V4LFrame->width = crect.width;
1266 V4LFrame->height = crect.height;
1267 PrimaryCCD.setFrame(x, y, w, h);
1268 updateFrameSize();
1269 Streamer->setSize(w, h);
1270 return true;
1271 }
1272 else
1273 {
1274 LOGF_INFO("ERROR (setcroprect): %s", errmsg);
1275 }
1276
1277 return false;
1278 }
1279
newFrame(void * p)1280 void V4L2_Driver::newFrame(void * p)
1281 {
1282 ((V4L2_Driver *)(p))->newFrame();
1283 }
1284
1285 /** @internal Stack normalized luminance pixels coming from the camera in an accumulator frame.
1286 */
stackFrame()1287 void V4L2_Driver::stackFrame()
1288 {
1289 /* FIXME: use unsigned floats, or double */
1290 size_t const size = v4l_base->getWidth() * v4l_base->getHeight();
1291 float const * src = v4l_base->getLinearY();
1292 float const * const end = v4l_base->getLinearY() + size;
1293 float * dest = V4LFrame->stackedFrame;
1294
1295 if (!V4LFrame->stackedFrame)
1296 {
1297 /* FIXME: allocate and reset the accumulator frame prior to this function, because memory owner is unclear, and we need more speed while accumulating */
1298 V4LFrame->stackedFrame = (float *)malloc(sizeof(float) * size);
1299 memcpy(V4LFrame->stackedFrame, src, sizeof(float) * size);
1300 subframeCount = 1;
1301 }
1302 else
1303 {
1304 /* Clamp to max float value */
1305 float const frameMax = std::numeric_limits<float>::max();
1306 while (src < end) if (frameMax - *dest < *src)
1307 {
1308 *dest++ = frameMax;
1309 src++;
1310 }
1311 else *dest++ += *src++;
1312 subframeCount += 1;
1313 }
1314 }
1315
getElapsedExposure() const1316 struct timeval V4L2_Driver::getElapsedExposure() const
1317 {
1318 struct timeval now = { .tv_sec = 0, .tv_usec = 0 }, duration = { .tv_sec = 0, .tv_usec = 0 };
1319 gettimeofday( &now, nullptr );
1320 timersub(&now, &capture_start, &duration);
1321 return duration;
1322 }
1323
getRemainingExposure() const1324 float V4L2_Driver::getRemainingExposure() const
1325 {
1326 struct timeval remaining = { .tv_sec = 0, .tv_usec = 0 };
1327 timersub(&exposure_duration, &elapsed_exposure, &remaining);
1328 return (float) remaining.tv_sec + (float) remaining.tv_usec / 1000000.0f;
1329 }
1330
newFrame()1331 void V4L2_Driver::newFrame()
1332 {
1333 struct timeval current_frame_duration = frame_received;
1334 gettimeofday(&frame_received, nullptr);
1335 timersub(&frame_received, ¤t_frame_duration, ¤t_frame_duration);
1336
1337
1338 if (Streamer->isBusy())
1339 {
1340 non_capture_frames = 0;
1341
1342 int width = v4l_base->getWidth();
1343 int height = v4l_base->getHeight();
1344 int bpp = v4l_base->getBpp();
1345 int dbpp = 8;
1346 int totalBytes = 0;
1347 unsigned char * buffer = nullptr;
1348
1349 std::unique_lock<std::mutex> guard(ccdBufferLock);
1350 if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON)
1351 {
1352 V4LFrame->Y = v4l_base->getY();
1353 totalBytes = width * height * (dbpp / 8);
1354 buffer = V4LFrame->Y;
1355 }
1356 else
1357 {
1358 V4LFrame->RGB24Buffer = v4l_base->getRGBBuffer();
1359 totalBytes = width * height * (dbpp / 8) * 3;
1360 buffer = V4LFrame->RGB24Buffer;
1361 }
1362
1363 // downscale Y10 Y12 Y16
1364 if (bpp > dbpp)
1365 {
1366 unsigned short * src = (unsigned short *)buffer;
1367 unsigned char * dest = buffer;
1368 unsigned char shift = 0;
1369
1370 if (bpp < 16)
1371 {
1372 switch (bpp)
1373 {
1374 case 10:
1375 shift = 2;
1376 break;
1377 case 12:
1378 shift = 4;
1379 break;
1380 }
1381 for (int i = 0; i < totalBytes; i++)
1382 {
1383 *dest++ = *(src++) >> shift;
1384 }
1385 }
1386 else
1387 {
1388 unsigned char * src = (unsigned char *)buffer + 1; // Y16 is little endian
1389
1390 for (int i = 0; i < totalBytes; i++)
1391 {
1392 *dest++ = *src;
1393 src += 2;
1394 }
1395 }
1396 }
1397
1398 if (PrimaryCCD.getBinX() > 1)
1399 {
1400 memcpy(PrimaryCCD.getFrameBuffer(), buffer, totalBytes);
1401 PrimaryCCD.binFrame();
1402 guard.unlock();
1403 Streamer->newFrame(PrimaryCCD.getFrameBuffer(), frameBytes / PrimaryCCD.getBinX());
1404 }
1405 else
1406 {
1407 guard.unlock();
1408 Streamer->newFrame(buffer, frameBytes);
1409 }
1410 return;
1411 }
1412
1413 if ( PrimaryCCD.isExposing() )
1414 {
1415 non_capture_frames = 0;
1416 if( !is_capturing )
1417 {
1418 LOG_DEBUG("Skip frame, setup not complete yet" );
1419 return; //skip this frame
1420 }
1421
1422 struct timeval capture_frame_dif = { .tv_sec = 0, .tv_usec = 0 };
1423 timersub(&frame_received, &capture_start, &capture_frame_dif);
1424
1425 float cfd = (float) capture_frame_dif.tv_sec + (float) capture_frame_dif.tv_usec / 1000000.0f;
1426 float fd = (float) frame_duration.tv_sec + (float) frame_duration.tv_usec / 1000000.0f;
1427
1428 if( cfd < fd * 0.9 )
1429 {
1430 LOGF_DEBUG("Skip early frame cfd = %ld.%06ld seconds.", capture_frame_dif.tv_sec, capture_frame_dif.tv_usec);
1431 return;
1432 }
1433
1434 timeradd(&elapsed_exposure, &frame_duration, &elapsed_exposure);
1435
1436 LOGF_DEBUG("Frame took %ld.%06ld s, e = %ld.%06ld s, t = %ld.%06ld s., cfd = %ld.%06ld s.",
1437 current_frame_duration.tv_sec, current_frame_duration.tv_usec,
1438 elapsed_exposure.tv_sec, elapsed_exposure.tv_usec,
1439 exposure_duration.tv_sec, exposure_duration.tv_usec,
1440 capture_frame_dif.tv_sec, capture_frame_dif.tv_usec
1441 );
1442
1443
1444 float remaining = getRemainingExposure();
1445 PrimaryCCD.setExposureLeft(remaining);
1446
1447 // Stack Mono frames
1448 if ((stackMode) && !(lx->isEnabled()) && !(ImageColorS[1].s == ISS_ON))
1449 {
1450 stackFrame();
1451 }
1452
1453 /* FIXME: stacking does not account for transfer time, so we'll miss the last frames probably */
1454 if ((stackMode) && !(lx->isEnabled()) && !(ImageColorS[1].s == ISS_ON) &&
1455 (timercmp(&elapsed_exposure, &exposure_duration, < )))
1456 return; // go on stacking
1457
1458 struct timeval const current_exposure = getElapsedExposure();
1459
1460 //IDLog("Copying frame.\n");
1461 if (ImageColorS[IMAGE_GRAYSCALE].s == ISS_ON)
1462 {
1463 if (!stackMode)
1464 {
1465 unsigned char * src, *dest;
1466 src = v4l_base->getY();
1467 dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1468
1469 std::unique_lock<std::mutex> guard(ccdBufferLock);
1470 memcpy(dest, src, frameBytes);
1471 guard.unlock();
1472 //for (i=0; i< frameBytes; i++)
1473 //*(dest++) = *(src++);
1474
1475 PrimaryCCD.binFrame();
1476 }
1477 else
1478 {
1479 float * src = V4LFrame->stackedFrame;
1480
1481 /* If we have a dark frame configured, substract it from the stack */
1482 if ((stackMode != STACK_TAKE_DARK) && (V4LFrame->darkFrame != nullptr))
1483 {
1484 float * dark = V4LFrame->darkFrame;
1485
1486 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1487 {
1488 if (*src > *dark)
1489 *src -= *dark;
1490 else
1491 *src = 0.0;
1492 src++;
1493 dark++;
1494 }
1495 src = V4LFrame->stackedFrame;
1496 }
1497
1498 //IDLog("Copying stack frame from %p to %p.\n", src, dest);
1499 if (stackMode == STACK_MEAN)
1500 {
1501 if (ImageDepthS[0].s == ISS_ON)
1502 {
1503 // depth 8 bits
1504 unsigned char * dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1505
1506 std::unique_lock<std::mutex> guard(ccdBufferLock);
1507 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1508 *dest++ = (unsigned char)((*src++ * 255.0f) / subframeCount);
1509 guard.unlock();
1510 }
1511 else
1512 {
1513 // depth 16 bits
1514 unsigned short * dest = (unsigned short *)PrimaryCCD.getFrameBuffer();
1515
1516 std::unique_lock<std::mutex> guard(ccdBufferLock);
1517 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1518 *dest++ = (unsigned short)((*src++ * 65535.0f) / subframeCount);
1519 guard.unlock();
1520 }
1521
1522 free(V4LFrame->stackedFrame);
1523 V4LFrame->stackedFrame = nullptr;
1524 }
1525 else if (stackMode == STACK_ADDITIVE)
1526 {
1527 /* Clamp additive stacking to frame dynamic range - that is, do not consider normalized source greater than 1.0f */
1528 if (ImageDepthS[0].s == ISS_ON)
1529 {
1530 // depth 8 bits
1531 unsigned char * dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1532
1533 std::unique_lock<std::mutex> guard(ccdBufferLock);
1534 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1535 {
1536 *dest++ = *src < 1.0f ? (unsigned char)((*src * 255)) : 255;
1537 src++;
1538 }
1539 guard.unlock();
1540 }
1541 else
1542 {
1543 // depth 16 bits
1544 unsigned short * dest = (unsigned short *)PrimaryCCD.getFrameBuffer();
1545
1546 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1547 {
1548 *dest++ = *src < 1.0f ? (unsigned short)((*src * 65535)) : 65535;
1549 src++;
1550 }
1551 }
1552
1553 free(V4LFrame->stackedFrame);
1554 V4LFrame->stackedFrame = nullptr;
1555 }
1556 else if (stackMode == STACK_TAKE_DARK)
1557 {
1558 if (V4LFrame->darkFrame != nullptr)
1559 free(V4LFrame->darkFrame);
1560 V4LFrame->darkFrame = V4LFrame->stackedFrame;
1561 V4LFrame->stackedFrame = nullptr;
1562 src = V4LFrame->darkFrame;
1563 if (ImageDepthS[0].s == ISS_ON)
1564 {
1565 // depth 8 bits
1566 unsigned char * dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1567
1568 std::unique_lock<std::mutex> guard(ccdBufferLock);
1569 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1570 *dest++ = (unsigned char)((*src++ * 255));
1571 guard.unlock();
1572 }
1573 else
1574 {
1575 // depth 16 bits
1576 unsigned short * dest = (unsigned short *)PrimaryCCD.getFrameBuffer();
1577
1578 std::unique_lock<std::mutex> guard(ccdBufferLock);
1579 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1580 *dest++ = (unsigned short)((*src++ * 65535));
1581 guard.unlock();
1582 }
1583 }
1584 }
1585 }
1586 else
1587 {
1588 // Binning not supported in color images for now
1589 std::unique_lock<std::mutex> guard(ccdBufferLock);
1590 unsigned char * src = v4l_base->getRGBBuffer();
1591 unsigned char * dest = PrimaryCCD.getFrameBuffer();
1592 // We have RGB RGB RGB data but for FITS file we need each color in separate plane. i.e. RRR GGG BBB ..etc
1593 unsigned char * red = dest;
1594 unsigned char * green = dest + v4l_base->getWidth() * v4l_base->getHeight() * (v4l_base->getBpp() / 8);
1595 unsigned char * blue = dest + v4l_base->getWidth() * v4l_base->getHeight() * (v4l_base->getBpp() / 8) * 2;
1596
1597 for (int i = 0; i < (int)frameBytes; i += 3)
1598 {
1599 *(red++) = *(src + i);
1600 *(green++) = *(src + i + 1);
1601 *(blue++) = *(src + i + 2);
1602 }
1603 guard.unlock();
1604
1605 }
1606 frameCount += 1;
1607
1608 if (lx->isEnabled())
1609 {
1610 //if (!is_streaming && !is_recording)
1611 if (Streamer->isBusy() == false)
1612 stop_capturing();
1613
1614 LOGF_INFO("Capture of LX frame took %ld.%06ld seconds.", current_exposure.tv_sec, current_exposure.tv_usec);
1615 ExposureComplete(&PrimaryCCD);
1616 }
1617 else
1618 {
1619 //if (!is_streaming && !is_recording) stop_capturing();
1620 if (Streamer->isBusy() == false)
1621 {
1622 //just mark stop
1623 is_capturing = false;
1624 }
1625 else
1626 IDLog("%s: streamer is busy, continue capturing\n", __FUNCTION__);
1627
1628 LOGF_INFO("Capture of one frame (%d stacked frames) took %ld.%06ld seconds.",
1629 subframeCount, current_exposure.tv_sec, current_exposure.tv_usec);
1630 ExposureComplete(&PrimaryCCD);
1631 }
1632 }
1633 else
1634 {
1635 non_capture_frames++;
1636
1637 if( non_capture_frames > 10 )
1638 {
1639 /* If we arrive here, PrimaryCCD is not exposing anymore, we can't forward the frame and we can't be aborted neither, thus abort the exposure right now.
1640 * That issue can be reproduced when clicking the "Set" button on the "Main Control" tab while an exposure is running.
1641 * Note that the patch in StartExposure returning busy instead of error prevents the flow from coming here, so now it's only a safeguard. */
1642 IDLog("%s: frame received while not exposing, force-aborting capture\n", __FUNCTION__);
1643 AbortExposure();
1644 }
1645 }
1646 }
1647
AbortExposure()1648 bool V4L2_Driver::AbortExposure()
1649 {
1650 if (lx->isEnabled())
1651 {
1652 lx->stopLx();
1653 return true;
1654 }
1655 else if (!Streamer->isBusy())
1656 {
1657 if (-1 != stdtimer)
1658 IERmTimer(stdtimer);
1659 return stop_capturing();
1660 }
1661
1662 LOG_WARN("Cannot abort exposure while video streamer is busy, stop streaming first");
1663 return false;
1664 }
1665
Connect()1666 bool V4L2_Driver::Connect()
1667 {
1668 char errmsg[ERRMSGSIZ];
1669 if (!isConnected())
1670 {
1671 if (v4l_base->connectCam(PortT[0].text, errmsg) < 0)
1672 {
1673 LOGF_ERROR("Error: unable to open device. %s", errmsg);
1674 return false;
1675 }
1676
1677 /* Sucess! */
1678 LOG_INFO("V4L2 CCD Device is online. Initializing properties.");
1679
1680 v4l_base->registerCallback(newFrame, this);
1681
1682 lx->setCamerafd(v4l_base->fd);
1683
1684 if (!(strcmp((const char *)v4l_base->cap.driver, "pwc")))
1685 LOG_INFO(
1686 "To use LED Long exposure mode with recent kernels, see https://code.google.com/p/pwc-lxled/");
1687 }
1688
1689 return true;
1690 }
1691
Disconnect()1692 bool V4L2_Driver::Disconnect()
1693 {
1694 if (isConnected())
1695 {
1696 v4l_base->disconnectCam(PrimaryCCD.isExposing() || Streamer->isBusy());
1697 if (PrimaryCCD.isExposing() || Streamer->isBusy())
1698 Streamer->close();
1699 }
1700 return true;
1701 }
1702
getDefaultName()1703 const char * V4L2_Driver::getDefaultName()
1704 {
1705 return (const char *)"V4L2 CCD";
1706 }
1707
1708 /* Retrieves basic data from the device upon connection.*/
getBasicData()1709 void V4L2_Driver::getBasicData()
1710 {
1711 //int xmax, ymax, xmin, ymin;
1712 unsigned int w, h;
1713 int inputindex = -1, formatindex = -1;
1714 struct v4l2_fract frate;
1715
1716 v4l_base->getinputs(&InputsSP);
1717 v4l_base->getcaptureformats(&CaptureFormatsSP);
1718 v4l_base->getcapturesizes(&CaptureSizesSP, &CaptureSizesNP);
1719 v4l_base->getframerates(&FrameRatesSP, &FrameRateNP);
1720
1721 w = v4l_base->getWidth();
1722 h = v4l_base->getHeight();
1723 V4LFrame->width = w;
1724 V4LFrame->height = h;
1725 V4LFrame->bpp = v4l_base->getBpp();
1726
1727 inputindex = IUFindOnSwitchIndex(&InputsSP);
1728 formatindex = IUFindOnSwitchIndex(&CaptureFormatsSP);
1729 frate = (v4l_base->*(v4l_base->getframerate))();
1730 if (inputindex >= 0 && formatindex >= 0)
1731 LOGF_INFO("Found initial Input \"%s\", Format \"%s\", Size %dx%d, Frame interval %d/%ds",
1732 InputsSP.sp[inputindex].name, CaptureFormatsSP.sp[formatindex].name, w, h, frate.numerator,
1733 frate.denominator);
1734 else
1735 LOGF_INFO("Found initial size %dx%d, frame interval %d/%ds", w, h, frate.numerator,
1736 frate.denominator);
1737
1738 IUSaveText(&camNameT[0], v4l_base->getDeviceName());
1739 IDSetText(&camNameTP, nullptr);
1740 #ifdef WITH_V4L2_EXPERIMENTS
1741 IUSaveText(&CaptureColorSpaceT[0], getColorSpaceName(&v4l_base->fmt));
1742 IUSaveText(&CaptureColorSpaceT[1], getYCbCrEncodingName(&v4l_base->fmt));
1743 IUSaveText(&CaptureColorSpaceT[2], getQuantizationName(&v4l_base->fmt));
1744 IDSetText(&CaptureColorSpaceTP, nullptr);
1745 #endif
1746 if (Options)
1747 free(Options);
1748 Options = nullptr;
1749 v4loptions = 0;
1750 updateV4L2Controls();
1751
1752 PrimaryCCD.setResolution(w, h);
1753 PrimaryCCD.setFrame(0, 0, w, h);
1754 PrimaryCCD.setBPP(V4LFrame->bpp);
1755 updateFrameSize();
1756 //direct_record=recorder->setpixelformat(v4l_base->fmt.fmt.pix.pixelformat);
1757 //recorder->setsize(w, h);
1758 INDI_PIXEL_FORMAT pixelFormat;
1759 uint8_t pixelDepth = 8;
1760 if (getPixelFormat(v4l_base->fmt.fmt.pix.pixelformat, pixelFormat, pixelDepth))
1761 Streamer->setPixelFormat(pixelFormat, pixelDepth);
1762
1763 Streamer->setSize(w, h);
1764 }
1765
updateV4L2Controls()1766 void V4L2_Driver::updateV4L2Controls()
1767 {
1768 unsigned int i;
1769
1770 LOG_DEBUG("Enumerating V4L2 controls...");
1771
1772 // #1 Query for INTEGER controls, and fill up the structure
1773 free(ImageAdjustNP.np);
1774 ImageAdjustNP.nnp = 0;
1775
1776 //if (v4l_base->queryINTControls(&ImageAdjustNP) > 0)
1777 //defineProperty(&ImageAdjustNP);
1778 v4l_base->enumerate_ext_ctrl();
1779 useExtCtrl = false;
1780
1781 if (v4l_base->queryExtControls(&ImageAdjustNP, &v4ladjustments, &Options, &v4loptions, getDeviceName(),
1782 IMAGE_BOOLEAN))
1783 useExtCtrl = true;
1784 else
1785 v4l_base->queryControls(&ImageAdjustNP, &v4ladjustments, &Options, &v4loptions, getDeviceName(), IMAGE_BOOLEAN);
1786
1787 if (v4ladjustments > 0)
1788 {
1789 LOGF_DEBUG("Found %d V4L2 adjustments", v4ladjustments);
1790 defineProperty(&ImageAdjustNP);
1791
1792 for (int i = 0; i < ImageAdjustNP.nnp; i++)
1793 {
1794 if (strcmp(ImageAdjustNP.np[i].label, "Exposure (Absolute)") == 0 ||
1795 strcmp(ImageAdjustNP.np[i].label, "Exposure Time, Absolute") == 0 ||
1796 strcmp(ImageAdjustNP.np[i].label, "Exposure") == 0)
1797 {
1798 AbsExposureN = ImageAdjustNP.np + i;
1799 LOGF_DEBUG("- %s (used for absolute exposure duration)", ImageAdjustNP.np[i].label);
1800 }
1801 else LOGF_DEBUG("- %s", ImageAdjustNP.np[i].label);
1802 }
1803 }
1804 LOGF_DEBUG("Found %d V4L2 options", v4loptions);
1805 for (i = 0; i < v4loptions; i++)
1806 {
1807 defineProperty(&Options[i]);
1808
1809 if (strcmp(Options[i].label, "Exposure, Auto") == 0 || strcmp(Options[i].label, "Auto Exposure") == 0)
1810 {
1811 ManualExposureSP = Options + i;
1812 LOGF_DEBUG("- %s (used for manual/auto exposure control)", Options[i].label);
1813 }
1814 else LOGF_DEBUG("- %s", Options[i].label);
1815 }
1816
1817 if(!AbsExposureN)
1818 {
1819 DEBUGF(INDI::Logger::DBG_WARNING, "Absolute exposure duration control is not possible on the device!", "");
1820 }
1821
1822 if(!ManualExposureSP)
1823 DEBUGF(INDI::Logger::DBG_WARNING, "Manual/auto exposure control is not possible on the device!", "");
1824
1825 //v4l_base->enumerate_ctrl();
1826 }
1827
allocateBuffers()1828 void V4L2_Driver::allocateBuffers()
1829 {
1830 V4LFrame = new img_t;
1831
1832 if (V4LFrame == nullptr)
1833 {
1834 LOG_ERROR("Critical Error: Unable to initialize driver. Low memory.");
1835 exit(-1);
1836 }
1837
1838 V4LFrame->Y = nullptr;
1839 V4LFrame->U = nullptr;
1840 V4LFrame->V = nullptr;
1841 V4LFrame->RGB24Buffer = nullptr;
1842 V4LFrame->stackedFrame = nullptr;
1843 V4LFrame->darkFrame = nullptr;
1844 }
1845
releaseBuffers()1846 void V4L2_Driver::releaseBuffers()
1847 {
1848 delete (V4LFrame);
1849 }
1850
StartStreaming()1851 bool V4L2_Driver::StartStreaming()
1852 {
1853 if (PrimaryCCD.getBinX() > 1 && PrimaryCCD.getNAxis() > 2)
1854 {
1855 LOG_WARN("Cannot stream binned color frame.");
1856 return false;
1857 }
1858
1859 /* Callee will take care of checking states */
1860 return start_capturing(true);
1861 }
1862
StopStreaming()1863 bool V4L2_Driver::StopStreaming()
1864 {
1865 if (!Streamer->isBusy() /*&& is_capturing*/)
1866 {
1867 /* Strange situation indeed, but it's theoretically possible to try to stop streaming while exposing - safeguard actually */
1868 LOGF_WARN("Cannot stop streaming, exposure running (%.1f seconds remaining)",
1869 getRemainingExposure());
1870 return false;
1871 }
1872
1873 return stop_capturing();
1874 }
1875
saveConfigItems(FILE * fp)1876 bool V4L2_Driver::saveConfigItems(FILE * fp)
1877 {
1878 INDI::CCD::saveConfigItems(fp);
1879
1880 IUSaveConfigText(fp, &PortTP);
1881
1882 if (ImageAdjustNP.nnp > 0)
1883 IUSaveConfigNumber(fp, &ImageAdjustNP);
1884
1885 return Streamer->saveConfigItems(fp);
1886 }
1887
getPixelFormat(uint32_t v4l2format,INDI_PIXEL_FORMAT & pixelFormat,uint8_t & pixelDepth)1888 bool V4L2_Driver::getPixelFormat(uint32_t v4l2format, INDI_PIXEL_FORMAT &pixelFormat, uint8_t &pixelDepth)
1889 {
1890 //IDLog("recorder: setpixelformat %d\n", format);
1891 pixelDepth = 8;
1892 switch (v4l2format)
1893 {
1894 case V4L2_PIX_FMT_GREY:
1895 #ifdef V4L2_PIX_FMT_Y10
1896 case V4L2_PIX_FMT_Y10:
1897 #endif
1898 #ifdef V4L2_PIX_FMT_Y12
1899 case V4L2_PIX_FMT_Y12:
1900 #endif
1901 #ifdef V4L2_PIX_FMT_Y16
1902 case V4L2_PIX_FMT_Y16:
1903 #endif
1904 pixelFormat = INDI_MONO;
1905 #ifdef V4L2_PIX_FMT_Y10
1906 if (v4l2format == V4L2_PIX_FMT_Y10)
1907 pixelDepth = 10;
1908 #endif
1909 #ifdef V4L2_PIX_FMT_Y12
1910 if (v4l2format == V4L2_PIX_FMT_Y12)
1911 pixelDepth = 12;
1912 #endif
1913 #ifdef V4L2_PIX_FMT_Y16
1914 if (v4l2format == V4L2_PIX_FMT_Y16)
1915 pixelDepth = 16;
1916 #endif
1917 return true;
1918 case V4L2_PIX_FMT_SBGGR8:
1919 #ifdef V4L2_PIX_FMT_SBGGR10
1920 case V4L2_PIX_FMT_SBGGR10:
1921 #endif
1922 #ifdef V4L2_PIX_FMT_SBGGR12
1923 case V4L2_PIX_FMT_SBGGR12:
1924 #endif
1925 case V4L2_PIX_FMT_SBGGR16:
1926 pixelFormat = INDI_BAYER_BGGR;
1927 #ifdef V4L2_PIX_FMT_SBGGR10
1928 if (v4l2format == V4L2_PIX_FMT_SBGGR10)
1929 pixelDepth = 10;
1930 #endif
1931 #ifdef V4L2_PIX_FMT_SBGGR12
1932 if (v4l2format == V4L2_PIX_FMT_SBGGR12)
1933 pixelDepth = 12;
1934 #endif
1935 if (v4l2format == V4L2_PIX_FMT_SBGGR16)
1936 pixelDepth = 16;
1937 return true;
1938 case V4L2_PIX_FMT_SGBRG8:
1939 #ifdef V4L2_PIX_FMT_SGBRG10
1940 case V4L2_PIX_FMT_SGBRG10:
1941 #endif
1942 #ifdef V4L2_PIX_FMT_SGBRG12
1943 case V4L2_PIX_FMT_SGBRG12:
1944 #endif
1945 pixelFormat = INDI_BAYER_GBRG;
1946 #ifdef V4L2_PIX_FMT_SGBRG10
1947 if (v4l2format == V4L2_PIX_FMT_SGBRG10)
1948 pixelDepth = 10;
1949 #endif
1950 #ifdef V4L2_PIX_FMT_SGBRG12
1951 if (v4l2format == V4L2_PIX_FMT_SGBRG12)
1952 pixelDepth = 12;
1953 #endif
1954 return true;
1955 #if defined(V4L2_PIX_FMT_SGRBG8) || defined(V4L2_PIX_FMT_SGRBG10) || defined(V4L2_PIX_FMT_SGRBG12)
1956 #ifdef V4L2_PIX_FMT_SGRBG8
1957 case V4L2_PIX_FMT_SGRBG8:
1958 #endif
1959 #ifdef V4L2_PIX_FMT_SGRBG10
1960 case V4L2_PIX_FMT_SGRBG10:
1961 #endif
1962 #ifdef V4L2_PIX_FMT_SGRBG12
1963 case V4L2_PIX_FMT_SGRBG12:
1964 #endif
1965 pixelFormat = INDI_BAYER_GRBG;
1966 #ifdef V4L2_PIX_FMT_SGRBG10
1967 if (v4l2format == V4L2_PIX_FMT_SGRBG10)
1968 pixelDepth = 10;
1969
1970 #endif
1971 #ifdef V4L2_PIX_FMT_SGRBG12
1972 if (v4l2format == V4L2_PIX_FMT_SGRBG12)
1973 pixelDepth = 12;
1974 #endif
1975 return true;
1976 #endif
1977 #if defined(V4L2_PIX_FMT_SRGGB8) || defined(V4L2_PIX_FMT_SRGGB10) || defined(V4L2_PIX_FMT_SRGGB12)
1978 #ifdef V4L2_PIX_FMT_SRGGB8
1979 case V4L2_PIX_FMT_SRGGB8:
1980 #endif
1981 #ifdef V4L2_PIX_FMT_SRGGB10
1982 case V4L2_PIX_FMT_SRGGB10:
1983 #endif
1984 #ifdef V4L2_PIX_FMT_SRGGB12
1985 case V4L2_PIX_FMT_SRGGB12:
1986 #endif
1987 pixelFormat = INDI_BAYER_RGGB;
1988 #ifdef V4L2_PIX_FMT_SRGGB10
1989 if (v4l2format == V4L2_PIX_FMT_SRGGB10)
1990 pixelDepth = 10;
1991 #endif
1992 #ifdef V4L2_PIX_FMT_SRGGB12
1993 if (v4l2format == V4L2_PIX_FMT_SRGGB12)
1994 pixelDepth = 12;
1995 #endif
1996 return true;
1997 #endif
1998 case V4L2_PIX_FMT_RGB24:
1999 pixelFormat = INDI_RGB;
2000 return true;
2001 case V4L2_PIX_FMT_BGR24:
2002 pixelFormat = INDI_BGR;
2003 return true;
2004 default:
2005 return false;
2006 }
2007 }
2008
2009
2010