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