Instrument Neutral Distributed Interface INDI 2.2.5
v4l2driver.cpp
Go to the documentation of this file.
1#if 0
2V4L INDI Driver
3INDI Interface for V4L devices
4Copyright (C) 2003 - 2013 Jasem Mutlaq ([email protected])
5
6 This library is free software;
7you can redistribute it and / or
8modify it under the terms of the GNU Lesser General Public
9License as published by the Free Software Foundation;
10either
11version 2.1 of the License, or (at your option) any later version.
12
13This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY;
15without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public
20License along with this library;
21if not, write to the Free Software
22Foundation, 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"
30#include "lx/Lx.h"
31
32static const PixelSizeInfo CameraDatabase[] =
33{
34 { "NexImage 5", "NexImage 5", nullptr, 2.2f, -1, 0, 0, true },
35 { "Logitech Webcam Pro 9000", "UVC Camera (046d:0809)", "Logitech Webcam Pro 9000", 3.3f, -1, 0, 0, true },
36 { "SVBONY SV105", "SVBONY SV105: SVBONY SV105", "SVBONY SV105", 3.0f, -1, 0, 0, true },
37 { "SVBONY SV205", "SVBONY SV205: SVBONY SV205", "SVBONY SV205", 4.0f, -1, 0, 0, true },
38 { "NexImage 10", "NexImage 10", nullptr, 1.67f, -1, 0, 0, true },
39 { "NexImage Burst Color", "NexImage Burst Color", nullptr, 3.75f, -1, 0, 0, false },
40 { "NexImage Burst Mono", "NexImage Burst Mono", nullptr, 3.75f, -1, 0, 0, false },
41 { "Skyris 132C", "Skyris 132C", nullptr, 3.75f, -1, 0, 0, false },
42 { "Skyris 132M", "Skyris 132M", nullptr, 3.75f, -1, 0, 0, false },
43 { "Skyris 236C", "Skyris 236C", nullptr, 2.8f, -1, 0, 0, false },
44 { "Skyris 236M", "Skyris 236M", nullptr, 2.8f, -1, 0, 0, false },
45 { "iOptron iPolar", "iOptron iPolar: iOptron iPolar", "iOptron iPolar", 3.75f, -1, 1280, 960, true },
46 { "iOptron iPolar", "iOptron iPolar", "iOptron iPolar", 3.75f, -1, 1280, 960, true },
47 { "iOptron iPolar", "iOptron iPolar 1.1: iOptron iPo", "iOptron iPolar", 3.75f, -1, 1280, 960, true },
48 { "iOptron iGuider", "iOptron iGuider: iOptron iGuide", "iOptron iGuider", 3.75f, -1, 1280, 960, true },
49 { "iOptron iGuider", "iOptron iGuider 1", "iOptron iGuider", 3.75f, -1, 1280, 960, true },
50 { "iOptron iGuider", "iOptron iGuider External: iOptr", "iOptron iGuider", 3.75f, -1, 1280, 960, true },
51 { "Raspberry Pi High Quality Camera", "mmal service 16.1", "Raspberry Pi High Quality Camera", 1.55f, -1, 0, 0, true },
52 { "Logitech HD C270", "UVC Camera (046d:0825)", "Logitech HD C270", 2.8f, -1, 0, 0, true },
53 { "IMX290 Camera", "USB 2.0 Camera: USB Camera", "USB 2.0 IMX290 Board", 2.9f, -1, 0, 0, true },
54 { "IMX290 H264 Camera", "0c45:6366 Microdia", "Spinel 2MP Full HD Low Light WDR H264 USB Camera Module IMX290", 2.9f, -1, 0, 0, true },
55 { "Microsoft LifeCam Cinema", "Microsoft® LifeCam Cinema(TM):", "Microsoft® LifeCam Cinema(TM)", 3.0f, -1, 0, 0, false },
56 { "OpenAstroGuider", "OpenAstroGuider IMX290", nullptr, 2.9f, -1, 0, 0, false },
57 { nullptr, nullptr, nullptr, 5.6f, -1, 0, 0, false} // sentinel and default pixel size, needs to be last
58};
59
60static const double IOptronDurations[] = { 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, -1 };
61static const double iGuiderTicks[] = { 1, 2, 5, 10, 20, 39, 78, 156, 312, 625, 1250, 2500, -1 };
62static const double iPolarTicks[] = { 13, 26, 66, 133, 266, 666, 1333, 1999, 2666, 3332, 3999, 4665, -1 };
63
64#define MAX_IOPTRON_INDEX (sizeof(IOptronDurations) / sizeof(double) - 2)
65#define MAX_IOPTRON_DURATION (IOptronDurations[MAX_IOPTRON_INDEX])
66#define MAX_IOPTRON_TICKS (iGuiderTicks[MAX_IOPTRON_INDEX])
67
68#define IOPTRON_ABS_EXPOSURE_CTRL_ID 0x9A0902
69#define IOPTRON_AUTO_EXPOSURE_CTRL_ID 0x9A0901
70#define IOPTRON_WATCHDOG_PERIOD_IN_MS 10000
71
72
73V4L2_Driver::V4L2_Driver(std::string label, std::string path)
74{
75 setDeviceName(label.c_str());
76 snprintf(defaultVideoPort, 256, "%s", path.c_str());
77 snprintf(configPort, 256, "%s", path.c_str());
78
79 setVersion(1, 2);
80
82
83 divider = 128.;
84
85 is_capturing = false;
87
88 Options = nullptr;
89 v4loptions = 0;
90 AbsExposureN = nullptr;
91 ManualExposureSP = nullptr;
92 CaptureSizesSP.sp = nullptr;
93 CaptureSizesNP.np = nullptr;
94 FrameRatesSP.sp = nullptr;
95
96 frame_received.tv_sec = 0;
97 frame_received.tv_usec = 0;
98
99 v4l_capture_started = false;
100
102
103 lx = new Lx();
104 lxtimer = -1;
105 stdtimer = -1;
106
109}
110
112{
113 setVersion(1, 0);
114
116
117 divider = 128.;
118
119 is_capturing = false;
121
122 Options = nullptr;
123 v4loptions = 0;
124 AbsExposureN = nullptr;
125 ManualExposureSP = nullptr;
126 CaptureSizesSP.sp = nullptr;
127 CaptureSizesNP.np = nullptr;
128 FrameRatesSP.sp = nullptr;
129
130 frame_received.tv_sec = 0;
131 frame_received.tv_usec = 0;
132
133 v4l_capture_started = false;
134
136
137 lx = new Lx();
138 lxtimer = -1;
139 stdtimer = -1;
142}
143
148
150{
153 else
155 (PrimaryCCD.getBPP() / 8 + (PrimaryCCD.getBPP() % 8 ? 1 : 0)) * 3;
156
158 LOGF_DEBUG("%s: frame bytes %d", __FUNCTION__, PrimaryCCD.getFrameBufferSize());
159}
160
162{
165
166 /* Port */
167 // Only load config port if it was empty. If it was already initialized, then we have an explicitly defined device
168 // with defaultVideoPort and we shouldn't mess this up.
169 if (configPort[0] == 0 && IUGetConfigText(getDeviceName(), PortTP.name, PortT[0].name, configPort, 256) == 0)
170 IUFillText(&PortT[0], "PORT", "Port", configPort);
171 else
172 IUFillText(&PortT[0], "PORT", "Port", defaultVideoPort);
174 IPS_IDLE);
175
176
177 // Capture format.
178 CaptureFormat mono = {"INDI_MONO", "Mono", 8};
179 CaptureFormat color = {"INDI_RGB", "RGB", 8, true};
180 addCaptureFormat(mono);
181 addCaptureFormat(color);
182 if (CaptureFormatSP[IMAGE_RGB].getState() == ISS_ON)
183 {
186 }
187
188 /* Image depth */
189 IUFillSwitch(&ImageDepthS[0], "8 bits", "", ISS_ON);
190 IUFillSwitch(&ImageDepthS[1], "16 bits", "", ISS_OFF);
193
194 /* Camera Name */
195 IUFillText(&camNameT[0], "Model", "", nullptr);
197 IPS_IDLE);
198
199 /* Stacking Mode */
200 StackModeSP[STACK_NONE].fill("None", "", ISS_ON);
201 StackModeSP[STACK_MEAN].fill("Mean", "", ISS_OFF);
202 StackModeSP[STACK_ADDITIVE].fill("Additive", "", ISS_OFF);
203 StackModeSP[STACK_TAKE_DARK].fill("Take Dark", "", ISS_OFF);
204 StackModeSP[STACK_RESET_DARK].fill("Reset Dark", "", ISS_OFF);
207
209
210 /* Inputs */
211 IUFillSwitchVector(&InputsSP, nullptr, 0, getDeviceName(), "V4L2_INPUT", "Inputs", CAPTURE_FORMAT, IP_RW,
213 /* Capture Formats */
214 IUFillSwitchVector(&CaptureFormatsSP, nullptr, 0, getDeviceName(), "V4L2_FORMAT", "Capture Format", CAPTURE_FORMAT,
216 /* Capture Sizes */
217 IUFillSwitchVector(&CaptureSizesSP, nullptr, 0, getDeviceName(), "V4L2_SIZE_DISCRETE", "Capture Size",
219 IUFillNumberVector(&CaptureSizesNP, nullptr, 0, getDeviceName(), "V4L2_SIZE_STEP", "Capture Size", CAPTURE_FORMAT,
220 IP_RW, 0, IPS_IDLE);
221 /* Frame Rate */
222 IUFillSwitchVector(&FrameRatesSP, nullptr, 0, getDeviceName(), "V4L2_FRAMEINT_DISCRETE", "Frame Interval",
224 IUFillNumberVector(&FrameRateNP, nullptr, 0, getDeviceName(), "V4L2_FRAMEINT_STEP", "Frame Interval",
226 /* Capture Colorspace */
227 IUFillText(&CaptureColorSpaceT[0], "Name", "", nullptr);
228 IUFillText(&CaptureColorSpaceT[1], "YCbCr Encoding", "", nullptr);
229 IUFillText(&CaptureColorSpaceT[2], "Quantization", "", nullptr);
231 "V4L2_COLORSPACE", "ColorSpace", IMAGE_INFO_TAB, IP_RO, 0, IPS_IDLE);
232
233 /* Color Processing */
234 IUFillSwitch(&ColorProcessingS[0], "Quantization", "", ISS_ON);
235 IUFillSwitch(&ColorProcessingS[1], "Color Conversion", "", ISS_OFF);
236 IUFillSwitch(&ColorProcessingS[2], "Linearization", "", ISS_OFF);
238 "V4L2_COLOR_PROCESSING", "Color Process", CAPTURE_FORMAT, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
239
240 /* V4L2 Settings */
241 IUFillNumberVector(&ImageAdjustNP, nullptr, 0, getDeviceName(), "Image Adjustments", "", IMAGE_GROUP, IP_RW, 60,
242 IPS_IDLE);
243
245
246 PrimaryCCD.setMinMaxStep("CCD_EXPOSURE", "CCD_EXPOSURE_VALUE", 0.001, 3600, 1, false);
247
248 if (!lx->initProperties(this))
249 LOG_WARN("Can not init Long Exposure");
250
252
254 return true;
255}
256
261
262void V4L2_Driver::ISGetProperties(const char * dev)
263{
264 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
265 return;
266
268
270
271 if (isConnected())
272 {
274
277
278 if (CaptureSizesSP.sp != nullptr)
280 else if (CaptureSizesNP.np != nullptr)
282 if (FrameRatesSP.sp != nullptr)
284 else if (FrameRateNP.np != nullptr)
286
288
290
291#ifdef WITH_V4L2_EXPERIMENTS
295#endif
296
297 }
298}
299
301{
303
304 if (isConnected())
305 {
307 getBasicData();
308
311
312 if (CaptureSizesSP.sp != nullptr)
314 else if (CaptureSizesNP.np != nullptr)
316 if (FrameRatesSP.sp != nullptr)
318 else if (FrameRateNP.np != nullptr)
320
322
323#ifdef WITH_V4L2_EXPERIMENTS
327#endif
328
329 if (m_Info)
330 {
331 const char * commonName = m_Info->commonName;
332 float pixX = m_Info->pixelSizeX;
333 float pixY = m_Info->pixelSizeY;
334
335 if (!commonName)
336 commonName = m_Info->deviceName;
337 if (pixY < 0)
338 pixY = pixX;
339
340 if (m_Info->deviceName)
341 {
342 LOGF_INFO("Setting pixel size correctly for %s", commonName);
343 if (m_Info->tested == false)
344 {
345 LOGF_INFO("Please report that the camera worked: Name: %s/%s Detected and working, to https://bit.ly/2S1Vxjq",
346 v4l_base->getDeviceName(), commonName);
347 }
348 }
349 else
350 {
351 LOGF_INFO("Setting pixel size to default of %5.2f", pixX);
352 LOGF_INFO("For future autodetection of pixel size, please report the following: Reported Name: %s, "
353 "Common Name (Eg: NexImage 10), Pixel Size to the following thread: https://bit.ly/2S1Vxjq",
355 }
357 }
358
362 return true;
363 }
364 else
365 {
366 unsigned int i;
367
370
372
375
376 if (CaptureSizesSP.sp != nullptr)
378 else if (CaptureSizesNP.np != nullptr)
380 if (FrameRatesSP.sp != nullptr)
382 else if (FrameRateNP.np != nullptr)
384
386 for (i = 0; i < v4loptions; i++)
387 deleteProperty(Options[i].name);
388 if (Options)
389 free(Options);
390 Options = nullptr;
391 v4loptions = 0;
392
394
395#ifdef WITH_V4L2_EXPERIMENTS
399#endif
400
401 return true;
402 }
403}
404
405bool V4L2_Driver::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
406{
407 char errmsg[ERRMSGSIZ];
408 unsigned int iopt;
409
410 /* ignore if not ours */
411 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
412 return true;
413
414 /* Input */
415 if (strcmp(name, InputsSP.name) == 0)
416 {
417 //if ((StreamSP.s == IPS_BUSY) || (ExposeTimeNP->s == IPS_BUSY) || (RecordStreamSP.s == IPS_BUSY)) {
418 if (PrimaryCCD.isExposing() || Streamer->isBusy())
419 {
420 LOG_ERROR("Can not set input while capturing.");
422 IDSetSwitch(&InputsSP, nullptr);
423 return false;
424 }
425 else
426 {
427 unsigned int inputindex, oldindex;
428 oldindex = IUFindOnSwitchIndex(&InputsSP);
430 IUUpdateSwitch(&InputsSP, states, names, n);
431 inputindex = IUFindOnSwitchIndex(&InputsSP);
432
433 if (v4l_base->setinput(inputindex, errmsg) == -1)
434 {
435 LOGF_INFO("ERROR (setinput): %s", errmsg);
437 InputsSP.sp[oldindex].s = ISS_ON;
439 IDSetSwitch(&InputsSP, nullptr);
440 return false;
441 }
442
446 if (CaptureSizesSP.sp != nullptr)
448 else if (CaptureSizesNP.np != nullptr)
450
452
453 if (CaptureSizesSP.sp != nullptr)
455 else if (CaptureSizesNP.np != nullptr)
457 InputsSP.s = IPS_OK;
458 IDSetSwitch(&InputsSP, nullptr);
459 LOGF_INFO("Capture input: %d. %s", inputindex, InputsSP.sp[inputindex].name);
460 return true;
461 }
462 }
463
464 /* Encoder Format */
465 if (EncodeFormatSP.isNameMatch(name))
466 {
467 auto format = IUFindOnSwitchName(states, names, n);
468 v4l_base->setNative(strcmp(format, EncodeFormatSP[FORMAT_NATIVE].getName()) == 0);
469 // Let parent handle the rest
470 return INDI::CCD::ISNewSwitch(dev, name, states, names, n);
471 }
472
473 /* Capture Format */
474 if (strcmp(name, CaptureFormatsSP.name) == 0)
475 {
476 //if ((StreamSP.s == IPS_BUSY) || (ExposeTimeNP->s == IPS_BUSY) || (RecordStreamSP.s == IPS_BUSY)) {
477 if (PrimaryCCD.isExposing() || Streamer->isBusy())
478 {
479 LOG_ERROR("Can not set format while capturing.");
481 IDSetSwitch(&CaptureFormatsSP, nullptr);
482 return false;
483 }
484 else
485 {
486 int index, oldindex;
489 IUUpdateSwitch(&CaptureFormatsSP, states, names, n);
491
492 if (index < 0 || v4l_base->setcaptureformat(*((unsigned int *)CaptureFormatsSP.sp[index].aux), errmsg) == -1)
493 {
494 LOGF_INFO("ERROR (setformat): %s", errmsg);
496 CaptureFormatsSP.sp[oldindex].s = ISS_ON;
498 IDSetSwitch(&CaptureFormatsSP, nullptr);
499 return false;
500 }
501
504
505 if (CaptureSizesSP.sp != nullptr)
507 else if (CaptureSizesNP.np != nullptr)
510
511 if (CaptureSizesSP.sp != nullptr)
513 else if (CaptureSizesNP.np != nullptr)
516
517#ifdef WITH_V4L2_EXPERIMENTS
522#endif
523 //direct_record=recorder->setpixelformat(v4l_base->fmt.fmt.pix.pixelformat);
524 INDI_PIXEL_FORMAT pixelFormat;
525 uint8_t pixelDepth = 8;
526 if (getPixelFormat(v4l_base->fmt.fmt.pix.pixelformat, pixelFormat, pixelDepth))
527 Streamer->setPixelFormat(pixelFormat, pixelDepth);
528
530 IDSetSwitch(&CaptureFormatsSP, "Capture format: %d. %s", index, CaptureFormatsSP.sp[index].name);
531 return true;
532 }
533 }
534
535 /* Capture Size (Discrete) */
536 if (strcmp(name, CaptureSizesSP.name) == 0)
537 {
538 //if ((StreamSP.s == IPS_BUSY) || (ExposeTimeNP->s == IPS_BUSY) || (RecordStreamSP.s == IPS_BUSY)) {
539 if (PrimaryCCD.isExposing() || Streamer->isBusy())
540 {
541 LOG_ERROR("Can not set capture size while capturing.");
543 IDSetSwitch(&CaptureSizesSP, nullptr);
544 return false;
545 }
546 else
547 {
548 int index{0}, w {0}, h {0};
549 IUUpdateSwitch(&CaptureSizesSP, states, names, n);
551
552 if (index >= 0)
553 sscanf(CaptureSizesSP.sp[index].name, "%dx%d", &w, &h);
554
555 if (updateCaptureSize(w, h) == false)
556 {
558 IDSetSwitch(&CaptureSizesSP, nullptr);
559 return false;
560 }
561 else
562 {
564 IDSetSwitch(&CaptureSizesSP, "Capture size (discrete): %d. %s", index, CaptureSizesSP.sp[index].name);
566 return true;
567 }
568 }
569 }
570
571 /* Frame Rate (Discrete) */
572 if (strcmp(name, FrameRatesSP.name) == 0)
573 {
574 if (PrimaryCCD.isExposing() || Streamer->isBusy())
575 {
576 LOG_ERROR("Can not change frame rate while capturing.");
578 IDSetSwitch(&FrameRatesSP, nullptr);
579 return false;
580 }
581 int index {0};
582 struct v4l2_fract frate;
583 IUUpdateSwitch(&FrameRatesSP, states, names, n);
585 if (index >= 0)
586 sscanf(FrameRatesSP.sp[index].name, "%d/%d", &frate.numerator, &frate.denominator);
587 if (index < 0 || (v4l_base->*(v4l_base->setframerate))(frate, errmsg) == -1)
588 {
589 LOGF_INFO("ERROR (setframerate): %s", errmsg);
591 IDSetSwitch(&FrameRatesSP, nullptr);
592 return false;
593 }
594
596 IDSetSwitch(&FrameRatesSP, "Frame Period (discrete): %d. %s", index, FrameRatesSP.sp[index].name);
597 return true;
598 }
599
600 /* Image Depth */
601 if (strcmp(name, ImageDepthSP.name) == 0)
602 {
603 if (Streamer->isRecording())
604 {
605 LOG_WARN("Can not set Image depth (8/16bits) while recording.");
606 return false;
607 }
608
610 IUUpdateSwitch(&ImageDepthSP, states, names, n);
612 if (ImageDepthS[0].s == ISS_ON)
613 {
615 }
616 else
617 {
618 PrimaryCCD.setBPP(16);
619 }
620 IDSetSwitch(&ImageDepthSP, nullptr);
621 return true;
622 }
623
624 /* Stacking Mode */
625 if (StackModeSP.isNameMatch(name))
626 {
627 StackModeSP.update(states, names, n);
631 {
632 if (V4LFrame->darkFrame != nullptr)
633 {
634 free(V4LFrame->darkFrame);
635 V4LFrame->darkFrame = nullptr;
636 }
637 }
638
640 LOGF_INFO("Setting Stacking Mode: %s", StackModeSP[m_StackMode].getName());
642 return true;
643 }
644
645 /* V4L2 Options/Menus */
646 for (iopt = 0; iopt < v4loptions; iopt++)
647 if (strcmp(Options[iopt].name, name) == 0)
648 break;
649 if (iopt < v4loptions)
650 {
651 unsigned int ctrl_id, optindex, ctrlindex;
652
653 LOGF_DEBUG("Toggle switch %s=%s", Options[iopt].name, Options[iopt].label);
654
655 Options[iopt].s = IPS_IDLE;
656 IUResetSwitch(&Options[iopt]);
657 if (IUUpdateSwitch(&Options[iopt], states, names, n) < 0)
658 return false;
659
660 optindex = IUFindOnSwitchIndex(&Options[iopt]);
661 if (Options[iopt].sp[optindex].aux != nullptr)
662 ctrlindex = *(unsigned int *)(Options[iopt].sp[optindex].aux);
663 else
664 ctrlindex = optindex;
665 ctrl_id = (*((unsigned int *)Options[iopt].aux));
666 LOGF_DEBUG(" On switch is (%d) %s=\"%s\", ctrl_id = 0x%X ctrl_index=%d", optindex,
667 Options[iopt].sp[optindex].name, Options[iopt].sp[optindex].label, ctrl_id, ctrlindex);
668 if (v4l_base->setOPTControl(ctrl_id, ctrlindex, errmsg) < 0)
669 {
670 if (Options[iopt].nsp == 1) // button
671 {
672 Options[iopt].sp[optindex].s = ISS_OFF;
673 }
674 Options[iopt].s = IPS_ALERT;
675 IDSetSwitch(&Options[iopt], nullptr);
676 LOGF_ERROR("Unable to adjust setting. %s", errmsg);
677 return false;
678 }
679 if (Options[iopt].nsp == 1) // button
680 {
681 Options[iopt].sp[optindex].s = ISS_OFF;
682 }
683 Options[iopt].s = IPS_OK;
684 IDSetSwitch(&Options[iopt], nullptr);
685 return true;
686 }
687
688 /* ColorProcessing */
689 if (strcmp(name, ColorProcessingSP.name) == 0)
690 {
691 if (CaptureFormatSP[IMAGE_MONO].getState() == ISS_ON)
692 {
693 IUUpdateSwitch(&ColorProcessingSP, states, names, n);
695 ColorProcessingS[2].s == ISS_ON);
702 return true;
703 }
704 else
705 {
706 LOG_WARN("No color processing in color mode ");
707 return false;
708 }
709 }
710 lx->ISNewSwitch(dev, name, states, names, n);
711 return INDI::CCD::ISNewSwitch(dev, name, states, names, n);
712}
713
714bool V4L2_Driver::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
715{
716 IText * tp;
717
718 /* ignore if not ours */
719 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
720 return true;
721
722 if (strcmp(name, PortTP.name) == 0)
723 {
724 PortTP.s = IPS_OK;
725 tp = IUFindText(&PortTP, names[0]);
726 if (!tp)
727 return false;
728 IUSaveText(tp, texts[0]);
729 IDSetText(&PortTP, nullptr);
730
731 saveConfig(true, PortTP.name);
732 return true;
733 }
734
735 lx->ISNewText(dev, name, texts, names, n);
736 return INDI::CCD::ISNewText(dev, name, texts, names, n);
737}
738
739int findIndexOfNearestValue(double value, const double allowed_values[])
740{
741 int n = 1;
742 while (allowed_values[n] != -1)
743 {
744 if (value < (allowed_values[n - 1] + allowed_values[n]) / 2.0)
745 {
746 return n - 1;
747 }
748 n++;
749 }
750 return n - 1;
751}
752
753bool V4L2_Driver::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
754{
755 char errmsg[ERRMSGSIZ];
756
757 /* ignore if not ours */
758 if (dev != nullptr && strcmp(getDeviceName(), dev) != 0)
759 return true;
760
761 /* Capture Size (Step/Continuous) */
762 if (strcmp(name, CaptureSizesNP.name) == 0)
763 {
764 if (PrimaryCCD.isExposing() || Streamer->isBusy())
765 {
766 LOG_ERROR("Can not set capture size while capturing.");
768 IDSetNumber(&CaptureSizesNP, nullptr);
769 return false;
770 }
771 else
772 {
773 unsigned int sizes[2], w = 0, h = 0;
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
786 if (updateCaptureSize(sizes[0], sizes[1]) == false)
787 {
789 IDSetNumber(&CaptureSizesNP, nullptr);
790 return false;
791 }
792 else
793 {
794 IUUpdateNumber(&CaptureSizesNP, values, names, n);
796 IDSetNumber(&CaptureSizesNP, "Capture size (step/cont): %dx%d", w, h);
797 return true;
798 }
799 }
800 }
801
802 if (strcmp(ImageAdjustNP.name, name) == 0)
803 {
805
806 if (IUUpdateNumber(&ImageAdjustNP, values, names, n) < 0)
807 return false;
808
809 for (int i = 0; i < ImageAdjustNP.nnp; i++)
810 {
811 unsigned int const ctrl_id = *((unsigned int *)ImageAdjustNP.np[i].aux0);
812
813 // iOptron cameras only accept some tick values. Round the value to the nearest one.
814 if (isIOptron() && ctrl_id == IOPTRON_ABS_EXPOSURE_CTRL_ID)
815 {
816 int n = 0;
817 if (strstr(getDeviceName(), "iGuider"))
818 n = findIndexOfNearestValue(ImageAdjustNP.np[i].value, iGuiderTicks);
819 else
820 n = findIndexOfNearestValue(ImageAdjustNP.np[i].value, iPolarTicks);
821
822 ImageAdjustNP.np[i].value = iGuiderTicks[n];
823 }
824
825 const double value = ImageAdjustNP.np[i].value;
826
827 LOGF_DEBUG(" Setting %s (%s) to %f, ctrl_id = 0x%X", ImageAdjustNP.np[i].name,
828 ImageAdjustNP.np[i].label, value, ctrl_id);
829
830 if (v4l_base->setINTControl(ctrl_id, ImageAdjustNP.np[i].value, errmsg) < 0)
831 {
832 /* Some controls may become read-only depending on selected options */
833 LOGF_WARN("Unable to adjust %s (ctrl_id = 0x%X)", ImageAdjustNP.np[i].label,
834 ctrl_id);
835 }
836 /* Some controls may have been adjusted by the driver */
837 /* a read is mandatory as VIDIOC_S_CTRL is write only and does not return the actual new value */
838 v4l_base->getControl(ctrl_id, &(ImageAdjustNP.np[i].value), errmsg);
839
840 /* Warn the client if the control returned another value than what was set */
841 if(value != ImageAdjustNP.np[i].value)
842 {
843 LOGF_WARN("Control %s set to %f returned %f (ctrl_id = 0x%X)",
844 ImageAdjustNP.np[i].label, value, ImageAdjustNP.np[i].value, ctrl_id);
845 }
846 }
848 IDSetNumber(&ImageAdjustNP, nullptr);
849 return true;
850 }
851
852 // Ensure duration is set to one of the allowed durations, or, if we're going to stack, a value greater than the maximum
853 if (isIOptron() && strcmp(name, "CCD_EXPOSURE") == 0 && values[0] < MAX_IOPTRON_DURATION)
854 {
855 values[0] = IOptronDurations[findIndexOfNearestValue(values[0], IOptronDurations)];
856 }
857
858 return INDI::CCD::ISNewNumber(dev, name, values, names, n);
859}
860
861bool V4L2_Driver::updateCaptureSize(uint32_t width, uint32_t height)
862{
863 char errorMessage[MAXRBUF] = {0};
864 if (v4l_base->setcapturesize(width, height, errorMessage) == -1)
865 {
866 LOGF_ERROR("Failed to update capture size to %dx%d: %s", width, height, errorMessage);
867 return false;
868 }
869
870 if (FrameRatesSP.sp != nullptr)
872 else if (FrameRateNP.np != nullptr)
875 if (FrameRatesSP.sp != nullptr)
877 else if (FrameRateNP.np != nullptr)
879
880 PrimaryCCD.setFrame(0, 0, width, height);
881 V4LFrame->width = width;
882 V4LFrame->height = height;
883 PrimaryCCD.setResolution(width, height);
885 Streamer->setSize(width, height);
886 return true;
887}
888
889bool V4L2_Driver::StartExposure(float duration)
890{
891 /* Clicking the "Expose" set button while an exposure is running arrives here.
892 * Now that V4L2 CCD has the option to abort, this will properly abort the exposure.
893 * If CAN_ABORT is not set, we have to tell the caller we're busy until the end of this exposure.
894 * If we don't, PrimaryCCD will stop exposing nonetheless and we won't be able to restart an exposure.
895 */
896 {
897 if (Streamer->isBusy())
898 {
899 LOG_ERROR("Cannot start new exposure while streamer is busy, stop streaming first");
900 return !(GetCCDCapability() & CCD_CAN_ABORT);
901 }
902
903 if (is_capturing)
904 {
906 "Cannot start new exposure until the current one completes (%.3f seconds left).",
908 return !(GetCCDCapability() & CCD_CAN_ABORT);
909
910 return true;
911 }
912 }
913
914 if (setShutter(duration))
915 {
916 V4LFrame->expose = duration;
918
919 if (!lx->isEnabled() || lx->getLxmode() == LXSERIAL)
920 start_capturing(false);
921
922 /* Update exposure duration in client */
923 /* FIXME: exposure update timer has period hardcoded 1 second */
924 if (is_capturing && 1.0f < duration)
925 {
926 //if (-1 != stdtimer)
927 // IERmTimer(stdtimer);
928 //stdtimer = IEAddTimer(1000, (IE_TCF *)stdtimerCallback, this);
929 stdtimer = -1;
930 }
931 else
932 stdtimer = -1;
933
934 // iGuider/iPolar occasionally fails to start. We deal with this in the same was
935 // as the iOptron ASCOM driver on windows: if it hasn't responded with a frame in
936 // 10 seconds, stop and start the capture.
937 if (isIOptron())
938 {
941 }
942 }
943
944 return is_capturing;
945}
946
947bool V4L2_Driver::setShutter(double duration)
948{
949 if (lx->isEnabled())
950 {
951 LOGF_INFO("Using long exposure mode for %.3f sec frame.", duration);
952 if (startlongexposure(duration))
953 {
954 LOGF_INFO("Started %.3f-second long exposure.", duration);
955 return true;
956 }
957 else
958 {
960 "Unable to start %.3f-second long exposure, falling back to auto exposure", duration);
961 return false;
962 }
963 }
964 else if (setManualExposure(duration))
965 {
966 exposure_duration.tv_sec = (long) duration;
967 exposure_duration.tv_usec = (long) ((duration - (double) exposure_duration.tv_sec) * 1000000.0f);
968
969 elapsed_exposure.tv_sec = 0;
970 elapsed_exposure.tv_usec = 0;
971
972 gettimeofday(&capture_start, nullptr);
973
974 frameCount = 0;
975 subframeCount = 0;
976
977 // Do not spam log for short exposures.
978 if (duration >= 3)
979 LOGF_INFO("Started %.3f-second manual exposure.", duration);
980 return true;
981 }
982 else
983 {
984 LOGF_WARN("Failed %.3f-second manual exposure, no adequate control is registered.",
985 duration);
986 return false;
987 }
988}
989
991{
992 return strstr(getDeviceName(), "iGuider") || strstr(getDeviceName(), "iPolar");
993}
994
996{
997 /* N.B. Check how this differs from one camera to another. This is just a proof of concept for now */
998 /* With DMx 21AU04.AS, exposing twice with the same duration causes an incomplete frame to pop in the buffer list
999 * This can be worked around by verifying the buffer size, but it won't work for anything else than Y8/Y16, so set
1000 * exposure unconditionally */
1001 /*if (duration * 10000 != AbsExposureN->value)*/
1002
1003 // INT control for manual exposure duration is an integer in 1/10000 seconds
1004 long ticks = lround(duration * 10000.0f);
1005
1006 // iOptron tick values are non-linear, so look them up.
1007 bool ioptron_maximum_duration_exceeded = false;
1008 if (isIOptron())
1009 {
1010 if (duration <= MAX_IOPTRON_DURATION)
1011 {
1012 if (strstr(getDeviceName(), "iGuider"))
1013 ticks = iGuiderTicks[findIndexOfNearestValue(duration, IOptronDurations)];
1014 else
1015 ticks = iPolarTicks[findIndexOfNearestValue(duration, IOptronDurations)];
1016 }
1017 else
1018 {
1019 ioptron_maximum_duration_exceeded = true;
1020 }
1021 }
1022
1023 /* First check the presence of an absolute exposure control */
1024 if (nullptr == AbsExposureN)
1025 {
1026 /* We don't have an absolute exposure control but we can stack gray frames until the exposure elapses */
1028 {
1029 //use frame interval as frame duration instead of max exposure time.
1030 if(FrameRatesSP.sp != nullptr)
1031 {
1032 LOGF_WARN("Absolute exposure duration control is undefined, stacking up to %.3f seconds using %.16s.",
1033 duration, StackModeSP[m_StackMode].getName());
1034 int index = IUFindOnSwitchIndex(&FrameRatesSP);
1035 int fn, fd;
1036 sscanf(FrameRatesSP.sp[index].name, "%d/%d", &fn, &fd);
1037 IDLog("Interval = %d %d\n", fn, fd);
1038 ticks = (long)(10000.0 * (float)fn / (float)fd + 0.5);
1039 frame_duration.tv_sec = ticks / 10000;
1040 frame_duration.tv_usec = (ticks % 10000) * 100;
1041 return true;
1042 }
1043 else
1044 {
1045 //ToDo: Same thing should be done for FrameRateNP
1046 LOG_ERROR("Absolute exposure duration control is undefined and tacking is not supported");
1047 return false;
1048 }
1049 }
1050 /* We don't have an absolute exposure control and stacking is not configured, bail out */
1051 else
1052 {
1053 LOGF_ERROR("Failed exposing, the absolute exposure duration control is undefined, and stacking is not ready.", "");
1054 LOGF_ERROR("Configure grayscale and stacking in order to stack streamed frames up to %.3f seconds.", duration);
1055 return false;
1056 }
1057 }
1058 /* Then if we have an exposure control, check the requested exposure duration */
1059 else if (AbsExposureN->max < ticks || ioptron_maximum_duration_exceeded)
1060 {
1061 if( CaptureFormatSP[IMAGE_MONO].getState() == ISS_ON && m_StackMode == STACK_NONE )
1062 {
1063 LOG_WARN("Requested manual exposure is out of device bounds auto set stackMode to ADDITIVE" );
1068 IDSetSwitch(ManualExposureSP, nullptr);
1069 }
1070
1071 /* We can't expose as long as requested but we can stack gray frames until the exposure elapses */
1073 {
1074 if( AbsExposureN->value != AbsExposureN->max )
1075 {
1076 LOGF_WARN("Requested manual exposure is out of device bounds [%.3f,%.3f], stacking up to %.3f seconds using %.16s.",
1077 (double) AbsExposureN->min / 10000.0f, (double) AbsExposureN->max / 10000.0f,
1078 duration, StackModeSP[m_StackMode].getName());
1079 }
1080 if (isIOptron())
1081 {
1082 ticks = MAX_IOPTRON_TICKS;
1083 frame_duration.tv_sec = floor(MAX_IOPTRON_DURATION);
1084 frame_duration.tv_usec = (MAX_IOPTRON_DURATION - frame_duration.tv_sec) * 1000000;
1085 }
1086 else
1087 {
1088 ticks = AbsExposureN->max;
1089 frame_duration.tv_sec = ticks / 10000;
1090 frame_duration.tv_usec = (ticks % 10000) * 100;
1091 }
1092 }
1093 /* We can't expose as long as requested and stacking is not configured, bail out */
1094 else
1095 {
1096 LOGF_ERROR("Failed %.3f-second manual exposure, out of device bounds [%.3f,%.3f], and stacking is not ready.",
1097 duration, (double) AbsExposureN->min / 10000.0f, (double) AbsExposureN->max / 10000.0f);
1098 LOGF_ERROR("Configure grayscale and stacking in order to stack streamed frames up to %.3f seconds.", duration);
1099 return false;
1100 }
1101 }
1102 else
1103 {
1104 frame_duration.tv_sec = floor(duration);
1105 frame_duration.tv_usec = (duration - frame_duration.tv_sec) * 1000000;
1106 }
1107
1108 /* Lower-than-minimal exposure duration is left managed below */
1109
1110 if ( v4l_capture_started )
1111 {
1112 if( AbsExposureN->value != ticks )
1113 {
1115 }
1116 else
1117 {
1118 return true;
1119 }
1120 }
1121
1122
1123 /* At this point we do have an absolute exposure control and a valid exposure duration, so start exposing. */
1124
1125 /* Manual mode should be set before changing Exposure (Auto), if possible.
1126 * In some cases there might be no control available, so don't fail and try to continue.
1127 */
1128 if (ManualExposureSP)
1129 {
1130 if (ManualExposureSP->sp[0].s == ISS_OFF)
1131 {
1135
1136 unsigned int const ctrlindex = ManualExposureSP->sp[0].aux ? *(unsigned int *)(ManualExposureSP->sp[0].aux) : 0;
1137 unsigned int const ctrl_id = (*((unsigned int *)ManualExposureSP->aux));
1138
1139 char errmsg[MAXRBUF];
1140 if (v4l_base->setOPTControl(ctrl_id, ctrlindex, errmsg) < 0)
1141 {
1145 IDSetSwitch(ManualExposureSP, nullptr);
1146
1147 LOGF_ERROR("Unable to adjust manual/auto exposure control. %s", errmsg);
1148 return false;
1149 }
1150
1152 IDSetSwitch(ManualExposureSP, nullptr);
1153 }
1154 }
1155 else
1156 {
1157 LOGF_WARN("Failed switching to manual exposure, control is unavailable", "");
1158 /* Don't fail, let the driver try to set the absolute duration, we'll see what happens */
1159 /* return false; */
1160 }
1161
1162 /* Configure absolute exposure */
1163 if (AbsExposureN->min <= ticks && ticks <= AbsExposureN->max)
1164 {
1165 double const restoredValue = AbsExposureN->value;
1166 AbsExposureN->value = ticks;
1167
1168 LOGF_DEBUG("%.3f-second exposure translates to %ld 1/10,000th-second device ticks.",
1169 duration, ticks);
1170
1171 unsigned int const ctrl_id = *((unsigned int *)AbsExposureN->aux0);
1172
1173 char errmsg[MAXRBUF];
1174 if (v4l_base->setINTControl(ctrl_id, AbsExposureN->value, errmsg) < 0)
1175 {
1177 AbsExposureN->value = restoredValue;
1178 IDSetNumber(&ImageAdjustNP, "Failed requesting %.3f-second exposure to the driver (%s).", duration, errmsg);
1179 return false;
1180 }
1181
1183 IDSetNumber(&ImageAdjustNP, nullptr);
1184 }
1185 else
1186 {
1187 LOGF_ERROR("Failed %.3f-second manual exposure, out of device bounds [%.3f,%.3f].",
1188 duration, (double) AbsExposureN->min / 10000.0f, (double) AbsExposureN->max / 10000.0f);
1189 return false;
1190 }
1191
1192 return true;
1193}
1194
1199void V4L2_Driver::stdtimerCallback(void * userpointer)
1200{
1201 V4L2_Driver * p = (V4L2_Driver *)userpointer;
1202 float remaining = p->getRemainingExposure();
1203 //DEBUGF(INDI::Logger::DBG_SESSION,"Exposure running, %f seconds left...", remaining);
1204 if (1.0f < remaining)
1205 p->stdtimer = IEAddTimer(1000, (IE_TCF *)stdtimerCallback, userpointer);
1206 else
1207 p->stdtimer = -1;
1208 p->PrimaryCCD.setExposureLeft(remaining);
1209}
1210
1211// iPolar and iGuider cameras occasionally fail to restart once capturing is stopped. This occurs in Windows too. Here
1212// we take the same approach as the iOptron ASCOM driver and restart the capture if no frames have been received for a while.
1213// JM 2024.03.07: Above approach can end in infinite capture, so we elect to abort immediately so that client can take action.
1223
1225{
1226 // FIXME Must migrate completely to Stream
1227 // The class shouldn't be making calls to encoder/recorder directly
1228 // Stream? Yes or No
1229 // Direct Record?
1230 INDI_UNUSED(do_stream);
1231 if (Streamer->isBusy())
1232 {
1233 LOG_WARN("Cannot start exposure while streaming is in progress");
1234 return false;
1235 }
1236
1237 if (is_capturing)
1238 {
1239 LOGF_WARN("Cannot start exposure while another is in progress (%.3f seconds left)",
1241 return false;
1242 }
1243
1244 if( !v4l_capture_started )
1245 {
1246 char errmsg[ERRMSGSIZ];
1247 if (v4l_base->start_capturing(errmsg))
1248 {
1249 LOGF_WARN("V4L2 base failed starting capture (%s)", errmsg);
1250 return false;
1251 }
1252 else
1253 {
1254 gettimeofday(&frame_received, nullptr);
1255 v4l_capture_started = true;
1256 }
1257 }
1258
1259 //if (do_stream)
1260 //v4l_base->doRecord(Streamer->isDirectRecording());
1261
1262 is_capturing = true;
1263 return true;
1264}
1265
1267{
1269 {
1270 LOG_WARN("No exposure or streaming in progress");
1271 return true;
1272 }
1273
1274 // For iGuider/iPolar we don't stop capturing, as it doesn't reliably restart. This
1275 // is the same behaviour as IOptron's ASCOM driver in Windows.
1276 if (isIOptron())
1277 {
1278 is_capturing = false;
1279 return true;
1280 }
1281
1282 if (!Streamer->isBusy() && 0.0f < getRemainingExposure())
1283 {
1284 LOGF_WARN("Stopping running exposure %.3f seconds before completion",
1286 }
1287
1288 // FIXME what to do with doRecord?
1289 //if(Streamer->isDirectRecording())
1290 //v4l_base->doRecord(false);
1291 char errmsg[ERRMSGSIZ];
1292 if (v4l_base->stop_capturing(errmsg))
1293 {
1294 LOGF_WARN("V4L2 base failed stopping capture (%s)", errmsg);
1295 }
1296
1297 is_capturing = false;
1298 v4l_capture_started = false;
1299 return true;
1300}
1301
1303{
1304 lxtimer = IEAddTimer((int)(timeinsec * 1000.0), (IE_TCF *)lxtimerCallback, this);
1306 return (lx->startLx());
1307}
1308
1309void V4L2_Driver::lxtimerCallback(void * userpointer)
1310{
1311 auto p = static_cast<V4L2_Driver *>(userpointer);
1312
1313 p->lx->stopLx();
1314 if (p->lx->getLxmode() == LXSERIAL)
1315 {
1316 p->v4l_base->setlxstate(LX_TRIGGERED);
1317 }
1318 else
1319 {
1320 p->v4l_base->setlxstate(LX_ACTIVE);
1321 }
1322 IERmTimer(p->lxtimer);
1323 if (!p->v4l_base->isstreamactive())
1324 p->is_capturing = p->start_capturing(false); // jump to new/updateFrame
1325}
1326
1327bool V4L2_Driver::UpdateCCDBin(int hor, int ver)
1328{
1329 if (CaptureFormatSP[IMAGE_RGB].getState() == ISS_ON)
1330 {
1331 if (hor == 1 && ver == 1)
1332 {
1333 PrimaryCCD.setBin(hor, ver);
1335 return true;
1336 }
1337
1338 LOG_WARN("Binning color frames is currently not supported.");
1339 return false;
1340 }
1341
1342 if (hor != ver)
1343 {
1344 LOGF_WARN("Cannot accept asymmetrical binning %dx%d.", hor, ver);
1345 return false;
1346 }
1347
1348 if (hor != 1 && hor != 2 && hor != 4)
1349 {
1350 LOG_WARN("Can only accept 1x1, 2x2, and 4x4 binning.");
1351 return false;
1352 }
1353
1354 if (Streamer->isBusy())
1355 {
1356 LOG_WARN("Cannot change binning while streaming/recording.");
1357 return false;
1358 }
1359
1360 PrimaryCCD.setBin(hor, ver);
1361 Streamer->setSize(PrimaryCCD.getSubW() / hor, PrimaryCCD.getSubH() / ver);
1362
1363 return true;
1364}
1365
1366bool V4L2_Driver::UpdateCCDFrame(int x, int y, int w, int h)
1367{
1368 char errmsg[ERRMSGSIZ];
1369
1370 //LOGF_INFO("calling updateCCDFrame: %d %d %d %d", x, y, w, h);
1371 //IDLog("calling updateCCDFrame: %d %d %d %d\n", x, y, w, h);
1372 if (v4l_base->setcroprect(x, y, w, h, errmsg) != -1)
1373 {
1374 struct v4l2_rect crect;
1375 crect = v4l_base->getcroprect();
1376
1377 V4LFrame->width = crect.width;
1378 V4LFrame->height = crect.height;
1379 PrimaryCCD.setFrame(x, y, w, h);
1381 Streamer->setSize(w, h);
1382 return true;
1383 }
1384 else
1385 {
1386 LOGF_INFO("ERROR (setcroprect): %s", errmsg);
1387 }
1388
1389 return false;
1390}
1391
1393{
1394 static_cast<V4L2_Driver *>(p)->newFrame();
1395}
1396
1400{
1401 /* FIXME: use unsigned floats, or double */
1402 size_t const size = v4l_base->getWidth() * v4l_base->getHeight();
1403 float const * src = v4l_base->getLinearY();
1404 float const * const end = v4l_base->getLinearY() + size;
1405 float * dest = V4LFrame->stackedFrame;
1406
1407 if (!V4LFrame->stackedFrame)
1408 {
1409 /* FIXME: allocate and reset the accumulator frame prior to this function, because memory owner is unclear, and we need more speed while accumulating */
1410 V4LFrame->stackedFrame = (float *)malloc(sizeof(float) * size);
1411 memcpy(V4LFrame->stackedFrame, src, sizeof(float) * size);
1412 subframeCount = 1;
1413 }
1414 else
1415 {
1416 /* Clamp to max float value */
1417 float const frameMax = std::numeric_limits<float>::max();
1418 while (src < end) if (frameMax - *dest < *src)
1419 {
1420 *dest++ = frameMax;
1421 src++;
1422 }
1423 else *dest++ += *src++;
1424 subframeCount += 1;
1425 }
1426}
1427
1428struct timeval V4L2_Driver::getElapsedExposure() const
1429{
1430 struct timeval now = { .tv_sec = 0, .tv_usec = 0 }, duration = { .tv_sec = 0, .tv_usec = 0 };
1431 gettimeofday( &now, nullptr );
1432 timersub(&now, &capture_start, &duration);
1433 return duration;
1434}
1435
1437{
1438 struct timeval remaining = { .tv_sec = 0, .tv_usec = 0 };
1439 timersub(&exposure_duration, &elapsed_exposure, &remaining);
1440 return (float) remaining.tv_sec + (float) remaining.tv_usec / 1000000.0f;
1441}
1442
1444{
1445 struct timeval current_frame_duration = frame_received;
1446 gettimeofday(&frame_received, nullptr);
1447 timersub(&frame_received, &current_frame_duration, &current_frame_duration);
1448
1449 if (Streamer->isBusy())
1450 {
1452
1453 int width = v4l_base->getWidth();
1454 int height = v4l_base->getHeight();
1455 int bpp = v4l_base->getBpp();
1456 int dbpp = 8;
1457 int totalBytes = 0;
1458 unsigned char * buffer = nullptr;
1459
1460 std::unique_lock<std::mutex> guard(ccdBufferLock);
1461
1462 if (v4l_base->getFormat() == V4L2_PIX_FMT_MJPEG)
1463 {
1464 Streamer->setPixelFormat(INDI_JPG);
1465 auto buffer = v4l_base->getMJPEGBuffer(totalBytes);
1466 if (buffer)
1467 {
1468 PrimaryCCD.setFrameBufferSize(totalBytes);
1469 memcpy(PrimaryCCD.getFrameBuffer(), buffer, totalBytes);
1470 }
1471 guard.unlock();
1472
1473 Streamer->newFrame(buffer, totalBytes);
1474 return;
1475 }
1476
1477 if (CaptureFormatSP[IMAGE_MONO].getState() == ISS_ON)
1478 {
1479 V4LFrame->Y = v4l_base->getY();
1480 totalBytes = width * height * (dbpp / 8);
1481 buffer = V4LFrame->Y;
1482 }
1483 else
1484 {
1486 totalBytes = width * height * (dbpp / 8) * 3;
1487 buffer = V4LFrame->RGB24Buffer;
1488 }
1489
1490 // downscale Y10 Y12 Y16
1491 if (bpp > dbpp)
1492 {
1493 unsigned short * src = (unsigned short *)buffer;
1494 unsigned char * dest = buffer;
1495 unsigned char shift = 0;
1496
1497 if (bpp < 16)
1498 {
1499 switch (bpp)
1500 {
1501 case 10:
1502 shift = 2;
1503 break;
1504 case 12:
1505 shift = 4;
1506 break;
1507 }
1508 for (int i = 0; i < totalBytes; i++)
1509 {
1510 *dest++ = *(src++) >> shift;
1511 }
1512 }
1513 else
1514 {
1515 unsigned char * src = (unsigned char *)buffer + 1; // Y16 is little endian
1516
1517 for (int i = 0; i < totalBytes; i++)
1518 {
1519 *dest++ = *src;
1520 src += 2;
1521 }
1522 }
1523 }
1524
1525 if (PrimaryCCD.getBinX() > 1)
1526 {
1527 memcpy(PrimaryCCD.getFrameBuffer(), buffer, totalBytes);
1529 guard.unlock();
1531 }
1532 else
1533 {
1534 guard.unlock();
1535 Streamer->newFrame(buffer, frameBytes);
1536 }
1537 return;
1538 }
1539
1540 if ( PrimaryCCD.isExposing() )
1541 {
1543 if( !is_capturing )
1544 {
1545 LOG_DEBUG("Skip frame, setup not complete yet" );
1546 return; //skip this frame
1547 }
1548
1549 struct timeval capture_frame_dif = { .tv_sec = 0, .tv_usec = 0 };
1550 timersub(&frame_received, &capture_start, &capture_frame_dif);
1551
1552 float time_since_capture_started = (float) capture_frame_dif.tv_sec + (float) capture_frame_dif.tv_usec / 1000000.0f;
1553 float expected_frame_duration = (float) frame_duration.tv_sec + (float) frame_duration.tv_usec / 1000000.0f;
1554
1555 if( time_since_capture_started < expected_frame_duration * 0.9 )
1556 {
1557 LOGF_DEBUG("Skip early frame cfd = %ld.%06ld seconds.", capture_frame_dif.tv_sec, capture_frame_dif.tv_usec);
1558 return;
1559 }
1560
1563 {
1565 }
1566
1568
1569 LOGF_INFO("Frame took %ld.%06ld s, e = %ld.%06ld s, t = %ld.%06ld s., cfd = %ld.%06ld s.",
1570 current_frame_duration.tv_sec, current_frame_duration.tv_usec,
1571 elapsed_exposure.tv_sec, elapsed_exposure.tv_usec,
1572 exposure_duration.tv_sec, exposure_duration.tv_usec,
1573 capture_frame_dif.tv_sec, capture_frame_dif.tv_usec
1574 );
1575
1576
1577 float remaining = getRemainingExposure();
1578 PrimaryCCD.setExposureLeft(remaining);
1579
1580 // Stack Mono frames
1582 {
1583 stackFrame();
1584 }
1585
1586 /* FIXME: stacking does not account for transfer time, so we'll miss the last frames probably */
1588 (timercmp(&elapsed_exposure, &exposure_duration, < )))
1589 return; // go on stacking
1590
1591 struct timeval const current_exposure = getElapsedExposure();
1592
1593 if (EncodeFormatSP.findOnSwitchIndex() == FORMAT_NATIVE && v4l_base->getFormat() == V4L2_PIX_FMT_MJPEG)
1594 {
1595 std::unique_lock<std::mutex> guard(ccdBufferLock);
1596 int totalBytes = 0;
1597 auto buffer = v4l_base->getMJPEGBuffer(totalBytes);
1598 if (buffer)
1599 {
1600 memcpy(PrimaryCCD.getFrameBuffer(), buffer, totalBytes);
1601 PrimaryCCD.setFrameBufferSize(totalBytes, false);
1602 }
1604 guard.unlock();
1605 }
1606 else if (CaptureFormatSP[IMAGE_MONO].getState() == ISS_ON)
1607 {
1608 if (!m_StackMode)
1609 {
1610 unsigned char * src, *dest;
1611 src = v4l_base->getY();
1612 dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1613
1614 std::unique_lock<std::mutex> guard(ccdBufferLock);
1615 memcpy(dest, src, frameBytes);
1616 guard.unlock();
1617 //for (i=0; i< frameBytes; i++)
1618 //*(dest++) = *(src++);
1619
1621 }
1622 else
1623 {
1624 float * src = V4LFrame->stackedFrame;
1625
1626 /* If we have a dark frame configured, substract it from the stack */
1627 if ((m_StackMode != STACK_TAKE_DARK) && (V4LFrame->darkFrame != nullptr))
1628 {
1629 float * dark = V4LFrame->darkFrame;
1630
1631 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1632 {
1633 if (*src > *dark)
1634 *src -= *dark;
1635 else
1636 *src = 0.0;
1637 src++;
1638 dark++;
1639 }
1640 src = V4LFrame->stackedFrame;
1641 }
1642
1643 //IDLog("Copying stack frame from %p to %p.\n", src, dest);
1644 if (m_StackMode == STACK_MEAN)
1645 {
1646 if (ImageDepthS[0].s == ISS_ON)
1647 {
1648 // depth 8 bits
1649 unsigned char * dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1650
1651 std::unique_lock<std::mutex> guard(ccdBufferLock);
1652 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1653 *dest++ = (unsigned char)((*src++ * 255.0f) / subframeCount);
1654 guard.unlock();
1655 }
1656 else
1657 {
1658 // depth 16 bits
1659 unsigned short * dest = (unsigned short *)PrimaryCCD.getFrameBuffer();
1660
1661 std::unique_lock<std::mutex> guard(ccdBufferLock);
1662 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1663 *dest++ = (unsigned short)((*src++ * 65535.0f) / subframeCount);
1664 guard.unlock();
1665 }
1666
1667 free(V4LFrame->stackedFrame);
1668 V4LFrame->stackedFrame = nullptr;
1669 }
1670 else if (m_StackMode == STACK_ADDITIVE)
1671 {
1672 /* Clamp additive stacking to frame dynamic range - that is, do not consider normalized source greater than 1.0f */
1673 if (ImageDepthS[0].s == ISS_ON)
1674 {
1675 // depth 8 bits
1676 unsigned char * dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1677
1678 std::unique_lock<std::mutex> guard(ccdBufferLock);
1679 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1680 {
1681 *dest++ = *src < 1.0f ? (unsigned char)((*src * 255)) : 255;
1682 src++;
1683 }
1684 guard.unlock();
1685 }
1686 else
1687 {
1688 // depth 16 bits
1689 unsigned short * dest = (unsigned short *)PrimaryCCD.getFrameBuffer();
1690
1691 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1692 {
1693 *dest++ = *src < 1.0f ? (unsigned short)((*src * 65535)) : 65535;
1694 src++;
1695 }
1696 }
1697
1698 free(V4LFrame->stackedFrame);
1699 V4LFrame->stackedFrame = nullptr;
1700 }
1701 else if (m_StackMode == STACK_TAKE_DARK)
1702 {
1703 if (V4LFrame->darkFrame != nullptr)
1704 free(V4LFrame->darkFrame);
1706 V4LFrame->stackedFrame = nullptr;
1707 src = V4LFrame->darkFrame;
1708 if (ImageDepthS[0].s == ISS_ON)
1709 {
1710 // depth 8 bits
1711 unsigned char * dest = (unsigned char *)PrimaryCCD.getFrameBuffer();
1712
1713 std::unique_lock<std::mutex> guard(ccdBufferLock);
1714 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1715 *dest++ = (unsigned char)((*src++ * 255));
1716 guard.unlock();
1717 }
1718 else
1719 {
1720 // depth 16 bits
1721 unsigned short * dest = (unsigned short *)PrimaryCCD.getFrameBuffer();
1722
1723 std::unique_lock<std::mutex> guard(ccdBufferLock);
1724 for (int i = 0; i < v4l_base->getWidth() * v4l_base->getHeight(); i++)
1725 *dest++ = (unsigned short)((*src++ * 65535));
1726 guard.unlock();
1727 }
1728 }
1729
1731 }
1733 }
1734 else
1735 {
1736 // Binning not supported in color images for now
1737 std::unique_lock<std::mutex> guard(ccdBufferLock);
1738 unsigned char * src = v4l_base->getRGBBuffer();
1739 unsigned char * dest = PrimaryCCD.getFrameBuffer();
1740 // We have RGB RGB RGB data but for FITS file we need each color in separate plane. i.e. RRR GGG BBB ..etc
1741 unsigned char * red = dest;
1742 unsigned char * green = dest + v4l_base->getWidth() * v4l_base->getHeight() * (v4l_base->getBpp() / 8);
1743 unsigned char * blue = dest + v4l_base->getWidth() * v4l_base->getHeight() * (v4l_base->getBpp() / 8) * 2;
1744
1745 for (int i = 0; i < (int)frameBytes; i += 3)
1746 {
1747 *(red++) = *(src + i);
1748 *(green++) = *(src + i + 1);
1749 *(blue++) = *(src + i + 2);
1750 }
1751 guard.unlock();
1752
1754 }
1755 frameCount += 1;
1756
1757 if (lx->isEnabled())
1758 {
1759 if (Streamer->isBusy() == false)
1761
1762 //if (PrimaryCCD.getExposureDuration() >= 3)
1763 LOGF_INFO("Capture of LX frame took %ld.%06ld seconds.", current_exposure.tv_sec, current_exposure.tv_usec);
1765 }
1766 else
1767 {
1768 //if (!is_streaming && !is_recording) stop_capturing();
1769 if (Streamer->isBusy() == false)
1770 {
1771 //just mark stop
1772 is_capturing = false;
1773 }
1774 else
1775 LOGF_DEBUG("%s: streamer is busy, continue capturing\n", __FUNCTION__);
1776
1777
1779 LOGF_INFO("Capture of one frame (%d stacked frames) took %ld.%06ld seconds.", subframeCount, current_exposure.tv_sec,
1780 current_exposure.tv_usec);
1781
1783 }
1784 }
1785 else
1786 {
1788
1789 // We don't abort for iGuider/iPolar as it doesn't reliably restart after having the stream stopped. This is the same behaviour as the ASCOM driver.
1790 if( non_capture_frames > 10 && !isIOptron())
1791 {
1792 /* 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.
1793 * That issue can be reproduced when clicking the "Set" button on the "Main Control" tab while an exposure is running.
1794 * Note that the patch in StartExposure returning busy instead of error prevents the flow from coming here, so now it's only a safeguard. */
1795 LOGF_WARN("%s: frame received while not exposing, force-aborting capture", __FUNCTION__);
1796 AbortExposure();
1797 }
1798 }
1799}
1800
1802{
1803 if (lx->isEnabled())
1804 {
1805 lx->stopLx();
1806 return true;
1807 }
1808 else if (!Streamer->isBusy())
1809 {
1810 if (-1 != stdtimer)
1812 return stop_capturing();
1813 }
1814
1815 LOG_WARN("Cannot abort exposure while video streamer is busy, stop streaming first");
1816 return false;
1817}
1818
1820{
1821 char errmsg[ERRMSGSIZ];
1822 if (!isConnected())
1823 {
1824 if (v4l_base->connectCam(PortT[0].text, errmsg) < 0)
1825 {
1826 LOGF_ERROR("Error: unable to open device %s: %s", PortT[0].text, errmsg);
1827 return false;
1828 }
1829
1830 /* Success! */
1831 LOGF_INFO("%s is online.", getDeviceName());
1832
1833 // If port not stored in config already then save it.
1834 if (strcmp(PortT[0].text, configPort))
1835 saveConfig(true, PortTP.name);
1836
1839
1840 if (!(strcmp((const char *)v4l_base->cap.driver, "pwc")))
1841 LOG_INFO(
1842 "To use LED Long exposure mode with recent kernels, see https://code.google.com/p/pwc-lxled/");
1843 }
1844
1845 return true;
1846}
1847
1849{
1850 if (isConnected())
1851 {
1853 if (PrimaryCCD.isExposing() || Streamer->isBusy())
1854 Streamer->close();
1855 }
1856 return true;
1857}
1858
1860{
1861 return "V4L2 CCD";
1862}
1863
1864/* Retrieves basic data from the device upon connection.*/
1866{
1867 int inputindex = -1, formatindex = -1;
1868 struct v4l2_fract frate;
1869
1870 // Check if we have pixel size info
1871 const PixelSizeInfo * info = CameraDatabase;
1872 std::string deviceName = std::string(v4l_base->getDeviceName());
1873 // to lower case.
1874 std::transform(deviceName.begin(), deviceName.end(), deviceName.begin(), ::tolower);
1875 while (info->deviceName)
1876 {
1877 std::string infoDeviceName = std::string(info->deviceName);
1878 std::transform(infoDeviceName.begin(), infoDeviceName.end(), infoDeviceName.begin(), ::tolower);
1879 std::string infoDeviceLabel = std::string(info->deviceLabel);
1880 std::transform(infoDeviceLabel.begin(), infoDeviceLabel.end(), infoDeviceLabel.begin(), ::tolower);
1881
1882 // Case insensitive comparison
1883 if (infoDeviceName == deviceName || infoDeviceLabel == deviceName)
1884 break;
1885 ++info;
1886 }
1887
1888 m_Info = const_cast<PixelSizeInfo*>(info);
1889
1894
1895 // CHeck if we need to set the default widthxheight for this camera model
1896 if (m_Info && m_Info->width > 0 && CaptureSizesSP.sp != nullptr)
1897 {
1898 char name[MAXINDINAME] = {0};
1899 auto captureSizeChangeRequired = true;
1900 // First check config and see if it is already set to the same desired resolution we want
1902 {
1903 uint32_t w = 0, h = 0;
1904 if (sscanf(name, "%ux%u", &w, &h) == 2)
1905 {
1906 if (w == m_Info->width && h == m_Info->height)
1907 captureSizeChangeRequired = false;
1908 }
1909 }
1910
1911 // If we need to change size then let's do that
1912 if (captureSizeChangeRequired)
1913 {
1914 auto resolution = std::to_string(m_Info->width) + "x" + std::to_string(m_Info->height);
1915 auto control = IUFindSwitch(&CaptureSizesSP, resolution.c_str());
1916 if (control)
1917 {
1918 ISState states[1] = {ISS_ON};
1919 const char * names[] = { resolution.c_str() };
1920 ISNewSwitch(getDeviceName(), CaptureSizesSP.name, states, (char **) names, 1);
1921 }
1922 }
1923 }
1924
1925 auto w = v4l_base->getWidth();
1926 auto h = v4l_base->getHeight();
1927 V4LFrame->width = w;
1928 V4LFrame->height = h;
1930
1931 inputindex = IUFindOnSwitchIndex(&InputsSP);
1932 formatindex = IUFindOnSwitchIndex(&CaptureFormatsSP);
1933 frate = (v4l_base->*(v4l_base->getframerate))();
1934 if (inputindex >= 0 && formatindex >= 0)
1935 LOGF_INFO("Found initial Input \"%s\", Format \"%s\", Size %dx%d, Frame interval %d/%ds",
1936 InputsSP.sp[inputindex].name, CaptureFormatsSP.sp[formatindex].name, w, h, frate.numerator,
1937 frate.denominator);
1938 else
1939 LOGF_INFO("Found initial size %dx%d, frame interval %d/%ds", w, h, frate.numerator,
1940 frate.denominator);
1941
1943 IDSetText(&camNameTP, nullptr);
1944#ifdef WITH_V4L2_EXPERIMENTS
1948 IDSetText(&CaptureColorSpaceTP, nullptr);
1949#endif
1950 if (Options)
1951 free(Options);
1952 Options = nullptr;
1953 v4loptions = 0;
1955
1957 PrimaryCCD.setFrame(0, 0, w, h);
1960 INDI_PIXEL_FORMAT pixelFormat;
1961 uint8_t pixelDepth = 8;
1962 if (getPixelFormat(v4l_base->fmt.fmt.pix.pixelformat, pixelFormat, pixelDepth))
1963 Streamer->setPixelFormat(pixelFormat, pixelDepth);
1964
1965 Streamer->setSize(w, h);
1966}
1967
1969{
1970 unsigned int i;
1971
1972 LOG_DEBUG("Enumerating V4L2 controls...");
1973
1974 // #1 Query for INTEGER controls, and fill up the structure
1975 free(ImageAdjustNP.np);
1976 ImageAdjustNP.nnp = 0;
1977
1978 //if (v4l_base->queryINTControls(&ImageAdjustNP) > 0)
1979 //defineProperty(&ImageAdjustNP);
1981 useExtCtrl = false;
1982
1985 useExtCtrl = true;
1986 else
1988
1989 if (v4ladjustments > 0)
1990 {
1991 LOGF_DEBUG("Found %d V4L2 adjustments", v4ladjustments);
1993
1994 for (int i = 0; i < ImageAdjustNP.nnp; i++)
1995 {
1996 if (strcmp(ImageAdjustNP.np[i].label, "Exposure (Absolute)") == 0 ||
1997 strcmp(ImageAdjustNP.np[i].label, "Exposure Time, Absolute") == 0 ||
1998 strcmp(ImageAdjustNP.np[i].label, "Exposure") == 0)
1999 {
2001 LOGF_DEBUG("- %s (used for absolute exposure duration)", ImageAdjustNP.np[i].label);
2002 }
2003 else LOGF_DEBUG("- %s", ImageAdjustNP.np[i].label);
2004 }
2005 }
2006 LOGF_DEBUG("Found %d V4L2 options", v4loptions);
2007 for (i = 0; i < v4loptions; i++)
2008 {
2010
2011 if (strcmp(Options[i].label, "Exposure, Auto") == 0 || strcmp(Options[i].label, "Auto Exposure") == 0)
2012 {
2014 LOGF_DEBUG("- %s (used for manual/auto exposure control)", Options[i].label);
2015 }
2016 else LOGF_DEBUG("- %s", Options[i].label);
2017 }
2018
2019 if(!AbsExposureN)
2020 LOG_WARN("Absolute exposure duration control is not possible on the device!");
2021
2022 if(!ManualExposureSP)
2023 LOG_WARN("Manual/auto exposure control is not possible on the device!");
2024}
2025
2027{
2028 V4LFrame = new img_t;
2029
2030 if (V4LFrame == nullptr)
2031 {
2032 LOG_ERROR("Critical Error: Unable to initialize driver. Low memory.");
2033 exit(-1);
2034 }
2035
2036 V4LFrame->Y = nullptr;
2037 V4LFrame->U = nullptr;
2038 V4LFrame->V = nullptr;
2039 V4LFrame->RGB24Buffer = nullptr;
2040 V4LFrame->stackedFrame = nullptr;
2041 V4LFrame->darkFrame = nullptr;
2042}
2043
2045{
2046 delete (V4LFrame);
2047}
2048
2050{
2051 if (PrimaryCCD.getBinX() > 1 && PrimaryCCD.getNAxis() > 2)
2052 {
2053 LOG_WARN("Cannot stream binned color frame.");
2054 return false;
2055 }
2056
2057 auto onSwitch = IUFindOnSwitch(&CaptureFormatsSP);
2058 if (onSwitch && strstr(onSwitch->label, "JPEG"))
2059 v4l_base->setNative(true);
2060 /* Callee will take care of checking states */
2061 return start_capturing(true);
2062}
2063
2065{
2066 if (!Streamer->isBusy() /*&& is_capturing*/)
2067 {
2068 /* Strange situation indeed, but it's theoretically possible to try to stop streaming while exposing - safeguard actually */
2069 LOGF_WARN("Cannot stop streaming, exposure running (%.1f seconds remaining)",
2071 return false;
2072 }
2073
2075 return stop_capturing();
2076}
2077
2079{
2081
2083 StackModeSP.save(fp);
2084
2085 if (ImageAdjustNP.nnp > 0)
2087
2088 // Try to save important auto-generated properties, if found.
2089
2090 // Format
2091 auto format = getProperty("V4L2_FORMAT");
2092 if (format.isValid())
2093 format.save(fp);
2094
2095 // Size
2096 auto size = getProperty("V4L2_SIZE_DISCRETE");
2097 if (size.isValid())
2098 size.save(fp);
2099
2100 auto fps = getProperty("V4L2_FRAMEINT_DISCRETE");
2101 if (fps.isValid())
2102 fps.save(fp);
2103
2104 if (CaptureSizesSP.sp != nullptr)
2106
2107 return Streamer->saveConfigItems(fp);
2108}
2109
2110bool V4L2_Driver::getPixelFormat(uint32_t v4l2format, INDI_PIXEL_FORMAT &pixelFormat, uint8_t &pixelDepth)
2111{
2112 //IDLog("recorder: setpixelformat %d\n", format);
2113 pixelDepth = 8;
2114 switch (v4l2format)
2115 {
2116 case V4L2_PIX_FMT_GREY:
2117#ifdef V4L2_PIX_FMT_Y10
2118 case V4L2_PIX_FMT_Y10:
2119#endif
2120#ifdef V4L2_PIX_FMT_Y12
2121 case V4L2_PIX_FMT_Y12:
2122#endif
2123#ifdef V4L2_PIX_FMT_Y16
2124 case V4L2_PIX_FMT_Y16:
2125#endif
2126 pixelFormat = INDI_MONO;
2127#ifdef V4L2_PIX_FMT_Y10
2128 if (v4l2format == V4L2_PIX_FMT_Y10)
2129 pixelDepth = 10;
2130#endif
2131#ifdef V4L2_PIX_FMT_Y12
2132 if (v4l2format == V4L2_PIX_FMT_Y12)
2133 pixelDepth = 12;
2134#endif
2135#ifdef V4L2_PIX_FMT_Y16
2136 if (v4l2format == V4L2_PIX_FMT_Y16)
2137 pixelDepth = 16;
2138#endif
2139 return true;
2140 case V4L2_PIX_FMT_SBGGR8:
2141#ifdef V4L2_PIX_FMT_SBGGR10
2142 case V4L2_PIX_FMT_SBGGR10:
2143#endif
2144#ifdef V4L2_PIX_FMT_SBGGR12
2145 case V4L2_PIX_FMT_SBGGR12:
2146#endif
2147 case V4L2_PIX_FMT_SBGGR16:
2148 pixelFormat = INDI_BAYER_BGGR;
2149#ifdef V4L2_PIX_FMT_SBGGR10
2150 if (v4l2format == V4L2_PIX_FMT_SBGGR10)
2151 pixelDepth = 10;
2152#endif
2153#ifdef V4L2_PIX_FMT_SBGGR12
2154 if (v4l2format == V4L2_PIX_FMT_SBGGR12)
2155 pixelDepth = 12;
2156#endif
2157 if (v4l2format == V4L2_PIX_FMT_SBGGR16)
2158 pixelDepth = 16;
2159 return true;
2160 case V4L2_PIX_FMT_SGBRG8:
2161#ifdef V4L2_PIX_FMT_SGBRG10
2162 case V4L2_PIX_FMT_SGBRG10:
2163#endif
2164#ifdef V4L2_PIX_FMT_SGBRG12
2165 case V4L2_PIX_FMT_SGBRG12:
2166#endif
2167 pixelFormat = INDI_BAYER_GBRG;
2168#ifdef V4L2_PIX_FMT_SGBRG10
2169 if (v4l2format == V4L2_PIX_FMT_SGBRG10)
2170 pixelDepth = 10;
2171#endif
2172#ifdef V4L2_PIX_FMT_SGBRG12
2173 if (v4l2format == V4L2_PIX_FMT_SGBRG12)
2174 pixelDepth = 12;
2175#endif
2176 return true;
2177#if defined(V4L2_PIX_FMT_SGRBG8) || defined(V4L2_PIX_FMT_SGRBG10) || defined(V4L2_PIX_FMT_SGRBG12)
2178#ifdef V4L2_PIX_FMT_SGRBG8
2179 case V4L2_PIX_FMT_SGRBG8:
2180#endif
2181#ifdef V4L2_PIX_FMT_SGRBG10
2182 case V4L2_PIX_FMT_SGRBG10:
2183#endif
2184#ifdef V4L2_PIX_FMT_SGRBG12
2185 case V4L2_PIX_FMT_SGRBG12:
2186#endif
2187 pixelFormat = INDI_BAYER_GRBG;
2188#ifdef V4L2_PIX_FMT_SGRBG10
2189 if (v4l2format == V4L2_PIX_FMT_SGRBG10)
2190 pixelDepth = 10;
2191
2192#endif
2193#ifdef V4L2_PIX_FMT_SGRBG12
2194 if (v4l2format == V4L2_PIX_FMT_SGRBG12)
2195 pixelDepth = 12;
2196#endif
2197 return true;
2198#endif
2199#if defined(V4L2_PIX_FMT_SRGGB8) || defined(V4L2_PIX_FMT_SRGGB10) || defined(V4L2_PIX_FMT_SRGGB12)
2200#ifdef V4L2_PIX_FMT_SRGGB8
2201 case V4L2_PIX_FMT_SRGGB8:
2202#endif
2203#ifdef V4L2_PIX_FMT_SRGGB10
2204 case V4L2_PIX_FMT_SRGGB10:
2205#endif
2206#ifdef V4L2_PIX_FMT_SRGGB12
2207 case V4L2_PIX_FMT_SRGGB12:
2208#endif
2209 pixelFormat = INDI_BAYER_RGGB;
2210#ifdef V4L2_PIX_FMT_SRGGB10
2211 if (v4l2format == V4L2_PIX_FMT_SRGGB10)
2212 pixelDepth = 10;
2213#endif
2214#ifdef V4L2_PIX_FMT_SRGGB12
2215 if (v4l2format == V4L2_PIX_FMT_SRGGB12)
2216 pixelDepth = 12;
2217#endif
2218 return true;
2219#endif
2220 case V4L2_PIX_FMT_RGB24:
2221 pixelFormat = INDI_RGB;
2222 return true;
2223 case V4L2_PIX_FMT_BGR24:
2224 pixelFormat = INDI_BGR;
2225 return true;
2226 default:
2227 return false;
2228 }
2229}
2230
2232{
2233 if (Streamer->isRecording())
2234 {
2235 LOG_WARN("Can not set Image type (GRAY/COLOR) while recording.");
2236 return false;
2237 }
2238
2239 PrimaryCCD.setNAxis(index == IMAGE_MONO ? 2 : 3);
2241 Streamer->setPixelFormat(index == 0 ? INDI_MONO : INDI_RGB, 8);
2242 return true;
2243}
#define LXSERIAL
Definition Lx.h:16
Options
Definition ccvt.h:199
bool isConnected() const
Property getProperty(const char *name, INDI_PROPERTY_TYPE type=INDI_UNKNOWN) const
Return a property and its type given its name.
void setDeviceName(const char *dev)
Set the device name.
void setExposureDuration(double duration)
setExposureDuration Set desired CCD frame exposure duration for next exposure. You must call this fun...
void setResolution(uint32_t x, uint32_t y)
setResolution set CCD Chip resolution
int getSubH() const
getSubH Get the height of the frame
double getExposureDuration() const
getExposureDuration Get requested exposure duration for the CCD chip in seconds.
int getBPP() const
getBPP Get CCD Chip depth (bits per pixel).
void setFrame(uint32_t subx, uint32_t suby, uint32_t subw, uint32_t subh)
setFrame Set desired frame resolutoin for an exposure.
void setMinMaxStep(const char *property, const char *element, double min, double max, double step, bool sendToClient=true)
setMinMaxStep for a number property element
bool isExposing() const
int getBinX() const
getBinX Get horizontal binning of the CCD chip.
void setImageExtension(const char *ext)
setImageExtension Set image extension
uint8_t * getFrameBuffer()
getFrameBuffer Get raw frame buffer of the CCD chip.
void setExposureFailed()
setExposureFailed Alert the client that the exposure failed.
void setExposureLeft(double duration)
setExposureLeft Update exposure time left. Inform the client of the new exposure time left value.
int getSubW() const
getSubW Get the width of the frame
void setFrameBufferSize(uint32_t nbuf, bool allocMem=true)
setFrameBufferSize Set desired frame buffer size. The function will allocate memory accordingly....
int getFrameBufferSize() const
getFrameBufferSize Get allocated frame buffer size to hold the CCD image frame.
void setBin(uint8_t hor, uint8_t ver)
setBin Set CCD Chip binnig
int getNAxis() const
void binFrame()
binFrame Perform software binning on the CCD frame. Only use this function if hardware binning is not...
void setBPP(uint8_t bpp)
setBPP Set depth of CCD chip.
void setNAxis(int value)
setNAxis Set FITS number of axis
INDI::PropertyNumber getCCDInfo()
Return CCD Info Property.
CCDChip PrimaryCCD
Definition indiccd.h:621
uint32_t GetCCDCapability() const
GetCCDCapability returns the CCD capabilities.
Definition indiccd.h:161
std::unique_ptr< StreamManager > Streamer
Definition indiccd.h:619
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition indiccd.cpp:1554
@ CCD_CAN_SUBFRAME
Definition indiccd.h:116
@ CCD_CAN_ABORT
Definition indiccd.h:117
@ CCD_CAN_BIN
Definition indiccd.h:115
@ CCD_HAS_STREAMING
Definition indiccd.h:123
virtual bool ExposureComplete(CCDChip *targetChip)
Uploads target Chip exposed buffer as FITS to the client. Derived classes should class this function ...
Definition indiccd.cpp:2361
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition indiccd.cpp:525
std::mutex ccdBufferLock
Definition indiccd.h:612
INDI::PropertySwitch EncodeFormatSP
Specifies Driver image encoding format (FITS, Native, JPG, ..etc)
Definition indiccd.h:702
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indiccd.cpp:157
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition indiccd.cpp:910
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition indiccd.cpp:540
virtual void addCaptureFormat(const CaptureFormat &format)
addCaptureFormat Add a supported camera native capture format (e.g. Mono, Bayer8.....
Definition indiccd.cpp:3264
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save configuration items in XML file.
Definition indiccd.cpp:2914
INDI::PropertySwitch CaptureFormatSP
Specifies Camera NATIVE capture format (e.g. Mono, RGB, RAW8..etc).
Definition indiccd.h:699
void SetCCDCapability(uint32_t cap)
SetCCDCapability Set the CCD capabilities. All fields must be initialized.
Definition indiccd.cpp:140
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition indiccd.cpp:1143
@ FORMAT_NATIVE
Definition indiccd.h:706
virtual void SetCCDParams(int x, int y, int bpp, float xf, float yf)
Setup CCD parameters for primary CCD. Child classes call this function to update CCD parameters.
Definition indiccd.cpp:2888
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
void addDebugControl()
Add Debug control to the driver.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
void setPermission(IPerm permission)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void callOnTimeout(const std::function< void()> &callback)
Definition inditimer.cpp:76
void start()
Starts or restarts the timer with the timeout specified in interval.
Definition inditimer.cpp:82
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
void setInterval(int msec)
Set the timeout interval in milliseconds.
void stop()
Stops the timer.
Definition inditimer.cpp:97
int stop_capturing(char *errmsg)
virtual void disconnectCam(bool stopcapture)
float * getLinearY()
int(V4L2_Base::* setframerate)(struct v4l2_fract frate, char *errmsg)
Definition v4l2_base.h:101
struct v4l2_rect getcroprect()
bool enumerate_ext_ctrl()
int setINTControl(unsigned int ctrl_id, double new_value, char *errmsg)
struct v4l2_capability cap
Definition v4l2_base.h:187
void setlxstate(short s)
Definition v4l2_base.h:145
void queryControls(INumberVectorProperty *nvp, unsigned int *nnumber, ISwitchVectorProperty **options, unsigned int *noptions, const char *dev, const char *group)
int getControl(unsigned int ctrl_id, double *value, char *errmsg)
void setColorProcessing(bool quantization, bool colorconvert, bool linearization)
void getframerates(ISwitchVectorProperty *frameratessp, INumberVectorProperty *frameratenp)
void registerCallback(WPF *fp, void *ud)
struct v4l2_format fmt
Definition v4l2_base.h:190
void getcaptureformats(ISwitchVectorProperty *captureformatssp)
unsigned char * getY()
int start_capturing(char *errmsg)
void getcapturesizes(ISwitchVectorProperty *capturesizessp, INumberVectorProperty *capturesizenp)
unsigned char * getMJPEGBuffer(int &size)
struct v4l2_fract(V4L2_Base::* getframerate)()
Definition v4l2_base.h:102
void setDeviceName(const char *name)
int setcapturesize(unsigned int w, unsigned int h, char *errmsg)
unsigned char * getRGBBuffer()
char * getDeviceName()
int setcroprect(int x, int y, int w, int h, char *errmsg)
bool queryExtControls(INumberVectorProperty *nvp, unsigned int *nnumber, ISwitchVectorProperty **options, unsigned int *noptions, const char *dev, const char *group)
void getinputs(ISwitchVectorProperty *inputssp)
void setNative(bool value)
Definition v4l2_base.h:96
int setinput(unsigned int inputindex, char *errmsg)
int setOPTControl(unsigned int ctrl_id, unsigned int new_value, char *errmsg)
bool isLXmodCapable()
virtual int connectCam(const char *devpath, char *errmsg, int pixelFormat=-1, int width=-1, int height=-1)
Definition Lx.h:24
bool updateProperties()
Definition Lx.cpp:106
bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Definition Lx.cpp:151
bool startLx()
Definition Lx.cpp:322
int stopLx()
Definition Lx.cpp:339
bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Definition Lx.cpp:281
bool isEnabled()
Definition Lx.cpp:21
bool initProperties(INDI::DefaultDevice *device)
Definition Lx.cpp:26
unsigned int getLxmode()
Definition Lx.cpp:317
void setCamerafd(int fd)
Definition Lx.cpp:16
bool setManualExposure(double duration)
ITextVectorProperty camNameTP
Definition v4l2driver.h:168
virtual bool StopStreaming() override
StopStreaming Stop live video streaming.
unsigned int v4loptions
Definition v4l2driver.h:156
virtual const char * getDefaultName() override
virtual bool StartStreaming() override
StartStreaming Start live video streaming.
ITextVectorProperty CaptureColorSpaceTP
Definition v4l2driver.h:169
INDI::PropertySwitch StackModeSP
Definition v4l2driver.h:148
INumberVectorProperty FrameRateNP
Definition v4l2driver.h:163
virtual void getBasicData()
PixelSizeInfo * m_Info
Definition v4l2driver.h:239
struct timeval getElapsedExposure() const
struct timeval exposure_duration
Definition v4l2driver.h:218
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
INumberVectorProperty CaptureSizesNP
Definition v4l2driver.h:162
struct timeval frame_received
Definition v4l2driver.h:216
bool setShutter(double duration)
double divider
Definition v4l2driver.h:208
ISwitchVectorProperty FrameRatesSP
Definition v4l2driver.h:152
virtual void updateV4L2Controls()
virtual bool Disconnect() override
Disconnect from device.
INumberVectorProperty ImageAdjustNP
Definition v4l2driver.h:164
ISwitchVectorProperty InputsSP
Definition v4l2driver.h:149
unsigned int m_StackMode
Definition v4l2driver.h:224
bool startlongexposure(double timeinsec)
char configPort[256]
Definition v4l2driver.h:242
bool useExtCtrl
Definition v4l2driver.h:158
ISwitchVectorProperty CaptureFormatsSP
Definition v4l2driver.h:150
bool isIOptron()
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool UpdateCCDFrame(int x, int y, int w, int h) override
CCD calls this function when CCD Frame dimension needs to be updated in the hardware....
IText camNameT[1]
Definition v4l2driver.h:138
ITextVectorProperty PortTP
Definition v4l2driver.h:167
void releaseBuffers()
virtual void initCamBase()
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void allocateBuffers()
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool AbortExposure() override
Abort ongoing exposure.
ISwitchVectorProperty ImageDepthSP
Definition v4l2driver.h:147
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool StartExposure(float duration) override
Start exposing primary CCD chip.
ulong frameBytes
Definition v4l2driver.h:225
char defaultVideoPort[256]
Definition v4l2driver.h:241
bool valid_frame_has_arrived
Definition v4l2driver.h:230
bool stop_capturing()
void stackFrame()
unsigned int non_capture_frames
Definition v4l2driver.h:226
INumber * AbsExposureN
Definition v4l2driver.h:172
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
struct timeval frame_duration
Definition v4l2driver.h:215
bool v4l_capture_started
Definition v4l2driver.h:227
struct timeval capture_start
Definition v4l2driver.h:211
ISwitchVectorProperty CaptureSizesSP
Definition v4l2driver.h:151
img_t * V4LFrame
Definition v4l2driver.h:209
bool updateCaptureSize(uint32_t width, uint32_t height)
struct timeval elapsed_exposure
Definition v4l2driver.h:219
int subframeCount
Definition v4l2driver.h:206
ISwitchVectorProperty ColorProcessingSP
Definition v4l2driver.h:154
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
ISwitchVectorProperty * ManualExposureSP
Definition v4l2driver.h:173
ISwitch ImageDepthS[2]
Definition v4l2driver.h:133
void iOptronWatchdogCallback()
virtual ~V4L2_Driver() override
virtual bool UpdateCCDBin(int hor, int ver) override
CCD calls this function when CCD Binning needs to be updated in the hardware. Derived classes should ...
INDI::Timer ioptron_watchdog_timer
Definition v4l2driver.h:236
IText CaptureColorSpaceT[3]
Definition v4l2driver.h:139
bool start_capturing(bool do_stream)
void updateFrameSize()
IText PortT[1]
Definition v4l2driver.h:137
virtual bool SetCaptureFormat(uint8_t index) override
SetCaptureFormat Set Active Capture format.
INDI::V4L2_Base * v4l_base
Definition v4l2driver.h:202
static void stdtimerCallback(void *userpointer)
unsigned int v4ladjustments
Definition v4l2driver.h:157
bool getPixelFormat(uint32_t v4l2format, INDI_PIXEL_FORMAT &pixelFormat, uint8_t &pixelDepth)
float getRemainingExposure() const
static void lxtimerCallback(void *userpointer)
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save configuration items in XML file.
ISwitch ColorProcessingS[3]
Definition v4l2driver.h:134
bool is_capturing
Definition v4l2driver.h:228
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
void IERmTimer(int timerid)
Remove the timer with the given timerid, as returned from IEAddTimer() or IEAddPeriodicTimer().
Definition eventloop.c:606
void() IE_TCF(void *userpointer)
Signature of a timeout caller.
Definition eventloop.c:573
int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
Register a new single-shot timer function, fp, to be called with ud as argument after ms.
Definition eventloop.c:586
double max(void)
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
#define NARRAY(a)
Handy macro to find the number of elements in array a[]. Must be used with actual array,...
Definition indiapi.h:498
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
#define MAXINDINAME
Definition indiapi.h:189
INDI_PIXEL_FORMAT
@ INDI_BAYER_GRBG
@ INDI_MONO
@ INDI_BAYER_GBRG
@ INDI_BAYER_BGGR
@ INDI_BGR
@ INDI_RGB
@ INDI_JPG
@ INDI_BAYER_RGGB
const char * IMAGE_SETTINGS_TAB
Definition indiccd.cpp:62
const char * IMAGE_INFO_TAB
Definition indiccd.cpp:63
void IDLog(const char *fmt,...)
Definition indicom.c:305
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
Definition indidevapi.c:265
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:490
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition indidevapi.c:285
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition indidevapi.c:317
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition indidevapi.c:416
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition indidevapi.c:28
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
const char * IUFindOnSwitchName(ISState *states, char *names[], int n)
Returns the name of the first ON switch it finds in the supplied arguments.
Definition indidevapi.c:355
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
int IUGetConfigOnSwitchName(const char *dev, const char *property, char *name, size_t size)
IUGetConfigOnSwitchLabel Opens configuration file and returns the name of the ON switch property,...
Definition indidriver.c:783
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition indidriver.c:908
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define getDeviceName
int fd
const char * DEVICE_PORT
Device serial (or bluetooth) connection port. The default value on Linux is /dev/ttyUSB0 while on Mac...
Namespace to encapsulate INDI client, drivers, and mediator classes.
One text descriptor.
uint32_t width
Definition v4l2driver.h:56
uint32_t height
Definition v4l2driver.h:57
const char * deviceName
Definition v4l2driver.h:52
const char * commonName
Definition v4l2driver.h:53
float pixelSizeX
Definition v4l2driver.h:54
float pixelSizeY
Definition v4l2driver.h:55
unsigned char * RGB24Buffer
Definition v4l2driver.h:110
unsigned char * Y
Definition v4l2driver.h:107
unsigned char * U
Definition v4l2driver.h:108
unsigned char * V
Definition v4l2driver.h:109
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:276
char label[MAXINDILABEL]
Definition indiapi.h:278
void * aux0
Definition indiapi.h:292
double value
Definition indiapi.h:288
char name[MAXINDINAME]
Definition indiapi.h:369
ISState s
Definition indiapi.h:353
char name[MAXINDINAME]
Definition indiapi.h:349
void * aux
Definition indiapi.h:357
char name[MAXINDINAME]
Definition indiapi.h:248
@ LX_ACCUMULATING
Definition v4l2_base.h:57
@ LX_ACTIVE
Definition v4l2_base.h:55
@ LX_TRIGGERED
Definition v4l2_base.h:56
const char * getColorSpaceName(struct v4l2_format *fmt)
const char * getQuantizationName(struct v4l2_format *fmt)
const char * getYCbCrEncodingName(struct v4l2_format *fmt)
int findIndexOfNearestValue(double value, const double allowed_values[])
#define IOPTRON_ABS_EXPOSURE_CTRL_ID
#define MAX_IOPTRON_DURATION
#define IOPTRON_WATCHDOG_PERIOD_IN_MS
#define MAX_IOPTRON_TICKS
#define CAPTURE_FORMAT
Definition v4l2driver.h:35
#define IMAGE_BOOLEAN
Definition v4l2driver.h:34
#define ERRMSGSIZ
Definition v4l2driver.h:38
#define IMAGE_GROUP
Definition v4l2driver.h:33