summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJasem Mutlaq <mutlaqja@ikarustech.com>2016-09-24 20:14:08 (GMT)
committerJasem Mutlaq <mutlaqja@ikarustech.com>2016-09-24 20:14:08 (GMT)
commitf19e281f7d0d928ffe53c9592b46bab173d7a253 (patch)
tree2bd240efffe5198628c732db29d3032eb9127cc2
parent37fecee9cca8503903c8e6e60ae74274bf5ad685 (diff)
Moving all align module files to their own dedicated directly. GUI is simplified even more
-rw-r--r--kstars/ekos/align/align.ui849
-rw-r--r--kstars/ekos/align/astrometryparser.cpp27
-rw-r--r--kstars/ekos/align/astrometryparser.h48
-rw-r--r--kstars/ekos/align/offlineastrometryparser.cpp296
-rw-r--r--kstars/ekos/align/offlineastrometryparser.h65
-rw-r--r--kstars/ekos/align/onlineastrometryparser.cpp524
-rw-r--r--kstars/ekos/align/onlineastrometryparser.h83
-rw-r--r--kstars/ekos/align/remoteastrometryparser.cpp167
-rw-r--r--kstars/ekos/align/remoteastrometryparser.h60
9 files changed, 2119 insertions, 0 deletions
diff --git a/kstars/ekos/align/align.ui b/kstars/ekos/align/align.ui
new file mode 100644
index 0000000..0faa926
--- /dev/null
+++ b/kstars/ekos/align/align.ui
@@ -0,0 +1,849 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Align</class>
+ <widget class="QWidget" name="Align">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>698</width>
+ <height>304</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <layout class="QHBoxLayout" name="topLayout">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="controlBox">
+ <property name="title">
+ <string>Solver Control</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Control:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="solveB">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Capture &amp;&amp; Solve</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="loadSlewB">
+ <property name="toolTip">
+ <string>Load a FITS image and solve. Slew mount to image central coordinates.</string>
+ </property>
+ <property name="text">
+ <string>Load &amp;&amp; Slew...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="stopB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>47</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="gotoBox">
+ <property name="toolTip">
+ <string>Select which action to perform after the captured image is solved</string>
+ </property>
+ <property name="statusTip">
+ <string>Select what action to take once a solution is found.</string>
+ </property>
+ <property name="title">
+ <string>Solver Action</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <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>
+ <item>
+ <widget class="QRadioButton" name="syncR">
+ <property name="toolTip">
+ <string>Synchronize the telescope to the solution coordinates</string>
+ </property>
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>S&amp;ync</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">gotoModeButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="slewR">
+ <property name="toolTip">
+ <string>Synchronize the telescope to the solution coordinates and then slew to the target coordinates</string>
+ </property>
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>S&amp;lew to Target</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">gotoModeButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="nothingR">
+ <property name="toolTip">
+ <string>Just solve</string>
+ </property>
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>&amp;Nothing</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">gotoModeButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="plateSolverOptionsGroup">
+ <property name="title">
+ <string>Solver Options</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>3</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>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="textLabel1_6">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="whatsThis">
+ <string/>
+ </property>
+ <property name="text">
+ <string>CCD:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="CCDCaptureCombo"/>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_7">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>Exp:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QDoubleSpinBox" name="exposureIN">
+ <property name="toolTip">
+ <string>Exposure duration in seconds</string>
+ </property>
+ <property name="minimum">
+ <double>0.100000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>60.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.500000000000000</double>
+ </property>
+ <property name="value">
+ <double>3.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="label_18">
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>Bin:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QComboBox" name="binningCombo">
+ <property name="toolTip">
+ <string>Camera binning</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="6" colspan="2">
+ <widget class="QCheckBox" name="alignDarkFrameCheck">
+ <property name="toolTip">
+ <string>Subtract dark frame. If no suitable dark frame is available, a dark frame shall be captured.</string>
+ </property>
+ <property name="text">
+ <string>Dark</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="toolTip">
+ <string>estimated RA of target center to limit the solver's search area</string>
+ </property>
+ <property name="text">
+ <string>RA:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="dmsBox" name="raBox">
+ <property name="toolTip">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="label_16">
+ <property name="toolTip">
+ <string>estimated DEC of target center to limit the solver's search area</string>
+ </property>
+ <property name="text">
+ <string>Dec:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="dmsBox" name="decBox">
+ <property name="toolTip">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Radius:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QLineEdit" name="radiusBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>The solver's search radius in degrees round the estimated RA and DEC</string>
+ </property>
+ <property name="statusTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string>30</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="6">
+ <widget class="QPushButton" name="syncBoxesB">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Fill RA &amp;amp; DEC values from telescope coordinates and update options</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="7">
+ <widget class="QPushButton" name="clearBoxesB">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ <property name="toolTip">
+ <string>Clear RA &amp; DEC values and reset solver options</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>22</width>
+ <height>22</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <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_11">
+ <item>
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Options:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="solverOptions">
+ <property name="statusTip">
+ <string>Additional options to be the solver</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <item>
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>Solver:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="onlineSolverR">
+ <property name="toolTip">
+ <string>Use online astrometry.net solver to solve the image. You must have an internet connection and a valid API key.</string>
+ </property>
+ <property name="text">
+ <string>Online</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">solverTypeGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="offlineSolverR">
+ <property name="toolTip">
+ <string>Use offline astrometry.net solver. You must install all the necessary index files for your field of view.</string>
+ </property>
+ <property name="text">
+ <string>Offline</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">solverTypeGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="remoteSolverR">
+ <property name="toolTip">
+ <string>Use astrometry solver on remote machine running INDI server.</string>
+ </property>
+ <property name="text">
+ <string>Remote</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">solverTypeGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <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>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="bottomLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Solution Coordinates</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="RotOut">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>RA:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>FOV:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Rot:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QCheckBox" name="kcfg_solverOTA">
+ <property name="toolTip">
+ <string>Use Off-Axis Guide Telescope for FOV calculations</string>
+ </property>
+ <property name="text">
+ <string>OAGT</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>DEC:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="SolverRAOut">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="SolverDecOut">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="FOVOut">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="label_11">
+ <property name="toolTip">
+ <string>East of North</string>
+ </property>
+ <property name="text">
+ <string comment="East of North">E of N</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="title">
+ <string>Telescope Coordinates</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_22">
+ <property name="toolTip">
+ <string>After telescope complete slewing, wait until it settles before capturing the next image.</string>
+ </property>
+ <property name="text">
+ <string>Settle</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>RA:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="ScopeRAOut">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>DEC:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="ScopeDecOut">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Accuracy</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QSpinBox" name="accuracySpin">
+ <property name="toolTip">
+ <string>Accuracy threshold in arcseconds between solution and target coordinates. Plate solver shall be repeatedly executed until the solution coordinates are below the accuracy threshold</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>300</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>arcsec</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QSpinBox" name="delaySpin">
+ <property name="toolTip">
+ <string>After telescope completes slewing, delay capture of next image</string>
+ </property>
+ <property name="maximum">
+ <number>5000</number>
+ </property>
+ <property name="singleStep">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_23">
+ <property name="toolTip">
+ <string>Milliseconds</string>
+ </property>
+ <property name="text">
+ <string>ms</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="polarAlignmentGroup">
+ <property name="title">
+ <string>Polar Alignment Tool</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Az Error:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="azError">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="measureAzB">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Measure Az Error</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="correctAzB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Correct Az Error</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Alt Error:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="altError">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="measureAltB">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Measure Alt Error</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="correctAltB">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Correct Alt Error</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>dmsBox</class>
+ <extends>QLineEdit</extends>
+ <header>widgets/dmsbox.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+ <buttongroups>
+ <buttongroup name="solverTypeGroup"/>
+ <buttongroup name="gotoModeButtonGroup"/>
+ </buttongroups>
+</ui>
diff --git a/kstars/ekos/align/astrometryparser.cpp b/kstars/ekos/align/astrometryparser.cpp
new file mode 100644
index 0000000..fff8ef7
--- /dev/null
+++ b/kstars/ekos/align/astrometryparser.cpp
@@ -0,0 +1,27 @@
+/* Astrometry.net Parser
+ Copyright (C) 2012 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 "astrometryparser.h"
+
+namespace Ekos
+{
+
+
+AstrometryParser::AstrometryParser()
+{
+
+}
+
+AstrometryParser::~AstrometryParser()
+{
+}
+
+}
+
+
diff --git a/kstars/ekos/align/astrometryparser.h b/kstars/ekos/align/astrometryparser.h
new file mode 100644
index 0000000..14e5b85
--- /dev/null
+++ b/kstars/ekos/align/astrometryparser.h
@@ -0,0 +1,48 @@
+/* Astrometry.net Parser
+ Copyright (C) 2012 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 ASTROMETRYPARSER_H
+#define ASTROMETRYPARSER_H
+
+#include <QObject>
+
+namespace Ekos
+{
+
+class Align;
+
+/**
+ * @class AstrometryParser
+ * AstrometryParser is an interface for online and offline astrometry parsers.
+ *
+ * @authro Jasem Mutlaq
+ */
+
+class AstrometryParser : public QObject
+{
+ Q_OBJECT
+
+public:
+ AstrometryParser();
+ virtual ~AstrometryParser();
+
+ virtual void setAlign(Align *align) = 0;
+ virtual bool init() = 0;
+ virtual void verifyIndexFiles(double fov_x, double fov_y) =0;
+ virtual bool startSovler(const QString &filename, const QStringList &args, bool generated=true) =0;
+ virtual bool stopSolver() = 0;
+
+signals:
+ void solverFinished(double orientation, double ra, double dec, double pixscale);
+ void solverFailed();
+};
+
+}
+
+#endif // ASTROMETRYPARSER_H
diff --git a/kstars/ekos/align/offlineastrometryparser.cpp b/kstars/ekos/align/offlineastrometryparser.cpp
new file mode 100644
index 0000000..9a978ea
--- /dev/null
+++ b/kstars/ekos/align/offlineastrometryparser.cpp
@@ -0,0 +1,296 @@
+/* Astrometry.net Parser
+ Copyright (C) 2012 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 <QDir>
+#include <QFileInfo>
+
+#include <KMessageBox>
+#include <KLocalizedString>
+#include "Options.h"
+
+#include "offlineastrometryparser.h"
+#include "align.h"
+
+namespace Ekos
+{
+
+OfflineAstrometryParser::OfflineAstrometryParser() : AstrometryParser()
+{
+ astrometryIndex[2.8] = "index-4200";
+ astrometryIndex[4.0] = "index-4201";
+ astrometryIndex[5.6] = "index-4202";
+ astrometryIndex[8] = "index-4203";
+ astrometryIndex[11] = "index-4204";
+ astrometryIndex[16] = "index-4205";
+ astrometryIndex[22] = "index-4206";
+ astrometryIndex[30] = "index-4207";
+ astrometryIndex[42] = "index-4208";
+ astrometryIndex[60] = "index-4209";
+ astrometryIndex[85] = "index-4210";
+ astrometryIndex[120] = "index-4211";
+ astrometryIndex[170] = "index-4212";
+ astrometryIndex[240] = "index-4213";
+ astrometryIndex[340] = "index-4214";
+ astrometryIndex[480] = "index-4215";
+ astrometryIndex[680] = "index-4216";
+ astrometryIndex[1000] = "index-4217";
+ astrometryIndex[1400] = "index-4218";
+ astrometryIndex[2000] = "index-4219";
+
+ astrometryFilesOK = false;
+}
+
+OfflineAstrometryParser::~OfflineAstrometryParser()
+{
+
+}
+
+bool OfflineAstrometryParser::init()
+{
+ if (astrometryFilesOK)
+ return true;
+
+ if (astrometryNetOK() == false)
+ {
+ if (align && align->isEnabled())
+ KMessageBox::information(NULL, i18n("Failed to find astrometry.net binaries. Please ensure astrometry.net is installed and try again."),
+ i18n("Missing astrometry files"), "missing_astrometry_binaries_warning");
+
+ return false;
+ }
+
+ astrometryFilesOK = true;
+ return true;
+}
+
+bool OfflineAstrometryParser::astrometryNetOK()
+{
+ bool solverOK=false, wcsinfoOK=false;
+
+ QFileInfo solver(Options::astrometrySolver());
+ solverOK = solver.exists() && solver.isFile();
+
+ QFileInfo wcsinfo(Options::astrometryWCSInfo());
+ wcsinfoOK = wcsinfo.exists() && wcsinfo.isFile();
+
+ return (solverOK && wcsinfoOK);
+}
+
+void OfflineAstrometryParser::verifyIndexFiles(double fov_x, double fov_y)
+{
+ static double last_fov_x=0, last_fov_y=0;
+
+ if (last_fov_x == fov_x && last_fov_y == fov_y)
+ return;
+
+ last_fov_x = fov_x;
+ last_fov_y = fov_y;
+ double fov_lower = 0.10 * fov_x;
+ double fov_upper = fov_x;
+ QStringList indexFiles;
+ QString astrometryDataDir;
+ bool indexesOK = true;
+
+ if (getAstrometryDataDir(astrometryDataDir) == false)
+ return;
+
+ QStringList nameFilter("*.fits");
+ QDir directory(astrometryDataDir);
+ QStringList indexList = directory.entryList(nameFilter);
+ QString indexSearch = indexList.join(" ");
+ QString startIndex, lastIndex;
+ unsigned int missingIndexes=0;
+
+ foreach(float skymarksize, astrometryIndex.keys())
+ {
+ if (skymarksize >= fov_lower && skymarksize <= fov_upper)
+ {
+ indexFiles << astrometryIndex.value(skymarksize);
+
+ if (indexSearch.contains(astrometryIndex.value(skymarksize)) == false)
+ {
+ if (startIndex.isEmpty())
+ startIndex = astrometryIndex.value(skymarksize);
+
+ lastIndex = astrometryIndex.value(skymarksize);
+
+ indexesOK = false;
+
+ missingIndexes++;
+ }
+
+ }
+ }
+
+ if (indexesOK == false)
+ {
+ if (missingIndexes == 1)
+ align->appendLogText(i18n("Index file %1 is missing. Astrometry.net would not be able to adequately solve plates until you install the missing index files. Download the index files from http://www.astrometry.net",
+ startIndex));
+ else
+ align->appendLogText(i18n("Index files %1 to %2 are missing. Astrometry.net would not be able to adequately solve plates until you install the missing index files. Download the index files from http://www.astrometry.net",
+ startIndex, lastIndex));
+
+ }
+}
+
+bool OfflineAstrometryParser::getAstrometryDataDir(QString &dataDir)
+{
+ QFile confFile(Options::astrometryConfFile());
+
+ if (confFile.open(QIODevice::ReadOnly) == false)
+ {
+ KMessageBox::error(0, i18n("Astrometry configuration file corrupted or missing: %1\nPlease set the configuration file full path in INDI options.", Options::astrometryConfFile()));
+ return false;
+ }
+
+ QTextStream in(&confFile);
+ QString line;
+ QStringList confOptions;
+ while ( !in.atEnd() )
+ {
+ line = in.readLine();
+ if (line.startsWith("#"))
+ continue;
+
+ confOptions = line.split(" ");
+ if (confOptions.size() == 2)
+ {
+ if (confOptions[0] == "add_path")
+ {
+ dataDir = confOptions[1];
+ return true;
+ }
+ }
+ }
+
+ KMessageBox::error(0, i18n("Unable to find data dir in astrometry configuration file."));
+ return false;
+}
+
+bool OfflineAstrometryParser::startSovler(const QString &filename, const QStringList &args, bool generated)
+{
+ INDI_UNUSED(generated);
+
+ QStringList solverArgs = args;
+ solverArgs << "-W" << "/tmp/solution.wcs" << filename;
+
+ connect(&solver, SIGNAL(finished(int)), this, SLOT(solverComplete(int)));
+ connect(&solver, SIGNAL(readyReadStandardOutput()), this, SLOT(logSolver()));
+
+ fitsFile = filename;
+
+ solverTimer.start();
+
+ solver.start(Options::astrometrySolver(), solverArgs);
+
+ align->appendLogText(i18n("Starting solver..."));
+
+ if (Options::solverVerbose())
+ {
+ QString command = Options::astrometrySolver() + " " + solverArgs.join(" ");
+ align->appendLogText(command);
+ }
+
+ return true;
+}
+
+bool OfflineAstrometryParser::stopSolver()
+{
+ solver.terminate();
+ solver.disconnect();
+
+ return true;
+
+}
+
+void OfflineAstrometryParser::solverComplete(int exist_status)
+{
+ solver.disconnect();
+
+ // TODO use QTemporaryFile later
+ QFileInfo solution("/tmp/solution.wcs");
+
+ if (exist_status != 0 || solution.exists() == false)
+ {
+ align->appendLogText(i18n("Solver failed. Try again."));
+ emit solverFailed();
+
+ return;
+ }
+
+ connect(&wcsinfo, SIGNAL(finished(int)), this, SLOT(wcsinfoComplete(int)));
+
+ wcsinfo.start(Options::astrometryWCSInfo(), QStringList("/tmp/solution.wcs"));
+}
+
+void OfflineAstrometryParser::wcsinfoComplete(int exist_status)
+{
+
+ wcsinfo.disconnect();
+
+ if (exist_status != 0)
+ {
+ align->appendLogText(i18n("WCS header missing or corrupted. Solver failed."));
+ emit solverFailed();
+ return;
+ }
+
+ QString wcsinfo_stdout = wcsinfo.readAllStandardOutput();
+
+ QStringList wcskeys = wcsinfo_stdout.split(QRegExp("[\n]"));
+
+ QStringList key_value;
+
+ double ra=0, dec=0, orientation=0, pixscale=0;
+
+ foreach(QString key, wcskeys)
+ {
+ key_value = key.split(" ");
+
+ if (key_value.size() > 1)
+ {
+ if (key_value[0] == "ra_center")
+ ra = key_value[1].toDouble();
+ else if (key_value[0] == "dec_center")
+ dec = key_value[1].toDouble();
+ else if (key_value[0] == "orientation_center")
+ orientation = key_value[1].toDouble();
+ else if (key_value[0] == "pixscale")
+ pixscale = key_value[1].toDouble();
+
+ }
+
+ }
+
+ int elapsed = (int) round(solverTimer.elapsed()/1000.0);
+ align->appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed));
+
+ emit solverFinished(orientation,ra,dec, pixscale);
+
+ // Remove files left over by the solver
+ QDir dir("/tmp");
+ dir.setNameFilters(QStringList() << "fits*" << "tmp.*");
+ dir.setFilter(QDir::Files);
+ foreach(QString dirFile, dir.entryList())
+ dir.remove(dirFile);
+
+}
+
+
+void OfflineAstrometryParser::logSolver()
+{
+ if (Options::solverVerbose())
+ align->appendLogText(solver.readAll().trimmed());
+}
+
+}
+
+
+
diff --git a/kstars/ekos/align/offlineastrometryparser.h b/kstars/ekos/align/offlineastrometryparser.h
new file mode 100644
index 0000000..4a2368f
--- /dev/null
+++ b/kstars/ekos/align/offlineastrometryparser.h
@@ -0,0 +1,65 @@
+/* Astrometry.net Parser
+ Copyright (C) 2012 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 OFFLINEASTROMETRYPARSER_H
+#define OFFLINEASTROMETRYPARSER_H
+
+#include <QMap>
+#include <QProcess>
+#include <QTime>
+
+#include "astrometryparser.h"
+
+namespace Ekos
+{
+
+class Align;
+
+/**
+ * @class OfflineAstrometryParser
+ * OfflineAstrometryParser invokes the offline astrometry.net solver to find solutions to captured images.
+ *
+ * @authro Jasem Mutlaq
+ */
+
+class OfflineAstrometryParser: public AstrometryParser
+{
+ Q_OBJECT
+
+public:
+ OfflineAstrometryParser();
+ virtual ~OfflineAstrometryParser();
+
+ virtual void setAlign(Align *_align) { align = _align; }
+ virtual bool init();
+ virtual void verifyIndexFiles(double fov_x, double fov_y);
+ virtual bool startSovler(const QString &filename, const QStringList &args, bool generated=true);
+ virtual bool stopSolver();
+
+public slots:
+ void solverComplete(int exist_status);
+ void wcsinfoComplete(int exist_status);
+ void logSolver();
+
+private:
+ bool astrometryNetOK();
+ bool getAstrometryDataDir(QString &dataDir);
+
+ QMap<float, QString> astrometryIndex;
+ QProcess solver;
+ QProcess wcsinfo;
+ QTime solverTimer;
+ QString fitsFile;
+ bool astrometryFilesOK;
+ Align *align;
+};
+
+}
+
+#endif // OFFLINEASTROMETRYPARSER_H
diff --git a/kstars/ekos/align/onlineastrometryparser.cpp b/kstars/ekos/align/onlineastrometryparser.cpp
new file mode 100644
index 0000000..8fd2b61
--- /dev/null
+++ b/kstars/ekos/align/onlineastrometryparser.cpp
@@ -0,0 +1,524 @@
+/* Astrometry.net Parser
+ Copyright (C) 2012 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 <QtNetwork/QNetworkReply>
+#include <QtNetwork/QNetworkRequest>
+#include <QUrl>
+#include <QVariantMap>
+#include <QDebug>
+#include <QHttpMultiPart>
+#include <QFile>
+#include <QJsonObject>
+#include <QJsonDocument>
+
+#include <KLocalizedString>
+
+#include "onlineastrometryparser.h"
+#include "align.h"
+
+#include "Options.h"
+
+#include "fitsviewer/fitsdata.h"
+
+#define JOB_RETRY_DURATION 2000 /* 2000 ms */
+#define JOB_RETRY_ATTEMPTS 15
+#define SOLVER_RETRY_DURATION 2000 /* 2000 ms */
+#define SOLVER_RETRY_ATTEMPTS 90
+
+namespace Ekos
+{
+
+
+OnlineAstrometryParser::OnlineAstrometryParser() : AstrometryParser()
+{
+ job_retries=0;
+ solver_retries=0;
+
+ networkManager = new QNetworkAccessManager(this);
+
+ connect(this, SIGNAL(authenticateFinished()), this, SLOT(uploadFile()));
+ connect(this, SIGNAL(uploadFinished()), this, SLOT(getJobID()));
+ connect(this, SIGNAL(jobIDFinished()), this, SLOT(checkJobs()));
+ connect(this, SIGNAL(jobFinished()), this, SLOT(checkJobCalibration()));
+
+ connect(this, SIGNAL(solverFailed()), this, SLOT(resetSolver()));
+ connect(this, SIGNAL(solverFinished(double,double,double, double)), this, SLOT(resetSolver()));
+
+ downsample_factor = 0;
+ isGenerated = true;
+
+}
+
+OnlineAstrometryParser::~OnlineAstrometryParser()
+{
+}
+
+bool OnlineAstrometryParser::init()
+{
+ return true;
+}
+
+void OnlineAstrometryParser::verifyIndexFiles(double fov_x, double fov_y)
+{
+ (void)fov_x;
+ (void)fov_y;
+}
+
+bool OnlineAstrometryParser::startSovler(const QString &in_filename, const QStringList &args, bool generated)
+{
+ bool ok;
+
+ isGenerated = generated;
+
+ job_retries=0;
+
+ if (networkManager->networkAccessible() == false)
+ {
+ align->appendLogText(i18n("Error: No connection to the internet."));
+ emit solverFailed();
+ return false;
+ }
+
+ QString finalFileName(in_filename);
+
+ if (Options::astrometryUseJPEG() && in_filename.endsWith(".jpg") == false && in_filename.endsWith(".jpeg") == false)
+ {
+ QFile fitsFile(in_filename);
+ bool rc = fitsFile.open(QIODevice::ReadOnly);
+ if (rc == false)
+ {
+ align->appendLogText(i18n("Failed to open file %1. %2", filename, fitsFile.errorString()));
+ emit solverFailed();
+ return false;
+ }
+
+ FITSData *image_data = new FITSData();
+ rc = image_data->loadFITS(in_filename);
+ if (rc)
+ {
+ double image_width,image_height;
+ double bscale, bzero;
+ double min, max;
+ int val;
+
+ image_data->getDimensions(&image_width, &image_height);
+ QImage *display_image = new QImage(image_width, image_height, QImage::Format_Indexed8);
+
+ display_image->setColorCount(256);
+ for (int i=0; i < 256; i++)
+ display_image->setColor(i, qRgb(i,i,i));
+
+ image_data->getMinMax(&min, &max);
+ float *image_buffer = image_data->getImageBuffer();
+
+ bscale = 255. / (max - min);
+ bzero = (-min) * (255. / (max - min));
+
+ int w = image_width;
+ int h = image_height;
+
+ /* Fill in pixel values using indexed map, linear scale */
+ for (int j = 0; j < h; j++)
+ for (int i = 0; i < w; i++)
+ {
+ val = image_buffer[j * w + i];
+ display_image->setPixel(i, j, ((int) (val * bscale + bzero)));
+ }
+
+ finalFileName.remove(finalFileName.lastIndexOf('.'), finalFileName.length());
+ finalFileName.append(".jpg");
+ bool isFileSaved = display_image->save(finalFileName, "JPG");
+ if (isFileSaved == false)
+ {
+ align->appendLogText(i18n("Warning: Converting FITS to JPEG failed. Uploading original FITS image."));
+ finalFileName = in_filename;
+ }
+ else
+ {
+ if (isGenerated)
+ QFile::remove(in_filename);
+ else
+ isGenerated = true;
+ }
+
+ delete (display_image);
+ }
+ else
+ {
+ align->appendLogText(i18n("Warning: Converting FITS to JPEG failed. Uploading original FITS image."));
+ }
+
+ delete (image_data);
+ }
+
+ filename = finalFileName;
+ for (int i=0; i < args.count(); i++)
+ {
+ if (args[i] == "-L")
+ lowerScale = args[i+1].toDouble(&ok);
+ else if (args[i] == "-H")
+ upperScale = args[i+1].toDouble(&ok);
+ else if (args[i] == "-3")
+ center_ra = args[i+1].toDouble(&ok);
+ else if (args[i] == "-4")
+ center_dec = args[i+1].toDouble(&ok);
+ else if (args[i] == "-5")
+ radius = args[i+1].toDouble(&ok);
+ else if (args[i] == "--downsample")
+ downsample_factor = args[i+1].toInt(&ok);
+ }
+
+ connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onResult(QNetworkReply*)));
+
+ solverTimer.start();
+
+ if (sessionKey.isEmpty())
+ authenticate();
+ else
+ uploadFile();
+
+
+ return true;
+}
+
+void OnlineAstrometryParser::resetSolver()
+{
+ stopSolver();
+}
+
+bool OnlineAstrometryParser::stopSolver()
+{
+
+ workflowStage = NO_STAGE;
+ solver_retries=0;
+
+ disconnect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onResult(QNetworkReply*)));
+
+ return true;
+}
+
+void OnlineAstrometryParser::authenticate()
+{
+ QNetworkRequest request;
+ //request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+
+ QUrl url(Options::astrometryAPIURL());
+ url.setPath("/api/login");
+ request.setUrl(url);
+
+ QVariantMap apiReq;
+ apiReq.insert("apikey", Options::astrometryAPIKey());
+ //QByteArray json = serializer.serialize(apiReq, &ok);
+ QJsonObject json = QJsonObject::fromVariantMap(apiReq);
+ QJsonDocument json_doc(json);
+
+ QString json_request = QString("request-json=%1").arg(QString(json_doc.toJson(QJsonDocument::Compact)));
+
+ workflowStage = AUTH_STAGE;
+
+ networkManager->post(request, json_request.toUtf8());
+}
+
+void OnlineAstrometryParser::uploadFile()
+{
+ QNetworkRequest request;
+
+ QFile *fitsFile = new QFile(filename);
+ bool rc = fitsFile->open(QIODevice::ReadOnly);
+ if (rc == false)
+ {
+ align->appendLogText(i18n("Failed to open file %1. %2", filename, fitsFile->errorString()));
+ delete (fitsFile);
+ emit solverFailed();
+ return;
+ }
+
+ QUrl url(Options::astrometryAPIURL());
+ url.setPath("/api/upload");
+ request.setUrl(url);
+
+ QHttpMultiPart *reqEntity = new QHttpMultiPart(QHttpMultiPart::FormDataType);
+
+ QVariantMap uploadReq;
+ uploadReq.insert("publicly_visible", "n");
+ uploadReq.insert("allow_modifications", "n");
+ uploadReq.insert("session", sessionKey);
+ uploadReq.insert("allow_commercial_use", "n");
+ uploadReq.insert("scale_units", "arcminwidth");
+ uploadReq.insert("scale_type", "ul");
+ uploadReq.insert("scale_lower", lowerScale);
+ uploadReq.insert("scale_upper", upperScale);
+ uploadReq.insert("center_ra", center_ra);
+ uploadReq.insert("center_dec", center_dec);
+ uploadReq.insert("radius", radius);
+ if (downsample_factor != 0)
+ uploadReq.insert("downsample_factor", downsample_factor);
+
+ QJsonObject json = QJsonObject::fromVariantMap(uploadReq);
+ QJsonDocument json_doc(json);
+
+ QHttpPart jsonPart;
+
+ jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/text/plain");
+ jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"request-json\"");
+ jsonPart.setBody(json_doc.toJson(QJsonDocument::Compact));
+
+ QHttpPart filePart;
+
+ filePart.setHeader(QNetworkRequest::ContentTypeHeader,"application/octet-stream");
+ filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"file\"; filename=\"%1\"").arg(filename));
+ filePart.setBodyDevice(fitsFile);
+
+ // Re-parent so that it get deleted later
+ fitsFile->setParent(reqEntity);
+
+ reqEntity->append(jsonPart);
+ reqEntity->append(filePart);
+
+ workflowStage = UPLOAD_STAGE;
+
+ align->appendLogText(i18n("Uploading file..."));
+
+ QNetworkReply *reply = networkManager->post(request, reqEntity);
+
+ // The entity should be deleted when reply is finished
+ reqEntity->setParent(reply);
+}
+
+void OnlineAstrometryParser::getJobID()
+{
+ QNetworkRequest request;
+ QUrl getJobID = QUrl(QString("%1/api/submissions/%2").arg(Options::astrometryAPIURL()).arg(subID));
+
+ request.setUrl(getJobID);
+
+ workflowStage = JOB_ID_STAGE;
+
+ networkManager->get(request);
+}
+
+void OnlineAstrometryParser::checkJobs()
+{
+ //qDebug() << "with jobID " << jobID << endl;
+
+ QNetworkRequest request;
+ QUrl getJobStatus = QUrl(QString("%1/api/jobs/%2").arg(Options::astrometryAPIURL()).arg(jobID));
+
+ request.setUrl(getJobStatus);
+
+ workflowStage = JOB_STATUS_STAGE;
+
+ networkManager->get(request);
+
+}
+
+void OnlineAstrometryParser::checkJobCalibration()
+{
+
+ QNetworkRequest request;
+ QUrl getCablirationResult = QUrl(QString("%1/api/jobs/%2/calibration").arg(Options::astrometryAPIURL()).arg(jobID));
+
+ request.setUrl(getCablirationResult);
+
+ workflowStage = JOB_CALIBRATION_STAGE;
+
+ networkManager->get(request);
+}
+
+void OnlineAstrometryParser::onResult(QNetworkReply* reply)
+{
+ bool ok;
+ QJsonParseError parseError;
+ QString status;
+ QList<QVariant> jsonArray;
+ int elapsed;
+
+ if (workflowStage == NO_STAGE)
+ {
+ reply->abort();
+ return;
+ }
+
+ if (reply->error() != QNetworkReply::NoError)
+ {
+ align->appendLogText(reply->errorString());
+ emit solverFailed();
+ return;
+ }
+
+ QString json = (QString) reply->readAll();
+
+ QJsonDocument json_doc = QJsonDocument::fromJson(json.toUtf8(), &parseError);
+
+ if (parseError.error != QJsonParseError::NoError)
+ {
+ align->appendLogText(i18n("JSon error during parsing (%1).", parseError.errorString()));
+ emit solverFailed();
+ return;
+ }
+
+ QVariant json_result = json_doc.toVariant();
+ QVariantMap result = json_result.toMap();
+
+ if (Options::solverVerbose())
+ align->appendLogText(json_doc.toJson(QJsonDocument::Compact));
+
+ switch (workflowStage)
+ {
+ case AUTH_STAGE:
+ status = result["status"].toString();
+ if (status != "success")
+ {
+ align->appendLogText(i18n("Astrometry.net authentication failed. Check the validity of the Astrometry.net API Key."));
+ emit solverFailed();
+ return;
+ }
+
+ sessionKey = result["session"].toString();
+
+ if (Options::solverVerbose())
+ align->appendLogText(i18n("Authentication to astrometry.net is successful. Session: %1", sessionKey));
+
+ emit authenticateFinished();
+ break;
+
+ case UPLOAD_STAGE:
+ status = result["status"].toString();
+ if (status != "success")
+ {
+ align->appendLogText(i18n("Upload failed."));
+ emit solverFailed();
+ return;
+ }
+
+ subID = result["subid"].toInt(&ok);
+
+ if (ok == false)
+ {
+ align->appendLogText(i18n("Parsing submission ID failed."));
+ emit solverFailed();
+ return;
+ }
+
+ align->appendLogText(i18n("Upload complete. Waiting for astrometry.net solver to complete..."));
+ emit uploadFinished();
+ if (isGenerated)
+ QFile::remove(filename);
+ break;
+
+ case JOB_ID_STAGE:
+ jsonArray = result["jobs"].toList();
+
+ if (jsonArray.isEmpty())
+ jobID = 0;
+ else
+ jobID = jsonArray.first().toInt(&ok);
+
+ if (jobID == 0 || !ok)
+ {
+ if (job_retries++ < JOB_RETRY_ATTEMPTS)
+ {
+ QTimer::singleShot(JOB_RETRY_DURATION, this, SLOT(getJobID()));
+ return;
+ }
+ else
+ {
+ align->appendLogText(i18n("Failed to retrieve job ID."));
+ emit solverFailed();
+ return;
+ }
+ }
+
+ job_retries=0;
+ emit jobIDFinished();
+ break;
+
+ case JOB_STATUS_STAGE:
+ status = result["status"].toString();
+ if (status == "success")
+ emit jobFinished();
+ else if (status == "solving")
+ {
+ if (status == "solving" && solver_retries++ < SOLVER_RETRY_ATTEMPTS)
+ {
+ QTimer::singleShot(SOLVER_RETRY_DURATION, this, SLOT(checkJobs()));
+ return;
+ }
+ else
+ {
+ align->appendLogText(i18n("Solver timed out."));
+ solver_retries=0;
+ emit solverFailed();
+ return;
+ }
+ }
+ else if (status == "failure")
+ {
+ elapsed = (int) round(solverTimer.elapsed()/1000.0);
+ align->appendLogText(i18np("Solver failed after %1 second.", "Solver failed after %1 seconds.", elapsed));
+ emit solverFailed();
+ return;
+ }
+ break;
+
+ case JOB_CALIBRATION_STAGE:
+ parity = result["parity"].toDouble(&ok);
+ if (ok == false)
+ {
+ align->appendLogText(i18n("Error parsing parity."));
+ emit solverFailed();
+ return;
+ }
+ orientation = result["orientation"].toDouble(&ok);
+ if (ok == false)
+ {
+ align->appendLogText(i18n("Error parsing orientation."));
+ emit solverFailed();
+ return;
+ }
+ orientation *= parity;
+ ra = result["ra"].toDouble(&ok);
+ if (ok == false)
+ {
+ align->appendLogText(i18n("Error parsing RA."));
+ emit solverFailed();
+ return;
+ }
+ dec = result["dec"].toDouble(&ok);
+ if (ok == false)
+ {
+ align->appendLogText(i18n("Error parsing DEC."));
+ emit solverFailed();
+ return;
+ }
+
+ pixscale = result["pixscale"].toDouble(&ok);
+ if (ok == false)
+ {
+ align->appendLogText(i18n("Error parsing DEC."));
+ emit solverFailed();
+ return;
+ }
+
+ elapsed = (int) round(solverTimer.elapsed()/1000.0);
+ align->appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed));
+ emit solverFinished(orientation, ra, dec, pixscale);
+
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+}
+
+
diff --git a/kstars/ekos/align/onlineastrometryparser.h b/kstars/ekos/align/onlineastrometryparser.h
new file mode 100644
index 0000000..7f57729
--- /dev/null
+++ b/kstars/ekos/align/onlineastrometryparser.h
@@ -0,0 +1,83 @@
+/* Astrometry.net Parser
+ Copyright (C) 2012 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 ONLINEASTROMETRYPARSER_H
+#define ONLINEASTROMETRYPARSER_H
+
+#include <QTime>
+#include <QtNetwork/QNetworkAccessManager>
+
+#include "astrometryparser.h"
+
+class QNetworkReply;
+
+namespace Ekos
+{
+
+class Align;
+
+/**
+ * @class OnlineAstrometryParser
+ * OnlineAstrometryParser invokes the online services provided by astrometry.net solver to find solutions to captured images.
+ *
+ * @authro Jasem Mutlaq
+ */
+class OnlineAstrometryParser: public AstrometryParser
+{
+ Q_OBJECT
+
+public:
+ OnlineAstrometryParser();
+ virtual ~OnlineAstrometryParser();
+
+ virtual void setAlign(Align *_align) { align = _align;}
+ virtual bool init();
+ virtual void verifyIndexFiles(double fov_x, double fov_y);
+ virtual bool startSovler(const QString &filename, const QStringList &args, bool generated=true);
+ virtual bool stopSolver();
+
+ typedef enum { NO_STAGE, AUTH_STAGE, UPLOAD_STAGE, JOB_ID_STAGE, JOB_STATUS_STAGE, JOB_CALIBRATION_STAGE } WorkflowStage;
+
+public slots:
+ void onResult(QNetworkReply* reply);
+ void uploadFile();
+ void getJobID();
+ void checkJobs();
+ void checkJobCalibration();
+ void resetSolver();
+signals:
+ void authenticateFinished();
+ void uploadFinished();
+ void jobIDFinished();
+ void jobFinished();
+private:
+
+ void authenticate();
+
+ WorkflowStage workflowStage;
+ QNetworkAccessManager *networkManager;
+
+ QString sessionKey;
+ int subID;
+ int jobID;
+ int job_retries, solver_retries;
+
+ QTime solverTimer;
+ QString filename;
+ double lowerScale, upperScale, center_ra, center_dec, radius, pixscale;
+ double parity,ra,dec,orientation;
+ int downsample_factor;
+ bool isGenerated;
+ Align *align;
+
+};
+
+}
+
+#endif // ONLINEASTROMETRYPARSER_H
diff --git a/kstars/ekos/align/remoteastrometryparser.cpp b/kstars/ekos/align/remoteastrometryparser.cpp
new file mode 100644
index 0000000..1aebd7b
--- /dev/null
+++ b/kstars/ekos/align/remoteastrometryparser.cpp
@@ -0,0 +1,167 @@
+/* Astrometry.net Parser - Remote
+ Copyright (C) 2016 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 <QDir>
+
+#include <KMessageBox>
+#include <KLocalizedString>
+#include "Options.h"
+
+#include <basedevice.h>
+#include "indi/clientmanager.h"
+#include "indi/driverinfo.h"
+
+#include "remoteastrometryparser.h"
+#include "align.h"
+
+
+namespace Ekos
+{
+
+RemoteAstrometryParser::RemoteAstrometryParser() : AstrometryParser()
+{
+ currentCCD = NULL;
+ solverRunning=false;
+}
+
+RemoteAstrometryParser::~RemoteAstrometryParser()
+{
+}
+
+bool RemoteAstrometryParser::init()
+{
+ return true;
+}
+
+void RemoteAstrometryParser::verifyIndexFiles(double, double)
+{
+}
+
+bool RemoteAstrometryParser::startSovler(const QString &filename, const QStringList &args, bool generated)
+{
+ INDI_UNUSED(filename);
+ INDI_UNUSED(generated);
+
+ solverRunning = true;
+
+ ITextVectorProperty *solverSettings = currentCCD->getBaseDevice()->getText("ASTROMETRY_SETTINGS");
+ ISwitchVectorProperty *solverSwitch = currentCCD->getBaseDevice()->getSwitch("ASTROMETRY_SOLVER");
+
+ if (solverSettings == NULL || solverSwitch == NULL)
+ {
+ align->appendLogText(i18n("CCD does not support remote solver."));
+ emit solverFailed();
+ return false;
+ }
+
+ for (int i=0; i < solverSettings->ntp; i++)
+ {
+ if (!strcmp(solverSettings->tp[i].name, "ASTROMETRY_SETTINGS_BINARY"))
+ IUSaveText(&solverSettings->tp[i], Options::astrometrySolver().toLatin1().constData());
+ else if (!strcmp(solverSettings->tp[i].name, "ASTROMETRY_SETTINGS_OPTIONS"))
+ IUSaveText(&solverSettings->tp[i], args.join(" ").toLatin1().constData());
+ }
+
+ currentCCD->getDriverInfo()->getClientManager()->sendNewText(solverSettings);
+
+ ISwitch *enableSW = IUFindSwitch(solverSwitch, "ASTROMETRY_SOLVER_ENABLE");
+ if (enableSW->s == ISS_OFF)
+ {
+ IUResetSwitch(solverSwitch);
+ enableSW->s = ISS_ON;
+ currentCCD->getDriverInfo()->getClientManager()->sendNewSwitch(solverSwitch);
+ }
+
+ align->appendLogText(i18n("Starting remote solver..."));
+ solverTimer.start();
+
+ return true;
+}
+
+bool RemoteAstrometryParser::stopSolver()
+{
+ // Disable solver
+ ISwitchVectorProperty *svp = currentCCD->getBaseDevice()->getSwitch("ASTROMETRY_SOLVER");
+ if (!svp)
+ return false;
+
+ ISwitch *disableSW = IUFindSwitch(svp, "ASTROMETRY_SOLVER_DISABLE");
+ if (disableSW->s == ISS_OFF)
+ {
+ IUResetSwitch(svp);
+ disableSW->s = ISS_ON;
+ currentCCD->getDriverInfo()->getClientManager()->sendNewSwitch(svp);
+ }
+
+ solverRunning=false;
+
+ return true;
+
+}
+
+void RemoteAstrometryParser::setCCD(ISD::CCD *ccd)
+{
+ if (ccd == currentCCD)
+ return;
+
+ currentCCD = ccd;
+
+ currentCCD->disconnect(this);
+
+ connect(currentCCD, SIGNAL(switchUpdated(ISwitchVectorProperty*)), this, SLOT(checkCCDStatus(ISwitchVectorProperty*)));
+ connect(currentCCD, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SLOT(checkCCDResults(INumberVectorProperty*)));
+}
+
+void RemoteAstrometryParser::checkCCDStatus(ISwitchVectorProperty *svp)
+{
+ if (solverRunning == false || strcmp(svp->name, "ASTROMETRY_SOLVER"))
+ return;
+
+ if (svp->s == IPS_ALERT)
+ {
+ stopSolver();
+ align->appendLogText(i18n("Solver failed. Try again."));
+ emit solverFailed();
+ return;
+ }
+}
+
+void RemoteAstrometryParser::checkCCDResults(INumberVectorProperty * nvp)
+{
+ if (solverRunning == false || strcmp(nvp->name, "ASTROMETRY_RESULTS") || nvp->s != IPS_OK)
+ return;
+
+ double pixscale, ra, de, orientation;
+ pixscale=ra=de=orientation=-1000;
+
+ for (int i=0; i < nvp->nnp; i++)
+ {
+ if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_PIXSCALE"))
+ pixscale = nvp->np[i].value;
+ else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_ORIENTATION"))
+ orientation = nvp->np[i].value;
+ else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_RA"))
+ ra = nvp->np[i].value;
+ else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_DE"))
+ de = nvp->np[i].value;
+ }
+
+ if (pixscale != -1000 && ra != -1000 && de != -1000 && orientation != -1000)
+ {
+ int elapsed = (int) round(solverTimer.elapsed()/1000.0);
+ align->appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed));
+ stopSolver();
+ emit solverFinished(orientation, ra, de, pixscale);
+ }
+}
+
+}
+
+
+
diff --git a/kstars/ekos/align/remoteastrometryparser.h b/kstars/ekos/align/remoteastrometryparser.h
new file mode 100644
index 0000000..dcdafc2
--- /dev/null
+++ b/kstars/ekos/align/remoteastrometryparser.h
@@ -0,0 +1,60 @@
+/* Astrometry.net Parser - Remote
+ Copyright (C) 2016 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 REMOTEASTROMETRYPARSER_H
+#define REMOTEASTROMETRYPARSER_H
+
+#include "indi/indiccd.h"
+#include "astrometryparser.h"
+
+namespace Ekos
+{
+
+class Align;
+
+/**
+ * @class RemoteAstrometryParser
+ * RemoteAstrometryParser invokes the remote astrometry.net solver in the remote CCD driver to solve the captured image.
+ * The offline astrometry.net plus index files must be installed and configured on the remote host running the INDI CCD driver.
+ *
+ * @authro Jasem Mutlaq
+ */
+
+class RemoteAstrometryParser: public AstrometryParser
+{
+ Q_OBJECT
+
+public:
+ RemoteAstrometryParser();
+ virtual ~RemoteAstrometryParser();
+
+ virtual void setAlign(Align *_align) { align = _align; }
+ virtual bool init();
+ virtual void verifyIndexFiles(double fov_x, double fov_y);
+ virtual bool startSovler(const QString &filename, const QStringList &args, bool generated=true);
+ virtual bool stopSolver();
+
+ void setCCD(ISD::CCD *ccd);
+
+public slots:
+ void checkCCDStatus(ISwitchVectorProperty * svp);
+ void checkCCDResults(INumberVectorProperty * nvp);
+
+private:
+ ISD::CCD *currentCCD;
+ bool solverRunning;
+ bool captureRunning;
+ Align *align;
+ QTime solverTimer;
+
+};
+
+}
+
+#endif // REMOTEASTROMETRYPARSER_H