Commit 572b4f5e authored by Jasem Mutlaq's avatar Jasem Mutlaq

Add rotator support to Ekos

parent 1c9b5c68
......@@ -134,6 +134,7 @@ if (INDI_FOUND)
ekos/capture/capture.ui
ekos/capture/calibrationoptions.ui
ekos/capture/dslrinfo.ui
ekos/capture/rotatorsettings.ui
# Align
ekos/align/align.ui
ekos/align/opsekos.ui
......@@ -172,6 +173,7 @@ if (INDI_FOUND)
ekos/capture/capture.cpp
ekos/capture/sequencejob.cpp
ekos/capture/dslrinfodialog.cpp
ekos/capture/rotatorsettings.cpp
# Scheduler
ekos/scheduler/schedulerjob.cpp
......
......@@ -44,16 +44,16 @@
#include "ui_calibrationoptions.h"
#include "auxiliary/QProgressIndicator.h"
#include "rotatorsettings.h"
#define INVALID_TEMPERATURE 10000
#define INVALID_HA 10000
#define INVALID_VALUE -1e6
#define MF_TIMER_TIMEOUT 90000
#define GD_TIMER_TIMEOUT 60000
#define MF_RA_DIFF_LIMIT 4
#define MAX_CAPTURE_RETRIES 3
// Current Sequence File Format:
#define SQ_FORMAT_VERSION 1.7
#define SQ_FORMAT_VERSION 1.8
// We accept file formats with version back to:
#define SQ_COMPAT_VERSION 1.6
......@@ -119,6 +119,8 @@ Capture::Capture()
//ADURaw1 = ADURaw2 = ExpRaw1 = ExpRaw2 = -1;
//ADUSlope = 0;
rotatorSettings = new RotatorSettings(this);
pi = new QProgressIndicator(this);
progressLayout->addWidget(pi, 0, 4, 1, 1);
......@@ -172,6 +174,7 @@ Capture::Capture()
connect(frameTypeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(checkFrameType(int)));
connect(resetFrameB, SIGNAL(clicked()), this, SLOT(resetFrame()));
connect(calibrationB, SIGNAL(clicked()), this, SLOT(openCalibrationDialog()));
connect(rotatorB, SIGNAL(clicked()), rotatorSettings, SLOT(show()));
addToQueueB->setIcon(QIcon::fromTheme("list-add", QIcon(":/icons/breeze/default/list-add.svg")));
addToQueueB->setAttribute(Qt::WA_LayoutUsesWidgetRect);
......@@ -250,6 +253,7 @@ Capture::Capture()
Capture::~Capture()
{
qDeleteAll(jobs);
delete (rotatorSettings);
}
void Capture::setDefaultCCD(QString ccd)
......@@ -1670,6 +1674,18 @@ void Capture::updateCCDTemperature(double value)
activeJob->setCurrentTemperature(value);
}
void Capture::updateRotatorNumber(INumberVectorProperty *nvp)
{
if (!strcmp(nvp->name, "ABS_ROTATOR_POSITION"))
{
// Update widget rotator position
rotatorSettings->setCurrentTicks(static_cast<int32_t>(nvp->np[0].value));
if (activeJob && (activeJob->getStatus() == SequenceJob::JOB_ABORTED || activeJob->getStatus() == SequenceJob::JOB_IDLE))
activeJob->setCurrentRotation(static_cast<int32_t>(nvp->np[0].value));
}
}
void Capture::addJob(bool preview)
{
SequenceJob *job = nullptr;
......@@ -1754,6 +1770,13 @@ void Capture::addJob(bool preview)
job->setActiveCCD(currentCCD);
job->setActiveFilter(currentFilter);
if (currentRotator && rotatorSettings->isRotationEnforced())
{
job->setActiveRotator(currentRotator);
job->setTargetRotation(rotatorSettings->getTargetRotationTicks());
job->setCurrentRotation(rotatorSettings->getCurrentRotationTicks());
}
job->setFrame(frameXIN->value(), frameYIN->value(), frameWIN->value(), frameHIN->value());
job->setRootFITSDir(fitsDir->text());
......@@ -2081,10 +2104,10 @@ void Capture::prepareJob(SequenceJob *job)
}
}
prepareFilterTemperature();
preparePreCaptureActions();
}
void Capture::prepareFilterTemperature()
void Capture::preparePreCaptureActions()
{
if (currentFilterPosition > 0)
{
......@@ -2107,7 +2130,7 @@ void Capture::prepareFilterTemperature()
if (currentCCD->hasCooler() && activeJob->getEnforceTemperature())
{
if (activeJob->getCurrentTemperature() != INVALID_TEMPERATURE &&
if (activeJob->getCurrentTemperature() != INVALID_VALUE &&
fabs(activeJob->getCurrentTemperature() - activeJob->getTargetTemperature()) >
Options::maxTemperatureDiff())
{
......@@ -2124,6 +2147,25 @@ void Capture::prepareFilterTemperature()
}
}
if (activeJob->getTargetRotation() != INVALID_VALUE)
{
activeJob->setCurrentRotation(rotatorSettings->getCurrentRotationTicks());
if (rotatorSettings->getCurrentRotationTicks() != activeJob->getTargetRotation())
{
appendLogText(i18n("Setting rotation to %1 ticks...", activeJob->getTargetRotation()));
secondsLabel->setText(i18n("Set Rotator %1...", activeJob->getTargetRotation()));
if (activeJob->isPreview() == false)
{
state = CAPTURE_SETTING_ROTATOR;
emit newStatus(Ekos::CAPTURE_SETTING_ROTATOR);
}
setBusy(true);
}
}
connect(activeJob, SIGNAL(prepareComplete()), this, SLOT(executeJob()));
activeJob->prepareCapture();
......@@ -2378,7 +2420,14 @@ void Capture::setFocusStatus(FocusState state)
void Capture::setRotator(ISD::GDInterface *newRotator)
{
currentRotator = newRotator;
connect(currentRotator, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SLOT(updateRotatorNumber(INumberVectorProperty*)));
rotatorB->setEnabled(true);
rotatorSettings->setRotator(newRotator);
INumberVectorProperty *nvp = newRotator->getBaseDevice()->getNumber("ABS_ROTATOR_POSITION");
rotatorSettings->setTicksMinMaxStep(static_cast<int32_t>(nvp->np[0].min), static_cast<int32_t>(nvp->np[0].max), static_cast<int32_t>(nvp->np[0].step));
rotatorSettings->setCurrentTicks(static_cast<int32_t>(nvp->np[0].value));
}
void Capture::setTelescope(ISD::GDInterface *newTelescope)
......@@ -2553,6 +2602,7 @@ bool Capture::processJobInfo(XMLEle *root)
{
XMLEle *ep;
XMLEle *subEP;
rotatorSettings->setRotationEnforced(false);
for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
{
......@@ -2655,6 +2705,11 @@ bool Capture::processJobInfo(XMLEle *root)
{
transferFormatCombo->setCurrentIndex(atoi(pcdataXMLEle(ep)));
}
else if (!strcmp(tagXMLEle(ep), "Rotation"))
{
rotatorSettings->setRotationEnforced(true);
rotatorSettings->setTargetRotationTicks(atoi(pcdataXMLEle(ep)));
}
else if (!strcmp(tagXMLEle(ep), "Calibration"))
{
subEP = findXMLEle(ep, "FlatSource");
......@@ -2844,7 +2899,7 @@ bool Capture::saveSequenceQueue(const QString &path)
outstream << "<W>" << job->getSubW() << "</W>" << endl;
outstream << "<H>" << job->getSubH() << "</H>" << endl;
outstream << "</Frame>" << endl;
if (job->getTargetTemperature() != INVALID_TEMPERATURE)
if (job->getTargetTemperature() != INVALID_VALUE)
outstream << "<Temperature force='" << (job->getEnforceTemperature() ? "true" : "false") << "'>"
<< job->getTargetTemperature() << "</Temperature>" << endl;
if (job->getTargetFilter() >= 0)
......@@ -2873,6 +2928,8 @@ bool Capture::saveSequenceQueue(const QString &path)
if (job->getGain() != -1)
outstream << "<Gain>" << (job->getGain()) << "</Gain>" << endl;
outstream << "<FormatIndex>" << (job->getTransforFormat()) << "</FormatIndex>" << endl;
if (job->getTargetRotation() != INVALID_VALUE)
outstream << "<Rotation>" << (job->getTargetRotation()) << "</Rotation>" << endl;
outstream << "<Calibration>" << endl;
outstream << "<FlatSource>" << endl;
......@@ -2991,6 +3048,14 @@ void Capture::syncGUIToJob(SequenceJob *job)
gainIN->setValue(job->getGain());
transferFormatCombo->setCurrentIndex(job->getTransforFormat());
if (job->getTargetRotation() != INVALID_VALUE)
{
rotatorSettings->setRotationEnforced(true);
rotatorSettings->setTargetRotationTicks(job->getTargetRotation());
}
else
rotatorSettings->setRotationEnforced(false);
}
void Capture::editJob(QModelIndex i)
......@@ -3370,12 +3435,12 @@ double Capture::getCurrentHA()
double currentRA, currentDEC;
if (currentTelescope == nullptr)
return INVALID_HA;
return INVALID_VALUE;
if (currentTelescope->getEqCoords(&currentRA, &currentDEC) == false)
{
appendLogText(i18n("Failed to retrieve telescope coordinates. Unable to calculate telescope's hour angle."));
return INVALID_HA;
return INVALID_VALUE;
}
dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
......@@ -3399,7 +3464,7 @@ bool Capture::checkMeridianFlip()
//appendLogText(i18n("Current hour angle %1", currentHA));
if (currentHA == INVALID_HA)
if (currentHA == INVALID_VALUE)
return false;
if (currentHA > meridianHours->value())
......
......@@ -30,6 +30,7 @@
class QProgressIndicator;
class QTableWidgetItem;
class KDirWatch;
class RotatorSettings;
/**
*@namespace Ekos
......@@ -391,9 +392,9 @@ class Capture : public QWidget, public Ui::Capture
void setTemperature();
/**
* @brief prepareFilterTemperature Check if we need to update filter position or CCD temperature before starting capture process
* @brief preparePreCaptureActions Check if we need to update filter position or CCD temperature before starting capture process
*/
void prepareFilterTemperature();
void preparePreCaptureActions();
// Pause Sequence Queue
void pause();
......@@ -478,6 +479,9 @@ class Capture : public QWidget, public Ui::Capture
// Observer
void showObserverDialog();
// Rotator
void updateRotatorNumber(INumberVectorProperty *nvp);
signals:
void newLog();
void checkFocus(double);
......@@ -614,6 +618,9 @@ class Capture : public QWidget, public Ui::Capture
// Post capture script
QProcess postCaptureScript;
// Rotator Settings
RotatorSettings *rotatorSettings=nullptr;
// How many images to capture before dithering operation is executed?
uint8_t ditherCounter;
......
/* Rotator Settings
Copyright (C) 2017 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 "rotatorsettings.h"
#include <indicom.h>
RotatorSettings::RotatorSettings(QWidget *parent) : QDialog(parent)
{
setupUi(this);
rotatorGauge->setFormat("%v");
rotatorGauge->setMinimum(0);
rotatorGauge->setMaximum(360);
connect(ticksSlider, SIGNAL(valueChanged(int)), ticksSpin, SLOT(setValue(int)));
connect(angleSlider, &QSlider::valueChanged, this, [this](int angle){angleSpin->setValue(angle);});
connect(setTickB, SIGNAL(clicked()), this, SLOT(gotoTicks()));
connect(setAngleB, SIGNAL(clicked()), this, SLOT(gotoAngle()));
connect(plannedTicksSpin, &QSpinBox::editingFinished, this, [this]() { enforceRotationCheck->setChecked(true);});
}
void RotatorSettings::setRotator(ISD::GDInterface *rotator)
{
currentRotator = rotator;
}
void RotatorSettings::setTicksMinMaxStep(int32_t min, int32_t max, int32_t step)
{
ticksSlider->setMinimum(min);
ticksSlider->setMaximum(max);
ticksSlider->setSingleStep(step);
ticksSpin->setMinimum(min);
ticksSpin->setMaximum(max);
ticksSpin->setSingleStep(step);
plannedTicksSpin->setMinimum(min);
plannedTicksSpin->setMaximum(max);
plannedTicksSpin->setSingleStep(step);
ticksPerDegree = max/360.0;
}
void RotatorSettings::gotoTicks()
{
int32_t ticks = ticksSpin->value();
currentRotator->runCommand(INDI_SET_ROTATOR, &ticks);
}
void RotatorSettings::gotoAngle()
{
double a=angleSpin->value();
double b=rotatorGauge->value();
double d=fabs(a-b);
double r=(d > 180) ? 360 - d : d;
int sign = (a - b >= 0 && a - b <= 180) || (a - b <=-180 && a- b>= -360) ? 1 : -1;
r *= sign;
double newTarget = (r+b) * ticksPerDegree;
if (newTarget < ticksSpin->minimum())
newTarget -= ticksSpin->minimum();
else if (newTarget > ticksSpin->maximum())
newTarget -= ticksSpin->maximum();
ticksSpin->setValue(newTarget);
gotoTicks();
}
void RotatorSettings::setCurrentTicks(int32_t ticks)
{
ticksEdit->setText(QString::number(ticks));
double angle = range360(ticks / ticksPerDegree);
angleEdit->setText(QString::number(angle, 'f', 2));
rotatorGauge->setValue(angle);
}
/* Rotator Settings
Copyright (C) 2017 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.
*/
#ifndef ROTATORSETTINGS_H
#define ROTATORSETTINGS_H
#include <QDialog>
#include "indi/indistd.h"
#include "ui_rotatorsettings.h"
class RotatorSettings : public QDialog, public Ui::RotatorDialog
{
Q_OBJECT
public:
RotatorSettings(QWidget *parent);
void setRotator(ISD::GDInterface *rotator);
void setTicksMinMaxStep(int32_t min, int32_t max, int32_t step);
void setCurrentTicks(int32_t ticks);
bool isRotationEnforced() { return enforceRotationCheck->isChecked(); }
void setRotationEnforced(bool enabled) { enforceRotationCheck->setChecked(enabled); }
int32_t getTargetRotationTicks() { return plannedTicksSpin->value(); }
void setTargetRotationTicks(int32_t value) { plannedTicksSpin->setValue(value); }
double getTargetAngle() { return angleSpin->value(); }
int32_t getCurrentRotationTicks() { return ticksEdit->text().toInt(); }
protected slots:
void gotoTicks();
void gotoAngle();
private:
ISD::GDInterface *currentRotator=nullptr;
double ticksPerDegree=0;
};
#endif // ROTATORSETTINGS_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RotatorDialog</class>
<widget class="QDialog" name="RotatorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>383</width>
<height>362</height>
</rect>
</property>
<property name="windowTitle">
<string>Rotator Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>3</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QRoundProgressBar" name="rotatorGauge" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
<property name="spacing">
<number>1</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;b&gt;Control&lt;/b&gt;:</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout" columnstretch="1,2,5,2,1">
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Ticks:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="ticksEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSlider" name="ticksSlider">
<property name="minimum">
<number>-500000</number>
</property>
<property name="maximum">
<number>500000</number>
</property>
<property name="singleStep">
<number>5000</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QSpinBox" name="ticksSpin">
<property name="maximum">
<number>100000</number>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="setTickB">
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Angle:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="angleEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QSlider" name="angleSlider">
<property name="maximum">
<number>360</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QDoubleSpinBox" name="angleSpin">
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="setAngleB">
<property name="text">
<string>Set</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;b&gt;Planning&lt;/b&gt;:</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set desired rotator ticks position before capture sequence is started.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>