You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kstars/kstars/imagesequence.cpp

681 lines
17 KiB

/* Image Sequence
Capture image sequence from an imaging device.
Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
This application is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
*/
#include "imagesequence.h"
#include "kstars.h"
#include "indidriver.h"
#include "indielement.h"
#include "indiproperty.h"
#include "indimenu.h"
#include "indidevice.h"
#include "indistd.h"
#include "devicemanager.h"
#include "Options.h"
#include <kdebug.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <klineedit.h>
#include <kprogress.h>
#include <knuminput.h>
#include <tqtimer.h>
#include <tqcombobox.h>
#include <tqpushbutton.h>
#include <tqlabel.h>
#include <tqcheckbox.h>
#include <tqstringlist.h>
#define RETRY_MAX 12
#define RETRY_PERIOD 5000
imagesequence::imagesequence(TQWidget* parent, const char* name, bool modal, WFlags fl)
: imgSequenceDlg(parent,name, modal,fl)
{
ksw = (KStars *) parent;
INDIMenu *devMenu = ksw->getINDIMenu();
if (devMenu)
{
connect (devMenu, TQT_SIGNAL(newDevice()), this, TQT_SLOT(newCCD()));
connect (devMenu, TQT_SIGNAL(newDevice()), this, TQT_SLOT(newFilter()));
}
seqTimer = new TQTimer(this);
setModal(false);
// Connect signals and slots
connect(startB, TQT_SIGNAL(clicked()), this, TQT_SLOT(startSequence()));
connect(stopB, TQT_SIGNAL(clicked()), this, TQT_SLOT(stopSequence()));
connect(closeB, TQT_SIGNAL(clicked()), this, TQT_SLOT(close()));
connect(seqTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(prepareCapture()));
connect(CCDCombo, TQT_SIGNAL(activated(int)), this, TQT_SLOT(checkCCD(int)));
connect(filterCombo, TQT_SIGNAL(activated(int)), this, TQT_SLOT(updateFilterCombo(int)));
active = false;
ISOStamp = false;
seqExpose = 0;
seqTotalCount = 0;
seqCurrentCount = 0;
seqDelay = 0;
lastCCD = 0;
lastFilter = 0;
stdDevCCD = NULL;
stdDevFilter = NULL;
}
imagesequence::~imagesequence()
{
}
bool imagesequence::updateStatus()
{
bool result;
result = setupCCDs();
setupFilters();
// If everything okay, let's show the dialog
return result;
}
void imagesequence::newCCD()
{
// Only update when it's visible
if (isVisible())
setupCCDs();
}
void imagesequence::newFilter()
{
// Only update when it's visible
if (isVisible())
setupFilters();
}
bool imagesequence::setupCCDs()
{
bool imgDeviceFound (false);
INDI_P *imgProp;
INDIMenu *devMenu = ksw->getINDIMenu();
if (devMenu == NULL)
return false;
CCDCombo->clear();
for (uint i=0; i < devMenu->mgr.count(); i++)
{
for (uint j=0; j < devMenu->mgr.at(i)->indi_dev.count(); j++)
{
imgProp = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("CCD_EXPOSE_DURATION");
if (!imgProp)
continue;
imgDeviceFound = true;
if (devMenu->mgr.at(i)->indi_dev.at(j)->label.isEmpty())
devMenu->mgr.at(i)->indi_dev.at(j)->label = devMenu->mgr.at(i)->indi_dev.at(j)->name;
CCDCombo->insertItem(devMenu->mgr.at(i)->indi_dev.at(j)->label);
}
}
if (imgDeviceFound)
{
CCDCombo->setCurrentItem(lastCCD);
currentCCD = CCDCombo->currentText();
}
else return false;
if (!verifyCCDIntegrity())
{
stopSequence();
return false;
}
else
{
INDI_P *exposeProp;
INDI_E *exposeElem;
exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSE_DURATION");
if (!exposeProp)
{
KMessageBox::error(this, i18n("Device does not support CCD_EXPOSE_DURATION property."));
return false;
}
exposeElem = exposeProp->findElement("EXPOSE_DURATION");
if (!exposeElem)
{
KMessageBox::error(this, i18n("CCD_EXPOSE_DURATION property is missing DURATION element."));
return false;
}
exposureIN->setValue(exposeElem->value);
}
return true;
}
bool imagesequence::setupFilters()
{
bool filterDeviceFound(false);
INDI_P *filterProp;
INDIMenu *devMenu = ksw->getINDIMenu();
if (devMenu == NULL)
return false;
filterCombo->clear();
filterPosCombo->clear();
filterCombo->insertItem(i18n("None"));
// Second step is to check for filter wheel, it is only optional.
for (uint i=0; i < devMenu->mgr.count(); i++)
{
for (uint j=0; j < devMenu->mgr.at(i)->indi_dev.count(); j++)
{
filterProp = devMenu->mgr.at(i)->indi_dev.at(j)->findProp("FILTER_SLOT");
if (!filterProp)
continue;
filterDeviceFound = true;
if (devMenu->mgr.at(i)->indi_dev.at(j)->label.isEmpty())
devMenu->mgr.at(i)->indi_dev.at(j)->label = devMenu->mgr.at(i)->indi_dev.at(j)->name;
filterCombo->insertItem(devMenu->mgr.at(i)->indi_dev.at(j)->label);
}
}
// If we found device, let's populate filters combo with aliases assigned to filter numbers
// In Configure INDI
if (filterDeviceFound)
{
filterCombo->setCurrentItem(lastFilter);
currentFilter = filterCombo->currentText();
updateFilterCombo(lastFilter);
return true;
}
else
return false;
}
void imagesequence::resetButtons()
{
startB->setEnabled(true);
stopB->setEnabled(false);
}
void imagesequence::startSequence()
{
if (active)
stopSequence();
// Let's find out which device has been selected and that it's connected.
if (!verifyCCDIntegrity())
return;
// Get expose paramater
active = true;
ISOStamp = ISOCheck->isChecked() ? true : false;
seqExpose = exposureIN->value();
seqTotalCount = countIN->value();
seqCurrentCount = 0;
seqDelay = delayIN->value() * 1000; /* in ms */
currentCCD = CCDCombo->currentText();
lastCCD = CCDCombo->currentItem();
currentFilter = filterCombo->currentText();
lastFilter = filterCombo->currentItem();
fullImgCountOUT->setText( TQString("%1").arg(seqTotalCount));
currentImgCountOUT->setText(TQString("%1").arg(seqCurrentCount));
// Ok, now let's connect signals and slots for this device
connect(stdDevCCD, TQT_SIGNAL(FITSReceived(TQString)), this, TQT_SLOT(newFITS(TQString)));
// set the progress info
imgProgress->setEnabled(true);
imgProgress->setTotalSteps(seqTotalCount);
imgProgress->setProgress(seqCurrentCount);
stdDevCCD->batchMode = true;
stdDevCCD->ISOMode = ISOStamp;
// Set this LAST
stdDevCCD->updateSequencePrefix(prefixIN->text());
// Update button status
startB->setEnabled(false);
stopB->setEnabled(true);
prepareCapture();
}
void imagesequence::stopSequence()
{
retries = 0;
seqTotalCount = 0;
seqCurrentCount = 0;
active = false;
imgProgress->setEnabled(false);
fullImgCountOUT->setText("");
currentImgCountOUT->setText("");
resetButtons();
seqTimer->stop();
if (stdDevCCD)
{
stdDevCCD->setCount = 0;
stdDevCCD->batchMode = false;
stdDevCCD->ISOMode = false;
stdDevCCD->disconnect( TQT_SIGNAL(FITSReceived(TQString)));
}
}
void imagesequence::checkCCD(int ccdNum)
{
INDI_D *idevice = NULL;
TQString targetCCD = CCDCombo->text(ccdNum);
INDIMenu *imenu = ksw->getINDIMenu();
if (!imenu)
{
KMessageBox::error(this, i18n("INDI Menu has not been initialized properly. Restart KStars."));
return;
}
idevice = imenu->findDeviceByLabel(targetCCD);
if (!idevice)
{
KMessageBox::error(this, i18n("INDI device %1 no longer exists.").arg(targetCCD));
CCDCombo->removeItem(ccdNum);
lastCCD = CCDCombo->currentItem();
if (lastCCD != -1)
checkCCD(lastCCD);
return;
}
if (!idevice->isOn())
{
KMessageBox::error(this, i18n("%1 is disconnected. Establish a connection to the device using the INDI Control Panel.").arg(targetCCD));
CCDCombo->setCurrentItem(lastCCD);
return;
}
currentCCD = targetCCD;
}
void imagesequence::newFITS(TQString deviceLabel)
{
// If the FITS is not for our device, simply ignore
if (deviceLabel != currentCCD)
return;
seqCurrentCount++;
imgProgress->setProgress(seqCurrentCount);
currentImgCountOUT->setText( TQString("%1").arg(seqCurrentCount));
// if we're done
if (seqCurrentCount == seqTotalCount)
{
stdDevCCD->batchMode = false;
stdDevCCD->ISOMode = false;
retries = 0;
seqTotalCount = 0;
seqCurrentCount = 0;
active = false;
seqTimer->stop();
if (stdDevCCD)
stdDevCCD->disconnect( TQT_SIGNAL(FITSReceived(TQString)));
resetButtons();
}
else
seqTimer->start(seqDelay);
}
bool imagesequence::verifyCCDIntegrity()
{
TQString targetCCD;
INDI_D *idevice = NULL;
INDI_P *exposeProp;
INDI_E *exposeElem;
stdDevCCD = NULL;
INDIMenu *imenu = ksw->getINDIMenu();
if (!imenu)
{
KMessageBox::error(this, i18n("INDI Menu has not been initialized properly. Restart KStars."));
return false;
}
targetCCD = CCDCombo->currentText();
if (targetCCD.isEmpty())
return false;
// #2 Check if the device exists
idevice = imenu->findDeviceByLabel(targetCCD);
if (!idevice)
{
KMessageBox::error(this, i18n("INDI device %1 no longer exists.").arg(targetCCD));
CCDCombo->removeItem(CCDCombo->currentItem());
lastCCD = CCDCombo->currentItem();
return false;
}
if (!idevice->isOn())
{
KMessageBox::error(this, i18n("%1 is disconnected. Establish a connection to the device using the INDI Control Panel.").arg(currentCCD));
return false;
}
stdDevCCD = idevice->stdDev;
exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSE_DURATION");
if (!exposeProp)
{
KMessageBox::error(this, i18n("Device does not support CCD_EXPOSE_DURATION property."));
return false;
}
exposeElem = exposeProp->findElement("EXPOSE_DURATION");
if (!exposeElem)
{
KMessageBox::error(this, i18n("CCD_EXPOSE_DURATION property is missing DURATION element."));
return false;
}
return true;
}
bool imagesequence::verifyFilterIntegrity()
{
TQString targetFilter;
INDIMenu *devMenu = ksw->getINDIMenu();
INDI_D *filterDevice (NULL);
INDI_E *filterElem(NULL);
if (devMenu == NULL)
return false;
targetFilter = filterCombo->currentText();
if (targetFilter.isEmpty() || targetFilter == i18n("None"))
{
filterPosCombo->clear();
return false;
}
// #1 Check the device exists
filterDevice = devMenu->findDeviceByLabel(targetFilter);
if (filterDevice == NULL)
{
KMessageBox::error(this, i18n("INDI device %1 no longer exists.").arg(targetFilter));
filterCombo->removeItem(filterCombo->currentItem());
filterCombo->setCurrentItem(0);
currentFilter = filterCombo->currentText();
filterPosCombo->clear();
stdDevFilter = NULL;
return false;
}
// #2 Make sure it's connected
if (!filterDevice->isOn())
{
KMessageBox::error(this, i18n("%1 is disconnected. Establish a connection to the device using the INDI Control Panel.").arg(targetFilter));
filterCombo->setCurrentItem(0);
currentFilter = filterCombo->currentText();
filterPosCombo->clear();
stdDevFilter = NULL;
return false;
}
// #3 Make sure it has FILTER_SLOT std property by searching for its TQT_SLOT element
filterElem = filterDevice->findElem("SLOT");
if (filterElem == NULL)
{
KMessageBox::error(this, i18n("Device does not support FILTER_SLOT property."));
filterCombo->setCurrentItem(0);
currentFilter = filterCombo->currentText();
filterPosCombo->clear();
stdDevFilter = NULL;
return false;
}
stdDevFilter = filterDevice->stdDev;
lastFilter = filterCombo->currentItem();
currentFilter = targetFilter;
// We're good
return true;
}
void imagesequence::prepareCapture()
{
INDI_P * tempProp(NULL);
// Do we need to select filter First??
if (currentFilter.isEmpty() || currentFilter == i18n("None"))
captureImage();
else
{
if (!verifyFilterIntegrity())
{
stopSequence();
return;
}
if ( stdDevFilter && ((tempProp = stdDevFilter->dp->findProp("FILTER_SLOT")) != NULL))
{
connect (tempProp, TQT_SIGNAL(okState()), this, TQT_SLOT(captureImage()));
selectFilter();
}
else
kdDebug() << "Error: std Filter device lost or missing FILTER_SLOT property" << endl;
}
}
void imagesequence::captureImage()
{
INDI_P * exposeProp(NULL);
INDI_E * exposeElem(NULL);
INDI_P * tempProp(NULL);
// Let's capture a new frame in acoord with the settings
// We need to take into consideration the following conditions:
// A. The device has been disconnected.
// B. The device has been lost.
// C. The property is still busy.
// D. The property has been lost.
if ( stdDevFilter && ((tempProp = stdDevFilter->dp->findProp("FILTER_SLOT")) != NULL))
tempProp->disconnect( TQT_SIGNAL (okState()));
if (!verifyCCDIntegrity())
{
stopSequence();
return;
}
exposeProp = stdDevCCD->dp->findProp("CCD_EXPOSE_DURATION");
exposeElem = exposeProp->findElement("EXPOSE_DURATION");
// disable timer until it's called again by newFITS, or called for retries
seqTimer->stop();
// Make sure it's not busy, if it is then schedual.
if (exposeProp->state == PS_BUSY)
{
retries++;
if (retries > RETRY_MAX)
{
seqTimer->stop();
KMessageBox::error(this, i18n("Device is busy and not responding."));
stopSequence();
retries = 0;
return;
}
seqTimer->start(RETRY_PERIOD);
}
// Set duration if applicable. We check the property permission, min, and max values
if (exposeProp->perm == PP_RW || exposeProp->perm == PP_WO)
{
if (seqExpose < exposeElem->min || seqExpose > exposeElem->max)
{
stopSequence();
KMessageBox::error(this, i18n("Expose duration is invalid. %1 supports expose durations from %2 to %3 seconds only.").arg(currentCCD).arg(exposeElem->min).arg(exposeElem->max));
return;
}
// we're okay, set it
exposeElem->targetValue = seqExpose;
if (exposeElem->spin_w)
{
exposeElem->spin_w->setValue(seqExpose);
exposeElem->spinChanged(seqExpose);
}
else
exposeElem->write_w->setText( TQString("%1").arg(seqExpose));
}
// We're done! Send it to the driver
exposeProp->newText();
}
void imagesequence::updateFilterCombo(int filterNum)
{
INDIMenu *devMenu = ksw->getINDIMenu();
TQStringList filterList;
INDI_E *filterElem;
unsigned int filterMax;
if (!verifyFilterIntegrity())
return;
filterList = Options::filterAlias();
filterElem = devMenu->findDeviceByLabel(filterCombo->text(filterNum))->findElem("SLOT");
filterMax = (int) filterElem->max;
// Clear combo
filterPosCombo->clear();
if (filterList.empty())
for (unsigned int i=0; i <= filterMax; i++)
filterList << TQString("%1").arg(i);
// Fill filter combo
if (filterList.count() <= filterMax)
{
filterPosCombo->insertStringList(filterList);
for (unsigned int i = filterList.count() ; i <= filterMax ; i++)
filterPosCombo->insertItem(TQString("%1").arg(i));
} else
{
// filterMax < filterList.count()
for (unsigned int i = 0 ; i <= filterMax ; i++)
filterPosCombo->insertItem(TQString("%1").arg(filterList[i]));
}
filterPosCombo->setCurrentItem(((int) filterElem->value));
}
void imagesequence::selectFilter()
{
INDI_P * filterProp(NULL);
INDI_E * filterElem(NULL);
INDI_D * filterDev(NULL);
INDIMenu *devMenu = ksw->getINDIMenu();
// Let's select a new filter in acoord with the settings
// We need to take into consideration the following conditions:
// A. The device has been disconnected.
// B. The device has been lost.
// C. The property is still busy.
// D. The property has been lost.
// We have a filter, let's check if it's valid
if (!verifyFilterIntegrity())
return;
filterDev = devMenu->findDeviceByLabel(currentFilter);
filterProp = filterDev->findProp("FILTER_SLOT");
filterElem = filterProp->findElement("SLOT");
// Do we need to change the filter position??
if (filterPosCombo->currentItem() == filterElem->read_w->text().toInt())
{
captureImage();
return;
}
if (filterProp->perm == PP_RW || filterProp->perm == PP_WO)
{
filterElem->targetValue = filterPosCombo->currentItem();
if (filterElem->spin_w)
{
filterElem->spin_w->setValue(filterElem->targetValue);
filterElem->spinChanged(filterElem->targetValue);
}
else
filterElem->write_w->setText(TQString("%1").arg(filterElem->targetValue));
// We're done! Send it to the driver
filterProp->newText();
}
}
#include "imagesequence.moc"