File indexing completed on 2024-07-21 06:28:20

0001 /*
0002     SPDX-FileCopyrightText: 2022 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "indicamerachip.h"
0008 #include "indicamera.h"
0009 
0010 #include "Options.h"
0011 
0012 #include "indi_debug.h"
0013 
0014 namespace ISD
0015 {
0016 
0017 CameraChip::CameraChip(ISD::Camera *camera, ChipType type): m_Camera(camera), m_Type(type) {}
0018 
0019 FITSView *CameraChip::getImageView(FITSMode imageType)
0020 {
0021     switch (imageType)
0022     {
0023         case FITS_NORMAL:
0024             return normalImage;
0025 
0026         case FITS_FOCUS:
0027             return focusImage;
0028 
0029         case FITS_GUIDE:
0030             return guideImage;
0031 
0032         case FITS_CALIBRATE:
0033             return calibrationImage;
0034 
0035         case FITS_ALIGN:
0036             return alignImage;
0037 
0038         default:
0039             break;
0040     }
0041 
0042     return nullptr;
0043 }
0044 
0045 void CameraChip::setImageView(FITSView *image, FITSMode imageType)
0046 {
0047     switch (imageType)
0048     {
0049         case FITS_NORMAL:
0050             normalImage = image;
0051             break;
0052 
0053         case FITS_FOCUS:
0054             focusImage = image;
0055             break;
0056 
0057         case FITS_GUIDE:
0058             guideImage = image;
0059             break;
0060 
0061         case FITS_CALIBRATE:
0062             calibrationImage = image;
0063             break;
0064 
0065         case FITS_ALIGN:
0066             alignImage = image;
0067             break;
0068 
0069         default:
0070             break;
0071     }
0072 
0073     if (image)
0074         imageData = image->imageData();
0075 }
0076 
0077 bool CameraChip::getFrameMinMax(int *minX, int *maxX, int *minY, int *maxY, int *minW, int *maxW, int *minH, int *maxH)
0078 {
0079     INumberVectorProperty *frameProp = nullptr;
0080 
0081     switch (m_Type)
0082     {
0083         case PRIMARY_CCD:
0084             frameProp = m_Camera->getNumber("CCD_FRAME");
0085             break;
0086 
0087         case GUIDE_CCD:
0088             frameProp = m_Camera->getNumber("GUIDER_FRAME");
0089             break;
0090     }
0091 
0092     if (frameProp == nullptr)
0093         return false;
0094 
0095     INumber *arg = IUFindNumber(frameProp, "X");
0096 
0097     if (arg == nullptr)
0098         return false;
0099 
0100     if (minX)
0101         *minX = arg->min;
0102     if (maxX)
0103         *maxX = arg->max;
0104 
0105     arg = IUFindNumber(frameProp, "Y");
0106 
0107     if (arg == nullptr)
0108         return false;
0109 
0110     if (minY)
0111         *minY = arg->min;
0112     if (maxY)
0113         *maxY = arg->max;
0114 
0115     arg = IUFindNumber(frameProp, "WIDTH");
0116 
0117     if (arg == nullptr)
0118         return false;
0119 
0120     if (minW)
0121         *minW = arg->min;
0122     if (maxW)
0123         *maxW = arg->max;
0124 
0125     arg = IUFindNumber(frameProp, "HEIGHT");
0126 
0127     if (arg == nullptr)
0128         return false;
0129 
0130     if (minH)
0131         *minH = arg->min;
0132     if (maxH)
0133         *maxH = arg->max;
0134 
0135     return true;
0136 }
0137 
0138 bool CameraChip::setImageInfo(uint16_t width, uint16_t height, double pixelX, double pixelY, uint8_t bitdepth)
0139 {
0140     INumberVectorProperty *ccdInfoProp = nullptr;
0141 
0142     switch (m_Type)
0143     {
0144         case PRIMARY_CCD:
0145             ccdInfoProp = m_Camera->getNumber("CCD_INFO");
0146             break;
0147 
0148         case GUIDE_CCD:
0149             ccdInfoProp = m_Camera->getNumber("GUIDER_INFO");
0150             break;
0151     }
0152 
0153     if (ccdInfoProp == nullptr)
0154         return false;
0155 
0156     ccdInfoProp->np[0].value = width;
0157     ccdInfoProp->np[1].value = height;
0158     ccdInfoProp->np[2].value = std::hypotf(pixelX, pixelY);
0159     ccdInfoProp->np[3].value = pixelX;
0160     ccdInfoProp->np[4].value = pixelY;
0161     ccdInfoProp->np[5].value = bitdepth;
0162 
0163     m_Camera->sendNewProperty(ccdInfoProp);
0164 
0165     return true;
0166 }
0167 
0168 bool CameraChip::getImageInfo(uint16_t &width, uint16_t &height, double &pixelX, double &pixelY, uint8_t &bitdepth)
0169 {
0170     INumberVectorProperty *ccdInfoProp = nullptr;
0171 
0172     switch (m_Type)
0173     {
0174         case PRIMARY_CCD:
0175             ccdInfoProp = m_Camera->getNumber("CCD_INFO");
0176             break;
0177 
0178         case GUIDE_CCD:
0179             ccdInfoProp = m_Camera->getNumber("GUIDER_INFO");
0180             break;
0181     }
0182 
0183     if (ccdInfoProp == nullptr)
0184         return false;
0185 
0186     width    = ccdInfoProp->np[0].value;
0187     height   = ccdInfoProp->np[1].value;
0188     pixelX   = ccdInfoProp->np[2].value;
0189     pixelY   = ccdInfoProp->np[3].value;
0190     bitdepth = ccdInfoProp->np[5].value;
0191 
0192     return true;
0193 }
0194 
0195 bool CameraChip::getBayerInfo(uint16_t &offsetX, uint16_t &offsetY, QString &pattern)
0196 {
0197     ITextVectorProperty * bayerTP = m_Camera->getText("CCD_CFA");
0198     if (!bayerTP)
0199         return false;
0200 
0201     offsetX = QString(bayerTP->tp[0].text).toInt();
0202     offsetY = QString(bayerTP->tp[1].text).toInt();
0203     pattern = QString(bayerTP->tp[2].text);
0204 
0205     return true;
0206 }
0207 
0208 bool CameraChip::getFrame(int *x, int *y, int *w, int *h)
0209 {
0210     INumberVectorProperty *frameProp = nullptr;
0211 
0212     switch (m_Type)
0213     {
0214         case PRIMARY_CCD:
0215             frameProp = m_Camera->getNumber("CCD_FRAME");
0216             break;
0217 
0218         case GUIDE_CCD:
0219             frameProp = m_Camera->getNumber("GUIDER_FRAME");
0220             break;
0221     }
0222 
0223     if (frameProp == nullptr)
0224         return false;
0225 
0226     INumber *arg = IUFindNumber(frameProp, "X");
0227 
0228     if (arg == nullptr)
0229         return false;
0230 
0231     *x = arg->value;
0232 
0233     arg = IUFindNumber(frameProp, "Y");
0234     if (arg == nullptr)
0235         return false;
0236 
0237     *y = arg->value;
0238 
0239     arg = IUFindNumber(frameProp, "WIDTH");
0240     if (arg == nullptr)
0241         return false;
0242 
0243     *w = arg->value;
0244 
0245     arg = IUFindNumber(frameProp, "HEIGHT");
0246     if (arg == nullptr)
0247         return false;
0248 
0249     *h = arg->value;
0250 
0251     return true;
0252 }
0253 
0254 bool CameraChip::resetFrame()
0255 {
0256     INumberVectorProperty *frameProp = nullptr;
0257 
0258     switch (m_Type)
0259     {
0260         case PRIMARY_CCD:
0261             frameProp = m_Camera->getNumber("CCD_FRAME");
0262             break;
0263 
0264         case GUIDE_CCD:
0265             frameProp = m_Camera->getNumber("GUIDER_FRAME");
0266             break;
0267     }
0268 
0269     if (frameProp == nullptr)
0270         return false;
0271 
0272     INumber *xarg = IUFindNumber(frameProp, "X");
0273     INumber *yarg = IUFindNumber(frameProp, "Y");
0274     INumber *warg = IUFindNumber(frameProp, "WIDTH");
0275     INumber *harg = IUFindNumber(frameProp, "HEIGHT");
0276 
0277     if (xarg && yarg && warg && harg)
0278     {
0279         if (!std::fabs(xarg->value - xarg->min) &&
0280                 !std::fabs(yarg->value - yarg->min) &&
0281                 !std::fabs(warg->value - warg->max) &&
0282                 !std::fabs(harg->value - harg->max))
0283             return false;
0284 
0285         xarg->value = xarg->min;
0286         yarg->value = yarg->min;
0287         warg->value = warg->max;
0288         harg->value = harg->max;
0289 
0290         m_Camera->sendNewProperty(frameProp);
0291         return true;
0292     }
0293 
0294     return false;
0295 }
0296 
0297 bool CameraChip::setFrame(int x, int y, int w, int h, bool force)
0298 {
0299     INumberVectorProperty *frameProp = nullptr;
0300 
0301     switch (m_Type)
0302     {
0303         case PRIMARY_CCD:
0304             frameProp = m_Camera->getNumber("CCD_FRAME");
0305             break;
0306 
0307         case GUIDE_CCD:
0308             frameProp = m_Camera->getNumber("GUIDER_FRAME");
0309             break;
0310     }
0311 
0312     if (frameProp == nullptr)
0313         return false;
0314 
0315     INumber *xarg = IUFindNumber(frameProp, "X");
0316     INumber *yarg = IUFindNumber(frameProp, "Y");
0317     INumber *warg = IUFindNumber(frameProp, "WIDTH");
0318     INumber *harg = IUFindNumber(frameProp, "HEIGHT");
0319 
0320     if (xarg && yarg && warg && harg)
0321     {
0322         if (!force &&
0323                 !std::fabs(xarg->value - x) &&
0324                 !std::fabs(yarg->value - y) &&
0325                 !std::fabs(warg->value - w) &&
0326                 !std::fabs(harg->value - h))
0327             return true;
0328 
0329         xarg->value = x;
0330         yarg->value = y;
0331         warg->value = w;
0332         harg->value = h;
0333 
0334         m_Camera->sendNewProperty(frameProp);
0335         return true;
0336     }
0337 
0338     return false;
0339 }
0340 
0341 bool CameraChip::capture(double exposure)
0342 {
0343     //qCDebug(KSTARS_INDI) << "IndiCCD: capture()" << (type==PRIMARY_CCD?"CCD":"Guide");
0344     INumberVectorProperty *expProp = nullptr;
0345 
0346     switch (m_Type)
0347     {
0348         case PRIMARY_CCD:
0349             expProp = m_Camera->getNumber("CCD_EXPOSURE");
0350             break;
0351 
0352         case GUIDE_CCD:
0353             expProp = m_Camera->getNumber("GUIDER_EXPOSURE");
0354             break;
0355     }
0356 
0357     if (expProp == nullptr)
0358         return false;
0359 
0360     // If we have exposure presets, let's limit the exposure value
0361     // to the preset values if it falls within their range of max/min
0362     if (Options::forceDSLRPresets())
0363     {
0364         QMap<QString, double> exposurePresets = m_Camera->getExposurePresets();
0365         if (!exposurePresets.isEmpty())
0366         {
0367             double min, max;
0368             QPair<double, double> minmax = m_Camera->getExposurePresetsMinMax();
0369             min = minmax.first;
0370             max = minmax.second;
0371             if (exposure > min && exposure < max)
0372             {
0373                 double diff = 1e6;
0374                 double closestMatch = exposure;
0375                 for (const auto &oneValue : exposurePresets.values())
0376                 {
0377                     double newDiff = std::fabs(exposure - oneValue);
0378                     if (newDiff < diff)
0379                     {
0380                         closestMatch = oneValue;
0381                         diff = newDiff;
0382                     }
0383                 }
0384 
0385                 qCDebug(KSTARS_INDI) << "Requested exposure" << exposure << "closes match is" << closestMatch;
0386                 exposure = closestMatch;
0387             }
0388         }
0389     }
0390 
0391     // clone the INumberVectorProperty, to avoid modifications to the same
0392     // property from two threads
0393     INumber n;
0394     strcpy(n.name, expProp->np[0].name);
0395     n.value = exposure;
0396 
0397     std::unique_ptr<INumberVectorProperty> newExpProp(new INumberVectorProperty());
0398     strncpy(newExpProp->device, expProp->device, MAXINDIDEVICE);
0399     strncpy(newExpProp->name, expProp->name, MAXINDINAME);
0400     strncpy(newExpProp->label, expProp->label, MAXINDILABEL);
0401     newExpProp->np = &n;
0402     newExpProp->nnp = 1;
0403 
0404     m_Camera->sendNewProperty(newExpProp.get());
0405 
0406     return true;
0407 }
0408 
0409 bool CameraChip::abortExposure()
0410 {
0411     if (!m_Camera) return false;
0412     ISwitchVectorProperty *abortProp = nullptr;
0413 
0414     switch (m_Type)
0415     {
0416         case PRIMARY_CCD:
0417             abortProp = m_Camera->getSwitch("CCD_ABORT_EXPOSURE");
0418             break;
0419 
0420         case GUIDE_CCD:
0421             abortProp = m_Camera->getSwitch("GUIDER_ABORT_EXPOSURE");
0422             break;
0423     }
0424 
0425     if (abortProp == nullptr)
0426         return false;
0427 
0428     ISwitch *abort = IUFindSwitch(abortProp, "ABORT");
0429 
0430     if (abort == nullptr)
0431         return false;
0432 
0433     abort->s = ISS_ON;
0434 
0435     m_Camera->sendNewProperty(abortProp);
0436 
0437     return true;
0438 }
0439 bool CameraChip::canBin() const
0440 {
0441     return CanBin && m_Camera->getEncodingFormat() != QLatin1String("Native");
0442 }
0443 
0444 void CameraChip::setCanBin(bool value)
0445 {
0446     CanBin = value;
0447 }
0448 
0449 bool CameraChip::canSubframe() const
0450 {
0451     return CanSubframe && m_Camera->getEncodingFormat() != QLatin1String("Native");
0452 }
0453 
0454 void CameraChip::setCanSubframe(bool value)
0455 {
0456     CanSubframe = value;
0457 }
0458 bool CameraChip::canAbort() const
0459 {
0460     return CanAbort;
0461 }
0462 
0463 void CameraChip::setCanAbort(bool value)
0464 {
0465     CanAbort = value;
0466 }
0467 
0468 const QSharedPointer<FITSData> &CameraChip::getImageData() const
0469 {
0470     return imageData;
0471 }
0472 
0473 int CameraChip::getISOIndex() const
0474 {
0475     auto isoProp = m_Camera->getSwitch("CCD_ISO");
0476 
0477     if (!isoProp)
0478         return -1;
0479 
0480     return isoProp->findOnSwitchIndex();
0481 }
0482 
0483 bool CameraChip::getISOValue(QString &value) const
0484 {
0485     auto index = getISOIndex();
0486     auto list = getISOList();
0487     if (!list.isEmpty() && index >= 0 && index < list.count())
0488     {
0489         value = list[index];
0490         return true;
0491     }
0492 
0493     return false;
0494 }
0495 
0496 bool CameraChip::setISOIndex(int value)
0497 {
0498     auto isoProp = m_Camera->getSwitch("CCD_ISO");
0499 
0500     if (!isoProp)
0501         return false;
0502 
0503     isoProp->reset();
0504     isoProp->at(value)->setState(ISS_ON);
0505 
0506     m_Camera->sendNewProperty(isoProp);
0507 
0508     return true;
0509 }
0510 
0511 QStringList CameraChip::getISOList() const
0512 {
0513     QStringList isoList;
0514 
0515     auto isoProp = m_Camera->getSwitch("CCD_ISO");
0516 
0517     if (!isoProp)
0518         return isoList;
0519 
0520     for (const auto &it : *isoProp)
0521         isoList << it.getLabel();
0522 
0523     return isoList;
0524 }
0525 
0526 bool CameraChip::isCapturing()
0527 {
0528     if (!m_Camera) return false;
0529     INumberVectorProperty *expProp = nullptr;
0530 
0531     switch (m_Type)
0532     {
0533         case PRIMARY_CCD:
0534             expProp = m_Camera->getNumber("CCD_EXPOSURE");
0535             break;
0536 
0537         case GUIDE_CCD:
0538             expProp = m_Camera->getNumber("GUIDER_EXPOSURE");
0539             break;
0540     }
0541 
0542     if (expProp == nullptr)
0543         return false;
0544 
0545     return (expProp->s == IPS_BUSY);
0546 }
0547 
0548 bool CameraChip::setFrameType(const QString &name)
0549 {
0550     CCDFrameType fType = FRAME_LIGHT;
0551 
0552     if (name == "FRAME_LIGHT" || name == "Light")
0553         fType = FRAME_LIGHT;
0554     else if (name == "FRAME_DARK" || name == "Dark")
0555         fType = FRAME_DARK;
0556     else if (name == "FRAME_BIAS" || name == "Bias")
0557         fType = FRAME_BIAS;
0558     else if (name == "FRAME_FLAT" || name == "Flat")
0559         fType = FRAME_FLAT;
0560     else
0561     {
0562         qCWarning(KSTARS_INDI) << name << " frame type is unknown." ;
0563         return false;
0564     }
0565 
0566     return setFrameType(fType);
0567 }
0568 
0569 bool CameraChip::setFrameType(CCDFrameType fType)
0570 {
0571     ISwitchVectorProperty *frameProp = nullptr;
0572 
0573     if (m_Type == PRIMARY_CCD)
0574         frameProp = m_Camera->getSwitch("CCD_FRAME_TYPE");
0575     else
0576         frameProp = m_Camera->getSwitch("GUIDER_FRAME_TYPE");
0577     if (frameProp == nullptr)
0578         return false;
0579 
0580     ISwitch *ccdFrame = nullptr;
0581 
0582     if (fType == FRAME_LIGHT)
0583         ccdFrame = IUFindSwitch(frameProp, "FRAME_LIGHT");
0584     else if (fType == FRAME_DARK)
0585         ccdFrame = IUFindSwitch(frameProp, "FRAME_DARK");
0586     else if (fType == FRAME_BIAS)
0587         ccdFrame = IUFindSwitch(frameProp, "FRAME_BIAS");
0588     else if (fType == FRAME_FLAT)
0589         ccdFrame = IUFindSwitch(frameProp, "FRAME_FLAT");
0590 
0591     if (ccdFrame == nullptr)
0592         return false;
0593 
0594     if (ccdFrame->s == ISS_ON)
0595         return true;
0596 
0597     if (fType != FRAME_LIGHT)
0598         captureMode = FITS_CALIBRATE;
0599 
0600     IUResetSwitch(frameProp);
0601     ccdFrame->s = ISS_ON;
0602 
0603     m_Camera->sendNewProperty(frameProp);
0604 
0605     return true;
0606 }
0607 
0608 CCDFrameType CameraChip::getFrameType()
0609 {
0610     CCDFrameType fType               = FRAME_LIGHT;
0611     ISwitchVectorProperty *frameProp = nullptr;
0612 
0613     if (m_Type == PRIMARY_CCD)
0614         frameProp = m_Camera->getSwitch("CCD_FRAME_TYPE");
0615     else
0616         frameProp = m_Camera->getSwitch("GUIDER_FRAME_TYPE");
0617 
0618     if (frameProp == nullptr)
0619         return fType;
0620 
0621     ISwitch *ccdFrame = nullptr;
0622 
0623     ccdFrame = IUFindOnSwitch(frameProp);
0624 
0625     if (ccdFrame == nullptr)
0626     {
0627         qCWarning(KSTARS_INDI) << "ISD:CCD Cannot find active frame in CCD!";
0628         return fType;
0629     }
0630 
0631     if (!strcmp(ccdFrame->name, "FRAME_LIGHT"))
0632         fType = FRAME_LIGHT;
0633     else if (!strcmp(ccdFrame->name, "FRAME_DARK"))
0634         fType = FRAME_DARK;
0635     else if (!strcmp(ccdFrame->name, "FRAME_FLAT"))
0636         fType = FRAME_FLAT;
0637     else if (!strcmp(ccdFrame->name, "FRAME_BIAS"))
0638         fType = FRAME_BIAS;
0639 
0640     return fType;
0641 }
0642 
0643 bool CameraChip::setBinning(CCDBinType binType)
0644 {
0645     switch (binType)
0646     {
0647         case SINGLE_BIN:
0648             return setBinning(1, 1);
0649         case DOUBLE_BIN:
0650             return setBinning(2, 2);
0651         case TRIPLE_BIN:
0652             return setBinning(3, 3);
0653         case QUADRAPLE_BIN:
0654             return setBinning(4, 4);
0655     }
0656 
0657     return false;
0658 }
0659 
0660 CCDBinType CameraChip::getBinning()
0661 {
0662     CCDBinType binType             = SINGLE_BIN;
0663     INumberVectorProperty *binProp = nullptr;
0664 
0665     switch (m_Type)
0666     {
0667         case PRIMARY_CCD:
0668             binProp = m_Camera->getNumber("CCD_BINNING");
0669             break;
0670 
0671         case GUIDE_CCD:
0672             binProp = m_Camera->getNumber("GUIDER_BINNING");
0673             break;
0674     }
0675 
0676     if (binProp == nullptr)
0677         return binType;
0678 
0679     INumber *horBin = nullptr, *verBin = nullptr;
0680 
0681     horBin = IUFindNumber(binProp, "HOR_BIN");
0682     verBin = IUFindNumber(binProp, "VER_BIN");
0683 
0684     if (!horBin || !verBin)
0685         return binType;
0686 
0687     switch (static_cast<int>(horBin->value))
0688     {
0689         case 2:
0690             binType = DOUBLE_BIN;
0691             break;
0692 
0693         case 3:
0694             binType = TRIPLE_BIN;
0695             break;
0696 
0697         case 4:
0698             binType = QUADRAPLE_BIN;
0699             break;
0700 
0701         default:
0702             break;
0703     }
0704 
0705     return binType;
0706 }
0707 
0708 bool CameraChip::getBinning(int *bin_x, int *bin_y)
0709 {
0710     INumberVectorProperty *binProp = nullptr;
0711     *bin_x = *bin_y = 1;
0712 
0713     switch (m_Type)
0714     {
0715         case PRIMARY_CCD:
0716             binProp = m_Camera->getNumber("CCD_BINNING");
0717             break;
0718 
0719         case GUIDE_CCD:
0720             binProp = m_Camera->getNumber("GUIDER_BINNING");
0721             break;
0722     }
0723 
0724     if (binProp == nullptr)
0725         return false;
0726 
0727     INumber *horBin = nullptr, *verBin = nullptr;
0728 
0729     horBin = IUFindNumber(binProp, "HOR_BIN");
0730     verBin = IUFindNumber(binProp, "VER_BIN");
0731 
0732     if (!horBin || !verBin)
0733         return false;
0734 
0735     *bin_x = horBin->value;
0736     *bin_y = verBin->value;
0737 
0738     return true;
0739 }
0740 
0741 bool CameraChip::getMaxBin(int *max_xbin, int *max_ybin)
0742 {
0743     if (!max_xbin || !max_ybin)
0744         return false;
0745 
0746     INumberVectorProperty *binProp = nullptr;
0747 
0748     *max_xbin = *max_ybin = 1;
0749 
0750     switch (m_Type)
0751     {
0752         case PRIMARY_CCD:
0753             binProp = m_Camera->getNumber("CCD_BINNING");
0754             break;
0755 
0756         case GUIDE_CCD:
0757             binProp = m_Camera->getNumber("GUIDER_BINNING");
0758             break;
0759     }
0760 
0761     if (binProp == nullptr)
0762         return false;
0763 
0764     INumber *horBin = nullptr, *verBin = nullptr;
0765 
0766     horBin = IUFindNumber(binProp, "HOR_BIN");
0767     verBin = IUFindNumber(binProp, "VER_BIN");
0768 
0769     if (!horBin || !verBin)
0770         return false;
0771 
0772     *max_xbin = horBin->max;
0773     *max_ybin = verBin->max;
0774 
0775     return true;
0776 }
0777 
0778 bool CameraChip::setBinning(int bin_x, int bin_y)
0779 {
0780     INumberVectorProperty *binProp = nullptr;
0781 
0782     switch (m_Type)
0783     {
0784         case PRIMARY_CCD:
0785             binProp = m_Camera->getNumber("CCD_BINNING");
0786             break;
0787 
0788         case GUIDE_CCD:
0789             binProp = m_Camera->getNumber("GUIDER_BINNING");
0790             break;
0791     }
0792 
0793     if (binProp == nullptr)
0794         return false;
0795 
0796     INumber *horBin = IUFindNumber(binProp, "HOR_BIN");
0797     INumber *verBin = IUFindNumber(binProp, "VER_BIN");
0798 
0799     if (!horBin || !verBin)
0800         return false;
0801 
0802     if (!std::fabs(horBin->value - bin_x) && !std::fabs(verBin->value - bin_y))
0803         return true;
0804 
0805     if (bin_x > horBin->max || bin_y > verBin->max)
0806         return false;
0807 
0808     horBin->value = bin_x;
0809     verBin->value = bin_y;
0810 
0811     m_Camera->sendNewProperty(binProp);
0812 
0813     return true;
0814 }
0815 
0816 }