summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtem Fedoskin <afedoskin3@gmail.com>2016-07-26 21:29:38 (GMT)
committerArtem Fedoskin <afedoskin3@gmail.com>2016-07-26 21:29:38 (GMT)
commit32b61546363013993a4eaaeef522f84e1067c774 (patch)
tree902f27f340c95b17e54e0b8d24beb9f60d5bb37e
parente5bf5081fc9fe5dba30d3c12c39d6c842009fdbd (diff)
Added image previewer (FITS and CR2), animation for tap and fixed some
minor UI bugs.
-rw-r--r--CMakeLists.txt23
-rw-r--r--cmake/modules/FindCFitsio.cmake10
-rw-r--r--cmake/modules/FindNova.cmake3
-rw-r--r--kstars/CMakeLists.txt40
-rw-r--r--kstars/auxiliary/kspaths.cpp7
-rw-r--r--kstars/auxiliary/kspaths.h4
-rw-r--r--kstars/fitsviewer/fitsdata.cpp2
-rw-r--r--kstars/fitsviewer/fitsdata.h2
-rw-r--r--kstars/fitsviewer/fitsdatalite.cpp2130
-rw-r--r--kstars/fitsviewer/fitsdatalite.h224
-rw-r--r--kstars/fitsviewer/fitsview.h2
-rw-r--r--kstars/fitsviewer/fitsviewlite.cpp724
-rw-r--r--kstars/fitsviewer/fitsviewlite.h174
-rw-r--r--kstars/indi/clientmanagerlite.cpp302
-rw-r--r--kstars/indi/clientmanagerlite.h51
-rw-r--r--kstars/indi/indiccd.cpp386
-rw-r--r--kstars/indi/inditelescopelite.cpp26
-rw-r--r--kstars/indi/inditelescopelite.h1
-rw-r--r--kstars/kstars.kcfg9
-rw-r--r--kstars/kstarslite.cpp18
-rw-r--r--kstars/kstarslite.h11
-rw-r--r--kstars/kstarslite/imageprovider.cpp30
-rw-r--r--kstars/kstarslite/imageprovider.h39
-rw-r--r--kstars/kstarslite/qml/indi/CMakeLists.txt7
-rw-r--r--kstars/kstarslite/qml/indi/DevicePanel.qml124
-rw-r--r--kstars/kstarslite/qml/indi/INDIControlPanel.qml39
-rw-r--r--kstars/kstarslite/qml/indi/ImagePreview.qml79
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSButtonSwitch.qml (renamed from kstars/kstarslite/qml/indi/modules/KSButton.qml)3
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSButtonsSwitchRow.qml (renamed from kstars/kstarslite/qml/indi/modules/KSButtonsRow.qml)2
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSINDINumber.qml37
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSINDINumberField.qml20
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSINDIText.qml18
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSINDITextField.qml66
-rw-r--r--kstars/kstarslite/qml/indi/modules/KSTab.qml1
-rw-r--r--kstars/kstarslite/qml/indi/modules/MotionControl.qml5
-rw-r--r--kstars/kstarslite/qml/indi/modules/Property.qml16
-rw-r--r--kstars/kstarslite/qml/main.qml104
-rw-r--r--kstars/kstarslite/qml/modules/KSPage.qml62
-rw-r--r--kstars/kstarslite/skyitems/skynodes/crosshairnode.cpp12
-rw-r--r--kstars/kstarslite/skyitems/telescopesymbolsitem.cpp37
-rw-r--r--kstars/kstarslite/skyitems/telescopesymbolsitem.h2
-rw-r--r--kstars/main.cpp6
-rw-r--r--kstars/skymaplite.cpp140
-rw-r--r--kstars/skymaplite.h28
-rw-r--r--kstars/skymapliteevents.cpp62
45 files changed, 4605 insertions, 483 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fe3bace..21b5bc3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -26,8 +26,11 @@ find_package(ECM 1.7.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${CMAKE_MODULE_PATH})
-find_package(Qt5 5.4 REQUIRED COMPONENTS Gui Qml Quick Xml Sql Svg Network PrintSupport)
-
+if(BUILD_KSTARS_LITE)
+ find_package(Qt5 5.4 REQUIRED COMPONENTS Gui Qml Quick Xml Sql Network Sensors)
+else()
+ find_package(Qt5 5.4 REQUIRED COMPONENTS Gui Qml Quick Xml Sql Svg Network PrintSupport)
+endif()
include(ECMInstallIcons)
include(ECMAddAppIcon)
include(KDEInstallDirs)
@@ -45,6 +48,16 @@ if(BUILD_KSTARS_LITE)
I18n
Plotting
)
+if(ANDROID)
+ find_package(OpenMP REQUIRED) #Needed for LibRaw
+ if(OPENMP_FOUND)
+ message("OPENMP FOUND")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
+ endif()
+ include_directories(SYSTEM ${OpenMP_INCLUDE_PATH})
+endif()
else(BUILD_KSTARS_LITE)
find_package(KF5 REQUIRED COMPONENTS
Config
@@ -65,7 +78,11 @@ find_package(Eigen3 REQUIRED)
add_definitions(${EIGEN_DEFINITIONS})
include_directories(${EIGEN3_INCLUDE_DIR})
-find_package(CFitsio)
+if(BUILD_KSTARS_LITE)
+ find_package(CFitsio REQUIRED)
+else()
+ find_package(CFitsio)
+endif()
if (CFITSIO_FOUND)
set(HAVE_CFITSIO 1)
else(CFITSIO_FOUND)
diff --git a/cmake/modules/FindCFitsio.cmake b/cmake/modules/FindCFitsio.cmake
index 826072f..3ff6d6d 100644
--- a/cmake/modules/FindCFitsio.cmake
+++ b/cmake/modules/FindCFitsio.cmake
@@ -28,9 +28,13 @@ else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES)
endif (NOT WIN32)
find_path(CFITSIO_INCLUDE_DIR fitsio.h
- ${PC_CFITSIO_INCLUDE_DIRS}
- ${_obIncDir}
- ${GNUWIN32_DIR}/include
+ if(ANDROID)
+ ${CMAKE_SOURCE_DIR}/android_lib/
+ elseif(ANDROID)
+ ${PC_CFITSIO_INCLUDE_DIRS}
+ ${_obIncDir}
+ ${GNUWIN32_DIR}/include
+ endif(ANDROID)
)
find_library(CFITSIO_LIBRARIES NAMES cfitsio libcfitsio
diff --git a/cmake/modules/FindNova.cmake b/cmake/modules/FindNova.cmake
index 6328828..6f571ef 100644
--- a/cmake/modules/FindNova.cmake
+++ b/cmake/modules/FindNova.cmake
@@ -20,6 +20,9 @@ if (NOVA_INCLUDE_DIR AND NOVA_LIBRARIES)
else (NOVA_INCLUDE_DIR AND NOVA_LIBRARIES)
find_path(NOVA_INCLUDE_DIR libnova.h
+ if(ANDROID)
+ ${CMAKE_SOURCE_DIR}/android_lib/include
+ endif(ANDROID)
PATH_SUFFIXES libnova
${_obIncDir}
${GNUWIN32_DIR}/include
diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt
index 10c64a1..9c2f420 100644
--- a/kstars/CMakeLists.txt
+++ b/kstars/CMakeLists.txt
@@ -41,7 +41,19 @@ endif(BUILD_KSTARS_LITE)
#Temporary solution to allow use of qml files from source dir DELETE
add_definitions( -DSOURCE_DIR=\"${kstars_SOURCE_DIR}\" )
-if(NOT BUILD_KSTARS_LITE)
+if(BUILD_KSTARS_LITE)
+ set (fits_SRCS
+ fitsviewer/fitsdatalite.cpp
+ fitsviewer/fitsviewlite.cpp
+ fitsviewer/bayer.c
+ )
+ if(ANDROID)
+ include_directories(${kstars_SOURCE_DIR}/android_lib/include)
+ else()
+ #if it is not Android we need only CFITSIO
+ include_directories(${CFITSIO_INCLUDE_DIR})
+ endif()
+else(BUILD_KSTARS_LITE)
if (CFITSIO_FOUND)
set (fits_SRCS
fitsviewer/fitshistogram.cpp
@@ -61,7 +73,7 @@ if(NOT BUILD_KSTARS_LITE)
)
include_directories(${CFITSIO_INCLUDE_DIR})
endif(CFITSIO_FOUND)
-endif(NOT BUILD_KSTARS_LITE)
+endif(BUILD_KSTARS_LITE)
if (INDI_FOUND)
if(BUILD_KSTARS_LITE)
@@ -540,7 +552,6 @@ set(kstars_extra_SRCS
auxiliary/ksutils.cpp
auxiliary/ksdssimage.cpp
auxiliary/ksdssdownloader.cpp
- auxiliary/qcustomplot.cpp
auxiliary/profileinfo.cpp
auxiliary/filedownloader.cpp
auxiliary/kspaths.cpp
@@ -597,6 +608,8 @@ if(BUILD_KSTARS_LITE)
#Wrappers
kstarslite/skypointlite.cpp
kstarslite/skyobjectlite.cpp
+ #ImageProvider
+ kstarslite/imageprovider.cpp
#RootNode
kstarslite/skyitems/rootnode.cpp
kstarslite/skyitems/skyopacitynode.cpp
@@ -676,17 +689,17 @@ if(BUILD_KSTARS_LITE)
kstarslite/qml/modules/TelescopeControlMenu.qml
kstarslite/qml/indi/INDIControlPanel.qml
kstarslite/qml/indi/DevicePanel.qml
+ kstarslite/qml/indi/ImagePreview.qml
kstarslite/qml/indi/modules/MotionControl.qml
kstarslite/qml/indi/modules/Led.qml
kstarslite/qml/indi/modules/Property.qml
kstarslite/qml/indi/modules/KSTab.qml
kstarslite/qml/indi/modules/KSComboBox.qml
- kstarslite/qml/indi/modules/KSButton.qml
+ kstarslite/qml/indi/modules/KSButtonSwitch.qml
kstarslite/qml/indi/modules/KSCheckBox.qml
kstarslite/qml/indi/modules/KSINDIText.qml
- kstarslite/qml/indi/modules/KSINDINumber.qml
- kstarslite/qml/indi/modules/KSINDINumberField.qml
- kstarslite/qml/indi/modules/KSButtonsRow.qml
+ kstarslite/qml/indi/modules/KSINDITextField.qml
+ kstarslite/qml/indi/modules/KSButtonsSwitchRow.qml
)
add_subdirectory(kstarslite/qml)
add_subdirectory(kstarslite/kirigami)
@@ -798,6 +811,8 @@ add_library( KStarsLib STATIC ${indi_SRCS} ${fits_SRCS} ${ekos_SRCS} ${onlinepar
${kstars_options_SRCS} ${kstars_skyobjects_SRCS} ${kstars_dialogs_SRCS} ${oal_SRCS}
${printing_SRCS} ${kstars_SRCS} ${kstarslite_SRCS})
+#target_link_libraries(KStarsLib)
+
# FIXME TODO
#target_include_directories(KStarsLib INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/>")
if(BUILD_KSTARS_LITE)
@@ -808,13 +823,19 @@ if(BUILD_KSTARS_LITE)
KF5::Plotting
KF5::ConfigGui
Qt5::Gui
- Qt5::PrintSupport
+ #Qt5::PrintSupport
Qt5::Sql
- Qt5::Svg
+ #Qt5::Svg
Qt5::Qml
Qt5::Quick
+ Qt5::Sensors
${ZLIB_LIBRARIES}
)
+ if(ANDROID)
+ add_library(RAWExtractor SHARED IMPORTED)
+ set_property(TARGET RAWExtractor PROPERTY IMPORTED_LOCATION ${kstars_SOURCE_DIR}/android_lib/libRAWExtractor.so)
+ target_link_libraries(KStarsLib RAWExtractor)
+ endif(ANDROID)
else(BUILD_KSTARS_LITE)
target_link_libraries(KStarsLib
LibKSDataHandlers
@@ -911,4 +932,3 @@ endif(NOT BUILD_KSTARS_LITE)
add_executable(kstars ${KSTARS_APP_SRCS})
target_link_libraries(kstars KStarsLib)
-
diff --git a/kstars/auxiliary/kspaths.cpp b/kstars/auxiliary/kspaths.cpp
index 52c772d..17fa37e 100644
--- a/kstars/auxiliary/kspaths.cpp
+++ b/kstars/auxiliary/kspaths.cpp
@@ -39,3 +39,10 @@ QStringList KSPaths::locateAll(QStandardPaths::StandardLocation location, const
fileName,options);
#endif
}
+
+QString KSPaths::writableLocation(QStandardPaths::StandardLocation type) {
+ QString dir;
+ if(type == QStandardPaths::GenericDataLocation) dir = "kstars";
+ return QStandardPaths::writableLocation(type) + dir;
+}
+
diff --git a/kstars/auxiliary/kspaths.h b/kstars/auxiliary/kspaths.h
index a9eba24..9f1de9e 100644
--- a/kstars/auxiliary/kspaths.h
+++ b/kstars/auxiliary/kspaths.h
@@ -36,8 +36,6 @@ public:
QStandardPaths::LocateOptions options = QStandardPaths::LocateFile);
static QStringList locateAll(QStandardPaths::StandardLocation, const QString &fileNames,
QStandardPaths::LocateOptions options = QStandardPaths::LocateFile);
- static inline QString writableLocation(QStandardPaths::StandardLocation type) {
- return QStandardPaths::writableLocation(type) + "/kstars/";
- }
+ static QString writableLocation(QStandardPaths::StandardLocation type);
};
#endif
diff --git a/kstars/fitsviewer/fitsdata.cpp b/kstars/fitsviewer/fitsdata.cpp
index 6f229e8..7610d01 100644
--- a/kstars/fitsviewer/fitsdata.cpp
+++ b/kstars/fitsviewer/fitsdata.cpp
@@ -29,7 +29,9 @@
#include <QFile>
#include <QProgressDialog>
+#ifndef KSTARS_LITE
#include <KMessageBox>
+#endif
#include <KLocalizedString>
#ifdef HAVE_WCSLIB
diff --git a/kstars/fitsviewer/fitsdata.h b/kstars/fitsviewer/fitsdata.h
index c287d7b..0100eb5 100644
--- a/kstars/fitsviewer/fitsdata.h
+++ b/kstars/fitsviewer/fitsdata.h
@@ -29,7 +29,9 @@
#include <QScrollArea>
#include <QLabel>
+#ifndef KSTARS_LITE
#include <kxmlguiwindow.h>
+#endif
#ifdef WIN32
// avoid compiler warning when windows.h is included after fitsio.h
diff --git a/kstars/fitsviewer/fitsdatalite.cpp b/kstars/fitsviewer/fitsdatalite.cpp
new file mode 100644
index 0000000..6ef5e4c
--- /dev/null
+++ b/kstars/fitsviewer/fitsdatalite.cpp
@@ -0,0 +1,2130 @@
+/***************************************************************************
+ fitsdatalite.cpp - FITS Image
+ -------------------
+ begin : Fri Jul 22 2016
+ copyright : (C) 2016 by Jasem Mutlaq and Artem Fedoskin
+ email : mutlaqja@ikarustech.com, afedoskin3@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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. *
+ * *
+ * Some code fragments were adapted from Peter Kirchgessner's FITS plugin*
+ * See http://members.aol.com/pkirchg for more details. *
+ ***************************************************************************/
+
+#include <config-kstars.h>
+#include "fitsdatalite.h"
+
+#include <cmath>
+#include <cstdlib>
+#include <climits>
+
+#include <QApplication>
+#include <QLocale>
+#include <QFile>
+
+#ifndef KSTARS_LITE
+#include <KMessageBox>
+#endif
+#include <KLocalizedString>
+
+#ifdef HAVE_WCSLIB
+#include <wcshdr.h>
+#include <wcsfix.h>
+#include <wcs.h>
+#endif
+
+#include "ksutils.h"
+#include "Options.h"
+
+#define ZOOM_DEFAULT 100.0
+#define ZOOM_MIN 10
+#define ZOOM_MAX 400
+#define ZOOM_LOW_INCR 10
+#define ZOOM_HIGH_INCR 50
+
+const int MINIMUM_ROWS_PER_CENTER=3;
+
+#define DIFFUSE_THRESHOLD 0.15
+
+#define MAX_EDGE_LIMIT 10000
+#define LOW_EDGE_CUTOFF_1 50
+#define LOW_EDGE_CUTOFF_2 10
+#define MINIMUM_EDGE_LIMIT 2
+#define SMALL_SCALE_SQUARE 256
+
+bool greaterThan(Edge *s1, Edge *s2)
+{
+ //return s1->width > s2->width;
+ return s1->sum > s2->sum;
+}
+
+FITSDataLite::FITSDataLite(FITSMode fitsMode)
+{
+ channels = 0;
+ image_buffer = NULL;
+ bayer_buffer = NULL;
+ wcs_coord = NULL;
+ fptr = NULL;
+ maxHFRStar = NULL;
+ darkFrame = NULL;
+ tempFile = false;
+ starsSearched = false;
+ HasWCS = false;
+ HasDebayer=false;
+ mode = fitsMode;
+
+ debayerParams.method = DC1394_BAYER_METHOD_NEAREST;
+ debayerParams.filter = DC1394_COLOR_FILTER_RGGB;
+ debayerParams.offsetX = debayerParams.offsetY = 0;
+}
+
+FITSDataLite::~FITSDataLite()
+{
+ int status=0;
+
+ clearImageBuffers();
+
+ if (starCenters.count() > 0)
+ qDeleteAll(starCenters);
+
+ delete[] wcs_coord;
+
+ if (fptr)
+ {
+ fits_close_file(fptr, &status);
+
+ if (tempFile)
+ QFile::remove(filename);
+
+ }
+}
+
+bool FITSDataLite::loadFITS (const QString &inFilename, bool silent)
+{
+ int status=0, anynull=0;
+ long naxes[3];
+ char error_status[512];
+ QString errMessage;
+
+ qDeleteAll(starCenters);
+ starCenters.clear();
+
+ if (fptr)
+ {
+ fits_close_file(fptr, &status);
+
+ if (tempFile)
+ QFile::remove(filename);
+ }
+
+ filename = inFilename;
+
+ if (filename.startsWith("/tmp/") || filename.contains("/Temp"))
+ tempFile = true;
+ else
+ tempFile = false;
+
+ if (fits_open_image(&fptr, filename.toLatin1(), READONLY, &status))
+ {
+ fits_report_error(stderr, status);
+ fits_get_errstatus(status, error_status);
+ errMessage = i18n("Could not open file %1. Error %2", filename, QString::fromUtf8(error_status));
+ if (silent == false)
+ if (Options::fITSLogging())
+ qDebug() << errMessage;
+ return false;
+ }
+
+ if (fits_get_img_param(fptr, 3, &(stats.bitpix), &(stats.ndim), naxes, &status))
+ {
+ fits_report_error(stderr, status);
+ fits_get_errstatus(status, error_status);
+ errMessage = i18n("FITS file open error (fits_get_img_param): %1", QString::fromUtf8(error_status));
+ if (silent == false)
+ if (Options::fITSLogging())
+ qDebug() << errMessage;
+ return false;
+ }
+
+ if (stats.ndim < 2)
+ {
+ errMessage = i18n("1D FITS images are not supported in KStars.");
+ if (silent == false)
+ if (Options::fITSLogging())
+ qDebug() << errMessage;
+ return false;
+ }
+
+ switch (stats.bitpix)
+ {
+ case 8:
+ data_type = TBYTE;
+ break;
+ case 16:
+ data_type = TUSHORT;
+ break;
+ case 32:
+ data_type = TINT;
+ break;
+ case -32:
+ data_type = TFLOAT;
+ break;
+ case 64:
+ data_type = TLONGLONG;
+ break;
+ case -64:
+ data_type = TDOUBLE;
+ default:
+ errMessage = i18n("Bit depth %1 is not supported.", stats.bitpix);
+ if (silent == false)
+ if (Options::fITSLogging())
+ qDebug() << errMessage;
+ return false;
+ break;
+ }
+
+ if (stats.ndim < 3)
+ naxes[2] = 1;
+
+ if (naxes[0] == 0 || naxes[1] == 0)
+ {
+ errMessage = i18n("Image has invalid dimensions %1x%2", naxes[0], naxes[1]);
+ if (silent == false)
+ if (Options::fITSLogging())
+ qDebug() << errMessage;
+ return false;
+ }
+
+ stats.width = naxes[0];
+ stats.height = naxes[1];
+ stats.samples_per_channel = stats.width*stats.height;
+
+ clearImageBuffers();
+
+ channels = naxes[2];
+
+ image_buffer = new float[stats.samples_per_channel * channels];
+ if (image_buffer == NULL)
+ {
+ qDebug() << "FITSData: Not enough memory for image_buffer channel. Requested: " << stats.samples_per_channel * channels * sizeof(float) << " bytes.";
+ clearImageBuffers();
+ return false;
+ }
+
+ rotCounter=0;
+ flipHCounter=0;
+ flipVCounter=0;
+ long nelements = stats.samples_per_channel * channels;
+
+ if (fits_read_img(fptr, TFLOAT, 1, nelements, 0, image_buffer, &anynull, &status))
+ {
+ char errmsg[512];
+ fits_get_errstatus(status, errmsg);
+ errMessage = i18n("Error reading image: %1", QString(errmsg));
+ if (silent == false) {
+ qDebug() << errMessage;
+ }
+ fits_report_error(stderr, status);
+ if (Options::fITSLogging())
+ qDebug() << errMessage;
+ return false;
+ }
+
+ if (darkFrame != NULL)
+ subtract(darkFrame);
+
+ if (darkFrame == NULL)
+ calculateStats();
+
+ if (mode == FITS_NORMAL)
+ checkWCS();
+
+ if (checkDebayer())
+ debayer();
+
+ starsSearched = false;
+
+ return true;
+
+}
+
+int FITSDataLite::saveFITS( const QString &newFilename )
+{
+ int status=0, exttype=0;
+ long nelements;
+ fitsfile *new_fptr;
+
+ nelements = stats.samples_per_channel * channels;
+
+ /* Create a new File, overwriting existing*/
+ if (fits_create_file(&new_fptr, newFilename.toLatin1(), &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ if (fits_movabs_hdu(fptr, 1, &exttype, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ if (fits_copy_file(fptr, new_fptr, 1, 1, 1, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ if (HasDebayer)
+ {
+ if (fits_close_file(fptr, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ if (tempFile)
+ {
+ QFile::remove(filename);
+ tempFile = false;
+ }
+
+ filename = newFilename;
+
+ fptr = new_fptr;
+
+ return 0;
+ }
+
+ /* close current file */
+ if (fits_close_file(fptr, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ status=0;
+
+ if (tempFile)
+ {
+ QFile::remove(filename);
+ tempFile = false;
+ }
+
+ filename = newFilename;
+
+ fptr = new_fptr;
+
+ if (fits_movabs_hdu(fptr, 1, &exttype, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ /* Write Data */
+ if (fits_write_img(fptr, TFLOAT, 1, nelements, image_buffer, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ /* Write keywords */
+
+ // Minimum
+ if (fits_update_key(fptr, TDOUBLE, "DATAMIN", &(stats.min), "Minimum value", &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ // Maximum
+ if (fits_update_key(fptr, TDOUBLE, "DATAMAX", &(stats.max), "Maximum value", &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ // NAXIS1
+ if (fits_update_key(fptr, TUSHORT, "NAXIS1", &(stats.width), "length of data axis 1", &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ // NAXIS2
+ if (fits_update_key(fptr, TUSHORT, "NAXIS2", &(stats.height), "length of data axis 2", &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ // ISO Date
+ if (fits_write_date(fptr, &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ QString history = QString("Modified by KStars on %1").arg(QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"));
+ // History
+ if (fits_write_history(fptr, history.toLatin1(), &status))
+ {
+ fits_report_error(stderr, status);
+ return status;
+ }
+
+ int rot=0, mirror=0;
+ if (rotCounter > 0)
+ {
+ rot = (90 * rotCounter) % 360;
+ if (rot < 0)
+ rot += 360;
+ }
+ if (flipHCounter %2 !=0 || flipVCounter % 2 != 0)
+ mirror = 1;
+
+ if (rot || mirror)
+ rotWCSFITS(rot, mirror);
+
+ rotCounter=flipHCounter=flipVCounter=0;
+
+ return status;
+}
+
+void FITSDataLite::clearImageBuffers()
+{
+ delete[] image_buffer;
+ image_buffer=NULL;
+ delete [] bayer_buffer;
+ bayer_buffer=NULL;
+}
+
+int FITSDataLite::calculateMinMax(bool refresh)
+{
+ int status, nfound=0;
+
+ status = 0;
+
+ if (refresh == false)
+ {
+ if (fits_read_key_dbl(fptr, "DATAMIN", &(stats.min[0]), NULL, &status) ==0)
+ nfound++;
+
+ if (fits_read_key_dbl(fptr, "DATAMAX", &(stats.max[0]), NULL, &status) == 0)
+ nfound++;
+
+ // If we found both keywords, no need to calculate them, unless they are both zeros
+ if (nfound == 2 && !(stats.min[0] == 0 && stats.max[0] ==0))
+ return 0;
+ }
+
+ stats.min[0]= 1.0E30;
+ stats.max[0]= -1.0E30;
+
+ stats.min[1]= 1.0E30;
+ stats.max[1]= -1.0E30;
+
+ stats.min[2]= 1.0E30;
+ stats.max[2]= -1.0E30;
+
+ if (channels == 1)
+ {
+ for (unsigned int i=0; i < stats.samples_per_channel; i++)
+ {
+ if (image_buffer[i] < stats.min[0]) stats.min[0] = image_buffer[i];
+ else if (image_buffer[i] > stats.max[0]) stats.max[0] = image_buffer[i];
+ }
+ }
+ else
+ {
+ int g_offset = stats.samples_per_channel;
+ int b_offset = stats.samples_per_channel*2;
+
+ for (unsigned int i=0; i < stats.samples_per_channel; i++)
+ {
+ if (image_buffer[i] < stats.min[0])
+ stats.min[0] = image_buffer[i];
+ else if (image_buffer[i] > stats.max[0])
+ stats.max[0] = image_buffer[i];
+
+ if (image_buffer[i+g_offset] < stats.min[1])
+ stats.min[1] = image_buffer[i+g_offset];
+ else if (image_buffer[i+g_offset] > stats.max[1])
+ stats.max[1] = image_buffer[i+g_offset];
+
+ if (image_buffer[i+b_offset] < stats.min[2])
+ stats.min[2] = image_buffer[i+b_offset];
+ else if (image_buffer[i+b_offset] > stats.max[2])
+ stats.max[2] = image_buffer[i+b_offset];
+ }
+ }
+
+ //qDebug() << "DATAMIN: " << stats.min << " - DATAMAX: " << stats.max;
+ return 0;
+}
+
+
+void FITSDataLite::calculateStats(bool refresh)
+{
+ // Calculate min max
+ calculateMinMax(refresh);
+
+ // Get standard deviation and mean in one run
+ runningAverageStdDev();
+
+ stats.SNR = stats.mean[0] / stats.stddev[0];
+
+ if (refresh && markStars)
+ // Let's try to find star positions again after transformation
+ starsSearched = false;
+
+}
+
+void FITSDataLite::runningAverageStdDev()
+{
+ int m_n = 2;
+ double m_oldM=0, m_newM=0, m_oldS=0, m_newS=0;
+ m_oldM = m_newM = image_buffer[0];
+
+ for (unsigned int i=1; i < stats.samples_per_channel; i++)
+ {
+ m_newM = m_oldM + (image_buffer[i] - m_oldM)/m_n;
+ m_newS = m_oldS + (image_buffer[i] - m_oldM) * (image_buffer[i] - m_newM);
+
+ m_oldM = m_newM;
+ m_oldS = m_newS;
+ m_n++;
+ }
+
+ double variance = m_newS / (m_n -2);
+
+ stats.mean[0] = m_newM;
+ stats.stddev[0] = sqrt(variance);
+}
+
+void FITSDataLite::setMinMax(double newMin, double newMax, uint8_t channel)
+{
+ stats.min[channel] = newMin;
+ stats.max[channel] = newMax;
+}
+
+int FITSDataLite::getFITSRecord(QString &recordList, int &nkeys)
+{
+ char *header;
+ int status=0;
+
+ if (fits_hdr2str(fptr, 0, NULL, 0, &header, &nkeys, &status))
+ {
+ fits_report_error(stderr, status);
+ free(header);
+ return -1;
+ }
+
+ recordList = QString(header);
+
+ free(header);
+
+ return 0;
+}
+
+bool FITSDataLite::checkCollision(Edge* s1, Edge*s2)
+{
+ int dis; //distance
+
+ int diff_x=s1->x - s2->x;
+ int diff_y=s1->y - s2->y;
+
+ dis = std::abs( sqrt( diff_x*diff_x + diff_y*diff_y));
+ dis -= s1->width/2;
+ dis -= s2->width/2;
+
+ if (dis<=0) //collision
+ return true;
+
+ //no collision
+ return false;
+}
+
+
+/*** Find center of stars and calculate Half Flux Radius */
+void FITSDataLite::findCentroid(const QRectF &boundary, int initStdDev, int minEdgeWidth)
+{
+ //Not working yet because we don't use histogram
+ /*double threshold=0,sum=0,avg=0,min=0;
+ int starDiameter=0;
+ int pixVal=0;
+ int minimumEdgeCount = MINIMUM_EDGE_LIMIT;
+
+ double JMIndex = histogram->getJMIndex();
+ float dispersion_ratio=1.5;
+
+ QList<Edge*> edges;
+
+ if (JMIndex < DIFFUSE_THRESHOLD)
+ {
+ minEdgeWidth = JMIndex*35+1;
+ minimumEdgeCount=minEdgeWidth-1;
+ }
+ else
+ {
+ minEdgeWidth =6;
+ minimumEdgeCount=4;
+ }
+
+ while (initStdDev >= 1)
+ {
+ minEdgeWidth--;
+ minimumEdgeCount--;
+
+ minEdgeWidth = qMax(3, minEdgeWidth);
+ minimumEdgeCount = qMax(3, minimumEdgeCount);
+
+ if (JMIndex < DIFFUSE_THRESHOLD)
+ {
+ // Taking the average out seems to have better result for noisy images
+ threshold = stats.max[0] - stats.mean[0] * ( (MINIMUM_STDVAR - initStdDev)*0.5 +1);
+
+ min =stats.min[0];
+ if (threshold-min < 0)
+ {
+ threshold=stats.mean[0]* ( (MINIMUM_STDVAR - initStdDev)*0.5 +1);
+ min=0;
+ }
+
+ dispersion_ratio=1.4 - (MINIMUM_STDVAR - initStdDev)*0.08;
+ }
+ else
+ {
+ threshold = stats.mean[0]+stats.stddev[0]*initStdDev*(0.3 - (MINIMUM_STDVAR - initStdDev) * 0.05);
+ min = stats.min[0];
+ // Ratio between centeroid center and edge
+ dispersion_ratio=1.8 - (MINIMUM_STDVAR - initStdDev)*0.2;
+ }
+
+ if (Options::fITSLogging())
+ {
+ qDebug() << "SNR: " << stats.SNR;
+ qDebug() << "The threshold level is " << threshold << "(actual " << threshold-min << ") minimum edge width" << minEdgeWidth << " minimum edge limit " << minimumEdgeCount;
+ }
+
+ threshold -= min;
+
+ int subX, subY, subW, subH;
+
+ if (boundary.isNull())
+ {
+ if (mode == FITS_GUIDE)
+ {
+ subX = stats.width/10;
+ subY = stats.height/10;
+ subW = stats.width - subX;
+ subH = stats.height - subY;
+ }
+ else
+ {
+ subX = 0;
+ subY = 0;
+ subW = stats.width;
+ subH = stats.height;
+ }
+ }
+ else
+ {
+ subX = boundary.x();
+ subY = boundary.y();
+ subW = subX + boundary.width();
+ subH = subY + boundary.height();
+ }
+
+ // Detect "edges" that are above threshold
+ for (int i=subY; i < subH; i++)
+ {
+ starDiameter = 0;
+
+ for(int j=subX; j < subW; j++)
+ {
+ pixVal = image_buffer[j+(i*stats.width)] - min;
+
+ // If pixel value > threshold, let's get its weighted average
+ if ( pixVal >= threshold )
+ {
+ avg += j * pixVal;
+ sum += pixVal;
+ starDiameter++;
+ }
+ // Value < threshold but avg exists
+ else if (sum > 0)
+ {
+ // We found a potential centroid edge
+ if (starDiameter >= minEdgeWidth)
+ {
+ float center = avg/sum + 0.5;
+ if (center > 0)
+ {
+ int i_center = floor(center);
+
+ // Check if center is 10% or more brighter than edge, if not skip
+ if ( ((image_buffer[i_center+(i*stats.width)]-min) / (image_buffer[i_center+(i*stats.width)-starDiameter/2]-min) >= dispersion_ratio) &&
+ ((image_buffer[i_center+(i*stats.width)]-min) / (image_buffer[i_center+(i*stats.width)+starDiameter/2]-min) >= dispersion_ratio))
+ {
+ if (Options::fITSLogging())
+ {
+ qDebug() << "Edge center is " << image_buffer[i_center+(i*stats.width)]-min << " Edge is " << image_buffer[i_center+(i*stats.width)-starDiameter/2]-min
+ << " and ratio is " << ((image_buffer[i_center+(i*stats.width)]-min) / (image_buffer[i_center+(i*stats.width)-starDiameter/2]-min))
+ << " located at X: " << center << " Y: " << i+0.5;
+ }
+
+ Edge *newEdge = new Edge();
+
+ newEdge->x = center;
+ newEdge->y = i + 0.5;
+ newEdge->scanned = 0;
+ newEdge->val = image_buffer[i_center+(i*stats.width)] - min;
+ newEdge->width = starDiameter;
+ newEdge->HFR = 0;
+ newEdge->sum = sum;
+
+ edges.append(newEdge);
+
+ }
+ }
+ }
+
+ // Reset
+ avg= sum = starDiameter=0;
+ }
+ }
+ }
+
+ if (Options::fITSLogging())
+ qDebug() << "Total number of edges found is: " << edges.count();
+
+ // In case of hot pixels
+ if (edges.count() == 1 && initStdDev > 1)
+ {
+ initStdDev--;
+ continue;
+ }
+
+ if (edges.count() >= MAX_EDGE_LIMIT)
+ {
+ if (Options::fITSLogging())
+ qDebug() << "Too many edges, aborting... " << edges.count();
+
+ qDeleteAll(edges);
+ return;
+ }
+
+ if (edges.count() >= minimumEdgeCount)
+ break;
+
+ qDeleteAll(edges);
+ edges.clear();
+ initStdDev--;
+ }
+
+ int cen_count=0;
+ int cen_x=0;
+ int cen_y=0;
+ int cen_v=0;
+ int cen_w=0;
+ int width_sum=0;
+
+ // Let's sort edges, starting with widest
+ qSort(edges.begin(), edges.end(), greaterThan);
+
+ // Now, let's scan the edges and find the maximum centroid vertically
+ for (int i=0; i < edges.count(); i++)
+ {
+ if (Options::fITSLogging())
+ {
+ qDebug() << "# " << i << " Edge at (" << edges[i]->x << "," << edges[i]->y << ") With a value of " << edges[i]->val << " and width of "
+ << edges[i]->width << " pixels. with sum " << edges[i]->sum;
+ }
+
+ // If edge scanned already, skip
+ if (edges[i]->scanned == 1)
+ {
+ if (Options::fITSLogging())
+ qDebug() << "Skipping check for center " << i << " because it was already counted";
+ continue;
+ }
+
+ if (Options::fITSLogging())
+ qDebug() << "Invetigating edge # " << i << " now ...";
+
+ // Get X, Y, and Val of edge
+ cen_x = edges[i]->x;
+ cen_y = edges[i]->y;
+ cen_v = edges[i]->sum;
+ cen_w = edges[i]->width;
+
+ float avg_x = 0;
+ float avg_y = 0;
+
+ sum = 0;
+ cen_count=0;
+
+ // Now let's compare to other edges until we hit a maxima
+ for (int j=0; j < edges.count();j++)
+ {
+ if (edges[j]->scanned)
+ continue;
+
+ if (checkCollision(edges[j], edges[i]))
+ {
+ if (edges[j]->sum >= cen_v)
+ {
+ cen_v = edges[j]->sum;
+ cen_w = edges[j]->width;
+ }
+
+ edges[j]->scanned = 1;
+ cen_count++;
+
+ avg_x += edges[j]->x * edges[j]->val;
+ avg_y += edges[j]->y * edges[j]->val;
+ sum += edges[j]->val;
+
+ continue;
+ }
+
+ }
+
+ int cen_limit = (MINIMUM_ROWS_PER_CENTER - (MINIMUM_STDVAR - initStdDev));
+
+ if (edges.count() < LOW_EDGE_CUTOFF_1)
+ {
+ if (edges.count() < LOW_EDGE_CUTOFF_2)
+ cen_limit = 1;
+ else
+ cen_limit = 2;
+ }
+
+ if (Options::fITSLogging())
+ qDebug() << "center_count: " << cen_count << " and initstdDev= " << initStdDev << " and limit is " << cen_limit;
+
+ if (cen_limit < 1)
+ continue;
+
+ // If centroid count is within acceptable range
+ //if (cen_limit >= 2 && cen_count >= cen_limit)
+ if (cen_count >= cen_limit)
+ {
+ // We detected a centroid, let's init it
+ Edge *rCenter = new Edge();
+
+ rCenter->x = avg_x/sum;
+ rCenter->y = avg_y/sum;
+ width_sum += rCenter->width;
+ rCenter->width = cen_w;
+
+ if (Options::fITSLogging())
+ qDebug() << "Found a real center with number with (" << rCenter->x << "," << rCenter->y << ")";
+
+ // Calculate Total Flux From Center, Half Flux, Full Summation
+ double TF=0;
+ double HF=0;
+ double FSum=0;
+
+ cen_x = (int) floor(rCenter->x);
+ cen_y = (int) floor(rCenter->y);
+
+ if (cen_x < 0 || cen_x > stats.width || cen_y < 0 || cen_y > stats.height)
+ continue;
+
+
+ // Complete sum along the radius
+ //for (int k=0; k < rCenter->width; k++)
+ for (int k=rCenter->width/2; k >= -(rCenter->width/2) ; k--)
+ {
+ FSum += image_buffer[cen_x-k+(cen_y*stats.width)] - min;
+ //qDebug() << image_buffer[cen_x-k+(cen_y*stats.width)] - min;
+ }
+
+ // Half flux
+ HF = FSum / 2.0;
+
+ // Total flux starting from center
+ TF = image_buffer[cen_y * stats.width + cen_x] - min;
+
+ int pixelCounter = 1;
+
+ // Integrate flux along radius axis until we reach half flux
+ for (int k=1; k < rCenter->width/2; k++)
+ {
+ if (TF >= HF)
+ {
+ if (Options::fITSLogging())
+ qDebug() << "Stopping at TF " << TF << " after #" << k << " pixels.";
+ break;
+ }
+
+ TF += image_buffer[cen_y * stats.width + cen_x + k] - min;
+ TF += image_buffer[cen_y * stats.width + cen_x - k] - min;
+
+ pixelCounter++;
+ }
+
+ // Calculate weighted Half Flux Radius
+ rCenter->HFR = pixelCounter * (HF / TF);
+ // Store full flux
+ rCenter->val = FSum;
+
+ if (Options::fITSLogging())
+ qDebug() << "HFR for this center is " << rCenter->HFR << " pixels and the total flux is " << FSum;
+
+ starCenters.append(rCenter);
+ }
+ }
+
+ if (starCenters.count() > 1 && mode != FITS_FOCUS)
+ {
+ float width_avg = width_sum / starCenters.count();
+
+ float lsum =0, sdev=0;
+
+ foreach(Edge *center, starCenters)
+ lsum += (center->width - width_avg) * (center->width - width_avg);
+
+ sdev = (sqrt(lsum/(starCenters.count() - 1))) * 4;
+
+ // Reject stars > 4 * stddev
+ foreach(Edge *center, starCenters)
+ if (center->width > sdev)
+ starCenters.removeOne(center);
+
+ //foreach(Edge *center, starCenters)
+ //qDebug() << center->x << "," << center->y << "," << center->width << "," << center->val << endl;
+
+ }
+
+ // Release memory
+ qDeleteAll(edges);
+ */
+}
+
+double FITSDataLite::getHFR(HFRType type)
+{
+ // This method is less susceptible to noise
+ // Get HFR for the brightest star only, instead of averaging all stars
+ // It is more consistent.
+ // TODO: Try to test this under using a real CCD.
+
+ if (starCenters.size() == 0)
+ return -1;
+
+ if (type == HFR_MAX)
+ {
+ maxHFRStar = NULL;
+ int maxVal=0;
+ int maxIndex=0;
+ for (int i=0; i < starCenters.count() ; i++)
+ {
+ if (starCenters[i]->val > maxVal)
+ {
+ maxIndex=i;
+ maxVal = starCenters[i]->val;
+
+ }
+ }
+
+ maxHFRStar = starCenters[maxIndex];
+ return starCenters[maxIndex]->HFR;
+ }
+
+ double FSum=0;
+ double avgHFR=0;
+
+ // Weighted average HFR
+ for (int i=0; i < starCenters.count() ; i++)
+ {
+ avgHFR += starCenters[i]->val * starCenters[i]->HFR;
+ FSum += starCenters[i]->val;
+ }
+
+ if (FSum != 0)
+ {
+ //qDebug() << "Average HFR is " << avgHFR / FSum << endl;
+ return (avgHFR / FSum);
+ }
+ else
+ return -1;
+
+}
+
+double FITSDataLite::getHFR(int x, int y)
+{
+ if (starCenters.size() == 0)
+ return -1;
+
+ for (int i=0; i < starCenters.count() ; i++)
+ {
+ if (fabs(starCenters[i]->x-x) <= starCenters[i]->width/2 && fabs(starCenters[i]->y-y) <= starCenters[i]->width/2)
+ {
+ return starCenters[i]->HFR;
+ }
+ }
+
+ return -1;
+}
+
+void FITSDataLite::applyFilter(FITSScale type, float *image, float min, float max)
+{
+ /*if (type == FITS_NONE /* || histogram == NULL)
+ return;
+
+ double coeff=0;
+ float val=0,bufferVal =0;
+ int offset=0, row=0;
+
+
+ if (image == NULL)
+ image = image_buffer;
+
+ int width = stats.width;
+ int height = stats.height;
+
+ if (min == -1)
+ min = stats.min[0];
+ if (max == -1)
+ max = stats.max[0];
+
+ int size = stats.samples_per_channel;
+ int index=0;
+
+ switch (type)
+ {
+ case FITS_AUTO:
+ case FITS_LINEAR:
+ {
+
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ bufferVal = image[index];
+ if (bufferVal < min) bufferVal = min;
+ else if (bufferVal > max) bufferVal = max;
+ image_buffer[index] = bufferVal;
+ }
+ }
+ }
+
+ stats.min[0] = min;
+ stats.max[0] = max;
+ //runningAverageStdDev();
+ }
+ break;
+
+ case FITS_LOG:
+ {
+ coeff = max / log(1 + max);
+
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ bufferVal = image[index];
+ if (bufferVal < min) bufferVal = min;
+ else if (bufferVal > max) bufferVal = max;
+ val = (coeff * log(1 + qBound(min, image[index], max)));
+ image_buffer[index] = qBound(min, val, max);
+ }
+ }
+
+ }
+
+ stats.min[0] = min;
+ stats.max[0] = max;
+ runningAverageStdDev();
+ }
+ break;
+
+ case FITS_SQRT:
+ {
+ coeff = max / sqrt(max);
+
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ val = (int) (coeff * sqrt(qBound(min, image[index], max)));
+ image_buffer[index] = val;
+ }
+ }
+ }
+
+ stats.min[0] = min;
+ stats.max[0] = max;
+ runningAverageStdDev();
+ }
+ break;
+
+ case FITS_AUTO_STRETCH:
+ {
+ min = stats.mean[0] - stats.stddev[0];
+ max = stats.mean[0] + stats.stddev[0] * 3;
+
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ image_buffer[index] = qBound(min, image[index], max);
+ }
+ }
+ }
+
+ stats.min[0] = min;
+ stats.max[0] = max;
+ runningAverageStdDev();
+ }
+ break;
+
+ case FITS_HIGH_CONTRAST:
+ {
+ min = stats.mean[0] + stats.stddev[0];
+ if (min < 0)
+ min =0;
+ max = stats.mean[0] + stats.stddev[0] * 3;
+
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ image_buffer[index] = qBound(min, image[index], max);
+ }
+ }
+ }
+ stats.min[0] = min;
+ stats.max[0] = max;
+ runningAverageStdDev();
+ }
+ break;
+
+ case FITS_EQUALIZE:
+ {
+ if (histogram == NULL)
+ return;
+ QVector<double> cumulativeFreq = histogram->getCumulativeFrequency();
+ coeff = 255.0 / (height * width);
+
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ bufferVal = (int) (image[index] - min) / histogram->getBinWidth();
+
+ if (bufferVal >= cumulativeFreq.size())
+ bufferVal = cumulativeFreq.size()-1;
+
+ val = (int) (coeff * cumulativeFreq[bufferVal]);
+
+ image_buffer[index] = val;
+ }
+ }
+ }
+ }
+ calculateStats(true);
+ break;
+
+ case FITS_HIGH_PASS:
+ {
+ min = stats.mean[0];
+ for (int i=0; i < channels; i++)
+ {
+ offset = i*size;
+ for (int j=0; j < height; j++)
+ {
+ row = offset + j * width;
+ for (int k=0; k < width; k++)
+ {
+ index=k + row;
+ image_buffer[index] = qBound(min, image[index], max);
+ }
+ }
+ }
+
+ stats.min[0] = min;
+ stats.max[0] = max;
+ runningAverageStdDev();
+ }
+ break;
+
+ // Based on http://www.librow.com/articles/article-1
+ case FITS_MEDIAN:
+ {
+ float* extension = new float[(width + 2) * (height + 2)];
+ // Check memory allocation
+ if (!extension)
+ return;
+ // Create image extension
+ for (int ch=0; ch < channels; ch++)
+ {
+ offset = ch*size;
+ int N=width,M=height;
+
+ for (int i = 0; i < M; ++i)
+ {
+ memcpy(extension + (N + 2) * (i + 1) + 1, image_buffer + N * i + offset, N * sizeof(float));
+ extension[(N + 2) * (i + 1)] = image_buffer[N * i + offset];
+ extension[(N + 2) * (i + 2) - 1] = image_buffer[N * (i + 1) - 1 + offset];
+ }
+ // Fill first line of image extension
+ memcpy(extension, extension + N + 2, (N + 2) * sizeof(float));
+ // Fill last line of image extension
+ memcpy(extension + (N + 2) * (M + 1), extension + (N + 2) * M, (N + 2) * sizeof(float));
+ // Call median filter implementation
+
+ N=width+2;
+ M=height+2;
+ // Move window through all elements of the image
+ for (int m = 1; m < M - 1; ++m)
+ for (int n = 1; n < N - 1; ++n)
+ {
+ // Pick up window elements
+ int k = 0;
+ float window[9];
+ for (int j = m - 1; j < m + 2; ++j)
+ for (int i = n - 1; i < n + 2; ++i)
+ window[k++] = extension[j * N + i];
+ // Order elements (only half of them)
+ for (int j = 0; j < 5; ++j)
+ {
+ // Find position of minimum element
+ int mine = j;
+ for (int l = j + 1; l < 9; ++l)
+ if (window[l] < window[mine])
+ mine = l;
+ // Put found minimum element in its place
+ const float temp = window[j];
+ window[j] = window[mine];
+ window[mine] = temp;
+ }
+ // Get result - the middle element
+ image_buffer[(m - 1) * (N - 2) + n - 1 + offset] = window[4];
+ }
+ }
+
+ // Free memory
+ delete[] extension;
+ runningAverageStdDev();
+ }
+ break;
+
+
+ case FITS_ROTATE_CW:
+ rotFITS(90, 0);
+ rotCounter++;
+ break;
+
+ case FITS_ROTATE_CCW:
+ rotFITS(270, 0);
+ rotCounter--;
+ break;
+
+ case FITS_FLIP_H:
+ rotFITS(0, 1);
+ flipHCounter++;
+ break;
+
+ case FITS_FLIP_V:
+ rotFITS(0, 2);
+ flipVCounter++;
+ break;
+
+ case FITS_CUSTOM:
+ default:
+ return;
+ break;
+ }
+*/
+}
+
+void FITSDataLite::subtract(float *dark_buffer)
+{
+ for (int i=0; i < stats.width*stats.height; i++)
+ {
+ image_buffer[i] -= dark_buffer[i];
+ if (image_buffer[i] < 0)
+ image_buffer[i] = 0;
+ }
+
+ calculateStats(true);
+}
+
+int FITSDataLite::findStars(const QRectF &boundary, bool force)
+{
+ /*if (histogram == NULL)
+ return -1;
+
+ if (starsSearched == false || force)
+ {
+ qDeleteAll(starCenters);
+ starCenters.clear();
+
+ findCentroid(boundary);
+ getHFR();
+ }
+
+ starsSearched = true;
+
+ return starCenters.count();
+*/
+ return -1;
+}
+
+void FITSDataLite::getCenterSelection(int *x, int *y)
+{
+ if (starCenters.count() == 0)
+ return;
+
+ Edge *pEdge = new Edge();
+ pEdge->x = *x;
+ pEdge->y = *y;
+ pEdge->width = 1;
+
+ foreach(Edge *center, starCenters)
+ if (checkCollision(pEdge, center))
+ {
+ *x = center->x;
+ *y = center->y;
+ break;
+ }
+
+ delete (pEdge);
+}
+
+void FITSDataLite::checkWCS()
+{
+#ifdef HAVE_WCSLIB
+
+ int status=0;
+ char *header;
+ int nkeyrec, nreject, nwcs, stat[2];
+ double imgcrd[2], phi, pixcrd[2], theta, world[2];
+ struct wcsprm *wcs=0;
+ int width=getWidth();
+ int height=getHeight();
+
+ if (fits_hdr2str(fptr, 1, NULL, 0, &header, &nkeyrec, &status))
+ {
+ fits_report_error(stderr, status);
+ return;
+ }
+
+ if ((status = wcspih(header, nkeyrec, WCSHDR_all, -3, &nreject, &nwcs, &wcs)))
+ {
+ fprintf(stderr, "wcspih ERROR %d: %s.\n", status, wcshdr_errmsg[status]);
+ return;
+ }
+
+ free(header);
+
+ if (wcs == 0)
+ {
+ //fprintf(stderr, "No world coordinate systems found.\n");
+ return;
+ }
+
+ // FIXME: Call above goes through EVEN if no WCS is present, so we're adding this to return for now.
+ if (wcs->crpix[0] == 0)
+ return;
+
+ HasWCS = true;
+
+ if ((status = wcsset(wcs)))
+ {
+ fprintf(stderr, "wcsset ERROR %d: %s.\n", status, wcs_errmsg[status]);
+ return;
+ }
+
+ delete[] wcs_coord;
+
+ wcs_coord = new wcs_point[width*height];
+
+ wcs_point *p = wcs_coord;
+
+ for (int i=0; i < height; i++)
+ {
+ for (int j=0; j < width; j++)
+ {
+ pixcrd[0]=j;
+ pixcrd[1]=i;
+
+ if ((status = wcsp2s(wcs, 1, 2, &pixcrd[0], &imgcrd[0], &phi, &theta, &world[0], &stat[0])))
+ {
+ fprintf(stderr, "wcsp2s ERROR %d: %s.\n", status,
+ wcs_errmsg[status]);
+ }
+ else
+ {
+ p->ra = world[0];
+ p->dec = world[1];
+
+ p++;
+ }
+ }
+ }
+#endif
+
+}
+float *FITSDataLite::getDarkFrame() const
+{
+ return darkFrame;
+}
+
+void FITSDataLite::setDarkFrame(float *value)
+{
+ darkFrame = value;
+}
+
+int FITSDataLite::getFlipVCounter() const
+{
+ return flipVCounter;
+}
+
+void FITSDataLite::setFlipVCounter(int value)
+{
+ flipVCounter = value;
+}
+
+int FITSDataLite::getFlipHCounter() const
+{
+ return flipHCounter;
+}
+
+void FITSDataLite::setFlipHCounter(int value)
+{
+ flipHCounter = value;
+}
+
+int FITSDataLite::getRotCounter() const
+{
+ return rotCounter;
+}
+
+void FITSDataLite::setRotCounter(int value)
+{
+ rotCounter = value;
+}
+
+
+/* Rotate an image by 90, 180, or 270 degrees, with an optional
+ * reflection across the vertical or horizontal axis.
+ * verbose generates extra info on stdout.
+ * return NULL if successful or rotated image.
+ */
+bool FITSDataLite::rotFITS (int rotate, int mirror)
+{
+ int ny, nx;
+ int x1, y1, x2, y2;
+ float *rotimage = NULL;
+ int offset=0;
+
+ if (rotate == 1)
+ rotate = 90;
+ else if (rotate == 2)
+ rotate = 180;
+ else if (rotate == 3)
+ rotate = 270;
+ else if (rotate < 0)
+ rotate = rotate + 360;
+
+ nx = stats.width;
+ ny = stats.height;
+
+ /* Allocate buffer for rotated image */
+ rotimage = new float[stats.samples_per_channel*channels];
+ if (rotimage == NULL)
+ {
+ qWarning() << "Unable to allocate memory for rotated image buffer!";
+ return false;
+ }
+
+ /* Mirror image without rotation */
+ if (rotate < 45 && rotate > -45)
+ {
+ if (mirror == 1)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ x2 = nx - x1 - 1;
+ for (y1 = 0; y1 < ny; y1++)
+ rotimage[(y1*nx) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+
+ }
+ else if (mirror == 2)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ y2 = ny - y1 - 1;
+ for (x1 = 0; x1 < nx; x1++)
+ rotimage[(y2*nx) + x1 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+
+ }
+ else
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ for (x1 = 0; x1 < nx; x1++)
+ rotimage[(y1*nx) + x1 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+
+ }
+ }
+
+ /* Rotate by 90 degrees */
+ else if (rotate >= 45 && rotate < 135)
+ {
+ if (mirror == 1)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ x2 = ny - y1 - 1;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ y2 = nx - x1 - 1;
+ rotimage[(y2*ny) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+
+ }
+ else if (mirror == 2)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ for (x1 = 0; x1 < nx; x1++)
+ rotimage[(x1*ny) + y1 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+
+ }
+ else
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ x2 = ny - y1 - 1;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ y2 = x1;
+ rotimage[(y2*ny) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+
+ }
+
+ stats.width = ny;
+ stats.height = nx;
+ }
+
+ /* Rotate by 180 degrees */
+ else if (rotate >= 135 && rotate < 225)
+ {
+ if (mirror == 1)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ y2 = ny - y1 - 1;
+ for (x1 = 0; x1 < nx; x1++)
+ rotimage[(y2*nx) + x1 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+
+ }
+ else if (mirror == 2)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ x2 = nx - x1 - 1;
+ for (y1 = 0; y1 < ny; y1++)
+ rotimage[(y1*nx) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+ else
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ y2 = ny - y1 - 1;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ x2 = nx - x1 - 1;
+ rotimage[(y2*nx) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+ }
+ }
+
+ /* Rotate by 270 degrees */
+ else if (rotate >= 225 && rotate < 315)
+ {
+ if (mirror == 1)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ for (x1 = 0; x1 < nx; x1++)
+ rotimage[(x1*ny) + y1 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+ else if (mirror == 2)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ x2 = ny - y1 - 1;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ y2 = nx - x1 - 1;
+ rotimage[(y2*ny) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ x2 = y1;
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ y2 = nx - x1 - 1;
+ rotimage[(y2*ny) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+ }
+
+ stats.width = ny;
+ stats.height = nx;
+ }
+
+ /* If rotating by more than 315 degrees, assume top-bottom reflection */
+ else if (rotate >= 315 && mirror)
+ {
+ for (int i=0; i < channels; i++)
+ {
+ offset = stats.samples_per_channel * i;
+ for (y1 = 0; y1 < ny; y1++)
+ {
+ for (x1 = 0; x1 < nx; x1++)
+ {
+ x2 = y1;
+ y2 = x1;
+ rotimage[(y2*ny) + x2 + offset] = image_buffer[(y1*nx) + x1 + offset];
+ }
+ }
+ }
+ }
+
+ delete[] image_buffer;
+ image_buffer = rotimage;
+
+ return true;
+}
+
+void FITSDataLite::rotWCSFITS (int angle, int mirror)
+{
+ int status=0;
+ char comment[100];
+ double ctemp1, ctemp2, ctemp3, ctemp4, naxis1, naxis2;
+ int WCS_DECIMALS=6;
+
+ naxis1=stats.width;
+ naxis2=stats.height;
+
+ if (fits_read_key_dbl(fptr, "CD1_1", &ctemp1, comment, &status ))
+ {
+ // No WCS keywords
+ return;
+ }
+
+ /* Reset CROTAn and CD matrix if axes have been exchanged */
+ if (angle == 90)
+ {
+ if (!fits_read_key_dbl(fptr, "CROTA1", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CROTA1", ctemp1+90.0, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CROTA2", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CROTA2", ctemp1+90.0, WCS_DECIMALS, comment, &status );
+ }
+
+ status=0;
+
+ /* Negate rotation angle if mirrored */
+ if (mirror)
+ {
+ if (!fits_read_key_dbl(fptr, "CROTA1", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CROTA1", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CROTA2", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CROTA2", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ status=0;
+
+ if (!fits_read_key_dbl(fptr, "LTM1_1", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "LTM1_1", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ status=0;
+
+ if (!fits_read_key_dbl(fptr, "CD1_1", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CD1_1", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CD1_2", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CD1_2", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CD2_1", &ctemp1, comment, &status ))
+ fits_update_key_dbl(fptr, "CD2_1", -ctemp1, WCS_DECIMALS, comment, &status );
+ }
+
+ status=0;
+
+ /* Unbin CRPIX and CD matrix */
+ if (!fits_read_key_dbl(fptr, "LTM1_1", &ctemp1, comment, &status ))
+ {
+ if (ctemp1 != 1.0)
+ {
+ if (!fits_read_key_dbl(fptr, "LTM2_2", &ctemp2, comment, &status ))
+ if (ctemp1 == ctemp2)
+ {
+ double ltv1 = 0.0;
+ double ltv2 = 0.0;
+ status=0;
+ if (!fits_read_key_dbl(fptr, "LTV1", &ltv1, comment, &status))
+ fits_delete_key(fptr, "LTV1", &status);
+ if (!fits_read_key_dbl(fptr, "LTV2", &ltv2, comment, &status))
+ fits_delete_key(fptr, "LTV2", &status);
+
+ status=0;
+
+ if (!fits_read_key_dbl(fptr, "CRPIX1", &ctemp3, comment, &status ))
+ fits_update_key_dbl(fptr, "CRPIX1", (ctemp3-ltv1)/ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CRPIX2", &ctemp3, comment, &status ))
+ fits_update_key_dbl(fptr, "CRPIX2", (ctemp3-ltv2)/ctemp1, WCS_DECIMALS, comment, &status );
+
+ status=0;
+
+ if (!fits_read_key_dbl(fptr, "CD1_1", &ctemp3, comment, &status ))
+ fits_update_key_dbl(fptr, "CD1_1", ctemp3/ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CD1_2", &ctemp3, comment, &status ))
+ fits_update_key_dbl(fptr, "CD1_2", ctemp3/ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CD2_1", &ctemp3, comment, &status ))
+ fits_update_key_dbl(fptr, "CD2_1", ctemp3/ctemp1, WCS_DECIMALS, comment, &status );
+
+ if (!fits_read_key_dbl(fptr, "CD2_2", &ctemp3, comment, &status ))
+ fits_update_key_dbl(fptr, "CD2_2", ctemp3/ctemp1, WCS_DECIMALS, comment, &status );
+
+ status=0;
+
+ fits_delete_key(fptr, "LTM1_1", &status);
+ fits_delete_key(fptr, "LTM1_2", &status);
+ }
+ }
+ }
+
+ status=0;
+
+ /* Reset CRPIXn */
+ if ( !fits_read_key_dbl(fptr, "CRPIX1", &ctemp1, comment, &status ) && !fits_read_key_dbl(fptr, "CRPIX2", &ctemp2, comment, &status ) )
+ {
+ if (mirror)
+ {
+ if (angle == 0)
+ fits_update_key_dbl(fptr, "CRPIX1", naxis1-ctemp1, WCS_DECIMALS, comment, &status );
+ else if (angle == 90)
+ {
+ fits_update_key_dbl(fptr, "CRPIX1", naxis2-ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CRPIX2", naxis1-ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 180)
+ {
+ fits_update_key_dbl(fptr, "CRPIX1", ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CRPIX2", naxis2-ctemp2, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 270)
+ {
+ fits_update_key_dbl(fptr, "CRPIX1", ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CRPIX2", ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ }
+ else
+ {
+ if (angle == 90)
+ {
+ fits_update_key_dbl(fptr, "CRPIX1", naxis2-ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CRPIX2", ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 180)
+ {
+ fits_update_key_dbl(fptr, "CRPIX1", naxis1-ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CRPIX2", naxis2-ctemp2, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 270)
+ {
+ fits_update_key_dbl(fptr, "CRPIX1", ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CRPIX2", naxis1-ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ }
+ }
+
+ status=0;
+
+ /* Reset CDELTn (degrees per pixel) */
+ if ( !fits_read_key_dbl(fptr, "CDELT1", &ctemp1, comment, &status ) && !fits_read_key_dbl(fptr, "CDELT2", &ctemp2, comment, &status ) )
+ {
+ if (mirror)
+ {
+ if (angle == 0)
+ fits_update_key_dbl(fptr, "CDELT1", -ctemp1, WCS_DECIMALS, comment, &status );
+ else if (angle == 90)
+ {
+ fits_update_key_dbl(fptr, "CDELT1", -ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CDELT2", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ }
+ else if (angle == 180)
+ {
+ fits_update_key_dbl(fptr, "CDELT1", ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CDELT2", -ctemp2, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 270)
+ {
+ fits_update_key_dbl(fptr, "CDELT1", ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CDELT2", ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ }
+ else
+ {
+ if (angle == 90)
+ {
+ fits_update_key_dbl(fptr, "CDELT1", -ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CDELT2", ctemp1, WCS_DECIMALS, comment, &status );
+
+ }
+ else if (angle == 180)
+ {
+ fits_update_key_dbl(fptr, "CDELT1", -ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CDELT2", -ctemp2, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 270)
+ {
+ fits_update_key_dbl(fptr, "CDELT1", ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CDELT2", -ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ }
+ }
+
+ /* Reset CD matrix, if present */
+ ctemp1 = 0.0;
+ ctemp2 = 0.0;
+ ctemp3 = 0.0;
+ ctemp4 = 0.0;
+ status=0;
+ if ( !fits_read_key_dbl(fptr, "CD1_1", &ctemp1, comment, &status ) )
+ {
+ fits_read_key_dbl(fptr, "CD1_2", &ctemp2, comment, &status );
+ fits_read_key_dbl(fptr, "CD2_1", &ctemp3, comment, &status );
+ fits_read_key_dbl(fptr, "CD2_2", &ctemp4, comment, &status );
+ status=0;
+ if (mirror)
+ {
+ if (angle == 0)
+ {
+ fits_update_key_dbl(fptr, "CD1_2", -ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", -ctemp3, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 90)
+ {
+ fits_update_key_dbl(fptr, "CD1_1", -ctemp4, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD1_2", -ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", -ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_2", -ctemp1, WCS_DECIMALS, comment, &status );
+
+ }
+ else if (angle == 180)
+ {
+ fits_update_key_dbl(fptr, "CD1_1", ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD1_2", ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", -ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_2", -ctemp4, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 270)
+ {
+ fits_update_key_dbl(fptr, "CD1_1", ctemp4, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD1_2", ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_2", ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ }
+ else {
+ if (angle == 90)
+ {
+ fits_update_key_dbl(fptr, "CD1_1", -ctemp4, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD1_2", -ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_2", ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 180)
+ {
+ fits_update_key_dbl(fptr, "CD1_1", -ctemp1, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD1_2", -ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", -ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_2", -ctemp4, WCS_DECIMALS, comment, &status );
+ }
+ else if (angle == 270)
+ {
+ fits_update_key_dbl(fptr, "CD1_1", ctemp4, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD1_2", ctemp3, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_1", -ctemp2, WCS_DECIMALS, comment, &status );
+ fits_update_key_dbl(fptr, "CD2_2", -ctemp1, WCS_DECIMALS, comment, &status );
+ }
+ }
+ }
+
+ /* Delete any polynomial solution */
+ /* (These could maybe be switched, but I don't want to work them out yet */
+ status=0;
+ if ( !fits_read_key_dbl(fptr, "CO1_1", &ctemp1, comment, &status ) )
+ {
+ int i;
+ char keyword[16];
+
+ for (i = 1; i < 13; i++)
+ {
+ sprintf (keyword,"CO1_%d", i);
+ fits_delete_key(fptr, keyword, &status);
+ }
+ for (i = 1; i < 13; i++)
+ {
+ sprintf (keyword,"CO2_%d", i);
+ fits_delete_key(fptr, keyword, &status);
+ }
+ }
+
+ return;
+}
+
+float * FITSDataLite::getImageBuffer()
+{
+ return image_buffer;
+}
+
+void FITSDataLite::setImageBuffer(float *buffer)
+{
+ delete[] image_buffer;
+ image_buffer = buffer;
+}
+
+bool FITSDataLite::checkDebayer()
+{
+
+ int status=0;
+ char bayerPattern[64];
+
+ // Let's search for BAYERPAT keyword, if it's not found we return as there is no bayer pattern in this image
+ if (fits_read_keyword(fptr, "BAYERPAT", bayerPattern, NULL, &status))
+ return false;
+
+ if (stats.bitpix != 16 && stats.bitpix != 8)
+ {
+ //KMessageBox::error(NULL, i18n("Only 8 and 16 bits bayered images supported."), i18n("Debayer error"));
+ return false;
+ }
+ QString pattern(bayerPattern);
+ pattern = pattern.remove("'").trimmed();
+
+ if (pattern == "RGGB")
+ debayerParams.filter = DC1394_COLOR_FILTER_RGGB;
+ else if (pattern == "GBRG")
+ debayerParams.filter = DC1394_COLOR_FILTER_GBRG;
+ else if (pattern == "GRBG")
+ debayerParams.filter = DC1394_COLOR_FILTER_GRBG;
+ else if (pattern == "BGGR")
+ debayerParams.filter = DC1394_COLOR_FILTER_BGGR;
+ // We return unless we find a valid pattern
+ else
+ return false;
+
+ fits_read_key(fptr, TINT, "XBAYROFF", &debayerParams.offsetX, NULL, &status);
+ fits_read_key(fptr, TINT, "YBAYROFF", &debayerParams.offsetY, NULL, &status);
+
+ delete[] bayer_buffer;
+ bayer_buffer = new float[stats.samples_per_channel * channels];
+ if (bayer_buffer == NULL)
+ {
+ //KMessageBox::error(NULL, i18n("Unable to allocate memory for bayer buffer."), i18n("Open FITS"));
+ return false;
+ }
+ memcpy(bayer_buffer, image_buffer, stats.samples_per_channel * channels * sizeof(float));
+
+ HasDebayer = true;
+
+ return true;
+
+}
+
+void FITSDataLite::getBayerParams(BayerParams *param)
+{
+ param->method = debayerParams.method;
+ param->filter = debayerParams.filter;
+ param->offsetX = debayerParams.offsetX;
+ param->offsetY = debayerParams.offsetY;
+}
+
+void FITSDataLite::setBayerParams(BayerParams *param)
+{
+ debayerParams.method = param->method;
+ debayerParams.filter = param->filter;
+ debayerParams.offsetX = param->offsetX;
+ debayerParams.offsetY = param->offsetY;
+}
+
+bool FITSDataLite::debayer()
+{
+ dc1394error_t error_code;
+
+ int rgb_size = stats.samples_per_channel*3;
+ float * dst = new float[rgb_size];
+ if (dst == NULL)
+ {
+ //KMessageBox::error(NULL, i18n("Unable to allocate memory for temporary bayer buffer."), i18n("Debayer Error"));
+ return false;
+ }
+
+ if ( (error_code = dc1394_bayer_decoding_float(bayer_buffer, dst, stats.width, stats.height, debayerParams.offsetX, debayerParams.offsetY,
+ debayerParams.filter, debayerParams.method)) != DC1394_SUCCESS)
+ {
+ //KMessageBox::error(NULL, i18n("Debayer failed (%1)", error_code), i18n("Debayer error"));
+ channels=1;
+ delete[] dst;
+ //Restore buffer
+ delete[] image_buffer;
+ image_buffer = new float[stats.samples_per_channel];
+ memcpy(image_buffer, bayer_buffer, stats.samples_per_channel * sizeof(float));
+ return false;
+ }
+
+ if (channels == 1)
+ {
+ delete[] image_buffer;
+ image_buffer = new float[rgb_size];
+
+ if (image_buffer == NULL)
+ {
+ delete[] dst;
+ //KMessageBox::error(NULL, i18n("Unable to allocate memory for debayerd buffer."), i18n("Debayer Error"));
+ return false;
+ }
+ }
+
+ // Data in R1G1B1, we need to copy them into 3 layers for FITS
+ float * rBuff = image_buffer;
+ float * gBuff = image_buffer + (stats.width * stats.height);
+ float * bBuff = image_buffer + (stats.width * stats.height * 2);
+
+ int imax = stats.samples_per_channel*3 - 3;
+ for (int i=0; i <= imax; i += 3)
+ {
+ *rBuff++ = dst[i];
+ *gBuff++ = dst[i+1];
+ *bBuff++ = dst[i+2];
+ }
+
+ channels=3;
+ delete[] dst;
+ return true;
+
+}
+
+double FITSDataLite::getADU()
+{
+ double adu=0;
+ for (int i=0; i < channels; i++)
+ adu += stats.mean[i];
+
+ return (adu/ (double) channels);
+}
+
diff --git a/kstars/fitsviewer/fitsdatalite.h b/kstars/fitsviewer/fitsdatalite.h
new file mode 100644
index 0000000..1b08364
--- /dev/null
+++ b/kstars/fitsviewer/fitsdatalite.h
@@ -0,0 +1,224 @@
+/***************************************************************************
+ fitsdatalite.h - FITS Image
+ -------------------
+ begin : Fri Jul 22 2016
+ copyright : (C) 2016 by Jasem Mutlaq and Artem Fedoskin
+ email : mutlaqja@ikarustech.com, afedoskin3@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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. *
+ * *
+ * Some code fragments were adapted from Peter Kirchgessner's FITS plugin*
+ * See http://members.aol.com/pkirchg for more details. *
+ ***************************************************************************/
+
+#ifndef FITSDATALITE_H_
+#define FITSDATALITE_H_
+
+#ifdef WIN32
+// avoid compiler warning when windows.h is included after fitsio.h
+#include <windows.h>
+#endif
+
+#include <fitsio.h>
+#include "fitscommon.h"
+#include <QRectF>
+
+#include "skypoint.h"
+#include "dms.h"
+#include "bayer.h"
+
+#define INITIAL_W 640
+#define INITIAL_H 480
+
+#define MINIMUM_PIXEL_RANGE 5
+#define MINIMUM_STDVAR 5
+
+typedef struct
+{
+ double ra;
+ double dec;
+} wcs_point;
+
+class Edge
+{
+public:
+ float x;
+ float y;
+ int val;
+ int scanned;
+ float width;
+ float HFR;
+ float sum;
+};
+
+class FITSDataLite
+{
+public:
+
+ typedef enum { CHANNEL_ONE, CHANNEL_TWO, CHANNEL_THREE } ColorChannel;
+
+ FITSDataLite(FITSMode mode=FITS_NORMAL);
+ ~FITSDataLite();
+
+ /* Loads FITS image, scales it, and displays it in the GUI */
+ bool loadFITS(const QString &filename, bool silent=true);
+ /* Save FITS */
+ int saveFITS(const QString &filename);
+ /* Rescale image lineary from image_buffer, fit to window if desired */
+ int rescale(FITSZoom type);
+ /* Calculate stats */
+ void calculateStats(bool refresh=false);
+ /* Calculate running average & standard deviation using Welford’s method for computing variance */
+ void runningAverageStdDev();
+
+ // Access functions
+ //double getValue(float *buffer, int i);
+ //void setValue(float *buffer, int i, double value);
+ //double getValue(int i);
+ //void setValue(int i, float value);
+ void clearImageBuffers();
+ void setImageBuffer(float *buffer);
+ float * getImageBuffer();
+
+ // Stats
+ int getDataType() { return data_type; }
+ unsigned int getSize() { return stats.samples_per_channel; }
+ void getDimensions(double *w, double *h) { *w = stats.width; *h = stats.height; }
+ void setWidth(long w) { stats.width = w;}
+ void setHeight(long h) { stats.height = h;}
+ long getWidth() { return stats.width; }
+ long getHeight() { return stats.height; }
+
+ // Statistics
+ int getNumOfChannels() { return channels;}
+ void setMinMax(double newMin, double newMax, uint8_t channel=0);
+ void getMinMax(double *min, double *max,uint8_t channel=0) { *min = stats.min[channel]; *max = stats.max[channel]; }
+ double getMin(uint8_t channel=0) { return stats.min[channel]; }
+ double getMax(uint8_t channel=0) { return stats.max[channel]; }
+ void setStdDev(double value, uint8_t channel=0) { stats.stddev[channel] = value;}
+ double getStdDev(uint8_t channel=0) { return stats.stddev[channel]; }
+ void setMean(double value, uint8_t channel=0) { stats.mean[channel] = value; }
+ double getMean(uint8_t channel=0) { return stats.mean[channel]; }
+ void setMedian(double val, uint8_t channel=0) { stats.median[channel] = val;}
+ double getMedian(uint8_t channel=0) { return stats.median[channel];}
+
+ void setSNR(double val) { stats.SNR = val;}
+ double getSNR() { return stats.SNR;}
+ void setBPP(int value) { stats.bitpix = value;}
+ int getBPP() { return stats.bitpix; }
+ double getADU();
+
+ // Star detection
+ int getDetectedStars() { return starCenters.count(); }
+ bool areStarsSearched() { return starsSearched; }
+ QList<Edge*> getStarCenters() { return starCenters;}
+ int findStars(const QRectF &boundary = QRectF(), bool force=false);
+ void findCentroid(const QRectF &boundary = QRectF(), int initStdDev=MINIMUM_STDVAR, int minEdgeWidth=MINIMUM_PIXEL_RANGE);
+ void getCenterSelection(int *x, int *y);
+
+ // Half Flux Radius
+ Edge * getMaxHFRStar() { return maxHFRStar;}
+ double getHFR(HFRType type=HFR_AVERAGE);
+ double getHFR(int x, int y);
+
+ // FITS Mode (Normal, Guide, Focus..etc).
+ FITSMode getMode() { return mode;}
+
+ // WCS
+ bool hasWCS() { return HasWCS; }
+ wcs_point *getWCSCoord() { return wcs_coord; }
+
+ // Debayer
+ bool hasDebayer() { return HasDebayer; }
+ bool debayer();
+ void getBayerParams(BayerParams *param);
+ void setBayerParams(BayerParams *param);
+
+ // FITS Record
+ int getFITSRecord(QString &recordList, int &nkeys);
+
+ // Histogram
+ //void setHistogram(FITSHistogram *inHistogram) { histogram = inHistogram; }
+
+ // Filter
+ void applyFilter(FITSScale type, float *image=NULL, float min=-1, float max=-1);
+
+ // Rotation counter. We keep count to rotate WCS keywords on save
+ int getRotCounter() const;
+ void setRotCounter(int value);
+
+ // Horizontal flip counter. We keep count to rotate WCS keywords on save
+ int getFlipHCounter() const;
+ void setFlipHCounter(int value);
+
+ // Horizontal flip counter. We keep count to rotate WCS keywords on save
+ int getFlipVCounter() const;
+ void setFlipVCounter(int value);
+
+ // Dark frame
+ float *getDarkFrame() const;
+ void setDarkFrame(float *value);
+ void subtract(float *darkFrame);
+
+ /* stats struct to hold statisical data about the FITS data */
+ struct
+ {
+ double min[3], max[3];
+ double mean[3];
+ double stddev[3];
+ double median[3];
+ double SNR;
+ int bitpix;
+ int ndim;
+ uint32_t samples_per_channel;
+ uint16_t width;
+ uint16_t height;
+ } stats;
+
+private:
+
+ bool rotFITS (int rotate, int mirror);
+ void rotWCSFITS (int angle, int mirror);
+ bool checkCollision(Edge* s1, Edge*s2);
+ int calculateMinMax(bool refresh=false);
+ void checkWCS();
+ bool checkDebayer();
+ void readWCSKeys();
+
+ fitsfile* fptr; // Pointer to CFITSIO FITS file struct
+
+ int data_type; // FITS image data type
+ int channels; // Number of channels
+ float *image_buffer; // Current image buffer
+ float *darkFrame; // Optional dark frame pointer
+
+
+ bool tempFile; // Is this a tempoprary file or one loaded from disk?
+ bool starsSearched; // Did we search for stars yet?
+ bool HasWCS; // Do we have WCS keywords in this FITS data?
+ bool markStars; // Do we need to mark stars for the user?
+ bool HasDebayer; // Is the image debayarable?
+
+ QString filename; // Our very own file name
+ FITSMode mode; // FITS Mode (Normal, WCS, Guide, Focus..etc)
+
+ int rotCounter; // How many times the image was rotated? Useful for WCS keywords rotation on save.
+ int flipHCounter; // How many times the image was flipped horizontally?
+ int flipVCounter; // How many times the image was flipped vertically?
+
+ wcs_point *wcs_coord; // Pointer to WCS coordinate data, if any.
+ QList<Edge*> starCenters; // All the stars we detected, if any.
+ Edge* maxHFRStar; // The biggest fattest star in the image.
+
+ float *bayer_buffer; // Bayer buffer
+ BayerParams debayerParams; // Bayer parameters
+
+};
+
+#endif
diff --git a/kstars/fitsviewer/fitsview.h b/kstars/fitsviewer/fitsview.h
index 19ce830..b7ef7e2 100644
--- a/kstars/fitsviewer/fitsview.h
+++ b/kstars/fitsviewer/fitsview.h
@@ -144,7 +144,7 @@ private:
void calculateMaxPixel(double min, double max);
void initDisplayImage();
- FITSLabel *image_frame;
+ //FITSLabel *image_frame;
FITSData *image_data;
int image_width, image_height;
diff --git a/kstars/fitsviewer/fitsviewlite.cpp b/kstars/fitsviewer/fitsviewlite.cpp
new file mode 100644
index 0000000..9b04c3d
--- /dev/null
+++ b/kstars/fitsviewer/fitsviewlite.cpp
@@ -0,0 +1,724 @@
+/***************************************************************************
+ FITSViewLite.cpp - FITS Image
+ -------------------
+ begin : Fri Jul 22 2016
+ copyright : (C) 2016 by Jasem Mutlaq and Artem Fedoskin
+ email : mutlaqja@ikarustech.com, afedoskin3@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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. *
+ * *
+ * Some code fragments were adapted from Peter Kirchgessner's FITS plugin*
+ * See http://members.aol.com/pkirchg for more details. *
+ ***************************************************************************/
+
+#include <config-kstars.h>
+
+#include <cmath>
+#include <cstdlib>
+
+#include <KLocalizedString>
+#include "fitsviewlite.h"
+
+#include "kstarsdata.h"
+#include "ksutils.h"
+#include "Options.h"
+
+#ifdef HAVE_INDI
+#include <basedevice.h>
+#include "indi/indilistener.h"
+#include "indi/indistd.h"
+#include "indi/driverinfo.h"
+#endif
+
+#define ZOOM_DEFAULT 100.0
+#define ZOOM_MIN 10
+#define ZOOM_MAX 400
+#define ZOOM_LOW_INCR 10
+#define ZOOM_HIGH_INCR 50
+
+#define DECAY_CONSTANT -0.04
+
+//#define FITS_DEBUG
+/*
+FITSLabel::FITSLabel(FITSView *img, QWidget *parent) : QLabel(parent)
+{
+ image = img;
+
+}
+
+void FITSLabel::setSize(double w, double h)
+{
+ width = w;
+ height = h;
+ size = w*h;
+}
+
+FITSLabel::~FITSLabel() {}
+
+void FITSLabel::mouseMoveEvent(QMouseEvent *e)
+{
+ double x,y;
+ FITSDataLiteLite *image_data = image->getImageData();
+
+ float *buffer = image_data->getImageBuffer();
+
+ if (buffer == NULL)
+ return;
+
+ x = round(e->x() / (image->getCurrentZoom() / ZOOM_DEFAULT));
+ y = round(e->y() / (image->getCurrentZoom() / ZOOM_DEFAULT));
+
+ x = KSUtils::clamp(x, 1.0, width);
+ y = KSUtils::clamp(y, 1.0, height);
+
+ emit newStatus(QString("X:%1 Y:%2").arg( (int)x ).arg( (int)y ), FITS_POSITION);
+
+ // Range is 0 to dim-1 when accessing array
+ x -= 1;
+ y -= 1;
+
+ if (image_data->getBPP() == -32 || image_data->getBPP() == 32)
+ emit newStatus(QLocale().toString(buffer[(int) (y * width + x)], 'f', 4), FITS_VALUE);
+ else
+ emit newStatus(QLocale().toString(buffer[(int) (y * width + x)], 'f', 2), FITS_VALUE);
+
+
+ if (image_data->hasWCS())
+ {
+ int index = x + y * width;
+
+ wcs_point * wcs_coord = image_data->getWCSCoord();
+
+ if (wcs_coord)
+ {
+ if (index > size)
+ return;
+
+ ra.setD(wcs_coord[index].ra);
+ dec.setD(wcs_coord[index].dec);
+
+ emit newStatus(QString("%1 , %2").arg( ra.toHMSString()).arg(dec.toDMSString()), FITS_WCS);
+ }
+ }
+
+ setCursor(Qt::CrossCursor);
+
+ e->accept();
+}
+
+void FITSLabel::mousePressEvent(QMouseEvent *e)
+{
+ double x,y;
+
+ x = round(e->x() / (image->getCurrentZoom() / ZOOM_DEFAULT));
+ y = round(e->y() / (image->getCurrentZoom() / ZOOM_DEFAULT));
+
+ x = KSUtils::clamp(x, 1.0, width);
+ y = KSUtils::clamp(y, 1.0, height);
+
+#ifdef HAVE_INDI
+ FITSDataLiteLite *image_data = image->getImageData();
+
+ if ( (e->buttons() & Qt::RightButton) && image_data->hasWCS())
+ {
+ QMenu fitspopup;
+ QAction *trackAction = fitspopup.addAction(i18n("Center In Telescope"));
+
+ if (fitspopup.exec(e->globalPos()) == trackAction)
+ {
+ int index = x + y * width;
+
+ wcs_point * wcs_coord = image_data->getWCSCoord();
+
+ if (wcs_coord)
+ {
+ if (index > size)
+ return;
+
+ centerTelescope(wcs_coord[index].ra/15.0, wcs_coord[index].dec);
+
+ return;
+ }
+ }
+ }
+#endif
+
+ emit pointSelected(x, y);
+
+ double HFR = image->getImageData()->getHFR(x, y);
+
+ if (HFR > 0)
+ QToolTip::showText(e->globalPos(), i18nc("Half Flux Radius", "HFR: %1", QString::number(HFR, 'g' , 3)), this);
+
+}
+
+void FITSLabel::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ double x,y;
+
+ x = round(e->x() / (image->getCurrentZoom() / ZOOM_DEFAULT));
+ y = round(e->y() / (image->getCurrentZoom() / ZOOM_DEFAULT));
+
+ x = KSUtils::clamp(x, 1.0, width);
+ y = KSUtils::clamp(y, 1.0, height);
+
+ emit markerSelected(x, y);
+
+ return;
+}
+
+void FITSLabel::centerTelescope(double raJ2000, double decJ2000)
+{
+#ifdef HAVE_INDI
+
+ if (INDIListener::Instance()->size() == 0)
+ {
+ KMessageBox::sorry(0, i18n("KStars did not find any active telescopes."));
+ return;
+ }
+
+ foreach(ISD::GDInterface *gd, INDIListener::Instance()->getDevices())
+ {
+ INDI::BaseDevice *bd = gd->getBaseDevice();
+
+ if (gd->getType() != KSTARS_TELESCOPE)
+ continue;
+
+ if (bd == NULL)
+ continue;
+
+ if (bd->isConnected() == false)
+ {
+ KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.", gd->getDeviceName()));
+ return;
+ }
+
+ ISD::GDSetCommand SlewCMD(INDI_SWITCH, "ON_COORD_SET", "TRACK", ISS_ON, this);
+
+ SkyObject selectedObject;
+
+ selectedObject.setRA0(raJ2000);
+ selectedObject.setDec0(decJ2000);
+
+ selectedObject.apparentCoord(J2000, KStarsData::Instance()->ut().djd());
+
+ gd->setProperty(&SlewCMD);
+ gd->runCommand(INDI_SEND_COORDS, &selectedObject);
+
+ return;
+ }
+
+ KMessageBox::sorry(0, i18n("KStars did not find any active telescopes."));
+
+#else
+
+ Q_UNUSED(raJ2000);
+ Q_UNUSED(decJ2000);
+
+#endif
+}*/
+
+FITSViewLite::FITSViewLite(FITSMode fitsMode, FITSScale filterType) :zoomFactor(1.2)
+{
+ image_data = NULL;
+ display_image = NULL;
+ firstLoad = true;
+ gammaValue=0;
+ filter = filterType;
+ mode = fitsMode;
+
+ markerCrosshair.setX(0);
+ markerCrosshair.setY(0);
+
+ currentZoom = 0.0;
+ markStars = false;
+
+ /*connect(image_frame, SIGNAL(newStatus(QString,FITSBar)), this, SIGNAL(newStatus(QString,FITSBar)));
+ connect(image_frame, SIGNAL(pointSelected(int,int)), this, SLOT(processPointSelection(int,int)));
+ connect(image_frame, SIGNAL(markerSelected(int,int)), this, SLOT(processMarkerSelection(int,int)));*/
+
+ //if (fitsMode == FITS_GUIDE)
+ //connect(image_frame, SIGNAL(pointSelected(int,int)), this, SLOT(processPointSelection(int,int)));
+
+ // Default size
+ //resize(INITIAL_W, INITIAL_H);
+}
+
+FITSViewLite::~FITSViewLite()
+{
+ delete(image_data);
+ delete(display_image);
+}
+
+QImage *FITSViewLite::loadFITS (const QString &inFilename , bool silent)
+{
+ bool setBayerParams=false;
+
+ BayerParams param;
+ if (image_data && image_data->hasDebayer())
+ {
+ setBayerParams=true;
+ image_data->getBayerParams(&param);
+ }
+
+ delete (image_data);
+ image_data = NULL;
+
+ image_data = new FITSDataLite(mode);
+
+ if (setBayerParams)
+ image_data->setBayerParams(&param);
+
+ if (mode == FITS_NORMAL)
+ {
+ /*fitsProg.setWindowModality(Qt::WindowModal);
+ fitsProg.setLabelText(i18n("Please hold while loading FITS file..."));
+ fitsProg.setWindowTitle(i18n("Loading FITS"));
+ fitsProg.setValue(10);
+ qApp->processEvents();*/
+ }
+
+ if (image_data->loadFITS(inFilename, silent) == false)
+ return false;
+
+ image_data->getDimensions(&currentWidth, &currentHeight);
+
+ image_width = currentWidth;
+ image_height = currentHeight;
+
+ hasWCS = image_data->hasWCS();
+
+ maxPixel = image_data->getMax();
+ minPixel = image_data->getMin();
+
+ if (gammaValue != 0 && (filter== FITS_NONE || filter >= FITS_FLIP_H))
+ {
+ double maxGammaPixel = maxPixel* (100 * exp(DECAY_CONSTANT * gammaValue))/100.0;
+ // If calculated maxPixel after gamma is different from image data max pixel, then we apply filter immediately.
+ image_data->applyFilter(FITS_LINEAR, NULL, minPixel, maxGammaPixel);
+ }
+
+ initDisplayImage();
+
+ if(rescale()) {
+ return display_image;
+ }
+ return NULL;
+}
+
+int FITSViewLite::saveFITS( const QString &newFilename )
+{
+ return image_data->saveFITS(newFilename);
+}
+
+
+bool FITSViewLite::rescale()
+{
+ double val=0;
+ double bscale, bzero;
+ double min, max;
+ unsigned int size = image_data->getSize();
+ image_data->getMinMax(&min, &max);
+
+ calculateMaxPixel(min, max);
+
+ min = minPixel;
+ max = maxGammaPixel;
+
+ if (min == max)
+ {
+ display_image->fill(Qt::white);
+ //emit newStatus(i18n("Image is saturated!"), FITS_MESSAGE);
+ }
+ else
+ {
+ bscale = 255. / (max - min);
+ bzero = (-min) * (255. / (max - min));
+
+ if (image_height != image_data->getHeight() || image_width != image_data->getWidth())
+ {
+ image_width = image_data->getWidth();
+ image_height = image_data->getHeight();
+
+ initDisplayImage();
+
+ //if (isVisible())
+ //emit newStatus(QString("%1x%2").arg(image_width).arg(image_height), FITS_RESOLUTION);
+ }
+
+ currentWidth = display_image->width();
+ currentHeight = display_image->height();
+
+ float *image_buffer = image_data->getImageBuffer();
+
+ if (image_data->getNumOfChannels() == 1)
+ {
+ /* Fill in pixel values using indexed map, linear scale */
+ for (int j = 0; j < image_height; j++)
+ {
+ unsigned char *scanLine = display_image->scanLine(j);
+
+ for (int i = 0; i < image_width; i++)
+ {
+ val = image_buffer[j * image_width + i];
+ if (gammaValue > 0)
+ val = qBound(minPixel, val, maxGammaPixel);
+ scanLine[i]= (val * bscale + bzero);
+ }
+ }
+ return true;
+ }
+ else
+ {
+ double rval=0,gval=0,bval=0;
+ QRgb value;
+ /* Fill in pixel values using indexed map, linear scale */
+ for (int j = 0; j < image_height; j++)
+ {
+ QRgb *scanLine = reinterpret_cast<QRgb*>((display_image->scanLine(j)));
+
+ for (int i = 0; i < image_width; i++)
+ {
+ rval = image_buffer[j * image_width + i];
+ gval = image_buffer[j * image_width + i + size];
+ bval = image_buffer[j * image_width + i + size * 2];
+ if (gammaValue > 0)
+ {
+ rval = qBound(minPixel, rval, maxGammaPixel);
+ gval = qBound(minPixel, gval, maxGammaPixel);
+ gval = qBound(minPixel, gval, maxGammaPixel);
+ }
+
+ value = qRgb(rval* bscale + bzero, gval* bscale + bzero, bval* bscale + bzero);
+
+ //display_image->setPixel(i, j, value);
+ scanLine[i] = value;
+
+ }
+ }
+ return true;
+ }
+ }
+
+ //if (type != ZOOM_KEEP_LEVEL)
+ //emit newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
+ return false;
+}
+/*
+void FITSViewLite::ZoomIn()
+{
+
+ if (currentZoom < ZOOM_DEFAULT)
+ currentZoom += ZOOM_LOW_INCR;
+ else
+ currentZoom += ZOOM_HIGH_INCR;
+
+
+ emit actionUpdated("view_zoom_out", true);
+ if (currentZoom >= ZOOM_MAX)
+ {
+ currentZoom = ZOOM_MAX;
+ emit actionUpdated("view_zoom_in", false);
+ }
+
+ currentWidth = image_width * (currentZoom / ZOOM_DEFAULT);
+ currentHeight = image_height * (currentZoom / ZOOM_DEFAULT);
+
+ updateFrame();
+
+ newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
+
+}
+
+void FITSViewLite::ZoomOut()
+{
+
+ if (currentZoom <= ZOOM_DEFAULT)
+ currentZoom -= ZOOM_LOW_INCR;
+ else
+ currentZoom -= ZOOM_HIGH_INCR;
+
+ if (currentZoom <= ZOOM_MIN)
+ {
+ currentZoom = ZOOM_MIN;
+ emit actionUpdated("view_zoom_out", false);
+ }
+
+ emit actionUpdated("view_zoom_in", true);
+
+ currentWidth = image_width * (currentZoom / ZOOM_DEFAULT);
+ currentHeight = image_height * (currentZoom / ZOOM_DEFAULT);
+
+ updateFrame();
+
+ newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
+}
+
+void FITSViewLite::updateFrame()
+{
+
+ QPixmap displayPixmap;
+ bool ok=false;
+
+ if (display_image == NULL)
+ return;
+
+ if (currentZoom != ZOOM_DEFAULT)
+ ok = displayPixmap.convertFromImage(display_image->scaled( (int) currentWidth, (int) currentHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation));
+ else
+ ok = displayPixmap.convertFromImage(*display_image);
+
+
+ if (ok == false)
+ return;
+
+ QPainter painter(&displayPixmap);
+
+ drawOverlay(&painter);
+
+ image_frame->setPixmap(displayPixmap);
+ image_frame->resize( (int) currentWidth, (int) currentHeight);
+}
+
+void FITSViewLite::ZoomDefault()
+{
+ emit actionUpdated("view_zoom_out", true);
+ emit actionUpdated("view_zoom_in", true);
+
+
+ currentZoom = ZOOM_DEFAULT;
+ currentWidth = image_width;
+ currentHeight = image_height;
+
+ updateFrame();
+
+ newStatus(QString("%1%").arg(currentZoom), FITS_ZOOM);
+
+ update();
+
+}
+
+void FITSViewLite::drawOverlay(QPainter *painter)
+{
+ if (markStars)
+ drawStarCentroid(painter);
+
+ //if (mode == FITS_GUIDE)
+ if (trackingBoxEnabled)
+ drawTrackingBox(painter);
+
+ if (markerCrosshair.isNull() == false)
+ drawMarker(painter);
+}
+
+void FITSViewLite::updateMode(FITSMode fmode)
+{
+ mode = fmode;
+}
+
+void FITSViewLite::drawMarker(QPainter *painter)
+{
+ painter->setPen( QPen( QColor( KStarsData::Instance()->colorScheme()->colorNamed("TargetColor" ) ) ) );
+ painter->setBrush( Qt::NoBrush );
+ float pxperdegree = (currentZoom/ZOOM_DEFAULT)* (57.3/1.8);
+
+ float s1 = 0.5*pxperdegree;
+ float s2 = pxperdegree;
+ float s3 = 2.0*pxperdegree;
+
+ float x0 = markerCrosshair.x() * (currentZoom / ZOOM_DEFAULT);
+ float y0 = markerCrosshair.y() * (currentZoom / ZOOM_DEFAULT);
+ float x1 = x0 - 0.5*s1; float y1 = y0 - 0.5*s1;
+ float x2 = x0 - 0.5*s2; float y2 = y0 - 0.5*s2;
+ float x3 = x0 - 0.5*s3; float y3 = y0 - 0.5*s3;
+
+ //Draw radial lines
+ painter->drawLine( QPointF(x1, y0), QPointF(x3, y0) );
+ painter->drawLine( QPointF(x0+s2, y0), QPointF(x0+0.5*s1, y0) );
+ painter->drawLine( QPointF(x0, y1), QPointF(x0, y3) );
+ painter->drawLine( QPointF(x0, y0+0.5*s1), QPointF(x0, y0+s2) );
+ //Draw circles at 0.5 & 1 degrees
+ painter->drawEllipse( QRectF(x1, y1, s1, s1) );
+ painter->drawEllipse( QRectF(x2, y2, s2, s2) );
+}
+
+void FITSViewLite::drawStarCentroid(QPainter *painter)
+{
+ painter->setPen(QPen(Qt::red, 2));
+
+ int x1,y1, w;
+
+ // image_data->getStarCenter();
+
+ QList<Edge*> starCenters = image_data->getStarCenters();
+
+ for (int i=0; i < starCenters.count() ; i++)
+ {
+ x1 = (starCenters[i]->x - starCenters[i]->width/2) * (currentZoom / ZOOM_DEFAULT);
+ y1 = (starCenters[i]->y - starCenters[i]->width/2) * (currentZoom / ZOOM_DEFAULT);
+ w = (starCenters[i]->width) * (currentZoom / ZOOM_DEFAULT);
+
+ painter->drawEllipse(x1, y1, w, w);
+ }
+}
+
+void FITSViewLite::drawTrackingBox(QPainter *painter)
+{
+ painter->setPen(QPen(Qt::green, 2));
+
+ if (trackingBox.isNull())
+ return;
+
+ int x1 = trackingBox.x() * (currentZoom / ZOOM_DEFAULT);
+ int y1 = trackingBox.y() * (currentZoom / ZOOM_DEFAULT);
+ int w = trackingBox.width() * (currentZoom / ZOOM_DEFAULT);
+ int h = trackingBox.height() * (currentZoom / ZOOM_DEFAULT);
+
+ painter->drawRect(x1, y1, w, h);
+}
+
+QPixmap & FITSViewLite::getTrackingBoxPixmap()
+{
+ if (trackingBox.isNull())
+ return trackingBoxPixmap;
+
+ int x1 = trackingBox.x() * (currentZoom / ZOOM_DEFAULT);
+ int y1 = trackingBox.y() * (currentZoom / ZOOM_DEFAULT);
+ int w = trackingBox.width() * (currentZoom / ZOOM_DEFAULT);
+ int h = trackingBox.height() * (currentZoom / ZOOM_DEFAULT);
+
+ trackingBoxPixmap = image_frame->grab(QRect(x1, y1, w, h));
+
+ return trackingBoxPixmap;
+}
+
+void FITSViewLite::setTrackingBox(const QRect & rect)
+{
+ if (rect != trackingBox)
+ {
+ trackingBoxUpdated=true;
+ trackingBox = rect;
+ updateFrame();
+ }
+}*/
+
+/*void FITSViewLite::toggleStars(bool enable)
+{
+ markStars = enable;
+
+ if (markStars == true)
+ {
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ emit newStatus(i18n("Finding stars..."), FITS_MESSAGE);
+ qApp->processEvents();
+ int count = -1;
+
+ /*if (trackingBoxEnabled)
+ {
+ count = image_data->findStars(trackingBox, trackingBoxUpdated);
+ trackingBoxUpdated=false;
+ }
+ else
+ count = image_data->findStars();
+
+ if (count >= 0 && isVisible())
+ emit newStatus(i18np("1 star detected.", "%1 stars detected.", count), FITS_MESSAGE);
+ QApplication::restoreOverrideCursor();
+ }
+}*
+
+/*void FITSViewLite::processPointSelection(int x, int y)
+{
+ //if (mode != FITS_GUIDE)
+ //return;
+
+ image_data->getCenterSelection(&x, &y);
+
+ //setGuideSquare(x,y);
+ emit trackingStarSelected(x,y);
+}
+
+void FITSViewLite::processMarkerSelection(int x, int y)
+{
+ markerCrosshair.setX(x);
+ markerCrosshair.setY(y);
+
+ updateFrame();
+}
+
+void FITSViewLite::setTrackingBoxEnabled(bool enable)
+{
+ if (enable != trackingBoxEnabled)
+ {
+ trackingBoxEnabled = enable;
+ updateFrame();
+ }
+}
+int FITSViewLite::getGammaValue() const
+{
+ return gammaValue;
+}
+
+void FITSViewLite::setGammaValue(int value)
+{
+ if (value == gammaValue)
+ return;
+
+ gammaValue = value;
+
+ calculateMaxPixel(minPixel, maxPixel);
+
+ // If calculated maxPixel after gamma is different from image data max pixel, then we apply filter immediately.
+ //image_data->applyFilter(FITS_LINEAR, NULL, minPixel, maxGammaPixel);
+ qApp->processEvents();
+ rescale(ZOOM_KEEP_LEVEL);
+ qApp->processEvents();
+ updateFrame();
+
+}
+
+void FITSViewLite::wheelEvent(QWheelEvent* event)
+{
+ if (event->angleDelta().y() > 0)
+ ZoomIn();
+ else
+ ZoomOut();
+
+ event->accept();
+}*/
+
+void FITSViewLite::calculateMaxPixel(double min, double max)
+{
+ minPixel=min;
+ maxPixel=max;
+
+ if (gammaValue == 0)
+ maxGammaPixel = maxPixel;
+ else
+ maxGammaPixel = maxPixel* (100 * exp(DECAY_CONSTANT * gammaValue))/100.0;
+}
+
+void FITSViewLite::initDisplayImage()
+{
+ delete (display_image);
+ display_image = NULL;
+
+ if (image_data->getNumOfChannels() == 1)
+ {
+ 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));
+ }
+ else
+ {
+ display_image = new QImage(image_width, image_height, QImage::Format_RGB32);
+ }
+}
diff --git a/kstars/fitsviewer/fitsviewlite.h b/kstars/fitsviewer/fitsviewlite.h
new file mode 100644
index 0000000..dc529ab
--- /dev/null
+++ b/kstars/fitsviewer/fitsviewlite.h
@@ -0,0 +1,174 @@
+/***************************************************************************
+ fitsviewlite.h - FITS Image
+ -------------------
+ begin : Fri Jul 22 2016
+ copyright : (C) 2016 by Jasem Mutlaq and Artem Fedoskin
+ email : mutlaqja@ikarustech.com, afedoskin3@gmail.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program 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. *
+ * *
+ * Some code fragments were adapted from Peter Kirchgessner's FITS plugin*
+ * See http://members.aol.com/pkirchg for more details. *
+ ***************************************************************************/
+
+#ifndef FITSView_H_
+#define FITSView_H_
+
+#ifdef WIN32
+// avoid compiler warning when windows.h is included after fitsio.h
+#include <windows.h>
+#endif
+
+#include "fitsio.h"
+#include "fitscommon.h"
+
+#include "dms.h"
+#include "fitsdatalite.h"
+
+#define INITIAL_W 640
+#define INITIAL_H 480
+
+#define MINIMUM_PIXEL_RANGE 5
+#define MINIMUM_STDVAR 5
+
+class FITSView;
+
+
+/*class FITSLabel : public QLabel
+{
+ Q_OBJECT
+public:
+ explicit FITSLabel(FITSView *img, QWidget *parent=NULL);
+ virtual ~FITSLabel();
+ void setSize(double w, double h);
+ void centerTelescope(double raJ2000, double decJ2000);
+
+protected:
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+private:
+ FITSView *image;
+ dms ra;
+ dms dec;
+ double width,height,size;
+
+signals:
+ void newStatus(const QString &msg, FITSBar id);
+ void pointSelected(int x, int y);
+ void markerSelected(int x, int y);
+
+
+};*/
+
+class FITSViewLite : public QObject//: public QScrollArea
+{
+ Q_OBJECT
+public:
+ FITSViewLite(FITSMode mode=FITS_NORMAL, FITSScale filter=FITS_NONE);
+ ~FITSViewLite();
+
+ /* Loads FITS image, scales it, and displays it in the GUI */
+ QImage *loadFITS(const QString &filename, bool silent=true);
+ /* Save FITS */
+ int saveFITS(const QString &filename);
+ /* Rescale image lineary from image_buffer, fit to window if desired */
+ bool rescale();
+
+ void setImageData(FITSDataLite *d) { image_data = d; }
+
+ // Access functions
+ FITSDataLite *getImageData() { return image_data; }
+ double getCurrentZoom() { return currentZoom; }
+ QImage * getDisplayImage() { return display_image; }
+
+ QImage * getImageFromFITS();
+
+ // Tracking square
+ /*void setTrackingBoxEnabled(bool enable);
+ bool isTrackingBoxEnabled() { return trackingBoxEnabled; }
+ QPixmap & getTrackingBoxPixmap();
+ void setTrackingBox(const QRect & rect);
+ const QRect & getTrackingBox() { return trackingBox; }*/
+
+ // Overlay
+ /*void drawOverlay(QPainter *);
+ void drawStarCentroid(QPainter *);
+ void drawTrackingBox(QPainter *);
+ void drawMarker(QPainter *);
+ void updateFrame();
+
+ // Star Detection
+ void toggleStars(bool enable);
+
+ // FITS Mode
+ void updateMode(FITSMode mode);
+ FITSMode getMode() { return mode;}*/
+
+ int getGammaValue() const;
+ void setGammaValue(int value);
+ void setFilter(FITSScale newFilter) { filter = newFilter;}
+
+public slots:
+ /*void ZoomIn();
+ void ZoomOut();
+ void ZoomDefault();
+
+ void processPointSelection(int x, int y);
+ void processMarkerSelection(int x, int y);*/
+private:
+
+ double average();
+ double stddev();
+ void calculateMaxPixel(double min, double max);
+ void initDisplayImage();
+
+ //FITSLabel *image_frame;
+ FITSDataLite *image_data;
+ int image_width, image_height;
+
+ double currentWidth,currentHeight; /* Current width and height due to zoom */
+ const double zoomFactor; /* Image zoom factor */
+ double currentZoom; /* Current Zoom level */
+
+ int data_type; /* FITS data type when opened */
+ QImage *display_image; /* FITS image that is displayed in the GUI */
+
+ int gammaValue;
+ double maxPixel, maxGammaPixel, minPixel;
+
+ bool firstLoad;
+ bool markStars;
+ bool starsSearched;
+ bool hasWCS;
+
+ QString filename;
+ FITSMode mode;
+ FITSScale filter;
+
+ // Cross hair
+ QPointF markerCrosshair;
+
+ // Tracking box
+ /*bool trackingBoxEnabled;
+ bool trackingBoxUpdated;
+ QRect trackingBox;
+ QPixmap trackingBoxPixmap;*/
+
+signals:
+ /*void newStatus(const QString &msg, FITSBar id);
+ void debayerToggled(bool);
+ void actionUpdated(const QString &name, bool enable);
+ void trackingStarSelected(int x, int y);*/
+
+ //friend class FITSLabel;
+};
+
+#endif
diff --git a/kstars/indi/clientmanagerlite.cpp b/kstars/indi/clientmanagerlite.cpp
index 8a1029d..ec09159 100644
--- a/kstars/indi/clientmanagerlite.cpp
+++ b/kstars/indi/clientmanagerlite.cpp
@@ -23,7 +23,21 @@
#include "indicom.h"
#include "skymaplite.h"
#include "kstarslite/skyitems/telescopesymbolsitem.h"
-
+#include <QDebug>
+#include <QImageReader>
+#include <QTemporaryFile>
+#include "kspaths.h"
+#include "fitsviewer/fitsdatalite.h"
+#include "fitsviewer/fitsviewlite.h"
+#include "kstarslite/imageprovider.h"
+#include <QProcess>
+#include <QFileDialog>
+#include "kspaths.h"
+#include <QApplication>
+
+#ifdef Q_OS_ANDROID
+#include "../../android_lib/include/libraw/libraw.h"
+#endif
DeviceInfoLite::DeviceInfoLite(INDI::BaseDevice *dev)
:device(dev), telescope(nullptr)
@@ -34,9 +48,15 @@ DeviceInfoLite::DeviceInfoLite(INDI::BaseDevice *dev)
ClientManagerLite::ClientManagerLite()
:m_connected(false)
{
+#ifdef ANDROID
+ defaultImageType = ".jpeg";
+ defaultImagesLocation = KSPaths::writableLocation(QStandardPaths::PicturesLocation);
+#endif
qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeNS");
qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeWE");
qmlRegisterType<TelescopeLite>("TelescopeLiteEnums", 1, 0, "TelescopeCommand");
+
+ fitsView = new FITSViewLite();
}
ClientManagerLite::~ClientManagerLite()
@@ -165,7 +185,7 @@ void ClientManagerLite::buildTextGUI(Property * property) {
write = true;
break;
}
- emit createINDIText(property->getDeviceName(), property->getName(), label, text, read, write);
+ emit createINDIText(property->getDeviceName(), property->getName(), label, name, text, read, write);
}
}
}
@@ -229,7 +249,7 @@ void ClientManagerLite::buildNumberGUI(Property * property) {
write = true;
break;
}
- emit createINDINumber(property->getDeviceName(), property->getName(), label, text, read, write, scale);
+ emit createINDINumber(property->getDeviceName(), property->getName(), label, name, text, read, write, scale);
}
}
}
@@ -352,9 +372,8 @@ void ClientManagerLite::sendNewINDISwitch(QString deviceName, QString propName,
foreach(DeviceInfoLite *devInfo, m_devices) {
INDI::BaseDevice *device = devInfo->device;
if(device->getDeviceName() == deviceName) {
- INDI::Property *property = device->getProperty(propName.toStdString().c_str());
+ INDI::Property *property = device->getProperty(propName.toLatin1());
if(property) {
-
ISwitchVectorProperty *svp = property->getSwitch();
if (svp == NULL)
@@ -387,6 +406,56 @@ void ClientManagerLite::sendNewINDISwitch(QString deviceName, QString propName,
}
}
+void ClientManagerLite::sendNewINDINumber(const QString& deviceName, const QString& propName, const QString &numberName, double value) {
+ foreach(DeviceInfoLite *devInfo, m_devices) {
+ INDI::BaseDevice *device = devInfo->device;
+ if(device->getDeviceName() == deviceName) {
+ INumberVectorProperty *np = device->getNumber(propName.toLatin1());
+ if (np)
+ {
+ INumber *n = IUFindNumber(np, numberName.toLatin1());
+ if (n)
+ {
+ n->value = value;
+ sendNewNumber(np);
+ return;
+ }
+
+ qDebug() << "Could not find property: " << deviceName << "." << propName << "." << numberName;
+ return;
+ }
+
+ qDebug() << "Could not find property: " << deviceName << "." << propName << "." << numberName;
+ return;
+ }
+ }
+}
+
+void ClientManagerLite::sendNewINDIText(const QString& deviceName, const QString& propName, const QString &fieldName, const QString &text) {
+ foreach(DeviceInfoLite *devInfo, m_devices) {
+ INDI::BaseDevice *device = devInfo->device;
+ if(device->getDeviceName() == deviceName) {
+ ITextVectorProperty *tp = device->getText(propName.toLatin1());
+ if (tp)
+ {
+ IText *t = IUFindText(tp, fieldName.toLatin1());
+ if (t)
+ {
+ IUSaveText(t, text.toLatin1().data());
+ sendNewText(tp);
+ return;
+ }
+
+ qDebug() << "Could not find property: " << deviceName << "." << propName << "." << fieldName;
+ return;
+ }
+
+ qDebug() << "Could not find property: " << deviceName << "." << propName << "." << fieldName;
+ return;
+ }
+ }
+}
+
void ClientManagerLite::sendNewINDISwitch(QString deviceName, QString propName, int index) {
if(index >= 0) {
foreach(DeviceInfoLite *devInfo, m_devices) {
@@ -415,6 +484,34 @@ void ClientManagerLite::sendNewINDISwitch(QString deviceName, QString propName,
}
}
+bool ClientManagerLite::saveDisplayImage() {
+ QString dateTime = QDateTime::currentDateTime().toString("dd-MM-yyyy-hh-mm-ss");
+ QString fileEnding = "kstars-lite-" + dateTime;
+ //QString filename = KSPaths::writableLocation(QStandardPaths::PicturesLocation);
+//#ifndef ANDROID
+ QString filename = QFileDialog::getSaveFileName(QApplication::activeWindow(), i18n("Save Image"),
+ KSPaths::writableLocation(QStandardPaths::PicturesLocation) + "/" + fileEnding + ".jpeg",
+ i18n("JPEG (*.jpeg);;JPG (*.jpg);;PNG (*.png);;BMP (*.bmp)" ));
+//#else
+ /*if(imageType.isEmpty() || !(imageType != ".jpeg" || imageType != ".jpg" || imageType != ".png" || imageType != ".bmp")) {
+ QString warning = imageType + " is a wrong image type. Switching to \"" + defaultImageType + "\"";
+ qDebug() << warning;
+ emit newINDIMessage(warning);
+ imageType = defaultImageType;
+ }*/
+ // QString filename(defaultImagesLocation + "/" + fileEnding + defaultImageType);
+//#endif
+ if(!filename.isEmpty()) {
+ int i = 100;
+ if(displayImage.save(filename)) {
+ emit newINDIMessage("File " + filename + " was successfully saved");
+ return true;
+ }
+ }
+ emit newINDIMessage("Couldn't save file " + filename);
+ return false;
+}
+
bool ClientManagerLite::isDeviceConnected(QString deviceName) {
INDI::BaseDevice * device = getDevice(deviceName.toStdString().c_str());
if(device != NULL) {
@@ -514,6 +611,170 @@ void ClientManagerLite::newProperty(INDI::Property *property)
void ClientManagerLite::removeProperty(INDI::Property *property) {
emit removeINDIProperty(property->getGroupName(),property->getName());
+
+ DeviceInfoLite *devInfo = nullptr;
+ foreach(DeviceInfoLite *di, m_devices) {
+ if(di->device == property->getBaseDevice()) {
+ devInfo = di;
+ }
+ }
+
+ if(devInfo) {
+ if ((!strcmp(property->getName(), "EQUATORIAL_EOD_COORD") ||
+ !strcmp(property->getName(), "HORIZONTAL_COORD")) ) {
+ if(devInfo->telescope) {
+ emit telescopeRemoved(devInfo->telescope);
+ }
+ KStarsLite::Instance()->map()->update(); // Update SkyMap if position of telescope is changed
+ }
+ }
+}
+
+void ClientManagerLite::newBLOB(IBLOB *bp) {
+ processBLOBasCCD(bp);
+}
+
+bool ClientManagerLite::processBLOBasCCD(IBLOB *bp) {
+ enum blobType { BLOB_IMAGE, BLOB_FITS, BLOB_CR2, BLOB_OTHER} BType;
+
+ BType = BLOB_OTHER;
+
+ QString format(bp->format);
+ QString deviceName = bp->bvp->device;
+
+ QByteArray fmt = QString(bp->format).toLower().remove(".").toUtf8();
+
+ // If it's not FITS or an image, don't process it.
+ if ( (QImageReader::supportedImageFormats().contains(fmt)))
+ BType = BLOB_IMAGE;
+ else if (format.contains("fits"))
+ BType = BLOB_FITS;
+ else if (format.contains("cr2"))
+ BType = BLOB_CR2;
+
+ if (BType == BLOB_OTHER)
+ {
+ return false;
+ }
+
+ QString currentDir = KSPaths::writableLocation(QStandardPaths::TempLocation);
+
+ int nr, n=0;
+ QTemporaryFile tmpFile(QDir::tempPath() + "/fitsXXXXXX");
+
+ if (currentDir.endsWith('/'))
+ currentDir.chop(1);
+
+ if (QDir(currentDir).exists() == false)
+ QDir().mkpath(currentDir);
+
+ QString filename(currentDir + '/');
+
+ if(true) {
+ tmpFile.setAutoRemove(false);
+
+ if (!tmpFile.open())
+ {
+ qDebug() << "ISD:CCD Error: Unable to open " << filename << endl;
+ //emit BLOBUpdated(NULL);
+ return false;
+ }
+
+ QDataStream out(&tmpFile);
+
+ for (nr=0; nr < (int) bp->size; nr += n)
+ n = out.writeRawData( static_cast<char *> (bp->blob) + nr, bp->size - nr);
+
+ tmpFile.close();
+
+ filename = tmpFile.fileName();
+ } else {
+ //Add support for batch mode
+ }
+
+ strncpy(BLOBFilename, filename.toLatin1(), MAXINDIFILENAME);
+ bp->aux2 = BLOBFilename;
+
+ /* Test images
+ BType = BLOB_IMAGE;
+ filename = "/home/polaris/Pictures/351181_0.jpeg";
+ */
+ /*Test CR2
+ BType = BLOB_CR2;
+
+ filename = "/home/polaris/test.CR2";
+ filename = "/storage/emulated/0/test.CR2";*/
+
+ if (BType == BLOB_IMAGE || BType == BLOB_CR2)
+ {
+ if (BType == BLOB_CR2)
+ {
+#ifdef Q_OS_ANDROID
+ LibRaw RawProcessor;
+ #define OUT RawProcessor.imgdata.params
+ OUT.user_qual = 0; // -q
+ OUT.use_camera_wb = 1; // -w
+ OUT.highlight = 5; // -H
+ OUT.bright = 8; // -b
+
+ QString rawFileName = filename;
+ rawFileName = rawFileName.remove(0, rawFileName.lastIndexOf(QLatin1Literal("/")));
+ QString templateName = QString("%1/%2.XXXXXX").arg(QDir::tempPath()).arg(rawFileName);
+ QTemporaryFile jpgPreview(templateName);
+ jpgPreview.setAutoRemove(false);
+ jpgPreview.open();
+ jpgPreview.close();
+ QString jpeg_filename = jpgPreview.fileName();
+
+ RawProcessor.open_file(filename.toLatin1());
+ RawProcessor.unpack();
+ RawProcessor.dcraw_process();
+ RawProcessor.dcraw_ppm_tiff_writer(jpeg_filename.toLatin1());
+ QFile::remove(filename);
+ filename = jpeg_filename;
+#else
+ if (QStandardPaths::findExecutable("dcraw").isEmpty() == false && QStandardPaths::findExecutable("cjpeg").isEmpty() == false)
+ {
+ QProcess dcraw;
+ QString rawFileName = filename;
+ rawFileName = rawFileName.remove(0, rawFileName.lastIndexOf(QLatin1Literal("/")));
+ QString templateName = QString("%1/%2.XXXXXX").arg(QDir::tempPath()).arg(rawFileName);
+ QTemporaryFile jpgPreview(templateName);
+ jpgPreview.setAutoRemove(false);
+ jpgPreview.open();
+ jpgPreview.close();
+ QString jpeg_filename = jpgPreview.fileName();
+
+ QString cmd = QString("/bin/sh -c \"dcraw -c -q 0 -w -H 5 -b 8 %1 | cjpeg -quality 80 > %2\"").arg(filename).arg(jpeg_filename);
+ dcraw.start(cmd);
+ dcraw.waitForFinished();
+ QFile::remove(filename); //Delete raw
+ filename = jpeg_filename;
+ }
+ else
+ {
+ emit newINDIMessage(i18n("Unable to find dcraw and cjpeg. Please install the required tools to convert CR2 to JPEG."));
+ emit newINDIBLOBImage(deviceName, false);
+ return false;
+ }
+#endif
+ }
+
+ displayImage.load(filename);
+ QFile::remove(filename);
+ KStarsLite::Instance()->imageProvider()->addImage("ccdPreview", displayImage);
+ emit newINDIBLOBImage(deviceName, true);
+ return true;
+
+ } else if (BType == BLOB_FITS) {
+ displayImage = *(fitsView->loadFITS(filename));
+ QFile::remove(filename);
+ KStarsLite::Instance()->imageProvider()->addImage("ccdPreview", displayImage);
+ emit newINDIBLOBImage(deviceName, true);
+ return true;
+ }
+ emit newINDIBLOBImage(deviceName, false);
+ return false;
}
void ClientManagerLite::newSwitch(ISwitchVectorProperty *svp) {
@@ -534,6 +795,37 @@ void ClientManagerLite::newNumber(INumberVectorProperty *nvp)
!strcmp(nvp->name, "HORIZONTAL_COORD")) ) {
KStarsLite::Instance()->map()->update(); // Update SkyMap if position of telescope is changed
}
+
+ QString deviceName = nvp->device;
+ QString propName = nvp->name;
+ for (int i = 0; i < nvp->nnp; ++i) {
+ INumber num = nvp->np[i];
+ char buf[MAXINDIFORMAT];
+ numberFormat(buf, num.format, num.value);
+ QString numberName = num.name;
+
+ emit newINDINumber(deviceName, propName, numberName, QString(buf).trimmed());
+ }
+}
+
+void ClientManagerLite::newText(ITextVectorProperty *tvp) {
+ QString deviceName = tvp->device;
+ QString propName = tvp->name;
+ for (int i = 0; i < tvp->ntp; ++i) {
+ IText text = tvp->tp[i];
+ QString fieldName = text.name;
+
+ emit newINDIText(deviceName, propName, fieldName, text.text);
+ }
+}
+
+void ClientManagerLite::newLight(ILightVectorProperty *lvp) {
+ QString deviceName = lvp->device;
+ QString propName = lvp->name;
+}
+
+void ClientManagerLite::newMessage(INDI::BaseDevice *dp, int messageID) {
+ emit newINDIMessage(QString::fromStdString(dp->messageQueue(messageID)));
}
void ClientManagerLite::serverDisconnected(int exit_code) {
diff --git a/kstars/indi/clientmanagerlite.h b/kstars/indi/clientmanagerlite.h
index ab7f4a0..cbd6fe0 100644
--- a/kstars/indi/clientmanagerlite.h
+++ b/kstars/indi/clientmanagerlite.h
@@ -20,6 +20,12 @@
#include <baseclientqt.h>
#include "inditelescopelite.h"
#include "indicommon.h"
+#include "Options.h"
+#include "indistd.h"
+#include <QImage>
+
+class FITSViewLite;
+class QFileDialog;
using namespace INDI;
class TelescopeLite;
@@ -44,6 +50,7 @@ class ClientManagerLite : public INDI::BaseClientQt
Q_PROPERTY(QString connectedHost READ connectedHost WRITE setConnectedHost NOTIFY connectedHostChanged)
Q_PROPERTY(bool connected READ isConnected WRITE setConnected NOTIFY connectedChanged)
public:
+ typedef enum { UPLOAD_CLIENT, UPLOAD_LOCAL, UPLOAD_BOTH } UploadMode;
ClientManagerLite();
virtual ~ClientManagerLite();
Q_INVOKABLE bool setHost(QString ip, unsigned int port);
@@ -65,22 +72,37 @@ public:
Q_INVOKABLE void sendNewINDISwitch(QString deviceName, QString propName, QString name);
Q_INVOKABLE void sendNewINDISwitch(QString deviceName, QString propName, int index);
+ Q_INVOKABLE void sendNewINDINumber(const QString& deviceName, const QString& propName, const QString &numberName, double value);
+ Q_INVOKABLE void sendNewINDIText(const QString& deviceName, const QString& propName, const QString &fieldName, const QString& text);
+
bool isConnected() { return m_connected; }
Q_INVOKABLE bool isDeviceConnected(QString deviceName);
- QList<DeviceInfoLite *> getDevices() { return m_devices; }
+ QList<DeviceInfoLite *> getDevices() { return m_devices; }
+
+ Q_INVOKABLE QString lastUsedServer() { return Options::lastServer(); }
+ Q_INVOKABLE void setLastUsedServer(QString server) { Options::setLastServer(server); }
+
+ Q_INVOKABLE int lastUsedPort() { return Options::lastServerPort(); }
+ Q_INVOKABLE void setLastUsedPort(int port) { Options::setLastServerPort(port); }
+ /**
+ * @brief saveDisplayImage
+ * @return true if image was saved false otherwise
+ */
+ Q_INVOKABLE bool saveDisplayImage();
+
protected:
virtual void newDevice(INDI::BaseDevice *dp);
virtual void removeDevice(INDI::BaseDevice *dp);
virtual void newProperty(INDI::Property *property);
virtual void removeProperty(INDI::Property *property);
- virtual void newBLOB(IBLOB *bp) {}
+ virtual void newBLOB(IBLOB *bp);
virtual void newSwitch(ISwitchVectorProperty *svp);
virtual void newNumber(INumberVectorProperty *nvp);
- virtual void newMessage(INDI::BaseDevice *dp, int messageID) { }
- virtual void newText(ITextVectorProperty *tvp) {}
- virtual void newLight(ILightVectorProperty *lvp) {}
+ virtual void newMessage(INDI::BaseDevice *dp, int messageID);
+ virtual void newText(ITextVectorProperty *tvp);
+ virtual void newLight(ILightVectorProperty *lvp);
virtual void serverConnected() {}
virtual void serverDisconnected(int exit_code);
signals:
@@ -91,8 +113,9 @@ signals:
void newINDIProperty(QString deviceName, QString propName, QString groupName, QString type, QString label);
- void createINDIText(QString deviceName, QString propName, QString propLabel, QString propText, bool read, bool write);
- void createINDINumber(QString deviceName, QString propName, QString propLabel, QString propText, bool read, bool write, bool scale);
+ void createINDIText(QString deviceName, QString propName, QString propLabel, QString fieldName, QString propText, bool read, bool write);
+
+ void createINDINumber(QString deviceName, QString propName, QString propLabel, QString numberName, QString propText, bool read, bool write, bool scale);
void createINDIButton(QString deviceName, QString propName, QString propText, QString switchName, bool read, bool write, bool exclusive, bool checked, bool checkable);
void createINDIRadio(QString deviceName, QString propName, QString propText, QString switchName, bool read, bool write, bool exclusive, bool checked, bool enabled);
@@ -103,14 +126,28 @@ signals:
//Update signals
void newINDISwitch(QString deviceName, QString propName, QString switchName, bool isOn);
+ void newINDINumber(QString deviceName, QString propName, QString numberName, QString value);
+ void newINDIText(QString deviceName, QString propName, QString fieldName, QString text);
+ void newINDIMessage(QString message);
+ void newINDIBLOBImage(QString deviceName, bool isLoaded);
void connectedHostChanged(QString);
void connectedChanged(bool);
void telescopeAdded(TelescopeLite *newTelescope);
+ void telescopeRemoved(TelescopeLite *delTelescope);
private:
+ bool processBLOBasCCD(IBLOB *bp);
+
QList<DeviceInfoLite *> m_devices;
QString m_connectedHost;
bool m_connected;
+ FITSViewLite *fitsView;
+ char BLOBFilename[MAXINDIFILENAME];
+ QImage displayImage;
+#ifdef ANDROID
+ QString defaultImageType;
+ QString defaultImagesLocation;
+#endif
};
#endif // CLIENTMANAGERLITE_H
diff --git a/kstars/indi/indiccd.cpp b/kstars/indi/indiccd.cpp
index 8454068..a85f489 100644
--- a/kstars/indi/indiccd.cpp
+++ b/kstars/indi/indiccd.cpp
@@ -66,23 +66,23 @@ FITSView * CCDChip::getImage(FITSMode imageType)
{
switch (imageType)
{
- case FITS_NORMAL:
- return normalImage;
- break;
+ case FITS_NORMAL:
+ return normalImage;
+ break;
- case FITS_FOCUS:
- return focusImage;
- break;
+ case FITS_FOCUS:
+ return focusImage;
+ break;
- case FITS_GUIDE:
- return guideImage;
- break;
+ case FITS_GUIDE:
+ return guideImage;
+ break;
- case FITS_CALIBRATE:
- return calibrationImage;
- break;
+ case FITS_CALIBRATE:
+ return calibrationImage;
+ break;
- default:
+ default:
break;
}
@@ -95,33 +95,33 @@ void CCDChip::setImage(FITSView *image, FITSMode imageType)
switch (imageType)
{
- case FITS_NORMAL:
- normalImage = image;
- if (normalImage)
- imageData = normalImage->getImageData();
- if (KStars::Instance()->ekosManager()->alignModule() && KStars::Instance()->ekosManager()->alignModule()->fov())
- KStars::Instance()->ekosManager()->alignModule()->fov()->setImage(normalImage->getDisplayImage()->copy());
- break;
+ case FITS_NORMAL:
+ normalImage = image;
+ if (normalImage)
+ imageData = normalImage->getImageData();
+ if (KStars::Instance()->ekosManager()->alignModule() && KStars::Instance()->ekosManager()->alignModule()->fov())
+ KStars::Instance()->ekosManager()->alignModule()->fov()->setImage(normalImage->getDisplayImage()->copy());
+ break;
- case FITS_FOCUS:
- focusImage = image;
- if (focusImage)
- imageData = focusImage->getImageData();
- break;
+ case FITS_FOCUS:
+ focusImage = image;
+ if (focusImage)
+ imageData = focusImage->getImageData();
+ break;
- case FITS_GUIDE:
- guideImage = image;
- if (guideImage)
- imageData = guideImage->getImageData();
- break;
+ case FITS_GUIDE:
+ guideImage = image;
+ if (guideImage)
+ imageData = guideImage->getImageData();
+ break;
- case FITS_CALIBRATE:
- calibrationImage = image;
- if (calibrationImage)
- imageData = calibrationImage->getImageData();
- break;
+ case FITS_CALIBRATE:
+ calibrationImage = image;
+ if (calibrationImage)
+ imageData = calibrationImage->getImageData();
+ break;
- default:
+ default:
break;
}
@@ -133,11 +133,11 @@ bool CCDChip::getFrameMinMax(int *minX, int *maxX, int *minY, int *maxY, int *mi
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
frameProp = baseDevice->getNumber("CCD_FRAME");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
frameProp = baseDevice->getNumber("GUIDER_FRAME");
break;
@@ -187,11 +187,11 @@ bool CCDChip::getFrame(int *x, int *y, int *w, int *h)
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
frameProp = baseDevice->getNumber("CCD_FRAME");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
frameProp = baseDevice->getNumber("GUIDER_FRAME");
break;
@@ -236,11 +236,11 @@ bool CCDChip::resetFrame()
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
frameProp = baseDevice->getNumber("CCD_FRAME");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
frameProp = baseDevice->getNumber("GUIDER_FRAME");
break;
@@ -278,11 +278,11 @@ bool CCDChip::setFrame(int x, int y, int w, int h)
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
frameProp = baseDevice->getNumber("CCD_FRAME");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
frameProp = baseDevice->getNumber("GUIDER_FRAME");
break;
@@ -320,11 +320,11 @@ bool CCDChip::capture(double exposure)
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
expProp = baseDevice->getNumber("CCD_EXPOSURE");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
expProp = baseDevice->getNumber("GUIDER_EXPOSURE");
break;
@@ -346,11 +346,11 @@ bool CCDChip::abortExposure()
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
abortProp = baseDevice->getSwitch("CCD_ABORT_EXPOSURE");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
abortProp = baseDevice->getSwitch("GUIDER_ABORT_EXPOSURE");
break;
@@ -454,14 +454,14 @@ bool CCDChip::setISOIndex(int value)
QStringList CCDChip::getISOList() const
{
- QStringList isoList;
+ QStringList isoList;
- ISwitchVectorProperty *isoProp = baseDevice->getSwitch("CCD_ISO");
- if (isoProp == NULL)
- return isoList;
+ ISwitchVectorProperty *isoProp = baseDevice->getSwitch("CCD_ISO");
+ if (isoProp == NULL)
+ return isoList;
- for (int i=0; i < isoProp->nsp; i++)
- isoList << isoProp->sp[i].label;
+ for (int i=0; i < isoProp->nsp; i++)
+ isoList << isoProp->sp[i].label;
return isoList;
}
@@ -469,14 +469,14 @@ QStringList CCDChip::getISOList() const
bool CCDChip::isCapturing()
{
INumberVectorProperty *expProp = NULL;
-
+
switch (type)
{
case PRIMARY_CCD:
expProp = baseDevice->getNumber("CCD_EXPOSURE");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
expProp = baseDevice->getNumber("GUIDER_EXPOSURE");
break;
@@ -576,11 +576,11 @@ CCDFrameType CCDChip::getFrameType()
if (!strcmp(ccdFrame->name, "FRAME_LIGHT"))
fType = FRAME_LIGHT;
else if (!strcmp(ccdFrame->name, "FRAME_DARK"))
- fType = FRAME_DARK;
+ fType = FRAME_DARK;
else if (!strcmp(ccdFrame->name, "FRAME_FLAT"))
- fType = FRAME_FLAT;
+ fType = FRAME_FLAT;
else if (!strcmp(ccdFrame->name, "FRAME_BIAS"))
- fType = FRAME_BIAS;
+ fType = FRAME_BIAS;
return fType;
@@ -591,18 +591,18 @@ bool CCDChip::setBinning(CCDBinType binType)
switch (binType)
{
- case SINGLE_BIN:
- return setBinning(1,1);
- break;
- case DOUBLE_BIN:
- return setBinning(2,2);
- break;
- case TRIPLE_BIN:
- return setBinning(3,3);
- break;
- case QUADRAPLE_BIN:
- return setBinning(4,4);
- break;
+ case SINGLE_BIN:
+ return setBinning(1,1);
+ break;
+ case DOUBLE_BIN:
+ return setBinning(2,2);
+ break;
+ case TRIPLE_BIN:
+ return setBinning(3,3);
+ break;
+ case QUADRAPLE_BIN:
+ return setBinning(4,4);
+ break;
}
return false;
@@ -615,11 +615,11 @@ CCDBinType CCDChip::getBinning()
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
binProp = baseDevice->getNumber("CCD_BINNING");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
binProp = baseDevice->getNumber("GUIDER_BINNING");
break;
}
@@ -637,19 +637,19 @@ CCDBinType CCDChip::getBinning()
switch ( (int) horBin->value)
{
- case 2:
- binType = DOUBLE_BIN;
- break;
+ case 2:
+ binType = DOUBLE_BIN;
+ break;
- case 3:
- binType = TRIPLE_BIN;
- break;
+ case 3:
+ binType = TRIPLE_BIN;
+ break;
- case 4:
- binType = QUADRAPLE_BIN;
- break;
+ case 4:
+ binType = QUADRAPLE_BIN;
+ break;
- default:
+ default:
break;
}
@@ -665,11 +665,11 @@ bool CCDChip::getBinning(int *bin_x, int *bin_y)
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
binProp = baseDevice->getNumber("CCD_BINNING");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
binProp = baseDevice->getNumber("GUIDER_BINNING");
break;
}
@@ -700,11 +700,11 @@ bool CCDChip::getMaxBin(int *max_xbin, int *max_ybin)
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
binProp = baseDevice->getNumber("CCD_BINNING");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
binProp = baseDevice->getNumber("GUIDER_BINNING");
break;
}
@@ -734,11 +734,11 @@ bool CCDChip::setBinning(int bin_x, int bin_y)
switch (type)
{
- case PRIMARY_CCD:
+ case PRIMARY_CCD:
binProp = baseDevice->getNumber("CCD_BINNING");
break;
- case GUIDE_CCD:
+ case GUIDE_CCD:
binProp = baseDevice->getNumber("GUIDER_BINNING");
break;
}
@@ -878,7 +878,7 @@ void CCD::processNumber(INumberVectorProperty *nvp)
{
INumber *np = IUFindNumber(nvp, "CCD_EXPOSURE_VALUE");
if (np)
- emit newExposureValue(primaryChip, np->value, nvp->s);
+ emit newExposureValue(primaryChip, np->value, nvp->s);
return;
}
@@ -887,17 +887,17 @@ void CCD::processNumber(INumberVectorProperty *nvp)
{
HasCooler = true;
INumber *np = IUFindNumber(nvp, "CCD_TEMPERATURE_VALUE");
- if (np)
- emit newTemperatureValue(np->value);
+ if (np)
+ emit newTemperatureValue(np->value);
- return;
+ return;
}
if (!strcmp(nvp->name, "GUIDER_EXPOSURE"))
{
INumber *np = IUFindNumber(nvp, "GUIDER_EXPOSURE_VALUE");
if (np)
- emit newExposureValue(guideChip, np->value, nvp->s);
+ emit newExposureValue(guideChip, np->value, nvp->s);
return;
}
@@ -1114,7 +1114,7 @@ void CCD::processBLOB(IBLOB* bp)
QTemporaryFile tmpFile(QDir::tempPath() + "/fitsXXXXXX");
if (currentDir.endsWith('/'))
- currentDir.truncate(sizeof(currentDir)-1);
+ currentDir.chop(1);
if (QDir(currentDir).exists() == false)
QDir().mkpath(currentDir);
@@ -1130,47 +1130,47 @@ void CCD::processBLOB(IBLOB* bp)
//tmpFile.setPrefix("fits");
tmpFile.setAutoRemove(false);
- if (!tmpFile.open())
- {
- qDebug() << "ISD:CCD Error: Unable to open " << filename << endl;
- emit BLOBUpdated(NULL);
- return;
- }
+ if (!tmpFile.open())
+ {
+ qDebug() << "ISD:CCD Error: Unable to open " << filename << endl;
+ emit BLOBUpdated(NULL);
+ return;
+ }
- QDataStream out(&tmpFile);
+ QDataStream out(&tmpFile);
- for (nr=0; nr < (int) bp->size; nr += n)
- n = out.writeRawData( static_cast<char *> (bp->blob) + nr, bp->size - nr);
+ for (nr=0; nr < (int) bp->size; nr += n)
+ n = out.writeRawData( static_cast<char *> (bp->blob) + nr, bp->size - nr);
- tmpFile.close();
+ tmpFile.close();
- filename = tmpFile.fileName();
+ filename = tmpFile.fileName();
}
// Create file name for others
else
{
- QString ts = QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss");
+ QString ts = QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss");
- if (ISOMode == false)
- filename += seqPrefix + (seqPrefix.isEmpty() ? "" : "_") + QString("%1.%2").arg(QString().sprintf("%03d", nextSequenceID)).arg(QString(fmt));
- else
- filename += seqPrefix + (seqPrefix.isEmpty() ? "" : "_") + QString("%1_%2.%3").arg(QString().sprintf("%03d", nextSequenceID)).arg(ts).arg(QString(fmt));
+ if (ISOMode == false)
+ filename += seqPrefix + (seqPrefix.isEmpty() ? "" : "_") + QString("%1.%2").arg(QString().sprintf("%03d", nextSequenceID)).arg(QString(fmt));
+ else
+ filename += seqPrefix + (seqPrefix.isEmpty() ? "" : "_") + QString("%1_%2.%3").arg(QString().sprintf("%03d", nextSequenceID)).arg(ts).arg(QString(fmt));
- QFile fits_temp_file(filename);
- if (!fits_temp_file.open(QIODevice::WriteOnly))
- {
- qDebug() << "ISD:CCD Error: Unable to open " << fits_temp_file.fileName() << endl;
- emit BLOBUpdated(NULL);
- return;
- }
+ QFile fits_temp_file(filename);
+ if (!fits_temp_file.open(QIODevice::WriteOnly))
+ {
+ qDebug() << "ISD:CCD Error: Unable to open " << fits_temp_file.fileName() << endl;
+ emit BLOBUpdated(NULL);
+ return;
+ }
- QDataStream out(&fits_temp_file);
+ QDataStream out(&fits_temp_file);
- for (nr=0; nr < (int) bp->size; nr += n)
- n = out.writeRawData( static_cast<char *> (bp->blob) + nr, bp->size - nr);
+ for (nr=0; nr < (int) bp->size; nr += n)
+ n = out.writeRawData( static_cast<char *> (bp->blob) + nr, bp->size - nr);
- fits_temp_file.close();
+ fits_temp_file.close();
}
if (BType == BLOB_FITS)
@@ -1227,7 +1227,7 @@ void CCD::processBLOB(IBLOB* bp)
QFile::remove(filename);
}
// Unless we have cfitsio, we're done.
-#ifdef HAVE_CFITSIO
+ #ifdef HAVE_CFITSIO
if (BType == BLOB_FITS && targetChip->getCaptureMode() != FITS_WCSM)
{
QUrl fileURL(filename);
@@ -1263,56 +1263,46 @@ void CCD::processBLOB(IBLOB* bp)
switch (targetChip->getCaptureMode())
{
- case FITS_NORMAL:
- {
- FITSScale actualFilter = captureFilter;
- if (Options::autoStretch() && captureFilter == FITS_NONE)
- actualFilter = FITS_AUTO_STRETCH;
-
- if (normalTabID == -1 || Options::singlePreviewFITS() == false)
- tabRC = fv->addFITS(&fileURL, FITS_NORMAL, actualFilter, previewTitle);
- else if (fv->updateFITS(&fileURL, normalTabID, actualFilter) == false)
- {
- fv->removeFITS(normalTabID);
- tabRC = fv->addFITS(&fileURL, FITS_NORMAL, actualFilter, previewTitle);
- }
- else
- tabRC = normalTabID;
-
- if (tabRC >= 0)
- {
- normalTabID = tabRC;
- targetChip->setImage(fv->getView(normalTabID), FITS_NORMAL);
-
- emit newImage(fv->getView(normalTabID)->getDisplayImage());
- }
- else
- // If opening file fails, we treat it the same as exposure failure and recapture again if possible
- emit newExposureValue(targetChip, 0, IPS_ALERT);
- }
- break;
-
- case FITS_FOCUS:
- if (focusTabID == -1)
- tabRC = fv->addFITS(&fileURL, FITS_FOCUS, captureFilter);
- else if (fv->updateFITS(&fileURL, focusTabID, captureFilter) == false)
- {
- fv->removeFITS(focusTabID);
- tabRC = fv->addFITS(&fileURL, FITS_FOCUS, captureFilter);
- }
- else
- tabRC = focusTabID;
-
- if (tabRC >= 0)
- {
- focusTabID = tabRC;
- targetChip->setImage(fv->getView(focusTabID), FITS_FOCUS);
-
- emit newImage(fv->getView(focusTabID)->getDisplayImage());
- }
- else
- emit newExposureValue(targetChip, 0, IPS_ALERT);
- break;
+ case FITS_NORMAL:
+ if (normalTabID == -1 || Options::singlePreviewFITS() == false)
+ tabRC = fv->addFITS(&fileURL, FITS_NORMAL, captureFilter, previewTitle);
+ else if (fv->updateFITS(&fileURL, normalTabID, captureFilter) == false)
+ {
+ fv->removeFITS(normalTabID);
+ tabRC = fv->addFITS(&fileURL, FITS_NORMAL, captureFilter, previewTitle);
+ }
+ else
+ tabRC = normalTabID;
+
+ if (tabRC >= 0)
+ {
+ normalTabID = tabRC;
+ targetChip->setImage(fv->getView(normalTabID), FITS_NORMAL);
+ }
+ else
+ // If opening file fails, we treat it the same as exposure failure and recapture again if possible
+ emit newExposureValue(targetChip, 0, IPS_ALERT);
+ break;
+
+ case FITS_FOCUS:
+ if (focusTabID == -1)
+ tabRC = fv->addFITS(&fileURL, FITS_FOCUS, captureFilter);
+ else if (fv->updateFITS(&fileURL, focusTabID, captureFilter) == false)
+ {
+ fv->removeFITS(focusTabID);
+ tabRC = fv->addFITS(&fileURL, FITS_FOCUS, captureFilter);
+ }
+ else
+ tabRC = focusTabID;
+
+ if (tabRC >= 0)
+ {
+ focusTabID = tabRC;
+ targetChip->setImage(fv->getView(focusTabID), FITS_FOCUS);
+ }
+ else
+ emit newExposureValue(targetChip, 0, IPS_ALERT);
+ break;
case FITS_GUIDE:
if (guideTabID == -1)
@@ -1329,8 +1319,6 @@ void CCD::processBLOB(IBLOB* bp)
{
guideTabID = tabRC;
targetChip->setImage(fv->getView(guideTabID), FITS_GUIDE);
-
- emit newImage(fv->getView(guideTabID)->getDisplayImage());
}
else
emit newExposureValue(targetChip, 0, IPS_ALERT);
@@ -1357,7 +1345,7 @@ void CCD::processBLOB(IBLOB* bp)
break;
- default:
+ default:
break;
}
@@ -1365,7 +1353,7 @@ void CCD::processBLOB(IBLOB* bp)
fv->show();
}
-#endif
+ #endif
emit BLOBUpdated(bp);
@@ -1410,21 +1398,21 @@ void CCD::FITSViewerDestroyed()
void CCD::StreamWindowHidden()
{
- if (baseDevice->isConnected())
- {
- ISwitchVectorProperty *streamSP = baseDevice->getSwitch("CCD_VIDEO_STREAM");
- if (streamSP == NULL)
- streamSP = baseDevice->getSwitch("VIDEO_STREAM");
- if (streamSP)
+ if (baseDevice->isConnected())
{
- IUResetSwitch(streamSP);
- streamSP->sp[1].s = ISS_ON;
- streamSP->s = IPS_IDLE;
- clientManager->sendNewSwitch(streamSP);
+ ISwitchVectorProperty *streamSP = baseDevice->getSwitch("CCD_VIDEO_STREAM");
+ if (streamSP == NULL)
+ streamSP = baseDevice->getSwitch("VIDEO_STREAM");
+ if (streamSP)
+ {
+ IUResetSwitch(streamSP);
+ streamSP->sp[1].s = ISS_ON;
+ streamSP->s = IPS_IDLE;
+ clientManager->sendNewSwitch(streamSP);
+ }
}
- }
- streamWindow->disconnect();
+ streamWindow->disconnect();
}
bool CCD::hasGuideHead()
@@ -1464,13 +1452,13 @@ CCDChip * CCD::getChip(CCDChip::ChipType cType)
{
switch (cType)
{
- case CCDChip::PRIMARY_CCD:
- return primaryChip;
- break;
+ case CCDChip::PRIMARY_CCD:
+ return primaryChip;
+ break;
- case CCDChip::GUIDE_CCD:
- return guideChip;
- break;
+ case CCDChip::GUIDE_CCD:
+ return guideChip;
+ break;
}
return NULL;
@@ -1571,7 +1559,7 @@ CCD::UploadMode CCD::getUploadMode()
{
ISwitchVectorProperty *uploadModeSP=NULL;
- uploadModeSP = baseDevice->getSwitch("UPLOAD_MODE");
+ uploadModeSP = baseDevice->getSwitch("");
if (uploadModeSP == NULL)
{
@@ -1613,7 +1601,7 @@ bool CCD::setUploadMode(UploadMode mode)
switch (mode)
{
- case UPLOAD_CLIENT:
+ case UPLOAD_CLIENT:
modeS = IUFindSwitch(uploadModeSP, "UPLOAD_CLIENT");
if (modeS == NULL)
return false;
@@ -1621,7 +1609,7 @@ bool CCD::setUploadMode(UploadMode mode)
return true;
break;
- case UPLOAD_BOTH:
+ case UPLOAD_BOTH:
modeS = IUFindSwitch(uploadModeSP, "UPLOAD_BOTH");
if (modeS == NULL)
return false;
@@ -1629,7 +1617,7 @@ bool CCD::setUploadMode(UploadMode mode)
return true;
break;
- case UPLOAD_LOCAL:
+ case UPLOAD_LOCAL:
modeS = IUFindSwitch(uploadModeSP, "UPLOAD_LOCAL");
if (modeS == NULL)
return false;
diff --git a/kstars/indi/inditelescopelite.cpp b/kstars/indi/inditelescopelite.cpp
index 29642a9..0ff1299 100644
--- a/kstars/indi/inditelescopelite.cpp
+++ b/kstars/indi/inditelescopelite.cpp
@@ -74,7 +74,6 @@ void TelescopeLite::registerProperty(INDI::Property *prop)
}
}
}
-
}
void TelescopeLite::processNumber(INumberVectorProperty *nvp)
@@ -136,6 +135,29 @@ void TelescopeLite::processSwitch(ISwitchVectorProperty *svp)
}
+ /*if (!strcmp(svp->name, "TELESCOPE_SLEW_RATE")) {
+
+ int maxSlewRate = svp->nsp;
+
+ for(int i = 0; i < maxSlewRate; ++i) {
+ if(svp->sp[i].s == ISS_ON) {
+ if(i) {
+ setSlewDecreasable(true);
+ } else {
+ setSlewDecreasable(false);
+ }
+ if(i == maxSlewRate - 1) {
+ setSlewIncreasable(false);
+ } else {
+ setSlewDecreasable(true);
+ }
+ slewRateIndex = i;
+ break;
+ }
+ }
+ return;
+ }*/
+
//DeviceDecorator::processSwitch(svp);
}
@@ -389,7 +411,7 @@ bool TelescopeLite::sendCoords(SkyPoint *ScopeTarget)
if (targetAlt < 0)
{
if (false/*KMessageBox::warningContinueCancel(NULL, i18n("Requested altitude is below the horizon. Are you sure you want to proceed?"), i18n("Telescope Motion"),
- KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString("telescope_coordintes_below_horizon_warning")) == KMessageBox::Cancel*/)
+ KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString("telescope_coordintes_below_horizon_warning")) == KMessageBox::Cancel*/)
{
if (EqProp)
{
diff --git a/kstars/indi/inditelescopelite.h b/kstars/indi/inditelescopelite.h
index 1507ab6..d5228eb 100644
--- a/kstars/indi/inditelescopelite.h
+++ b/kstars/indi/inditelescopelite.h
@@ -17,7 +17,6 @@
#include "kstarslite/skyobjectlite.h"
class ClientManagerLite;
-class INDI::BaseDevice;
/**
* @class Telescope
diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg
index 9aef696..7744eb8 100644
--- a/kstars/kstars.kcfg
+++ b/kstars/kstars.kcfg
@@ -1682,4 +1682,13 @@
<default>0</default>
</entry>
</group>
+ <group name="INDI Lite">
+ <entry name="LastServer" type="String">
+ <label>The address of last used server</label>
+ </entry>
+ <entry name="LastServerPort" type="UInt">
+ <label>The port of last used server</label>
+ <default>7624</default>
+ </entry>
+ </group>
</kcfg>
diff --git a/kstars/kstarslite.cpp b/kstars/kstarslite.cpp
index f3d7b97..3f037a3 100644
--- a/kstars/kstarslite.cpp
+++ b/kstars/kstarslite.cpp
@@ -22,6 +22,7 @@
#include <QQuickWindow>
#include <QSurfaceFormat>
#include "indi/clientmanagerlite.h"
+#include "kstarslite/imageprovider.h"
#include "kspaths.h"
@@ -65,6 +66,8 @@ KStarsLite::KStarsLite( bool doSplash, bool startClock, const QString &startDate
m_SkyMapLite = SkyMapLite::createInstance();
m_Engine.rootContext()->setContextProperty("SkyMapLite", m_SkyMapLite);
+ m_imgProvider = new ImageProvider;
+ m_Engine.addImageProvider(QLatin1String("images"), m_imgProvider);
//qmlRegisterType<SkyPoint>("skymaplite",1,0,"SkyMapLite");
#ifdef Q_OS_ANDROID
@@ -82,12 +85,19 @@ KStarsLite::KStarsLite( bool doSplash, bool startClock, const QString &startDate
QQuickItem *skyMapLiteWrapper = m_RootObject->findChild<QQuickItem*>("skyMapLiteWrapper");
m_SkyMapLite->setParentItem(skyMapLiteWrapper);
+ // Whenever the wrapper's(parent) dimensions changed, change SkyMapLite too
+ connect(skyMapLiteWrapper, &QQuickItem::widthChanged, m_SkyMapLite, &SkyMapLite::resizeItem);
+ connect(skyMapLiteWrapper, &QQuickItem::heightChanged, m_SkyMapLite, &SkyMapLite::resizeItem);
+
+ m_SkyMapLite->resizeItem(); /* Set initial size pf SkyMapLite. Without it on Android SkyMapLite is
+ not displayed until screen orientation is not changed */
+
//QQuickWindow *mainWindow = m_RootObject->findChild<QQuickWindow*>("mainWindow");
QQuickWindow *mainWindow = static_cast<QQuickWindow *>(m_Engine.rootObjects()[0]);
QSurfaceFormat format = mainWindow->format();
format.setSamples(4);
- format.setSwapBehavior (QSurfaceFormat::TripleBuffer);
+ format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
mainWindow->setFormat(format);
connect( qApp, SIGNAL( aboutToQuit() ), this, SLOT( slotAboutToQuit() ) );
@@ -142,6 +152,10 @@ KStarsLite *KStarsLite::createInstance( bool doSplash, bool clockrunning, const
return nullptr;
}
+KStarsLite::~KStarsLite() {
+ delete m_imgProvider;
+}
+
void KStarsLite::fullUpdate() {
m_KStarsData->setFullTimeUpdate();
updateTime();
@@ -179,7 +193,6 @@ void KStarsLite::updateTime( const bool automaticDSTchange ) {
void KStarsLite::writeConfig() {
Options::self()->save();
-
//Store current simulation time
//Refer to // FIXME: Used in kstarsdcop.cpp only in kstarsdata.cpp
//data()->StoredDate = data()->lt();
@@ -197,7 +210,6 @@ void KStarsLite::slotAboutToQuit()
//explicitly save the colorscheme data to the config file
data()->colorScheme()->saveToConfig();
-
//synch the config file with the Config object
writeConfig();
}
diff --git a/kstars/kstarslite.h b/kstars/kstarslite.h
index 757d0ee..a87e2e7 100644
--- a/kstars/kstarslite.h
+++ b/kstars/kstarslite.h
@@ -27,6 +27,8 @@ class KStarsData;
class SkyMapLite;
class SkyPoint;
class GeoLocation;
+class ImageProvider;
+
#ifdef INDI_FOUND
class ClientManagerLite;
#endif
@@ -73,7 +75,7 @@ public:
inline static KStarsLite *Instance() { return pinstance; }
/** Destructor. Does nothing yet*/
- virtual ~KStarsLite() { }
+ virtual ~KStarsLite();
/** @return pointer to SkyMapLite object which draws SkyMap. */
inline SkyMapLite* map() const { return m_SkyMapLite; }
@@ -81,6 +83,8 @@ public:
/** @return pointer to KStarsData object which contains application data. */
inline KStarsData* data() const { return m_KStarsData; }
+ inline ImageProvider *imageProvider() const { return m_imgProvider; }
+
inline QQmlApplicationEngine *qmlEngine() { return &m_Engine; }
/** @short used from QML to update positions of sky objects and update SkyMapLite */
@@ -90,7 +94,6 @@ public:
/** @return pointer to KStarsData object which handles connection to INDI server. */
inline ClientManagerLite *clientManagerLite() const { return m_clientManager; }
#endif
-
signals:
/** Sent when KStarsData finishes loading data */
void dataLoadFinished();
@@ -135,7 +138,9 @@ private:
//QQuickItem *m_SkyMapLiteWrapper;
bool StartClockRunning;
- KStarsData* m_KStarsData;
+ KStarsData *m_KStarsData;
+ ImageProvider *m_imgProvider;
+
#ifdef INDI_FOUND
ClientManagerLite *m_clientManager;
#endif
diff --git a/kstars/kstarslite/imageprovider.cpp b/kstars/kstarslite/imageprovider.cpp
new file mode 100644
index 0000000..7a73523
--- /dev/null
+++ b/kstars/kstarslite/imageprovider.cpp
@@ -0,0 +1,30 @@
+/** *************************************************************************
+ imageprovider.cpp - K Desktop Planetarium
+ -------------------
+ begin : 22/07/2016
+ copyright : (C) 2016 by Artem Fedoskin
+ email : afedoskin3@gmail.com
+ ***************************************************************************/
+/** *************************************************************************
+ * *
+ * This program 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 "imageprovider.h"
+
+ImageProvider::ImageProvider()
+ :QQuickImageProvider(QQmlImageProviderBase::Image)
+{
+
+}
+
+QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) {
+ return images.value(id);
+}
+
+void ImageProvider::addImage(const QString &id, QImage image) {
+ images.insert(id, image);
+}
diff --git a/kstars/kstarslite/imageprovider.h b/kstars/kstarslite/imageprovider.h
new file mode 100644
index 0000000..7d11558
--- /dev/null
+++ b/kstars/kstarslite/imageprovider.h
@@ -0,0 +1,39 @@
+/** *************************************************************************
+ imageprovider.h - K Desktop Planetarium
+ -------------------
+ begin : 22/07/2016
+ copyright : (C) 2016 by Artem Fedoskin
+ email : afedoskin3@gmail.com
+ ***************************************************************************/
+/** *************************************************************************
+ * *
+ * This program 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 IMAGEPROVIDER_H_
+#define IMAGEPROVIDER_H_
+
+#include <QQuickImageProvider>
+
+ /**
+ * @class ImageProvider
+ * This class makes it possible to use QImages in QML
+ *
+ * @author Artem Fedoskin
+ * @version 1.0
+ */
+class ImageProvider : public QQuickImageProvider
+{
+public:
+ ImageProvider();
+ virtual QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize);
+ void addImage(const QString &id, QImage image);
+private:
+ QHash<QString, QImage> images;
+};
+#endif
+
+
diff --git a/kstars/kstarslite/qml/indi/CMakeLists.txt b/kstars/kstarslite/qml/indi/CMakeLists.txt
index 8d82e42..742f480 100644
--- a/kstars/kstarslite/qml/indi/CMakeLists.txt
+++ b/kstars/kstarslite/qml/indi/CMakeLists.txt
@@ -1,16 +1,17 @@
#install_files
install( FILES INDIControlPanel.qml
DevicePanel.qml
+ ImagePreview.qml
DESTINATION ${KDE_INSTALL_DATADIR}/kstars/kstarslite/qml/indi )
install( FILES modules/MotionControl.qml
modules/Led.qml
modules/Property.qml
modules/KSTab.qml
- modules/KSButton.qml
+ modules/KSButtonSwitch.qml
+ modules/KSButtonsSwitchRow.qml
modules/KSINDIText.qml
- modules/KSINDINumber.qml
- modules/KSINDINumberField.qml
+ modules/KSINDITextField.qml
modules/KSCheckBox.qml
DESTINATION ${KDE_INSTALL_DATADIR}/kstars/kstarslite/qml/indi/modules )
diff --git a/kstars/kstarslite/qml/indi/DevicePanel.qml b/kstars/kstarslite/qml/indi/DevicePanel.qml
index 0ce87f4..621cec1 100644
--- a/kstars/kstarslite/qml/indi/DevicePanel.qml
+++ b/kstars/kstarslite/qml/indi/DevicePanel.qml
@@ -6,71 +6,87 @@ import QtQuick.Layouts 1.2
import QtQuick.Controls 1.4
import org.kde.kirigami 1.0 as Kirigami
-KSPage {
- id: devicesPage
- contentItem: deviceTabView
- TabView {
- id: deviceTabView
+Item {
+ id: devicesPanel
+ anchors.fill: parent
+ function showPage(backtoInit) {
+ devicesPage.showPage(backtoInit)
+ }
+
+ property string title
+
+ KSPage {
+ id: devicesPage
+ contentItem: deviceTabView
+ title: devicesPanel.title
+ TabView {
+ id: deviceTabView
- property var groups: []
- property var properties: []
- property var tabs: []
+ property var groups: []
+ property var properties: []
+ property var tabs: []
- Component.onCompleted: {
- if(Qt.platform.os != "android") {
- anchors.topMargin = 10 * num.dp
- anchors.leftMargin = 5 * num.dp
- anchors.rightMargin = 5 * num.dp
+ Component.onCompleted: {
+ if(Qt.platform.os != "android") {
+ anchors.topMargin = 10 * num.dp
+ anchors.leftMargin = 5 * num.dp
+ anchors.rightMargin = 5 * num.dp
+ }
+ anchors.bottomMargin = 5 * num.dp
+ anchors.bottom = parent.bottom
}
- anchors.bottomMargin = 5 * num.dp
- anchors.bottom = parent.bottom
- }
- Connections {
- target: ClientManagerLite
- onNewINDIProperty: {
- if(devicesPage.title === deviceName) {
- if(deviceTabView.groups.indexOf(groupName) == -1) {
- deviceTabView.groups.push(groupName)
- var newTabComp = Qt.createComponent("modules/KSTab.qml");
- var newTab = newTabComp.createObject(deviceTabView)
- newTab.title = groupName
+ Connections {
+ target: ClientManagerLite
+ onNewINDIProperty: {
+ if(devicesPage.title === deviceName) {
+ if(deviceTabView.groups.indexOf(groupName) == -1) {
+ deviceTabView.groups.push(groupName)
+ var newTabComp = Qt.createComponent("modules/KSTab.qml");
+ var newTab = newTabComp.createObject(deviceTabView)
+ newTab.title = groupName
- deviceTabView.tabs.push(newTab)
- if(groupName == "Motion Control") {
- var component = Qt.createComponent("modules/MotionControl.qml");
- var motionControl = component.createObject(newTab)
- motionControl.deviceName = deviceName
+ deviceTabView.tabs.push(newTab)
+ if(groupName == "Motion Control") {
+ var component = Qt.createComponent("modules/MotionControl.qml");
+ var motionControl = component.createObject(newTab)
+ motionControl.deviceName = deviceName
+ }
}
- }
- if(groupName != "Motion Control") {
- for(var i = 0; i < deviceTabView.count; ++i) {
- var tab = deviceTabView.getTab(i)
- if(tab.title === groupName) {
- var propComp = Qt.createComponent("modules/Property.qml");
- var property = propComp.createObject(tab.columnItem)
- property.name = propName
- property.label = label
- property.device = deviceName
+ if(groupName != "Motion Control") {
+ for(var i = 0; i < deviceTabView.count; ++i) {
+ var tab = deviceTabView.getTab(i)
+ if(tab.title === groupName) {
+ var propComp = Qt.createComponent("modules/Property.qml");
+ var property = propComp.createObject(tab.columnItem)
+ property.name = propName
+ property.label = label
+ property.device = deviceName
+ if(propName == "CCD_EXPOSURE") {
+ var imgPreviewComp = Qt.createComponent("ImagePreview.qml");
+ var imagePreview = imgPreviewComp.createObject(devicesPanel)
+ imagePreview.deviceName = devicesPanel.title
+ }
+ }
}
}
}
}
- }
- onRemoveINDIProperty: {
- for(var i = 0; i < deviceTabView.tabs.length; ++i) {
- var tab = deviceTabView.tabs[i]
- if(tab.title === groupName && groupName != "Motion Control") {
- var columnItem = deviceTabView.tabs[i].columnItem
- for(var c = 0; c < columnItem.children.length; ++c) {
- if(columnItem.children[c].name === propName) {
- columnItem.children[c].destroy()
+ onRemoveINDIProperty: {
+ for(var i = 0; i < deviceTabView.tabs.length; ++i) {
+ var tab = deviceTabView.tabs[i]
+ if(tab.title === groupName && groupName != "Motion Control") {
+ var columnItem = deviceTabView.tabs[i].columnItem
+ for(var c = 0; c < columnItem.children.length; ++c) {
+ if(columnItem.children[c].name === propName) {
+ columnItem.children[c].destroy()
+ }
+ }
+ if(columnItem.children.length == 0) {
+ var groups = deviceTabView.groups
+ groups.splice(groups.indexOf(groupName), 1)
+ tab.destroy()
}
- }
- if(columnItem.children.length == 0) {
- var groups = deviceTabView.groups
- groups.splice(groups.indexOf(groupName), 1)
- tab.destroy()
}
}
}
diff --git a/kstars/kstarslite/qml/indi/INDIControlPanel.qml b/kstars/kstarslite/qml/indi/INDIControlPanel.qml
index e0f2f2c..008d0ee 100644
--- a/kstars/kstarslite/qml/indi/INDIControlPanel.qml
+++ b/kstars/kstarslite/qml/indi/INDIControlPanel.qml
@@ -14,20 +14,25 @@ KSPage {
property bool connected: ClientManagerLite.connected
Component.onCompleted: {
- ClientManagerLite.setHost("localhost", parseInt(7624))
+ if(Qt.platform.os != "android") {
+ ClientManagerLite.setHost("localhost", parseInt(7624))
+ }
+ var i = 100
+ while(i--) {
+ console.log(ClientManagerLite.lastUsedServer())
+ }
}
- onHeightChanged: {
+ /*onHeightChanged: {
devicesPage.height = indiPage.height - devicesPage.y
- }
+ }*/
onConnectedChanged: {
- if(!connected) {
+ if(!indiPage.connected) {
for(var i = 0; i < devicesModel.count; ++i) {
devicesModel.get(i).panel.destroy()
}
devicesModel.clear()
- //showPage()
showPassiveNotification("Disconnected from the server")
}
}
@@ -46,7 +51,7 @@ KSPage {
}
ColumnLayout {
- visible: !connected
+ visible: !indiPage.connected
anchors {
left: parent.left
right: parent.right
@@ -67,6 +72,7 @@ KSPage {
placeholderText: "IP"
Layout.alignment: Qt.AlignHCenter
implicitWidth: parent.width*0.8
+ text: ClientManagerLite.lastUsedServer()
}
Controls.TextField {
@@ -74,22 +80,25 @@ KSPage {
placeholderText: "Port"
Layout.alignment: Qt.AlignHCenter
implicitWidth: parent.width*0.2
+ text: ClientManagerLite.lastUsedPort()
}
}
}
Kirigami.Label {
id: connectedTo
- visible: connected
+ visible: indiPage.connected
text: "Connected to " + ClientManagerLite.connectedHost
}
Controls.Button {
- text: connected ? "Disconnect" : "Connect"
+ text: indiPage.connected ? "Disconnect" : "Connect"
onClicked: {
- if(!connected) {
+ if(!indiPage.connected) {
if(ClientManagerLite.setHost(ipHost.text, parseInt(portHost.text))) {
showPassiveNotification("Successfully connected to the server")
+ ClientManagerLite.setLastUsedServer(ipHost.text)
+ ClientManagerLite.setLastUsedPort(portHost.text)
} else {
showPassiveNotification("Couldn't connect to the server")
}
@@ -103,13 +112,7 @@ KSPage {
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
- visible : connected
-
- Component.onCompleted: {
- for(var i = 0; i < 100; ++i) {
- console.log(height)
- }
- }
+ visible : indiPage.connected
Rectangle {
Layout.fillWidth: true
@@ -142,6 +145,9 @@ KSPage {
}
}
}
+ onNewINDIMessage: {
+ showPassiveNotification(message)
+ }
}
}
}
@@ -176,6 +182,5 @@ KSPage {
}
}
}
-
}
}
diff --git a/kstars/kstarslite/qml/indi/ImagePreview.qml b/kstars/kstarslite/qml/indi/ImagePreview.qml
new file mode 100644
index 0000000..8358df1
--- /dev/null
+++ b/kstars/kstarslite/qml/indi/ImagePreview.qml
@@ -0,0 +1,79 @@
+import QtQuick 2.4
+import QtQuick.Window 2.2
+import "../modules"
+import "../constants" 1.0
+import QtQuick.Layouts 1.2
+import QtQuick.Controls 1.4 as Controls
+import org.kde.kirigami 1.0 as Kirigami
+import QtQuick.Dialogs 1.2 as Dialogs
+
+KSPage {
+ id: imagePreview
+ contentItem: imgPreviewColumn
+ title: "Image Preview - " + deviceName
+
+ property string deviceName: title
+ property Item buttonRow
+
+ Item {
+ id: imgPreviewColumn
+ anchors.bottom: parent.bottom
+
+ RowLayout {
+ id: saveButtons
+ anchors {
+ top: parent.top
+ left: parent.left
+ margins: 10 * num.dp
+ }
+ Layout.fillWidth: true
+
+ spacing: 5 * num.dp
+ Controls.Button {
+ text: "Save As"
+
+ onClicked: {
+ ClientManagerLite.saveDisplayImage()
+ }
+ }
+ }
+
+ Image {
+ id: image
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ fillMode: Image.PreserveAspectFit
+ anchors {
+ top: saveButtons.bottom
+ left: parent.left
+ right: parent.right
+ bottom: parent.bottom
+
+ margins: 15 * num.dp
+ }
+ }
+
+ }
+
+ Connections {
+ target: ClientManagerLite
+ onNewINDIBLOBImage: {
+ image.source = "image://images/ccdPreview"
+ imagePreview.showPage()
+ }
+ onCreateINDIButton: {
+ if(deviceName == imagePreview.deviceName) {
+ if(propName == "UPLOAD_MODE") {
+ if(imagePreview.buttonRow == null) {
+ var buttonRowComp = Qt.createComponent("modules/KSButtonsSwitchRow.qml");
+ imagePreview.buttonRow = buttonRowComp.createObject(saveButtons)
+ imagePreview.buttonRow.deviceName = deviceName
+ imagePreview.buttonRow.propName = propName
+ imagePreview.buttonRow.exclusive = exclusive
+ }
+ imagePreview.buttonRow.addButton(propText, switchName, checked, enabled)
+ }
+ }
+ }
+ }
+}
diff --git a/kstars/kstarslite/qml/indi/modules/KSButton.qml b/kstars/kstarslite/qml/indi/modules/KSButtonSwitch.qml
index dedb200..64d0c6b 100644
--- a/kstars/kstarslite/qml/indi/modules/KSButton.qml
+++ b/kstars/kstarslite/qml/indi/modules/KSButtonSwitch.qml
@@ -5,9 +5,10 @@ Button {
property string switchName: ""
property Row parentRow
checkable: true
+ checked: true
onClicked: {
parentRow.sendNewSwitch(switchName, null)
- checked = true
+ Qt.inputMethod.hide()
}
}
diff --git a/kstars/kstarslite/qml/indi/modules/KSButtonsRow.qml b/kstars/kstarslite/qml/indi/modules/KSButtonsSwitchRow.qml
index ba62c26..ac1d1ba 100644
--- a/kstars/kstarslite/qml/indi/modules/KSButtonsRow.qml
+++ b/kstars/kstarslite/qml/indi/modules/KSButtonsSwitchRow.qml
@@ -26,7 +26,7 @@ Row {
function addButton(propText, switchName, initChecked, enabled) {
- var buttonComp = Qt.createComponent("KSButton.qml");
+ var buttonComp = Qt.createComponent("KSButtonSwitch.qml");
var button = buttonComp.createObject(this)
button.text = propText
button.switchName = switchName
diff --git a/kstars/kstarslite/qml/indi/modules/KSINDINumber.qml b/kstars/kstarslite/qml/indi/modules/KSINDINumber.qml
deleted file mode 100644
index d1ce9a0..0000000
--- a/kstars/kstarslite/qml/indi/modules/KSINDINumber.qml
+++ /dev/null
@@ -1,37 +0,0 @@
-import QtQuick 2.4
-import QtQuick.Layouts 1.2
-import "../../constants" 1.0
-import org.kde.kirigami 1.0 as Kirigami
-
-Column {
- Layout.fillHeight: true
- spacing: 5 * num.dp
- width: parent.width
- property string propLabel: ""
-
- Kirigami.Label {
- text: propLabel
- }
-
- Rectangle {
- id:separator
- color: "grey"
- height: 1 * num.dp
- width: parent.width
- }
-
- function addText(propText, writable) {
- var textItem
- if(writable) {
- var textComp = Qt.createComponent("KSINDINumberField.qml");
- textItem = textComp.createObject(columnProp)
- } else {
- textItem = Qt.createQmlObject('import QtQuick 2.4
- import QtQuick.Layouts 1.2
- Text {
- }', this)
- }
- textItem.textField.text = propText
- //textItem.anchors.top = separator.bottom
- }
-}
diff --git a/kstars/kstarslite/qml/indi/modules/KSINDINumberField.qml b/kstars/kstarslite/qml/indi/modules/KSINDINumberField.qml
deleted file mode 100644
index 6547bb5..0000000
--- a/kstars/kstarslite/qml/indi/modules/KSINDINumberField.qml
+++ /dev/null
@@ -1,20 +0,0 @@
-import QtQuick 2.4
-import QtQuick.Layouts 1.2
-import "../../constants" 1.0
-import org.kde.kirigami 1.0 as Kirigami
-
-import QtQuick 2.4
-import QtQuick.Layouts 1.1
-import QtQuick.Controls 1.4
-
-RowLayout {
- Layout.fillWidth: true
- Layout.fillHeight: true
- property Item textField: field
- TextField {
- id: field
- }
- Button {
- text:"Set"
- }
-}
diff --git a/kstars/kstarslite/qml/indi/modules/KSINDIText.qml b/kstars/kstarslite/qml/indi/modules/KSINDIText.qml
index b535be9..012d43e 100644
--- a/kstars/kstarslite/qml/indi/modules/KSINDIText.qml
+++ b/kstars/kstarslite/qml/indi/modules/KSINDIText.qml
@@ -20,20 +20,24 @@ Column {
width: parent.width
}
- function addText(propText, writable) {
+ function addField(isNumber, deviceName, propName, fieldName, propText, writable) {
var textItem
if(writable) {
- textItem = Qt.createQmlObject('import QtQuick 2.4
- import QtQuick.Controls 1.4
- TextField {
- }', this)
+ var textComp = Qt.createComponent("KSINDITextField.qml");
+ textItem = textComp.createObject(columnProp)
+ textItem.deviceName = deviceName
+ textItem.propName = propName
+ textItem.fieldName = fieldName
+ textItem.textField.text = propText
+ textItem.isNumber = isNumber
} else {
- textItem = Qt.createQmlObject('import QtQuick 2.4
+ textItem = Qt.createQmlObject('import QtQuick 2.4
import QtQuick.Layouts 1.2
Text {
+
}', this)
+ textItem.text = propText
}
- textItem.text = propText
//textItem.anchors.top = separator.bottom
}
}
diff --git a/kstars/kstarslite/qml/indi/modules/KSINDITextField.qml b/kstars/kstarslite/qml/indi/modules/KSINDITextField.qml
new file mode 100644
index 0000000..e3f4711
--- /dev/null
+++ b/kstars/kstarslite/qml/indi/modules/KSINDITextField.qml
@@ -0,0 +1,66 @@
+import QtQuick 2.4
+import QtQuick.Layouts 1.2
+import "../../constants" 1.0
+import org.kde.kirigami 1.0 as Kirigami
+
+import QtQuick 2.4
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 1.4
+
+Row {
+ id: textRow
+ spacing: 5 * num.dp
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+
+ property Item textField: field
+ property bool isNumber: true // false - text, true - number
+
+ property string deviceName
+ property string propName
+ property string fieldName
+
+ TextField {
+ id: field
+ }
+ Button {
+ text:"Set"
+ onClicked: {
+ if(isNumber) {
+ ClientManagerLite.sendNewINDINumber(deviceName, propName, fieldName, field.text)
+ } else {
+ ClientManagerLite.sendNewINDIText(deviceName, propName, fieldName, field.text)
+ }
+ Qt.inputMethod.hide()
+ }
+ }
+
+ Connections {
+ target: ClientManagerLite
+ onNewINDINumber: {
+ if(isNumber) {
+ if(textRow.deviceName == deviceName) {
+ if(textRow.propName == propName) {
+ if(textRow.fieldName == numberName) {
+ field.text = value
+ }
+ }
+ }
+ }
+ }
+ onNewINDIText: {
+ if(!isNumber) {
+ if(textRow.deviceName == deviceName) {
+ if(textRow.propName == propName) {
+ if(textRow.fieldName == fieldName) {
+ field.text = text
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/kstars/kstarslite/qml/indi/modules/KSTab.qml b/kstars/kstarslite/qml/indi/modules/KSTab.qml
index 2dc5a37..ef8cb75 100644
--- a/kstars/kstarslite/qml/indi/modules/KSTab.qml
+++ b/kstars/kstarslite/qml/indi/modules/KSTab.qml
@@ -22,6 +22,7 @@ Tab {
Component.onCompleted: {
contentItem: column
+ flickableItem.flickableDirection = Flickable.VerticalFlick
}
Column {
diff --git a/kstars/kstarslite/qml/indi/modules/MotionControl.qml b/kstars/kstarslite/qml/indi/modules/MotionControl.qml
index 0836db1..d667167 100644
--- a/kstars/kstarslite/qml/indi/modules/MotionControl.qml
+++ b/kstars/kstarslite/qml/indi/modules/MotionControl.qml
@@ -18,7 +18,8 @@ ColumnLayout {
enabled: buttonsEnabled
property string deviceName
- property var telescope
+ property var telescope: ClientManagerLite.getTelescope(deviceName)
+ property bool buttonsEnabled: telescope.isConnected()
Connections {
target: ClientManagerLite
@@ -34,8 +35,6 @@ ColumnLayout {
}
}
- property bool buttonsEnabled: ClientManagerLite.isDeviceConnected(deviceName)
-
//Row 1
RowLayout {
Layout.fillHeight: true
diff --git a/kstars/kstarslite/qml/indi/modules/Property.qml b/kstars/kstarslite/qml/indi/modules/Property.qml
index 1c26568..63fdf3a 100644
--- a/kstars/kstarslite/qml/indi/modules/Property.qml
+++ b/kstars/kstarslite/qml/indi/modules/Property.qml
@@ -33,6 +33,12 @@ ColumnLayout {
}
}
+ Item {
+ id: propertyHolder
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+
Rectangle {
height: num.dp
color: "grey"
@@ -52,7 +58,7 @@ ColumnLayout {
if(deviceName == device) {
if(name == propName) {
if(buttonRow == null) {
- var buttonRowComp = Qt.createComponent("KSButtonsRow.qml");
+ var buttonRowComp = Qt.createComponent("KSButtonsSwitchRow.qml");
buttonRow = buttonRowComp.createObject(columnProp)
buttonRow.deviceName = device
buttonRow.propName = name
@@ -66,7 +72,7 @@ ColumnLayout {
if(deviceName == device) {
if(name == propName) {
if(buttonRow == null) {
- var buttonRowComp = Qt.createComponent("KSButtonsRow.qml");
+ var buttonRowComp = Qt.createComponent("KSButtonsSwitchRow.qml");
buttonRow = buttonRowComp.createObject(columnProp)
buttonRow.deviceName = device
buttonRow.propName = name
@@ -100,7 +106,7 @@ ColumnLayout {
if(name == propName) {
var indiTextComp = Qt.createComponent("KSINDIText.qml");
var indiText = indiTextComp.createObject(columnProp)
- indiText.addText(propText, write)
+ indiText.addField(false, deviceName, propName, fieldName, propText, write)
indiText.propLabel = propLabel
}
}
@@ -108,9 +114,9 @@ ColumnLayout {
onCreateINDINumber: {
if(deviceName == device) {
if(name == propName) {
- var indiNumComp = Qt.createComponent("KSINDINumber.qml");
+ var indiNumComp = Qt.createComponent("KSINDIText.qml");
var indiNum = indiNumComp.createObject(columnProp)
- indiNum.addText(propText, write)
+ indiNum.addField(true, deviceName, propName, numberName, propText, write)
indiNum.propLabel = propLabel
}
}
diff --git a/kstars/kstarslite/qml/main.qml b/kstars/kstarslite/qml/main.qml
index 6ee7ec5..fadfc50 100644
--- a/kstars/kstarslite/qml/main.qml
+++ b/kstars/kstarslite/qml/main.qml
@@ -6,6 +6,7 @@ import "indi"
import "constants" 1.0
import QtQuick.Controls 1.4 as Controls
import org.kde.kirigami 1.0 as Kirigami
+import QtSensors 5.0
Kirigami.ApplicationWindow {
id: mainWindow
@@ -20,7 +21,7 @@ Kirigami.ApplicationWindow {
property Item initPage: initPage
- pageStack.initialPage: initPage
+ pageStack.initialPage: null
property var contextActions: []
@@ -41,7 +42,6 @@ Kirigami.ApplicationWindow {
contentItem.anchors.topMargin: 0
wideScreen: true
-
Item {
id: pagesWindow
anchors.fill: parent
@@ -70,12 +70,44 @@ Kirigami.ApplicationWindow {
}
}
]
+
+ Kirigami.Label {
+ text: "Magnitude Limit"
+ }
+
+ Controls.Slider {
+ Layout.fillWidth: true
+
+ maximumValue: 5.75954
+ minimumValue: 1.18778
+ value: SkyMapLite.magLim
+ onValueChanged: {
+ SkyMapLite.magLim = value
+ }
+ }
}
contextDrawer: Kirigami.ContextDrawer {
id: contextDrawer
property Item contextMenu
+ actions: initPage.actions.contextualActions
+
+ onOpenedChanged: {
+ if(opened) {
+ //If no telescopes are connected then we disable actions in context drawer
+ var areTelescopesLoaded = false
+ if(ClientManagerLite.connected) {
+ for(var i = 0; i < mainWindow.telescopes.length; ++i) {
+ if(mainWindow.telescopes[i].isConnected()) {
+ areTelescopesLoaded = true
+ }
+ }
+ }
+ initPage.telescopeLoaded = areTelescopesLoaded
+ }
+ }
+
Component.onCompleted: {
var contDrawMenuComp = Qt.createComponent("modules/ContextDrawerMenu.qml");
contextMenu = contDrawMenuComp.createObject(contentItem)
@@ -91,6 +123,12 @@ Kirigami.ApplicationWindow {
title: "Main Screen"
visible:true
+ TapSensor {
+ onReadingChanged: {
+ console.log(reading.doubleTap)
+ }
+ }
+
onVisibleChanged: {
if(visible) {
SkyMapLite.update() //Update SkyMapLite once user opened initPage
@@ -98,6 +136,7 @@ Kirigami.ApplicationWindow {
}
property bool isClickedObject: false
+ property bool telescopeLoaded: false
Connections {
target: SkyMapLite
@@ -113,10 +152,17 @@ Kirigami.ApplicationWindow {
}
}
- property bool telescopeLoaded: false
-
Connections {
target: ClientManagerLite
+ /*onDeviceConnected: {
+ var isConnected = false
+ for(var i = 0; i < telescopes.length; ++i) {
+ if(telescopes[i].isConnected()) {
+ isConnected = true
+ }
+ }
+ initPage.telescopeLoaded = Connected
+ }*/
onTelescopeAdded: {
telescopes.push(newTelescope)
initPage.telescopeLoaded = true
@@ -126,13 +172,11 @@ Kirigami.ApplicationWindow {
actions {
contextualActions: [
Kirigami.Action {
- enabled: telescopeLoaded
+ enabled: initPage.telescopeLoaded
text: "Slew to object"
onTriggered: {
- var isConnected = false
for(var i = 0; i < telescopes.length; ++i) {
if(telescopes[i].isConnected()) {
- isConnected = true
if(initPage.isClickedObject) {
telescopes[i].slew(ClickedObject)
} else {
@@ -140,15 +184,12 @@ Kirigami.ApplicationWindow {
}
}
}
- if(!isConnected) telescopeLoaded = false
- else telescopeLoaded = true
}
},
Kirigami.Action {
- enabled: telescopeLoaded
+ enabled: initPage.telescopeLoaded
text: "Sync"
onTriggered: {
- var isConnected = false
for(var i = 0; i < telescopes.length; ++i) {
if(telescopes[i].isConnected()) {
if(initPage.isClickedObject) {
@@ -158,8 +199,6 @@ Kirigami.ApplicationWindow {
}
}
}
- if(!isConnected) telescopeLoaded = false
- else telescopeLoaded = true
}
}
]
@@ -188,12 +227,41 @@ Kirigami.ApplicationWindow {
anchors.fill: parent
color: "black"
- onWidthChanged: {
- SkyMapLite.width = width
- }
- onHeightChanged: {
- SkyMapLite.height = height
+ Rectangle {
+ id: tapCircle
+ z: 1
+ width: 20 * num.dp
+ height: width
+ color: "grey"
+ radius: width*0.5
+ opacity: 0
+
+ Connections {
+ target: SkyMapLite
+ onPosClicked: {
+ tapCircle.x = pos.x - tapCircle.width * 0.5
+ tapCircle.y = pos.y - tapCircle.height * 0.5
+ tapAnimation.start()
+ }
+ }
+
+ SequentialAnimation on opacity {
+ id: tapAnimation
+ OpacityAnimator { from: 0; to: 0.8; duration: 100 }
+ OpacityAnimator { from: 0.8; to: 0; duration: 400 }
+ }
}
+
+ /*MouseArea {
+ z: 1
+ anchors.fill: parent
+ propagateComposedEvents: true
+ onClicked: {
+ tapCircle.x = mouseX - tapCircle.width/2
+ tapCircle.y = mouseY - tapCircle.height/2
+ mouse.accepted = false
+ }
+ }*/
}
MouseArea {
diff --git a/kstars/kstarslite/qml/modules/KSPage.qml b/kstars/kstarslite/qml/modules/KSPage.qml
index 5f0dc53..2c4c5bd 100644
--- a/kstars/kstarslite/qml/modules/KSPage.qml
+++ b/kstars/kstarslite/qml/modules/KSPage.qml
@@ -66,19 +66,15 @@ Kirigami.Page {
anchors.left: parent.left
anchors.leftMargin: 10
- Image {
- id: backButton
- visible: prevPage !== null
- source: "images/" + num.density + "/icons/back.png"
- anchors {
- left: parent.left
- top: parent.top
- bottom: parent.bottom
- }
+ Rectangle {
+ id: backRect
+ radius: width * 0.5
+ color: "grey"
+ opacity: 0
+ state: "released"
- Component.onCompleted: {
- console.log(num.density)
- }
+ width: 30 * num.dp
+ height: 30 * num.dp
MouseArea {
anchors.fill: parent
@@ -87,15 +83,52 @@ Kirigami.Page {
}
onPressedChanged: {
if(pressed) {
- backButton.opacity = 0.6
+ backRect.state = "pressed"
} else {
- backButton.opacity = 1
+ backRect.state = "released"
}
}
}
+
+ states: [
+ State {
+ name: "pressed"
+ PropertyChanges { target: backRect; opacity: 0.6 }
+ PropertyChanges { target: backButton; opacity: 0.6 }
+ },
+ State {
+ name: "released"
+ PropertyChanges { target: backRect; opacity: 0 }
+ PropertyChanges { target: backButton; opacity: 1 }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: "pressed"
+ to: "released"
+ OpacityAnimator { target: backRect; duration: 1000}
+ OpacityAnimator { target: backButton; duration: 1000}
+ },
+ Transition {
+ from: "released"
+ to: "released"
+ OpacityAnimator { target: backRect; duration: 1000}
+ OpacityAnimator { target: backButton; duration: 1000}
+ }
+ ]
+ }
+
+ Image {
+ id: backButton
+ visible: prevPage !== null
+ source: "images/" + num.density + "/icons/back.png"
+ sourceSize.height: pageTitle.width
+ anchors.centerIn: backRect
}
Text {
+ id: pageTitle
visible: prevPage !== null
text: title
color: "white"
@@ -103,6 +136,7 @@ Kirigami.Page {
anchors.leftMargin: 20
}
}
+
Rectangle {
id: headerSeparator
anchors {
diff --git a/kstars/kstarslite/skyitems/skynodes/crosshairnode.cpp b/kstars/kstarslite/skyitems/skynodes/crosshairnode.cpp
index 50b33e5..236975b 100644
--- a/kstars/kstarslite/skyitems/skynodes/crosshairnode.cpp
+++ b/kstars/kstarslite/skyitems/skynodes/crosshairnode.cpp
@@ -31,12 +31,12 @@ CrosshairNode::CrosshairNode(INDI::BaseDevice *baseDevice, RootNode *rootNode)
QSGGeometry *linesGeo = new QSGGeometry (QSGGeometry::defaultAttributes_Point2D(),0);
lines->setGeometry(linesGeo);
- lines->setFlag(QSGNode::OwnsGeometry);
+ //lines->setFlag(QSGNode::OwnsGeometry);
material = new QSGFlatColorMaterial;
lines->setMaterial(material);
- lines->setFlag(QSGNode::OwnsMaterial);
+ //lines->setFlag(QSGNode::OwnsMaterial);
linesGeo->setDrawingMode(GL_LINES);
linesGeo->allocate(8);
@@ -48,6 +48,14 @@ CrosshairNode::CrosshairNode(INDI::BaseDevice *baseDevice, RootNode *rootNode)
CrosshairNode::~CrosshairNode() {
labelsItem->deleteLabel(label);
+ removeChildNode(lines);
+ removeChildNode(el1);
+ removeChildNode(el2);
+ delete lines->geometry();
+ delete material;
+ delete lines;
+ delete el1;
+ delete el2;
}
void CrosshairNode::setColor(QColor color) {
diff --git a/kstars/kstarslite/skyitems/telescopesymbolsitem.cpp b/kstars/kstarslite/skyitems/telescopesymbolsitem.cpp
index 812f42e..4e8452f 100644
--- a/kstars/kstarslite/skyitems/telescopesymbolsitem.cpp
+++ b/kstars/kstarslite/skyitems/telescopesymbolsitem.cpp
@@ -40,15 +40,13 @@ void TelescopeSymbolsItem::addTelescope(INDI::BaseDevice *bd) {
m_telescopes.insert(bd, crossHair);
}
-void TelescopeSymbolsItem::removeTelescope(QString deviceName) {
- QHash<INDI::BaseDevice *, CrosshairNode *>::iterator i = m_telescopes.begin();
- while(i != m_telescopes.end()) {
- if(i.key()->getDeviceName() == deviceName) {
- delete i.value();
- m_telescopes.remove(i.key());
- }
- i++;
+void TelescopeSymbolsItem::removeTelescope(INDI::BaseDevice *bd) {
+ CrosshairNode *crossHair = m_telescopes.value(bd);
+ if(crossHair) {
+ removeChildNode(crossHair);
+ delete crossHair;
}
+ m_telescopes.remove(bd);
}
void TelescopeSymbolsItem::update() {
@@ -64,20 +62,23 @@ void TelescopeSymbolsItem::update() {
for (i = m_telescopes.begin(); i != m_telescopes.end(); ++i) {
CrosshairNode *crossHair = i.value();
- if(deleteAll) {
- removeChildNode(crossHair);
- delete crossHair;
- } else if(show) {
- if(i.key()->isConnected()){
- crossHair->setColor(color);
- crossHair->update();
- } else {
- crossHair->hide();
+ INDI::BaseDevice *device = i.key();
+ if(crossHair) {
+ if(deleteAll || !(device->isConnected())) {
+ removeChildNode(crossHair);
+ delete crossHair;
+ m_telescopes.insert(device, nullptr);
+ } else if(show) {
+ if(device->isConnected()){
+ crossHair->setColor(color);
+ crossHair->update();
+ } else {
+ crossHair->hide();
+ }
}
}
}
if(deleteAll) {
m_telescopes.clear();
}
-
}
diff --git a/kstars/kstarslite/skyitems/telescopesymbolsitem.h b/kstars/kstarslite/skyitems/telescopesymbolsitem.h
index 1726b69..1b3f0da 100644
--- a/kstars/kstarslite/skyitems/telescopesymbolsitem.h
+++ b/kstars/kstarslite/skyitems/telescopesymbolsitem.h
@@ -46,7 +46,7 @@ public:
virtual void update() override;
void addTelescope(INDI::BaseDevice *bd);
- void removeTelescope(QString deviceName);
+ void removeTelescope(INDI::BaseDevice *bd);
private:
QHash<INDI::BaseDevice *, CrosshairNode *> m_telescopes;
diff --git a/kstars/main.cpp b/kstars/main.cpp
index 8b6f0af..d7aeb0c 100644
--- a/kstars/main.cpp
+++ b/kstars/main.cpp
@@ -56,7 +56,7 @@ static const char notice[] =
int main(int argc, char *argv[])
{
- QGuiApplication app(argc, argv);
+ QApplication app(argc, argv);
app.setApplicationVersion(KSTARS_VERSION);
/**
* enable high dpi support
@@ -64,6 +64,7 @@ int main(int argc, char *argv[])
// app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
QByteArray data = "1";
+
//qputenv("QSG_RENDER_TIMING", data);
#ifndef KSTARS_LITE
@@ -282,7 +283,6 @@ int main(int argc, char *argv[])
Options::setHideOnSlew(true);
Options::setHideGrids(false);
-
// Create writable data dir if it does not exist
QDir writableDir;
writableDir.mkdir(KSPaths::writableLocation(QStandardPaths::GenericDataLocation));
@@ -307,7 +307,7 @@ int main(int argc, char *argv[])
KStarsLite::createInstance( true );
#endif
- QObject::connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()));
+ //QObject::connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit()));
return app.exec();
}
diff --git a/kstars/skymaplite.cpp b/kstars/skymaplite.cpp
index fbb3c22..16c5743 100644
--- a/kstars/skymaplite.cpp
+++ b/kstars/skymaplite.cpp
@@ -52,6 +52,7 @@
#include <QQuickWindow>
#include <QLinkedList>
#include <QQmlContext>
+#include <QTapSensor>
namespace {
@@ -108,6 +109,7 @@ SkyMapLite::SkyMapLite(QQuickItem* parent)
setFlag(ItemHasContents, true);
m_rootNode = 0;
+ m_magLim = 2.222 * log10(static_cast<double>( Options::starDensity() )) + 0.35;
midMouseButtonDown = false;
mouseButtonDown = false;
@@ -120,6 +122,10 @@ SkyMapLite::SkyMapLite(QQuickItem* parent)
ClickedObjectLite = new SkyObjectLite;
ClickedPointLite = new SkyPointLite;
+ m_tapSensor = new QTapSensor(this);
+ m_tapSensor->setReturnDoubleTapEvents(true);
+ m_tapSensor->start();
+
KStarsLite::Instance()->qmlEngine()->rootContext()->setContextProperty("ClickedObject",ClickedObjectLite);
KStarsLite::Instance()->qmlEngine()->rootContext()->setContextProperty("ClickedPoint",ClickedPointLite);
@@ -130,29 +136,26 @@ SkyMapLite::SkyMapLite(QQuickItem* parent)
setupProjector();
- // Whenever the wrapper's(parent) dimensions changed, change SkyMapLite too
- //connect(parent, &QQuickItem::widthChanged, this, &SkyMapLite::resizeItem);
- //connect(parent, &QQuickItem::heightChanged, this, &SkyMapLite::resizeItem);
-
- /*resizeItem(); /* Set initial size. Without it on Android SkyMapLite is not displayed until screen
- orientation is not changed */
-
//Initialize images for stars
initStarImages();
// Set pinstance to yourself
pinstance = this;
+ connect( this, SIGNAL( destinationChanged() ), this, SLOT( slewFocus() ) );
+ connect( KStarsData::Instance(), SIGNAL( skyUpdate( bool ) ), this, SLOT( slotUpdateSky( bool ) ) );
+
#ifdef INDI_FOUND
ClientManagerLite *clientMng = KStarsLite::Instance()->clientManagerLite();
- connect(clientMng, &ClientManagerLite::telescopeAdded, [this](TelescopeLite *newTelescope){ this->m_newTelescopes.append(newTelescope); });
- connect(clientMng, &ClientManagerLite::removeINDIDevice, [this](QString deviceName){ this->m_delTelescopes.append(deviceName); });
+ connect(clientMng, &ClientManagerLite::telescopeAdded, [this](TelescopeLite *newTelescope){ this->m_newTelescopes.append(newTelescope->getDevice()); });
+ connect(clientMng, &ClientManagerLite::telescopeRemoved, [this](TelescopeLite *newTelescope){ this->m_delTelescopes.append(newTelescope->getDevice()); });
#endif
}
void SkyMapLite::setUpdateCounter() {
- m_updatesCount = m_updatesCountTemp; m_updatesCountTemp = 0;
+ m_updatesCount = m_updatesCountTemp;
+ m_updatesCountTemp = 0;
}
void SkyMapLite::addTelescope(TelescopeLite *) {
@@ -176,15 +179,15 @@ QSGNode* SkyMapLite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *upda
m_rootNode = n;
}
if(m_newTelescopes.count() > 0) {
- foreach(TelescopeLite *telescope, m_newTelescopes) {
- n->telescopeSymbolsItem()->addTelescope(telescope->getDevice());
+ foreach(INDI::BaseDevice *telescope, m_newTelescopes) {
+ n->telescopeSymbolsItem()->addTelescope(telescope);
}
m_newTelescopes.clear();
}
if(m_delTelescopes.count() > 0) {
- foreach(QString deviceName, m_delTelescopes) {
- n->telescopeSymbolsItem()->removeTelescope(deviceName);
+ foreach(INDI::BaseDevice *telescope, m_delTelescopes) {
+ n->telescopeSymbolsItem()->removeTelescope(telescope);
}
m_delTelescopes.clear();
}
@@ -297,6 +300,72 @@ void SkyMapLite::setFocusObject( SkyObject *o ) {
Options::setFocusObject( i18n( "nothing" ) );
}
+void SkyMapLite::slotCenter() {
+ /*KStars* kstars = KStars::Instance();
+ TrailObject* trailObj = dynamic_cast<TrailObject*>( focusObject() );*/
+
+ setFocusPoint( clickedPoint() );
+ if ( Options::useAltAz() ) {
+ focusPoint()->updateCoords( data->updateNum(), true, data->geo()->lat(), data->lst(), false );
+ focusPoint()->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
+ }
+ else
+ focusPoint()->updateCoords( data->updateNum(), true, data->geo()->lat(), data->lst(), false );
+ qDebug() << "Centering on " << focusPoint()->ra().toHMSString() << " " << focusPoint()->dec().toDMSString();
+
+ //clear the planet trail of old focusObject, if it was temporary
+ /*if( trailObj && data->temporaryTrail ) {
+ trailObj->clearTrail();
+ data->temporaryTrail = false;
+ }*/
+
+ //If the requested object is below the opaque horizon, issue a warning message
+ //(unless user is already pointed below the horizon)
+ if ( Options::useAltAz() && Options::showGround() &&
+ focus()->alt().Degrees() > -1.0 && focusPoint()->alt().Degrees() < -1.0 ) {
+
+ QString caption = i18n( "Requested Position Below Horizon" );
+ QString message = i18n( "The requested position is below the horizon.\nWould you like to go there anyway?" );
+ /*if ( KMessageBox::warningYesNo( this, message, caption,
+ KGuiItem(i18n("Go Anyway")), KGuiItem(i18n("Keep Position")), "dag_focus_below_horiz" )==KMessageBox::No ) {
+ setClickedObject( NULL );
+ setFocusObject( NULL );
+ Options::setIsTracking( false );
+
+ return;
+ }*/
+ }
+
+ //set FocusObject before slewing. Otherwise, KStarsData::updateTime() can reset
+ //destination to previous object...
+ setFocusObject( ClickedObject );
+ Options::setIsTracking( true );
+ /*if ( kstars ) {
+ kstars->actionCollection()->action("track_object")->setIcon( QIcon::fromTheme("document-encrypt") );
+ kstars->actionCollection()->action("track_object")->setText( i18n( "Stop &Tracking" ) );
+ }*/
+
+ //If focusObject is a SS body and doesn't already have a trail, set the temporaryTrail
+
+ /*if( Options::useAutoTrail() && trailObj && trailObj->hasTrail() ) {
+ trailObj->addToTrail();
+ data->temporaryTrail = true;
+ }*/
+
+ //update the destination to the selected coordinates
+ if ( Options::useAltAz() ) {
+ setDestinationAltAz( focusPoint()->altRefracted(), focusPoint()->az() );
+ } else {
+ setDestination( *focusPoint() );
+ }
+
+ focusPoint()->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
+
+ //display coordinates in statusBar
+ emit mousePointChanged( focusPoint() );
+ //showFocusCoords(); //update FocusBox
+}
+
void SkyMapLite::slewFocus() {
//Don't slew if the mouse button is pressed
//Also, no animated slews if the Manual Clock is active
@@ -322,7 +391,7 @@ void SkyMapLite::slewFocus() {
if ( r0 < 20.0 ) { //smaller slews have smaller maxstep
maxstep *= (10.0 + 0.5*r0)/20.0;
}
- double step = 0.5;
+ double step = 0.1;
double r = r0;
while ( r > step ) {
//DEBUG
@@ -508,6 +577,47 @@ void SkyMapLite::forceUpdate() {
update();
}
+void SkyMapLite::slotUpdateSky( bool now ) {
+ Q_UNUSED(now);
+ updateFocus();
+ forceUpdate();
+}
+
+void SkyMapLite::updateFocus() {
+ if( slewing )
+ return;
+
+ //Tracking on an object
+ if ( Options::isTracking() && focusObject() != NULL ) {
+ if ( Options::useAltAz() ) {
+ //Tracking any object in Alt/Az mode requires focus updates
+ focusObject()->EquatorialToHorizontal(data->lst(), data->geo()->lat());
+ setFocusAltAz( focusObject()->altRefracted(), focusObject()->az() );
+ focus()->HorizontalToEquatorial( data->lst(), data->geo()->lat() );
+ setDestination( *focus() );
+ } else {
+ //Tracking in equatorial coords
+ setFocus( focusObject() );
+ focus()->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
+ setDestination( *focus() );
+ }
+
+ //Tracking on empty sky
+ } else if ( Options::isTracking() && focusPoint() != NULL ) {
+ if ( Options::useAltAz() ) {
+ //Tracking on empty sky in Alt/Az mode
+ setFocus( focusPoint() );
+ focus()->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
+ setDestination( *focus() );
+ }
+
+ // Not tracking and not slewing, let sky drift by
+ // This means that horizontal coordinates are constant.
+ } else {
+ focus()->HorizontalToEquatorial(data->lst(), data->geo()->lat() );
+ }
+}
+
void SkyMapLite::setupProjector() {
//Update View Parameters for projection
ViewParams p;
diff --git a/kstars/skymaplite.h b/kstars/skymaplite.h
index 34939f2..85b8ce3 100644
--- a/kstars/skymaplite.h
+++ b/kstars/skymaplite.h
@@ -51,6 +51,7 @@ class TelescopeLite;
class SkyObjectLite;
class SkyPointLite;
+class QTapSensor;
class QSGTexture;
@@ -70,7 +71,7 @@ class QSGTexture;
class SkyMapLite : public QQuickItem {
Q_OBJECT
-
+ Q_PROPERTY(double magLim READ getMagLim WRITE setMagLim NOTIFY magLimChanged)
protected:
/**
*Constructor.
@@ -117,7 +118,7 @@ public:
//float fov();
/** @short Update the focus position according to current options. */
- //void updateFocus();
+ void updateFocus();
/** @short Retrieve the Focus point; the position on the sky at the
*center of the skymap.
@@ -282,6 +283,10 @@ public:
bool isSlewing() const;
+ inline double getMagLim() { return m_magLim; }
+
+ void setMagLim(double magLim);
+
// NOTE: This method is draw-backend independent.
/** @short update the geometry of the angle ruler. */
//void updateAngleRuler();
@@ -335,8 +340,6 @@ public slots:
void resizeItem();
/** Recalculates the positions of objects in the sky, and then repaints the sky map.
- * If the positions don't need to be recalculated, use update() instead of forceUpdate().
- * This saves a lot of CPU time.
*/
void forceUpdate();
@@ -349,7 +352,7 @@ public slots:
* @short Update the focus point and call forceUpdate()
* @param now is saved for compatibility reasons
*/
- void slotUpdateSky( bool now ) {Q_UNUSED(now)}
+ void slotUpdateSky( bool now );
/** Toggle visibility of geo infobox */
//void slotToggleGeoBox(bool);
@@ -379,7 +382,7 @@ public slots:
* @see destinationChanged()
* @see slewFocus()
*/
- //void slotCenter();
+ void slotCenter();
/** @short Popup menu function: Display 1st-Generation DSS image with the Image Viewer.
* @note the URL is generated using the coordinates of ClickedPoint.
@@ -451,6 +454,13 @@ signals:
/** Emitted when a position is clicked */
void positionClicked(SkyPoint*);
+
+ /** Emitted when user clicks on SkyMapLite (analagous to positionClicked but sends QPoint) */
+ void posClicked(QPointF pos);
+
+ /** Emitted when magnitude limit is changed */
+ void magLimChanged(double magLim);
+
protected:
/** Process keystrokes:
* @li arrow keys Slew the map
@@ -632,11 +642,13 @@ private:
QLinkedList<SkyNode *> m_deleteNodes;
float m_sizeMagLim; //Used in PointSourceNode
+ double m_magLim; //Mag limit for all objects
// Used to notify zoom-dependent labels about font size change
bool m_fontSizeChanged;
// Used for drawing labels
QPainter m_painter;
+ QTapSensor *m_tapSensor;
static int starColorMode;
@@ -656,8 +668,8 @@ private:
QVector<QVector<QSGTexture*>> textureCache;
#ifdef INDI_FOUND
- QList<TelescopeLite *> m_newTelescopes;
- QList<QString> m_delTelescopes;
+ QList<INDI::BaseDevice *> m_newTelescopes;
+ QList<INDI::BaseDevice *> m_delTelescopes;
#endif
};
diff --git a/kstars/skymapliteevents.cpp b/kstars/skymapliteevents.cpp
index bb358a4..b895286 100644
--- a/kstars/skymapliteevents.cpp
+++ b/kstars/skymapliteevents.cpp
@@ -12,6 +12,7 @@
#include "projections/projector.h"
#include "skymapcomposite.h"
#include "ksutils.h"
+#include <QTapSensor>
void SkyMapLite::mousePressEvent( QMouseEvent *e ) {
KStarsLite* kstars = KStarsLite::Instance();
@@ -22,7 +23,6 @@ void SkyMapLite::mousePressEvent( QMouseEvent *e ) {
update(); //refresh without redrawing skymap
return;
}*/
-
// if button is down and cursor is not moved set the move cursor after 500 ms
QTimer::singleShot(500, this, SLOT (setMouseMoveCursor()));
@@ -122,11 +122,11 @@ void SkyMapLite::mouseReleaseEvent( QMouseEvent * ) {
}
void SkyMapLite::mouseDoubleClickEvent( QMouseEvent *e ) {
- /*if ( e->button() == Qt::LeftButton && !projector()->unusablePoint( e->pos() ) ) {
+ if ( e->button() == Qt::LeftButton && !projector()->unusablePoint( e->pos() ) ) {
mouseButtonDown = false;
if( e->x() != width()/2 || e->y() != height()/2 )
slotCenter();
- }*/
+ }
}
void SkyMapLite::mouseMoveEvent( QMouseEvent *e ) {
@@ -235,6 +235,17 @@ void SkyMapLite::wheelEvent( QWheelEvent *e ) {
void SkyMapLite::touchEvent( QTouchEvent *e) {
QList<QTouchEvent::TouchPoint> points = e->touchPoints();
+ QTapReading *reading = m_tapSensor->reading();
+
+ if(reading) {
+ qDebug() << reading->isDoubleTap();
+ }
+
+ bool conn = m_tapSensor->isConnectedToBackend();
+ qDebug() << conn << "Connect State";
+ qDebug() << m_tapSensor->connectToBackend() << "trying to connect";
+
+
if(points.length() == 2) {
if ( projector()->unusablePoint( points[0].pos() ) ||
projector()->unusablePoint( points[1].pos() ))
@@ -319,7 +330,7 @@ void SkyMapLite::touchEvent( QTouchEvent *e) {
} else {
//If only pan is needed we just use the first touch point
if(e->touchPointStates() & Qt::TouchPointMoved || slewing) {
- QPointF newFocus = points[0].pos();
+ QPointF newFocus = points[0].screenPos();
QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, newFocus,
Qt::LeftButton, Qt::LeftButton, Qt::ControlModifier);
if(e->type() == QEvent::TouchBegin) {
@@ -343,12 +354,16 @@ void SkyMapLite::touchEvent( QTouchEvent *e) {
}
delete event;
} else if((e->touchPointStates() & (Qt::TouchPointReleased))) { //&& !slewing && points.length() == 1) {
+ //Show tap animation
+ emit posClicked(points[0].screenPos());
//determine RA, Dec of touch
- m_MousePoint = projector()->fromScreen( points[0].pos() , data->lst(), data->geo()->lat() );
+ m_MousePoint = projector()->fromScreen( points[0].screenPos() , data->lst(), data->geo()->lat() );
setClickedPoint( &m_MousePoint );
//Find object nearest to clickedPoint()
- double maxrad = 1000.0/Options::zoomFactor();
+ double maxrad = 1000.0/Options::zoomFactor()*2; /* On high zoom-level it is very hard to select the object using touch screen.
+ That's why radius remains constant*/
+ qDebug() << maxrad << "maxrad";
SkyObject* obj = data->skyComposite()->objectNearest( clickedPoint(), maxrad );
setClickedObject( obj );
if( obj ) setClickedPoint( obj );
@@ -398,20 +413,35 @@ double SkyMapLite::magFactor( const int modifier ) {
return factor;
}
+void SkyMapLite::setMagLim(double magLim) {
+ if(m_magLim != magLim) {
+ m_magLim = magLim;
+ if ( m_magLim > 5.75954 ) m_magLim = 5.75954;
+ if ( m_magLim < 1.18778 ) m_magLim = 1.18778;
+ emit magLimChanged(m_magLim);
+
+ Options::setStarDensity( pow( 10, ( m_magLim - 0.35 ) / 2.222) );
+ //printf("maglim set to %3.1f\n", m_magLim);
+ forceUpdate();
+ }
+}
+
void SkyMapLite::incMagLimit( const int modifier ) {
- double limit = 2.222 * log10(static_cast<double>( Options::starDensity() )) + 0.35;
- limit += magFactor( modifier );
- if ( limit > 5.75954 ) limit = 5.75954;
- Options::setStarDensity( pow( 10, ( limit - 0.35 ) / 2.222) );
- //printf("maglim set to %3.1f\n", limit);
+ m_magLim = 2.222 * log10(static_cast<double>( Options::starDensity() )) + 0.35;
+ m_magLim += magFactor( modifier );
+ if ( m_magLim > 5.75954 ) m_magLim = 5.75954;
+ emit magLimChanged(m_magLim);
+ Options::setStarDensity( pow( 10, ( m_magLim - 0.35 ) / 2.222) );
+ //printf("maglim set to %3.1f\n", m_magLim);
forceUpdate();
}
void SkyMapLite::decMagLimit( const int modifier ) {
- double limit = 2.222 * log10(static_cast<double>( Options::starDensity() )) + 0.35;
- limit -= magFactor( modifier );
- if ( limit < 1.18778 ) limit = 1.18778;
- Options::setStarDensity( pow( 10, ( limit - 0.35 ) / 2.222) );
- //printf("maglim set to %3.1f\n", limit);
+ m_magLim = 2.222 * log10(static_cast<double>( Options::starDensity() )) + 0.35;
+ m_magLim -= magFactor( modifier );
+ if ( m_magLim < 1.18778 ) m_magLim = 1.18778;
+ emit magLimChanged(m_magLim);
+ Options::setStarDensity( pow( 10, ( m_magLim - 0.35 ) / 2.222) );
+ //printf("maglim set to %3.1f\n", m_magLim);
forceUpdate();
}