diff --git a/Tests/auxiliary/testfwparser.cpp b/Tests/auxiliary/testfwparser.cpp index ceb761d3d..43f461af3 100644 --- a/Tests/auxiliary/testfwparser.cpp +++ b/Tests/auxiliary/testfwparser.cpp @@ -1,185 +1,181 @@ /*************************************************************************** TestFWParser.cpp - K Desktop Planetarium ------------------- begin : 2012/24/07 copyright : (C) 2012 by Rishab Arora email : ra.rishab@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 "testfwparser.h" #include #include TestFWParser::TestFWParser() : QObject() { } void TestFWParser::initTestCase() { test_cases_.append("this is an exam ple of 256 cases being tested -3.14 times\n"); test_cases_.append(" \n"); test_cases_.append("this is an ex\n\n"); QTemporaryFile temp_file; //temp_file.setPrefix(QDir::tempPath() + QDir::separator()); //temp_file.setSuffix(".txt"); temp_file.setAutoRemove(false); QVERIFY(temp_file.open()); test_file_name_ = temp_file.fileName(); QTextStream out_stream(&temp_file); foreach (const QString &test_case, test_cases_) out_stream << test_case; temp_file.close(); //Building the sequence to be used. Includes all available types. sequence_.clear(); sequence_.append(qMakePair(QString("field1"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field2"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field3"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field4"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field5"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field6"), KSParser::D_INT)); sequence_.append(qMakePair(QString("field7"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field8"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field9"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field10"), KSParser::D_FLOAT)); sequence_.append(qMakePair(QString("field11"), KSParser::D_QSTRING)); sequence_.append(qMakePair(QString("field12"), KSParser::D_QSTRING)); widths_.append(5); widths_.append(3); widths_.append(3); widths_.append(9); widths_.append(3); widths_.append(4); widths_.append(6); widths_.append(6); widths_.append(7); widths_.append(6); widths_.append(6); //'repeatedly' doesn't need a width test_parser_ = new KSParser(test_file_name_, '#', sequence_, widths_); } -TestFWParser::~TestFWParser() -{ -} - void TestFWParser::cleanupTestCase() { delete test_parser_; } void TestFWParser::MixedInputs() { /* * Test 1: Checks all conversions are working as expected */ QHash row_content = test_parser_->ReadNextRow(); QCOMPARE(row_content["field1"].toString(), QString("this")); QCOMPARE(row_content["field2"].toString(), QString("is")); QCOMPARE(row_content["field3"].toString(), QString("an")); QCOMPARE(row_content["field4"].toString(), QString("exam ple")); QCOMPARE(row_content["field5"].toString(), QString("of")); QCOMPARE(row_content["field6"].toInt(), 256); QCOMPARE(row_content["field7"].toString(), QString("cases")); QCOMPARE(row_content["field8"].toString(), QString("being")); QCOMPARE(row_content["field9"].toString(), QString("tested")); QVERIFY(row_content["field10"].toFloat() + 3.141 < 0.1); QCOMPARE(row_content["field11"].toString(), QString("")); QCOMPARE(row_content["field12"].toString(), QString("times")); } void TestFWParser::OnlySpaceRow() { /* * Test 2: Checks what happens in case of reading an empty space row */ QHash row_content = test_parser_->ReadNextRow(); QCOMPARE(row_content["field1"].toString(), QString("")); QCOMPARE(row_content["field2"].toString(), QString("")); QCOMPARE(row_content["field3"].toString(), QString("")); QCOMPARE(row_content["field4"].toString(), QString("")); QCOMPARE(row_content["field5"].toString(), QString("")); QCOMPARE(row_content["field6"].toInt(), 0); QCOMPARE(row_content["field7"].toString(), QString("")); QCOMPARE(row_content["field8"].toString(), QString("")); QCOMPARE(row_content["field9"].toString(), QString("")); QCOMPARE(row_content["field10"].toFloat(), float(0.0)); QCOMPARE(row_content["field11"].toString(), QString("")); QCOMPARE(row_content["field12"].toString(), QString("")); } void TestFWParser::NoRow() { /* * Test 3: * This test also tests what happens if we have a partial row or a * truncated row. It is simply skipped. * * It then reaches a point where the file ends. * We attempt reading a file after EOF 20 times */ QHash row_content; qDebug() << row_content["field12"]; for (int times = 0; times < 20; times++) { row_content = test_parser_->ReadNextRow(); QCOMPARE(row_content["field1"].toString(), QString("Null")); QCOMPARE(row_content["field2"].toString(), QString("Null")); QCOMPARE(row_content["field3"].toString(), QString("Null")); QCOMPARE(row_content["field4"].toString(), QString("Null")); QCOMPARE(row_content["field5"].toString(), QString("Null")); QCOMPARE(row_content["field6"].toInt(), 0); QCOMPARE(row_content["field7"].toString(), QString("Null")); QCOMPARE(row_content["field8"].toString(), QString("Null")); QCOMPARE(row_content["field9"].toString(), QString("Null")); QCOMPARE(row_content["field10"].toFloat(), float(0.0)); QCOMPARE(row_content["field11"].toString(), QString("Null")); QCOMPARE(row_content["field12"].toString(), QString("Null")); } } void TestFWParser::FWReadMissingFile() { /* * Test 4: * This tests how the parser reacts if there is no file with the * given path. */ QFile::remove(test_file_name_); KSParser missing_parser(test_file_name_, '#', sequence_, widths_); QHash row_content = missing_parser.ReadNextRow(); for (int times = 0; times < 20; times++) { row_content = missing_parser.ReadNextRow(); QCOMPARE(row_content["field1"].toString(), QString("Null")); QCOMPARE(row_content["field2"].toString(), QString("Null")); QCOMPARE(row_content["field3"].toString(), QString("Null")); QCOMPARE(row_content["field4"].toString(), QString("Null")); QCOMPARE(row_content["field5"].toString(), QString("Null")); QCOMPARE(row_content["field6"].toInt(), 0); QCOMPARE(row_content["field7"].toString(), QString("Null")); QCOMPARE(row_content["field8"].toString(), QString("Null")); QCOMPARE(row_content["field9"].toString(), QString("Null")); QCOMPARE(row_content["field10"].toFloat(), float(0.0)); QCOMPARE(row_content["field11"].toString(), QString("Null")); QCOMPARE(row_content["field12"].toString(), QString("Null")); } } QTEST_GUILESS_MAIN(TestFWParser) diff --git a/Tests/auxiliary/testfwparser.h b/Tests/auxiliary/testfwparser.h index 20dfd4a59..ed8289170 100644 --- a/Tests/auxiliary/testfwparser.h +++ b/Tests/auxiliary/testfwparser.h @@ -1,45 +1,45 @@ /*************************************************************************** KSParser.cpp - K Desktop Planetarium ------------------- begin : 2012/15/08 copyright : (C) 2012 by Rishab Arora email : ra.rishab@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. * * * ***************************************************************************/ #pragma once #include "ksparser.h" #include #include class TestFWParser : public QObject { Q_OBJECT public: TestFWParser(); - ~TestFWParser() override; + ~TestFWParser() override = default; private slots: void initTestCase(); void cleanupTestCase(); void MixedInputs(); void OnlySpaceRow(); void NoRow(); void FWReadMissingFile(); private: QStringList test_cases_; QList widths_; QList> sequence_; QString test_file_name_; KSParser *test_parser_ { nullptr }; }; diff --git a/Tests/scheduler/1x1s_Lum.esq b/Tests/scheduler/1x1s_Lum.esq index 3ce87e7ff..62c28253b 100644 --- a/Tests/scheduler/1x1s_Lum.esq +++ b/Tests/scheduler/1x1s_Lum.esq @@ -1,48 +1,48 @@ CCD Simulator CCD Simulator 2 0 60 0 1 1 1 0 0 1280 1024 0 Luminance Light 0 0 0 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False diff --git a/Tests/scheduler/1x1s_RGBLumRGB.esq b/Tests/scheduler/1x1s_RGBLumRGB.esq index 723dc61a7..a49d3e0e3 100644 --- a/Tests/scheduler/1x1s_RGBLumRGB.esq +++ b/Tests/scheduler/1x1s_RGBLumRGB.esq @@ -1,282 +1,282 @@ CCD Simulator CCD Simulator 2 0 60 0 1 1 1 0 0 1280 1024 0 Red Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False 1 1 1 0 0 1280 1024 0 Green Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False 1 1 1 0 0 1280 1024 0 Blue Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False 1 1 1 0 0 1280 1024 0 Luminance Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False 1 1 1 0 0 1280 1024 0 Red Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False 1 1 1 0 0 1280 1024 0 Green Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False 1 1 1 0 0 1280 1024 0 Blue Light 1 1 1 1 1 -/tmp/kstars_tests +/var/tmp/kstars_tests 0 0 Manual Manual False False diff --git a/Tests/skyobjects/test_skypoint.h b/Tests/skyobjects/test_skypoint.h index eee66fc61..71f905f6c 100644 --- a/Tests/skyobjects/test_skypoint.h +++ b/Tests/skyobjects/test_skypoint.h @@ -1,46 +1,46 @@ /*************************************************************************** test_skypoint.h - KStars Planetarium ------------------- begin : Tue 27 Sep 2016 20:51:21 CDT copyright : (c) 2016 by Akarsh Simha email : akarsh.simha@kdemail.net ***************************************************************************/ /*************************************************************************** * * * 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 TEST_SKYPOINT_H #define TEST_SKYPOINT_H #include #include #define UNIT_TEST #include "skyobjects/skypoint.h" /** * @class TestSkyPoint * @short Tests for some SkyPoint operations * @author Akarsh Simha */ class TestSkyPoint : public QObject { Q_OBJECT public: TestSkyPoint() : QObject(){}; - ~TestSkyPoint() override{}; + ~TestSkyPoint() override = default; private slots: void testPrecession(); }; #endif diff --git a/doc/commands.docbook b/doc/commands.docbook index 5ead555bd..4b7f6c0a7 100644 --- a/doc/commands.docbook +++ b/doc/commands.docbook @@ -1,1677 +1,1677 @@ Command Reference Menu Commands CommandsMenu File Menu &Ctrl;O File Open FITS... Open a FITS image in the FITS Viewer tool &Ctrl;I File Save Sky Image... Create image on disk from current display &Ctrl;R File Run Script... Run the specified &kstars; script File Printing Wizard Starts a wizard to configure capturing sky objects images using telescope and printing the results as a good looking printout for further use or catalogization. &Ctrl;P File Print... Send the current sky map to the printer (or to a PostScript/PDF file) &Ctrl;Q File Quit Quit &kstars; Time Menu &Ctrl;E Time Set Time to Now Sync time to system clock &Ctrl;S Time Set Time... Set time and date < Time Advance one step backward in time Advance one step backward in time in the &kstars; simulation of the sky chart. The duration of the time step can be configured after pressing the little > button on the &kstars; toolbar. Time Stop Clock Toggle whether time passes > Time Advance one step forward in time Advance one step forward in time in the &kstars; simulation of the sky chart. The duration of the time step can be configured after pressing the little > button on the &kstars; toolbar. Pointing Menu Z Pointing Zenith Center the display at the Zenith point (straight up) N Pointing North Center the display above the North point on the horizon E Pointing East Center the display above the East point on the horizon S Pointing South Center the display above the South point on the horizon W Pointing West Center the display above the West point on the horizon &Ctrl;M Pointing Set Coordinates Manually... Center the display on specific sky coordinates &Ctrl;F Pointing Find Object Locate an object by name using the Find Object Window &Ctrl;T Pointing Stop Tracking Toggle tracking on/off. While tracking, the display will remain centered on the current position or object. View Menu &Ctrl;+ View Zoom in Zooms view in &Ctrl;- View Zoom out Zooms view out &Ctrl;Z View Default Zoom Restore the default Zoom setting &Ctrl;&Shift;Z View Zoom to Angular Size... Zoom to specified field-of-view angle &Ctrl;&Shift;F View Full Screen Mode Toggle full-screen mode Space View Switch to star globe view / Switch to horizontal view Toggle between the Horizontal and Equatorial Coordinate Systems F5 View Projection Lambert Azimuthal Equal-area Switch sky chart view to the Lambert azimuthal equal-area projection. F6 View Projection Azimuthal Equidistant Switch sky chart view to the azimuthal equidistant projection. F7 View Projection Orthographic Switch sky chart view to the orthographic projection. F8 View Projection Equirectangular Switch sky chart view to the equirectangular projection. F9 View Projection Stereographic Switch sky chart view to the stereographic projection. F10 View Projection Gnomonic Switch sky chart view to the gnomonic projection. View HiPS All Sky Overlay -Select and configure &hips; sky overlays +Select and configure HiPS sky overlays Tools Menu &Ctrl;&Shift;C Tools Calculator Opens the AstroCalculator Tool, which provides full access to many of the mathematical functions used by &kstars;. Tools Devices Telescope Wizard... Opens the Telescope Wizard, which provides a step-by-step guide to help you connect to your telescope and control it with &kstars;. &Ctrl;D Tools Devices Device Manager... Opens up the device manager, which allows you to start/shutdown device drivers and connect to remote INDI servers. Tools Devices INDI Control Panel... Opens up INDI Control Panel, which allows you to control all the features supported by a device. Tools Sky Calendar Opens the Sky Calendar Tool, which allows you to plan observations of Solar System planets by giving graphical data on sets and rises of these objects. &Ctrl;A Tools Altitude vs. Time Opens the Altitude vs. Time Tool, which can plot curves representing the altitude of any object as a function of time. This is useful for planning observing sessions. &Ctrl;U Tools What's Up Tonight Opens the What's Up Tonight Tool, which presents a summary of the objects which are observable from your location on a given date. &Ctrl;W Tools What's Interesting... Opens the What's Interesting Tool, which allows you to be informed about the most interesting observations that can be made from your current location using the given equipment. &Ctrl;B Tools Script Builder Opens the Script Builder Tool, which provides a GUI interface for building &kstars; &DBus; scripts. &Ctrl;Y Tools Solar System Opens the Solar System Viewer, which displays an overhead view of the solar system on the current simulation date. &Ctrl;K Tools Ekos Opens Ekos, a complete and powerful tool for astrophotography. With Ekos, you can align and guide your telescope, focus your CCD, and capture images using an easy intuitive interface. &Ctrl;J Tools Jupiter's Moons Opens the Jupiter Moons Tool, which displays the positions of Jupiter's four brightest moons as a function of time. Tools Flags Opens the Flag manager Tool, which can be used to assign color labels and icons to the given positions on the sky chart. Data Menu &Ctrl;N Data Download New Data... Open the Get Hot New Stuff dialog to download additional data for &kstars;. Data Updates This submenu can be used to update various object data, namely asteroids, comets and satellites orbital elements as well as recent supernovae data from the Internet. The data will be downloaded for the current user account only. If you use &kstars; from different account you should download them for every account separately. &kstars; tries to download recent supernovae list automatically by default. You can switch the download off using Supernovae page of &kstars; Settings window. Observation Menu &Ctrl;L Observation Observation Planner Open the Observation Planner tool. &Ctrl;2 Observation Execute the session Plan Opens session plan wizard or execute the planned session. Settings Menu Settings Info Boxes Show Info Boxes Toggle display of all three Info Boxes: Time Box Focus Box Location Box Settings Info Boxes Show Time Box Toggle display of the Time Info Box. By default, the Time Info Box is located in the top left side of the screen. You can change the position of Time Info Box by holding the left-click mouse button and dragging it to the new position. Settings Info Boxes Show Focus Box Toggle display of the Focus Info Box. By default, the Focus Info Box is located in the top right side of the screen. You can change the position of Focus Info Box by holding the left-click mouse button and dragging it to the new position. Settings Info Boxes Show Location Box Toggle display of the Location Info Box. By default, the Location Info Box is located in the bottom left side of the screen. You can change the position of Location Info Box by holding the left-click mouse button and dragging it to the new position. Settings Toolbars Shown Main Toolbar Toggle display of the Main Toolbar. By default, the Main Toolbar provides useful shortcuts for controlling the sky map view (&ie; Zoom in and Zoom out) and for controlling &kstars; clock as well. You can Start / Stop the clock, advance one step backward / forward in time and also you can easily set the time step used by &kstars;' clock. The time step is the rate at which time flows in the simulation. For setting the time step, you can use the spin box used for establishing the time step units and the spin box used for increasing / decreasing the time step value. Using the Main Toolbar you can quickly open the Find Object, Set Time or Set Geographic Location window. Main Toolbar can be configured using SettingsConfigure Toolbars. Settings Toolbars Shown View Toolbar Toggle display of the View Toolbar. View Toolbar controls which sky objects are drawn on &kstars; Sky Map (stars, deep sky objects, Solar system objects, supernovae or satellites) as well as what constellation information is included (constellation lines, constellation names, constellation art or constellation boundaries). It also provides icon shortcuts for: showing milky way (drawn with dark grey color), showing equatorial/horizontal coordinate grid and for showing the green opaque ground. Note that when the horizon is switched off, refraction effects are temporarily disabled. View Toolbar can be configured using SettingsConfigure Toolbars. Settings Toolbars Shown INDI Toolbar Toggle display of the INDI Toolbar. By default, INDI Toolbar contains three shortcut icons: Toggle Ekos Toggle INDI Control Panel Toggle FITS Viewer INDI Toolbar can be configured using SettingsConfigure Toolbars. Settings Statusbar Show Statusbar Toggle display of the Statusbar. It is located in the bottom of &kstars; window. Settings Statusbar Show Az/Alt Field Toggle display of the mouse cursor's horizontal coordinates in the statusbar. Settings Statusbar Show RA/Dec Field Toggle display of the mouse cursor's equatorial coordinates in the statusbar. Settings Statusbar Show J2000.0 RA/Dec Field Toggle display of the mouse cursor's equatorial J2000 coordinates in the statusbar. Settings Color Schemes This submenu contains all of the defined color schemes: Classic, Star Chart, Night Vision and Moonless Night. It can also include your custom color schemes. Select any item to set that color scheme. Settings FOV Symbols This submenu lists the available field-of-view (FOV) Symbols. The FOV Symbol is drawn at the center of the display. You may choose one or more from the list of predefined symbols (7x35 Binoculars, Telrad, One Degree, HST WFPC2 or 30m at 1.3cm) by checking their specific checkbox, or you may use no symbol by unchecking all the items from the symbols list. You can also define your own symbols (or modify existing symbols) using the Edit FOV Symbols... option. Settings Artificial Horizon If you select this submenu, then Artificial Horizon Manager will be opened. Artificial Horizon Manager is used to define on the skymap one or more regions that are blocked from view from your current location (&ie; tall trees or building). The window is split in two sections: in the left side is the Regions section while in the right side is the Points section. You can add a new region by pressing the + (Add Region) button or you can remove a region by selecting it in the regions list and then pressing the - (Remove Region) button. In order to draw a region you need to define a list of points that encompasses the blocked area. You can add a new point by pressing the + (Add Point) button from the right Points section. You can also remove a highlighted point from the list of points or you can clear all the points. There are two ways to add a new point to a selected region: manually by entering the point's coordinates or by selecting the point from the skymap after clicking Select Points button. Note that each point is described by a set of horizontal coordinates: Az (Azimuth) and Alt (Altitude). If you want to modify a point's coordinates, just double click on its Az/Alt text box value and enter the new value. First and Last points must be on the horizon. Polygons must be closed to be considered valid regions. Artificial Horizon Manager provides a easy way to rename your regions. By default, regions are named as: Region plus an index (&ie; Region 1 or Region 2). For renaming a region, just double click on its name and then you will be invited to complete a new region name. You can also control which regions are enabled or disabled for marking on the map by a simple click on the checkbox in front of each region. After you defined your desired regions you can apply them by pressing the Apply button. If you want to use these regions again, in further astronomy sessions, you can save them such that next time when you will open &kstars; they will be automatically marked on the skymap. &Ctrl;0 Settings Configure Observation Logging List your Equipment Allows you to define your equipment characteristics for observation logs. The Configure Equipment window is divided into four tabs: Telescope, Eyepiece, Lens and Filter. You can add a new equipment by completing its characteristics and pressing Add New... button. You can also save or remove an equipment from the list. &Ctrl;1 Settings Configure Observation Logging Manage Observer Selecting this will open Manage Observers window that enables you to register a number of observers which are using &kstars; on this computer. You can add a new entry to observers list by completing the mandatory fields: Name and Surname and then pressing the + (Add observer) button. Note that Contact field is optional, &kstars; allowing you to add a new observer even though Contact text box is empty. You can also remove an observer from the list using - (Remove observer) button. &Ctrl;G Settings Geographic Select a new geographic location. Settings Configure &kstars; Modify configuration options. Settings Configure Shortcuts Open the Configure Shortcuts window which allows you to modify &kstars; shortcuts. You can use &kstars; default shortcuts scheme or define your own shortcuts scheme. In order to add a new custom shortcut to an action, you need to click on action's name and then press the Custom checkbox. Then click on the button next to Custom checkbox and enter the shortcut you would like to use in &kstars;. You can also remove a shortcut by pressing the Delete button after you select an action from the list. &kstars; helps you to find an action by providing its search method; just enter the name of the action and the list will be reduced to its best matches. For example, I will give you a brief example on how to use &kstars; Configure Shortcuts feature: Firstly, open the Configure Shortcuts window from the Settings menu. Then select the action you would like to add a shortcut (&ie; Artificial Horizon). You can use the search method provided by &kstars;. Just type art in the search text box and the list will be reduced to only four actions. The Artificial Horizon is the first action in the list. Now that you find your desired action, you can add a new shortcut by clicking on its name. After you click on the action name, press the Custom checkbox. There is a button next to Custom checkbox used for adding new shortcuts. By default, if an action has no custom shortcut, the None tag will be assigned to this button. Press this button and then add your custom shortcut. For example, for Artificial Horizon, you can use &Ctrl;+H shortcut. Hold the &Ctrl; key and press H. Your shortcut be automatically saved, thus you can use it next time when you open &kstars;. Settings Configure &kstars;... Modify configuration options. Settings Startup Wizard By selecting the Startup Wizard... submenu, the Setup Wizard window will pop up. It is the same window that you saw when you ran &kstars; for the very first time. &kstars; Setup Wizard will help you set up some basic options, such as your location on Earth. The first page of Setup Wizard is a welcome page and if you press Next button, you will get the Choose Your Home Location page. Here you can select your exact home location or a city near your home location if your accurate location is not present in &kstars; cities database. Browsing the entire list is not so efficient and thus, &kstars; provides you a easy-to-use method for selecting your desired city in a short time. You may filter the list of cities by the name of your city, province and country. By giving more details about your location, the list's size will reduced only to its best matches. When you highlight a city in the cities list, the Longitude and Latitude boxes will be updated, containing the accurate information of your selected location. When you hit the Next button, you will get the last page of &kstars; Setup Wizard: Download Extra Data Files page. Here you may download optional data files via the Internet, in order to enhance &kstars;, such as Messier object images, or a more complete NGC/IC catalog. Press the Download Extra Data button to proceed. After you pressed the Download Extra Data button, the Get Hot New Stuff window will appear. It is &kstars; Add-On Installer, which will help you to manage what extra information you added to &kstars;. Firstly, you can choose how the add-ons list is displayed, by pressing the Details view mode or Icons view mode button. You may also order the list by various criteria like: newest, rating, most downloads or installed. This can be easily done by selecting an option using the radio button. You can also make searches using the Search: text box. Regarding the list of add-ons, you may perform several actions by selecting an add-on from the list: you can install or uninstall an add-on using the Install / Uninstall button, you can open the Details window using the Details button or you can rate an add-on by giving a number of gold stars, up to five stars. If you want to send an email to the author of an add-on, you are able to do that by clicking on his e-mail address. Then, your default e-mail application will guide you through the sending process. After you managed your &kstars; add-ons, then you can close the window by pressing the Close button. You can also use the Download Extra Data Files tool later, by selecting Data Download New Data.... Additionally &kstars; has the common &kde; Settings and Help menu items, for more information read the sections about the Settings Menu and Help Menu of the &kde; Fundamentals. Help Menu &kstars; has the common &kde; Help menu item, for more information read the section about the Help Menu of the &kde; Fundamentals. Popup Menu Popup MenuDescription The right click popup menu is context-sensitive, meaning its content varies depending on what kind of object you click on. We list all possible popup menu items here, with the relevant object type [in brackets]. [All] Identification and type: The top one to three lines are devoted to the name(s) of the object, and its type. For stars, the Spectral Type is also shown here. [All] Rise, Transit, and Set times for the object on the current simulation date are shown on the next three lines. [All] Center & Track: Center the display on this location, and engage tracking. Equivalent to double-clicking. [All] Add flag...: Open &kstars; Flag manager window. Here you can manage your flags and use some of &kstars; features. When Flag manager window is open for a selected object, Right ascension and Declination text boxes will be automatically filled with the coordinate values of the selected object on the Sky Map. Besides these two text boxes, you can set the Epoch, add a Label, set the Label color or even add an Icon. To add custom icons, just add images in `qtpaths --paths GenericDataLocation`/kstars/. File names must begin with the flag keyword. For example, the file flagSmall_red_cross.gif will be shown as Small red cross in the combo box. After you set up the information of the new flag, you can add it to the flags list using the Add button. You may also change flags details (&ie; RA/Dec, label or icon) and then save the new ones by pressing the Save changes button. Using the Flag manager, you can easily center an object in Map or in Telescope, by pressing Center in Map or Center in Telescope button. In order to delete a value from the list, just select it in the list and then press Delete button. Your flags are saved after you close current session and thus you will be able to see them everytime you will use &kstars; again. [All] Angular Distance To...: Enter "angular distance mode". In this mode, a dotted line is drawn from the first target object to the current mouse position. When you click with the &RMB; on a second object, this will display the angular distance between the two objects near the second object. You can press the &Esc; key to exit angular distance mode without measuring an angle. [All] Starhop from here to: will enable you to find a path between two points in the sky. Star hopping is a technique that uses bright stars as a guide for finding fainter objects. So if you have a bright star, you can use it as a reference to find a fainter object. Starting from your reference star, &kstars; will find a route to the destination, traversing a sequence of stars/patterns of stars. The object for which you called Starhop from here to tool will be your starting point. When your starting point is established, a dotted line will appear, allowing you to decide which will be your end point. You have to move the mouse cursor to your destination object position and right click on it. Then, a dialog box will pop up, requiring you to set the FOV used for star hopping. For selecting a FOV, you need to make a choice from the FOV combo box list of values. The values you can choose include default FOV values (7x35 Binoculars, Telrad, One Degree, HST WFPC2 and 30m at 1.3cm) plus your customized FOVs. After you selected the FOV, &kstars; will find a route for you. Thus, the dialog box will contain a list of object used in the starhop. If &kstars; did not manage to find a route, then an error dialog box will be displayed, giving you a helping hand. When the Star-Hopper algorithm ends, the dialog box will contain a list of objects used in starhop route. For every object from the list, you can perform a set of actions: you can ask for details using the Details button, center selected object in the map using Center on map button or you can go to next star by pressing the Next button. Note that when go to next star, it will be automatically centered on the map. Also, Star-Hopper tool provides directions to star hop for every object of the list. [All] Details: Open the Object Details window for this object. [All] Attach Label: Attach a permanent name label to the object. If the object already has a label attached, this item will read Remove Label. [All] Add to Observing WishList: Add the selected object to Observation Planner's Wish List. You can see the Wish List, by selecting ObservationObservation Planner. If the object is already in the Observing WishList, then this item will become Remove Label. [Solar system objects] Add Trail: Add a trail to current Solar system object. A star trail is the continuous path created by the star in the night sky due to the rotation of the Earth. If the object already has a trail, this item will become Remove Trail. [All] Simulate eyepiece view: Eyepiece View feature renders the view through the eyepiece of various telescope types. Firstly, an input dialog is shown, which asks you to select one of the visible FOVs to use as a FOV for the eyepiece view. The user also has the option of trying to determine it from the image (accurate if image has metadata, otherwise it will be random). After you set the FOV, the "Eyepiece Field View" window will pop up. You can easily rotate the view using the "Rotation" slider. You can also flip and invert the view to help match the view through a telescope/camera. "Eyepiece view" tool can help you locate many objects with ease, instead of trying to work with relative positions and relative orientations. This is an important advantage, especially when you are star hopping. Note that for getting best results, time must be synced with current time in &kstars;, and &kstars; must be in horizontal coordinates mode. [Solar system objects] Image Resources: gives a list of image links for current Solar system object. The images are displayed in &kstars; Image Viewer tool. Image Viewer tool allows you to invert the colors and save the image in your computer. [Solar system objects] Information Resources: gives a list of documentation links for current Solar system object. The links are opened in your default browser. [All objects without Solar system objects] Show SDSS Image: download a SDSS (Sloan Digital Sky Survey) image of the object from the Internet, and display it in the Image Viewer tool. [All objects without Solar system objects] Show DSS Image: download a DSS (Digitized Sky Survey) image of the object from the Internet, and display it in the Image Viewer tool. Keyboard Commands Commands Keyboard Navigation Keys Navigation Controls Keyboard Arrow Keys Use the arrow keys to pan the display. Holding down the &Shift; key doubles the scrolling speed. + / - Zoom In/Out &Ctrl;Z Restore the default Zoom setting &Ctrl;&Shift;Z Zoom to specified field-of-view angle 0–9 Center Display on a major Solar System body: 0: Sun 1: Mercury 2: Venus 3: Moon 4: Mars 5: Jupiter 6: Saturn 7: Uranus 8: Neptune 9: Pluto Z Center the display at the Zenith Point (straight up) N Center the display above the North point on the horizon E Center the display above the East point on the horizon S Center the display above the South point on the horizon W Center the display above the West point on the horizon &Ctrl;T Toggle tracking mode < Advance the simulation clock backwards by one time step > Advance the simulation clock forwards by one time step Menu Shortcuts Commands Menu Keyboard Shortcuts &Ctrl;N Download extra data &Ctrl;O Open a FITS image in the FITS Editor &Ctrl;I Export sky image to a file &Ctrl;L Run a &kstars; Observation Planner. &Ctrl;R Run a &kstars; &DBus; script &Ctrl;P Print the current sky map &Ctrl;Q Quit &kstars; &Ctrl;E Sync the simulation clock with the current system time &Ctrl;S Set the simulation clock to a specified Time and Date &Ctrl;&Shift;F Toggle full-screen mode &Ctrl;0 Define equipment (telescope, eyepiece, lens and filter) characteristics for observation logs. &Ctrl;1 Add a new observer item for your observation logs Space Toggle between the Horizontal and Equatorial Coordinate Systems F1 Open the &kstars; Handbook F5 Switch sky chart view to the Lambert azimuthal equal-area projection. F6 Switch sky chart view to the azimuthal equidistant projection. F7 Switch sky chart view to the orthographic projection. F8 Switch sky chart view to the equirectangular projection. F9 Switch sky chart view to the stereographic projection. F10 Switch sky chart view to the gnomonic projection. Actions for the Selected Object Objects in the Sky Keyboard Actions Each of the following keystrokes performs an action on the selected object. The selected object is the last object which was clicked on (identified in the status bar). Alternatively, if you hold down the &Shift; key, then the action is performed on the centered object instead. C Center and Track on the selected object D Open the Details window for the selected object L Toggle a name label for the selected object O Add the selected object to the observing list P Open the selected object's popup menu T Toggle a trail on the selected object (solar system bodies only) Tools Shortcuts &Ctrl;F Open the Find Object window, for specifying a sky object on which to center &Ctrl;M Open the Set Coordinates Manually tool, for specifying RA/Dec or Az/Alt coordinates on which to center [ Start an Angular Distance measurement at the current mouse cursor position. The angular distance between start and end points is displayed at the endpoint. &Ctrl;G Open the Set Geographic Location window &Ctrl;C Open the AstroCalculator &Ctrl;A Open the Altitude vs. Time tool &Ctrl;U Open the What's Up Tonight? tool &Ctrl;W Open the What's Interesting tool &Ctrl;B Open the Script Builder tool &Ctrl;Y Open the Solar System Viewer &Ctrl;J Open the Jupiter Moons tool Mouse Commands Commands Mouse Navigation Controls Mouse Moving the mouse The sky coordinates (Az/Alt, RA/Dec and J2000.0 RA/Dec) of the mouse cursor are updated in the status bar. The status bar is located in the right bottom corner of the screen. The status bar can be customized by selecting Settings Statusbar submenu. Here you may choose what coordinates systems &kstars; will display in the status bar. Furthermore, you can hide the status bar by unchecking the Show Statusbar checkbox. "Hovering" the mouse A temporary name label is attached to the object nearest to the mouse cursor. Left-clicking Objects in the Sky Identifying The object nearest the mouse click is identified in the status bar. Double-clicking Objects in the Sky Centering Center and track on the location or object nearest the mouse click. Double-clicking on an Info Box will shade it to show/hide extra information. Right-clicking Objects in the Sky Invoking Popup Menu for Open the popup menu for the location or object nearest the mouse cursor. Scrolling the mouse wheel Zoom the display in or out. If you do not have a mouse wheel, you can hold the middle mouse button and drag vertically. Click-and-dragging Dragging the sky map Pan the display, following the drag motion. &Ctrl;+dragging the sky map Define a rectangle in the map. When the mouse button is released, the display is zoomed in to match the field-of-view to the bounds of the rectangle. Dragging an Info Box The Info Box is repositioned in the map. Info Boxes will stick to window edges, so that they remain on the edge when the window is resized. diff --git a/doc/config.docbook b/doc/config.docbook index f088e51ec..541f907cf 100644 --- a/doc/config.docbook +++ b/doc/config.docbook @@ -1,1184 +1,1223 @@ Configuring &kstars; Setting the Geographic Location Here is a screenshot of the Set Geographic Location window: Changing the Geographic Location Set Location Window There is a list of over 3400 predefined cities available to choose from. You set your location by highlighting a city from this list. Each city is represented in the world map as a small dot, and when a city is highlighted in the list, a red crosshairs appears on its location in the map. Geographic Location Tool Filtering It is not practical to scroll through the full list of 3400 locations, looking for a specific city. To make searches easier, the list can be filtered by entering text in the boxes below the map. For example, in the screenshot, the text A appears in the City Filter box, while Te has been entered in the Province Filter box, and USA is in the Country Filter box. Note that all of the cities displayed in the list have city, province, and country names that begin with the entered filter strings, and that the message below the filter boxes indicates that 6 cities are matched by the filters. Also notice that the dots representing these six cities in the map have been colored white, while the unmatched cities remain gray. The list can also be filtered by location in the map. Clicking anywhere in the world map will show only those cities within two degrees of the clicked location. At this time, you can search by name, or by location, but not both at once. In other words, when you click on the map, the name filters are ignored, and vice versa. Geographic Location Tool Custom locations The longitude, latitude and time zone information for the currently-selected location are displayed in the boxes at the bottom of the window. If you feel that any of these values are inaccurate, you can modify them and press the + (Add City) button to record your custom version of the location. You can also define a completely new location by pressing the Clear Fields button, and entering the data for the new location. Note that all fields except the optional State/Province must be filled before the new location can be added to the list. &kstars; will automatically load your custom locations for all future sessions. Please note, at this point, the only way to remove a custom location is to remove the appropriate line from the file kstars/mycities.dat in your folder qtpaths . If you add custom locations (or modify existing ones), please send us your mycities.dat file so that we can add your locations to the master list. Setting the Time Date and Time The simulation clock When &kstars; starts up, the time is set to your computer's system clock, and the &kstars; clock is running to keep up with the real time. If you want to stop the clock, select Stop Clock from the Time menu, or simply click on the Stop Clock icon in the toolbar. You can make the clock run slower or faster than normal, or even make it run backward, using the time-step spinbox in the toolbar. This spinbox has two sets of up/down buttons. The first one will step through all 83 available time steps, one by one. The second one will skip to the next higher (or lower) unit of time, which allows you to make large timestep changes more quickly. Date and Time Setting You can set the time and date by selecting Set Time... from the Time menu, or by pressing the time icon in the toolbar. The Set Time window uses a standard &kde; Date Picker widget, coupled with a spinbox for setting the hours and minutes. If you want to re-synchronize the simulation clock back to the current CPU time, just select Set Time to Now from the Time menu. Date and Time Extended range of dates &kstars; can accept very remote dates beyond the usual limits imposed by QDate. Currently, you can set the date between the years -100000 and +100000. We may extend this range even further in future releases. However, please be aware that the accuracy of the simulation becomes more and more degraded as more remote dates are examined. This is especially true for the positions of solar system bodies. The Configure &kstars; Window Configure &kstars; window The Configure &kstars; window allows you to modify a wide range of display options. You can access the window with the configure toolbar icon, or by selecting Configure &kstars;... from the Settings menu. The window is depicted below: Configure &kstars; Window Configure &kstars; Window -The Configure &kstars; window is divided into nine pages: +The Configure &kstars; window is divided into eleven pages: Catalogs, Solar System, Satellites, -Supernovae, Guides, Colors, -INDI, Ekos and Advanced. +Supernovae, Guides, Colors, FITS, +INDI, Ekos, Xplanet and Advanced. Configure &kstars; window Catalogs page In the Catalogs page, you determine which object catalogs are displayed in the map along with several properties. Configure &kstars; window Solar System page In the Solar System page, you can specify whether the Sun, Moon, planets, comets and asteroids are displayed. Configure &kstars; window Satellites page The Satellites page allows you to set the satellites view options. Configure &kstars; window Supernovae page The Supernovae page allows you to manage how supernovae are displayed by &kstars;. Configure &kstars; window Guides page The Guides page lets you toggle whether non-objects are displayed (&ie;, constellation lines, constellation names, the Milky Way contour). Configure &kstars; window Colors page Color Schemes Customizing The Colors page allows you to set the color scheme, and to define new custom color schemes. +For detailed explanation of the options on the FITS +page see the Configure FITS section. + + For detailed explanation of the options on the INDI page see the Configure INDI section. For detailed explanation of Ekos astrophotography suite, see the official Ekos page. + +Configure &kstars; window +Xplanet page +The Xplanet page provides fine-grained control +over Solar system planet surface renderer Xplanet (should be installed separately). + + Configure &kstars; window Advanced page The Advanced page provides fine-grained control over the more subtle behaviors of &kstars;. Catalogs Catalogs Window Catalogs Window Catalogs page Short overview In the Catalogs page, you can configure which object catalogs are displayed by &kstars;, as well as how much information you would like to be included on the Sky Map. By default, &kstars; includes ~300,000 named and unnamed stars up to magnitude 8. For Deep Sky Objects, the included catalogs are New General Catalog (NGC), Index Catalog (IC), and Messier Catalog. New General Catalogue of Nebulae and Clusters of Stars (abbreviated as NGC) is a catalogue of 7,840 deep-sky objects. Index Catalogue of Nebulae and Clusters of Stars (abbreviated as IC) serves as a supplement to the NGC, and contains an additional 5,386 objects, collectively known as the IC objects. Messier Catalogue is a catalogue of 110 deep-sky objects, including diffuse nebulae, planetary nebulae, open clusters, globular clusters and galaxies. Messier objects have names like M1, M2, up to M110. The maximum apparent visual magnitude of Messier Catalogue is represented by M91's value of 10.2. You can install new catalogues using &kstars; Add-On Installer. You can open it by opening the DataDownload New Data... submenu. You can choose from a list of catalogues, including: Steinicke NGC/IC Catalog: is a more complete NGC/IC catalog. Abell Planetary Nebulae Catalog: is a catalog of 86 planetary nebulae. The maximum magnitude is represented by Abell 47's value of 19.5. Sharpless HII region Catalog: is the Sharpless (Sh2) catalog of HII regions (diffuse nebulae). Hickson Compact Groups: is a catalog consisting of 99 compact groups of galaxies. Tycho-2 Star Catalog: is a catalog of more than 2.5 million of the brightest stars. It contains stars with a magnitude value from 8.0 to 12.5. USNO NOMAD Catalog: is a catalog of about 100 million stars with magnitude from 12.5 to 16.5. Note that is requires Tycho-2 to be installed. The following is a summary of catalogs in KStars: Stars Catalogues Name Abbreviation Number of objects Magnitude Add-On Default Default Catalog Default ~300,000 Up to 8 magnitude No Yes Tycho-2 Tycho2 more than 2.5 million 8.0-12.5 Yes No Naval Observatory Merged Astronomic Dataset USNO NOMAD 100 million 12.5-16.5 Yes No
Deep-sky objects Catalogues Name Abbreviation Number of objects Magnitude Add-On Default Index Catalogue of Nebulae and Clusters of Stars IC 5,386 Up to 18.3 magnitude No Yes New General Catalogue of Nebulae and Clusters of Stars NGC 7,840 - No Yes Messier Catalogue - 110 Up to 10.2 magnitude No Yes Steinicke NGC/IC - - - Yes No Abell Planetary Nebulae Catalog - 86 Up to 19.5 magnitude Yes No Sharpless HII region Catalog Sh2 - - Yes No Hickson Compact Groups - 99 - Yes No
Catalogs Star Catalogs The Stars section allows you to manage how stars are displayed in &kstars;. You can choose to see the stars or not by checking the Star Catalogs checkbox. If you check it, then multiple options will be enabled. Thus, you can set how many stars are drawn on the map using the Star Density slider. You can also customize &kstars; to toggle star name and magnitudes. Star names are drawn next to bright stars. To display labels of fainter stars, increase the Label density slider. Catalogs Deep-Sky Catalogs Below the stars section, the Deep-Sky Objects section controls the display of several non-stellar object catalogs. You can toggle the display of Deep Sky Objects and control the display of their names and magnitudes. By default, the list of deep-sky objects includes the Messier, NGC and IC catalogs. Addons catalogs are available via the DataDownload New Data... submenu where you can download catalogs provided by &kstars; team and the community. Furthermore, &kstars; supports import of custom catalogs. To import a raw ASCII catalog data file into &kstars;, press the Import Catalog and follow the instructions. To import a custom catalog already in &kstars; catalog format, press the Load Catalog button. Each line in the custom catalog file should contain the following space-separated fields: For stars: type(0 for stars), RA, Dec, mag, SpType, name(optional) For other types: type(3-8), RA, Dec, mag (optional), flux(optional), name(optional) The types are: 0: star 1: star (in object catalog...probably don't want to use this) 2: planet (don't use this in custom catalog) 3: open cluster 4: globular cluster 5: gaseous nebula 6: planetary nebula 7: supernova remnant 8: galaxy 18: radio source The SpType is a short string for the spectral type. For example, B5 or G2. The coordinates should be given as floating-point values, in the J2000.0 epoch. The name can be anything you wish. If the name is more than one word, it must be enclosed in quotation marks. Once you have constructed a custom data file, open the &kstars; configuration window to the Catalogs tab, and press the Import Catalog... button. A popup window appears in which you can specify a name for the catalog, and the name of the file (including the path): Import Catalog Window Import Catalog Window When you press Ok button, &kstars; will attempt to read the lines of your data file. It will report any problems, and if any lines at all were successfully parsed, you are given a choice to accept the data file (ignoring any unparsed lines), or to cancel the operation to attempt to resolve the problems first. You can load a new catalog using the Load Catalog... button. A new window will appear, asking you to specify the file that contains the catalog. Load Catalog Window Load Catalog Window Once the data file has been accepted, your custom catalog will be loaded on startup along with the standard catalogs. In the Catalogs window is a checkbox for each catalog which toggles the display of catalog objects. Add Catalog Add Catalog Note that, if you want to load a catalog that is already loaded, a warning dialog will pop-up. Overwrite Catalog Overwrite Catalog You can remove custom catalogs by highlighting its checkbox in the Catalogs window, and pressing the Remove Catalog... button (this button is active only if a custom catalog is highlighted in the list of checkboxes). Note that it can not be used for removing &kstars; default catalogs. Delete Catalog Delete Catalog For radio sources catalogs, you must include the flux frequency and units. For example: # Flux Frequency: 1420 Mhz # Flux Unit: mJy The following is a simple catalog file: # Name: my_catalog # Prefix: et_radio # Color: #00ff00 # Epoch: 2000 # Flux Frequency: 1420 Mhz # Flux Unit: mJy # ID RA Dc Tp Mj Mn PA Nm Flux J0001 12:31:23.1 +11:29:34 18 180.60 360.30 45 my_radio_source 70 Using the Catalogs window, you can define faint limits for sky objects for zoomed in and zoomed out states of the rendering. When the Show objects of unknown magnitude item is enabled, objects whose magnitudes are unknown, or not available to &kstars;, are drawn irrespective of the faint limits set. The following is a brief tutorial on adding new catalogues to &kstars;. To import a new catalog, download a raw catalog data file where the data columns are space delimited. Any lines starting with # shall be ignored. For this example, we shall use the Lynds Catalog of Dark Nebulae. Download / write the raw catalog data file (the raw file is the file containing catalog's objects described by a set of parameters, like: ID Number, Right Ascension, Declination and so on). In order to successfully load a custom catalog into &kstars;, you need to use the following syntax: (otherwise your catalog will be entirely ignored or maybe some objects from your catalog will be wrongly drawn) Every object should be written on a separate row. The fields of each line should be separated by white space. The catalog header may contain comment lines beginning with the # symbol. ID number: integer value. Right Ascension: colon-delimited hh:mm:ss.s or floating-point value. Declination: colon-delimited dd:mm:ss.s or floating-point value. Object type: integer value, one of [ 0,1,2,3,4,5,6,7,8 ]. Common name: string value (if it contains a space, it *must* be enclosed in quotes!). Magnitude: floating-point value. Major axis: floating-point value (length of major axis in arcmin). Minor axis: floating-point value (length of minor axis in arcmin). Position angle: floating-point value (position angle, in degrees). The following is a subset of the original raw data file: 1 16 26.0 -16 0 .18 +21.82 .054 3 49 8 452 2 18 4.0 -31 30 .13 -05.32 1.240 2 0 4 837 3 18 0.0 -31 0 .15 -04.33 5.600 2 0 6 817 4 16 59.5 -22 8 .18 +11.82 .004 5 27 7 533 5 17 13.2 -24 22 .20 +07.96 .012 4 0 9 595 The raw file contains some extra information, unusable for &kstars;. It also contains extra white spaces and values are not meeting &kstars; expectations (&ie; for Right Ascension: colon-delimited hh:mm:ss.s or floating-point value). Thus, it need to be modified in order to match &kstars; format. For a better understanding on what each column means, you can take a look at the original source of the catalog. It contains the raw data file and, in addition, it contains an useful readme, which will help you understand what you should keep and furthermore, what you need to remove from the raw data file. Minimally, the raw data file should contain the following fields: ID Number Object Type Right Ascension Declination The raw Dark Nebulae by Lynds contains only three usable fields for &kstars;: Right Ascension, Declination and Area (square degrees). Therefore, in order to properly import the catalog into &kstars;, the ID and Object Type fields need to be added. You can insert these values manually using your favorite text editor. However, it is recommended to use any spreadsheet application to import the raw data file and add the necessary columns. This is especially convenient for large data sets. Since the original raw data contains an area field which is not supported by &kstars;, we need to approximate it to a usable value which is the Major Axis. Therefore, we use the following formula in the spreadsheet to convert area to major axis in arcminutes: Major Axis = sqrt(Area) * 60 After importing the raw data file into &kstars; and selecting the appropriate columns, &kstars; shall generate the final catalog file suitable for loading directly into &kstars;. For example, this is a small subset of the content (header + first five objects) of the Dark Nebulae by Lynds catalog which was created by &kstars; after importing the raw data file which only contains the data columns: # Delimiter: # Name: LyndsCatalog # Prefix: Lynds # Color: #ff7600 # Epoch: 2000 # ID RA Dc Mj Tp 1 16:26:0 -16:0:0.1 13.943 5 2 18:4:0 -31:30:0.1 66.813 5 3 18:0:0 -31:0:0.1 141.986 5 4 16:59:5 -22:8:0.1 3.795 5 5 17:13:2 -24:22:0.2 6.573 5 As seen above, each column was assigned a &kstars; designated header such as the ID, Right Ascension, Declination, Major axis and Object Type fields. Note that the Catalog Prefix (Lynds) and the ID field are used together for identifying objects in the Sky Map (&ie; objects from this catalog will have names like: Lynds 1, Lynds 2, Lynds 617 up to the last object, Lynds 1791). Open the SettingsConfigure &kstars;... menu and choose Catalogs tab. In the Deep-Sky objects section, press the Import Catalog... button. If the button is not available, check the Deep-Sky Catalogs checkbox. This will enable you to configure &kstars; deep-sky objects catalogs. After you press the Import Catalog... button, Import Catalog window will pop up. At first, click on Open file dialog button in order to select the raw data file. Import catalog Import new catalog In the dialog window, find your raw file, select it and then press Open button. Open catalog Open Dark Nebulae catalog Now, you need to specify the correct order of the catalog fields within the raw data file. The fields must be added inside Catalog fields list. Note that you can drag fields in order to build the right order or you can use additional fields from Available fields fields. For example, if your raw data file contains a magnitude column, then you need to add Magnitude field to the Catalog fields list. Complete info for new catalog Complete info for new catalog After you set the fields so that they match to your catalog raw file, you can move to the next step: completing the remaining input fields: Coordinate epoch, Catalog name prefix, Catalog name and Save catalog as. You can also choose the Symbol color used for your catalog. There you can specify how the fields are split within the raw data file: CSV (Comma-separated values) or Space Delimited. You can preview the output by pressing the Preview Output button. Pay attention to the header fields to have the same order as your catalog fields (&ie; ID RA Dec Major Axis and Object Type). Press OK button to close the Catalog Preview window. Then press OK button again to create and save your catalog. After you successfully imported your catalog, it will be displayed in the catalogs list. You can choose to be displayed or not, by pressing on its checkbox. Added Dark Nebulae by Lynds catalog Dark Nebulae by Lynds catalog
Solar System Solar System Window Solar System Window Configure &kstars; window Solar System page In the Solar System page, you can specify whether the Sun, Moon, planets, comets and asteroids are displayed, and whether the major bodies are drawn as colored circles or actual images. You can also toggle whether solar system bodies have name labels attached, and control how many of the comets and asteroids get name labels. There is an option to automatically attach a temporary orbit trail whenever a solar system body is tracked, and another to toggle whether the color of the orbit trail fades into the background sky color. Satellites Satellites Window Satellites Window Configure &kstars; window Satellites page The Satellites page allows you to set the satellites view options. Firstly, you can see or hide the satellites on the skymap using Show satellites checkbox from the top View options section. By default, satellites are drawn as small light red filled circles with an optional dark red name label next to them. You can enable or disable these labels by checking or not the Show labels checkbox. It is located below the Show satellites checkbox, within the View options section. The colors of the dots representing satellites and their name labels can be easily customized using the Colors page from the same Configure &kstars; window. In addition, satellites can be drawn just like regular stars by checking the Draw satellites like stars checkbox. To display only the visible satellites from your current geographic location and time, select Show only visible satellites. &kstars; can draw artificial satellites from many predefined groups. Thus, you can select to display a particular group, multiple groups or partially select subgroups. Under each group, a list of individual satellites is presented. To select all satellites from a group, you need to check the group checkbox. You can also select only the satellites of interest in each group. The satellites orbital elements can be updated via the internet by pressing the Update TLEs button. Another way for updating the satellites orbital elements is to use the UpdatesUpdate satellites orbital elements in the Data menu. If you know the name of a desired satellite then you can use the search satellites method that &kstars; provides. You need to enter the name of satellite in the Search satellites text box and the list will be reduced only to its best matches. You can add new satellites to &kstars; default satellites set by editing the kstars/data/satellites.dat file. As each line of this file is a group of satellites, you need to add a new entry for your desired satellites group. An entry should have the following format: Group Name;local_filename;url. For example: Iridium;iridium.tle;http://celestrak.com/NORAD/elements/iridium.txt. Supernovae Supernovae Window Supernovae Window Configure &kstars; window Supernovae page The Supernovae page allows you to decide if the supernovae are displayed or not by checking the Show supernovae checkbox. By default, supernovae are drawn as small light orange + mark. As for satellites, the color of supernovae can be easily customized using the Colors page. You can configure &kstars; to check for newly discovered supernovae on startup by checking the Check for Recent Supernovae on Startup checkbox. This way, &kstars; will be always up-to-date with the most recent supernovae. You can enable supernova alerts using the Enable Supernova alerts checkbox. Thus, &kstars; will automatically alert you every time it learns of a new supernova in the sky. You can set the magnitude limit for showing a supernova as well as magnitude limit for supernova alerts using the spin boxes control. The limiting magnitude is the faintest apparent magnitude of an skyobject that is visible with the naked-eye or a telescope. The list of recent supernovae can be updated via the internet by pressing the Update List of Recent Supernovae button. Another way for updating the supernovae list is to use the UpdatesUpdate Recent Supernovae data item in the Data menu. Guides Guides Window Guides Window Configure &kstars; window Guides page The Guides page lets you toggle whether non-objects are displayed (&ie;, constellation lines, constellation names, the Milky Way contour, the celestial equator, the ecliptic, the horizon line, and the opaque ground). You can also choose whether you would like to see Latin constellation names, IAU-standard three-letter abbreviations, or constellation names using your local language. Colors Colors Window Colors Window Configure &kstars; window Colors page Color Schemes Customizing The Colors page allows you to set the color scheme, and to define custom color schemes. The tab is split into two panels: The left panel shows a list of all display items with adjustable colors. Click on any item to bring up a color selection window to adjust its color. Below the list is the Star Color Mode selection box. By default, &kstars; draws stars with a realistic color tint according to the spectral type of the star. However, you may also choose to draw the stars as solid white, black or red circles. If you are using the realistic star colors, you can set the saturation level of the star colors with the Star Color Intensity spinbox. The right panel lists the defined color schemes. There are four predefined schemes: the Default Colors scheme, Star Chart, which uses black stars on a white background, Night Vision, which uses only shades of red in order to protect dark-adapted vision, and Moonless Night, a more realistic, dark theme. Additionally, you can save the current color settings as a custom scheme by clicking the Save Current Colors button. It will prompt you for a name for the new scheme, and then your scheme will appear in the list in all future &kstars; sessions. To remove a custom scheme, simply highlight it in the list, and press the Remove Color Scheme button. + +FITS +Configure &kstars; window +FITS page +FITS (Flexible Image Transport System) is a popular open standard for storage, transmission and processing of digital data. For the details, one is referred to the corresponding Wikipedia article. This page allows you to configure presentation and processing of FITS data in &kstars;. + + + The left panel is for configuring FITS viewer itself. + + +Check the Use FITS Viewer item if you want automatically display received images in the FITS Viewer. + +The Single Preview Tab item is to display all captured FITS images in a single tab instead of multiple tabs +per image. The Single Window Capture item is to display captured FITS images from all cameras in a single FITS Viewer window +instead of a dedicated window to each camera. The Single Window Open item is to display opened FITS images in a single FITS Viewer window instead of a +dedicated window to each file and the Independent Window item is to make FITS Viewer window independent from &kstars;. + + +The right panel lists processing options. The Auto Stretch item is to always apply auto stretch to images in FITS Viewer, Limited Resources Mode is to enable limited resource mode to turn off any resource-intensive operations, namely: Auto Debayer (bayered images will not be debayered; only grayscale images are shown), Auto WCS (World Coordinate System data +will not be processed; WCS maps sky coordinates to image coordinates; +equatorial grid lines, object identification, and telescope slew within an +image are disabled), and 3D Cube (RGB images will not be +processed; only grayscale images are shown). You can switch off some of these resource-greedy operations separately as well. + + + INDI INDI Window INDI Window For detailed explanation of the options on the INDI page see the Configure INDI section. Ekos Ekos Window Ekos Window Ekos is an astrophotography suite, a complete astrophotography solution that can control all INDI devices including numerous telescopes, CCDs, DSLRs, focusers, filters, and a lot more. Ekos supports highly accurate tracking using online and offline astrometry solver, autofocus and autoguiding capabilities, and capture of single or multiple images using the powerful built in sequence manager. For detailed explanation of Ekos, see the official Ekos page. Advanced Advanced Window Advanced Window Configure &kstars; window Advanced page The Advanced page provides fine-grained control over the more subtle behaviors of &kstars;. Atmospheric Refraction The Correct for atmospheric refraction checkbox controls whether the positions of objects are corrected for the effects of the atmosphere. Because the atmosphere is a spherical shell, light from outer space is bent as it passes through the atmosphere to our telescopes or eyes on the Earth's surface. The effect is largest for objects near the horizon, and actually changes the predicted rise or set times of objects by a few minutes. In fact, when you see a sunset, the Sun's actual position is already well below the horizon; atmospheric refraction makes it seem as if the Sun is still in the sky. Note that atmospheric refraction is never applied if you are using Equatorial coordinates. Animated Slewing The Use animated slewing checkbox controls how the display changes when a new focus position is selected in the map. By default, you will see the sky drift or slew to the new position; if you uncheck this option, then the display will instead snap immediately to the new focus position. Objects in the Sky Labeling Automatic If the Attach label to centered object checkbox is selected, then a name label will automatically be attached to an object when it is being tracked by the program. The label will be removed when the object is no longer being tracked. Note that you can also manually attach a persistent name label to any object with its popup menu. Objects in the Sky Hiding There are three situations when &kstars; must redraw the sky display very rapidly: when a new focus position is selected (and Use animated slewing is checked), when the sky is dragged with the mouse, and when the time step is large. In these situations, the positions of all objects must be recomputed as rapidly as possible, which can put a large load on the CPU. If the CPU cannot keep up with the demand, then the display will seem sluggish or jerky. To mitigate this, &kstars; will hide certain objects during these rapid-redraw situations, as long as the Hide objects while moving checkbox is selected. The timestep threshold above which objects will be hidden is determined by the Also hide if time step larger than: timestep-spinbox. You can specify the objects that should be hidden in the Configure Hidden Objects group box. Customizing the Display There are several ways to modify the display to your liking. Color SchemesSelecting Select a different color scheme in the SettingsColor Schemes menu. There are four predefined color schemes, and you can define your own in the Configure &kstars; window. Toolbars Customizing Toggle whether the Toolbars are drawn in the SettingsToolbars Shown menu. Like most KDE toolbars, they can also be dragged around and anchored on any window edge, or even detached from the window completely if they are unlocked. Info BoxesCustomizing Info BoxesShading Toggle whether the Info Boxes are drawn in the SettingsInfo Boxes menu. In addition, you can manipulate the three Info Boxes with the mouse. Each box has additional lines of data that are hidden by default. You can toggle whether these additional lines are visible by double-clicking a box to shade it. Also, you can reposition a box by dragging it with the mouse. When a box hits a window edge, it will stick to the edge when the window is resized. Field-of-View SymbolsDescription Choose an FOV Symbol using the SettingsFOV Symbols menu. FOV is an acronym for field-of-view. An FOV symbol is drawn at the center of the window to indicate where the display is pointing. Different symbols have different angular sizes; you can use a symbol to show what the view through a particular telescope would look like. For example, if you choose the 7x35 Binoculars FOV symbol, then a circle is drawn on the display that is 9.2 degrees in diameter; this is the field-of-view for 7x35 binoculars. Field-of-View SymbolsCustomizing You can define your own FOV symbols (or modify the existing symbols) using the Edit FOV Symbols... menu item, which launches the FOV Editor: Field-of-View Symbols Editor FOV Symbol Editor The list of defined FOV symbols is displayed on the left. On the right are buttons for adding a new symbol, editing the highlighted symbol's properties, and removing the highlighted symbol from the list. Note that you can even modify or remove the four predefined symbols (if you remove all symbols, the four defaults will be restored the next time you start &kstars;). Below these three buttons is a graphical preview display showing the highlighted symbol from the list. When the New... or Edit... button is pressed, the New FOV Indicator window is opened: New Field-of-View Symbol New FOV Symbol Field-of-View SymbolsDefining New This window lets you modify the four properties that define a FOV symbol: name, size, shape, and color. The angular size for the symbol can either be entered directly in the Field of View edit box, or you can use the Eyepiece/Camera Tabs to calculate the field-of-view angle, given parameters of your telescope/eyepiece or telescope/camera setup. The five available shapes are: Square, Circle, Crosshairs, Bullseye and Semitransparent circle. Once you have specified all four parameters, press Ok, and the symbol will appear in the list of defined symbols. It will also be available from the SettingsFOV Symbols menu. +&hips; +
diff --git a/doc/hips.docbook b/doc/hips.docbook index 2c609f95b..c24fb9054 100644 --- a/doc/hips.docbook +++ b/doc/hips.docbook @@ -1,35 +1,35 @@ <acronym>HiPS</acronym> Progressive Overlay Views HiPS Progressive Overlay &kstars; provides support for HiPS: Hierarchical Progressive Surveys. HiPS provides multi-resolution progressive surveys to be overlayed directly in client applications. It provides an immersive experience as you can explore the night sky dynamically. With over 200+ surveys across the whole electromagnetic spectrum from radio, infrared, visual, to even gamma rays, the user can pan and zoom progressively deeper into the data visually. It can be enabled from the HiPS All Sky Overlay submenu in the View menu. -Under the submenu, a list of enabled survery are listed. Click on the survey of interest to activate it. You can only activate one-overlay at a time. After activating the survery, &kstars; shall begin downloading -the data in the background and progressivly overlay the images unto the sky map as they become ready. Zooming in usually requires another patch of images that should trigger another download cycle. +Under the submenu, a list of enabled surveys are listed. Click on the survey of interest to activate it. You can only activate one-overlay at a time. After activating the survey, &kstars; shall begin downloading +the data in the background and progressively overlay the images unto the sky map as they become ready. Zooming in usually requires another patch of images that should trigger another download cycle. DSS Color HiPS DSS Color Overlay The above screenshot shows the DSS Color visual overlay in &kstars;. HiPS Settings submenu includes the following pages: Display: Enable or disable and . The interpolation is enabled by default and should make the overlay appear smoother. Cache: Set the and cache size in MB. Increase cache size if you have abundant resources and want to reduce bandwidth required to download the images. - Sources: Browse a list of HiPS source and enable/disable them accordingly. When you select each source, a summary and a preview are downloaded that include information on the mission in additional to technical data on the survery. + Sources: Browse a list of HiPS sources and enable/disable them accordingly. When you select each source, a summary and a preview are downloaded that include information on the mission in additional to technical data on the survey. diff --git a/doc/index.docbook b/doc/index.docbook index e3337f2a3..9ffee73e0 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,270 +1,270 @@ ]> The &kstars; Handbook Jason Harris
&Jason.Harris.mail;
Jasem Mutlaq
mutlaqja AT ikarustech DOT com
Core Developer
Akarsh Simha
akarshsimha AT gmail dot com
Core Developer
James Bowlin
bowlin AT mindspring DOT com
Core Developer
Heiko Evermann
&Heiko.Evermann.mail;
Core Developer
Thomas Kabelmann
&Thomas.Kabelmann.mail;
Core Developer
Pablo de Vicente
&Pablo.de.Vicente.mail;
Core Developer
Carsten Niehaus
&Carsten.Niehaus.mail;
Core Developer
Mark Holloman
&Mark.Holloman.mail;
Core Developer
2001-2018 &Jason.Harris; and the &kstars; Team &FDLNotice; 2018-05-25 2.9.6 &kstars; is free, open source, cross-platform Astronomy Software. It provides an accurate graphical simulation of the night sky, from any location on Earth, at any date and time. The display includes up to 100 million stars, 13,000 deep-sky objects,all 8 planets, the Sun and Moon, and thousands of comets, asteroids, supernovae, and satellites. For students and teachers, it supports adjustable simulation speeds in order to view phenomena that happen over long timescales, the &kstars; Astrocalculator to predict conjunctions, and many common astronomical calculations. For the amateur astronomer, it provides an observation planner, a sky calendar tool, and an FOV editor to calculate field of view of equipment and display them. Find out interesting objects in the "What's up Tonight" tool, plot altitude vs. time graphs for any object, print high-quality sky charts, and gain access to lots of information and resources to help you explore the universe! HiPS all-sky progressive overlay provide stunning views from numerous surveys spanning the whole electromagnetic spectrum. Included with &kstars; is Ekos astrophotography suite, a complete astrophotography solution that can control all INDI devices including numerous telescopes, CCDs, DSLRs, focusers, filters, and a lot more. Ekos supports highly accurate tracking using online and offline astrometry solver, autofocus and autoguiding capabilities, and capture of single or multiple images using the powerful built in sequence manager. KDE kdeedu Astronomy KStars
Introduction &kstars; lets you explore the night sky from the comfort of your computer chair. It provides an accurate graphical representation of the night sky for any date, from any location on Earth. The display includes 126,000 stars to 9th magnitude (100 million with addon catalogs), 13,000 deep-sky objects (Messier, NGC, and IC catalogs), all planets, the Sun and Moon, hundreds of comets and asteroids, the Milky Way, 88 constellations, and guide lines such as the celestial equator, the horizon and the ecliptic. However, &kstars; is more than a simple night-sky simulator. The display provides a compelling interface to a number of tools with which you can learn more about astronomy and the night sky. There is a context-sensitive popup menu attached to each displayed object, which displays object-specific information and actions. Hundreds of objects provide links in their popup menus to informative web pages and beautiful images taken by the Hubble Space Telescope and other observatories. From an object's popup menu, you can open its Detailed Information Window, where you can examine positional data about the object, and query a huge treasury of online databases for professional-grade astronomical data and literature references about the object. You can even attach your own Internet links, images and text notes, making &kstars; a graphical front-end to your observing logs and your personal astronomical notebook. Our Astrocalculator tool provides direct access to many of the algorithms the program uses behind the scenes, including coordinate converters and time calculators. You can plan an observing session using our Altitude vs. Time tool, which will plot curves representing the Altitude as a function of time for any group of objects. If that is too much detail, we also provide a What's Up Tonight? tool that summarizes the objects that you will be able to see from your location on any given night. You can add your favorite objects to your observing wish-list using the Observation Planner tool, which allows you to plan your observation sessions professionally. To see how object appears in the eyepiece under different telescopes and field of views, use the Simulate Eyepiece View tool to render a simulated view of what you see. &kstars; also provides a Solar System Viewer, which shows the current configuration of the major planets in our solar system. There is also a Jupiter Moons Tool which shows the positions of Jupiter's four largest moons as a function of time. Our primary goal is to make &kstars; an interactive educational tool for learning about astronomy and the night sky. To this end, the &kstars; Handbook includes the AstroInfo Project, a series of short, hyperlinked articles on astronomical topics that can be explored with &kstars;. In addition, &kstars; includes &DBus; functions that allow you to write complex scripts, making &kstars; a powerful "demo engine" for classroom use or general illustration of astronomical topics. Furthermore, any 3rd party tool or language with support of &DBus; can be used to write powerful scripts using &kstars; &DBus; API Enable HiPS all-sky progressive overlay to fetch high-resolution images and display them directly in the sky map. You can select from numerous catalogs compiled from different earth and space based missions. -This features requires a fast interenet connection in order to download the images. The image are cached locally to reduce bandwidth. You can optimize the caching options to best balance between disk space versus bandwidth. +This features requires a fast internet connection in order to download the images. The images are cached locally to reduce bandwidth. You can optimize the caching options to best balance between disk space versus bandwidth. However, &kstars; is not just for students. You can control telescopes and cameras with &kstars;, using the elegant and powerful INDI protocol. &kstars; supports several popular telescopes including Meade's LX200 family and Celestron GPS. Several popular CCD cameras, webcams, and computerized focusers are also supported. Simple slew/track commands are integrated directly into the main window's popup menu, and the INDI Control Panel provides full access to all of your telescope's functions. INDI's Client/Server architecture allows for seamless control of any number of local or remote telescopes using a single &kstars; session.For advanced users, &kstars; provides Ekos, a complete astrophotography suite for Linux. Ekos is based on a modular extensible framework to perform common astrophotography tasks. This includes highly accurate GOTOs using astrometry solver, ability to measure and correct polar alignment errors , auto-focus and auto-guide capabilities, and capture of single or stack of images with filter wheel support. We are very interested in your feedback; please report bugs or feature requests to the &kstars; development mailing list: kstars-devel@kde.org. You can also use the automated bug reporting tool, accessible from the Help menu. &underFDL; &quicktour; &config; &commands; &tools; &dumpmode; &indi; &faq; &astroinfo; &credits; &documentation.index;
diff --git a/kstars/CMakeLists.txt b/kstars/CMakeLists.txt index 7746fe775..5929c4e4d 100644 --- a/kstars/CMakeLists.txt +++ b/kstars/CMakeLists.txt @@ -1,1122 +1,1128 @@ add_subdirectory( data ) add_subdirectory( icons ) add_subdirectory( htmesh ) if (${KF5_VERSION} VERSION_EQUAL 5.18.0 OR ${KF5_VERSION} VERSION_GREATER 5.18.0) SET(HAVE_KF5WIT 1) # if(NOT BUILD_KSTARS_LITE) # add_subdirectory( tools/whatsinteresting/qml) # endif(NOT BUILD_KSTARS_LITE) else() SET(HAVE_KF5WIT 0) endif() if (ANDROID AND CMAKE_TOOLCHAIN_FILE) include(${CMAKE_TOOLCHAIN_FILE}) endif () if (NOT ANDROID) find_package(ZLIB REQUIRED) find_package(Threads REQUIRED) endif () if(MSVC) add_definitions(-D_USE_MATH_DEFINES=1) add_definitions(-DNOMINMAX) endif() include_directories( ${kstars_SOURCE_DIR}/kstars ${kstars_SOURCE_DIR}/kstars/skyobjects ${kstars_SOURCE_DIR}/kstars/skycomponents ${kstars_SOURCE_DIR}/kstars/auxiliary ${kstars_SOURCE_DIR}/kstars/time ) if(BUILD_KSTARS_LITE) include_directories( ${kstars_SOURCE_DIR}/kstars/triangle ) else(BUILD_KSTARS_LITE) include_directories( ${kstars_SOURCE_DIR}/kstars/tools ) endif(BUILD_KSTARS_LITE) if(NOT BUILD_KSTARS_LITE) if (CFITSIO_FOUND) set (sep_SRCS fitsviewer/sep/analyse.c fitsviewer/sep/aperture.c fitsviewer/sep/background.c fitsviewer/sep/convolve.c fitsviewer/sep/deblend.c fitsviewer/sep/extract.c fitsviewer/sep/lutz.c fitsviewer/sep/util.c ) set (fits_SRCS fitsviewer/fitslabel.cpp fitsviewer/fitsviewer.cpp fitsviewer/fitstab.cpp fitsviewer/fitsdebayer.cpp fitsviewer/opsfits.cpp ) if (Qt5DataVisualization_FOUND) set(fits_SRCS ${fits_SRCS} fitsviewer/starprofileviewer.cpp) endif() set (fits2_SRCS fitsviewer/bayer.c fitsviewer/fitshistogram.cpp fitsviewer/fitsdata.cpp fitsviewer/fitsview.cpp ) set (fitsui_SRCS fitsviewer/fitsheaderdialog.ui fitsviewer/statform.ui fitsviewer/fitsdebayer.ui indi/streamform.ui indi/recordingoptions.ui fitsviewer/fitshistogramui.ui fitsviewer/opsfits.ui ) include_directories(${CFITSIO_INCLUDE_DIR}) endif(CFITSIO_FOUND) endif(NOT BUILD_KSTARS_LITE) -IF (CFITSIO_FOUND AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")) - IF (SANITIZERS) - SET_SOURCE_FILES_PROPERTIES(fitsviewer/bayer.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -fno-sanitize=address,undefined -fomit-frame-pointer") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitsdata.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitshistogram.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitsview.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") +IF (CFITSIO_FOUND) + IF (("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")) + IF (SANITIZERS) + SET_SOURCE_FILES_PROPERTIES(fitsviewer/bayer.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -fno-sanitize=address,undefined -fomit-frame-pointer") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitsdata.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitshistogram.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/fitsview.cpp PROPERTIES COMPILE_FLAGS "-fno-sanitize=address,undefined -fomit-frame-pointer") + ELSE () + SET_SOURCE_FILES_PROPERTIES(fitsviewer/bayer.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") + ENDIF () + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/analyse.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/aperture.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -Wno-pointer-arith") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/background.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/deblend.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -Wno-incompatible-pointer-types-discards-qualifiers") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/extract.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/lutz.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/util.c PROPERTIES COMPILE_FLAGS "-Wno-incompatible-pointer-types-discards-qualifiers") ELSE () - SET_SOURCE_FILES_PROPERTIES(fitsviewer/bayer.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/aperture.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-arith") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/deblend.c PROPERTIES COMPILE_FLAGS "-Wno-discarded-qualifiers") + SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/util.c PROPERTIES COMPILE_FLAGS "-Wno-discarded-qualifiers") ENDIF () - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/analyse.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/aperture.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -Wno-pointer-arith") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/background.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/deblend.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align -Wno-incompatible-pointer-types-discards-qualifiers") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/extract.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/lutz.c PROPERTIES COMPILE_FLAGS "-Wno-cast-align") - SET_SOURCE_FILES_PROPERTIES(fitsviewer/sep/util.c PROPERTIES COMPILE_FLAGS "-Wno-incompatible-pointer-types-discards-qualifiers") ENDIF () if (INDI_FOUND) if(BUILD_KSTARS_LITE) set (fits_SRCS fitsviewer/fitsdata.cpp ) set (fits2_SRCS fitsviewer/bayer.c ) include_directories(${CFITSIO_INCLUDE_DIR}) include_directories(${NOVA_INCLUDE_DIR}) set (indi_SRCS indi/clientmanagerlite.cpp indi/inditelescopelite.cpp kstarslite/skyitems/skynodes/crosshairnode.cpp kstarslite/skyitems/telescopesymbolsitem.cpp ) else(BUILD_KSTARS_LITE) set(indiui_SRCS indi/streamform.ui indi/drivermanager.ui indi/opsindi.ui indi/indihostconf.ui indi/telescopewizard.ui ) set(indi_SRCS indi/drivermanager.cpp indi/servermanager.cpp indi/clientmanager.cpp indi/guimanager.cpp indi/driverinfo.cpp indi/deviceinfo.cpp indi/indidevice.cpp indi/indigroup.cpp indi/indiproperty.cpp indi/indielement.cpp indi/indistd.cpp indi/indilistener.cpp indi/inditelescope.cpp indi/indiccd.cpp indi/indifocuser.cpp indi/indifilter.cpp indi/indidome.cpp indi/indiweather.cpp indi/indicap.cpp indi/indilightbox.cpp indi/indidbus.cpp indi/opsindi.cpp indi/telescopewizardprocess.cpp indi/streamwg.cpp indi/videowg.cpp indi/indiwebmanager.cpp ) if (CFITSIO_FOUND) set(ekosui_SRCS ekos/opsekos.ui ekos/ekosmanager.ui ekos/profileeditor.ui ekos/profilewizard.ui # Scheduler ekos/scheduler/scheduler.ui ekos/scheduler/mosaic.ui # Capture ekos/capture/capture.ui ekos/capture/calibrationoptions.ui ekos/capture/dslrinfo.ui ekos/capture/rotatorsettings.ui ekos/capture/customproperties.ui # Align ekos/align/align.ui ekos/align/opsastrometry.ui ekos/align/opsalign.ui ekos/align/opsastrometrycfg.ui ekos/align/opsastrometryindexfiles.ui ekos/align/mountmodel.ui # Focus ekos/focus/focus.ui # Mount ekos/mount/mount.ui # Guide ekos/guide/guide.ui ekos/guide/opscalibration.ui ekos/guide/opsguide.ui #TODO remove from GIT #ekos/guide/guider.ui #ekos/guide/rcalibration.ui # Auxiliary ekos/auxiliary/filtersettings.ui ekos/auxiliary/opslogs.ui ) set(ekos_SRCS ekos/ekos.cpp ekos/ekosmanager.cpp ekos/profileeditor.cpp ekos/profilewizard.cpp ekos/qMDNS.cpp ekos/opsekos.cpp # Auxiliary ekos/auxiliary/dome.cpp ekos/auxiliary/weather.cpp ekos/auxiliary/dustcap.cpp ekos/auxiliary/darklibrary.cpp ekos/auxiliary/filtermanager.cpp ekos/auxiliary/filterdelegate.cpp ekos/auxiliary/opslogs.cpp # Capture ekos/capture/capture.cpp ekos/capture/sequencejob.cpp ekos/capture/dslrinfodialog.cpp ekos/capture/rotatorsettings.cpp ekos/capture/customproperties.cpp # Scheduler ekos/scheduler/schedulerjob.cpp ekos/scheduler/scheduler.cpp ekos/scheduler/mosaic.cpp # Focus ekos/focus/focus.cpp # Mount ekos/mount/mount.cpp # Align ekos/align/align.cpp ekos/align/alignview.cpp ekos/align/astrometryparser.cpp ekos/align/opsastrometry.cpp ekos/align/opsalign.cpp ekos/align/opsastrometrycfg.cpp ekos/align/opsastrometryindexfiles.cpp ekos/align/offlineastrometryparser.cpp ekos/align/onlineastrometryparser.cpp ekos/align/remoteastrometryparser.cpp # Guide ekos/guide/guide.cpp ekos/guide/guideinterface.cpp ekos/guide/opscalibration.cpp ekos/guide/opsguide.cpp # Internal Guide ekos/guide/internalguide/gmath.cpp ekos/guide/internalguide/internalguider.cpp #ekos/guide/internalguide/guider.cpp ekos/guide/internalguide/matr.cpp #ekos/guide/internalguide/rcalibration.cpp ekos/guide/internalguide/vect.cpp ekos/guide/internalguide/imageautoguiding.cpp # External Guide ekos/guide/externalguide/phd2.cpp ekos/guide/externalguide/linguider.cpp ) endif(CFITSIO_FOUND) endif(BUILD_KSTARS_LITE) include_directories(${INDI_INCLUDE_DIR}) endif (INDI_FOUND) if(NOT BUILD_KSTARS_LITE) if(WCSLIB_FOUND) include_directories( ${WCSLIB_INCLUDE_DIR} ) endif(WCSLIB_FOUND) set(xplanet_SRCS xplanet/opsxplanet.cpp ) set(xplanetui_SRCS xplanet/opsxplanet.ui ) ########### next target ############### set(libkstarstools_SRCS tools/altvstime.cpp tools/avtplotwidget.cpp tools/calendarwidget.cpp tools/conjunctions.cpp # tools/jmoontool.cpp tools/ksconjunct.cpp tools/eqplotwidget.cpp tools/astrocalc.cpp tools/modcalcangdist.cpp tools/modcalcapcoord.cpp tools/modcalcaltaz.cpp tools/modcalcdaylength.cpp tools/modcalceclipticcoords.cpp tools/modcalcvizequinox.cpp tools/modcalcgalcoord.cpp tools/modcalcgeodcoord.cpp tools/modcalcjd.cpp tools/modcalcplanets.cpp tools/modcalcsidtime.cpp tools/modcalcvlsr.cpp tools/observinglist.cpp tools/obslistpopupmenu.cpp tools/sessionsortfilterproxymodel.cpp tools/obslistwizard.cpp tools/planetviewer.cpp tools/pvplotwidget.cpp tools/scriptargwidgets.cpp tools/scriptbuilder.cpp tools/scriptfunction.cpp tools/skycalendar.cpp tools/wutdialog.cpp tools/flagmanager.cpp tools/horizonmanager.cpp tools/nameresolver.cpp tools/polarishourangle.cpp #FIXME Port to KF5 #tools/moonphasetool.cpp tools/starhopper.cpp tools/eyepiecefield.cpp tools/exporteyepieceview.cpp tools/starhopperdialog.cpp tools/adddeepskyobject.cpp ) if(${KF5_VERSION} VERSION_EQUAL 5.18.0 OR ${KF5_VERSION} VERSION_GREATER 5.18.0) set(libkstarstools_SRCS ${libkstarstools_SRCS} tools/whatsinteresting/skyobjlistmodel.cpp tools/whatsinteresting/wiview.cpp tools/whatsinteresting/modelmanager.cpp tools/whatsinteresting/skyobjitem.cpp tools/whatsinteresting/wilpsettings.cpp tools/whatsinteresting/wiequipsettings.cpp tools/whatsinteresting/obsconditions.cpp tools/whatsinteresting/skyobjdescription.cpp ) endif() ki18n_wrap_ui(libkstarstools_ui_SRCS tools/altvstime.ui tools/argchangeviewoption.ui tools/argexportimage.ui tools/argloadcolorscheme.ui tools/arglooktoward.ui tools/argfindobject.ui tools/argprintimage.ui tools/argsetaltaz.ui tools/argsetcolor.ui tools/argsetgeolocation.ui tools/argsetlocaltime.ui tools/argsetradec.ui tools/argsettrack.ui tools/argtimescale.ui tools/argwaitfor.ui tools/argwaitforkey.ui tools/argzoom.ui tools/conjunctions.ui tools/modcalcangdist.ui tools/modcalcapcoord.ui tools/modcalcaltaz.ui tools/modcalcdaylength.ui tools/modcalceclipticcoords.ui tools/modcalcvizequinox.ui tools/modcalcgalcoord.ui tools/modcalcgeod.ui tools/modcalcjd.ui tools/modcalcplanets.ui tools/modcalcsidtime.ui tools/modcalcvlsr.ui tools/observinglist.ui tools/obslistwizard.ui tools/optionstreeview.ui tools/planetviewer.ui tools/scriptbuilder.ui tools/scriptnamedialog.ui tools/skycalendar.ui tools/wutdialog.ui tools/flagmanager.ui tools/starhopperdialog.ui tools/horizonmanager.ui tools/adddeepskyobject.ui tools/polarishourangle.ui ) if (${KF5_VERSION} VERSION_EQUAL 5.18.0 OR ${KF5_VERSION} VERSION_GREATER 5.18.0) ki18n_wrap_ui(libkstarstools_ui_SRCS tools/whatsinteresting/wilpsettings.ui tools/whatsinteresting/wiequipsettings.ui ) endif() set(libkstarswidgets_SRCS widgets/clicklabel.cpp widgets/dmsbox.cpp widgets/draglistbox.cpp widgets/fovwidget.cpp widgets/logedit.cpp widgets/magnitudespinbox.cpp widgets/mapcanvas.cpp widgets/thumbimage.cpp widgets/timespinbox.cpp widgets/timestepbox.cpp widgets/timeunitbox.cpp widgets/infoboxwidget.cpp # widgets/genericcalendarwidget.cpp # widgets/moonphasecalendarwidget.cpp widgets/kshelplabel.cpp widgets/unitspinboxwidget.cpp ) ki18n_wrap_ui(libkstarswidgets_ui_SRCS # widgets/genericcalendarwidget.ui widgets/unitspinboxwidget.ui ) set(kstars_KCFG_SRCS Options.kcfgc) set(kstars_options_SRCS options/opsadvanced.cpp options/opscatalog.cpp options/opscolors.cpp options/opsguides.cpp options/opssolarsystem.cpp options/opssatellites.cpp options/opssupernovae.cpp ) set(kstars_optionsui_SRCS options/opsadvanced.ui options/opscatalog.ui options/opscolors.ui options/opsguides.ui options/opssolarsystem.ui options/opssatellites.ui options/opssupernovae.ui ) set(kstars_dialogs_SRCS dialogs/addcatdialog.cpp dialogs/addlinkdialog.cpp dialogs/detaildialog.cpp dialogs/finddialog.cpp dialogs/focusdialog.cpp dialogs/fovdialog.cpp dialogs/locationdialog.cpp dialogs/timedialog.cpp dialogs/exportimagedialog.cpp ) set(kstars_dialogsui_SRCS dialogs/addcatdialog.ui dialogs/addlinkdialog.ui dialogs/details_database.ui dialogs/details_data.ui dialogs/details_data_comet.ui dialogs/details_links.ui dialogs/details_log.ui dialogs/details_position.ui dialogs/finddialog.ui dialogs/focusdialog.ui dialogs/fovdialog.ui dialogs/locationdialog.ui dialogs/wizwelcome.ui dialogs/wizlocation.ui dialogs/wizdownload.ui dialogs/wizdata.ui dialogs/wizastrometry.ui dialogs/newfov.ui dialogs/exportimagedialog.ui ) set(hips_SRCS hips/healpix.cpp hips/hipsrenderer.cpp hips/scanrender.cpp hips/pixcache.cpp hips/urlfiledownload.cpp hips/opships.cpp ) set(hips_manager_SRCS hips/hipsmanager.cpp ) set(oal_SRCS oal/log.cpp oal/observer.cpp oal/site.cpp oal/session.cpp oal/scope.cpp oal/eyepiece.cpp oal/filter.cpp oal/observation.cpp oal/lens.cpp oal/equipmentwriter.cpp oal/observeradd.cpp oal/execute.cpp ) set(printing_SRCS printing/detailstable.cpp printing/finderchart.cpp printing/foveditordialog.cpp printing/fovsnapshot.cpp printing/kstarsdocument.cpp printing/legend.cpp printing/loggingform.cpp printing/printingwizard.cpp printing/pwizchartconfig.cpp printing/pwizchartcontents.cpp printing/pwizfovbrowse.cpp printing/pwizfovconfig.cpp printing/pwizfovmanual.cpp printing/pwizfovsh.cpp printing/pwizfovtypeselection.cpp printing/pwizobjectselection.cpp printing/pwizprint.cpp printing/shfovexporter.cpp printing/simplefovexporter.cpp ) set(printingui_SRCS printing/foveditordialog.ui printing/pwizchartconfig.ui printing/pwizchartcontents.ui printing/pwizfovbrowse.ui printing/pwizfovconfig.ui printing/pwizfovmanual.ui printing/pwizfovsh.ui printing/pwizfovtypeselection.ui printing/pwizobjectselection.ui printing/pwizprint.ui printing/pwizwelcome.ui ) endif(NOT BUILD_KSTARS_LITE) set( kstars_KCFG_SRCS Options.kcfgc ) set(libkstarscomponents_SRCS skycomponents/skylabeler.cpp skycomponents/highpmstarlist.cpp skycomponents/skymapcomposite.cpp skycomponents/skymesh.cpp skycomponents/linelistindex.cpp skycomponents/linelistlabel.cpp skycomponents/noprecessindex.cpp skycomponents/listcomponent.cpp skycomponents/pointlistcomponent.cpp skycomponents/solarsystemsinglecomponent.cpp skycomponents/solarsystemlistcomponent.cpp skycomponents/asteroidscomponent.cpp skycomponents/cometscomponent.cpp skycomponents/planetmoonscomponent.cpp skycomponents/solarsystemcomposite.cpp skycomponents/satellitescomponent.cpp skycomponents/starcomponent.cpp skycomponents/deepstarcomponent.cpp skycomponents/deepskycomponent.cpp skycomponents/catalogcomponent.cpp skycomponents/syncedcatalogcomponent.cpp skycomponents/constellationartcomponent.cpp skycomponents/constellationboundarylines.cpp skycomponents/constellationlines.cpp skycomponents/constellationnamescomponent.cpp skycomponents/supernovaecomponent.cpp skycomponents/coordinategrid.cpp skycomponents/equatorialcoordinategrid.cpp skycomponents/horizontalcoordinategrid.cpp skycomponents/localmeridiancomponent.cpp skycomponents/ecliptic.cpp skycomponents/equator.cpp skycomponents/artificialhorizoncomponent.cpp skycomponents/hipscomponent.cpp skycomponents/horizoncomponent.cpp skycomponents/milkyway.cpp skycomponents/skycomponent.cpp skycomponents/skycomposite.cpp skycomponents/starblock.cpp skycomponents/starblocklist.cpp skycomponents/starblockfactory.cpp skycomponents/culturelist.cpp skycomponents/flagcomponent.cpp skycomponents/targetlistcomponent.cpp ) if(NOT BUILD_KSTARS_LITE) LIST(APPEND libkstarscomponents_SRCS #skycomponents/notifyupdatesui.cpp ) else(NOT BUILD_KSTARS_LITE) set(libkstarstools_ui_SRCS tools/nameresolver.cpp ) endif(NOT BUILD_KSTARS_LITE) set(kstars_skyobjects_SRCS skyobjects/constellationsart.cpp skyobjects/deepskyobject.cpp # skyobjects/jupitermoons.cpp skyobjects/planetmoons.cpp skyobjects/ksasteroid.cpp skyobjects/kscomet.cpp skyobjects/ksmoon.cpp skyobjects/ksplanetbase.cpp skyobjects/ksplanet.cpp #skyobjects/kspluto.cpp skyobjects/kssun.cpp skyobjects/skyline.cpp skyobjects/skyobject.cpp skyobjects/skypoint.cpp skyobjects/starobject.cpp skyobjects/trailobject.cpp skyobjects/satellite.cpp skyobjects/satellitegroup.cpp skyobjects/supernova.cpp ) set(kstars_projection_SRCS projections/projector.cpp projections/lambertprojector.cpp projections/gnomonicprojector.cpp projections/stereographicprojector.cpp projections/orthographicprojector.cpp projections/azimuthalequidistantprojector.cpp projections/equirectangularprojector.cpp ) set(kstars_extra_SRCS auxiliary/colorscheme.cpp auxiliary/dms.cpp auxiliary/cachingdms.cpp auxiliary/geolocation.cpp auxiliary/ksfilereader.cpp auxiliary/ksuserdb.cpp auxiliary/binfilehelper.cpp auxiliary/ksutils.cpp auxiliary/ksdssimage.cpp auxiliary/ksdssdownloader.cpp auxiliary/nonlineardoublespinbox.cpp auxiliary/profileinfo.cpp auxiliary/filedownloader.cpp auxiliary/kspaths.cpp auxiliary/QRoundProgressBar.cpp auxiliary/skyobjectlistmodel.cpp auxiliary/ksnotification.cpp auxiliary/QProgressIndicator.cpp time/simclock.cpp time/kstarsdatetime.cpp time/timezonerule.cpp ksnumbers.cpp kstarsdata.cpp texturemanager.cpp #to minimize number of indef KSTARS_LITE skypainter.cpp ) if(NOT BUILD_KSTARS_LITE) LIST(APPEND kstars_extra_SRCS auxiliary/thememanager.cpp auxiliary/schememanager.cpp auxiliary/imageviewer.cpp auxiliary/fov.cpp auxiliary/thumbnailpicker.cpp auxiliary/thumbnaileditor.cpp auxiliary/imageexporter.cpp auxiliary/kswizard.cpp auxiliary/qcustomplot.cpp kstarsdbus.cpp kspopupmenu.cpp ksalmanac.cpp kstarsactions.cpp kstarsinit.cpp kstars.cpp kstarssplash.cpp skymap.cpp skymapdrawabstract.cpp skymapqdraw.cpp skymapevents.cpp skyqpainter.cpp ) endif(NOT BUILD_KSTARS_LITE) if(BUILD_KSTARS_LITE) #Temporary solution to allow use of qml files from source dir DELETE add_definitions( -DSOURCE_DIR=\"${kstars_SOURCE_DIR}\" ) add_definitions(-DQML_IMPORT="${CMAKE_CURRENT_SOURCE_DIR}") set(kstarslite_SRCS kstarslite.cpp kstarsliteinit.cpp skymaplite.cpp skymapliteevents.cpp #Wrappers kstarslite/skypointlite.cpp kstarslite/skyobjectlite.cpp #ImageProvider kstarslite/imageprovider.cpp #Dialogs kstarslite/dialogs/detaildialoglite.cpp kstarslite/dialogs/finddialoglite.cpp kstarslite/dialogs/locationdialoglite.cpp #RootNode kstarslite/skyitems/rootnode.cpp kstarslite/skyitems/skyopacitynode.cpp kstarslite/skyitems/typedeflite.h #SkyItems kstarslite/skyitems/skyitem.cpp kstarslite/skyitems/planetsitem.cpp kstarslite/skyitems/asteroidsitem.cpp kstarslite/skyitems/cometsitem.cpp kstarslite/skyitems/horizonitem.cpp kstarslite/skyitems/labelsitem.cpp kstarslite/skyitems/constellationnamesitem.cpp kstarslite/skyitems/staritem.cpp kstarslite/skyitems/deepstaritem.cpp kstarslite/skyitems/deepskyitem.cpp kstarslite/skyitems/constellationartitem.cpp kstarslite/skyitems/satellitesitem.cpp kstarslite/skyitems/supernovaeitem.cpp kstarslite/skyitems/fovitem.cpp kstarslite/skyitems/syncedcatalogitem.cpp #Line kstarslite/skyitems/lines/linesitem.cpp kstarslite/skyitems/lines/equatoritem.cpp kstarslite/skyitems/lines/eclipticitem.cpp kstarslite/skyitems/lines/milkywayitem.cpp #SkyNodes kstarslite/skyitems/skynodes/planetnode.cpp kstarslite/skyitems/skynodes/skynode.cpp kstarslite/skyitems/skynodes/pointsourcenode.cpp kstarslite/skyitems/skynodes/planetmoonsnode.cpp kstarslite/skyitems/skynodes/horizonnode.cpp kstarslite/skyitems/skynodes/labelnode.cpp kstarslite/skyitems/skynodes/guidelabelnode.cpp kstarslite/skyitems/skynodes/deepskynode.cpp kstarslite/skyitems/skynodes/dsosymbolnode.cpp kstarslite/skyitems/skynodes/skypolygonnode.cpp kstarslite/skyitems/skynodes/constellationartnode.cpp kstarslite/skyitems/skynodes/satellitenode.cpp kstarslite/skyitems/skynodes/supernovanode.cpp kstarslite/skyitems/skynodes/trixelnode.cpp kstarslite/skyitems/skynodes/fovsymbolnode.cpp #Nodes kstarslite/skyitems/skynodes/nodes/pointnode.cpp kstarslite/skyitems/skynodes/nodes/polynode.cpp kstarslite/skyitems/skynodes/nodes/linenode.cpp kstarslite/skyitems/skynodes/nodes/ellipsenode.cpp kstarslite/skyitems/skynodes/nodes/rectnode.cpp #Other kstarslite/deviceorientation.cpp ) set(kstarslite_libtess_SRC #libtess libtess/gluos.h libtess/priorityq-sort.h libtess/sweep.c libtess/tessmono.c libtess/dict-list.h libtess/glu.h libtess/tessellate.c libtess/dict.c libtess/geom.c libtess/memalloc.c libtess/mesh.c libtess/normal.c libtess/priorityq.c libtess/priorityq-heap.c libtess/render.c libtess/tess.c ) IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") SET_SOURCE_FILES_PROPERTIES(${kstarslite_libtess_SRC} PROPERTIES COMPILE_FLAGS "-Wno-error") ENDIF () #Qml files will be probably moved to user's data dir, but for use #with QtCreator it is more convenient to have them here set(kstarsliteqml_SRCS kstarslite/qml/main.qml kstarslite/qml/constants/Constants.qml kstarslite/qml/modules/SkyMapLiteWrapper.qml kstarslite/qml/modules/BottomMenu.qml kstarslite/qml/modules/KSPage.qml kstarslite/qml/modules/KSListView.qml kstarslite/qml/modules/KSLabel.qml kstarslite/qml/modules/KSText.qml kstarslite/qml/modules/KSTabButton.qml kstarslite/qml/modules/KSTab.qml kstarslite/qml/modules/KSTabBarArrow.qml kstarslite/qml/modules/KSTextField.qml kstarslite/qml/modules/KSButton.qml kstarslite/qml/modules/TopMenu.qml kstarslite/qml/modules/helpers/TopMenuButton.qml kstarslite/qml/modules/helpers/BottomMenuButton.qml kstarslite/qml/modules/Splash.qml kstarslite/qml/modules/helpers/TimeSpinBox.qml kstarslite/qml/modules/TimePage.qml #Popups kstarslite/qml/modules/popups/ProjectionsPopup.qml kstarslite/qml/modules/popups/FOVPopup.qml kstarslite/qml/modules/popups/ColorSchemePopup.qml #Menus kstarslite/qml/modules/menus/ContextMenu.qml #Helpers kstarslite/qml/modules/helpers/PassiveNotification.qml kstarslite/qml/modules/helpers/KSMenuItem.qml kstarslite/qml/modules/helpers/TelescopeControl.qml #Dialogs kstarslite/qml/dialogs/FindDialog.qml kstarslite/qml/dialogs/LocationDialog.qml kstarslite/qml/dialogs/DetailsDialog.qml kstarslite/qml/dialogs/helpers/DetailsItem.qml kstarslite/qml/dialogs/helpers/DetailsAddLink.qml kstarslite/qml/dialogs/helpers/LocationEdit.qml kstarslite/qml/dialogs/helpers/LocationLoading.qml kstarslite/qml/dialogs/menus/DetailsLinkMenu.qml kstarslite/qml/dialogs/menus/LocationsGeoMenu.qml #INDI 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/KSLed.qml kstarslite/qml/indi/modules/Property.qml kstarslite/qml/indi/modules/KSComboBox.qml kstarslite/qml/indi/modules/KSButtonSwitch.qml kstarslite/qml/indi/modules/KSCheckBox.qml kstarslite/qml/indi/modules/KSINDIText.qml kstarslite/qml/indi/modules/KSINDITextField.qml kstarslite/qml/indi/modules/KSButtonsSwitchRow.qml #Tutorial kstarslite/qml/modules/tutorial/TutorialPopup.qml kstarslite/qml/modules/tutorial/TutorialExitPopup.qml kstarslite/qml/modules/tutorial/TutorialStep1.qml kstarslite/qml/modules/tutorial/TutorialStep2.qml kstarslite/qml/modules/tutorial/TutorialStep3.qml kstarslite/qml/modules/tutorial/TutorialStep4.qml kstarslite/qml/modules/tutorial/TutorialStep5.qml kstarslite/qml/modules/tutorial/TutorialPane.qml ) add_subdirectory(kstarslite/qml) ADD_CUSTOM_TARGET(kstarsliteqml SOURCES ${kstarsliteqml_SRCS}) if(ANDROID) add_subdirectory(kstarslite/res) endif(ANDROID) endif(BUILD_KSTARS_LITE) set(kstars_SRCS ${indi_SRCS} ${fits_SRCS} ${ekos_SRCS} ${onlineparser_SRCS} ${libkstarswidgets_SRCS} ${libkstarscomponents_SRCS} ${libkstarstools_SRCS} ${kstars_extra_SRCS} ${kstars_gl_SRCS} ${kstars_projection_SRCS} ${xplanet_SRCS} ${kstars_options_SRCS} ${kstars_skyobjects_SRCS} ${kstars_dialogs_SRCS} ${hips_SRCS} ${oal_SRCS} ${printing_SRCS} #KStars Lite ${kstarslite_SRCS} ${indi_lite_SRCS} # Generated files ${libkstarstools_ui_SRCS} ${libkstarswidgets_ui_SRCS} ) # Generate all the necessary QLoggingCategory files ecm_qt_declare_logging_category(kstars_SRCS HEADER kstars_debug.h IDENTIFIER KSTARS CATEGORY_NAME org.kde.kstars) ecm_qt_declare_logging_category(kstars_SRCS HEADER indi_debug.h IDENTIFIER KSTARS_INDI CATEGORY_NAME org.kde.kstars.indi) ecm_qt_declare_logging_category(kstars_SRCS HEADER fits_debug.h IDENTIFIER KSTARS_FITS CATEGORY_NAME org.kde.kstars.fits) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_debug.h IDENTIFIER KSTARS_EKOS CATEGORY_NAME org.kde.kstars.ekos) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_capture_debug.h IDENTIFIER KSTARS_EKOS_CAPTURE CATEGORY_NAME org.kde.kstars.ekos.capture) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_focus_debug.h IDENTIFIER KSTARS_EKOS_FOCUS CATEGORY_NAME org.kde.kstars.ekos.focus) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_align_debug.h IDENTIFIER KSTARS_EKOS_ALIGN CATEGORY_NAME org.kde.kstars.ekos.align) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_guide_debug.h IDENTIFIER KSTARS_EKOS_GUIDE CATEGORY_NAME org.kde.kstars.ekos.guide) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_mount_debug.h IDENTIFIER KSTARS_EKOS_MOUNT CATEGORY_NAME org.kde.kstars.ekos.mount) ecm_qt_declare_logging_category(kstars_SRCS HEADER ekos_scheduler_debug.h IDENTIFIER KSTARS_EKOS_SCHEDULER CATEGORY_NAME org.kde.kstars.ekos.scheduler) kconfig_add_kcfg_files(kstars_SRCS ${kstars_KCFG_SRCS}) IF (UNITY_BUILD) ENABLE_UNITY_BUILD(kstars kstars_SRCS 10 cpp) ENDIF () set(kstars_SRCS ${kstars_SRCS} ${fits2_SRCS} ${sep_SRCS} ${hips_manager_SRCS} ${kstarslite_libtess_SRC}) if(NOT BUILD_KSTARS_LITE) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.xml kstars.h KStars) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.SimClock.xml simclock.h SimClock) if (INDI_FOUND) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.INDI.xml indi/indidbus.h INDIDBus) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.xml ekos/ekosmanager.h EkosManager) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Capture.xml ekos/capture/capture.h Ekos::Capture) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Focus.xml ekos/focus/focus.h Ekos::Focus) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Guide.xml ekos/guide/guide.h Ekos::Guide) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Align.xml ekos/align/align.h Ekos::Align) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Mount.xml ekos/mount/mount.h Ekos::Mount) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Dome.xml ekos/auxiliary/dome.h Ekos::Dome) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Weather.xml ekos/auxiliary/weather.h Ekos::Weather) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.DustCap.xml ekos/auxiliary/dustcap.h Ekos::DustCap) qt5_add_dbus_adaptor(kstars_SRCS org.kde.kstars.Ekos.Scheduler.xml ekos/scheduler/scheduler.h Ekos::Scheduler) endif(INDI_FOUND) ki18n_wrap_ui(kstars_SRCS ${indiui_SRCS} ${ui_SRCS} ${fitsui_SRCS} ${ekosui_SRCS} ${xplanetui_SRCS} ${kstars_optionsui_SRCS} ${kstars_dialogsui_SRCS} ${printingui_SRCS} auxiliary/thumbnailpicker.ui auxiliary/thumbnaileditor.ui oal/observeradd.ui oal/equipmentwriter.ui oal/execute.ui hips/opships.ui hips/opshipsdisplay.ui hips/opshipscache.ui #skycomponents/notifyupdatesui.ui ) endif(NOT BUILD_KSTARS_LITE) add_library(KStarsLib STATIC ${kstars_SRCS}) if(BUILD_KSTARS_LITE) target_link_libraries(KStarsLib LibKSDataHandlers htmesh KF5::I18n KF5::Plotting KF5::ConfigGui Qt5::Gui Qt5::Sql Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Positioning Qt5::Concurrent ${ZLIB_LIBRARIES} ) if (ANDROID) target_link_libraries(KStarsLib Qt5::AndroidExtras) endif () else(BUILD_KSTARS_LITE) target_link_libraries(KStarsLib LibKSDataHandlers htmesh KF5::Crash KF5::I18n KF5::NewStuff KF5::KIOFileWidgets KF5::WidgetsAddons KF5::Plotting KF5::Notifications Qt5::Gui Qt5::PrintSupport Qt5::Sql Qt5::Svg Qt5::Qml Qt5::Quick Qt5::Network #Qt5::Positioning Qt5::Concurrent ${ZLIB_LIBRARIES} ) if (Qt5DataVisualization_FOUND) target_link_libraries(KStarsLib Qt5::DataVisualization) endif(Qt5DataVisualization_FOUND) if (KF5NotifyConfig_FOUND) target_link_libraries(KStarsLib KF5::NotifyConfig) endif(KF5NotifyConfig_FOUND) endif(BUILD_KSTARS_LITE) if(NOT WIN32) target_link_libraries(KStarsLib m) endif(NOT WIN32) if (CFITSIO_FOUND) target_include_directories(KStarsLib PUBLIC ${CFITSIO_INCLUDE_DIR}) target_link_libraries(KStarsLib ${CFITSIO_LIBRARIES}) endif(CFITSIO_FOUND) if(INDI_FOUND) if (NOT ANDROID) find_package(Nova REQUIRED) include_directories(${NOVA_INCLUDE_DIR}) endif () ## Support Multiple Platforms. All Require INDI ## WIN32 Desktop: Requires INDI Qt5 Client + GSL ## WIN32 Lite: Requires INDI Qt5 Client ## Linux + MacOS Desktop: Requires INDI Client + GSL ## Linux + MacOS Lite: Requires INDI Qt5 Client ## Android: Requires INDI Qt5 Client built for Android #if(BUILD_KSTARS_LITE) #target_link_libraries(KStarsLib ${CMAKE_THREAD_LIBS_INIT}) #else(BUILD_KSTARS_LITE) if(NOT BUILD_KSTARS_LITE) find_package(GSL REQUIRED) include_directories(${GSL_INCLUDE_DIRS}) target_link_libraries(KStarsLib ${GSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} KF5::Notifications) endif(NOT BUILD_KSTARS_LITE) if(WIN32 OR ANDROID) if(ANDROID) add_definitions(-DUSE_QT5_INDI) target_link_libraries(KStarsLib ${INDI_CLIENT_ANDROID_LIBRARIES} ${CFITSIO_LIBRARIES} ${LIBRAW_LIBRARIES}) else(ANDROID) target_link_libraries(KStarsLib ${INDI_CLIENT_LIBRARIES} ${NOVA_LIBRARIES}) endif(ANDROID) else(WIN32 OR ANDROID) if(BUILD_KSTARS_LITE) add_definitions(-DUSE_QT5_INDI) target_link_libraries(KStarsLib ${INDI_CLIENT_QT_LIBRARIES} ${NOVA_LIBRARIES} z) else(BUILD_KSTARS_LITE) target_link_libraries(KStarsLib ${INDI_CLIENT_LIBRARIES} ${NOVA_LIBRARIES} z) endif(BUILD_KSTARS_LITE) endif(WIN32 OR ANDROID) endif(INDI_FOUND) if(WCSLIB_FOUND) target_link_libraries(KStarsLib ${WCSLIB_LIBRARIES}) endif (WCSLIB_FOUND) if(LibRaw_FOUND) target_link_libraries(KStarsLib ${LibRaw_LIBRARIES}) endif (LibRaw_FOUND) #FIXME Enable OpenGL Later #if( OPENGL_FOUND ) # target_link_libraries(KStarsLib # ${OPENGL_LIBRARIES} # ${QT_QTOPENGL_LIBRARY} # ) #endif( OPENGL_FOUND ) set (KSTARS_APP_SRCS main.cpp ) if(NOT BUILD_KSTARS_LITE) # add icon to application sources ecm_add_app_icon(KSTARS_APP_SRCS ICONS ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-kstars.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-kstars.png ) qt5_add_resources(KSTARS_APP_SRCS data/kstars.qrc) endif(NOT BUILD_KSTARS_LITE) if (CMAKE_TOOLCHAIN_FILE STREQUAL ${CMAKE_SOURCE_DIR}/android/toolchain-android.cmake) add_library(kstars SHARED ${KSTARS_APP_SRCS}) add_dependencies(KStarsLib cfitsio indi raw) set(ANDROID_NDK $ENV{ANDROID_NDK}) # This hack is needed by a strange NDK bug. Some symbols are missing in locale.o (libc.a) at linking stage. But # if we force-link with a working libc.a from android-24, there will be duplicated symbols, some symbols will # conflict with libandroid_support.a what is also needed. # Workaround: Extract the needed locale.o from libc.a of android-24 and link KStars Lite with it. ADD_CUSTOM_TARGET(extract_locale_o COMMAND ar -xv ${ANDROID_NDK}/platforms/android-24/arch-arm/usr/lib/libc.a locale.o WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) ADD_DEPENDENCIES(kstars extract_locale_o) target_link_libraries(kstars KStarsLib ${CMAKE_BINARY_DIR}/locale.o -lgnustl_static) else () if(BUILD_KSTARS_LITE) add_executable(kstars_lite ${KSTARS_APP_SRCS}) target_link_libraries(kstars_lite KStarsLib) else() add_executable(kstars ${KSTARS_APP_SRCS}) target_link_libraries(kstars KStarsLib) endif() endif () if(NOT BUILD_KSTARS_LITE) install(TARGETS kstars ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install(PROGRAMS org.kde.kstars.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES kstars.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR}) install(FILES kstars.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR}) if(INDI_FOUND) #install(FILES ekos/mount/mountbox.qml DESTINATION ${KDE_INSTALL_DATADIR}/kstars/ekos/mount/qml) #install(DIRECTORY ekos/mount/ DESTINATION ${KDE_INSTALL_DATADIR}/kstars/ekos/mount/qml # FILES_MATCHING PATTERN "*.png") endif() elseif (NOT ANDROID) install(TARGETS kstars_lite ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) endif() diff --git a/kstars/auxiliary/dms.h b/kstars/auxiliary/dms.h index 541241642..37422d8f4 100644 --- a/kstars/auxiliary/dms.h +++ b/kstars/auxiliary/dms.h @@ -1,485 +1,485 @@ /*************************************************************************** dms.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "../nan.h" #include #include #include //#define COUNT_DMS_SINCOS_CALLS true //#define PROFILE_SINCOS true #ifdef PROFILE_SINCOS #include #endif /** @class dms * @short An angle, stored as degrees, but expressible in many ways. * @author Jason Harris * @version 1.0 * * dms encapsulates an angle. The angle is stored as a double, * equal to the value of the angle in degrees. Methods are available * for setting/getting the angle as a floating-point measured in * Degrees or Hours, or as integer triplets (degrees, arcminutes, * arcseconds or hours, minutes, seconds). There is also a method * to set the angle according to a radian value, and to return the * angle expressed in radians. Finally, a SinCos() method computes * the sin and cosine of the angle. */ class dms { public: /** Default constructor. */ dms() : D(NaN::d) #ifdef COUNT_DMS_SINCOS_CALLS , m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true) #endif { #ifdef COUNT_DMS_SINCOS_CALLS ++dms_constructor_calls; #endif } /** Empty virtual destructor */ - virtual ~dms() {} + virtual ~dms() = default; /** @short Set the floating-point value of the angle according to the four integer arguments. * @param d degree portion of angle (int). Defaults to zero. * @param m arcminute portion of angle (int). Defaults to zero. * @param s arcsecond portion of angle (int). Defaults to zero. * @param ms arcsecond portion of angle (int). Defaults to zero. */ explicit dms(const int &d, const int &m = 0, const int &s = 0, const int &ms = 0) #ifdef COUNT_DMS_SINCOS_CALLS : m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true) #endif { dms::setD(d, m, s, ms); #ifdef COUNT_DMS_SINCOS_CALLS ++dms_constructor_calls; #endif } /** @short Construct an angle from a double value. * * Creates an angle whose value in Degrees is equal to the argument. * @param x angle expressed as a floating-point number (in degrees) */ explicit dms(const double &x) : D(x) #ifdef COUNT_DMS_SINCOS_CALLS , m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true) #endif { #ifdef COUNT_DMS_SINCOS_CALLS ++dms_constructor_calls; #endif } /** @short Construct an angle from a string representation. * * Attempt to create the angle according to the string argument. If the string * cannot be parsed as an angle value, the angle is set to zero. * * @warning There is not an unambiguous notification that it failed to parse the string, * since the string could have been a valid representation of zero degrees. * If this is a concern, use the setFromString() function directly instead. * * @param s the string to parse as a dms value. * @param isDeg if true, value is in degrees; if false, value is in hours. * @sa setFromString() */ explicit dms(const QString &s, bool isDeg = true) #ifdef COUNT_DMS_SINCOS_CALLS : m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true) #endif { setFromString(s, isDeg); #ifdef COUNT_DMS_SINCOS_CALLS ++dms_constructor_calls; #endif } /** @return integer degrees portion of the angle */ inline int degree() const { if (std::isnan(D)) return 0; return int(D); } /** @return integer arcminutes portion of the angle. * @note an arcminute is 1/60 degree. */ int arcmin() const; /** @return integer arcseconds portion of the angle * @note an arcsecond is 1/60 arcmin, or 1/3600 degree. */ int arcsec() const; /** @return integer milliarcseconds portion of the angle * @note a milliarcsecond is 1/1000 arcsecond. */ int marcsec() const; /** @return angle in degrees expressed as a double. */ inline const double &Degrees() const { return D; } /** @return integer hours portion of the angle * @note an angle can be measured in degrees/arcminutes/arcseconds * or hours/minutes/seconds. An hour is equal to 15 degrees. */ inline int hour() const { return int(reduce().Degrees() / 15.0); } /** @return integer minutes portion of the angle * @note a minute is 1/60 hour (not the same as an arcminute) */ int minute() const; /** @return integer seconds portion of the angle * @note a second is 1/3600 hour (not the same as an arcsecond) */ int second() const; /** @return integer milliseconds portion of the angle * @note a millisecond is 1/1000 second (not the same as a milliarcsecond) */ int msecond() const; /** @return angle in hours expressed as a double. * @note an angle can be measured in degrees/arcminutes/arcseconds * or hours/minutes/seconds. An hour is equal to 15 degrees. */ inline double Hours() const { return reduce().Degrees() / 15.0; } /** Sets floating-point value of angle, in degrees. * @param x new angle (double) */ inline virtual void setD(const double &x) { #ifdef COUNT_DMS_SINCOS_CALLS m_sinDirty = m_cosDirty = true; #endif D = x; } /** @short Sets floating-point value of angle, in degrees. * * This is an overloaded member function; it behaves essentially * like the above function. The floating-point value of the angle * (D) is determined from the following formulae: * * \f$ fabs(D) = fabs(d) + \frac{(m + (s/60))}{60} \f$ * \f$ sgn(D) = sgn(d) \f$ * * @param d integer degrees portion of angle * @param m integer arcminutes portion of angle * @param s integer arcseconds portion of angle * @param ms integer arcseconds portion of angle */ virtual void setD(const int &d, const int &m, const int &s, const int &ms = 0); /** @short Sets floating-point value of angle, in hours. * * Converts argument from hours to degrees, then * sets floating-point value of angle, in degrees. * @param x new angle, in hours (double) * @sa setD() */ inline virtual void setH(const double &x) { dms::setD(x * 15.0); #ifdef COUNT_DMS_SINCOS_CALLS m_cosDirty = m_sinDirty = true; #endif } /** @short Sets floating-point value of angle, in hours. * * Converts argument values from hours to degrees, then * sets floating-point value of angle, in degrees. * This is an overloaded member function, provided for convenience. It * behaves essentially like the above function. * @param h integer hours portion of angle * @param m integer minutes portion of angle * @param s integer seconds portion of angle * @param ms integer milliseconds portion of angle * @sa setD() */ virtual void setH(const int &h, const int &m, const int &s, const int &ms = 0); /** @short Attempt to parse the string argument as a dms value, and set the dms object * accordingly. * @param s the string to be parsed as a dms value. The string can be an int or * floating-point value, or a triplet of values (d/h, m, s) separated by spaces or colons. * @param isDeg if true, the value is in degrees. Otherwise, it is in hours. * @return true if sting was parsed successfully. Otherwise, set the dms value * to 0.0 and return false. */ virtual bool setFromString(const QString &s, bool isDeg = true); /** @short Compute Sine and Cosine of the angle simultaneously. * On machines using glibc >= 2.1, calling SinCos() is somewhat faster * than calling sin() and cos() separately. * The values are returned through the arguments (passed by reference). * * @param s Sine of the angle * @param c Cosine of the angle * @sa sin() cos() */ inline void SinCos(double &s, double &c) const; /** @short Compute the Angle's Sine. * * @return the Sine of the angle. * @sa cos() */ double sin() const { #ifdef COUNT_DMS_SINCOS_CALLS if (!m_sinCosCalled) { m_sinCosCalled = true; ++dms_with_sincos_called; } if (m_sinDirty) m_sinDirty = false; else ++redundant_trig_function_calls; ++trig_function_calls; #endif #ifdef PROFILE_SINCOS std::clock_t start, stop; double s; start = std::clock(); s = ::sin(D * DegToRad); stop = std::clock(); seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC); return s; #else return ::sin(D * DegToRad); #endif } /** @short Compute the Angle's Cosine. * * @return the Cosine of the angle. * @sa sin() */ double cos() const { #ifdef COUNT_DMS_SINCOS_CALLS if (!m_sinCosCalled) { m_sinCosCalled = true; ++dms_with_sincos_called; } if (m_cosDirty) m_cosDirty = false; else ++redundant_trig_function_calls; ++trig_function_calls; #endif #ifdef PROFILE_SINCOS std::clock_t start, stop; double c; start = std::clock(); c = ::cos(D * DegToRad); stop = std::clock(); seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC); return c; #else return ::cos(D * DegToRad); #endif } /** @short Express the angle in radians. * @return the angle in radians (double) */ inline double radians() const { return D * DegToRad; } /** @short Set angle according to the argument, in radians. * * This function converts the argument to degrees, then sets the angle * with setD(). * @param a angle in radians */ inline virtual void setRadians(const double &Rad) { dms::setD(Rad / DegToRad); #ifdef COUNT_DMS_SINCOS_CALLS m_cosDirty = m_sinDirty = true; #endif } /** return the equivalent angle between 0 and 360 degrees. * @warning does not change the value of the parent angle itself. */ const dms reduce() const; /** * @short an enum defining standard angle ranges */ enum AngleRanges { ZERO_TO_2PI, MINUSPI_TO_PI }; /** * @short Reduce _this_ angle to the given range */ void reduceToRange(enum dms::AngleRanges range); /** @return a nicely-formatted string representation of the angle * in degrees, arcminutes, and arcseconds. * @param machineReadable uses a colon separator and produces +/-dd:mm:ss format instead */ const QString toDMSString(const bool forceSign = false, const bool machineReadable = false, const bool highPrecision=false) const; /** @return a nicely-formatted string representation of the angle * in hours, minutes, and seconds. * @param machineReadable uses a colon separator and produces hh:mm:ss format instead */ const QString toHMSString(const bool machineReadable = false, const bool highPrecision=false) const; /** PI is a const static member; it's public so that it can be used anywhere, * as long as dms.h is included. */ static constexpr double PI = { M_PI }; /** DegToRad is a const static member equal to the number of radians in * one degree (dms::PI/180.0). */ static constexpr double DegToRad = { M_PI / 180.0 }; /** @short Static function to create a DMS object from a QString. * * There are several ways to specify the angle: * @li Integer numbers ( 5 or -33 ) * @li Floating-point numbers ( 5.0 or -33.0 ) * @li colon-delimited integers ( 5:0:0 or -33:0:0 ) * @li colon-delimited with float seconds ( 5:0:0.0 or -33:0:0.0 ) * @li colon-delimited with float minutes ( 5:0.0 or -33:0.0 ) * @li space-delimited ( 5 0 0; -33 0 0 ) or ( 5 0.0 or -33 0.0 ) * @li space-delimited, with unit labels ( 5h 0m 0s or -33d 0m 0s ) * @param s the string to be parsed as an angle value * @param deg if true, s is expressed in degrees; if false, s is expressed in hours * @return a dms object whose value is parsed from the string argument */ static dms fromString(const QString &s, bool deg); inline dms operator-() { return dms(-D); } #ifdef COUNT_DMS_SINCOS_CALLS static long unsigned dms_constructor_calls; // counts number of DMS constructor calls static long unsigned dms_with_sincos_called; static long unsigned trig_function_calls; // total number of trig function calls static long unsigned redundant_trig_function_calls; // counts number of redundant trig function calls static double seconds_in_trig; // accumulates number of seconds spent in trig function calls #endif protected: double D; private: #ifdef COUNT_DMS_SINCOS_CALLS mutable bool m_sinDirty, m_cosDirty, m_sinCosCalled; #endif friend dms operator+(dms, dms); friend dms operator-(dms, dms); friend QDataStream &operator<<(QDataStream &out, const dms &d); friend QDataStream &operator>>(QDataStream &in, dms &d); }; /// Add two angles inline dms operator+(dms a, dms b) { return dms(a.D + b.D); } /// Subtract angles inline dms operator-(dms a, dms b) { return dms(a.D - b.D); } // Inline sincos inline void dms::SinCos(double &s, double &c) const { #ifdef PROFILE_SINCOS std::clock_t start, stop; start = std::clock(); #endif #ifdef __GLIBC__ #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 && !defined(__UCLIBC__)) //GNU version sincos(radians(), &s, &c); #else //For older GLIBC versions s = ::sin(radians()); c = ::cos(radians()); #endif #else //ANSI-compliant version s = ::sin(radians()); c = ::cos(radians()); #endif #ifdef PROFILE_SINCOS stop = std::clock(); seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC); #endif #ifdef COUNT_DMS_SINCOS_CALLS if (!m_sinCosCalled) { m_sinCosCalled = true; ++dms_with_sincos_called; } if (m_sinDirty) m_sinDirty = false; else ++redundant_trig_function_calls; if (m_cosDirty) m_cosDirty = false; else ++redundant_trig_function_calls; trig_function_calls += 2; #endif } /** Overloaded equality operator */ inline bool operator==(const dms &a1, const dms &a2) { return a1.Degrees() == a2.Degrees(); } diff --git a/kstars/auxiliary/filedownloader.cpp b/kstars/auxiliary/filedownloader.cpp index a595f72c3..141974292 100644 --- a/kstars/auxiliary/filedownloader.cpp +++ b/kstars/auxiliary/filedownloader.cpp @@ -1,240 +1,236 @@ /* KStars File Downloader * Copyright (C) 2016 Jasem Mutlaq Adapted from https://wiki.qt.io/Download_Data_from_URL This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "filedownloader.h" #ifndef KSTARS_LITE #include "kstars.h" #endif #include "kstars_debug.h" #include #include #include FileDownloader::FileDownloader(QObject *parent) : QObject(parent) { connect(&m_WebCtrl, SIGNAL(finished(QNetworkReply*)), this, SLOT(dataFinished(QNetworkReply*))); registerDataVerification([](const QByteArray &) { return true; }); registerFileVerification([](const QString &) { return true;}); } -FileDownloader::~FileDownloader() -{ -} - void FileDownloader::get(const QUrl &fileUrl) { QNetworkRequest request(fileUrl); m_DownloadedData.clear(); isCancelled = false; m_Reply = m_WebCtrl.get(request); connect(m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError())); connect(m_Reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64))); connect(m_Reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(setDownloadProgress(qint64,qint64))); connect(m_Reply, SIGNAL(readyRead()), this, SLOT(dataReady())); setDownloadProgress(0, 0); } void FileDownloader::post(const QUrl &fileUrl, QByteArray &data) { QNetworkRequest request(fileUrl); m_DownloadedData.clear(); isCancelled = false; m_Reply = m_WebCtrl.post(request, data); connect(m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError())); connect(m_Reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64))); connect(m_Reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(setDownloadProgress(qint64,qint64))); connect(m_Reply, SIGNAL(readyRead()), this, SLOT(dataReady())); setDownloadProgress(0, 0); } void FileDownloader::post(const QUrl &fileUrl, QHttpMultiPart *parts) { QNetworkRequest request(fileUrl); m_DownloadedData.clear(); isCancelled = false; m_Reply = m_WebCtrl.post(request, parts); connect(m_Reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(slotError())); connect(m_Reply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64))); connect(m_Reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(setDownloadProgress(qint64,qint64))); connect(m_Reply, SIGNAL(readyRead()), this, SLOT(dataReady())); setDownloadProgress(0, 0); } void FileDownloader::dataReady() { if (m_downloadTemporaryFile.isOpen()) m_downloadTemporaryFile.write(m_Reply->readAll()); else m_DownloadedData += m_Reply->readAll(); } void FileDownloader::dataFinished(QNetworkReply *pReply) { if (pReply->error() != QNetworkReply::NoError) return; dataReady(); if (m_verifyData(m_DownloadedData) == false) { emit error(i18n("Data verification failed!")); pReply->deleteLater(); return; } else if (m_downloadTemporaryFile.isOpen()) { m_downloadTemporaryFile.flush(); m_downloadTemporaryFile.close(); if (m_verifyFile(m_downloadTemporaryFile.fileName()) == false) { emit error(i18n("File verification failed!")); pReply->deleteLater(); return; } else { QFile::remove(m_DownloadedFileURL.toLocalFile()); m_downloadTemporaryFile.copy(m_DownloadedFileURL.toLocalFile()); } } if (isCancelled == false) emit downloaded(); pReply->deleteLater(); } void FileDownloader::slotError() { m_Reply->deleteLater(); #ifndef KSTARS_LITE if (progressDialog != nullptr) progressDialog->hide(); #endif if (isCancelled) { // Remove partially downloaded file, should we download to %tmp first? if (m_downloadTemporaryFile.isOpen()) { m_downloadTemporaryFile.close(); m_downloadTemporaryFile.remove(); } emit canceled(); } else { emit error(m_Reply->errorString()); } } void FileDownloader::setProgressDialogEnabled(bool ShowProgressDialog, const QString &textTitle, const QString &textLabel) { m_ShowProgressDialog = ShowProgressDialog; if (title.isEmpty()) title = i18n("Downloading"); else title = textTitle; if (textLabel.isEmpty()) label = i18n("Downloading Data..."); else label = textLabel; } QUrl FileDownloader::getDownloadedFileURL() const { return m_DownloadedFileURL; } bool FileDownloader::setDownloadedFileURL(const QUrl &DownloadedFile) { m_DownloadedFileURL = DownloadedFile; if (m_DownloadedFileURL.isEmpty() == false) { bool rc= m_downloadTemporaryFile.open(); if (rc == false) qCWarning(KSTARS) << m_downloadTemporaryFile.errorString(); else qCDebug(KSTARS) << "Opened" << m_downloadTemporaryFile.fileName() << "to download data into" << DownloadedFile.toLocalFile(); return rc; } return true; } void FileDownloader::setDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { #ifndef KSTARS_LITE if (m_ShowProgressDialog) { if (progressDialog == nullptr) { isCancelled = false; progressDialog = new QProgressDialog(KStars::Instance()); progressDialog->setWindowTitle(title); progressDialog->setLabelText(i18n("Awaiting response from server...")); connect(progressDialog, SIGNAL(canceled()), this, SIGNAL(canceled())); connect(progressDialog, &QProgressDialog::canceled, this, [&]() { isCancelled = true; m_Reply->abort(); progressDialog->close(); }); progressDialog->setMinimum(0); progressDialog->setMaximum(0); progressDialog->show(); progressDialog->raise(); } if (bytesReceived > 0) { progressDialog->setLabelText(label); } if (bytesTotal > 0) { progressDialog->setMaximum(bytesTotal); progressDialog->setValue(bytesReceived); } else { progressDialog->setMaximum(0); } } #else Q_UNUSED(bytesReceived); Q_UNUSED(bytesTotal); #endif } QByteArray FileDownloader::downloadedData() const { return m_DownloadedData; } diff --git a/kstars/auxiliary/filedownloader.h b/kstars/auxiliary/filedownloader.h index 7d490236d..0842897d8 100644 --- a/kstars/auxiliary/filedownloader.h +++ b/kstars/auxiliary/filedownloader.h @@ -1,87 +1,87 @@ /* KStars File Downloader * Copyright (C) 2016 Jasem Mutlaq Adapted from https://wiki.qt.io/Download_Data_from_URL This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include #include #include #include #include #include #include #include class QProgressDialog; class FileDownloader : public QObject { Q_OBJECT public: explicit FileDownloader(QObject *parent = nullptr); - ~FileDownloader() override; + ~FileDownloader() override = default; void get(const QUrl &fileUrl); void post(const QUrl &fileUrl, QByteArray &data); void post(const QUrl &fileUrl, QHttpMultiPart *parts); QByteArray downloadedData() const; QUrl getDownloadedFileURL() const; bool setDownloadedFileURL(const QUrl &DownloadedFile); void setProgressDialogEnabled(bool ShowProgressDialog, const QString &textTitle = QString(), const QString &textLabel = QString()); // Callbacks to verify data before being accepted void registerDataVerification(std::function verifyFunc) { m_verifyData = verifyFunc; } void registerFileVerification(std::function verifyFile){ m_verifyFile = verifyFile; } signals: void downloaded(); void canceled(); void error(const QString &errorString); void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); private slots: void dataFinished(QNetworkReply *pReply); void dataReady(); void slotError(); void setDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); private: QNetworkAccessManager m_WebCtrl; QByteArray m_DownloadedData; // Downloaded file QUrl m_DownloadedFileURL; // Temporary file used until download is successful QTemporaryFile m_downloadTemporaryFile; // Network reply QNetworkReply *m_Reply { nullptr }; // Optional Progress dialog bool m_ShowProgressDialog { false }; #ifndef KSTARS_LITE QProgressDialog *progressDialog { nullptr }; #endif bool isCancelled { false }; QString label; QString title; std::function m_verifyData; std::function m_verifyFile; }; diff --git a/kstars/auxiliary/fov.cpp b/kstars/auxiliary/fov.cpp index 86645f782..0b79f0a84 100644 --- a/kstars/auxiliary/fov.cpp +++ b/kstars/auxiliary/fov.cpp @@ -1,366 +1,362 @@ /*************************************************************************** fov.cpp - description ------------------- begin : Fri 05 Sept 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "fov.h" #include "Options.h" #ifndef KSTARS_LITE #include "kstars.h" #endif #include "kstarsdata.h" #include "geolocation.h" #include "skymap.h" #include "projections/projector.h" #include #include #include #include #include #include #include "kspaths.h" QList FOVManager::m_FOVs; -FOVManager::FOVManager() -{ -} - FOVManager::~FOVManager() { qDeleteAll(m_FOVs); } QList FOVManager::defaults() { QList fovs; fovs << new FOV(i18nc("use field-of-view for binoculars", "7x35 Binoculars"), 558, 558, 0, 0, 0, FOV::CIRCLE, "#AAAAAA") << new FOV(i18nc("use a Telrad field-of-view indicator", "Telrad"), 30, 30, 0, 0, 0, FOV::BULLSEYE, "#AA0000") << new FOV(i18nc("use 1-degree field-of-view indicator", "One Degree"), 60, 60, 0, 0, 0, FOV::CIRCLE, "#AAAAAA") << new FOV(i18nc("use HST field-of-view indicator", "HST WFPC2"), 2.4, 2.4, 0, 0, 0, FOV::SQUARE, "#AAAAAA") << new FOV(i18nc("use Radiotelescope HPBW", "30m at 1.3cm"), 1.79, 1.79, 0, 0, 0, FOV::SQUARE, "#AAAAAA"); return fovs; } bool FOVManager::save() { QFile f; // TODO: Move FOVs to user database instead of file!! f.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "fov.dat"); if (!f.open(QIODevice::WriteOnly)) { qDebug() << "Could not open fov.dat."; return false; } QTextStream ostream(&f); foreach (FOV *fov, m_FOVs) { ostream << fov->name() << ':' << fov->sizeX() << ':' << fov->sizeY() << ':' << fov->offsetX() << ':' << fov->offsetY() << ':' << fov->PA() << ':' << QString::number(fov->shape()) << ':' << fov->color() << ':' << (fov->lockCelestialPole() ? 1 : 0) << endl; } f.close(); return true; } const QList &FOVManager::readFOVs() { qDeleteAll(m_FOVs); m_FOVs.clear(); QFile f; f.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "fov.dat"); if (!f.exists()) { m_FOVs = defaults(); save(); return m_FOVs; } if (f.open(QIODevice::ReadOnly)) { QTextStream istream(&f); while (!istream.atEnd()) { QStringList fields = istream.readLine().split(':'); bool ok; QString name, color; float sizeX, sizeY, xoffset, yoffset, rot; bool lockedCP = false; FOV::Shape shape; if (fields.count() >= 8) { name = fields[0]; sizeX = fields[1].toFloat(&ok); if (!ok) { return m_FOVs; } sizeY = fields[2].toFloat(&ok); if (!ok) { return m_FOVs; } xoffset = fields[3].toFloat(&ok); if (!ok) { return m_FOVs; } yoffset = fields[4].toFloat(&ok); if (!ok) { return m_FOVs; } rot = fields[5].toFloat(&ok); if (!ok) { return m_FOVs; } shape = FOV::intToShape(fields[6].toInt(&ok)); if (!ok) { return m_FOVs; } color = fields[7]; if (fields.count() == 9) lockedCP = (fields[8].toInt(&ok) == 1); } else { continue; } m_FOVs.append(new FOV(name, sizeX, sizeY, xoffset, yoffset, rot, shape, color, lockedCP)); } } return m_FOVs; } void FOVManager::releaseCache() { qDeleteAll(m_FOVs); m_FOVs.clear(); } FOV::Shape FOV::intToShape(int s) { return (s >= FOV::UNKNOWN || s < 0) ? FOV::UNKNOWN : static_cast(s); } FOV::FOV(const QString &n, float a, float b, float xoffset, float yoffset, float rot, Shape sh, const QString &col, bool useLockedCP) { m_name = n; m_sizeX = a; m_sizeY = (b < 0.0) ? a : b; m_offsetX = xoffset; m_offsetY = yoffset; m_PA = rot; m_shape = sh; m_color = col; m_northPA = 0; m_center.setRA(0); m_center.setDec(0); m_imageDisplay = false; m_lockCelestialPole = useLockedCP; } FOV::FOV() { m_name = i18n("No FOV"); m_color = "#FFFFFF"; m_sizeX = m_sizeY = 0; m_shape = SQUARE; m_offsetX = m_offsetY = m_PA = 0, m_northPA = 0; m_imageDisplay = false; m_lockCelestialPole = false; } void FOV::draw(QPainter &p, float zoomFactor) { // Do not draw empty FOVs if (m_sizeX == 0 || m_sizeY == 0) return; p.setPen(QColor(color())); p.setBrush(Qt::NoBrush); p.setRenderHint(QPainter::Antialiasing, Options::useAntialias()); float pixelSizeX = sizeX() * zoomFactor / 57.3 / 60.0; float pixelSizeY = sizeY() * zoomFactor / 57.3 / 60.0; float offsetXPixelSize = offsetX() * zoomFactor / 57.3 / 60.0; float offsetYPixelSize = offsetY() * zoomFactor / 57.3 / 60.0; p.save(); if (m_center.ra().Degrees() > 0) { m_center.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF skypoint_center = KStars::Instance()->map()->projector()->toScreen(&m_center); p.translate(skypoint_center.toPoint()); } else p.translate(p.viewport().center()); p.translate(offsetXPixelSize, offsetYPixelSize); p.rotate( (m_PA - m_northPA) * -1); QPointF center(0, 0); switch (shape()) { case SQUARE: { QRect targetRect(center.x() - pixelSizeX / 2, center.y() - pixelSizeY / 2, pixelSizeX, pixelSizeY); if (m_imageDisplay) p.drawImage(targetRect, m_image); p.drawRect(targetRect); p.drawRect(center.x(), center.y() - (3 * pixelSizeY / 5), pixelSizeX / 40, pixelSizeX / 10); p.drawLine(center.x() - pixelSizeX / 30, center.y() - (3 * pixelSizeY / 5), center.x() + pixelSizeX / 20, center.y() - (3 * pixelSizeY / 5)); p.drawLine(center.x() - pixelSizeX / 30, center.y() - (3 * pixelSizeY / 5), center.x() + pixelSizeX / 70, center.y() - (0.7 * pixelSizeY)); p.drawLine(center.x() + pixelSizeX / 20, center.y() - (3 * pixelSizeY / 5), center.x() + pixelSizeX / 70, center.y() - (0.7 * pixelSizeY)); int fontSize = pixelSizeX / 15; if (fontSize <= 4) break; QFont font = p.font(); font.setPixelSize(fontSize); p.setFont(font); QRect nameRect(targetRect.topLeft().x(), targetRect.topLeft().y()-(pixelSizeY/8), targetRect.width()/2, pixelSizeX / 10); p.drawText(nameRect, Qt::AlignCenter, name()); QRect sizeRect(targetRect.center().x(), targetRect.topLeft().y()-(pixelSizeY/8), targetRect.width()/2, pixelSizeX / 10); p.drawText(sizeRect, Qt::AlignCenter, QString("%1'x%2'").arg(QString::number(m_sizeX, 'f', 1)).arg(QString::number(m_sizeY, 'f', 1))); } break; case CIRCLE: p.drawEllipse(center, pixelSizeX / 2, pixelSizeY / 2); break; case CROSSHAIRS: //Draw radial lines p.drawLine(center.x() + 0.5 * pixelSizeX, center.y(), center.x() + 1.5 * pixelSizeX, center.y()); p.drawLine(center.x() - 0.5 * pixelSizeX, center.y(), center.x() - 1.5 * pixelSizeX, center.y()); p.drawLine(center.x(), center.y() + 0.5 * pixelSizeY, center.x(), center.y() + 1.5 * pixelSizeY); p.drawLine(center.x(), center.y() - 0.5 * pixelSizeY, center.x(), center.y() - 1.5 * pixelSizeY); //Draw circles at 0.5 & 1 degrees p.drawEllipse(center, 0.5 * pixelSizeX, 0.5 * pixelSizeY); p.drawEllipse(center, pixelSizeX, pixelSizeY); break; case BULLSEYE: p.drawEllipse(center, 0.5 * pixelSizeX, 0.5 * pixelSizeY); p.drawEllipse(center, 2.0 * pixelSizeX, 2.0 * pixelSizeY); p.drawEllipse(center, 4.0 * pixelSizeX, 4.0 * pixelSizeY); break; case SOLIDCIRCLE: { QColor colorAlpha = color(); colorAlpha.setAlpha(127); p.setBrush(QBrush(colorAlpha)); p.drawEllipse(center, pixelSizeX / 2, pixelSizeY / 2); p.setBrush(Qt::NoBrush); break; } default:; } p.restore(); } void FOV::draw(QPainter &p, float x, float y) { float xfactor = x / sizeX() * 57.3 * 60.0; float yfactor = y / sizeY() * 57.3 * 60.0; float zoomFactor = std::min(xfactor, yfactor); switch (shape()) { case CROSSHAIRS: zoomFactor /= 3; break; case BULLSEYE: zoomFactor /= 8; break; default:; } draw(p, zoomFactor); } void FOV::setShape(int s) { m_shape = intToShape(s); } SkyPoint FOV::center() const { return m_center; } void FOV::setCenter(const SkyPoint ¢er) { m_center = center; } float FOV::northPA() const { return m_northPA; } void FOV::setNorthPA(float northPA) { m_northPA = northPA; } void FOV::setImage(const QImage &image) { m_image = image; } void FOV::setImageDisplay(bool value) { m_imageDisplay = value; } bool FOV::lockCelestialPole() const { return m_lockCelestialPole; } void FOV::setLockCelestialPole(bool lockCelestialPole) { m_lockCelestialPole = lockCelestialPole; } diff --git a/kstars/auxiliary/fov.h b/kstars/auxiliary/fov.h index a03283c81..d3c48d90a 100644 --- a/kstars/auxiliary/fov.h +++ b/kstars/auxiliary/fov.h @@ -1,161 +1,161 @@ /*************************************************************************** fov.h - description ------------------- begin : Fri 05 Sept 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "skypoint.h" #include #include #include class QPainter; /** * @class FOV * A simple class encapsulating a Field-of-View symbol * @author Jason Harris * @version 1.0 */ class FOV { public: enum Shape { SQUARE, CIRCLE, CROSSHAIRS, BULLSEYE, SOLIDCIRCLE, UNKNOWN }; static FOV::Shape intToShape(int); /** Default constructor */ FOV(); FOV(const QString &name, float a, float b = -1, float xoffset = 0, float yoffset = 0, float rot = 0, Shape shape = SQUARE, const QString &color = "#FFFFFF", bool useLockedCP = false); inline QString name() const { return m_name; } void setName(const QString &n) { m_name = n; } inline Shape shape() const { return m_shape; } void setShape(Shape s) { m_shape = s; } void setShape(int s); inline float sizeX() const { return m_sizeX; } inline float sizeY() const { return m_sizeY; } void setSize(float s) { m_sizeX = m_sizeY = s; } void setSize(float sx, float sy) { m_sizeX = sx; m_sizeY = sy; } void setOffset(float fx, float fy) { m_offsetX = fx; m_offsetY = fy; } inline float offsetX() const { return m_offsetX; } inline float offsetY() const { return m_offsetY; } void setPA(float rt) { m_PA = rt; } inline float PA() const { return m_PA; } inline QString color() const { return m_color; } void setColor(const QString &c) { m_color = c; } /** * @short draw the FOV symbol on a QPainter * @param p reference to the target QPainter. The painter should already be started. * @param zoomFactor is zoom factor as in SkyMap. */ void draw(QPainter &p, float zoomFactor); /** * @short draw FOV symbol so it will be inside a rectangle * @param p reference to the target QPainter. The painter should already be started. * @param x is X size of rectangle * @param y is Y size of rectangle */ void draw(QPainter &p, float x, float y); SkyPoint center() const; void setCenter(const SkyPoint ¢er); float northPA() const; void setNorthPA(float northPA); void setImage(const QImage &image); void setImageDisplay(bool value); bool lockCelestialPole() const; void setLockCelestialPole(bool lockCelestialPole); private: QString m_name, m_color; Shape m_shape; float m_sizeX { 0 }, m_sizeY { 0 }; float m_offsetX { 0 }, m_offsetY { 0 }; float m_PA { 0 }; float m_northPA { 0 }; SkyPoint m_center; QImage m_image; bool m_imageDisplay { false }; bool m_lockCelestialPole { false }; }; /** * @class FOVManager * A simple class handling FOVs. * @note Should migrate this from file (fov.dat) to using the user sqlite database * @author Jasem Mutlaq * @version 1.0 */ class FOVManager { public: /** @short Read list of FOVs from "fov.dat" */ static const QList &readFOVs(); /** @short Release the FOV cache */ static void releaseCache(); static void addFOV(FOV *newFOV) { Q_ASSERT(newFOV); m_FOVs.append(newFOV); } static void removeFOV(FOV *fov) { Q_ASSERT(fov); m_FOVs.removeOne(fov); } static const QList &getFOVs() { return m_FOVs; } /** @short Write list of FOVs to "fov.dat" */ static bool save(); private: - FOVManager(); + FOVManager() = default; ~FOVManager(); /** @short Fill list with default FOVs*/ static QList defaults(); static QList m_FOVs; }; diff --git a/kstars/auxiliary/imageviewer.cpp b/kstars/auxiliary/imageviewer.cpp index 112239ccd..a43e990be 100644 --- a/kstars/auxiliary/imageviewer.cpp +++ b/kstars/auxiliary/imageviewer.cpp @@ -1,379 +1,375 @@ /*************************************************************************** imageviewer.cpp - An ImageViewer for KStars ------------------- begin : Mon Aug 27 2001 copyright : (C) 2001 by Thomas Kabelmann email : tk78@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "imageviewer.h" #ifndef KSTARS_LITE #include "kstars.h" #endif #ifndef KSTARS_LITE #include #endif #include #include #include #include #include #include #include #include #include QUrl ImageViewer::lastURL = QUrl::fromLocalFile(QDir::homePath()); ImageLabel::ImageLabel(QWidget *parent) : QFrame(parent) { #ifndef KSTARS_LITE setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); setFrameStyle(QFrame::StyledPanel | QFrame::Plain); setLineWidth(2); #endif } -ImageLabel::~ImageLabel() -{ -} - void ImageLabel::setImage(const QImage &img) { #ifndef KSTARS_LITE m_Image = img; pix = QPixmap::fromImage(m_Image); #endif } void ImageLabel::invertPixels() { #ifndef KSTARS_LITE m_Image.invertPixels(); pix = QPixmap::fromImage(m_Image.scaled(width(), height(), Qt::KeepAspectRatio)); #endif } void ImageLabel::paintEvent(QPaintEvent *) { #ifndef KSTARS_LITE QPainter p; p.begin(this); int x = 0; if (pix.width() < width()) x = (width() - pix.width()) / 2; p.drawPixmap(x, 0, pix); p.end(); #endif } void ImageLabel::resizeEvent(QResizeEvent *event) { int w = pix.width(); int h = pix.height(); if (event->size().width() == w && event->size().height() == h) return; pix = QPixmap::fromImage(m_Image.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); } ImageViewer::ImageViewer(const QString &caption, QWidget *parent) : QDialog(parent), fileIsImage(false), downloadJob(nullptr) { #ifndef KSTARS_LITE init(caption, QString()); #endif } ImageViewer::ImageViewer(const QUrl &url, const QString &capText, QWidget *parent) : QDialog(parent), m_ImageUrl(url) { #ifndef KSTARS_LITE init(url.fileName(), capText); // check URL if (!m_ImageUrl.isValid()) qDebug() << "URL is malformed: " << m_ImageUrl; if (m_ImageUrl.isLocalFile()) { loadImage(m_ImageUrl.toLocalFile()); return; } { QTemporaryFile tempfile; tempfile.open(); file.setFileName(tempfile.fileName()); } // we just need the name and delete the tempfile from disc; if we don't do it, a dialog will be show loadImageFromURL(); #endif } void ImageViewer::init(QString caption, QString capText) { #ifndef KSTARS_LITE setAttribute(Qt::WA_DeleteOnClose, true); setModal(false); setWindowTitle(i18n("KStars image viewer: %1", caption)); // Create widget QFrame *page = new QFrame(this); //setMainWidget( page ); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(page); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QPushButton *invertB = new QPushButton(i18n("Invert colors")); invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects " "only the display and not the saving.")); QPushButton *saveB = new QPushButton(QIcon::fromTheme("document-save"), i18n("Save")); saveB->setToolTip(i18n("Save the image to disk")); buttonBox->addButton(invertB, QDialogButtonBox::ActionRole); buttonBox->addButton(saveB, QDialogButtonBox::ActionRole); connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors())); connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisc())); m_View = new ImageLabel(page); m_View->setAutoFillBackground(true); m_Caption = new QLabel(page); m_Caption->setAutoFillBackground(true); m_Caption->setFrameShape(QFrame::StyledPanel); m_Caption->setText(capText); // Add layout QVBoxLayout *vlay = new QVBoxLayout(page); vlay->setSpacing(0); vlay->setMargin(0); vlay->addWidget(m_View); vlay->addWidget(m_Caption); //Reverse colors QPalette p = palette(); p.setColor(QPalette::Window, palette().color(QPalette::WindowText)); p.setColor(QPalette::WindowText, palette().color(QPalette::Window)); m_Caption->setPalette(p); m_View->setPalette(p); //If the caption is wider than the image, try to shrink the font a bit QFont capFont = m_Caption->font(); capFont.setPointSize(capFont.pointSize() - 2); m_Caption->setFont(capFont); #endif } ImageViewer::~ImageViewer() { QString filename = file.fileName(); if (filename.startsWith(QLatin1String("/tmp/")) || filename.contains("/Temp")) { if (m_ImageUrl.isEmpty() == false || KMessageBox::questionYesNo(nullptr, i18n("Remove temporary file %1 from disk?", filename), i18n("Confirm Removal"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "imageviewer_temporary_file_removal") == KMessageBox::Yes) QFile::remove(filename); } QApplication::restoreOverrideCursor(); } void ImageViewer::loadImageFromURL() { #ifndef KSTARS_LITE QUrl saveURL = QUrl::fromLocalFile(file.fileName()); if (!saveURL.isValid()) qWarning() << "tempfile-URL is malformed"; QApplication::setOverrideCursor(Qt::WaitCursor); downloadJob.setProgressDialogEnabled(true, i18n("Download"), i18n("Please wait while image is being downloaded...")); connect(&downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); connect(&downloadJob, SIGNAL(canceled()), this, SLOT(close())); connect(&downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); downloadJob.get(m_ImageUrl); #endif } void ImageViewer::downloadReady() { #ifndef KSTARS_LITE QApplication::restoreOverrideCursor(); if (file.open(QFile::WriteOnly)) { file.write(downloadJob.downloadedData()); file.close(); // to get the newest information from the file and not any information from opening of the file if (file.exists()) { showImage(); return; } close(); } else KMessageBox::error(nullptr, file.errorString(), i18n("Image Viewer")); #endif } void ImageViewer::downloadError(const QString &errorString) { #ifndef KSTARS_LITE QApplication::restoreOverrideCursor(); KMessageBox::error(this, errorString); #endif } bool ImageViewer::loadImage(const QString &filename) { #ifndef KSTARS_LITE file.setFileName(filename); return showImage(); #else return false; #endif } bool ImageViewer::showImage() { #ifndef KSTARS_LITE QImage image; if (!image.load(file.fileName())) { QString text = i18n("Loading of the image %1 failed.", m_ImageUrl.url()); KMessageBox::error(this, text); close(); return false; } fileIsImage = true; // we loaded the file and know now, that it is an image //If the image is larger than screen width and/or screen height, //shrink it to fit the screen QRect deskRect = QApplication::desktop()->availableGeometry(); int w = deskRect.width(); // screen width int h = deskRect.height(); // screen height if (image.width() <= w && image.height() > h) //Window is taller than desktop image = image.scaled(int(image.width() * h / image.height()), h); else if (image.height() <= h && image.width() > w) //window is wider than desktop image = image.scaled(w, int(image.height() * w / image.width())); else if (image.width() > w && image.height() > h) //window is too tall and too wide { //which needs to be shrunk least, width or height? float fx = float(w) / float(image.width()); float fy = float(h) / float(image.height()); if (fx > fy) //width needs to be shrunk less, so shrink to fit in height image = image.scaled(int(image.width() * fy), h); else //vice versa image = image.scaled(w, int(image.height() * fx)); } show(); // hide is default m_View->setImage(image); w = image.width(); //If the caption is wider than the image, set the window size //to fit the caption if (m_Caption->width() > w) w = m_Caption->width(); //setFixedSize( w, image.height() + m_Caption->height() ); resize(w, image.height()); update(); show(); return true; #else return false; #endif } void ImageViewer::saveFileToDisc() { #ifndef KSTARS_LITE QFileDialog dialog; QUrl newURL = dialog.getSaveFileUrl(KStars::Instance(), i18n("Save Image"), lastURL); // save-dialog with default filename if (!newURL.isEmpty()) { //QFile f (newURL.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).toLocalFile() + '/' + newURL.fileName()); QFile f(newURL.toLocalFile()); if (f.exists()) { int r = KMessageBox::warningContinueCancel(static_cast(parent()), i18n("A file named \"%1\" already exists. " "Overwrite it?", newURL.fileName()), i18n("Overwrite File?"), KStandardGuiItem::overwrite()); if (r == KMessageBox::Cancel) return; f.remove(); } lastURL = QUrl(newURL.toString(QUrl::RemoveFilename)); saveFile(newURL); } #endif } void ImageViewer::saveFile(QUrl &url) { // synchronous access to prevent segfaults //if (!KIO::NetAccess::file_copy (QUrl (file.fileName()), url, (QWidget*) 0)) //QUrl tmpURL((file.fileName())); //tmpURL.setScheme("file"); if (file.copy(url.toLocalFile()) == false) //if (KIO::file_copy(tmpURL, url)->exec() == false) { QString text = i18n("Saving of the image %1 failed.", url.toString()); #ifndef KSTARS_LITE KMessageBox::error(this, text); #else qDebug() << text; #endif } #ifndef KSTARS_LITE else KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", url.toString())); #endif } void ImageViewer::invertColors() { #ifndef KSTARS_LITE // Invert colors m_View->invertPixels(); m_View->update(); #endif } diff --git a/kstars/auxiliary/imageviewer.h b/kstars/auxiliary/imageviewer.h index fd01f986c..25ff7678b 100644 --- a/kstars/auxiliary/imageviewer.h +++ b/kstars/auxiliary/imageviewer.h @@ -1,129 +1,129 @@ /*************************************************************************** imageviewer.h - An ImageViewer for KStars ------------------- begin : Mon Aug 27 2001 copyright : (C) 2001 by Thomas Kabelmann email : tk78@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "auxiliary/filedownloader.h" #include #include #include #include #include #include class QLabel; class ImageLabel : public QFrame { Q_OBJECT public: explicit ImageLabel(QWidget *parent); - ~ImageLabel() override; + ~ImageLabel() override = default; void setImage(const QImage &img); void invertPixels(); QImage m_Image; // ImageViewer needs access to the image in order to modify it protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *) override; private: QPixmap pix; }; /** * @class ImageViewer * @short Image viewer window for KStars * @author Thomas Kabelmann * @author Jasem Mutlaq * @version 1.1 * * This image-viewer automatically resizes the picture. The output * works with kio-slaves and not directly with the QImage save-routines * because normally the image-files are in GIF-format and QT does not * save these files. For other formats, like PNG, this is not so important * because they can directly saved by QImage. * * The download-slave works asynchron so the parent-widget can be used at * this time. The save-slave works synchronously, but this is not important * because the files are at this time local saved and this works not so long. */ class ImageViewer : public QDialog { Q_OBJECT public: /** Creates empty viewer. */ explicit ImageViewer(const QString &caption, QWidget *parent = nullptr); /** Create image viewer from URL with caption */ explicit ImageViewer(const QUrl &imageURL, const QString &capText = QString(), QWidget *parent = nullptr); /** Destructor. If there is a partially downloaded image file, delete it.*/ ~ImageViewer() override; /** * @brief loadImage Load image from local file and display it * @param filename path to local image * @return True if opened and displayed, false otherwise */ bool loadImage(const QString &filename); private: /** * Display the downloaded image. Resize the window to fit the image, If the image is * larger than the screen, make the image as large as possible while preserving the * original aspect ratio */ bool showImage(); /** Download the image file pointed to by the URL string. */ void loadImageFromURL(); /** Save the downloaded image to a local file. */ void saveFile(QUrl &url); QFile file; const QUrl m_ImageUrl; bool fileIsImage { false }; QString filename; FileDownloader downloadJob; ImageLabel *m_View { nullptr }; QLabel *m_Caption { nullptr }; // Share among image viewers static QUrl lastURL; private slots: /** Initialize (common part of onstructors) */ void init(QString caption, QString capText); /** Make sure download has finished, then make sure file exists, then display the image */ //void downloadReady (KJob *); void downloadReady(); void downloadError(const QString &errorString); /** Saves file to disc. */ void saveFileToDisc(); /** Inverts colors **/ void invertColors(); }; diff --git a/kstars/auxiliary/kswizard.cpp b/kstars/auxiliary/kswizard.cpp index 49b532b69..148547976 100644 --- a/kstars/auxiliary/kswizard.cpp +++ b/kstars/auxiliary/kswizard.cpp @@ -1,516 +1,511 @@ /*************************************************************************** kswizard.cpp - description ------------------- begin : Wed 28 Jan 2004 copyright : (C) 2004 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "kswizard.h" #include "geolocation.h" #include "kspaths.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include "Options.h" #include "widgets/dmsbox.h" #include #include #include #include #include #include #include #include namespace { bool hasPrefix(QString str, QString prefix) { if (prefix.isEmpty()) return true; return str.startsWith(prefix, Qt::CaseInsensitive); } } WizWelcomeUI::WizWelcomeUI(QWidget *parent) : QFrame(parent) { setupUi(this); } WizLocationUI::WizLocationUI(QWidget *parent) : QFrame(parent) { setupUi(this); } WizDownloadUI::WizDownloadUI(QWidget *parent) : QFrame(parent) { setupUi(this); } #ifdef Q_OS_OSX WizDataUI::WizDataUI(QWidget *parent) : QFrame(parent) { setupUi(this); } WizAstrometryUI::WizAstrometryUI(QWidget *parent) : QFrame(parent) { setupUi(this); } #endif KSWizard::KSWizard(QWidget *parent) : QDialog(parent) { #ifdef Q_OS_OSX setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); #endif wizardStack = new QStackedWidget(this); adjustSize(); setWindowTitle(i18n("Setup Wizard")); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(wizardStack); setLayout(mainLayout); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); nextB = new QPushButton(i18n("&Next >")); nextB->setDefault(true); backB = new QPushButton(i18n("< &Back")); backB->setEnabled(false); buttonBox->addButton(backB, QDialogButtonBox::ActionRole); buttonBox->addButton(nextB, QDialogButtonBox::ActionRole); mainLayout->addWidget(buttonBox); welcome = new WizWelcomeUI(wizardStack); #ifdef Q_OS_OSX data = new WizDataUI(wizardStack); astrometry = new WizAstrometryUI(wizardStack); #endif location = new WizLocationUI(wizardStack); WizDownloadUI *download = new WizDownloadUI(wizardStack); wizardStack->addWidget(welcome); #ifdef Q_OS_OSX wizardStack->addWidget(data); wizardStack->addWidget(astrometry); #endif wizardStack->addWidget(location); wizardStack->addWidget(download); wizardStack->setCurrentWidget(welcome); //Load images into banner frames. QPixmap im; if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzstars.png"))) welcome->Banner->setPixmap(im); else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath() + "/wzstars.png")) welcome->Banner->setPixmap(im); if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzgeo.png"))) location->Banner->setPixmap(im); else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath() + "/wzgeo.png")) location->Banner->setPixmap(im); if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzdownload.png"))) download->Banner->setPixmap(im); else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath() + "/wzdownload.png")) download->Banner->setPixmap(im); #ifdef Q_OS_OSX if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzdownload.png"))) data->Banner->setPixmap(im); else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath() + "/wzdownload.png")) data->Banner->setPixmap(im); if (im.load(KSPaths::locate(QStandardPaths::GenericDataLocation, "wzdownload.png"))) astrometry->Banner->setPixmap(im); else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath() + "/wzdownload.png")) astrometry->Banner->setPixmap(im); data->dataPath->setText( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(), QStandardPaths::LocateDirectory) + "kstars"); slotUpdateDataButtons(); astrometry->astrometryPath->setText( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(), QStandardPaths::LocateDirectory) + "Astrometry"); updateAstrometryButtons(); connect(data->copyKStarsData, SIGNAL(clicked()), this, SLOT(slotOpenOrCopyKStarsDataDirectory())); connect(astrometry->astrometryButton, SIGNAL(clicked()), this, SLOT(slotOpenOrCreateAstrometryFolder())); connect(data->installGSC, SIGNAL(clicked()), this, SLOT(slotInstallGSC())); connect(this, SIGNAL(accepted()), this, SLOT(slotFinishWizard())); gscMonitor = new QProgressIndicator(data); data->GSCLayout->addWidget(gscMonitor); data->downloadProgress->setValue(0); data->downloadProgress->setEnabled(false); data->downloadProgress->setVisible(false); #endif //connect signals/slots connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage())); connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(location->CityListBox, SIGNAL(itemSelectionChanged()), this, SLOT(slotChangeCity())); connect(location->CityFilter, SIGNAL(textChanged(QString)), this, SLOT(slotFilterCities())); connect(location->ProvinceFilter, SIGNAL(textChanged(QString)), this, SLOT(slotFilterCities())); connect(location->CountryFilter, SIGNAL(textChanged(QString)), this, SLOT(slotFilterCities())); connect(download->DownloadButton, SIGNAL(clicked()), this, SLOT(slotDownload())); //Initialize Geographic Location page if (KStars::Instance()) initGeoPage(); } -//Do NOT delete members of filteredCityList! They are not created by KSWizard. -KSWizard::~KSWizard() -{ -} - void KSWizard::setButtonsEnabled() { nextB->setEnabled(wizardStack->currentIndex() < wizardStack->count() - 1); backB->setEnabled(wizardStack->currentIndex() > 0); #ifdef Q_OS_OSX buttonBox->button(QDialogButtonBox::Ok)->setEnabled(dataDirExists()); if ((wizardStack->currentWidget() == data) && (!dataDirExists())) { nextB->setEnabled(false); } if (wizardStack->currentWidget() == location) { if (KStars::Instance()) { if (location->LongBox->isReadOnly() == false) { initGeoPage(); } } } #endif } void KSWizard::slotNextPage() { wizardStack->setCurrentIndex(wizardStack->currentIndex() + 1); setButtonsEnabled(); } void KSWizard::slotPrevPage() { wizardStack->setCurrentIndex(wizardStack->currentIndex() - 1); setButtonsEnabled(); } void KSWizard::initGeoPage() { KStarsData *data = KStarsData::Instance(); location->LongBox->setReadOnly(true); location->LatBox->setReadOnly(true); //Populate the CityListBox //flag the ID of the current City foreach (GeoLocation *loc, data->getGeoList()) { location->CityListBox->addItem(loc->fullName()); filteredCityList.append(loc); if (loc->fullName() == data->geo()->fullName()) { Geo = loc; } } //Sort alphabetically location->CityListBox->sortItems(); //preset to current city QList locations = location->CityListBox->findItems(QString(data->geo()->fullName()), Qt::MatchExactly); if (locations.isEmpty() == false) location->CityListBox->setCurrentItem(locations[0]); } void KSWizard::slotChangeCity() { if (location->CityListBox->currentItem()) { for (int i = 0; i < filteredCityList.size(); ++i) { if (filteredCityList[i]->fullName() == location->CityListBox->currentItem()->text()) { Geo = filteredCityList[i]; break; } } location->LongBox->showInDegrees(Geo->lng()); location->LatBox->showInDegrees(Geo->lat()); } } void KSWizard::slotFilterCities() { location->CityListBox->clear(); //Do NOT delete members of filteredCityList! filteredCityList.clear(); foreach (GeoLocation *loc, KStarsData::Instance()->getGeoList()) { if (hasPrefix(loc->translatedName(), location->CityFilter->text()) && hasPrefix(loc->translatedCountry(), location->CountryFilter->text()) && hasPrefix(loc->translatedProvince(), location->ProvinceFilter->text())) { location->CityListBox->addItem(loc->fullName()); filteredCityList.append(loc); } } location->CityListBox->sortItems(); if (location->CityListBox->count() > 0) // set first item in list as selected location->CityListBox->setCurrentItem(location->CityListBox->item(0)); } void KSWizard::slotDownload() { KNS3::DownloadDialog dlg; dlg.exec(); } void KSWizard::slotFinishWizard() { Options::setRunStartupWizard(false); if (KStars::Instance() && geo()) KStars::Instance()->updateLocationFromWizard(*(geo())); delete this; } void KSWizard::slotOpenOrCopyKStarsDataDirectory() { #ifdef Q_OS_OSX QString dataLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); if (dataLocation.isEmpty()) { QDir dataSourceDir = QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath(); if (! dataSourceDir.exists()) //If there is no default data directory in the app bundle { KMessageBox::sorry(0, i18n("Error! There was no default data directory found in the app bundle!")); return; } QDir writableDir; writableDir.mkdir(KSPaths::writableLocation(QStandardPaths::GenericDataLocation)); dataLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); if (dataLocation.isEmpty()) //If there *still* is not a kstars data directory { KMessageBox::sorry( 0, i18n("Error! There was a problem creating the data directory ~/Library/Application Support/ !")); return; } KSUtils::copyRecursively(dataSourceDir.absolutePath(), dataLocation); //This will update the next, ok, and copy kstars dir buttons. slotUpdateDataButtons(); //This will let the program load after the data folder is copied hide(); setModal(false); setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); show(); } else { QUrl path = QUrl::fromLocalFile( QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory)); QDesktopServices::openUrl(path); } #endif } void KSWizard::slotOpenOrCreateAstrometryFolder() { #ifdef Q_OS_OSX QString astrometryLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "Astrometry", QStandardPaths::LocateDirectory); if (astrometryLocation.isEmpty()) { KSUtils::configureDefaultAstrometry(); updateAstrometryButtons(); } else { QUrl path = QUrl::fromLocalFile( QStandardPaths::locate(QStandardPaths::GenericDataLocation, "Astrometry", QStandardPaths::LocateDirectory)); QDesktopServices::openUrl(path); } #endif } void KSWizard::slotInstallGSC() { #ifdef Q_OS_OSX QString location = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); gscZipPath = location + "/gsc.zip"; QProcess *downloadGSC = new QProcess(); downloadGSC->setWorkingDirectory(location); connect(downloadGSC, SIGNAL(finished(int)), this, SLOT(slotExtractGSC())); connect(downloadGSC, SIGNAL(finished(int)), this, SLOT(downloadGSC.deleteLater())); downloadGSC->start("curl", QStringList() << "-L" << "-o" << "gsc.zip" << "http://www.indilib.org/jdownloads/Mac/gsc.zip"); data->GSCFeedback->setText("downloading GSC . . ."); downloadMonitor = new QTimer(this); connect(downloadMonitor, SIGNAL(timeout()), this, SLOT(slotCheckDownloadProgress())); downloadMonitor->start(1000); gscMonitor->startAnimation(); data->downloadProgress->setVisible(true); data->downloadProgress->setEnabled(true); data->downloadProgress->setMaximum(240); data->downloadProgress->setValue(0); #endif } void KSWizard::slotExtractGSC() { #ifdef Q_OS_OSX QString location = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); QProcess *gscExtractor = new QProcess(); connect(gscExtractor, SIGNAL(finished(int)), this, SLOT(slotGSCInstallerFinished())); connect(gscExtractor, SIGNAL(finished(int)), this, SLOT(gscExtractor.deleteLater())); gscExtractor->setWorkingDirectory(location); gscExtractor->start("unzip", QStringList() << "-ao" << "gsc.zip"); gscMonitor->startAnimation(); #endif } void KSWizard::slotCheckDownloadProgress() { #ifdef Q_OS_OSX if (QFileInfo(gscZipPath).exists()) data->downloadProgress->setValue(QFileInfo(gscZipPath).size() / 1048576); #endif } void KSWizard::slotGSCInstallerFinished() { #ifdef Q_OS_OSX if (downloadMonitor) { downloadMonitor->stop(); delete downloadMonitor; } data->downloadProgress->setEnabled(false); data->downloadProgress->setValue(0); data->downloadProgress->setVisible(false); gscMonitor->stopAnimation(); slotUpdateDataButtons(); if (QFile(gscZipPath).exists()) QFile(gscZipPath).remove(); #endif } #ifdef Q_OS_OSX bool KSWizard::dataDirExists() { QString dataLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); return !dataLocation.isEmpty(); } bool KSWizard::astrometryDirExists() { QString astrometryLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "Astrometry", QStandardPaths::LocateDirectory); return !astrometryLocation.isEmpty(); } bool KSWizard::pythonExists() { return QFileInfo("/usr/local/bin/python").exists()|| QFileInfo("/usr/bin/python").exists()|| QFileInfo(QCoreApplication::applicationDirPath() + "/python/bin/python").exists(); } bool KSWizard::GSCExists() { QString GSCLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars/gsc", QStandardPaths::LocateDirectory); return !GSCLocation.isEmpty(); } bool KSWizard::pyfitsExists() { return QFileInfo("/usr/local/lib/python2.7/site-packages/pyfits").exists()|| QFileInfo("/usr/lib/python2.7/site-packages/pyfits").exists()|| QFileInfo(QCoreApplication::applicationDirPath() + "/python/bin/site-packages/pyfits").exists(); } bool KSWizard::netpbmExists() { return QFileInfo("/usr/local/bin/jpegtopnm").exists()|| QFileInfo("/usr/bin/jpegtopnm").exists()|| QFileInfo(QCoreApplication::applicationDirPath() + "/netpbm/bin/jpegtopnm").exists(); } void KSWizard::updateAstrometryButtons() { astrometry->astrometryFound->setChecked(astrometryDirExists()); astrometry->pythonFound->setChecked(pythonExists()); astrometry->pyfitsFound->setChecked(pyfitsExists()); astrometry->netpbmFound->setChecked(netpbmExists()); } #endif void KSWizard::slotUpdateDataButtons() { #ifdef Q_OS_OSX data->dataDirFound->setChecked(dataDirExists()); if (dataDirExists()) { data->copyKStarsData->setText("Open KStars Data Directory"); data->foundFeedback1->setText("The KStars Data Directory called kstars is located at:"); data->foundFeedback2->setText("Your data directory was found. If you have any problems with it, you can " "always delete this data directory and KStars will give you a new data " "directory. You can click this button to open the data directory, just be " "careful not to delete any important files."); } else { data->foundFeedback1->setText("The KStars Data Directory called kstars should be located at:"); data->foundFeedback2->setText("

Your data directory was not found. You can click the " "button below to copy a default KStars data directory to the correct location, " "or if you have a KStars directory already some place else, you can exit KStars " "and copy it to that location yourself.

"); } bool ifGSCExists = GSCExists(); data->GSCFound->setChecked(ifGSCExists); data->installGSC->setDisabled(ifGSCExists || !dataDirExists()); if (ifGSCExists) data->GSCFeedback->setText("GSC was found on your system. To use it, just take an image in the CCD simulator. " "To uninstall, just delete the gsc folder from your data directory above."); setButtonsEnabled(); #endif } diff --git a/kstars/auxiliary/kswizard.h b/kstars/auxiliary/kswizard.h index 66ea20d6e..88cc8bfff 100644 --- a/kstars/auxiliary/kswizard.h +++ b/kstars/auxiliary/kswizard.h @@ -1,174 +1,175 @@ /*************************************************************************** kswizard.h - description ------------------- begin : Wed 28 Jan 2004 copyright : (C) 2004 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include #include #include #include #include #include "ui_wizwelcome.h" #include "ui_wizlocation.h" #include "ui_wizdownload.h" #include "ui_wizdata.h" #include "ui_wizastrometry.h" #include "QProgressIndicator.h" class GeoLocation; class QStackedWidget; class WizWelcomeUI : public QFrame, public Ui::WizWelcome { Q_OBJECT public: explicit WizWelcomeUI(QWidget *parent = nullptr); }; class WizLocationUI : public QFrame, public Ui::WizLocation { Q_OBJECT public: explicit WizLocationUI(QWidget *parent = nullptr); }; class WizDataUI : public QFrame, public Ui::WizData { Q_OBJECT public: explicit WizDataUI(QWidget *parent = nullptr); }; class WizAstrometryUI : public QFrame, public Ui::WizAstrometry { Q_OBJECT public: explicit WizAstrometryUI(QWidget *parent = nullptr); }; class WizDownloadUI : public QFrame, public Ui::WizDownload { Q_OBJECT public: explicit WizDownloadUI(QWidget *parent = nullptr); }; /** * @class KSWizard * The Setup Wizard will be automatically opened when KStars runs * for the first time. It allows the user to set up some basic parameters: * @li Geographic Location * @li Download extra data files * * @author Jason Harris * @version 1.0 */ class KSWizard : public QDialog { Q_OBJECT public: /** * Constructor * @param parent Pointer to the parent widget */ explicit KSWizard(QWidget *parent = nullptr); - ~KSWizard() override; + // Do NOT delete members of filteredCityList! They are not created by KSWizard. + ~KSWizard() override = default; /** @return pointer to the geographic location selected by the user */ const GeoLocation *geo() const { return Geo; } private slots: void slotNextPage(); void slotPrevPage(); /** * Set the geo pointer to the user's selected city, and display * its longitude and latitude in the window. * @note called when the highlighted city in the list box changes */ void slotChangeCity(); /** * Display only those cities which meet the user's search criteria in the city list box. * @note called when one of the name filters is modified */ void slotFilterCities(); void slotDownload(); void slotFinishWizard(); void slotOpenOrCreateAstrometryFolder(); void slotInstallGSC(); void slotExtractGSC(); void slotCheckDownloadProgress(); void slotGSCInstallerFinished(); void slotUpdateDataButtons(); void slotOpenOrCopyKStarsDataDirectory(); private: /** * @short Initialize the geographic location page. * Populate the city list box, and highlight the current location in the list. */ void initGeoPage(); /** @short set enabled/disable state of Next/Prev buttins based on current page */ void setButtonsEnabled(); #ifdef Q_OS_OSX bool pythonExists(); bool GSCExists(); bool pyfitsExists(); bool netpbmExists(); bool dataDirExists(); bool astrometryDirExists(); void updateAstrometryButtons(); QProgressIndicator *gscMonitor { nullptr }; QTimer *downloadMonitor { nullptr }; QString gscZipPath; #endif QStackedWidget *wizardStack { nullptr }; WizWelcomeUI *welcome { nullptr }; WizLocationUI *location { nullptr }; WizDataUI *data { nullptr }; WizAstrometryUI *astrometry { nullptr }; QPushButton *nextB { nullptr }; QPushButton *backB { nullptr }; QDialogButtonBox *buttonBox { nullptr }; GeoLocation *Geo { nullptr }; QList filteredCityList; }; diff --git a/kstars/auxiliary/profileinfo.h b/kstars/auxiliary/profileinfo.h index 1cfdf688d..7b52f06c1 100644 --- a/kstars/auxiliary/profileinfo.h +++ b/kstars/auxiliary/profileinfo.h @@ -1,54 +1,54 @@ /* Profile Info Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include #include class ProfileInfo { public: ProfileInfo(int id, const QString &name); - ~ProfileInfo() {} + ~ProfileInfo() = default; // Is connection local or remote bool isLocal() { return host.isEmpty(); } QString mount(); QString ccd(); QString guider(); QString focuser(); QString filter(); QString dome(); QString ao(); QString weather(); QString aux1(); QString aux2(); QString aux3(); QString aux4(); QString name; QString host; QString city; QString province; QString country; int guidertype { 0 }; int guiderport { 0 }; int primaryscope { 0 }; int guidescope { 0 }; QString guiderhost; int id { 0 }; int port { -1 }; bool autoConnect { false }; int INDIWebManagerPort { -1 }; // driver[role] = label QMap drivers; }; diff --git a/kstars/auxiliary/schememanager.cpp b/kstars/auxiliary/schememanager.cpp index 8d39ddace..7a8e47aae 100644 --- a/kstars/auxiliary/schememanager.cpp +++ b/kstars/auxiliary/schememanager.cpp @@ -1,1134 +1,1127 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-08-02 * Description : colors scheme manager * * Copyright (C) 2006-2018 by Gilles Caulier * Copyright (C) 2007 by Matthew Woehlke * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "schememanager.h" -// C++ includes - -#include - -// Qt includes +#include +#include #include #include -// KDE includes - -#include -#include +#include class HCYColorSpace { public: explicit HCYColorSpace(const QColor&); explicit HCYColorSpace(qreal h_, qreal c_, qreal y_, qreal a_ = 1.0); QColor qColor() const; static qreal luma(const QColor &); public: qreal h; qreal c; qreal y; qreal a; private: static qreal gamma(qreal); static qreal igamma(qreal); static qreal lumag(qreal, qreal, qreal); }; // ------------------------------------------------------------------------------ namespace ColorTools { static inline qreal wrap(qreal a, qreal d = 1.0) { qreal r = fmod(a, d); return (r < 0.0 ? d + r : (r > 0.0 ? r : 0.0)); } /** * normalize: like qBound(a, 0.0, 1.0) but without needing the args and with * "safer" behavior on NaN (isnan(a) -> return 0.0) */ static inline qreal normalize(qreal a) { return (a < 1.0 ? (a > 0.0 ? a : 0.0) : 1.0); } static inline qreal mixQreal(qreal a, qreal b, qreal bias) { return (a + (b - a) * bias); } /** * Calculate the luma of a color. Luma is weighted sum of gamma-adjusted * R'G'B' components of a color. The result is similar to qGray. The range * is from 0.0 (black) to 1.0 (white). * */ qreal luma(const QColor& color) { return HCYColorSpace::luma(color); } /** * Calculate hue, chroma and luma of a color in one call. */ void getHcy(const QColor& color, qreal* h, qreal* c, qreal* y, qreal* a = 0) { if (!c || !h || !y) { return; } HCYColorSpace khcy(color); *c = khcy.c; *h = khcy.h; *y = khcy.y; if (a) { *a = khcy.a; } } /** * Calculate the contrast ratio between two colors, according to the * W3C/WCAG2.0 algorithm, (Lmax + 0.05)/(Lmin + 0.05), where Lmax and Lmin * are the luma values of the lighter color and the darker color, * respectively. * * A contrast ration of 5:1 (result == 5.0) is the minimum for "normal" * text to be considered readable (large text can go as low as 3:1). The * ratio ranges from 1:1 (result == 1.0) to 21:1 (result == 21.0). */ static qreal contrastRatioForLuma(qreal y1, qreal y2) { if (y1 > y2) { return (y1 + 0.05) / (y2 + 0.05); } return (y2 + 0.05) / (y1 + 0.05); } qreal contrastRatio(const QColor& c1, const QColor& c2) { return contrastRatioForLuma(luma(c1), luma(c2)); } /** * Adjust the luma of a color by changing its distance from white. */ QColor lighten(const QColor& color, qreal ky = 0.5, qreal kc = 1.0) { HCYColorSpace c(color); c.y = 1.0 - ColorTools::normalize((1.0 - c.y) * (1.0 - ky)); c.c = 1.0 - ColorTools::normalize((1.0 - c.c) * kc); return c.qColor(); } /** * Adjust the luma of a color by changing its distance from black. */ QColor darken(const QColor& color, qreal ky = 0.5, qreal kc = 1.0) { HCYColorSpace c(color); c.y = ColorTools::normalize(c.y * (1.0 - ky)); c.c = ColorTools::normalize(c.c * kc); return c.qColor(); } /** * Adjust the luma and chroma components of a color. The amount is added * to the corresponding component. */ QColor shade(const QColor& color, qreal ky, qreal kc = 0.0) { HCYColorSpace c(color); c.y = ColorTools::normalize(c.y + ky); c.c = ColorTools::normalize(c.c + kc); return c.qColor(); } /** * Blend two colors into a new color by linear combination. */ QColor mix(const QColor& c1, const QColor& c2, qreal bias) { if (bias <= 0.0) { return c1; } if (bias >= 1.0) { return c2; } if (qIsNaN(bias)) { return c1; } qreal r = mixQreal(c1.redF(), c2.redF(), bias); qreal g = mixQreal(c1.greenF(), c2.greenF(), bias); qreal b = mixQreal(c1.blueF(), c2.blueF(), bias); qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); return QColor::fromRgbF(r, g, b, a); } static QColor tintHelper(const QColor& base, qreal baseLuma, const QColor& color, qreal amount) { HCYColorSpace result(mix(base, color, pow(amount, 0.3))); result.y = mixQreal(baseLuma, result.y, amount); return result.qColor(); } /** * Create a new color by tinting one color with another. This function is * meant for creating additional colors withings the same class (background, * foreground) from colors in a different class. Therefore when @p amount * is low, the luma of @p base is mostly preserved, while the hue and * chroma of @p color is mostly inherited. * * @param base color to be tinted * @param color color with which to tint * @param amount how strongly to tint the base; 0.0 gives @p base, * 1.0 gives @p color */ QColor tint(const QColor& base, const QColor& color, qreal amount = 0.3) { if (amount <= 0.0) { return base; } if (amount >= 1.0) { return color; } if (qIsNaN(amount)) { return base; } qreal baseLuma = luma(base); //cache value because luma call is expensive double ri = contrastRatioForLuma(baseLuma, luma(color)); double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); double u = 1.0, l = 0.0; QColor result; for (int i = 12; i; --i) { double a = 0.5 * (l + u); result = tintHelper(base, baseLuma, color, a); double ra = contrastRatioForLuma(baseLuma, luma(result)); if (ra > rg) { u = a; } else { l = a; } } return result; } /** * Blend two colors into a new color by painting the second color over the * first using the specified composition mode. * * @param base the base color (alpha channel is ignored). * @param paint the color to be overlayed onto the base color. * @param comp the CompositionMode used to do the blending. */ QColor overlayColors(const QColor& base, const QColor& paint, QPainter::CompositionMode comp) { // This isn't the fastest way, but should be "fast enough". // It's also the only safe way to use QPainter::CompositionMode QImage img(1, 1, QImage::Format_ARGB32_Premultiplied); QPainter p(&img); QColor start = base; start.setAlpha(255); // opaque p.fillRect(0, 0, 1, 1, start); p.setCompositionMode(comp); p.fillRect(0, 0, 1, 1, paint); p.end(); return img.pixel(0, 0); } } // namespace ColorTools // ------------------------------------------------------------------------------ #define HCY_REC 709 // use 709 for now #if HCY_REC == 601 static const qreal yc[3] = {0.299, 0.587, 0.114 }; #elif HCY_REC == 709 static const qreal yc[3] = {0.2126, 0.7152, 0.0722 }; #else // use Qt values static const qreal yc[3] = {0.34375, 0.5, 0.15625}; #endif qreal HCYColorSpace::gamma(qreal n) { return pow(ColorTools::normalize(n), 2.2); } qreal HCYColorSpace::igamma(qreal n) { return pow(ColorTools::normalize(n), 1.0 / 2.2); } qreal HCYColorSpace::lumag(qreal r, qreal g, qreal b) { return r * yc[0] + g * yc[1] + b * yc[2]; } HCYColorSpace::HCYColorSpace(qreal h_, qreal c_, qreal y_, qreal a_) { h = h_; c = c_; y = y_; a = a_; } HCYColorSpace::HCYColorSpace(const QColor& color) { qreal r = gamma(color.redF()); qreal g = gamma(color.greenF()); qreal b = gamma(color.blueF()); a = color.alphaF(); // luma component y = lumag(r, g, b); // hue component qreal p = qMax(qMax(r, g), b); qreal n = qMin(qMin(r, g), b); qreal d = 6.0 * (p - n); if (n == p) { h = 0.0; } else if (r == p) { h = ((g - b) / d); } else if (g == p) { h = ((b - r) / d) + (1.0 / 3.0); } else { h = ((r - g) / d) + (2.0 / 3.0); } // chroma component if (r == g && g == b) { c = 0.0; } else { c = qMax((y - n) / y, (p - y) / (1 - y)); } } QColor HCYColorSpace::qColor() const { // start with sane component values qreal _h = ColorTools::wrap(h); qreal _c = ColorTools::normalize(c); qreal _y = ColorTools::normalize(y); // calculate some needed variables qreal _hs = _h * 6.0, th, tm; if (_hs < 1.0) { th = _hs; tm = yc[0] + yc[1] * th; } else if (_hs < 2.0) { th = 2.0 - _hs; tm = yc[1] + yc[0] * th; } else if (_hs < 3.0) { th = _hs - 2.0; tm = yc[1] + yc[2] * th; } else if (_hs < 4.0) { th = 4.0 - _hs; tm = yc[2] + yc[1] * th; } else if (_hs < 5.0) { th = _hs - 4.0; tm = yc[2] + yc[0] * th; } else { th = 6.0 - _hs; tm = yc[0] + yc[2] * th; } // calculate RGB channels in sorted order qreal tn, to, tp; if (tm >= _y) { tp = _y + _y * _c * (1.0 - tm) / tm; to = _y + _y * _c * (th - tm) / tm; tn = _y - (_y * _c); } else { tp = _y + (1.0 - _y) * _c; to = _y + (1.0 - _y) * _c * (th - tm) / (1.0 - tm); tn = _y - (1.0 - _y) * _c * tm / (1.0 - tm); } // return RGB channels in appropriate order if (_hs < 1.0) { return QColor::fromRgbF(igamma(tp), igamma(to), igamma(tn), a); } else if (_hs < 2.0) { return QColor::fromRgbF(igamma(to), igamma(tp), igamma(tn), a); } else if (_hs < 3.0) { return QColor::fromRgbF(igamma(tn), igamma(tp), igamma(to), a); } else if (_hs < 4.0) { return QColor::fromRgbF(igamma(tn), igamma(to), igamma(tp), a); } else if (_hs < 5.0) { return QColor::fromRgbF(igamma(to), igamma(tn), igamma(tp), a); } else { return QColor::fromRgbF(igamma(tp), igamma(tn), igamma(to), a); } } qreal HCYColorSpace::luma(const QColor& color) { return lumag(gamma(color.redF()), gamma(color.greenF()), gamma(color.blueF())); } // ------------------------------------------------------------------------------------- class StateEffects { public: explicit StateEffects(QPalette::ColorGroup state, const KSharedConfigPtr&); - ~StateEffects() {} + ~StateEffects() = default; QBrush brush(const QBrush& background) const; QBrush brush(const QBrush& foreground, const QBrush& background) const; private: enum Effects { // Effects Intensity = 0, Color = 1, Contrast = 2, // Intensity IntensityNoEffect = 0, IntensityShade = 1, IntensityDarken = 2, IntensityLighten = 3, // Color ColorNoEffect = 0, ColorDesaturate = 1, ColorFade = 2, ColorTint = 3, // Contrast ContrastNoEffect = 0, ContrastFade = 1, ContrastTint = 2 }; private: int _effects[3]; double _amount[3]; QColor _color; }; StateEffects::StateEffects(QPalette::ColorGroup state, const KSharedConfigPtr& config) : _color(0, 0, 0, 0) { QString group; if (state == QPalette::Disabled) { group = QLatin1String("ColorEffects:Disabled"); } else if (state == QPalette::Inactive) { group = QLatin1String("ColorEffects:Inactive"); } _effects[0] = 0; _effects[1] = 0; _effects[2] = 0; if (!group.isEmpty()) { KConfigGroup cfg(config, group); const bool enabledByDefault = (state == QPalette::Disabled); if (cfg.readEntry("Enable", enabledByDefault)) { _effects[Intensity] = cfg.readEntry("IntensityEffect", (int)((state == QPalette::Disabled) ? IntensityDarken : IntensityNoEffect)); _effects[Color] = cfg.readEntry("ColorEffect", (int)((state == QPalette::Disabled) ? ColorNoEffect : ColorDesaturate)); _effects[Contrast] = cfg.readEntry("ContrastEffect", (int)((state == QPalette::Disabled) ? ContrastFade : ContrastTint)); _amount[Intensity] = cfg.readEntry("IntensityAmount", (state == QPalette::Disabled) ? 0.10 : 0.0); _amount[Color] = cfg.readEntry("ColorAmount", (state == QPalette::Disabled) ? 0.0 : -0.9); _amount[Contrast] = cfg.readEntry("ContrastAmount", (state == QPalette::Disabled) ? 0.65 : 0.25); if (_effects[Color] > ColorNoEffect) { _color = cfg.readEntry("Color", (state == QPalette::Disabled) ? QColor(56, 56, 56) : QColor(112, 111, 110)); } } } } QBrush StateEffects::brush(const QBrush& background) const { QColor color = background.color(); // TODO - actually work on brushes switch (_effects[Intensity]) { case IntensityShade: color = ColorTools::shade(color, _amount[Intensity]); break; case IntensityDarken: color = ColorTools::darken(color, _amount[Intensity]); break; case IntensityLighten: color = ColorTools::lighten(color, _amount[Intensity]); break; } switch (_effects[Color]) { case ColorDesaturate: color = ColorTools::darken(color, 0.0, 1.0 - _amount[Color]); break; case ColorFade: color = ColorTools::mix(color, _color, _amount[Color]); break; case ColorTint: color = ColorTools::tint(color, _color, _amount[Color]); break; } return QBrush(color); } QBrush StateEffects::brush(const QBrush& foreground, const QBrush& background) const { QColor color = foreground.color(); QColor bg = background.color(); // Apply the foreground effects switch (_effects[Contrast]) { case ContrastFade: color = ColorTools::mix(color, bg, _amount[Contrast]); break; case ContrastTint: color = ColorTools::tint(color, bg, _amount[Contrast]); break; } // Now apply global effects return brush(color); } // ------------------------------------------------------------------------------------ struct SetDefaultColors { int NormalBackground[3]; int AlternateBackground[3]; int NormalText[3]; int InactiveText[3]; int ActiveText[3]; int LinkText[3]; int VisitedText[3]; int NegativeText[3]; int NeutralText[3]; int PositiveText[3]; }; struct DecoDefaultColors { int Hover[3]; int Focus[3]; }; // these numbers come from the Breeze color scheme static const SetDefaultColors defaultViewColors = { { 252, 252, 252 }, // Background { 239, 240, 241 }, // Alternate { 49, 54, 59 }, // Normal { 127, 140, 141 }, // Inactive { 61, 174, 233 }, // Active { 41, 128, 185 }, // Link { 127, 140, 141 }, // Visited { 218, 68, 83 }, // Negative { 246, 116, 0 }, // Neutral { 39, 174, 96 } // Positive }; static const SetDefaultColors defaultWindowColors = { { 239, 240, 241 }, // Background { 189, 195, 199 }, // Alternate { 49, 54, 59 }, // Normal { 127, 140, 141 }, // Inactive { 61, 174, 233 }, // Active { 41, 128, 185 }, // Link { 127, 140, 141 }, // Visited { 218, 68, 83 }, // Negative { 246, 116, 0 }, // Neutral { 39, 174, 96 } // Positive }; static const SetDefaultColors defaultButtonColors = { { 239, 240, 241 }, // Background { 189, 195, 199 }, // Alternate { 49, 54, 59 }, // Normal { 127, 140, 141 }, // Inactive { 61, 174, 233 }, // Active { 41, 128, 185 }, // Link { 127, 140, 141 }, // Visited { 218, 68, 83 }, // Negative { 246, 116, 0 }, // Neutral { 39, 174, 96 } // Positive }; static const SetDefaultColors defaultSelectionColors = { { 61, 174, 233 }, // Background { 29, 153, 243 }, // Alternate { 239, 240, 241 }, // Normal { 239, 240, 241 }, // Inactive { 252, 252, 252 }, // Active { 253, 188, 75 }, // Link { 189, 195, 199 }, // Visited { 218, 68, 83 }, // Negative { 246, 116, 0 }, // Neutral { 39, 174, 96 } // Positive }; static const SetDefaultColors defaultTooltipColors = { { 49, 54, 59 }, // Background { 77, 77, 77 }, // Alternate { 239, 240, 241 }, // Normal { 189, 195, 199 }, // Inactive { 61, 174, 233 }, // Active { 41, 128, 185 }, // Link { 127, 140, 141 }, // Visited { 218, 68, 83 }, // Negative { 246, 116, 0 }, // Neutral { 39, 174, 96 } // Positive }; static const SetDefaultColors defaultComplementaryColors = { { 49, 54, 59 }, // Background { 77, 77, 77 }, // Alternate { 239, 240, 241 }, // Normal { 189, 195, 199 }, // Inactive { 61, 174, 233 }, // Active { 41, 128, 185 }, // Link { 127, 140, 141 }, // Visited { 218, 68, 83 }, // Negative { 246, 116, 0 }, // Neutral { 39, 174, 96 } // Positive }; static const DecoDefaultColors defaultDecorationColors = { { 147, 206, 233 }, // Hover { 61, 174, 233 }, // Focus }; // ------------------------------------------------------------------------------------ class SchemeManagerPrivate : public QSharedData { public: explicit SchemeManagerPrivate(const KSharedConfigPtr&, QPalette::ColorGroup, const char*, SetDefaultColors); explicit SchemeManagerPrivate(const KSharedConfigPtr&, QPalette::ColorGroup, const char*, SetDefaultColors, const QBrush&); - ~SchemeManagerPrivate() {} + ~SchemeManagerPrivate() = default; QBrush background(SchemeManager::BackgroundRole) const; QBrush foreground(SchemeManager::ForegroundRole) const; QBrush decoration(SchemeManager::DecorationRole) const; qreal contrast() const; private: void init(const KSharedConfigPtr&, QPalette::ColorGroup, const char*, SetDefaultColors); private: struct { QBrush fg[8], bg[8], deco[2]; } _brushes; qreal _contrast; }; #define DEFAULT(c) QColor( c[0], c[1], c[2] ) #define SET_DEFAULT(a) DEFAULT( defaults.a ) #define DECO_DEFAULT(a) DEFAULT( defaultDecorationColors.a ) SchemeManagerPrivate::SchemeManagerPrivate(const KSharedConfigPtr& config, QPalette::ColorGroup state, const char* group, SetDefaultColors defaults) { KConfigGroup cfg(config, group); _contrast = SchemeManager::contrastF(config); // loaded-from-config colors (no adjustment) _brushes.bg[0] = cfg.readEntry("BackgroundNormal", SET_DEFAULT(NormalBackground)); _brushes.bg[1] = cfg.readEntry("BackgroundAlternate", SET_DEFAULT(AlternateBackground)); // the rest init(config, state, group, defaults); } SchemeManagerPrivate::SchemeManagerPrivate(const KSharedConfigPtr& config, QPalette::ColorGroup state, const char* group, SetDefaultColors defaults, const QBrush& tint) { KConfigGroup cfg(config, group); _contrast = SchemeManager::contrastF(config); // loaded-from-config colors _brushes.bg[0] = cfg.readEntry("BackgroundNormal", SET_DEFAULT(NormalBackground)); _brushes.bg[1] = cfg.readEntry("BackgroundAlternate", SET_DEFAULT(AlternateBackground)); // adjustment _brushes.bg[0] = ColorTools::tint(_brushes.bg[0].color(), tint.color(), 0.4); _brushes.bg[1] = ColorTools::tint(_brushes.bg[1].color(), tint.color(), 0.4); // the rest init(config, state, group, defaults); } void SchemeManagerPrivate::init(const KSharedConfigPtr& config, QPalette::ColorGroup state, const char* group, SetDefaultColors defaults) { KConfigGroup cfg(config, group); // loaded-from-config colors _brushes.fg[0] = cfg.readEntry("ForegroundNormal", SET_DEFAULT(NormalText)); _brushes.fg[1] = cfg.readEntry("ForegroundInactive", SET_DEFAULT(InactiveText)); _brushes.fg[2] = cfg.readEntry("ForegroundActive", SET_DEFAULT(ActiveText)); _brushes.fg[3] = cfg.readEntry("ForegroundLink", SET_DEFAULT(LinkText)); _brushes.fg[4] = cfg.readEntry("ForegroundVisited", SET_DEFAULT(VisitedText)); _brushes.fg[5] = cfg.readEntry("ForegroundNegative", SET_DEFAULT(NegativeText)); _brushes.fg[6] = cfg.readEntry("ForegroundNeutral", SET_DEFAULT(NeutralText)); _brushes.fg[7] = cfg.readEntry("ForegroundPositive", SET_DEFAULT(PositiveText)); _brushes.deco[0] = cfg.readEntry("DecorationHover", DECO_DEFAULT(Hover)); _brushes.deco[1] = cfg.readEntry("DecorationFocus", DECO_DEFAULT(Focus)); // apply state adjustments if (state != QPalette::Active) { StateEffects effects(state, config); for (int i = 0; i < 8; i++) { _brushes.fg[i] = effects.brush(_brushes.fg[i], _brushes.bg[0]); } _brushes.deco[0] = effects.brush(_brushes.deco[0], _brushes.bg[0]); _brushes.deco[1] = effects.brush(_brushes.deco[1], _brushes.bg[0]); _brushes.bg[0] = effects.brush(_brushes.bg[0]); _brushes.bg[1] = effects.brush(_brushes.bg[1]); } // calculated backgrounds _brushes.bg[2] = ColorTools::tint(_brushes.bg[0].color(), _brushes.fg[2].color()); _brushes.bg[3] = ColorTools::tint(_brushes.bg[0].color(), _brushes.fg[3].color()); _brushes.bg[4] = ColorTools::tint(_brushes.bg[0].color(), _brushes.fg[4].color()); _brushes.bg[5] = ColorTools::tint(_brushes.bg[0].color(), _brushes.fg[5].color()); _brushes.bg[6] = ColorTools::tint(_brushes.bg[0].color(), _brushes.fg[6].color()); _brushes.bg[7] = ColorTools::tint(_brushes.bg[0].color(), _brushes.fg[7].color()); } QBrush SchemeManagerPrivate::background(SchemeManager::BackgroundRole role) const { switch (role) { case SchemeManager::AlternateBackground: return _brushes.bg[1]; case SchemeManager::ActiveBackground: return _brushes.bg[2]; case SchemeManager::LinkBackground: return _brushes.bg[3]; case SchemeManager::VisitedBackground: return _brushes.bg[4]; case SchemeManager::NegativeBackground: return _brushes.bg[5]; case SchemeManager::NeutralBackground: return _brushes.bg[6]; case SchemeManager::PositiveBackground: return _brushes.bg[7]; default: return _brushes.bg[0]; } } QBrush SchemeManagerPrivate::foreground(SchemeManager::ForegroundRole role) const { switch (role) { case SchemeManager::InactiveText: return _brushes.fg[1]; case SchemeManager::ActiveText: return _brushes.fg[2]; case SchemeManager::LinkText: return _brushes.fg[3]; case SchemeManager::VisitedText: return _brushes.fg[4]; case SchemeManager::NegativeText: return _brushes.fg[5]; case SchemeManager::NeutralText: return _brushes.fg[6]; case SchemeManager::PositiveText: return _brushes.fg[7]; default: return _brushes.fg[0]; } } QBrush SchemeManagerPrivate::decoration(SchemeManager::DecorationRole role) const { switch (role) { case SchemeManager::FocusColor: return _brushes.deco[1]; default: return _brushes.deco[0]; } } qreal SchemeManagerPrivate::contrast() const { return _contrast; } // ------------------------------------------------------------------------------------ -SchemeManager::SchemeManager(const SchemeManager& other) - : d(other.d) +SchemeManager::SchemeManager(const SchemeManager& other) : d(other.d) { } -SchemeManager& SchemeManager::operator=(const SchemeManager& other) +SchemeManager::~SchemeManager() { - d = other.d; - return *this; } -SchemeManager::~SchemeManager() +SchemeManager& SchemeManager::operator=(const SchemeManager& other) { + d = other.d; + return *this; } SchemeManager::SchemeManager(QPalette::ColorGroup state, ColorSet set, KSharedConfigPtr config) { if (!config) { config = KSharedConfig::openConfig(); } switch (set) { case Window: d = new SchemeManagerPrivate(config, state, "Colors:Window", defaultWindowColors); break; case Button: d = new SchemeManagerPrivate(config, state, "Colors:Button", defaultButtonColors); break; case Selection: { KConfigGroup group(config, "ColorEffects:Inactive"); // NOTE: keep this in sync with kdebase/workspace/kcontrol/colors/colorscm.cpp bool inactiveSelectionEffect = group.readEntry("ChangeSelectionColor", group.readEntry("Enable", true)); // if enabled, inactiver/disabled uses Window colors instead, ala gtk // ...except tinted with the Selection:NormalBackground color so it looks more like selection if (state == QPalette::Active || (state == QPalette::Inactive && !inactiveSelectionEffect)) { d = new SchemeManagerPrivate(config, state, "Colors:Selection", defaultSelectionColors); } else if (state == QPalette::Inactive) { d = new SchemeManagerPrivate(config, state, "Colors:Window", defaultWindowColors, SchemeManager(QPalette::Active, Selection, config).background()); } else { // disabled (...and still want this branch when inactive+disabled exists) d = new SchemeManagerPrivate(config, state, "Colors:Window", defaultWindowColors); } } break; case Tooltip: d = new SchemeManagerPrivate(config, state, "Colors:Tooltip", defaultTooltipColors); break; case Complementary: d = new SchemeManagerPrivate(config, state, "Colors:Complementary", defaultComplementaryColors); break; default: d = new SchemeManagerPrivate(config, state, "Colors:View", defaultViewColors); } } int SchemeManager::contrast() { KConfigGroup g(KSharedConfig::openConfig(), "KDE"); return g.readEntry("contrast", 7); } qreal SchemeManager::contrastF(const KSharedConfigPtr& config) { if (config) { KConfigGroup g(config, "KDE"); return 0.1 * g.readEntry("contrast", 7); } return 0.1 * (qreal)contrast(); } QBrush SchemeManager::background(BackgroundRole role) const { return d->background(role); } QBrush SchemeManager::foreground(ForegroundRole role) const { return d->foreground(role); } QBrush SchemeManager::decoration(DecorationRole role) const { return d->decoration(role); } QColor SchemeManager::shade(ShadeRole role) const { return shade(background().color(), role, d->contrast()); } QColor SchemeManager::shade(const QColor& color, ShadeRole role) { return shade(color, role, SchemeManager::contrastF()); } QColor SchemeManager::shade(const QColor& color, ShadeRole role, qreal contrast, qreal chromaAdjust) { // nan -> 1.0 contrast = ((1.0 > contrast) ? ((-1.0 < contrast) ? contrast : -1.0) : 1.0); qreal y = ColorTools::luma(color); qreal yi = 1.0 - y; // handle very dark colors (base, mid, dark, shadow == midlight, light) if (y < 0.006) { switch (role) { case SchemeManager::LightShade: return ColorTools::shade(color, 0.05 + 0.95 * contrast, chromaAdjust); case SchemeManager::MidShade: return ColorTools::shade(color, 0.01 + 0.20 * contrast, chromaAdjust); case SchemeManager::DarkShade: return ColorTools::shade(color, 0.02 + 0.40 * contrast, chromaAdjust); default: return ColorTools::shade(color, 0.03 + 0.60 * contrast, chromaAdjust); } } // handle very light colors (base, midlight, light == mid, dark, shadow) if (y > 0.93) { switch (role) { case SchemeManager::MidlightShade: return ColorTools::shade(color, -0.02 - 0.20 * contrast, chromaAdjust); case SchemeManager::DarkShade: return ColorTools::shade(color, -0.06 - 0.60 * contrast, chromaAdjust); case SchemeManager::ShadowShade: return ColorTools::shade(color, -0.10 - 0.90 * contrast, chromaAdjust); default: return ColorTools::shade(color, -0.04 - 0.40 * contrast, chromaAdjust); } } // handle everything else qreal lightAmount = (0.05 + y * 0.55) * (0.25 + contrast * 0.75); qreal darkAmount = (- y) * (0.55 + contrast * 0.35); switch (role) { case SchemeManager::LightShade: return ColorTools::shade(color, lightAmount, chromaAdjust); case SchemeManager::MidlightShade: return ColorTools::shade(color, (0.15 + 0.35 * yi) * lightAmount, chromaAdjust); case SchemeManager::MidShade: return ColorTools::shade(color, (0.35 + 0.15 * y) * darkAmount, chromaAdjust); case SchemeManager::DarkShade: return ColorTools::shade(color, darkAmount, chromaAdjust); default: return ColorTools::darken(ColorTools::shade(color, darkAmount, chromaAdjust), 0.5 + 0.3 * y); } } void SchemeManager::adjustBackground(QPalette& palette, BackgroundRole newRole, QPalette::ColorRole color, ColorSet set, KSharedConfigPtr config) { palette.setBrush(QPalette::Active, color, SchemeManager(QPalette::Active, set, config).background(newRole)); palette.setBrush(QPalette::Inactive, color, SchemeManager(QPalette::Inactive, set, config).background(newRole)); palette.setBrush(QPalette::Disabled, color, SchemeManager(QPalette::Disabled, set, config).background(newRole)); } void SchemeManager::adjustForeground(QPalette& palette, ForegroundRole newRole, QPalette::ColorRole color, ColorSet set, KSharedConfigPtr config) { palette.setBrush(QPalette::Active, color, SchemeManager(QPalette::Active, set, config).foreground(newRole)); palette.setBrush(QPalette::Inactive, color, SchemeManager(QPalette::Inactive, set, config).foreground(newRole)); palette.setBrush(QPalette::Disabled, color, SchemeManager(QPalette::Disabled, set, config).foreground(newRole)); } QPalette SchemeManager::createApplicationPalette(const KSharedConfigPtr& config) { QPalette palette; static const QPalette::ColorGroup states[3] = { QPalette::Active, QPalette::Inactive, QPalette::Disabled }; // TT thinks tooltips shouldn't use active, so we use our active colors for all states SchemeManager schemeTooltip(QPalette::Active, SchemeManager::Tooltip, config); for (int i = 0; i < 3; i++) { QPalette::ColorGroup state = states[i]; SchemeManager schemeView(state, SchemeManager::View, config); SchemeManager schemeWindow(state, SchemeManager::Window, config); SchemeManager schemeButton(state, SchemeManager::Button, config); SchemeManager schemeSelection(state, SchemeManager::Selection, config); palette.setBrush(state, QPalette::WindowText, schemeWindow.foreground()); palette.setBrush(state, QPalette::Window, schemeWindow.background()); palette.setBrush(state, QPalette::Base, schemeView.background()); palette.setBrush(state, QPalette::Text, schemeView.foreground()); palette.setBrush(state, QPalette::Button, schemeButton.background()); palette.setBrush(state, QPalette::ButtonText, schemeButton.foreground()); palette.setBrush(state, QPalette::Highlight, schemeSelection.background()); palette.setBrush(state, QPalette::HighlightedText, schemeSelection.foreground()); palette.setBrush(state, QPalette::ToolTipBase, schemeTooltip.background()); palette.setBrush(state, QPalette::ToolTipText, schemeTooltip.foreground()); palette.setColor(state, QPalette::Light, schemeWindow.shade(SchemeManager::LightShade)); palette.setColor(state, QPalette::Midlight, schemeWindow.shade(SchemeManager::MidlightShade)); palette.setColor(state, QPalette::Mid, schemeWindow.shade(SchemeManager::MidShade)); palette.setColor(state, QPalette::Dark, schemeWindow.shade(SchemeManager::DarkShade)); palette.setColor(state, QPalette::Shadow, schemeWindow.shade(SchemeManager::ShadowShade)); palette.setBrush(state, QPalette::AlternateBase, schemeView.background(SchemeManager::AlternateBackground)); palette.setBrush(state, QPalette::Link, schemeView.foreground(SchemeManager::LinkText)); palette.setBrush(state, QPalette::LinkVisited, schemeView.foreground(SchemeManager::VisitedText)); } return palette; } diff --git a/kstars/auxiliary/schememanager.h b/kstars/auxiliary/schememanager.h index 846a60df8..7bd1dc7ef 100644 --- a/kstars/auxiliary/schememanager.h +++ b/kstars/auxiliary/schememanager.h @@ -1,464 +1,453 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2004-08-02 * Description : colors scheme manager * * Copyright (C) 2006-2018 by Gilles Caulier * Copyright (C) 2007 by Matthew Woehlke * * 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ -#ifndef SCHEME_MANAGER_H -#define SCHEME_MANAGER_H +#pragma once -// Qt includes +#include #include #include #include #include -// KDE includes - -#include - -// Local includes - class SchemeManagerPrivate; /** * A set of methods used to work with colors. * * SchemeManager currently provides access to the system color palette that the * user has selected (in the future, it is expected to do more). It greatly * expands on QPalette by providing five distinct "sets" with several color * choices each, covering background, foreground, and decoration colors. * * A SchemeManager instance represents colors corresponding to a "set", where a * set consists of those colors used to draw a particular type of element, such * as a menu, button, view, selected text, or tooltip. Each set has a distinct * set of colors, so you should always use the correct set for drawing and * never assume that a particular foreground for one set is the same as the * foreground for any other set. Individual colors may be quickly referenced by * creating an anonymous instance and invoking a lookup member. * * @note * The color palettes for the various states of a widget (active, inactive, * disabled) may be wildly different. Therefore, it is important to take the * state into account. This is why the SchemeManager constructor requires a * QPalette::ColorGroup as an argument. * * To facilitate working with potentially-varying states, two convenience API's * are provided. These are SchemeManager::adjustBackground and its sister * SchemeManager::adjustForeground, and the helper class ::KStatefulBrush. * * @see SchemeManager::ColorSet, SchemeManager::ForegroundRole, * SchemeManager::BackgroundRole, SchemeManager::DecorationRole, * SchemeManager::ShadeRole */ class SchemeManager { public: /** * This enumeration describes the color set for which a color is being * selected. * * Color sets define a color "environment", suitable for drawing all parts * of a given region. Colors from different sets should not be combined. */ enum ColorSet { /** * Views; for example, frames, input fields, etc. * * If it contains things that can be selected, it is probably a View. */ View, /** * Non-editable window elements; for example, menus. * * If it isn't a Button, View, or Tooltip, it is probably a Window. */ Window, /** * Buttons and button-like controls. * * In addition to buttons, "button-like" controls such as non-editable * dropdowns, scrollbar sliders, slider handles, etc. should also use * this role. */ Button, /** * Selected items in views. * * Note that unfocused or disabled selections should use the Window * role. This makes it more obvious to the user that the view * containing the selection does not have input focus. */ Selection, /** * Tooltips. * * The tooltip set can often be substituted for the view * set when editing is not possible, but the Window set is deemed * inappropriate. "What's This" help is an excellent example, another * might be pop-up notifications (depending on taste). */ Tooltip, /** * Complementary areas. * * Some applications want some areas to have a different color scheme. * Ususally dark areas over a light theme. For instance the fullscreen UI * of a picture viewer, or the logout/lock screen of the plasma workspace * ask for a dark color scheme even on light themes. * @since 5.19 */ Complementary }; /** * This enumeration describes the background color being selected from the * given set. * * Background colors are suitable for drawing under text, and should never * be used to draw text. In combination with one of the overloads of * SchemeManager::shade, they may be used to generate colors for drawing * frames, bevels, and similar decorations. */ enum BackgroundRole { /** * Normal background. */ NormalBackground = 0, /** * Alternate background; for example, for use in lists. * * This color may be the same as BackgroundNormal, especially in sets * other than View and Window. */ AlternateBackground = 1, /** * Third color; for example, items which are new, active, requesting * attention, etc. * * Alerting the user that a certain field must be filled out would be a * good usage (although NegativeBackground could be used to the same * effect, depending on what you are trying to achieve). Unlike * ActiveText, this should not be used for mouseover effects. */ ActiveBackground = 2, /** * Fourth color; corresponds to (unvisited) links. * * Exactly what this might be used for is somewhat harder to qualify; * it might be used for bookmarks, as a 'you can click here' indicator, * or to highlight recent content (i.e. in a most-recently-accessed * list). */ LinkBackground = 3, /** * Fifth color; corresponds to visited links. * * This can also be used to indicate "not recent" content, especially * when a color is needed to denote content which is "old" or * "archival". */ VisitedBackground = 4, /** * Sixth color; for example, errors, untrusted content, etc. */ NegativeBackground = 5, /** * Seventh color; for example, warnings, secure/encrypted content. */ NeutralBackground = 6, /** * Eigth color; for example, success messages, trusted content. */ PositiveBackground = 7 }; /** * This enumeration describes the foreground color being selected from the * given set. * * Foreground colors are suitable for drawing text or glyphs (such as the * symbols on window decoration buttons, assuming a suitable background * brush is used), and should never be used to draw backgrounds. * * For window decorations, the following is suggested, but not set in * stone: * @li Maximize - PositiveText * @li Minimize - NeutralText * @li Close - NegativeText * @li WhatsThis - LinkText * @li Sticky - ActiveText */ enum ForegroundRole { /** * Normal foreground. */ NormalText = 0, /** * Second color; for example, comments, items which are old, inactive * or disabled. Generally used for things that are meant to be "less * important". InactiveText is not the same role as NormalText in the * inactive state. */ InactiveText = 1, /** * Third color; for example items which are new, active, requesting * attention, etc. May be used as a hover color for clickable items. */ ActiveText = 2, /** * Fourth color; use for (unvisited) links. May also be used for other * clickable items or content that indicates relationships, items that * indicate somewhere the user can visit, etc. */ LinkText = 3, /** * Fifth color; used for (visited) links. As with LinkText, may be used * for items that have already been "visited" or accessed. May also be * used to indicate "historical" (i.e. "old") items or information, * especially if InactiveText is being used in the same context to * express something different. */ VisitedText = 4, /** * Sixth color; for example, errors, untrusted content, deletions, * etc. */ NegativeText = 5, /** * Seventh color; for example, warnings, secure/encrypted content. */ NeutralText = 6, /** * Eigth color; for example, additions, success messages, trusted * content. */ PositiveText = 7 }; /** * This enumeration describes the decoration color being selected from the * given set. * * Decoration colors are used to draw decorations (such as frames) for * special purposes. Like color shades, they are neither foreground nor * background colors. Text should not be painted over a decoration color, * and decoration colors should not be used to draw text. */ enum DecorationRole { /** * Color used to draw decorations for items which have input focus. */ FocusColor, /** * Color used to draw decorations for items which will be activated by * clicking. */ HoverColor }; /** * This enumeration describes the color shade being selected from the given * set. * * Color shades are used to draw "3d" elements, such as frames and bevels. * They are neither foreground nor background colors. Text should not be * painted over a shade, and shades should not be used to draw text. */ enum ShadeRole { /** * The light color is lighter than dark() or shadow() and contrasts * with the base color. */ LightShade, /** * The midlight color is in between base() and light(). */ MidlightShade, /** * The mid color is in between base() and dark(). */ MidShade, /** * The dark color is in between mid() and shadow(). */ DarkShade, /** * The shadow color is darker than light() or midlight() and contrasts * the base color. */ ShadowShade }; public: /** * Construct a copy of another SchemeManager. */ SchemeManager(const SchemeManager&); - /** - * Destructor - */ + // Don't use dtor with default keyword here because it breaks the PIMPL pattern and refactoring is needed virtual ~SchemeManager(); /** * Standard assignment operator */ SchemeManager& operator=(const SchemeManager&); /** * Construct a palette from given color set and state, using the colors * from the given KConfig (if null, the system colors are used). */ explicit SchemeManager(QPalette::ColorGroup, ColorSet = View, KSharedConfigPtr = KSharedConfigPtr()); /** * Retrieve the requested background brush. */ QBrush background(BackgroundRole = NormalBackground) const; /** * Retrieve the requested foreground brush. */ QBrush foreground(ForegroundRole = NormalText) const; /** * Retrieve the requested decoration brush. */ QBrush decoration(DecorationRole) const; /** * Retrieve the requested shade color, using * SchemeManager::background(SchemeManager::NormalBackground) * as the base color and the contrast setting from the KConfig used to * create this SchemeManager instance (the system contrast setting, if no * KConfig was specified). * * @note Shades are chosen such that all shades would contrast with the * base color. This means that if base is very dark, the 'dark' shades will * be lighter than the base color, with midlight() == shadow(). * Conversely, if the base color is very light, the 'light' shades will be * darker than the base color, with light() == mid(). */ QColor shade(ShadeRole) const; /** * Returns the contrast for borders. * @return the contrast (between 0 for minimum and 10 for maximum * contrast) */ static int contrast(); /** * Returns the contrast for borders as a floating point value. * @param config pointer to the config from which to read the contrast * setting (the default is to use KSharedConfig::openConfig()) * @return the contrast (between 0.0 for minimum and 1.0 for maximum * contrast) */ static qreal contrastF(const KSharedConfigPtr& config = KSharedConfigPtr()); /** * Retrieve the requested shade color, using the specified color as the * base color and the system contrast setting. * * @note Shades are chosen such that all shades would contrast with the * base color. This means that if base is very dark, the 'dark' shades will * be lighter than the base color, with midlight() == shadow(). * Conversely, if the base color is very light, the 'light' shades will be * darker than the base color, with light() == mid(). */ static QColor shade(const QColor&, ShadeRole); /** * Retrieve the requested shade color, using the specified color as the * base color and the specified contrast. * * @param contrast Amount roughly specifying the contrast by which to * adjust the base color, between -1.0 and 1.0 (values between 0.0 and 1.0 * correspond to the value from SchemeManager::contrastF) * @param chromaAdjust (optional) Amount by which to adjust the chroma of * the shade (1.0 means no adjustment) * * @note Shades are chosen such that all shades would contrast with the * base color. This means that if base is very dark, the 'dark' shades will * be lighter than the base color, with midlight() == shadow(). * Conversely, if the base color is very light, the 'light' shades will be * darker than the base color, with light() == mid(). * */ static QColor shade(const QColor&, ShadeRole, qreal contrast, qreal chromaAdjust = 0.0); /** * Adjust a QPalette by replacing the specified QPalette::ColorRole with * the requested background color for all states. Using this method is * safer than replacing individual states, as it insulates you against * changes in QPalette::ColorGroup. * * @note Although it is possible to replace a foreground color using this * method, it's bad usability to do so. Just say "no". */ static void adjustBackground(QPalette&, BackgroundRole newRole = NormalBackground, QPalette::ColorRole color = QPalette::Base, ColorSet set = View, KSharedConfigPtr = KSharedConfigPtr()); /** * Adjust a QPalette by replacing the specified QPalette::ColorRole with * the requested foreground color for all states. Using this method is * safer than replacing individual states, as it insulates you against * changes in QPalette::ColorGroup. * * @note Although it is possible to replace a background color using this * method, it's bad usability to do so. Just say "no". */ static void adjustForeground(QPalette&, ForegroundRole newRole = NormalText, QPalette::ColorRole color = QPalette::Text, ColorSet set = View, KSharedConfigPtr = KSharedConfigPtr()); /** * Used to obtain the QPalette that will be used to set the application * palette from KDE Platform theme. * * @param config KConfig from which to load the colors * * @returns the QPalette */ static QPalette createApplicationPalette(const KSharedConfigPtr& config); private: QExplicitlySharedDataPointer d; }; - -#endif // SCHEME_MANAGER_H diff --git a/kstars/auxiliary/thumbnaileditor.cpp b/kstars/auxiliary/thumbnaileditor.cpp index 94f91baac..b2cbfc31b 100644 --- a/kstars/auxiliary/thumbnaileditor.cpp +++ b/kstars/auxiliary/thumbnaileditor.cpp @@ -1,86 +1,84 @@ /*************************************************************************** thumbnaileditor.cpp - description ------------------- begin : Thu Mar 2 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "thumbnaileditor.h" -#include "ui_thumbnaileditor.h" + #include "thumbnailpicker.h" +#include "ui_thumbnaileditor.h" + +#include -#include -#include -#include #include +#include +#include #include +#include -#include #include -#include #include +#include ThumbnailEditorUI::ThumbnailEditorUI(QWidget *parent) : QFrame(parent) { setupUi(this); } ThumbnailEditor::ThumbnailEditor(ThumbnailPicker *_tp, double _w, double _h) : QDialog(_tp), tp(_tp) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ui = new ThumbnailEditorUI(this); w = _w; h = _h; ui->MessageLabel->setText(i18n("Crop region will be scaled to [ %1 * %2 ]", w, h)); setWindowTitle(i18n("Edit Thumbnail Image")); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); ui->ImageCanvas->setCropRect(tp->imageRect()->x(), tp->imageRect()->y(), tp->imageRect()->width(), tp->imageRect()->height()); ui->ImageCanvas->setImage(tp->currentListImage()); //DEBUG //qDebug() << tp->currentListImage()->size(); connect(ui->ImageCanvas, SIGNAL(cropRegionModified()), SLOT(slotUpdateCropLabel())); slotUpdateCropLabel(); update(); } -ThumbnailEditor::~ThumbnailEditor() -{ -} - QPixmap ThumbnailEditor::thumbnail() { QImage im = ui->ImageCanvas->croppedImage().toImage(); im = im.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); return QPixmap::fromImage(im); } void ThumbnailEditor::slotUpdateCropLabel() { QRect *r = ui->ImageCanvas->cropRect(); ui->CropLabel->setText(i18n("Crop region: [%1,%2 %3x%4]", r->left(), r->top(), r->width(), r->height())); } diff --git a/kstars/auxiliary/thumbnaileditor.h b/kstars/auxiliary/thumbnaileditor.h index 6721dee3b..56757dd26 100644 --- a/kstars/auxiliary/thumbnaileditor.h +++ b/kstars/auxiliary/thumbnaileditor.h @@ -1,54 +1,51 @@ /*************************************************************************** thumbnaileditor.h - description ------------------- begin : Thu Mar 2 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 THUMBNAILEDITOR_H_ -#define THUMBNAILEDITOR_H_ +#pragma once #include #include #include #include "ui_thumbnaileditor.h" class ThumbnailPicker; class ThumbnailEditorUI : public QFrame, public Ui::ThumbnailEditor { Q_OBJECT public: explicit ThumbnailEditorUI(QWidget *parent); }; class ThumbnailEditor : public QDialog { Q_OBJECT public: ThumbnailEditor(ThumbnailPicker *_tp, double _w, double _h); - ~ThumbnailEditor() override; + ~ThumbnailEditor() override = default; QPixmap thumbnail(); private slots: void slotUpdateCropLabel(); private: ThumbnailEditorUI *ui; ThumbnailPicker *tp; double w, h; }; - -#endif diff --git a/kstars/dialogs/addlinkdialog.h b/kstars/dialogs/addlinkdialog.h index 7b3290296..489ddeed0 100644 --- a/kstars/dialogs/addlinkdialog.h +++ b/kstars/dialogs/addlinkdialog.h @@ -1,109 +1,96 @@ /*************************************************************************** addlinQDialog - K Desktop Planetarium ------------------- begin : Sun Oct 21 2001 copyright : (C) 2001 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 ADDLINKDIALOG_H_ -#define ADDLINKDIALOG_H_ +#pragma once + +#include "ui_addlinkdialog.h" -#include -#include #include #include -#include "ui_addlinkdialog.h" +#include +#include class QString; class AddLinkDialogUI : public QFrame, public Ui::AddLinkDialog { Q_OBJECT public: explicit AddLinkDialogUI(QWidget *parent = nullptr); }; /** - *@class AddLinkDialog - * AddLinkDialog is a simple dialog for adding a custom URL to a popup menu. - *@author Jason Harris - *@version 1.0 - */ + * @class AddLinkDialog + * AddLinkDialog is a simple dialog for adding a custom URL to a popup menu. + * + * @author Jason Harris + * @version 1.0 + */ class AddLinkDialog : public QDialog { Q_OBJECT public: - /** - *Constructor. - */ + /** Constructor */ explicit AddLinkDialog(QWidget *parent = nullptr, const QString &oname = i18n("object")); - /** - *Destructor (empty) - */ - ~AddLinkDialog() override {} + /** Destructor */ + ~AddLinkDialog() override = default; - /** - *@return QString of the entered URL - */ + /** @return QString of the entered URL */ QString url() const { return ald->URLBox->text(); } /** - *@short Set the URL text - *@param s the new URL text - */ + * @short Set the URL text + * @param s the new URL text + */ void setURL(const QString &s) { ald->URLBox->setText(s); } - /** - *@return QString of the entered menu entry text - */ + /** @return QString of the entered menu entry text */ QString desc() const { return ald->DescBox->text(); } /** - *@short Set the Description text - *@param s the new description text - */ + * @short Set the Description text + * @param s the new description text + */ void setDesc(const QString &s) { ald->DescBox->setText(s); } - /** - *@return true if user declared the link is an image - */ + /** @return true if user declared the link is an image */ bool isImageLink() const { return ald->ImageRadio->isChecked(); } /** - *@short Set the link type - *@param b if true, link is an image link. - */ + * @short Set the link type + * @param b if true, link is an image link. + */ void setImageLink(bool b) { ald->ImageRadio->setChecked(b); } private slots: - /** - *Open the entered URL in the web browser - */ + /** Open the entered URL in the web browser */ void checkURL(void); /** - *We provide a default menu text string; this function changes the - *default string if the link type (image/webpage) is changed. Note - *that if the user has changed the menu text, this function does nothing. - *@param imageEnabled if true, show image string; otherwise show webpage string. - */ + * We provide a default menu text string; this function changes the + * default string if the link type (image/webpage) is changed. Note + * that if the user has changed the menu text, this function does nothing. + * @param imageEnabled if true, show image string; otherwise show webpage string. + */ void changeDefaultDescription(bool imageEnabled); private: QString ObjectName; AddLinkDialogUI *ald; }; - -#endif diff --git a/kstars/dialogs/detaildialog.cpp b/kstars/dialogs/detaildialog.cpp index 14e749c73..037cd9b20 100644 --- a/kstars/dialogs/detaildialog.cpp +++ b/kstars/dialogs/detaildialog.cpp @@ -1,1336 +1,1332 @@ /*************************************************************************** detaildialog.cpp - description ------------------- begin : Sun May 5 2002 copyright : (C) 2002 by Jason Harris and Jasem Mutlaq email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "detaildialog.h" #include "config-kstars.h" #include "addlinkdialog.h" #include "kspaths.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include "observinglist.h" #include "skymap.h" #include "thumbnailpicker.h" #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksasteroid.h" #include "skyobjects/kscomet.h" #include "skyobjects/ksmoon.h" #include "skyobjects/starobject.h" #include "skyobjects/supernova.h" #include "skycomponents/catalogcomponent.h" #ifdef HAVE_INDI #include #include "indi/indilistener.h" #endif #include #include DetailDialog::DetailDialog(SkyObject *o, const KStarsDateTime &ut, GeoLocation *geo, QWidget *parent) : KPageDialog(parent), selectedObject(o), Data(nullptr), DataComet(nullptr), Pos(nullptr), Links(nullptr), Adv(nullptr), Log(nullptr) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif setFaceType(Tabbed); setBackgroundRole(QPalette::Base); titlePalette = palette(); titlePalette.setColor(backgroundRole(), palette().color(QPalette::Active, QPalette::Highlight)); titlePalette.setColor(foregroundRole(), palette().color(QPalette::Active, QPalette::HighlightedText)); //Create thumbnail image Thumbnail.reset(new QPixmap(200, 200)); setWindowTitle(i18n("Object Details")); // JM 2016-11-22: Do we really need a close button? //setStandardButtons(QDialogButtonBox::Close); setStandardButtons(QDialogButtonBox::NoButton); createGeneralTab(); createPositionTab(ut, geo); createLinksTab(); createAdvancedTab(); createLogTab(); } -DetailDialog::~DetailDialog() -{ -} - void DetailDialog::createGeneralTab() { Data = new DataWidget(this); addPage(Data, i18n("General")); Data->Names->setPalette(titlePalette); //Connections connect(Data->ObsListButton, SIGNAL(clicked()), this, SLOT(addToObservingList())); connect(Data->CenterButton, SIGNAL(clicked()), this, SLOT(centerMap())); #ifdef HAVE_INDI connect(Data->ScopeButton, SIGNAL(clicked()), this, SLOT(centerTelescope())); #else Data->ScopeButton->setEnabled(false); #endif connect(Data->Image, SIGNAL(clicked()), this, SLOT(updateThumbnail())); // Stuff that should be visible only for specific types of objects Data->IllumLabel->setVisible(false); // Only shown for the moon Data->Illumination->setVisible(false); Data->BVIndex->setVisible(false); // Only shown for stars Data->BVLabel->setVisible(false); //Show object thumbnail image showThumbnail(); //Fill in the data fields //Contents depend on type of object QString objecttyp; switch (selectedObject->type()) { case SkyObject::STAR: { StarObject *s = (StarObject *)selectedObject; if (s->getHDIndex()) { Data->Names->setText(QString("%1, HD %2").arg(s->longname()).arg(s->getHDIndex())); } else { Data->Names->setText(s->longname()); } objecttyp = s->sptype() + ' ' + i18n("star"); Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString(s->mag(), 'f', 2))); //show to hundredth place Data->BVLabel->setVisible(true); Data->BVIndex->setVisible(true); if (s->getBVIndex() < 30.) { Data->BVIndex->setText(QString::number(s->getBVIndex(), 'f', 2)); } //The thumbnail image is empty, and isn't clickable for stars //Also, don't show the border around the Image QFrame. Data->Image->setFrameStyle(QFrame::NoFrame); disconnect(Data->Image, SIGNAL(clicked()), this, SLOT(updateThumbnail())); //distance if (s->distance() > 2000. || s->distance() < 0.) // parallax < 0.5 mas { Data->Distance->setText(QString(i18nc("larger than 2000 parsecs", "> 2000 pc"))); } else if (s->distance() > 50.) //show to nearest integer { Data->Distance->setText(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 0))); } else if (s->distance() > 10.0) //show to tenths place { Data->Distance->setText(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 1))); } else //show to hundredths place { Data->Distance->setText(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 2))); } //Note multiplicity/variablility in angular size label Data->AngSizeLabel->setText(QString()); Data->AngSize->setText(QString()); Data->AngSizeLabel->setFont(Data->AngSize->font()); if (s->isMultiple() && s->isVariable()) { Data->AngSizeLabel->setText(i18nc("the star is a multiple star", "multiple") + ','); Data->AngSize->setText(i18nc("the star is a variable star", "variable")); } else if (s->isMultiple()) { Data->AngSizeLabel->setText(i18nc("the star is a multiple star", "multiple")); } else if (s->isVariable()) { Data->AngSizeLabel->setText(i18nc("the star is a variable star", "variable")); } break; //end of stars case } case SkyObject::ASTEROID: //[fall through to planets] case SkyObject::COMET: //[fall through to planets] case SkyObject::MOON: //[fall through to planets] case SkyObject::PLANET: { KSPlanetBase *ps = (KSPlanetBase *)selectedObject; Data->Names->setText(ps->longname()); //Type is "G5 star" for Sun if (ps->name() == "Sun") { objecttyp = i18n("G5 star"); } else if (ps->name() == "Moon") { objecttyp = ps->translatedName(); } else if (ps->name() == i18nc("Asteroid name (optional)", "Pluto") || ps->name() == i18nc("Asteroid name (optional)", "Ceres") || ps->name() == i18nc("Asteroid name (optional)", "Eris")) { objecttyp = i18n("Dwarf planet"); } else { objecttyp = ps->typeName(); } //The moon displays illumination fraction and updateMag is called to calculate moon's current magnitude if (selectedObject->name() == "Moon") { Data->IllumLabel->setVisible(true); Data->Illumination->setVisible(true); Data->Illumination->setText( QString("%1 %").arg(QLocale().toString(((KSMoon *)selectedObject)->illum() * 100., 'f', 0))); ((KSMoon *)selectedObject)->updateMag(); } // JM: Shouldn't we use the calculated magnitude? Disabling the following /* if(selectedObject->type() == SkyObject::COMET){ Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString( ((KSComet *)selectedObject)->getTotalMagnitudeParameter(), 'f', 2))); //show to hundredth place } else{*/ Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString(ps->mag(), 'f', 2))); //show to hundredth place //} //Distance from Earth. The moon requires a unit conversion if (ps->name() == "Moon") { Data->Distance->setText( i18nc("distance in kilometers", "%1 km", QLocale().toString(ps->rearth() * AU_KM, 'f', 2))); } else { Data->Distance->setText( i18nc("distance in Astronomical Units", "%1 AU", QLocale().toString(ps->rearth(), 'f', 3))); } //Angular size; moon and sun in arcmin, others in arcsec if (ps->angSize()) { if (ps->name() == "Sun" || ps->name() == "Moon") { Data->AngSize->setText(i18nc( "angular size in arcminutes", "%1 arcmin", QLocale().toString( ps->angSize(), 'f', 1))); // Needn't be a plural form because sun / moon will never contract to 1 arcminute } else { Data->AngSize->setText(i18nc("angular size in arcseconds", "%1 arcsec", QLocale().toString(ps->angSize() * 60.0, 'f', 1))); } } else { Data->AngSize->setText("--"); } break; //end of planets/comets/asteroids case } case SkyObject::SUPERNOVA: { Supernova *sup = (Supernova *)selectedObject; objecttyp = i18n("Supernova"); Data->Names->setText(sup->name()); if (sup->mag() < 99) Data->Magnitude->setText( i18nc("number in magnitudes", "%1 mag", QLocale().toString(sup->mag(), 'f', 2))); else Data->Magnitude->setText("--"); Data->DistanceLabel->setVisible(false); Data->Distance->setVisible(false); Data->AngSizeLabel->setVisible(false); Data->AngSize->setVisible(false); QLabel *discoveryDateLabel = new QLabel(i18n("Discovery Date:"), this); QLabel *discoveryDate = new QLabel(sup->getDate(), this); Data->dataGridLayout->addWidget(discoveryDateLabel, 1, 0); Data->dataGridLayout->addWidget(discoveryDate, 1, 1); QLabel *typeLabel = new QLabel(i18n("Type:"), this); QLabel *type = new QLabel(sup->getType(), this); Data->dataGridLayout->addWidget(typeLabel, 2, 0); Data->dataGridLayout->addWidget(type, 2, 1); QLabel *hostGalaxyLabel = new QLabel(i18n("Host Galaxy:"), this); QLabel *hostGalaxy = new QLabel(sup->getHostGalaxy().isEmpty() ? "--" : sup->getHostGalaxy(), this); Data->dataGridLayout->addWidget(hostGalaxyLabel, 3, 0); Data->dataGridLayout->addWidget(hostGalaxy, 3, 1); QLabel *redShiftLabel = new QLabel(i18n("Red Shift:"), this); QLabel *redShift = new QLabel( (sup->getRedShift() < 99) ? QString::number(sup->getRedShift(), 'f', 2) : QString("--"), this); Data->dataGridLayout->addWidget(redShiftLabel, 4, 0); Data->dataGridLayout->addWidget(redShift, 4, 1); break; } default: //deep-sky objects { DeepSkyObject *dso = (DeepSkyObject *)selectedObject; //Show all names recorded for the object QStringList nameList; if (!dso->longname().isEmpty() && dso->longname() != dso->name()) { nameList.append(dso->translatedLongName()); nameList.append(dso->translatedName()); } else { nameList.append(dso->translatedName()); } if (!dso->translatedName2().isEmpty()) { nameList.append(dso->translatedName2()); } if (dso->ugc() != 0) { nameList.append(QString("UGC %1").arg(dso->ugc())); } if (dso->pgc() != 0) { nameList.append(QString("PGC %1").arg(dso->pgc())); } Data->Names->setText(nameList.join(",")); objecttyp = dso->typeName(); if (dso->type() == SkyObject::RADIO_SOURCE) { Q_ASSERT(dso->customCatalog()); // the in-built catalogs don't have radio sources Data->MagLabel->setText( i18nc("integrated flux at a frequency", "Flux(%1):", dso->customCatalog()->fluxFrequency())); Data->Magnitude->setText(i18nc("integrated flux value", "%1 %2", QLocale().toString(dso->flux(), 'f', 1), dso->customCatalog()->fluxUnit())); //show to tenths place } else if (dso->mag() > 90.0) { Data->Magnitude->setText("--"); } else { Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString(dso->mag(), 'f', 1))); //show to tenths place } //No distances at this point... Data->Distance->setText("--"); //Only show decimal place for small angular sizes if (dso->a() > 10.0) { Data->AngSize->setText( i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 0))); } else if (dso->a()) { Data->AngSize->setText( i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 1))); } else { Data->AngSize->setText("--"); } break; } } // Add specifics data switch (selectedObject->type()) { case SkyObject::ASTEROID: { KSAsteroid *ast = dynamic_cast(selectedObject); // Show same specifics data as comets DataComet = new DataCometWidget(this); Data->IncludeData->layout()->addWidget(DataComet); // Perihelion DataComet->Perihelion->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(ast->getPerihelion()))); // Earth MOID if (ast->getEarthMOID() == 0) DataComet->EarthMOID->setText("--"); else DataComet->EarthMOID->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(ast->getEarthMOID()))); // Orbit ID DataComet->OrbitID->setText(ast->getOrbitID()); // Orbit Class DataComet->OrbitClass->setText(ast->getOrbitClass()); // NEO if (ast->isNEO()) DataComet->NEO->setText(i18n("Yes")); else DataComet->NEO->setText(i18n("No")); // Albedo if (ast->getAlbedo() == 0.0) DataComet->Albedo->setText("--"); else DataComet->Albedo->setText(QString::number(ast->getAlbedo())); // Diameter if (ast->getDiameter() == 0.0) DataComet->Diameter->setText("--"); else DataComet->Diameter->setText(i18nc("Diameter in kilometers", "%1 km", QString::number(ast->getDiameter()))); // Dimensions if (ast->getDimensions().isEmpty()) DataComet->Dimensions->setText("--"); else DataComet->Dimensions->setText(i18nc("Dimension in kilometers", "%1 km", ast->getDimensions())); // Rotation period if (ast->getRotationPeriod() == 0.0) DataComet->Rotation->setText("--"); else DataComet->Rotation->setText(i18nc("Rotation period in hours", "%1 h", QString::number(ast->getRotationPeriod()))); // Period if (ast->getPeriod() == 0.0) DataComet->Period->setText("--"); else DataComet->Period->setText(i18nc("Orbit period in years", "%1 y", QString::number(ast->getPeriod()))); break; } case SkyObject::COMET: { KSComet *com = dynamic_cast(selectedObject); DataComet = new DataCometWidget(this); Data->IncludeData->layout()->addWidget(DataComet); // Perihelion DataComet->Perihelion->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(com->getPerihelion()))); // Earth MOID if (com->getEarthMOID() == 0) DataComet->EarthMOID->setText("--"); else DataComet->EarthMOID->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(com->getEarthMOID()))); // Orbit ID DataComet->OrbitID->setText(com->getOrbitID()); // Orbit Class DataComet->OrbitClass->setText(com->getOrbitClass()); // NEO if (com->isNEO()) DataComet->NEO->setText(i18n("Yes")); else DataComet->NEO->setText(i18n("No")); // Albedo if (com->getAlbedo() == 0.0) DataComet->Albedo->setText("--"); else DataComet->Albedo->setText(QString::number(com->getAlbedo())); // Diameter if (com->getDiameter() == 0.0) DataComet->Diameter->setText("--"); else DataComet->Diameter->setText(i18nc("Diameter in kilometers", "%1 km", QString::number(com->getDiameter()))); // Dimensions if (com->getDimensions().isEmpty()) DataComet->Dimensions->setText("--"); else DataComet->Dimensions->setText(i18nc("Dimension in kilometers", "%1 km", com->getDimensions())); // Rotation period if (com->getRotationPeriod() == 0.0) DataComet->Rotation->setText("--"); else DataComet->Rotation->setText(i18nc("Rotation period in hours", "%1 h", QString::number(com->getRotationPeriod()))); // Period if (com->getPeriod() == 0.0) DataComet->Period->setText("--"); else DataComet->Period->setText(i18nc("Orbit period in years", "%1 y", QString::number(com->getPeriod()))); break; } } //Common to all types: QString cname = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(selectedObject); if (selectedObject->type() != SkyObject::CONSTELLATION) { cname = i18nc("%1 type of sky object (planet, asteroid etc), %2 name of a constellation", "%1 in %2", objecttyp, cname); } Data->ObjectTypeInConstellation->setText(cname); } void DetailDialog::createPositionTab(const KStarsDateTime &ut, GeoLocation *geo) { Pos = new PositionWidget(this); addPage(Pos, i18n("Position")); Pos->CoordTitle->setPalette(titlePalette); Pos->RSTTitle->setPalette(titlePalette); KStarsData *data = KStarsData::Instance(); //Coordinates Section: //Don't use KLocale::formatNumber() for the epoch string, //because we don't want a thousands-place separator! selectedObject->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); QString sEpoch = QString::number(KStarsDateTime::jdToEpoch(selectedObject->getLastPrecessJD()), 'f', 1); //Replace the decimal point with localized decimal symbol sEpoch.replace('.', QLocale().decimalPoint()); // Is this necessary? -- asimha Oct 2016 /*qDebug() << (selectedObject->deprecess(data->updateNum())).ra0().toHMSString() << (selectedObject->deprecess(data->updateNum())).dec0().toDMSString() << endl;*/ //qDebug() << selectedObject->ra().toHMSString() << selectedObject->dec().toDMSString() << endl; Pos->RALabel->setText(i18n("RA (%1):", sEpoch)); Pos->DecLabel->setText(i18n("DE (%1):", sEpoch)); Pos->RA->setText(selectedObject->ra().toHMSString(false, true)); Pos->Dec->setText(selectedObject->dec().toDMSString(false, false, true)); selectedObject->EquatorialToHorizontal(data->lst(), data->geo()->lat()); Pos->Az->setText(selectedObject->az().toDMSString()); dms a; if (Options::useAltAz()) a = selectedObject->alt(); else a = selectedObject->altRefracted(); Pos->Alt->setText(a.toDMSString()); // Display the RA0 and Dec0 for objects that are outside the solar system // 2017-09-10 JM: Exception added for asteroids and comets since we have J2000 for them. // Maybe others? Pos->RA0->setText(selectedObject->ra0().toHMSString(false, true)); Pos->Dec0->setText(selectedObject->dec0().toDMSString(false, false, true)); #if 0 if (!selectedObject->isSolarSystem() || selectedObject->type() == SkyObject::COMET || selectedObject->type() == SkyObject::ASTEROID) { Pos->RA0->setText(selectedObject->ra0().toHMSString()); Pos->Dec0->setText(selectedObject->dec0().toDMSString()); } else { Pos->RA0->setText("--"); Pos->Dec0->setText("--"); } #endif //Hour Angle can be negative, but dms HMS expressions cannot. //Here's a kludgy workaround: dms lst = geo->GSTtoLST(ut.gst()); dms ha(lst.Degrees() - selectedObject->ra().Degrees()); QChar sgn('+'); if (ha.Hours() > 12.0) { ha.setH(24.0 - ha.Hours()); sgn = '-'; } Pos->HA->setText(QString("%1%2").arg(sgn).arg(ha.toHMSString())); //Airmass is approximated as the secant of the zenith distance, //equivalent to 1./sin(Alt). Beware of Inf at Alt=0! if (selectedObject->alt().Degrees() > 0.0) Pos->Airmass->setText(QLocale().toString(selectedObject->airmass(), 'f', 2)); else Pos->Airmass->setText("--"); //Rise/Set/Transit Section: //Prepare time/position variables QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time dms raz = selectedObject->riseSetTimeAz(ut, geo, true); //true = use rise time //If transit time is before rise time, use transit time for tomorrow QTime tt = selectedObject->transitTime(ut, geo); dms talt = selectedObject->transitAltitude(ut, geo); if (tt < rt) { tt = selectedObject->transitTime(ut.addDays(1), geo); talt = selectedObject->transitAltitude(ut.addDays(1), geo); } //If set time is before rise time, use set time for tomorrow QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time dms saz = selectedObject->riseSetTimeAz(ut, geo, false); //false = use set time if (st < rt) { st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time saz = selectedObject->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time } if (rt.isValid()) { Pos->TimeRise->setText(QString().sprintf("%02d:%02d", rt.hour(), rt.minute())); Pos->TimeSet->setText(QString().sprintf("%02d:%02d", st.hour(), st.minute())); Pos->AzRise->setText(raz.toDMSString()); Pos->AzSet->setText(saz.toDMSString()); } else { if (selectedObject->alt().Degrees() > 0.0) { Pos->TimeRise->setText(i18n("Circumpolar")); Pos->TimeSet->setText(i18n("Circumpolar")); } else { Pos->TimeRise->setText(i18n("Never rises")); Pos->TimeSet->setText(i18n("Never rises")); } Pos->AzRise->setText(i18nc("Not Applicable", "N/A")); Pos->AzSet->setText(i18nc("Not Applicable", "N/A")); } Pos->TimeTransit->setText(QString().sprintf("%02d:%02d", tt.hour(), tt.minute())); Pos->AltTransit->setText(talt.toDMSString()); // Restore the position and other time-dependent parameters selectedObject->recomputeCoords(ut, geo); } void DetailDialog::createLinksTab() { // don't create a link tab for an unnamed star if (selectedObject->name() == QString("star")) return; Links = new LinksWidget(this); addPage(Links, i18n("Links")); Links->InfoTitle->setPalette(titlePalette); Links->ImagesTitle->setPalette(titlePalette); foreach (const QString &s, selectedObject->InfoTitle()) Links->InfoTitleList->addItem(i18nc("Image/info menu item (should be translated)", s.toLocal8Bit())); //Links->InfoTitleList->setCurrentRow(0); foreach (const QString &s, selectedObject->ImageTitle()) Links->ImageTitleList->addItem(i18nc("Image/info menu item (should be translated)", s.toLocal8Bit())); // Signals/Slots connect(Links->ViewButton, SIGNAL(clicked()), this, SLOT(viewLink())); connect(Links->AddLinkButton, SIGNAL(clicked()), this, SLOT(addLink())); connect(Links->EditLinkButton, SIGNAL(clicked()), this, SLOT(editLinkDialog())); connect(Links->RemoveLinkButton, SIGNAL(clicked()), this, SLOT(removeLinkDialog())); // When an item is selected in info list, selected items are cleared image list. connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(setCurrentLink(QListWidgetItem*))); connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), Links->ImageTitleList, SLOT(clearSelection())); // vice versa connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(setCurrentLink(QListWidgetItem*))); connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), Links->InfoTitleList, SLOT(clearSelection())); connect(Links->InfoTitleList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(viewLink())); connect(Links->ImageTitleList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(viewLink())); connect(Links->InfoTitleList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); connect(Links->ImageTitleList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); updateLists(); } void DetailDialog::addLink() { if (!selectedObject) return; QPointer adialog = new AddLinkDialog(this, selectedObject->name()); QString entry; QFile file; if (adialog->exec() == QDialog::Accepted) { if (adialog->isImageLink()) { //Add link to object's ImageList, and descriptive text to its ImageTitle list selectedObject->ImageList().append(adialog->url()); selectedObject->ImageTitle().append(adialog->desc()); //Also, update the user's custom image links database //check for user's image-links database. If it doesn't exist, create it. file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat"); //determine filename in local user KDE directory tree. if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { QString message = i18n("Custom image-links file could not be opened.\nLink cannot be recorded for future sessions."); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); delete adialog; return; } else { entry = selectedObject->name() + ':' + adialog->desc() + ':' + adialog->url(); QTextStream stream(&file); stream << entry << endl; file.close(); updateLists(); } } else { selectedObject->InfoList().append(adialog->url()); selectedObject->InfoTitle().append(adialog->desc()); //check for user's image-links database. If it doesn't exist, create it. file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat"); //determine filename in local user KDE directory tree. if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { QString message = i18n( "Custom information-links file could not be opened.\nLink cannot be recorded for future sessions."); KMessageBox::sorry(nullptr, message, i18n("Could not Open File")); delete adialog; return; } else { entry = selectedObject->name() + ':' + adialog->desc() + ':' + adialog->url(); QTextStream stream(&file); stream << entry << endl; file.close(); updateLists(); } } } delete adialog; } void DetailDialog::createAdvancedTab() { // Don't create an adv tab for an unnamed star or if advinterface file failed loading // We also don't need adv dialog for solar system objects. if (selectedObject->name() == QString("star") || KStarsData::Instance()->avdTree().isEmpty() || selectedObject->type() == SkyObject::PLANET || selectedObject->type() == SkyObject::COMET || selectedObject->type() == SkyObject::ASTEROID) return; Adv = new DatabaseWidget(this); addPage(Adv, i18n("Advanced")); connect(Adv->ADVTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(viewADVData())); populateADVTree(); } void DetailDialog::createLogTab() { //Don't create a log tab for an unnamed star if (selectedObject->name() == QString("star")) return; // Log Tab Log = new LogWidget(this); addPage(Log, i18n("Log")); Log->LogTitle->setPalette(titlePalette); if (selectedObject->userLog().isEmpty()) Log->UserLog->setText( i18n("Record here observation logs and/or data on %1.", selectedObject->translatedName())); else Log->UserLog->setText(selectedObject->userLog()); //Automatically save the log contents when the widget loses focus connect(Log->UserLog, SIGNAL(focusOut()), this, SLOT(saveLogData())); } void DetailDialog::setCurrentLink(QListWidgetItem *it) { m_CurrentLink = it; } void DetailDialog::viewLink() { QString URL; if (m_CurrentLink == nullptr) return; if (m_CurrentLink->listWidget() == Links->InfoTitleList) { URL = QString(selectedObject->InfoList().at(Links->InfoTitleList->row(m_CurrentLink))); } else if (m_CurrentLink->listWidget() == Links->ImageTitleList) { URL = QString(selectedObject->ImageList().at(Links->ImageTitleList->row(m_CurrentLink))); } if (!URL.isEmpty()) QDesktopServices::openUrl(QUrl(URL)); } void DetailDialog::updateLists() { Links->InfoTitleList->clear(); Links->ImageTitleList->clear(); foreach (const QString &s, selectedObject->InfoTitle()) Links->InfoTitleList->addItem(s); foreach (const QString &s, selectedObject->ImageTitle()) Links->ImageTitleList->addItem(s); updateButtons(); } void DetailDialog::updateButtons() { bool anyLink = false; if (!Links->InfoTitleList->selectedItems().isEmpty() || !Links->ImageTitleList->selectedItems().isEmpty()) anyLink = true; // Buttons could be disabled if lists are initially empty, we enable and disable them here // depending on the current status of the list. Links->ViewButton->setEnabled(anyLink); Links->EditLinkButton->setEnabled(anyLink); Links->RemoveLinkButton->setEnabled(anyLink); } void DetailDialog::editLinkDialog() { int type = 0, row = 0; QString search_line, replace_line, currentItemTitle, currentItemURL; if (m_CurrentLink == nullptr) return; QDialog editDialog(this); editDialog.setWindowTitle(i18n("Edit Link")); QVBoxLayout *mainLayout = new QVBoxLayout; QFrame editFrame(&editDialog); mainLayout->addWidget(&editFrame); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), &editDialog, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), &editDialog, SLOT(reject())); editDialog.setLayout(mainLayout); if (m_CurrentLink->listWidget() == Links->InfoTitleList) { row = Links->InfoTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->InfoList().at(row); search_line = selectedObject->name(); search_line += ':'; search_line += currentItemTitle; search_line += ':'; search_line += currentItemURL; type = 0; } else if (m_CurrentLink->listWidget() == Links->ImageTitleList) { row = Links->ImageTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->ImageList().at(row); search_line = selectedObject->name(); search_line += ':'; search_line += currentItemTitle; search_line += ':'; search_line += currentItemURL; type = 1; } else return; QLineEdit editNameField(&editFrame); editNameField.setObjectName("nameedit"); editNameField.home(false); editNameField.setText(currentItemTitle); QLabel editLinkURL(i18n("URL:"), &editFrame); QLineEdit editLinkField(&editFrame); editLinkField.setObjectName("urledit"); editLinkField.home(false); editLinkField.setText(currentItemURL); QVBoxLayout vlay(&editFrame); vlay.setObjectName("vlay"); QHBoxLayout editLinkLayout(&editFrame); editLinkLayout.setObjectName("editlinklayout"); editLinkLayout.addWidget(&editLinkURL); editLinkLayout.addWidget(&editLinkField); vlay.addWidget(&editNameField); vlay.addLayout(&editLinkLayout); bool go(true); // If user presses cancel then skip the action if (editDialog.exec() != QDialog::Accepted) go = false; // If nothing changed, skip th action if (editLinkField.text() == currentItemURL && editNameField.text() == currentItemTitle) go = false; if (go) { replace_line = selectedObject->name() + ':' + editNameField.text() + ':' + editLinkField.text(); // Info Link if (type == 0) { selectedObject->InfoTitle().replace(row, editNameField.text()); selectedObject->InfoList().replace(row, editLinkField.text()); // Image Links } else { selectedObject->ImageTitle().replace(row, editNameField.text()); selectedObject->ImageList().replace(row, editLinkField.text()); } // Update local files updateLocalDatabase(type, search_line, replace_line); // Set focus to the same item again if (type == 0) Links->InfoTitleList->setCurrentRow(row); else Links->ImageTitleList->setCurrentRow(row); } } void DetailDialog::removeLinkDialog() { int type = 0, row = 0; QString currentItemURL, currentItemTitle, LineEntry, TempFileName; QFile URLFile; QTemporaryFile TempFile; TempFile.setAutoRemove(false); TempFile.open(); TempFileName = TempFile.fileName(); if (m_CurrentLink == nullptr) return; if (m_CurrentLink->listWidget() == Links->InfoTitleList) { row = Links->InfoTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->InfoList()[row]; LineEntry = selectedObject->name(); LineEntry += ':'; LineEntry += currentItemTitle; LineEntry += ':'; LineEntry += currentItemURL; type = 0; } else if (m_CurrentLink->listWidget() == Links->ImageTitleList) { row = Links->ImageTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->ImageList()[row]; LineEntry = selectedObject->name(); LineEntry += ':'; LineEntry += currentItemTitle; LineEntry += ':'; LineEntry += currentItemURL; type = 1; } else return; if (KMessageBox::warningContinueCancel(nullptr, i18n("Are you sure you want to remove the %1 link?", currentItemTitle), i18n("Delete Confirmation"), KStandardGuiItem::del()) != KMessageBox::Continue) return; if (type == 0) { selectedObject->InfoTitle().removeAt(row); selectedObject->InfoList().removeAt(row); } else { selectedObject->ImageTitle().removeAt(row); selectedObject->ImageList().removeAt(row); } // Remove link from file updateLocalDatabase(type, LineEntry); // Set focus to the 1st item in the list if (type == 0) Links->InfoTitleList->clearSelection(); else Links->ImageTitleList->clearSelection(); } void DetailDialog::updateLocalDatabase(int type, const QString &search_line, const QString &replace_line) { QString TempFileName, file_line; QFile URLFile; QTemporaryFile TempFile; TempFile.setAutoRemove(false); TempFile.open(); QTextStream *temp_stream = nullptr; QTextStream *out_stream = nullptr; bool replace = !replace_line.isEmpty(); if (search_line.isEmpty()) return; TempFileName = TempFile.fileName(); switch (type) { // Info Links case 0: // Get name for our local info_url file URLFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat"); break; // Image Links case 1: // Get name for our local info_url file URLFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat"); break; } // Copy URL file to temp file KIO::file_copy(QUrl::fromLocalFile(URLFile.fileName()), QUrl::fromLocalFile(TempFileName), -1, KIO::Overwrite | KIO::HideProgressInfo); if (!URLFile.open(QIODevice::WriteOnly)) { qDebug() << "DetailDialog: Failed to open " << URLFile.fileName(); qDebug() << "KStars cannot save to user database"; return; } // Get streams; temp_stream = new QTextStream(&TempFile); out_stream = new QTextStream(&URLFile); while (!temp_stream->atEnd()) { file_line = temp_stream->readLine(); // If we find a match, either replace, or remove (by skipping). if (file_line == search_line) { if (replace) (*out_stream) << replace_line << endl; else continue; } else (*out_stream) << file_line << endl; } URLFile.close(); delete (temp_stream); delete (out_stream); updateLists(); } void DetailDialog::populateADVTree() { QTreeWidgetItem *parent = nullptr; QTreeWidgetItem *temp = nullptr; // We populate the tree iterativley, keeping track of parents as we go // This solution is more efficient than the previous recursion algorithm. foreach (ADVTreeData *item, KStarsData::Instance()->avdTree()) { switch (item->Type) { // Top Level case 0: temp = new QTreeWidgetItem(parent, QStringList(i18nc("Advanced URLs: description or category", item->Name.toLocal8Bit().data()))); if (parent == nullptr) Adv->ADVTree->addTopLevelItem(temp); parent = temp; break; // End of top level case 1: if (parent != nullptr) parent = parent->parent(); break; // Leaf case 2: new QTreeWidgetItem(parent, QStringList(i18nc("Advanced URLs: description or category", item->Name.toLocal8Bit().data()))); break; } } } void DetailDialog::viewADVData() { QString link; QTreeWidgetItem *current = Adv->ADVTree->currentItem(); //If the item has children or is invalid, do nothing if (!current || current->childCount() > 0) return; foreach (ADVTreeData *item, KStarsData::Instance()->avdTree()) { if (item->Name == current->text(0)) { link = item->Link; link = parseADVData(link); QDesktopServices::openUrl(QUrl(link)); return; } } } QString DetailDialog::parseADVData(const QString &inlink) { QString link = inlink; QString subLink; int index; if ((index = link.indexOf("KSOBJ")) != -1) { link.remove(index, 5); link = link.insert(index, selectedObject->name()); } if ((index = link.indexOf("KSRA")) != -1) { link.remove(index, 4); subLink.sprintf("%02d%02d%02d", selectedObject->ra0().hour(), selectedObject->ra0().minute(), selectedObject->ra0().second()); subLink = subLink.insert(2, "%20"); subLink = subLink.insert(7, "%20"); link = link.insert(index, subLink); } if ((index = link.indexOf("KSDEC")) != -1) { link.remove(index, 5); if (selectedObject->dec().degree() < 0) { subLink.sprintf("%03d%02d%02d", selectedObject->dec0().degree(), selectedObject->dec0().arcmin(), selectedObject->dec0().arcsec()); subLink = subLink.insert(3, "%20"); subLink = subLink.insert(8, "%20"); } else { subLink.sprintf("%02d%02d%02d", selectedObject->dec0().degree(), selectedObject->dec0().arcmin(), selectedObject->dec0().arcsec()); subLink = subLink.insert(0, "%2B"); subLink = subLink.insert(5, "%20"); subLink = subLink.insert(10, "%20"); } link = link.insert(index, subLink); } return link; } void DetailDialog::saveLogData() { selectedObject->saveUserLog(Log->UserLog->toPlainText()); } void DetailDialog::addToObservingList() { KStarsData::Instance()->observingList()->slotAddObject(selectedObject); } void DetailDialog::centerMap() { SkyMap::Instance()->setClickedObject(selectedObject); SkyMap::Instance()->slotCenter(); } void DetailDialog::centerTelescope() { #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 == nullptr) continue; if (bd->isConnected() == false) { KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.", gd->getDeviceName())); return; } // Display Sun warning on slew if (selectedObject && selectedObject->name() == "Sun") { if (KMessageBox::warningContinueCancel(nullptr, i18n("Danger! Viewing the Sun without adequate solar filters is dangerous and will result in permanent eye damage!")) ==KMessageBox::Cancel) return; } ISD::GDSetCommand SlewCMD(INDI_SWITCH, "ON_COORD_SET", "TRACK", ISS_ON, this); gd->setProperty(&SlewCMD); gd->runCommand(INDI_SEND_COORDS, selectedObject); return; } KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); #endif } void DetailDialog::showThumbnail() { //No image if object is a star if (selectedObject->type() == SkyObject::STAR || selectedObject->type() == SkyObject::CATALOG_STAR) { Thumbnail->scaled(Data->Image->width(), Data->Image->height()); Thumbnail->fill(Data->DataFrame->palette().color(QPalette::Window)); Data->Image->setPixmap(*Thumbnail); return; } //Try to load the object's image from disk //If no image found, load "no image" image QFile file; QString fname = "thumb-" + selectedObject->name().toLower().remove(' ') + ".png"; if (KSUtils::openDataFile(file, fname)) { file.close(); Thumbnail->load(file.fileName(), "PNG"); } else Thumbnail->load(":/images/noimage.png"); *Thumbnail = Thumbnail->scaled(Data->Image->width(), Data->Image->height(), Qt::KeepAspectRatio, Qt::FastTransformation); Data->Image->setPixmap(*Thumbnail); } void DetailDialog::updateThumbnail() { QPointer tp = new ThumbnailPicker(selectedObject, *Thumbnail, this); if (tp->exec() == QDialog::Accepted) { QString fname = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "thumb-" + selectedObject->name().toLower().remove(' ') + ".png"; Data->Image->setPixmap(*(tp->image())); //If a real image was set, save it. //If the image was unset, delete the old image on disk. if (tp->imageFound()) { bool rc = Data->Image->pixmap()->save(fname, "PNG"); if (rc == false) { KMessageBox::error(nullptr, i18n("Error: Unable to save image to %1", fname), i18n("Save Thumbnail")); } else *Thumbnail = *(Data->Image->pixmap()); } else { QFile f; f.setFileName(fname); f.remove(); } } delete tp; } DataWidget::DataWidget(QWidget *p) : QFrame(p) { setupUi(this); DataFrame->setBackgroundRole(QPalette::Base); } DataCometWidget::DataCometWidget(QWidget *p) : QFrame(p) { setupUi(this); } PositionWidget::PositionWidget(QWidget *p) : QFrame(p) { setupUi(this); CoordFrame->setBackgroundRole(QPalette::Base); RSTFrame->setBackgroundRole(QPalette::Base); } LinksWidget::LinksWidget(QWidget *p) : QFrame(p) { setupUi(this); } DatabaseWidget::DatabaseWidget(QWidget *p) : QFrame(p) { setupUi(this); } LogWidget::LogWidget(QWidget *p) : QFrame(p) { setupUi(this); } diff --git a/kstars/dialogs/detaildialog.h b/kstars/dialogs/detaildialog.h index c094557e5..7832d7a6d 100644 --- a/kstars/dialogs/detaildialog.h +++ b/kstars/dialogs/detaildialog.h @@ -1,256 +1,256 @@ /*************************************************************************** detaildialog.h - description ------------------- begin : Sun May 5 2002 copyright : (C) 2002 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_details_data.h" #include "ui_details_data_comet.h" #include "ui_details_database.h" #include "ui_details_links.h" #include "ui_details_log.h" #include "ui_details_position.h" #include #include #include #include class QListWidgetItem; class QPixmap; class DataCometWidget; class DataWidget; class GeoLocation; class KStars; class KStarsDateTime; class SkyObject; class PositionWidget; class LinksWidget; class DatabaseWidget; class LogWidget; struct ADVTreeData { QString Name; QString Link; int Type; }; /** * @class DetailDialog * DetailDialog is a window showing detailed information for a selected object. * The window is split into four Tabs: General, Links, Advanced and Log. * The General Tab displays some type-specific data about the object, as well as its * present coordinates and Rise/Set/Transit times for the current date. The Type-specific * data are: * @li Stars: common name, genetive name, Spectral type, magnitude, distance * @li Solar System: name, object type (planet/comet/asteroid), Distance, magnitude (TBD), * angular size (TBD) * @li Deep Sky: Common name, other names, object type, magnitude, angular size * * The Links Tab allows the user to manage the list of Image and Information links * listed in the object's popup menu. The Advanced Tab allows the user to query * a number of professional-grade online astronomical databases for data on the object. * The Log tab allows the user to attach their own text notes about the object. * * The General Tab includes a clickable image of the object. Clicking the image opens * a Thumbnail picker tool, which downloads a list of mages of the object from the * network, which the user may select as the new image for this objects Details window. * * @author Jason Harris, Jasem Mutlaq * @version 1.0 */ class DetailDialog : public KPageDialog { Q_OBJECT public: /** Constructor */ DetailDialog(SkyObject *o, const KStarsDateTime &ut, GeoLocation *geo, QWidget *parent = nullptr); /** Destructor */ - ~DetailDialog() override; + ~DetailDialog() override = default; /** @return pointer to the QPixmap of the object's thumbnail image */ inline QPixmap *thumbnail() { return Thumbnail.get(); } public slots: /** @short Slot to add this object to the observing list. */ void addToObservingList(); /** @short Slot to center this object in the display. */ void centerMap(); /** @short Slot to center this object in the telescope. */ void centerTelescope(); //TODO: showThumbnail() is only called in the ctor; make it private and not a slot. /** @short Slot to display the thumbnail image for the object */ void showThumbnail(); /** * @short Slot to update thumbnail image for the object, using the Thumbnail * Picker tool. * @sa ThumbnailPicker */ void updateThumbnail(); /** @short Slot for viewing the selected image or info URL in the web browser. */ void viewLink(); /** * Popup menu function: Add a custom Image or Information URL. * Opens the AddLinkDialog window. */ void addLink(); /** * @short Set the currently-selected URL resource. * * This function is needed because there are two QListWidgets, * each with its own selection. We need to know which the user selected most recently. */ void setCurrentLink(QListWidgetItem *it); /** * @short Rebuild the Image and Info URL lists for this object. * @note used when an item is added to either list. */ void updateLists(); /** * @short Open a dialog to edit a URL in either the Images or Info lists, * and update the user's *url.dat file. */ void editLinkDialog(); /** * @short remove a URL entry from either the Images or Info lists, and * update the user's *url.dat file. */ void removeLinkDialog(); /** * Open the web browser to the selected online astronomy database, * with a query to the object of this Detail Dialog. */ void viewADVData(); /** Save the User's text in the Log Tab to the userlog.dat file. */ void saveLogData(); /** Update View/Edit/Remove buttons */ void updateButtons(); private: /** Build the General Data Tab for the current object. */ void createGeneralTab(); /** Build the Position Tab for the current object. */ void createPositionTab(const KStarsDateTime &ut, GeoLocation *geo); /** * Build the Links Tab, populating the image and info lists with the * known URLs for the current Object. */ void createLinksTab(); /** Build the Advanced Tab */ void createAdvancedTab(); /** Build the Log Tab */ void createLogTab(); /** Populate the TreeView of known astronomical databases in the Advanced Tab */ void populateADVTree(); /** * Data for the Advanced Tab TreeView is stored in the file advinterface.dat. * This function parses advinterface.dat. */ QString parseADVData(const QString &link); /** * Update the local info_url and image_url files * @param type The URL type. 0 for Info Links, 1 for Images. * @param search_line The line to be search for in the local URL files * @param replace_line The replacement line once search_line is found. * @note If replace_line is empty, the function will remove search_line from the file */ void updateLocalDatabase(int type, const QString &search_line, const QString &replace_line = QString()); SkyObject *selectedObject { nullptr }; QPalette titlePalette; QListWidgetItem *m_CurrentLink { nullptr }; std::unique_ptr Thumbnail; DataWidget *Data { nullptr }; DataCometWidget *DataComet { nullptr }; PositionWidget *Pos { nullptr }; LinksWidget *Links { nullptr }; DatabaseWidget *Adv { nullptr }; LogWidget *Log { nullptr }; }; class DataWidget : public QFrame, public Ui::DetailsData { Q_OBJECT public: explicit DataWidget(QWidget *parent = nullptr); }; class DataCometWidget : public QFrame, public Ui::DetailsDataComet { Q_OBJECT public: explicit DataCometWidget(QWidget *parent = nullptr); }; class PositionWidget : public QFrame, public Ui::DetailsPosition { Q_OBJECT public: explicit PositionWidget(QWidget *parent = nullptr); }; class LinksWidget : public QFrame, public Ui::DetailsLinks { Q_OBJECT public: explicit LinksWidget(QWidget *parent = nullptr); }; class DatabaseWidget : public QFrame, public Ui::DetailsDatabase { Q_OBJECT public: explicit DatabaseWidget(QWidget *parent = nullptr); }; class LogWidget : public QFrame, public Ui::DetailsLog { Q_OBJECT public: explicit LogWidget(QWidget *parent = nullptr); }; diff --git a/kstars/dialogs/exportimagedialog.cpp b/kstars/dialogs/exportimagedialog.cpp index e10155cde..1654599eb 100644 --- a/kstars/dialogs/exportimagedialog.cpp +++ b/kstars/dialogs/exportimagedialog.cpp @@ -1,137 +1,138 @@ /*************************************************************************** exportimagedialog.cpp - K Desktop Planetarium ------------------- begin : Mon Jun 13 2011 copyright : (C) 2011 by RafaÅ‚ KuÅ‚aga email : rl.kulaga@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 -#include -#include -#include - -#include - #include "exportimagedialog.h" + +#include "imageexporter.h" #include "kstars.h" #include "skymap.h" -#include "printing/legend.h" #include "skyqpainter.h" -#include "imageexporter.h" +#include "printing/legend.h" + +#include + +#include +#include +#include +#include ExportImageDialogUI::ExportImageDialogUI(QWidget *parent) : QFrame(parent) { setupUi(this); } ExportImageDialog::ExportImageDialog(const QString &url, const QSize &size, ImageExporter *imgExporter) : QDialog((QWidget *)KStars::Instance()), m_KStars(KStars::Instance()), m_Url(url), m_Size(size) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif m_DialogUI = new ExportImageDialogUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(m_DialogUI); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(exportImage())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); QPushButton *previewB = new QPushButton(i18n("Preview image")); buttonBox->addButton(previewB, QDialogButtonBox::ActionRole); connect(previewB, SIGNAL(clicked()), this, SLOT(previewImage())); connect(m_DialogUI->addLegendCheckBox, SIGNAL(toggled(bool)), this, SLOT(switchLegendEnabled(bool))); connect(m_DialogUI->addLegendCheckBox, SIGNAL(toggled(bool)), previewB, SLOT(setEnabled(bool))); m_ImageExporter = ((imgExporter) ? imgExporter : new ImageExporter(this)); setWindowTitle(i18n("Export sky image")); setupWidgets(); } void ExportImageDialog::switchLegendEnabled(bool enabled) { m_DialogUI->legendOrientationLabel->setEnabled(enabled); m_DialogUI->legendOrientationComboBox->setEnabled(enabled); m_DialogUI->legendTypeLabel->setEnabled(enabled); m_DialogUI->legendTypeComboBox->setEnabled(enabled); m_DialogUI->legendPositionLabel->setEnabled(enabled); m_DialogUI->legendPositionComboBox->setEnabled(enabled); } void ExportImageDialog::previewImage() { updateLegendSettings(); const Legend *legend = m_ImageExporter->getLegend(); // Preview current legend settings on sky map m_KStars->map()->setLegend(Legend(*legend)); m_KStars->map()->setPreviewLegend(true); // Update sky map m_KStars->map()->forceUpdate(true); // Hide export dialog hide(); } void ExportImageDialog::setupWidgets() { m_DialogUI->addLegendCheckBox->setChecked(true); m_DialogUI->legendOrientationComboBox->addItem(i18n("Horizontal")); m_DialogUI->legendOrientationComboBox->addItem(i18n("Vertical")); QStringList types; types << i18n("Full legend") << i18n("Scale with magnitudes chart") << i18n("Only scale") << i18n("Only magnitudes") << i18n("Only symbols"); m_DialogUI->legendTypeComboBox->addItems(types); QStringList positions; positions << i18n("Upper left corner") << i18n("Upper right corner") << i18n("Lower left corner") << i18n("Lower right corner"); m_DialogUI->legendPositionComboBox->addItems(positions); } void ExportImageDialog::updateLegendSettings() { Legend::LEGEND_ORIENTATION orientation = ((m_DialogUI->legendOrientationComboBox->currentIndex() == 1) ? Legend::LO_VERTICAL : Legend::LO_HORIZONTAL); Legend::LEGEND_TYPE type = static_cast(m_DialogUI->legendTypeComboBox->currentIndex()); Legend::LEGEND_POSITION pos = static_cast(m_DialogUI->legendPositionComboBox->currentIndex()); m_ImageExporter->setLegendProperties(type, orientation, pos); } void ExportImageDialog::exportImage() { qDebug() << "Exporting sky image"; updateLegendSettings(); m_ImageExporter->includeLegend(m_DialogUI->addLegendCheckBox->isChecked()); if (!m_ImageExporter->exportImage(m_Url)) { KMessageBox::sorry(nullptr, m_ImageExporter->getLastErrorMessage(), i18n("Could not export image")); } } diff --git a/kstars/dialogs/exportimagedialog.h b/kstars/dialogs/exportimagedialog.h index 9bf0df5de..76cc7f5f9 100644 --- a/kstars/dialogs/exportimagedialog.h +++ b/kstars/dialogs/exportimagedialog.h @@ -1,79 +1,75 @@ /*************************************************************************** exportimagedialog.h - K Desktop Planetarium ------------------- begin : Mon Jun 13 2011 copyright : (C) 2011 by RafaÅ‚ KuÅ‚aga email : rl.kulaga@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 EXPORTIMAGEDIALOG_H -#define EXPORTIMAGEDIALOG_H +#pragma once #include "ui_exportimagedialog.h" #include "../printing/legend.h" #include class KStars; class QString; class QSize; class ImageExporter; // ExportImageDialog user interface. class ExportImageDialogUI : public QFrame, public Ui::ExportImageDialog { Q_OBJECT public: explicit ExportImageDialogUI(QWidget *parent = nullptr); }; /** @short Export sky image dialog. This dialog enables user to set up basic legend properties before image is exported. */ class ExportImageDialog : public QDialog { Q_OBJECT public: /**short Default constructor. Creates dialog operating on passed URL and output image width and height. *@param url URL for exported image. *@param size size of exported image. *@param imgExporter A pointer to an ImageExporter that we can use instead of creating our own. if 0, we will create our own. */ ExportImageDialog(const QString &url, const QSize &size, ImageExporter *imgExporter = nullptr); - /** @short Default destructor. */ - ~ExportImageDialog() override {} + ~ExportImageDialog() override = default; inline void setOutputUrl(const QString &url) { m_Url = url; } inline void setOutputSize(const QSize &size) { m_Size = size; } private slots: void switchLegendEnabled(bool enabled); void previewImage(); void exportImage(); void setupWidgets(); void updateLegendSettings(); private: KStars *m_KStars; ExportImageDialogUI *m_DialogUI; QString m_Url; QSize m_Size; ImageExporter *m_ImageExporter; }; - -#endif // EXPORTIMAGEDIALOG_H diff --git a/kstars/dialogs/finddialog.cpp b/kstars/dialogs/finddialog.cpp index fd954e538..8cf045047 100644 --- a/kstars/dialogs/finddialog.cpp +++ b/kstars/dialogs/finddialog.cpp @@ -1,422 +1,418 @@ /*************************************************************************** finddialog.cpp - K Desktop Planetarium ------------------- begin : Wed Jul 4 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "finddialog.h" #include "kstars.h" #include "kstarsdata.h" #include "Options.h" #include "detaildialog.h" #include "skyobjects/skyobject.h" #include "skyobjects/deepskyobject.h" #include "skycomponents/starcomponent.h" #include "skycomponents/syncedcatalogcomponent.h" #include "skycomponents/skymapcomposite.h" #include "tools/nameresolver.h" #include "skyobjectlistmodel.h" #include #include #include #include FindDialogUI::FindDialogUI(QWidget *parent) : QFrame(parent) { setupUi(this); FilterType->addItem(i18n("Any")); FilterType->addItem(i18n("Stars")); FilterType->addItem(i18n("Solar System")); FilterType->addItem(i18n("Open Clusters")); FilterType->addItem(i18n("Globular Clusters")); FilterType->addItem(i18n("Gaseous Nebulae")); FilterType->addItem(i18n("Planetary Nebulae")); FilterType->addItem(i18n("Galaxies")); FilterType->addItem(i18n("Comets")); FilterType->addItem(i18n("Asteroids")); FilterType->addItem(i18n("Constellations")); FilterType->addItem(i18n("Supernovae")); FilterType->addItem(i18n("Satellites")); SearchList->setMinimumWidth(256); SearchList->setMinimumHeight(320); } FindDialog::FindDialog(QWidget *parent) : QDialog(parent), timer(nullptr), m_targetObject(nullptr) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ui = new FindDialogUI(this); setWindowTitle(i18n("Find Object")); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); okB = buttonBox->button(QDialogButtonBox::Ok); QPushButton *detailB = new QPushButton(i18n("Details...")); buttonBox->addButton(detailB, QDialogButtonBox::ActionRole); connect(detailB, SIGNAL(clicked()), this, SLOT(slotDetails())); ui->InternetSearchButton->setVisible(Options::resolveNamesOnline()); ui->InternetSearchButton->setEnabled(false); connect(ui->InternetSearchButton, SIGNAL(clicked()), this, SLOT(slotResolve())); ui->FilterType->setCurrentIndex(0); // show all types of objects fModel = new SkyObjectListModel(this); sortModel = new QSortFilterProxyModel(ui->SearchList); sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); sortModel->setSourceModel(fModel); sortModel->setSortRole(Qt::DisplayRole); sortModel->setFilterRole(Qt::DisplayRole); sortModel->setDynamicSortFilter(true); sortModel->sort(0); ui->SearchList->setModel(sortModel); // Connect signals to slots connect(ui->SearchBox, SIGNAL(textChanged(QString)), SLOT(enqueueSearch())); connect(ui->SearchBox, SIGNAL(returnPressed()), SLOT(slotOk())); connect(ui->FilterType, SIGNAL(activated(int)), this, SLOT(enqueueSearch())); connect(ui->SearchList, SIGNAL(doubleClicked(QModelIndex)), SLOT(slotOk())); // Set focus to object name edit ui->SearchBox->setFocus(); // First create and paint dialog and then load list QTimer::singleShot(0, this, SLOT(init())); listFiltered = false; } -FindDialog::~FindDialog() -{ -} - void FindDialog::init() { ui->SearchBox->clear(); filterByType(); sortModel->sort(0); initSelection(); m_targetObject = nullptr; } void FindDialog::initSelection() { if (sortModel->rowCount() <= 0) { okB->setEnabled(false); return; } if (ui->SearchBox->text().isEmpty()) { //Pre-select the first item QModelIndex selectItem = sortModel->index(0, sortModel->filterKeyColumn(), QModelIndex()); switch (ui->FilterType->currentIndex()) { case 0: //All objects, choose Andromeda galaxy { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Andromeda Galaxy"))); selectItem = sortModel->mapFromSource(qmi); break; } case 1: //Stars, choose Aldebaran { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Aldebaran"))); selectItem = sortModel->mapFromSource(qmi); break; } case 2: //Solar system or Asteroids, choose Aaltje case 9: { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Aaltje"))); selectItem = sortModel->mapFromSource(qmi); break; } case 8: //Comets, choose 'Aarseth-Brewington (1989 W1)' { QModelIndex qmi = fModel->index(fModel->indexOf(i18n("Aarseth-Brewington (1989 W1)"))); selectItem = sortModel->mapFromSource(qmi); break; } } if (selectItem.isValid()) { ui->SearchList->selectionModel()->select(selectItem, QItemSelectionModel::ClearAndSelect); ui->SearchList->scrollTo(selectItem); ui->SearchList->setCurrentIndex(selectItem); okB->setEnabled(true); } } listFiltered = true; } void FindDialog::filterByType() { KStarsData *data = KStarsData::Instance(); switch (ui->FilterType->currentIndex()) { case 0: // All object types { QVector> allObjects; foreach (int type, data->skyComposite()->objectLists().keys()) { allObjects.append(data->skyComposite()->objectLists(SkyObject::TYPE(type))); } fModel->setSkyObjectsList(allObjects); break; } case 1: //Stars { QVector> starObjects; starObjects.append(data->skyComposite()->objectLists(SkyObject::STAR)); starObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR)); fModel->setSkyObjectsList(starObjects); break; } case 2: //Solar system { QVector> ssObjects; ssObjects.append(data->skyComposite()->objectLists(SkyObject::PLANET)); ssObjects.append(data->skyComposite()->objectLists(SkyObject::COMET)); ssObjects.append(data->skyComposite()->objectLists(SkyObject::ASTEROID)); ssObjects.append(data->skyComposite()->objectLists(SkyObject::MOON)); fModel->setSkyObjectsList(ssObjects); break; } case 3: //Open Clusters fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::OPEN_CLUSTER)); break; case 4: //Globular Clusters fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::GLOBULAR_CLUSTER)); break; case 5: //Gaseous nebulae fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::GASEOUS_NEBULA)); break; case 6: //Planetary nebula fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::PLANETARY_NEBULA)); break; case 7: //Galaxies fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::GALAXY)); break; case 8: //Comets fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::COMET)); break; case 9: //Asteroids fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::ASTEROID)); break; case 10: //Constellations fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::CONSTELLATION)); break; case 11: //Supernovae fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::SUPERNOVA)); break; case 12: //Satellites fModel->setSkyObjectsList(data->skyComposite()->objectLists(SkyObject::SATELLITE)); break; } } void FindDialog::filterList() { QString SearchText = processSearchText(); sortModel->setFilterFixedString(SearchText); ui->InternetSearchButton->setText(i18n("or search the internet for %1", SearchText)); filterByType(); initSelection(); //Select the first item in the list that begins with the filter string if (!SearchText.isEmpty()) { QStringList mItems = fModel->filter(QRegExp('^' + SearchText, Qt::CaseInsensitive)); mItems.sort(); if (mItems.size()) { QModelIndex qmi = fModel->index(fModel->indexOf(mItems[0])); QModelIndex selectItem = sortModel->mapFromSource(qmi); if (selectItem.isValid()) { ui->SearchList->selectionModel()->select(selectItem, QItemSelectionModel::ClearAndSelect); ui->SearchList->scrollTo(selectItem); ui->SearchList->setCurrentIndex(selectItem); okB->setEnabled(true); } } ui->InternetSearchButton->setEnabled(!mItems.contains( SearchText)); // Disable searching the internet when an exact match for SearchText exists in KStars } else ui->InternetSearchButton->setEnabled(false); listFiltered = true; } SkyObject *FindDialog::selectedObject() const { QModelIndex i = ui->SearchList->currentIndex(); QVariant sObj = sortModel->data(sortModel->index(i.row(), 0), SkyObjectListModel::SkyObjectRole); return reinterpret_cast(sObj.value()); } void FindDialog::enqueueSearch() { listFiltered = false; if (timer) { timer->stop(); } else { timer = new QTimer(this); timer->setSingleShot(true); connect(timer, SIGNAL(timeout()), this, SLOT(filterList())); } timer->start(500); } // Process the search box text to replace equivalent names like "m93" with "m 93" QString FindDialog::processSearchText() { QRegExp re; QString searchtext = ui->SearchBox->text(); re.setCaseSensitivity(Qt::CaseInsensitive); // If it is an NGC/IC/M catalog number, as in "M 76" or "NGC 5139", check for absence of the space re.setPattern("^(m|ngc|ic)\\s*\\d*$"); if (ui->SearchBox->text().contains(re)) { re.setPattern("\\s*(\\d+)"); searchtext.replace(re, " \\1"); re.setPattern("\\s*$"); searchtext.remove(re); re.setPattern("^\\s*"); searchtext.remove(re); } // TODO after KDE 4.1 release: // If it is a IAU standard three letter abbreviation for a constellation, then go to that constellation // Check for genetive names of stars. Example: alp CMa must go to alpha Canis Majoris return searchtext; } void FindDialog::slotOk() { //If no valid object selected, show a sorry-box. Otherwise, emit accept() SkyObject *selObj; if (!listFiltered) { filterList(); } selObj = selectedObject(); finishProcessing(selObj, Options::resolveNamesOnline()); } void FindDialog::slotResolve() { finishProcessing(nullptr, true); } void FindDialog::finishProcessing(SkyObject *selObj, bool resolve) { if (!selObj && resolve) { CatalogEntryData cedata; cedata = NameResolver::resolveName(processSearchText()); DeepSkyObject *dso = nullptr; if (!std::isnan(cedata.ra) && !std::isnan(cedata.dec)) { dso = KStarsData::Instance()->skyComposite()->internetResolvedComponent()->addObject(cedata); if (dso) qDebug() << dso->ra0().toHMSString() << ";" << dso->dec0().toDMSString(); selObj = dso; } } m_targetObject = selObj; if (selObj == nullptr) { QString message = i18n("No object named %1 found.", ui->SearchBox->text()); KMessageBox::sorry(nullptr, message, i18n("Bad object name")); } else { selObj->updateCoordsNow(KStarsData::Instance()->updateNum()); accept(); } } void FindDialog::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Escape: reject(); break; case Qt::Key_Up: { int currentRow = ui->SearchList->currentIndex().row(); if (currentRow > 0) { QModelIndex selectItem = sortModel->index(currentRow - 1, sortModel->filterKeyColumn(), QModelIndex()); ui->SearchList->selectionModel()->setCurrentIndex(selectItem, QItemSelectionModel::SelectCurrent); } break; } case Qt::Key_Down: { int currentRow = ui->SearchList->currentIndex().row(); if (currentRow < sortModel->rowCount() - 1) { QModelIndex selectItem = sortModel->index(currentRow + 1, sortModel->filterKeyColumn(), QModelIndex()); ui->SearchList->selectionModel()->setCurrentIndex(selectItem, QItemSelectionModel::SelectCurrent); } break; } } } void FindDialog::slotDetails() { if (selectedObject()) { QPointer dd = new DetailDialog(selectedObject(), KStarsData::Instance()->ut(), KStarsData::Instance()->geo(), KStars::Instance()); dd->exec(); delete dd; } } diff --git a/kstars/dialogs/finddialog.h b/kstars/dialogs/finddialog.h index 480d5117e..43815f65d 100644 --- a/kstars/dialogs/finddialog.h +++ b/kstars/dialogs/finddialog.h @@ -1,134 +1,132 @@ /*************************************************************************** finddialog.h - K Desktop Planetarium ------------------- begin : Wed Jul 4 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 FINDDIALOG_H_ -#define FINDDIALOG_H_ - -#include -#include +#pragma once #include "ui_finddialog.h" +#include +#include + class QTimer; class QStringListModel; class QSortFilterProxyModel; class SkyObjectListModel; class SkyObject; class FindDialogUI : public QFrame, public Ui::FindDialog { Q_OBJECT public: explicit FindDialogUI(QWidget *parent = nullptr); }; -/** @class FindDialog +/** + * @class FindDialog * Dialog window for finding SkyObjects by name. The dialog contains * a QListBox showing the list of named objects, a QLineEdit for filtering * the list by name, and a QCombobox for filtering the list by object type. * * @short Find Object Dialog * @author Jason Harris * @version 1.0 */ class FindDialog : public QDialog { Q_OBJECT public: - /**Constructor. Creates all widgets and packs them in QLayouts. Connects - * Signals and Slots. Runs initObjectList(). - */ + /** + * Constructor. Creates all widgets and packs them in QLayouts. Connects + * Signals and Slots. Runs initObjectList(). + */ explicit FindDialog(QWidget *parent = nullptr); - /** Destructor */ - ~FindDialog() override; + ~FindDialog() override = default; /** - * @return the target object (need not be the same as currently selected object!) - * - * @note Avoid using selectedObject() - */ + * @return the target object (need not be the same as currently selected object!) + * + * @note Avoid using selectedObject() + */ inline SkyObject *targetObject() { return m_targetObject; } public slots: - /**When Text is entered in the QLineEdit, filter the List of objects - * so that only objects which start with the filter text are shown. - */ + /** + * When Text is entered in the QLineEdit, filter the List of objects + * so that only objects which start with the filter text are shown. + */ void filterList(); - //FIXME: Still valid for QDialog? i.e., does QDialog have a slotOk() ? + // FIXME: Still valid for QDialog? i.e., does QDialog have a slotOk() ? /** - *Overloading the Standard QDialogBase slotOk() to show a "sorry" - *message box if no object is selected and internet resolution was - *disabled/failed when the user presses Ok. The window is not - *closed in this case. - */ + * Overloading the Standard QDialogBase slotOk() to show a "sorry" + * message box if no object is selected and internet resolution was + * disabled/failed when the user presses Ok. The window is not + * closed in this case. + */ void slotOk(); /** - * @short This slot resolves the object on the internet, ignoring the selection on the list - */ + * @short This slot resolves the object on the internet, ignoring the selection on the list + */ void slotResolve(); private slots: /** Init object list after opening dialog. */ void init(); /** Set the selected item to the first item in the list */ void initSelection(); void enqueueSearch(); void slotDetails(); protected: - /**Process Keystrokes. The Up and Down arrow keys are used to select the - * Previous/Next item in the listbox of named objects. The Esc key closes - * the window with no selection, using reject(). - * @param e The QKeyEvent pointer - */ + /** + * Process Keystrokes. The Up and Down arrow keys are used to select the + * Previous/Next item in the listbox of named objects. The Esc key closes + * the window with no selection, using reject(). + * @param e The QKeyEvent pointer + */ void keyPressEvent(QKeyEvent *e) override; /** @return the currently-selected item from the listbox of named objects */ SkyObject *selectedObject() const; private: - /** @short Do some post processing on the search text to interpret what the user meant - * This could include replacing text like "m93" with "m 93" - */ + /** + * @short Do some post processing on the search text to interpret what the user meant + * This could include replacing text like "m93" with "m 93" + */ QString processSearchText(); - /** - * @short Finishes the processing towards closing the dialog initiated by slotOk() or slotResolve() - */ + /** @short Finishes the processing towards closing the dialog initiated by slotOk() or slotResolve() */ void finishProcessing(SkyObject *selObj = nullptr, bool resolve = true); - /** @short pre-filter the list of objects according to the - * selected object type. - */ + /** @short pre-filter the list of objects according to the selected object type. */ void filterByType(); - FindDialogUI *ui; - SkyObjectListModel *fModel; - QSortFilterProxyModel *sortModel; - QTimer *timer; - bool listFiltered; - QPushButton *okB; - SkyObject *m_targetObject; + FindDialogUI *ui { nullptr }; + SkyObjectListModel *fModel { nullptr }; + QSortFilterProxyModel *sortModel { nullptr }; + QTimer *timer { nullptr }; + bool listFiltered { false }; + QPushButton *okB { nullptr }; + SkyObject *m_targetObject { nullptr }; }; -#endif diff --git a/kstars/dialogs/fovdialog.h b/kstars/dialogs/fovdialog.h index 2c4774c4b..5f05e288a 100644 --- a/kstars/dialogs/fovdialog.h +++ b/kstars/dialogs/fovdialog.h @@ -1,129 +1,129 @@ /*************************************************************************** fovdialog.h - description ------------------- begin : Fri 05 Sept 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 FOVDIALOG_H_ #define FOVDIALOG_H_ #include #include #include #include "fov.h" #include "ui_fovdialog.h" #include "ui_newfov.h" class FOVDialogUI : public QFrame, public Ui::FOVDialog { Q_OBJECT public: explicit FOVDialogUI(QWidget *parent = nullptr); }; class NewFOVUI : public QFrame, public Ui::NewFOV { Q_OBJECT public: explicit NewFOVUI(QWidget *parent = nullptr); }; /** @class FOVDialog * FOVDialog is dialog to select a Field-of-View indicator (or create a new one) *@author Jason Harris *@version 1.0 */ class FOVDialog : public QDialog { Q_OBJECT public: explicit FOVDialog(QWidget *parent = nullptr); ~FOVDialog() override; private slots: void slotNewFOV(); void slotEditFOV(); void slotRemoveFOV(); void slotSelect(int); void slotDetectFromINDI(); private: /** Add new widget to list box */ QListWidgetItem *addListWidget(FOV *f); unsigned int currentItem() const; FOVDialogUI *fov; static int fovID; }; /** @class NewFOV Dialog for defining a new FOV symbol *@author Jason Harris *@version 1.0 */ class NewFOV : public QDialog { Q_OBJECT public: /** Create new dialog * @param parent parent widget * @fov widget to copy data from. If it's empty will create empty one. */ explicit NewFOV(QWidget *parent = nullptr, const FOV *fov = nullptr); - ~NewFOV() override {} + ~NewFOV() override = default; /** Return reference to FOV. */ const FOV &getFOV() const { return f; } public slots: void slotBinocularFOVDistanceChanged(int index); void slotUpdateFOV(); void slotComputeFOV(); void slotEyepieceAFOVChanged(int index); void slotComputeTelescopeFL(); private: FOV f; NewFOVUI *ui; QPushButton *okB; }; /** *@class TelescopeFL Dialog for calculating telescope focal length from f-number and diameter *@author Akarsh Simha *@version 1.0 */ class TelescopeFL : public QDialog { Q_OBJECT public: /** * Create a telescope focal length dialog * @param parent parent widget */ explicit TelescopeFL(QWidget *parent = nullptr); - ~TelescopeFL() override {} + ~TelescopeFL() override = default; /** * Compute and return the focal length in mm * @return focal length in mm */ double computeFL() const; private: QDoubleSpinBox *aperture, *fNumber; QComboBox *apertureUnit; }; #endif diff --git a/kstars/dialogs/timedialog.cpp b/kstars/dialogs/timedialog.cpp index ba9eefb49..ee8560f12 100644 --- a/kstars/dialogs/timedialog.cpp +++ b/kstars/dialogs/timedialog.cpp @@ -1,126 +1,126 @@ /*************************************************************************** timedialog.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "timedialog.h" +#include "geolocation.h" +#include "kstarsdata.h" +#include "kstarsdatetime.h" +#include "simclock.h" + #include -#include #include #include -#include #include #include +#include #include - -#include "kstarsdatetime.h" -#include "kstarsdata.h" -#include "simclock.h" -#include "geolocation.h" +#include TimeDialog::TimeDialog(const KStarsDateTime &now, GeoLocation *_geo, QWidget *parent, bool UTCFrame) : QDialog(parent), geo(_geo) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif UTCNow = UTCFrame; QFrame *page = new QFrame(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(page); setLayout(mainLayout); if (UTCNow) setWindowTitle(i18nc("set clock to a new time", "Set UTC Time")); else setWindowTitle(i18nc("set clock to a new time", "Set Time")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); vlay = new QVBoxLayout(page); vlay->setMargin(2); vlay->setSpacing(2); hlay = new QHBoxLayout(); //this layout will be added to the VLayout hlay->setSpacing(2); dPicker = new KDatePicker(now.date(), page); tEdit = new QTimeEdit(now.time(), page); NowButton = new QPushButton(page); NowButton->setObjectName("NowButton"); NowButton->setText(UTCNow ? i18n("UTC Now") : i18n("Now")); vlay->addWidget(dPicker, 0, nullptr); vlay->addLayout(hlay, 0); hlay->addWidget(tEdit); hlay->addWidget(NowButton); vlay->activate(); QObject::connect(NowButton, SIGNAL(clicked()), this, SLOT(setNow())); } //Add handler for Escape key to close window //Use keyReleaseEvent because keyPressEvents are already consumed //by the KDatePicker. void TimeDialog::keyReleaseEvent(QKeyEvent *kev) { switch (kev->key()) { case Qt::Key_Escape: { close(); break; } default: { kev->ignore(); break; } } } void TimeDialog::setNow(void) { KStarsDateTime dt(KStarsDateTime::currentDateTimeUtc()); if (!UTCNow) dt = geo->UTtoLT(dt); dPicker->setDate(dt.date()); tEdit->setTime(dt.time()); } QTime TimeDialog::selectedTime(void) { return tEdit->time(); } QDate TimeDialog::selectedDate(void) { return dPicker->date(); } KStarsDateTime TimeDialog::selectedDateTime(void) { return KStarsDateTime(selectedDate(), selectedTime()); } diff --git a/kstars/dialogs/timedialog.h b/kstars/dialogs/timedialog.h index 56c8bb085..5d75ba22d 100644 --- a/kstars/dialogs/timedialog.h +++ b/kstars/dialogs/timedialog.h @@ -1,95 +1,88 @@ /*************************************************************************** timedialog.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 TIMEDIALOG_H_ -#define TIMEDIALOG_H_ +#pragma once + +#include "kstarsdatetime.h" #include -#include -#include -#include #include #include - -#include "kstarsdatetime.h" +#include +#include +#include class QHBoxLayout; class QVBoxLayout; class KDatePicker; class QTimeEdit; class QPushButton; class GeoLocation; -/** @class TimeDialog - *A class for adjusting the Time and Date. Contains a KDatePicker widget - *for selecting the date, and a QTimeEdit for selecting the time. There - *is also a "Now" button for selecting the Time and Date from the system clock. - *@short Dialog for adjusting the Time and Date. - *@author Jason Harris - *@version 1.0 - */ - +/** + * @class TimeDialog + * + * A class for adjusting the Time and Date. Contains a KDatePicker widget + * for selecting the date, and a QTimeEdit for selecting the time. There + * is also a "Now" button for selecting the Time and Date from the system clock. + * + * + * @short Dialog for adjusting the Time and Date. + * @author Jason Harris + * @version 1.0 + */ class TimeDialog : public QDialog { Q_OBJECT public: /** - *Constructor. Creates widgets and packs them into QLayouts. - *Connects Signals and Slots. - */ + * Constructor. Creates widgets and packs them into QLayouts. + * Connects Signals and Slots. + */ TimeDialog(const KStarsDateTime &now, GeoLocation *_geo, QWidget *parent, bool UTCFrame = false); - /** - *Destructor (empty) - */ - ~TimeDialog() override {} + ~TimeDialog() override = default; - /** @returns a QTime object with the selected time - */ + /** @returns a QTime object with the selected time */ QTime selectedTime(void); - /** @returns a QDate object with the selected date - */ + /** @returns a QDate object with the selected date */ QDate selectedDate(void); - /** @returns a KStarsDateTime object with the selected date and time - */ + /** @returns a KStarsDateTime object with the selected date and time */ KStarsDateTime selectedDateTime(void); public slots: /** - *When the "Now" button is pressed, read the time and date - *from the system clock. Change the selected date in the KDatePicker - *to the system's date, and the displayed time - *to the system time. - */ + * When the "Now" button is pressed, read the time and date from the system clock. + * Change the selected date in the KDatePicker to the system's date, and the displayed time + * to the system time. + */ void setNow(void); protected: void keyReleaseEvent(QKeyEvent *) override; private: bool UTCNow; - QHBoxLayout *hlay; - QVBoxLayout *vlay; - KDatePicker *dPicker; - QTimeEdit *tEdit; - QPushButton *NowButton; - GeoLocation *geo; + QHBoxLayout *hlay { nullptr }; + QVBoxLayout *vlay { nullptr }; + KDatePicker *dPicker { nullptr }; + QTimeEdit *tEdit { nullptr }; + QPushButton *NowButton { nullptr }; + GeoLocation *geo { nullptr }; }; - -#endif diff --git a/kstars/ekos/align/astrometryparser.cpp b/kstars/ekos/align/astrometryparser.cpp index 828063be3..23527c625 100644 --- a/kstars/ekos/align/astrometryparser.cpp +++ b/kstars/ekos/align/astrometryparser.cpp @@ -1,21 +1,18 @@ /* Astrometry.net Parser Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "astrometryparser.h" namespace Ekos { +// At least a dtor is needed for the correct mocing AstrometryParser::AstrometryParser() { } - -AstrometryParser::~AstrometryParser() -{ -} } diff --git a/kstars/ekos/align/astrometryparser.h b/kstars/ekos/align/astrometryparser.h index e39656c93..a39de5c27 100644 --- a/kstars/ekos/align/astrometryparser.h +++ b/kstars/ekos/align/astrometryparser.h @@ -1,46 +1,43 @@ /* Astrometry.net Parser Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef ASTROMETRYPARSER_H -#define ASTROMETRYPARSER_H +#pragma once #include namespace Ekos { class Align; /** * @class AstrometryParser - * AstrometryParser is an interface for online and offline astrometry parsers. + * AstrometryParser is an interface for online and offline astrometry parsers. * * @authro Jasem Mutlaq */ class AstrometryParser : public QObject { Q_OBJECT public: AstrometryParser(); - virtual ~AstrometryParser(); + virtual ~AstrometryParser() = default; virtual void setAlign(Align *align) = 0; virtual bool init() = 0; virtual void verifyIndexFiles(double fov_x, double fov_y) = 0; virtual bool startSovler(const QString &filename, const QStringList &args, bool generated = true) = 0; virtual bool stopSolver() = 0; signals: void solverFinished(double orientation, double ra, double dec, double pixscale); void solverFailed(); }; } - -#endif // ASTROMETRYPARSER_H diff --git a/kstars/ekos/align/offlineastrometryparser.cpp b/kstars/ekos/align/offlineastrometryparser.cpp index 93e612acb..5144c6381 100644 --- a/kstars/ekos/align/offlineastrometryparser.cpp +++ b/kstars/ekos/align/offlineastrometryparser.cpp @@ -1,404 +1,400 @@ /* Astrometry.net Parser Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "offlineastrometryparser.h" -#include "ekos_align_debug.h" + #include "align.h" +#include "ekos_align_debug.h" #include "ksutils.h" #include "Options.h" #include - namespace Ekos { OfflineAstrometryParser::OfflineAstrometryParser() : AstrometryParser() { astrometryIndex[2.8] = "index-4200"; astrometryIndex[4.0] = "index-4201"; astrometryIndex[5.6] = "index-4202"; astrometryIndex[8] = "index-4203"; astrometryIndex[11] = "index-4204"; astrometryIndex[16] = "index-4205"; astrometryIndex[22] = "index-4206"; astrometryIndex[30] = "index-4207"; astrometryIndex[42] = "index-4208"; astrometryIndex[60] = "index-4209"; astrometryIndex[85] = "index-4210"; astrometryIndex[120] = "index-4211"; astrometryIndex[170] = "index-4212"; astrometryIndex[240] = "index-4213"; astrometryIndex[340] = "index-4214"; astrometryIndex[480] = "index-4215"; astrometryIndex[680] = "index-4216"; astrometryIndex[1000] = "index-4217"; astrometryIndex[1400] = "index-4218"; astrometryIndex[2000] = "index-4219"; // Reset parity on solver failure connect(this, &OfflineAstrometryParser::solverFailed, this, [&]() { parity = QString(); }); } -OfflineAstrometryParser::~OfflineAstrometryParser() -{ -} - bool OfflineAstrometryParser::init() { #ifdef Q_OS_OSX if (Options::astrometryConfFileIsInternal()) KSUtils::configureDefaultAstrometry(); #endif if (astrometryFilesOK) return true; if (astrometryNetOK() == false) { if (align && align->isEnabled()) KMessageBox::information( nullptr, i18n( "Failed to find astrometry.net binaries. Please ensure astrometry.net is installed and try again."), i18n("Missing astrometry files"), "missing_astrometry_binaries_warning"); return false; } astrometryFilesOK = true; QString solverPath; if (Options::astrometrySolverIsInternal()) solverPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/solve-field"; else solverPath = Options::astrometrySolverBinary(); QProcess solveField; solveField.start("bash", QStringList() << "-c" << (solverPath + " --help | grep Revision")); solveField.waitForFinished(); QString output = solveField.readAllStandardOutput(); qCDebug(KSTARS_EKOS_ALIGN) << "solve-field Revision" << output; if (output.isEmpty() == false) { QString version = output.mid(9, 4); align->appendLogText(i18n("Detected Astrometry.net version %1", version)); if (version <= "0.67" && Options::astrometryUseNoFITS2FITS() == false) { Options::setAstrometryUseNoFITS2FITS(true); align->appendLogText(i18n("Setting astrometry option --no-fits2fits")); } else if (version > "0.67" && Options::astrometryUseNoFITS2FITS()) { Options::setAstrometryUseNoFITS2FITS(false); align->appendLogText(i18n("Turning off option --no-fits2fits")); } } return true; } bool OfflineAstrometryParser::astrometryNetOK() { bool solverOK = false, wcsinfoOK = false; if (Options::astrometrySolverIsInternal()) { QFileInfo solverFileInfo(QCoreApplication::applicationDirPath() + "/astrometry/bin/solve-field"); solverOK = solverFileInfo.exists() && solverFileInfo.isFile(); } else { QFileInfo solverFileInfo(Options::astrometrySolverBinary()); solverOK = solverFileInfo.exists() && solverFileInfo.isFile(); } if (Options::astrometryWCSIsInternal()) { QFileInfo wcsFileInfo(QCoreApplication::applicationDirPath() + "/astrometry/bin/wcsinfo"); wcsinfoOK = wcsFileInfo.exists() && wcsFileInfo.isFile(); } else { QFileInfo wcsFileInfo(Options::astrometryWCSInfo()); wcsinfoOK = wcsFileInfo.exists() && wcsFileInfo.isFile(); } return (solverOK && wcsinfoOK); } void OfflineAstrometryParser::verifyIndexFiles(double fov_x, double fov_y) { static double last_fov_x = 0, last_fov_y = 0; if (last_fov_x == fov_x && last_fov_y == fov_y) return; last_fov_x = fov_x; last_fov_y = fov_y; double fov_lower = 0.10 * fov_x; double fov_upper = fov_x; QStringList indexFiles; QString astrometryDataDir; bool indexesOK = true; if (getAstrometryDataDir(astrometryDataDir) == false) return; QStringList nameFilter("*.fits"); QDir directory(astrometryDataDir); QStringList indexList = directory.entryList(nameFilter); QString indexSearch = indexList.join(" "); QString startIndex, lastIndex; unsigned int missingIndexes = 0; foreach (float skymarksize, astrometryIndex.keys()) { if (skymarksize >= fov_lower && skymarksize <= fov_upper) { indexFiles << astrometryIndex.value(skymarksize); if (indexSearch.contains(astrometryIndex.value(skymarksize)) == false) { if (startIndex.isEmpty()) startIndex = astrometryIndex.value(skymarksize); lastIndex = astrometryIndex.value(skymarksize); indexesOK = false; missingIndexes++; } } } if (indexesOK == false) { if (missingIndexes == 1) align->appendLogText( i18n("Index file %1 is missing. Astrometry.net would not be able to adequately solve plates until you " "install the missing index files. Download the index files from http://www.astrometry.net", startIndex)); else align->appendLogText(i18n("Index files %1 to %2 are missing. Astrometry.net would not be able to " "adequately solve plates until you install the missing index files. Download the " "index files from http://www.astrometry.net", startIndex, lastIndex)); } } bool OfflineAstrometryParser::getAstrometryDataDir(QString &dataDir) { QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); QFile confFile(confPath); if (confFile.open(QIODevice::ReadOnly) == false) { KMessageBox::error(0, i18n("Astrometry configuration file corrupted or missing: %1\nPlease set the " "configuration file full path in INDI options.", Options::astrometryConfFile())); return false; } QTextStream in(&confFile); QString line; while (!in.atEnd()) { line = in.readLine(); if (line.isEmpty() || line.startsWith('#')) continue; line = line.trimmed(); if (line.startsWith(QLatin1String("add_path"))) { dataDir = line.mid(9).trimmed(); return true; } } KMessageBox::error(0, i18n("Unable to find data dir in astrometry configuration file.")); return false; } bool OfflineAstrometryParser::startSovler(const QString &filename, const QStringList &args, bool generated) { INDI_UNUSED(generated); QStringList solverArgs = args; // Use parity if it is: 1. Already known from previous solve. 2. This is NOT a blind solve if (Options::astrometryDetectParity() && (parity.isEmpty() == false) && (args.contains("parity") == false) && (args.contains("-3") || args.contains("-L"))) solverArgs << "--parity" << parity; QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); solverArgs << "--config" << confPath; QString solutionFile = QDir::tempPath() + "/solution.wcs"; solverArgs << "-W" << solutionFile << filename; fitsFile = filename; solver.clear(); solver = new QProcess(this); #ifdef Q_OS_OSX QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH", ""); if (Options::astrometrySolverIsInternal()) { env.insert("PATH", QCoreApplication::applicationDirPath() + "/netpbm/bin:" + QCoreApplication::applicationDirPath() + "/python/bin:/usr/local/bin:" + path); env.insert("PYTHONPATH", QCoreApplication::applicationDirPath() + "/python/bin/site-packages"); } else { env.insert("PATH", "/usr/local/bin:" + path); } solver->setProcessEnvironment(env); #endif connect(solver, SIGNAL(finished(int)), this, SLOT(solverComplete(int))); connect(solver, SIGNAL(readyReadStandardOutput()), this, SLOT(logSolver())); #if QT_VERSION > QT_VERSION_CHECK(5, 6, 0) connect(solver.data(), &QProcess::errorOccurred, this, [&]() { align->appendLogText(i18n("Error starting solver: %1", solver->errorString())); emit solverFailed(); }); #else connect(solver, SIGNAL(error(QProcess::ProcessError)), this, SIGNAL(solverFailed())); #endif solverTimer.start(); QString solverPath; if (Options::astrometrySolverIsInternal()) solverPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/solve-field"; else solverPath = Options::astrometrySolverBinary(); solver->start(solverPath, solverArgs); align->appendLogText(i18n("Starting solver...")); if (Options::astrometrySolverVerbose()) { QString command = solverPath + ' ' + solverArgs.join(' '); align->appendLogText(command); } return true; } bool OfflineAstrometryParser::stopSolver() { if (solver.isNull() == false) { solver->terminate(); solver->disconnect(); } return true; } void OfflineAstrometryParser::solverComplete(int exist_status) { solver->disconnect(); // TODO use QTemporaryFile later QString solutionFile = QDir::tempPath() + "/solution.wcs"; QFileInfo solution(solutionFile); if (exist_status != 0 || solution.exists() == false) { align->appendLogText(i18n("Solver failed. Try again.")); emit solverFailed(); return; } connect(&wcsinfo, SIGNAL(finished(int)), this, SLOT(wcsinfoComplete(int))); QString wcsPath; if (Options::astrometryWCSIsInternal()) wcsPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/wcsinfo"; else wcsPath = Options::astrometryWCSInfo(); wcsinfo.start(wcsPath, QStringList(solutionFile)); } void OfflineAstrometryParser::wcsinfoComplete(int exist_status) { wcsinfo.disconnect(); if (exist_status != 0) { align->appendLogText(i18n("WCS header missing or corrupted. Solver failed.")); emit solverFailed(); return; } QString wcsinfo_stdout = wcsinfo.readAllStandardOutput(); QStringList wcskeys = wcsinfo_stdout.split(QRegExp("[\n]")); QStringList key_value; double ra = 0, dec = 0, orientation = 0, pixscale = 0; for (auto &key : wcskeys) { key_value = key.split(' '); if (key_value.size() > 1) { if (key_value[0] == "ra_center") ra = key_value[1].toDouble(); else if (key_value[0] == "dec_center") dec = key_value[1].toDouble(); else if (key_value[0] == "orientation_center") orientation = key_value[1].toDouble(); else if (key_value[0] == "pixscale") pixscale = key_value[1].toDouble(); else if (key_value[0] == "parity") parity = (key_value[1].toInt() == 0) ? "pos" : "neg"; } } int elapsed = (int)round(solverTimer.elapsed() / 1000.0); align->appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed)); emit solverFinished(orientation, ra, dec, pixscale); } void OfflineAstrometryParser::logSolver() { if (Options::astrometrySolverVerbose()) align->appendLogText(solver->readAll().trimmed()); } } diff --git a/kstars/ekos/align/offlineastrometryparser.h b/kstars/ekos/align/offlineastrometryparser.h index a36db0698..b427b4e36 100644 --- a/kstars/ekos/align/offlineastrometryparser.h +++ b/kstars/ekos/align/offlineastrometryparser.h @@ -1,62 +1,62 @@ /* Astrometry.net Parser Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "astrometryparser.h" #include #include #include #include namespace Ekos { class Align; /** * @class OfflineAstrometryParser * OfflineAstrometryParser invokes the offline astrometry.net solver to find solutions to captured images. * * @authro Jasem Mutlaq */ class OfflineAstrometryParser : public AstrometryParser { Q_OBJECT public: OfflineAstrometryParser(); - virtual ~OfflineAstrometryParser(); + virtual ~OfflineAstrometryParser() override = default; virtual void setAlign(Align *_align) { align = _align; } virtual bool init(); virtual void verifyIndexFiles(double fov_x, double fov_y); virtual bool startSovler(const QString &filename, const QStringList &args, bool generated = true); virtual bool stopSolver(); public slots: void solverComplete(int exist_status); void wcsinfoComplete(int exist_status); void logSolver(); private: bool astrometryNetOK(); bool getAstrometryDataDir(QString &dataDir); QMap astrometryIndex; QString parity; QPointer solver; QProcess wcsinfo; QTime solverTimer; QString fitsFile; bool astrometryFilesOK { false }; Align *align { nullptr }; }; } diff --git a/kstars/ekos/align/opsalign.cpp b/kstars/ekos/align/opsalign.cpp index 8df184f85..f5d211893 100644 --- a/kstars/ekos/align/opsalign.cpp +++ b/kstars/ekos/align/opsalign.cpp @@ -1,96 +1,93 @@ /* Astrometry.net Options Editor Copyright (C) 2017 Jasem Mutlaq Copyright (C) 2017 Robert Lancaster This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#include +#include "opsalign.h" +#include "align.h" #include "fov.h" -#include "opsalign.h" #include "kstars.h" -#include "align.h" #include "Options.h" +#include + namespace Ekos { OpsAlign::OpsAlign(Align *parent) : QWidget(KStars::Instance()) { setupUi(this); alignModule = parent; //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("alignsettings"); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); #ifdef Q_OS_OSX connect(kcfg_AstrometrySolverIsInternal, SIGNAL(clicked()), this, SLOT(toggleSolverInternal())); kcfg_AstrometrySolverIsInternal->setToolTip(i18n("Internal or External Plate Solver?")); if (Options::astrometrySolverIsInternal()) kcfg_AstrometrySolverBinary->setEnabled(false); connect(kcfg_AstrometryConfFileIsInternal, SIGNAL(clicked()), this, SLOT(toggleConfigInternal())); kcfg_AstrometryConfFileIsInternal->setToolTip(i18n("Internal or External astrometry.cfg?")); if (Options::astrometryConfFileIsInternal()) kcfg_AstrometryConfFile->setEnabled(false); connect(kcfg_AstrometryWCSIsInternal, SIGNAL(clicked()), this, SLOT(toggleWCSInternal())); kcfg_AstrometryWCSIsInternal->setToolTip(i18n("Internal or External wcsinfo?")); if (Options::astrometryWCSIsInternal()) kcfg_AstrometryWCSInfo->setEnabled(false); #else kcfg_AstrometrySolverIsInternal->setVisible(false); kcfg_AstrometryConfFileIsInternal->setVisible(false); kcfg_AstrometryWCSIsInternal->setVisible(false); #endif #ifdef Q_OS_WIN kcfg_AstrometrySolverBinary->setEnabled(false); kcfg_AstrometryWCSInfo->setEnabled(false); kcfg_AstrometryConfFile->setEnabled(false); #endif } -OpsAlign::~OpsAlign() -{ -} - void OpsAlign::toggleSolverInternal() { kcfg_AstrometrySolverBinary->setEnabled(!kcfg_AstrometrySolverIsInternal->isChecked()); if (kcfg_AstrometrySolverIsInternal->isChecked()) kcfg_AstrometrySolverBinary->setText("*Internal Solver*"); else kcfg_AstrometrySolverBinary->setText(KSUtils::getDefaultPath("AstrometrySolverBinary")); } void OpsAlign::toggleConfigInternal() { kcfg_AstrometryConfFile->setEnabled(!kcfg_AstrometryConfFileIsInternal->isChecked()); if (kcfg_AstrometryConfFileIsInternal->isChecked()) kcfg_AstrometryConfFile->setText("*Internal astrometry.cfg*"); else kcfg_AstrometryConfFile->setText(KSUtils::getDefaultPath("AstrometryConfFile")); } void OpsAlign::toggleWCSInternal() { kcfg_AstrometryWCSInfo->setEnabled(!kcfg_AstrometryWCSIsInternal->isChecked()); if (kcfg_AstrometryWCSIsInternal->isChecked()) kcfg_AstrometryWCSInfo->setText("*Internal wcsinfo*"); else kcfg_AstrometryWCSInfo->setText(KSUtils::getDefaultPath("AstrometryWCSInfo")); } void OpsAlign::slotApply() { emit settingsUpdated(); } } diff --git a/kstars/ekos/align/opsalign.h b/kstars/ekos/align/opsalign.h index bed588cac..9e4ac0aa5 100644 --- a/kstars/ekos/align/opsalign.h +++ b/kstars/ekos/align/opsalign.h @@ -1,48 +1,45 @@ /* Astrometry.net Options Editor Copyright (C) 2017 Jasem Mutlaq Copyright (C) 2017 Robert Lancaster This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef OPSALIGN_H -#define OPSALIGN_H - -#include +#pragma once #include "ui_opsalign.h" +#include + class KConfigDialog; namespace Ekos { class Align; class OpsAlign : public QWidget, public Ui::OpsAlign { Q_OBJECT public: explicit OpsAlign(Align *parent); - ~OpsAlign(); + virtual ~OpsAlign() override = default; protected: private slots: void toggleSolverInternal(); void toggleConfigInternal(); void toggleWCSInternal(); void slotApply(); signals: void settingsUpdated(); private: - KConfigDialog *m_ConfigDialog; - Align *alignModule; + KConfigDialog *m_ConfigDialog { nullptr }; + Align *alignModule { nullptr }; }; } - -#endif // OpsAlign_H diff --git a/kstars/ekos/align/opsastrometry.cpp b/kstars/ekos/align/opsastrometry.cpp index ea6d80ef6..72acf9bd1 100644 --- a/kstars/ekos/align/opsastrometry.cpp +++ b/kstars/ekos/align/opsastrometry.cpp @@ -1,163 +1,160 @@ /* Astrometry.net Options Editor Copyright (C) 2017 Jasem Mutlaq Copyright (C) 2017 Robert Lancaster This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#include - #include "opsastrometry.h" -#include "kstars.h" + #include "align.h" +#include "kstars.h" #include "Options.h" +#include + namespace Ekos { OpsAstrometry::OpsAstrometry(Align *parent) : QWidget(KStars::Instance()) { setupUi(this); alignModule = parent; //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("alignsettings"); dms ra, de; ra.setD(Options::astrometryPositionRA()); de.setD(Options::astrometryPositionDE()); estRA->setText(ra.toHMSString()); estDec->setText(de.toDMSString()); imageWarningLabel->setHidden(kcfg_AstrometryAutoUpdateImageScale->isChecked()); positionWarningLabel->setHidden(kcfg_AstrometryAutoUpdatePosition->isChecked()); connect(kcfg_AstrometryAutoUpdateImageScale, SIGNAL(toggled(bool)), imageWarningLabel, SLOT(setHidden(bool))); connect(kcfg_AstrometryAutoUpdatePosition, SIGNAL(toggled(bool)), positionWarningLabel, SLOT(setHidden(bool))); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); connect(updateScale, SIGNAL(clicked()), this, SLOT(slotUpdateScale())); connect(updatePosition, SIGNAL(clicked()), this, SLOT(slotUpdatePosition())); updateScale->setIcon(QIcon::fromTheme("edit-copy")); updateScale->setAttribute(Qt::WA_LayoutUsesWidgetRect); updatePosition->setIcon(QIcon::fromTheme("edit-copy")); updatePosition->setAttribute(Qt::WA_LayoutUsesWidgetRect); } -OpsAstrometry::~OpsAstrometry() -{ -} - void OpsAstrometry::showEvent(QShowEvent *) { dms ra, de; ra.setD(Options::astrometryPositionRA()); de.setD(Options::astrometryPositionDE()); estRA->setText(ra.toHMSString()); estDec->setText(de.toDMSString()); } //This updates the telescope/image field scale in the astrometry options editor to match the currently connected devices. void OpsAstrometry::slotUpdateScale() { double fov_w, fov_h, fov_pixscale; // Values in arcmins. Scale in arcsec per pixel alignModule->getFOVScale(fov_w, fov_h, fov_pixscale); if (fov_w == 0 || fov_h == 0) { kcfg_AstrometryImageScaleLow->setValue(0); kcfg_AstrometryImageScaleHigh->setValue(0); return; } switch (kcfg_AstrometryImageScaleUnits->currentIndex()) { case SCALE_DEGREES: fov_w /= 60; fov_h /= 60; kcfg_AstrometryImageScaleLow->setValue(qMin(fov_w, fov_h)); kcfg_AstrometryImageScaleHigh->setValue(qMax(fov_w, fov_h)); break; case SCALE_ARCMINUTES: kcfg_AstrometryImageScaleLow->setValue(qMin(fov_w, fov_h)); kcfg_AstrometryImageScaleHigh->setValue(qMax(fov_w, fov_h)); break; case SCALE_ARCSECPERPIX: // 10% range kcfg_AstrometryImageScaleLow->setValue(fov_pixscale * 0.9); kcfg_AstrometryImageScaleHigh->setValue(fov_pixscale * 1.1); break; default: return; } } //This updates the RA and DEC position in the astrometry options editor to match the current telescope position. void OpsAstrometry::slotUpdatePosition() { estRA->setText(alignModule->ScopeRAOut->text()); estDec->setText(alignModule->ScopeDecOut->text()); } void OpsAstrometry::slotApply() { bool raOK = false, deOK = false; dms RA = estRA->createDms(false, &raOK); dms DE = estDec->createDms(true, &deOK); if (raOK && deOK) { Options::setAstrometryPositionRA(RA.Degrees()); Options::setAstrometryPositionDE(DE.Degrees()); } QVariantMap optionsMap; if (kcfg_AstrometryUseNoVerify->isChecked()) optionsMap["noverify"] = true; if (kcfg_AstrometryUseResort->isChecked()) optionsMap["resort"] = true; if (kcfg_AstrometryUseNoFITS2FITS->isChecked()) optionsMap["nofits2fits"] = true; if (kcfg_AstrometryUseImageScale->isChecked() && kcfg_AstrometryImageScaleLow->value() > 0 && kcfg_AstrometryImageScaleHigh->value() > 0) { optionsMap["scaleL"] = kcfg_AstrometryImageScaleLow->value(); optionsMap["scaleH"] = kcfg_AstrometryImageScaleHigh->value(); optionsMap["scaleUnits"] = kcfg_AstrometryImageScaleUnits->currentText(); } if (kcfg_AstrometryUsePosition->isChecked()) { optionsMap["ra"] = RA.Degrees(); optionsMap["de"] = DE.Degrees(); optionsMap["radius"] = kcfg_AstrometryRadius->value(); } if (kcfg_AstrometryUseDownsample->isChecked()) optionsMap["downsample"] = kcfg_AstrometryDownsample->value(); if (kcfg_AstrometryCustomOptions->text().isEmpty() == false) optionsMap["custom"] = kcfg_AstrometryCustomOptions->text(); QStringList solverArgs = Align::generateOptions(optionsMap); QString options = solverArgs.join(" "); alignModule->solverOptions->setText(options); alignModule->solverOptions->setToolTip(options); } } diff --git a/kstars/ekos/align/opsastrometry.h b/kstars/ekos/align/opsastrometry.h index de2059624..258411a3b 100644 --- a/kstars/ekos/align/opsastrometry.h +++ b/kstars/ekos/align/opsastrometry.h @@ -1,53 +1,50 @@ /* Astrometry.net Options Editor Copyright (C) 2017 Jasem Mutlaq Copyright (C) 2017 Robert Lancaster This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef OPSASTROMETRY_H -#define OPSASTROMETRY_H - -#include +#pragma once #include "ui_opsastrometry.h" +#include + class KConfigDialog; namespace Ekos { class Align; class OpsAstrometry : public QWidget, public Ui::OpsAstrometry { Q_OBJECT public: enum { SCALE_DEGREES, SCALE_ARCMINUTES, SCALE_ARCSECPERPIX }; explicit OpsAstrometry(Align *parent); - ~OpsAstrometry(); + virtual ~OpsAstrometry() override = default; protected: void showEvent(QShowEvent *); private slots: void slotUpdatePosition(); void slotUpdateScale(); void slotApply(); private: - KConfigDialog *m_ConfigDialog; - Align *alignModule; + KConfigDialog *m_ConfigDialog { nullptr }; + Align *alignModule { nullptr }; }; } - -#endif // OPSASTROMETRY_H diff --git a/kstars/ekos/align/opsastrometrycfg.cpp b/kstars/ekos/align/opsastrometrycfg.cpp index 4aa571acc..f9d2f8ec3 100644 --- a/kstars/ekos/align/opsastrometrycfg.cpp +++ b/kstars/ekos/align/opsastrometrycfg.cpp @@ -1,92 +1,91 @@ + #include "opsastrometrycfg.h" -#include "ui_opsastrometrycfg.h" -#include -#include "kstars.h" + #include "align.h" +#include "kstars.h" #include "Options.h" +#include "ui_opsastrometrycfg.h" + +#include #include namespace Ekos { OpsAstrometryCfg::OpsAstrometryCfg(Align *parent) : QDialog(KStars::Instance()) { setupUi(this); alignModule = parent; //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("alignsettings"); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); connect(astrometryCFGDisplay, SIGNAL(textChanged()), SLOT(slotCFGEditorUpdated())); connect(loadCFG, SIGNAL(clicked()), this, SLOT(slotLoadCFG())); slotLoadCFG(); } -OpsAstrometryCfg::~OpsAstrometryCfg() -{ -} - void OpsAstrometryCfg::slotLoadCFG() { QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); QFile confFile(confPath); astrometryCFGLocation->setText(confPath); if (confFile.open(QIODevice::ReadOnly) == false) { KMessageBox::error(0, i18n("Astrometry configuration file corrupted or missing: %1\nPlease set the " "configuration file full path in INDI options.", Options::astrometryConfFile())); return; } QTextStream in(&confFile); currentCFGText = in.readAll(); astrometryCFGDisplay->setPlainText(currentCFGText); confFile.close(); } void OpsAstrometryCfg::slotApply() { if (currentCFGText != astrometryCFGDisplay->toPlainText()) { QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); QFile confFile(confPath); if (confFile.open(QIODevice::WriteOnly) == false) KMessageBox::error(0, i18n("Internal Astrometry Configuration File Write Error.")); else { QTextStream out(&confFile); out << astrometryCFGDisplay->toPlainText(); confFile.close(); KMessageBox::error(0, i18n("Astrometry.cfg successfully saved!")); currentCFGText = astrometryCFGDisplay->toPlainText(); } } } void OpsAstrometryCfg::slotCFGEditorUpdated() { if (currentCFGText != astrometryCFGDisplay->toPlainText()) m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true); } } diff --git a/kstars/ekos/align/opsastrometrycfg.h b/kstars/ekos/align/opsastrometrycfg.h index 97cd6dec8..8088bd108 100644 --- a/kstars/ekos/align/opsastrometrycfg.h +++ b/kstars/ekos/align/opsastrometrycfg.h @@ -1,33 +1,32 @@ -#ifndef OPSASTROMETRYCFG_H -#define OPSASTROMETRYCFG_H -#include +#pragma once + #include "ui_opsastrometrycfg.h" +#include + class KConfigDialog; namespace Ekos { class Align; class OpsAstrometryCfg : public QDialog, public Ui::OpsAstrometryCfg { Q_OBJECT public: explicit OpsAstrometryCfg(Align *parent); - ~OpsAstrometryCfg(); + virtual ~OpsAstrometryCfg() override = default; private slots: void slotLoadCFG(); void slotApply(); void slotCFGEditorUpdated(); private: - KConfigDialog *m_ConfigDialog; - Align *alignModule; + KConfigDialog *m_ConfigDialog { nullptr }; + Align *alignModule { nullptr }; QString currentCFGText; }; } - -#endif // OPSASTROMETRYCFG_H diff --git a/kstars/ekos/align/opsastrometryindexfiles.cpp b/kstars/ekos/align/opsastrometryindexfiles.cpp index 599772343..fbcde0670 100644 --- a/kstars/ekos/align/opsastrometryindexfiles.cpp +++ b/kstars/ekos/align/opsastrometryindexfiles.cpp @@ -1,512 +1,507 @@ #include "opsastrometryindexfiles.h" #include "align.h" #include "kstars.h" #include "Options.h" #include #include #include #include namespace Ekos { OpsAstrometryIndexFiles::OpsAstrometryIndexFiles(Align *parent) : QDialog(KStars::Instance()) { setupUi(this); - downloadSpeed=100; - actualdownloadSpeed=downloadSpeed; - - alignModule = parent; - manager = new QNetworkAccessManager(); + downloadSpeed = 100; + actualdownloadSpeed = downloadSpeed; + alignModule = parent; + manager = new QNetworkAccessManager(); //Get a pointer to the KConfigDialog // m_ConfigDialog = KConfigDialog::exists( "alignsettings" ); connect(openIndexFileDirectory, SIGNAL(clicked()), this, SLOT(slotOpenIndexFileDirectory())); astrometryIndex[2.8] = "00"; astrometryIndex[4.0] = "01"; astrometryIndex[5.6] = "02"; astrometryIndex[8] = "03"; astrometryIndex[11] = "04"; astrometryIndex[16] = "05"; astrometryIndex[22] = "06"; astrometryIndex[30] = "07"; astrometryIndex[42] = "08"; astrometryIndex[60] = "09"; astrometryIndex[85] = "10"; astrometryIndex[120] = "11"; astrometryIndex[170] = "12"; astrometryIndex[240] = "13"; astrometryIndex[340] = "14"; astrometryIndex[480] = "15"; astrometryIndex[680] = "16"; astrometryIndex[1000] = "17"; astrometryIndex[1400] = "18"; astrometryIndex[2000] = "19"; QList checkboxes = findChildren(); for (auto &checkBox : checkboxes) { connect(checkBox, SIGNAL(clicked(bool)), this, SLOT(downloadOrDeleteIndexFiles(bool))); } QList progressBars = findChildren(); QList qLabels = findChildren(); QList qButtons = findChildren(); for (auto &bar : progressBars) { bar->setVisible(false); bar->setTextVisible(false); } for (auto &button : qButtons) { button->setVisible(false); } for (QLabel * label: qLabels) { if(label->text().contains("info")||label->text().contains("perc")) { label->setVisible(false); } } } -OpsAstrometryIndexFiles::~OpsAstrometryIndexFiles() -{ -} - void OpsAstrometryIndexFiles::showEvent(QShowEvent *) { slotUpdate(); } void OpsAstrometryIndexFiles::slotUpdate() { double fov_w, fov_h, fov_pixscale; // Values in arcmins. Scale in arcsec per pixel alignModule->getFOVScale(fov_w, fov_h, fov_pixscale); double fov_check = qMax(fov_w, fov_h); FOVOut->setText(QString("%1' x %2'").arg(QString::number(fov_w, 'f', 2), QString::number(fov_h, 'f', 2))); QString astrometryDataDir; if (getAstrometryDataDir(astrometryDataDir) == false) return; indexLocation->setText(astrometryDataDir); QStringList nameFilter("*.fits"); QDir directory(astrometryDataDir); QStringList indexList = directory.entryList(nameFilter); for (auto &indexName : indexList) { if(fileCountMatches(directory,indexName)){ indexName = indexName.replace('-', '_').left(10); QCheckBox *indexCheckBox = findChild(indexName); if (indexCheckBox) indexCheckBox->setChecked(true); } } QList checkboxes = findChildren(); for (auto &checkBox : checkboxes) { checkBox->setIcon(QIcon(":/icons/astrometry-optional.svg")); checkBox->setToolTip(i18n("Optional")); } float last_skymarksize = 2; for (auto &skymarksize : astrometryIndex.keys()) { if ((skymarksize >= 0.40 * fov_check && skymarksize <= 0.9 * fov_check) || (fov_check > last_skymarksize && fov_check < skymarksize)) { QString indexName1 = "index_41" + astrometryIndex.value(skymarksize); QString indexName2 = "index_42" + astrometryIndex.value(skymarksize); QCheckBox *indexCheckBox1 = findChild(indexName1); QCheckBox *indexCheckBox2 = findChild(indexName2); if (indexCheckBox1) { indexCheckBox1->setIcon(QIcon(":/icons/astrometry-required.svg")); indexCheckBox1->setToolTip(i18n("Required")); } if (indexCheckBox2) { indexCheckBox2->setIcon(QIcon(":/icons/astrometry-required.svg")); indexCheckBox2->setToolTip(i18n("Required")); } } else if (skymarksize >= 0.10 * fov_check && skymarksize <= fov_check) { QString indexName1 = "index_41" + astrometryIndex.value(skymarksize); QString indexName2 = "index_42" + astrometryIndex.value(skymarksize); QCheckBox *indexCheckBox1 = findChild(indexName1); QCheckBox *indexCheckBox2 = findChild(indexName2); if (indexCheckBox1) { indexCheckBox1->setIcon(QIcon(":/icons/astrometry-recommended.svg")); indexCheckBox1->setToolTip(i18n("Recommended")); } if (indexCheckBox2) { indexCheckBox2->setIcon(QIcon(":/icons/astrometry-recommended.svg")); indexCheckBox2->setToolTip(i18n("Recommended")); } } last_skymarksize = skymarksize; } } bool OpsAstrometryIndexFiles::fileCountMatches(QDir directory, QString indexName){ QString indexNameMatch = indexName.left(10) + "*.fits"; QStringList list = directory.entryList(QStringList(indexNameMatch)); int count=0; if(indexName.contains("4207")||indexName.contains("4206")||indexName.contains("4205")) count = 12; else if(indexName.contains("4204")||indexName.contains("4203")||indexName.contains("4202")||indexName.contains("4201")||indexName.contains("4200")) count = 48; else count = 1; return list.count()==count; } void OpsAstrometryIndexFiles::slotOpenIndexFileDirectory() { QString astrometryDataDir; if (getAstrometryDataDir(astrometryDataDir) == false) return; QUrl path = QUrl::fromLocalFile(astrometryDataDir); QDesktopServices::openUrl(path); } bool OpsAstrometryIndexFiles::getAstrometryDataDir(QString &dataDir) { QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); QFile confFile(confPath); if (confFile.open(QIODevice::ReadOnly) == false) { KMessageBox::error(0, i18n("Astrometry configuration file corrupted or missing: %1\nPlease set the " "configuration file full path in INDI options.", Options::astrometryConfFile())); return false; } QTextStream in(&confFile); QString line; while (!in.atEnd()) { line = in.readLine(); if (line.isEmpty() || line.startsWith('#')) continue; line = line.trimmed(); if (line.startsWith(QLatin1String("add_path"))) { dataDir = line.mid(9).trimmed(); return true; } } KMessageBox::error(0, i18n("Unable to find data dir in astrometry configuration file.")); return false; } bool OpsAstrometryIndexFiles::astrometryIndicesAreAvailable() { QNetworkReply *response = manager->get(QNetworkRequest(QUrl("http://broiler.astrometry.net"))); QTimer timeout(this); timeout.setInterval(5000); timeout.setSingleShot(true); timeout.start(); while (!response->isFinished()) { if (!timeout.isActive()) { response->deleteLater(); return false; } qApp->processEvents(); } timeout.stop(); bool wasSuccessful = (response->error() == QNetworkReply::NoError); response->deleteLater(); return wasSuccessful; } void OpsAstrometryIndexFiles::downloadIndexFile(const QString &URL, const QString &fileN, QCheckBox *checkBox, int currentIndex, int maxIndex, double fileSize) { QTime downloadTime; downloadTime.start(); QString indexString = QString::number(currentIndex); if (currentIndex < 10) indexString = '0' + indexString; QString indexSeriesName = checkBox->text().remove('&'); QProgressBar *indexDownloadProgress = findChild(indexSeriesName.replace('-', '_').left(10) + "_progress"); QLabel *indexDownloadInfo = findChild(indexSeriesName.replace('-', '_').left(10) + "_info"); QPushButton *indexDownloadCancel = findChild(indexSeriesName.replace('-', '_').left(10) + "_cancel"); QLabel *indexDownloadPerc = findChild(indexSeriesName.replace('-', '_').left(10) + "_perc"); setDownloadInfoVisible(indexSeriesName, checkBox, true); if(indexDownloadInfo){ if (indexDownloadProgress && maxIndex > 0) indexDownloadProgress->setValue(currentIndex*100 / maxIndex); indexDownloadInfo->setText("(" + QString::number(currentIndex) + "/" + QString::number(maxIndex + 1) + ") "); } QString indexURL = URL; indexURL.replace('*', indexString); QNetworkReply *response = manager->get(QNetworkRequest(QUrl(indexURL))); //Shut it down after too much time elapses. //If the filesize is less than 4 MB, it sets the timeout for 1 minute or 60000 s. //If it's larger, it assumes a bad download rate of 1 Mbps (100 bytes/ms) //and the calculation estimates the time in milliseconds it would take to download. int timeout=60000; if(fileSize>4000000) timeout=fileSize/downloadSpeed; //qDebug()<<"Filesize: "<< fileSize << ", timeout: " << timeout; QMetaObject::Connection *cancelConnection = new QMetaObject::Connection(); QMetaObject::Connection *replyConnection = new QMetaObject::Connection(); QMetaObject::Connection *percentConnection = new QMetaObject::Connection(); if(indexDownloadPerc){ *percentConnection=connect(response,&QNetworkReply::downloadProgress, [=](qint64 bytesReceived, qint64 bytesTotal){ if (indexDownloadProgress){ indexDownloadProgress->setValue(bytesReceived); indexDownloadProgress->setMaximum(bytesTotal); } indexDownloadPerc->setText(QString::number(bytesReceived*100/bytesTotal)+"%"); }); } QTimer::singleShot(timeout, response, [=]() { KMessageBox::error(0, i18n("Download Timed out. Either the network is not fast enough, the file is not accessible, or you aren't connected.")); disconnectDownload(cancelConnection,replyConnection,percentConnection); if(response){ response->abort(); response->deleteLater(); } setDownloadInfoVisible(indexSeriesName, checkBox, false); }); *cancelConnection=connect(indexDownloadCancel, &QPushButton::clicked, [=](){ qDebug() << "Download Cancelled."; disconnectDownload(cancelConnection,replyConnection,percentConnection); if(response){ response->abort(); response->deleteLater(); } setDownloadInfoVisible(indexSeriesName, checkBox, false); }); *replyConnection=connect(response, &QNetworkReply::finished, this, [=]() { if(response){ disconnectDownload(cancelConnection,replyConnection,percentConnection); setDownloadInfoVisible(indexSeriesName, checkBox, false); response->deleteLater(); if (response->error() != QNetworkReply::NoError) return; QByteArray responseData = response->readAll(); QString indexFileN = fileN; indexFileN.replace('*', indexString); QFile file(indexFileN); if (QFileInfo(QFileInfo(file).path()).isWritable()) { if (!file.open(QIODevice::WriteOnly)) { KMessageBox::error(0, i18n("File Write Error")); slotUpdate(); return; } else { file.write(responseData.data(), responseData.size()); file.close(); int downloadedFileSize = QFileInfo(file).size(); int dtime=downloadTime.elapsed(); actualdownloadSpeed=(actualdownloadSpeed+(downloadedFileSize/dtime))/2; qDebug()<<"Filesize: "<< downloadedFileSize<<", time: "<exec()) { QMessageBox::information( this, "Error", QString("KAuth returned an error code: %1 %2").arg(job->error()).arg(job->errorString())); slotUpdate(); return; } #endif } if (currentIndex == maxIndex) { slotUpdate(); } else downloadIndexFile(URL, fileN, checkBox, currentIndex + 1, maxIndex, fileSize); } }); } void OpsAstrometryIndexFiles::setDownloadInfoVisible(QString indexSeriesName, QCheckBox *checkBox, bool set){ QProgressBar *indexDownloadProgress = findChild(indexSeriesName.replace('-', '_').left(10) + "_progress"); QLabel *indexDownloadInfo = findChild(indexSeriesName.replace('-', '_').left(10) + "_info"); QPushButton *indexDownloadCancel = findChild(indexSeriesName.replace('-', '_').left(10) + "_cancel"); QLabel *indexDownloadPerc = findChild(indexSeriesName.replace('-', '_').left(10) + "_perc"); if (indexDownloadProgress) indexDownloadProgress->setVisible(set); if (indexDownloadInfo) indexDownloadInfo->setVisible(set); if (indexDownloadCancel) indexDownloadCancel->setVisible(set); if (indexDownloadPerc) indexDownloadPerc->setVisible(set); checkBox->setEnabled(!set); } void OpsAstrometryIndexFiles::disconnectDownload(QMetaObject::Connection *cancelConnection,QMetaObject::Connection *replyConnection,QMetaObject::Connection *percentConnection){ if(cancelConnection) disconnect(*cancelConnection); if(replyConnection) disconnect(*replyConnection); if(percentConnection) disconnect(*percentConnection); } void OpsAstrometryIndexFiles::downloadOrDeleteIndexFiles(bool checked) { QCheckBox *checkBox = (QCheckBox *)QObject::sender(); QString astrometryDataDir; if (getAstrometryDataDir(astrometryDataDir) == false) return; if (checkBox) { QString indexSeriesName = checkBox->text().remove('&'); QString filePath = astrometryDataDir + '/' + indexSeriesName; QString fileNumString = indexSeriesName.mid(8, 2); int indexFileNum = fileNumString.toInt(); if (checked) { checkBox->setChecked(!checked); if (astrometryIndicesAreAvailable()) { QString URL; if (indexSeriesName.startsWith(QLatin1String("index-41"))) URL = "http://broiler.astrometry.net/~dstn/4100/" + indexSeriesName; else if (indexSeriesName.startsWith(QLatin1String("index-42"))) URL = "http://broiler.astrometry.net/~dstn/4200/" + indexSeriesName; int maxIndex = 0; if (indexFileNum < 8 && URL.contains("*")) { maxIndex = 11; if (indexFileNum < 5) maxIndex = 47; } double fileSize=1E11*qPow(astrometryIndex.key(fileNumString),-1.909); //This estimates the file size based on skymark size obtained from the index number. if(maxIndex!=0) fileSize/=maxIndex; //FileSize is divided between multiple files for some index series. downloadIndexFile(URL, filePath, checkBox, 0, maxIndex,fileSize); } else { KMessageBox::sorry(0, i18n("Could not contact Astrometry Index Server: broiler.astrometry.net")); } } else { if (KMessageBox::Continue == KMessageBox::warningContinueCancel( NULL, "Are you sure you want to delete these index files? " + indexSeriesName, i18n("Delete File(s)"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "delete_index_files_warning")) { if (QFileInfo(astrometryDataDir).isWritable()) { QStringList nameFilter("*.fits"); QDir directory(astrometryDataDir); QStringList indexList = directory.entryList(nameFilter); for (auto &fileName : indexList) { if (fileName.contains(indexSeriesName.left(10))) { if (!directory.remove(fileName)) { KMessageBox::error(0, i18n("File Delete Error")); slotUpdate(); return; } } } } else { #ifdef Q_OS_OSX KMessageBox::error(0, i18n("Astrometry Folder Permissions Error")); slotUpdate(); #else KAuth::Action action(QStringLiteral("org.kde.kf5auth.kstars.removeindexfileset")); action.setHelperId(QStringLiteral("org.kde.kf5auth.kstars")); action.setArguments( QVariantMap({ { "indexSetName", indexSeriesName }, { "astrometryDataDir", astrometryDataDir } })); KAuth::ExecuteJob *job = action.execute(); if (!job->exec()) QMessageBox::information( this, "Error", QString("KAuth returned an error code: %1 %2").arg(job->error()).arg(job->errorString())); #endif } } } } } } diff --git a/kstars/ekos/align/opsastrometryindexfiles.h b/kstars/ekos/align/opsastrometryindexfiles.h index fed5d1d3e..059cc6a6e 100644 --- a/kstars/ekos/align/opsastrometryindexfiles.h +++ b/kstars/ekos/align/opsastrometryindexfiles.h @@ -1,52 +1,52 @@ #pragma once #include "ui_opsastrometryindexfiles.h" #include #include #include #include class QNetworkAccessManager; class Align; class KConfigDialog; namespace Ekos { class Align; class OpsAstrometryIndexFiles : public QDialog, public Ui::OpsAstrometryIndexFiles { Q_OBJECT public: explicit OpsAstrometryIndexFiles(Align *parent); - ~OpsAstrometryIndexFiles(); + virtual ~OpsAstrometryIndexFiles() override = default; protected: void showEvent(QShowEvent *); public slots: void slotUpdate(); void slotOpenIndexFileDirectory(); void downloadOrDeleteIndexFiles(bool checked); private: bool getAstrometryDataDir(QString &dataDir); void downloadIndexFile(const QString &URL, const QString &fileN, QCheckBox *checkBox, int currentIndex, int maxIndex, double fileSize); bool astrometryIndicesAreAvailable(); void setDownloadInfoVisible(QString indexSeriesName,QCheckBox *checkBox, bool set); bool fileCountMatches(QDir directory, QString indexName); void disconnectDownload(QMetaObject::Connection *cancelConnection, QMetaObject::Connection *replyConnection, QMetaObject::Connection *percentConnection); KConfigDialog *m_ConfigDialog { nullptr }; Align *alignModule { nullptr }; QNetworkAccessManager *manager { nullptr }; QMap astrometryIndex; - int downloadSpeed; //bytes per millisecond - int actualdownloadSpeed; //bytes per millisecond + int downloadSpeed { 0 }; //bytes per millisecond + int actualdownloadSpeed { 0 }; //bytes per millisecond }; } diff --git a/kstars/ekos/align/remoteastrometryparser.cpp b/kstars/ekos/align/remoteastrometryparser.cpp index 3f865fb98..6f32cea61 100644 --- a/kstars/ekos/align/remoteastrometryparser.cpp +++ b/kstars/ekos/align/remoteastrometryparser.cpp @@ -1,296 +1,292 @@ /* Astrometry.net Parser - Remote Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "remoteastrometryparser.h" -#include "ekos_align_debug.h" #include "align.h" +#include "ekos_align_debug.h" #include "Options.h" #include "indi/clientmanager.h" #include "indi/driverinfo.h" #include "indi/guimanager.h" #include "indi/indidevice.h" #include #include namespace Ekos { RemoteAstrometryParser::RemoteAstrometryParser() : AstrometryParser() { } -RemoteAstrometryParser::~RemoteAstrometryParser() -{ -} - bool RemoteAstrometryParser::init() { return remoteAstrometry != nullptr; } void RemoteAstrometryParser::verifyIndexFiles(double, double) { } bool RemoteAstrometryParser::startSovler(const QString &filename, const QStringList &args, bool generated) { INDI_UNUSED(generated); QFile fp(filename); if (fp.open(QIODevice::ReadOnly) == false) { align->appendLogText(i18n("Cannot open file %1 for reading!", filename)); emit solverFailed(); return false; } ISwitchVectorProperty *solverSwitch = remoteAstrometry->getBaseDevice()->getSwitch("ASTROMETRY_SOLVER"); IBLOBVectorProperty *solverBLOB = remoteAstrometry->getBaseDevice()->getBLOB("ASTROMETRY_DATA"); if (solverSwitch == nullptr || solverBLOB == nullptr) { align->appendLogText(i18n("Failed to find solver properties.")); fp.close(); emit solverFailed(); return false; } sendArgs(args); ISwitch *enableSW = IUFindSwitch(solverSwitch, "ASTROMETRY_SOLVER_ENABLE"); if (enableSW->s == ISS_OFF) { IUResetSwitch(solverSwitch); enableSW->s = ISS_ON; remoteAstrometry->getDriverInfo()->getClientManager()->sendNewSwitch(solverSwitch); } IBLOB *bp = &(solverBLOB->bp[0]); bp->bloblen = bp->size = fp.size(); bp->blob = (uint8_t *)realloc(bp->blob, bp->size); if (bp->blob == nullptr) { align->appendLogText(i18n("Not enough memory for file %1", filename)); fp.close(); emit solverFailed(); return false; } memcpy(bp->blob, fp.readAll().constData(), bp->size); solverRunning = true; remoteAstrometry->getDriverInfo()->getClientManager()->startBlob(solverBLOB->device, solverBLOB->name, timestamp()); #if (INDI_VERSION_MINOR >= 4 && INDI_VERSION_RELEASE >= 2) remoteAstrometry->getDriverInfo()->getClientManager()->sendOneBlob(bp); #else remoteAstrometry->getDriverInfo()->getClientManager()->sendOneBlob(bp->name, bp->size, bp->format, bp->blob); #endif remoteAstrometry->getDriverInfo()->getClientManager()->finishBlob(); align->appendLogText(i18n("Starting remote solver...")); solverTimer.start(); return true; } bool RemoteAstrometryParser::sendArgs(const QStringList &args) { ITextVectorProperty *solverSettings = remoteAstrometry->getBaseDevice()->getText("ASTROMETRY_SETTINGS"); if (solverSettings == nullptr) { align->appendLogText(i18n("Failed to find solver settings.")); emit solverFailed(); return false; } QStringList solverArgs = args; // Add parity option if none is give and we already know parity before // and is NOT a blind solve if (Options::astrometryDetectParity() && parity.isEmpty() == false && args.contains("parity") == false && (args.contains("-3") || args.contains("-L"))) solverArgs << "--parity" << parity; for (int i = 0; i < solverSettings->ntp; i++) { // 2016-10-20: Disable setting this automatically since remote device might have different // settings /*if (!strcmp(solverSettings->tp[i].name, "ASTROMETRY_SETTINGS_BINARY")) IUSaveText(&solverSettings->tp[i], Options::astrometrySolver().toLatin1().constData());*/ if (!strcmp(solverSettings->tp[i].name, "ASTROMETRY_SETTINGS_OPTIONS")) IUSaveText(&solverSettings->tp[i], solverArgs.join(" ").toLatin1().constData()); } remoteAstrometry->getDriverInfo()->getClientManager()->sendNewText(solverSettings); INDI_D *guiDevice = GUIManager::Instance()->findGUIDevice(remoteAstrometry->getDeviceName()); if (guiDevice) guiDevice->updateTextGUI(solverSettings); return true; } void RemoteAstrometryParser::setEnabled(bool enable) { ISwitchVectorProperty *solverSwitch = remoteAstrometry->getBaseDevice()->getSwitch("ASTROMETRY_SOLVER"); if (solverSwitch == nullptr) return; ISwitch *enableSW = IUFindSwitch(solverSwitch, "ASTROMETRY_SOLVER_ENABLE"); ISwitch *disableSW = IUFindSwitch(solverSwitch, "ASTROMETRY_SOLVER_DISABLE"); if (enableSW == nullptr || disableSW == nullptr) return; if (enable && enableSW->s == ISS_OFF) { IUResetSwitch(solverSwitch); enableSW->s = ISS_ON; remoteAstrometry->getDriverInfo()->getClientManager()->sendNewSwitch(solverSwitch); solverRunning = true; qCDebug(KSTARS_EKOS_ALIGN) << "Enabling remote solver..."; } else if (enable == false && disableSW->s == ISS_OFF) { IUResetSwitch(solverSwitch); disableSW->s = ISS_ON; remoteAstrometry->getDriverInfo()->getClientManager()->sendNewSwitch(solverSwitch); solverRunning = false; qCDebug(KSTARS_EKOS_ALIGN) << "Disabling remote solver..."; } } bool RemoteAstrometryParser::setCCD(const QString &ccd) { targetCCD = ccd; if (remoteAstrometry == nullptr) return false; ITextVectorProperty *activeDevices = remoteAstrometry->getBaseDevice()->getText("ACTIVE_DEVICES"); if (activeDevices == nullptr) return false; IText *activeCCD = IUFindText(activeDevices, "ACTIVE_CCD"); if (activeCCD == nullptr) return false; // If same device, no need to update if (QString(activeCCD->text) == ccd) return true; IUSaveText(activeCCD, ccd.toLatin1().data()); remoteAstrometry->getDriverInfo()->getClientManager()->sendNewText(activeDevices); return true; } bool RemoteAstrometryParser::stopSolver() { // Disable solver ISwitchVectorProperty *svp = remoteAstrometry->getBaseDevice()->getSwitch("ASTROMETRY_SOLVER"); if (!svp) return false; ISwitch *disableSW = IUFindSwitch(svp, "ASTROMETRY_SOLVER_DISABLE"); if (disableSW->s == ISS_OFF) { IUResetSwitch(svp); disableSW->s = ISS_ON; remoteAstrometry->getDriverInfo()->getClientManager()->sendNewSwitch(svp); } solverRunning = false; return true; } void RemoteAstrometryParser::setAstrometryDevice(ISD::GDInterface *device) { if (device == remoteAstrometry) return; remoteAstrometry = device; remoteAstrometry->disconnect(this); connect(remoteAstrometry, SIGNAL(switchUpdated(ISwitchVectorProperty*)), this, SLOT(checkStatus(ISwitchVectorProperty*))); connect(remoteAstrometry, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SLOT(checkResults(INumberVectorProperty*))); if (targetCCD.isEmpty() == false) setCCD(targetCCD); } void RemoteAstrometryParser::checkStatus(ISwitchVectorProperty *svp) { if (solverRunning == false || strcmp(svp->name, "ASTROMETRY_SOLVER")) return; if (svp->s == IPS_ALERT) { stopSolver(); align->appendLogText(i18n("Solver failed. Try again.")); emit solverFailed(); return; } // In case the remote solver started solving by listening to ACTIVE_CCD BLOB remotely via snooping // then we need to start the timer. else if (svp->s == IPS_BUSY) { solverTimer.restart(); } } void RemoteAstrometryParser::checkResults(INumberVectorProperty *nvp) { if (solverRunning == false || strcmp(nvp->name, "ASTROMETRY_RESULTS") || nvp->s != IPS_OK) return; double pixscale, ra, de, orientation; pixscale = ra = de = orientation = -1000; for (int i = 0; i < nvp->nnp; i++) { if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_PIXSCALE")) pixscale = nvp->np[i].value; else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_ORIENTATION")) orientation = nvp->np[i].value; else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_RA")) ra = nvp->np[i].value; else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_DE")) de = nvp->np[i].value; else if (!strcmp(nvp->np[i].name, "ASTROMETRY_RESULTS_PARITY")) { if (nvp->np[i].value == 1) parity = "pos"; else if (nvp->np[i].value == -1) parity = "neg"; } } if (pixscale != -1000 && ra != -1000 && de != -1000 && orientation != -1000) { int elapsed = (int)round(solverTimer.elapsed() / 1000.0); align->appendLogText(i18np("Solver completed in %1 second.", "Solver completed in %1 seconds.", elapsed)); stopSolver(); emit solverFinished(orientation, ra, de, pixscale); } } } diff --git a/kstars/ekos/align/remoteastrometryparser.h b/kstars/ekos/align/remoteastrometryparser.h index 0cf2b55b6..d434e9dab 100644 --- a/kstars/ekos/align/remoteastrometryparser.h +++ b/kstars/ekos/align/remoteastrometryparser.h @@ -1,58 +1,58 @@ /* Astrometry.net Parser - Remote Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "astrometryparser.h" #include "indi/indiccd.h" namespace Ekos { class Align; /** * @class RemoteAstrometryParser * RemoteAstrometryParser invokes the remote astrometry.net solver in the remote CCD driver to solve the captured image. * The offline astrometry.net plus index files must be installed and configured on the remote host running the INDI CCD driver. * * @authro Jasem Mutlaq */ class RemoteAstrometryParser : public AstrometryParser { Q_OBJECT public: RemoteAstrometryParser(); - virtual ~RemoteAstrometryParser(); + virtual ~RemoteAstrometryParser() override = default; - virtual void setAlign(Align *_align) { align = _align; } - virtual bool init(); - virtual void verifyIndexFiles(double fov_x, double fov_y); - virtual bool startSovler(const QString &filename, const QStringList &args, bool generated = true); - virtual bool stopSolver(); + virtual void setAlign(Align *_align) override { align = _align; } + virtual bool init() override; + virtual void verifyIndexFiles(double fov_x, double fov_y) override; + virtual bool startSovler(const QString &filename, const QStringList &args, bool generated = true) override; + virtual bool stopSolver() override; void setAstrometryDevice(ISD::GDInterface *device); void setEnabled(bool enable); bool sendArgs(const QStringList &args); bool setCCD(const QString& ccd); public slots: void checkStatus(ISwitchVectorProperty *svp); void checkResults(INumberVectorProperty *nvp); private: ISD::GDInterface *remoteAstrometry { nullptr }; bool solverRunning { false }; bool captureRunning { false }; Align *align { nullptr }; QTime solverTimer; QString parity; QString targetCCD; }; } diff --git a/kstars/ekos/auxiliary/dome.cpp b/kstars/ekos/auxiliary/dome.cpp index 77ad0f60a..0006f86b0 100644 --- a/kstars/ekos/auxiliary/dome.cpp +++ b/kstars/ekos/auxiliary/dome.cpp @@ -1,129 +1,126 @@ /* Ekos Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "dome.h" -#include "ekos/ekosmanager.h" -#include "kstars.h" + #include "domeadaptor.h" +#include "ekos/ekosmanager.h" #include "indi/driverinfo.h" #include "indi/clientmanager.h" +#include "kstars.h" #include namespace Ekos { Dome::Dome() { new DomeAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Dome", this); currentDome = nullptr; } -Dome::~Dome() -{ -} - void Dome::setDome(ISD::GDInterface *newDome) { currentDome = static_cast(newDome); } void Dome::setTelescope(ISD::GDInterface *newTelescope) { if (currentDome == nullptr) return; ITextVectorProperty *activeDevices = currentDome->getBaseDevice()->getText("ACTIVE_DEVICES"); if (activeDevices) { IText *activeTelescope = IUFindText(activeDevices, "ACTIVE_TELESCOPE"); if (activeTelescope) { IUSaveText(activeTelescope, newTelescope->getDeviceName()); currentDome->getDriverInfo()->getClientManager()->sendNewText(activeDevices); } } } bool Dome::canPark() { if (currentDome == nullptr) return false; return currentDome->canPark(); } bool Dome::park() { if (currentDome == nullptr || currentDome->canPark() == false) return false; return currentDome->Park(); } bool Dome::unpark() { if (currentDome == nullptr || currentDome->canPark() == false) return false; return currentDome->UnPark(); } bool Dome::abort() { if (currentDome == nullptr) return false; return currentDome->Abort(); } bool Dome::isMoving() { if (currentDome == nullptr) return false; return currentDome->isMoving(); } Dome::ParkingStatus Dome::getParkingStatus() { if (currentDome == nullptr || currentDome->canPark() == false) return PARKING_ERROR; ISwitchVectorProperty *parkSP = currentDome->getBaseDevice()->getSwitch("DOME_PARK"); if (parkSP == nullptr) return PARKING_ERROR; switch (parkSP->s) { case IPS_IDLE: return PARKING_IDLE; case IPS_OK: if (parkSP->sp[0].s == ISS_ON) return PARKING_OK; else return UNPARKING_OK; break; case IPS_BUSY: if (parkSP->sp[0].s == ISS_ON) return PARKING_BUSY; else return UNPARKING_BUSY; case IPS_ALERT: return PARKING_ERROR; } return PARKING_ERROR; } } diff --git a/kstars/ekos/auxiliary/dome.h b/kstars/ekos/auxiliary/dome.h index ee985f5de..aa2031de5 100644 --- a/kstars/ekos/auxiliary/dome.h +++ b/kstars/ekos/auxiliary/dome.h @@ -1,93 +1,98 @@ /* Ekos Dome interface Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef DOME_H -#define DOME_H - -#include +#pragma once #include "indi/indistd.h" #include "indi/indidome.h" +#include + namespace Ekos { /** - *@class Dome - *@short Supports basic dome functions - *@author Jasem Mutlaq - *@version 1.0 + * @class Dome + * @short Supports basic dome functions + * + * @author Jasem Mutlaq + * @version 1.0 */ class Dome : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Dome") public: Dome(); - ~Dome(); + virtual ~Dome() override = default; typedef enum { PARKING_IDLE, PARKING_OK, UNPARKING_OK, PARKING_BUSY, UNPARKING_BUSY, PARKING_ERROR } ParkingStatus; - /** @defgroup DomeDBusInterface Ekos DBus Interface - Dome Interface - * Ekos::Dome interface provides advanced basic dome operations. - */ + /** + * @defgroup DomeDBusInterface Ekos DBus Interface - Dome Interface + * Ekos::Dome interface provides advanced basic dome operations. + */ /*@{*/ - /** DBUS interface function. - * Abort dome - */ + /** + * DBUS interface function. + * Abort dome + */ Q_SCRIPTABLE bool abort(); - /** DBUS interface function. - * Can dome park? - */ + /** + * DBUS interface function. + * Can dome park? + */ Q_SCRIPTABLE bool canPark(); - /** DBUS interface function. - * Park dome - */ + /** + * DBUS interface function. + * Park dome + */ Q_SCRIPTABLE bool park(); - /** DBUS interface function. - * Park dome - */ + /** + * DBUS interface function. + * Park dome + */ Q_SCRIPTABLE bool unpark(); - /** DBUS interface function. - * Get the dome park status - */ + /** + * DBUS interface function. + * Get the dome park status + */ Q_SCRIPTABLE ParkingStatus getParkingStatus(); - /** DBUS interface function. - * Check if the dome is in motion - */ + /** + * DBUS interface function. + * Check if the dome is in motion + */ Q_SCRIPTABLE bool isMoving(); /** @}*/ /** - * @brief setDome set the dome device - * @param newDome pointer to Dome device. - */ + * @brief setDome set the dome device + * @param newDome pointer to Dome device. + */ void setDome(ISD::GDInterface *newDome); /** * @brief setTelescope Set the telescope device. This is only used to sync ACTIVE_TELESCOPE to the current active telescope. * @param newTelescope pointer to telescope device. */ void setTelescope(ISD::GDInterface *newTelescope); private: // Devices needed for Dome operation ISD::Dome *currentDome; }; } - -#endif // Dome_H diff --git a/kstars/ekos/auxiliary/dustcap.cpp b/kstars/ekos/auxiliary/dustcap.cpp index ff342e96a..14c7cf758 100644 --- a/kstars/ekos/auxiliary/dustcap.cpp +++ b/kstars/ekos/auxiliary/dustcap.cpp @@ -1,118 +1,113 @@ /* Ekos DustCap Interface Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "dustcap.h" + +#include "dustcapadaptor.h" #include "ekos/ekosmanager.h" #include "kstars.h" -#include "dustcapadaptor.h" #include namespace Ekos { DustCap::DustCap() { new DustCapAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/DustCap", this); - - currentDustCap = nullptr; -} - -DustCap::~DustCap() -{ } void DustCap::setDustCap(ISD::GDInterface *newDustCap) { currentDustCap = static_cast(newDustCap); } DustCap::ParkingStatus DustCap::getParkingStatus() { if (currentDustCap == nullptr) return PARKING_ERROR; ISwitchVectorProperty *parkSP = currentDustCap->getBaseDevice()->getSwitch("CAP_PARK"); if (parkSP == nullptr) return PARKING_ERROR; switch (parkSP->s) { case IPS_IDLE: return PARKING_IDLE; case IPS_OK: if (parkSP->sp[0].s == ISS_ON) return PARKING_OK; else return UNPARKING_OK; break; case IPS_BUSY: if (parkSP->sp[0].s == ISS_ON) return PARKING_BUSY; else return UNPARKING_BUSY; case IPS_ALERT: return PARKING_ERROR; } return PARKING_ERROR; } bool DustCap::park() { if (currentDustCap == nullptr) return false; return currentDustCap->Park(); } bool DustCap::unpark() { if (currentDustCap == nullptr) return false; return currentDustCap->UnPark(); } bool DustCap::canPark() { if (currentDustCap == nullptr) return false; return currentDustCap->canPark(); } bool DustCap::hasLight() { if (currentDustCap == nullptr) return false; return currentDustCap->hasLight(); } bool DustCap::setLightEnabled(bool enable) { if (currentDustCap == nullptr) return false; return currentDustCap->SetLightEnabled(enable); } bool DustCap::setBrightness(uint16_t val) { if (currentDustCap == nullptr) return false; return currentDustCap->SetBrightness(val); } } diff --git a/kstars/ekos/auxiliary/dustcap.h b/kstars/ekos/auxiliary/dustcap.h index 02f8b9947..e9820f209 100644 --- a/kstars/ekos/auxiliary/dustcap.h +++ b/kstars/ekos/auxiliary/dustcap.h @@ -1,99 +1,105 @@ /* Ekos DustCap interface Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef DUSTCAP_H -#define DUSTCAP_H - -#include +#pragma once #include "indi/indistd.h" #include "indi/indicap.h" +#include + namespace Ekos { /** - *@class DustCap - *@short Supports basic DustCap functions (open/close) and optionally control flat light - *@author Jasem Mutlaq - *@version 1.0 + * @class DustCap + * @short Supports basic DustCap functions (open/close) and optionally control flat light + * + * @author Jasem Mutlaq + * @version 1.0 */ class DustCap : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.DustCap") public: typedef enum { PARKING_IDLE, PARKING_OK, UNPARKING_OK, PARKING_BUSY, UNPARKING_BUSY, PARKING_ERROR } ParkingStatus; DustCap(); - ~DustCap(); + virtual ~DustCap() override = default; - /** @defgroup DustCapDBusInterface Ekos DBus Interface - DustCap Interface - * Ekos::DustCap interface provides basic DustCap operations. - */ + /** + * @defgroup DustCapDBusInterface Ekos DBus Interface - DustCap Interface + * Ekos::DustCap interface provides basic DustCap operations. + */ /*@{*/ - /** DBUS interface function. - * If dust cap can park/unpark or is it just a light source? - * @return True if park is supported, false otherwise - */ + /** + * DBUS interface function. + * If dust cap can park/unpark or is it just a light source? + * @return True if park is supported, false otherwise + */ Q_SCRIPTABLE bool canPark(); - /** DBUS interface function. - * Park / Close dust cap - * @return True if operation started/successful, false otherwise - */ + /** + * DBUS interface function. + * Park / Close dust cap + * @return True if operation started/successful, false otherwise + */ Q_SCRIPTABLE bool park(); - /** DBUS interface function. - * UnPark / Open dust cap - * @return True if operation started/successful, false otherwise - */ + /** + * DBUS interface function. + * UnPark / Open dust cap + * @return True if operation started/successful, false otherwise + */ Q_SCRIPTABLE bool unpark(); - /** DBUS interface function. - * hasLight: Does the dust cap have a flat light source? - * @return True if there if flat light, false othereise - */ + /** + * DBUS interface function. + * hasLight: Does the dust cap have a flat light source? + * @return True if there if flat light, false othereise + */ Q_SCRIPTABLE bool hasLight(); - /** DBUS interface function. - * setLightEnabled: Turn on/off light box - * @param enable If true, turn light on, otherwise turn light off - * @return True if operation started/successful, false otherwise - */ + /** + * DBUS interface function. + * setLightEnabled: Turn on/off light box + * @param enable If true, turn light on, otherwise turn light off + * @return True if operation started/successful, false otherwise + */ Q_SCRIPTABLE bool setLightEnabled(bool enable); - /** DBUS interface function. - * SetLight: Set light source brightness level - * @return True if operation started/successful, false otherwise - */ + /** + * DBUS interface function. + * SetLight: Set light source brightness level + * @return True if operation started/successful, false otherwise + */ Q_SCRIPTABLE bool setBrightness(uint16_t val); - /** DBUS interface function. - * Get the dome park status - */ + /** + * DBUS interface function. + * Get the dome park status + */ Q_SCRIPTABLE ParkingStatus getParkingStatus(); /** @}*/ /** - * @brief setDustCap set the DustCap device - * @param newDustCap pointer to DustCap device. - */ + * @brief setDustCap set the DustCap device + * @param newDustCap pointer to DustCap device. + */ void setDustCap(ISD::GDInterface *newDustCap); private: // Devices needed for DustCap operation - ISD::DustCap *currentDustCap; + ISD::DustCap *currentDustCap { nullptr }; }; } - -#endif // DustCap_H diff --git a/kstars/ekos/auxiliary/weather.cpp b/kstars/ekos/auxiliary/weather.cpp index 32aeca8c7..838168bd8 100644 --- a/kstars/ekos/auxiliary/weather.cpp +++ b/kstars/ekos/auxiliary/weather.cpp @@ -1,51 +1,46 @@ /* Ekos Weather Interface Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "weather.h" + #include "ekos/ekosmanager.h" #include "kstars.h" #include "weatheradaptor.h" #include namespace Ekos { Weather::Weather() { new WeatherAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Weather", this); - - currentWeather = nullptr; -} - -Weather::~Weather() -{ } void Weather::setWeather(ISD::GDInterface *newWeather) { currentWeather = static_cast(newWeather); } IPState Weather::getWeatherStatus() { if (currentWeather == nullptr) return IPS_ALERT; return currentWeather->getWeatherStatus(); } uint16_t Weather::getUpdatePeriod() { if (currentWeather == nullptr) return 0; return currentWeather->getUpdatePeriod(); } } diff --git a/kstars/ekos/auxiliary/weather.h b/kstars/ekos/auxiliary/weather.h index d383688b2..caa7d077d 100644 --- a/kstars/ekos/auxiliary/weather.h +++ b/kstars/ekos/auxiliary/weather.h @@ -1,67 +1,68 @@ /* Ekos Weather interface Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef Weather_H -#define Weather_H - -#include +#pragma once #include "indi/indistd.h" #include "indi/indiweather.h" +#include + namespace Ekos { /** - *@class Weather - *@short Supports basic weather station functions - *@author Jasem Mutlaq - *@version 1.0 + * @class Weather + * @short Supports basic weather station functions + * + * @author Jasem Mutlaq + * @version 1.0 */ class Weather : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Weather") public: Weather(); - ~Weather(); + virtual ~Weather() override = default; - /** @defgroup WeatherDBusInterface Ekos DBus Interface - Weather Interface - * Ekos::Weather interface provides basic weather operations. - */ + /** + * @defgroup WeatherDBusInterface Ekos DBus Interface - Weather Interface + * Ekos::Weather interface provides basic weather operations. + */ /*@{*/ - /** DBUS interface function. - * Get Weather status. - * @return Either IPS_OK for OK acceptable weather, IPS_BUSY for weather in warning zone, and IPS_ALERT for weather in danger zone. The zones ranges are defined by the INDI weather driver. - */ + /** + * DBUS interface function. + * Get Weather status. + * @return Either IPS_OK for OK acceptable weather, IPS_BUSY for weather in warning zone, and IPS_ALERT for weather in danger zone. The zones ranges are defined by the INDI weather driver. + */ Q_SCRIPTABLE IPState getWeatherStatus(); - /** DBUS interface function. - * Get Weather Update Period - * @return Return weather update period in minute. The weather is updated every X minutes from the weather source. - */ + /** + * DBUS interface function. + * Get Weather Update Period + * @return Return weather update period in minute. The weather is updated every X minutes from the weather source. + */ Q_SCRIPTABLE uint16_t getUpdatePeriod(); /** @}*/ /** - * @brief setWeather set the Weather device - * @param newWeather pointer to Weather device. - */ + * @brief setWeather set the Weather device + * @param newWeather pointer to Weather device. + */ void setWeather(ISD::GDInterface *newWeather); private: // Devices needed for Weather operation - ISD::Weather *currentWeather; + ISD::Weather *currentWeather { nullptr }; }; } - -#endif // Weather_H diff --git a/kstars/ekos/capture/sequencejob.h b/kstars/ekos/capture/sequencejob.h index f8dbef375..b113bcb1e 100644 --- a/kstars/ekos/capture/sequencejob.h +++ b/kstars/ekos/capture/sequencejob.h @@ -1,283 +1,283 @@ /* Ekos Capture tool Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "indi/indistd.h" #include "indi/indiccd.h" #include "ekos/auxiliary/filtermanager.h" #include "skyobjects/skypoint.h" #include /** * @class SequenceJob * @short Sequence Job is a container for the details required to capture a series of images. * * @author Jasem Mutlaq * @version 1.0 */ namespace Ekos { class SequenceJob : public QObject { Q_OBJECT public: typedef enum { JOB_IDLE, JOB_BUSY, JOB_ERROR, JOB_ABORTED, JOB_DONE } JOBStatus; typedef enum { CAPTURE_OK, CAPTURE_FRAME_ERROR, CAPTURE_BIN_ERROR, CAPTURE_FILTER_BUSY, CAPTURE_FOCUS_ERROR } CAPTUREResult; typedef enum { ACTION_FILTER, ACTION_TEMPERATURE, ACTION_ROTATOR } PrepareActions; SequenceJob(); - ~SequenceJob() {} + ~SequenceJob() = default; CAPTUREResult capture(bool noCaptureFilter); void reset(); void abort(); void done(); void prepareCapture(); JOBStatus getStatus() { return status; } const QString &getStatusString() { return statusStrings[status]; } bool isPreview() { return preview; } int getDelay() { return delay; } int getCount() { return count; } unsigned int getCompleted() { return completed; } const QString &getRawPrefix() { return rawPrefix; } double getExposure() const { return exposure; } void setActiveCCD(ISD::CCD *ccd) { activeCCD = ccd; } ISD::CCD *getActiveCCD() { return activeCCD; } void setActiveFilter(ISD::GDInterface *filter) { activeFilter = filter; } ISD::GDInterface *getActiveFilter() { return activeFilter; } void setActiveRotator(ISD::GDInterface *rotator) { activeRotator = rotator; } ISD::GDInterface *getActiveRotator() { return activeRotator; } void setActiveChip(ISD::CCDChip *chip) { activeChip = chip; } ISD::CCDChip *getActiveChip() { return activeChip; } void setLocalDir(const QString &dir) { localDirectory = dir; } const QString &getLocalDir() { return localDirectory; } void setTargetFilter(int pos, const QString &name); int getTargetFilter() { return targetFilter; } int getCurrentFilter() const; void setCurrentFilter(int value); const QString &getFilterName() { return filter; } void setFrameType(CCDFrameType type); CCDFrameType getFrameType() { return frameType; } void setFilterManager(const QSharedPointer &manager) { filterManager = manager; } void setCaptureFilter(FITSScale capFilter) { captureFilter = capFilter; } FITSScale getCaptureFilter() { return captureFilter; } void setPreview(bool enable) { preview = enable; } void setFullPrefix(const QString &cprefix) { fullPrefix = cprefix; } const QString &getFullPrefix() { return fullPrefix; } void setFrame(int in_x, int in_y, int in_w, int in_h) { x = in_x; y = in_y; w = in_w; h = in_h; } int getSubX() { return x; } int getSubY() { return y; } int getSubW() { return w; } int getSubH() { return h; } void setBin(int xbin, int ybin) { binX = xbin; binY = ybin; } int getXBin() { return binX; } int getYBin() { return binY; } void setDelay(int in_delay) { delay = in_delay; } void setCount(int in_count) { count = in_count; } void setExposure(double duration) { exposure = duration; } void setStatusCell(QTableWidgetItem *cell) { statusCell = cell; } void setCompleted(unsigned int in_completed) { completed = in_completed; } int getISOIndex() const; void setISOIndex(int value); double getExposeLeft() const; void setExposeLeft(double value); void resetStatus(); void setPrefixSettings(const QString &rawFilePrefix, bool filterEnabled, bool exposureEnabled, bool tsEnabled); void getPrefixSettings(QString &rawFilePrefix, bool &filterEnabled, bool &exposureEnabled, bool &tsEnabled); bool isFilterPrefixEnabled() { return filterPrefixEnabled; } bool isExposurePrefixEnabled() { return expPrefixEnabled; } bool isTimestampPrefixEnabled() { return timeStampPrefixEnabled; } double getCurrentTemperature() const; void setCurrentTemperature(double value); double getTargetTemperature() const; void setTargetTemperature(double value); double getTargetADU() const; void setTargetADU(double value); double getTargetADUTolerance() const; void setTargetADUTolerance(double value); int getCaptureRetires() const; void setCaptureRetires(int value); FlatFieldSource getFlatFieldSource() const; void setFlatFieldSource(const FlatFieldSource &value); FlatFieldDuration getFlatFieldDuration() const; void setFlatFieldDuration(const FlatFieldDuration &value); SkyPoint getWallCoord() const; void setWallCoord(const SkyPoint &value); bool isPreMountPark() const; void setPreMountPark(bool value); bool isPreDomePark() const; void setPreDomePark(bool value); bool getEnforceTemperature() const; void setEnforceTemperature(bool value); QString getPostCaptureScript() const; void setPostCaptureScript(const QString &value); ISD::CCD::UploadMode getUploadMode() const; void setUploadMode(const ISD::CCD::UploadMode &value); QString getRemoteDir() const; void setRemoteDir(const QString &value); ISD::CCD::TransferFormat getTransforFormat() const; void setTransforFormat(const ISD::CCD::TransferFormat &value); double getGain() const; void setGain(double value); double getTargetRotation() const; void setTargetRotation(double value); void setCurrentRotation(double value); QMap > getCustomProperties() const; void setCustomProperties(const QMap > &value); QString getDirectoryPostfix() const; void setDirectoryPostfix(const QString &value); signals: void prepareComplete(); void prepareState(Ekos::CaptureState state); void checkFocus(); private: bool areActionsReady(); void setAllActionsReady(); QStringList statusStrings; ISD::CCDChip *activeChip { nullptr }; ISD::CCD *activeCCD { nullptr }; ISD::GDInterface *activeFilter { nullptr }; ISD::GDInterface *activeRotator { nullptr }; double exposure { -1 }; CCDFrameType frameType { FRAME_LIGHT }; int targetFilter { -1 }; int currentFilter { 0 }; QString filter; int binX { 0 }; int binY { 0 }; int x { 0 }; int y { 0 }; int w { 0 }; int h { 0 }; QString fullPrefix; int count { -1 }; int delay { -1 }; bool preview { false }; bool prepareReady { true }; bool enforceTemperature { false }; int isoIndex { -1 }; int captureRetires { 0 }; unsigned int completed { 0 }; double exposeLeft { 0 }; double currentTemperature { 0 }; double targetTemperature { 0 }; double gain { -1 }; // Rotation in absolute ticks, NOT angle double targetRotation { 0 }; double currentRotation { 0 }; FITSScale captureFilter { FITS_NONE }; QTableWidgetItem *statusCell { nullptr }; QString postCaptureScript; ISD::CCD::UploadMode uploadMode { ISD::CCD::UPLOAD_CLIENT }; // Transfer Format ISD::CCD::TransferFormat transforFormat { ISD::CCD::FORMAT_FITS }; // Directory Settings QString localDirectory; QString remoteDirectory; QString directoryPostfix; bool filterPrefixEnabled { false }; bool expPrefixEnabled { false }; bool timeStampPrefixEnabled { false }; QString rawPrefix; JOBStatus status { JOB_IDLE }; // Flat field variables struct { double targetADU { 0 }; double targetADUTolerance { 250 }; FlatFieldSource flatFieldSource { SOURCE_MANUAL }; FlatFieldDuration flatFieldDuration { DURATION_MANUAL }; SkyPoint wallCoord; bool preMountPark { false }; bool preDomePark { false }; } calibrationSettings; QMap prepareActions; QMap> customProperties; // Filter Manager QSharedPointer filterManager; }; } diff --git a/kstars/ekos/guide/externalguide/linguider.cpp b/kstars/ekos/guide/externalguide/linguider.cpp index 299b6a128..9d4e60578 100644 --- a/kstars/ekos/guide/externalguide/linguider.cpp +++ b/kstars/ekos/guide/externalguide/linguider.cpp @@ -1,368 +1,364 @@ /* Ekos Lin Guider Handler Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "linguider.h" #include "Options.h" #include #include #include namespace Ekos { LinGuider::LinGuider() { tcpSocket = new QTcpSocket(this); rawBuffer.clear(); connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readLinGuider())); connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError))); connect(tcpSocket, SIGNAL(connected()), this, SLOT(onConnected())); deviationTimer.setInterval(1000); connect(&deviationTimer, &QTimer::timeout, this, [&]() { sendCommand(GET_RA_DEC_DRIFT); }); } -LinGuider::~LinGuider() -{ -} - bool LinGuider::Connect() { if (connection == DISCONNECTED) { rawBuffer.clear(); connection = CONNECTING; tcpSocket->connectToHost(Options::linGuiderHost(), Options::linGuiderPort()); } // Already connected, let's connect equipment else emit newStatus(GUIDE_CONNECTED); return true; } bool LinGuider::Disconnect() { rawBuffer.clear(); connection = DISCONNECTED; tcpSocket->disconnectFromHost(); emit newStatus(GUIDE_DISCONNECTED); return true; } void LinGuider::displayError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: emit newLog(i18n("The host was not found. Please check the host name and port settings in Guide options.")); emit newStatus(GUIDE_DISCONNECTED); break; case QAbstractSocket::ConnectionRefusedError: emit newLog(i18n("The connection was refused by the peer. Make sure the LinGuider is running, and check " "that the host name and port settings are correct.")); emit newStatus(GUIDE_DISCONNECTED); break; default: emit newLog(i18n("The following error occurred: %1.", tcpSocket->errorString())); } connection = DISCONNECTED; } void LinGuider::readLinGuider() { while (tcpSocket->atEnd() == false) { rawBuffer += tcpSocket->readAll(); while (1) { if (rawBuffer.count() < 8) break; if (Options::guideLogging()) qDebug() << "Guide:" << rawBuffer; qint16 magicNumber = *(reinterpret_cast(rawBuffer.data())); if (magicNumber != 0x02) { emit newLog(i18n("Invalid response.")); rawBuffer = rawBuffer.mid(1); continue; } qint16 command = *(reinterpret_cast(rawBuffer.data() + 2)); if (command < GET_VER || command > GET_RA_DEC_DRIFT) { emit newLog(i18n("Invalid response.")); rawBuffer = rawBuffer.mid(1); continue; } qint16 datalen = *(reinterpret_cast(rawBuffer.data() + 4)); if (rawBuffer.count() < datalen + 8) break; QString reply = rawBuffer.mid(8, datalen); processResponse(static_cast(command), reply); rawBuffer = rawBuffer.mid(8 + datalen); } } } void LinGuider::processResponse(LinGuiderCommand command, const QString &reply) { if (reply == "Error: Guiding not started.") { state = IDLE; emit newStatus(GUIDE_ABORTED); deviationTimer.stop(); return; } switch (command) { case GET_VER: emit newLog(i18n("Connected to LinGuider %1", reply)); if (reply < "v.4.1.0") { emit newLog( i18n("Only LinGuider v4.1.0 or higher is supported. Please upgrade LinGuider and try again.")); Disconnect(); } sendCommand(GET_GUIDER_STATE); break; case GET_GUIDER_STATE: if (reply == "GUIDING") { state = GUIDING; emit newStatus(GUIDE_GUIDING); deviationTimer.start(); } else { state = IDLE; deviationTimer.stop(); } break; case FIND_STAR: { emit newLog(i18n("Auto star selected %1", reply)); QStringList pos = reply.split(' '); if (pos.count() == 2) { starCenter = reply; sendCommand(SET_GUIDER_RETICLE_POS, reply); } else { emit newLog(i18n("Failed to process star position.")); emit newStatus(GUIDE_CALIBRATION_ERROR); } } break; case SET_GUIDER_RETICLE_POS: if (reply == "OK") { sendCommand(SET_GUIDER_SQUARE_POS, starCenter); } else { emit newLog(i18n("Failed to set guider reticle position.")); emit newStatus(GUIDE_CALIBRATION_ERROR); } break; case SET_GUIDER_SQUARE_POS: if (reply == "OK") { emit newStatus(GUIDE_CALIBRATION_SUCESS); } else { emit newLog(i18n("Failed to set guider square position.")); emit newStatus(GUIDE_CALIBRATION_ERROR); } break; case GUIDER: if (reply == "OK") { if (state == IDLE) { emit newStatus(GUIDE_GUIDING); state = GUIDING; deviationTimer.start(); } else { emit newStatus(GUIDE_IDLE); state = IDLE; deviationTimer.stop(); } } else { if (state == IDLE) emit newLog(i18n("Failed to start guider.")); else emit newLog(i18n("Failed to stop guider.")); } break; case GET_RA_DEC_DRIFT: { if (state != GUIDING) { state = GUIDING; emit newStatus(GUIDE_GUIDING); } QStringList pos = reply.split(' '); if (pos.count() == 2) { bool raOK = false, deOK = false; double raDev = pos[0].toDouble(&raOK); double deDev = pos[1].toDouble(&deOK); if (raDev && deDev) emit newAxisDelta(raDev, deDev); } else { emit newLog(i18n("Failed to get RA/DEC Drift.")); } } break; case SET_DITHERING_RANGE: if (reply == "OK") { sendCommand(DITHER); deviationTimer.stop(); } else { emit newLog(i18n("Failed to set dither range.")); } break; case DITHER: if (reply == "Long time cmd finished") emit newStatus(GUIDE_DITHERING_SUCCESS); else emit newStatus(GUIDE_DITHERING_ERROR); state = GUIDING; deviationTimer.start(); break; default: break; } } void LinGuider::onConnected() { connection = CONNECTED; emit newStatus(GUIDE_CONNECTED); // Get version sendCommand(GET_VER); } void LinGuider::sendCommand(LinGuiderCommand command, const QString &args) { // Command format: Magic Number (0x00 0x02), cmd (2 bytes), len_of_param (4 bytes), param (ascii) int size = 8 + args.size(); QByteArray cmd(size, 0); // Magic number cmd[0] = 0x02; cmd[1] = 0x00; // Command cmd[2] = command; cmd[3] = 0x00; // Len qint32 len = args.size(); memcpy(cmd.data() + 4, &len, 4); // Params if (args.isEmpty() == false) memcpy(cmd.data() + 8, args.toLatin1().data(), args.size()); tcpSocket->write(cmd); } bool LinGuider::calibrate() { // Let's start calibraiton. It is already calibrated but in this step we auto-select and star and set the square emit newStatus(Ekos::GUIDE_CALIBRATING); sendCommand(FIND_STAR); return true; } bool LinGuider::guide() { sendCommand(GUIDER, "start"); return true; } bool LinGuider::abort() { sendCommand(GUIDER, "stop"); return true; } bool LinGuider::suspend() { return abort(); } bool LinGuider::resume() { return guide(); } bool LinGuider::dither(double pixels) { QString pixelsString = QString::number(pixels, 'f', 2); QString args = QString("%1 %2").arg(pixelsString, pixelsString); sendCommand(SET_DITHERING_RANGE, args); return true; } } diff --git a/kstars/ekos/guide/externalguide/linguider.h b/kstars/ekos/guide/externalguide/linguider.h index d36a5ae79..f77e86df7 100644 --- a/kstars/ekos/guide/externalguide/linguider.h +++ b/kstars/ekos/guide/externalguide/linguider.h @@ -1,86 +1,86 @@ /* Ekos Lin Guider Handler Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once +#include "../guideinterface.h" + #include #include -#include "../guideinterface.h" - class QTcpSocket; namespace Ekos { /** * @class LinGuider * Uses external LinGuider for guiding. * * @author Jasem Mutlaq * @version 1.0 */ class LinGuider : public GuideInterface { Q_OBJECT public: typedef enum { GET_VER = 1, SET_GUIDER_SQUARE_POS, SAVE_FRAME, DITHER, DITHER_NO_WAIT_XY, GET_DISTANCE, SAVE_FRAME_DECORATED, GUIDER, GET_GUIDER_STATE, SET_GUIDER_OVLS_POS, SET_GUIDER_RETICLE_POS, FIND_STAR, SET_DITHERING_RANGE, GET_RA_DEC_DRIFT } LinGuiderCommand; typedef enum { IDLE, GUIDING } LinGuiderState; typedef enum { DISCONNECTED, CONNECTING, CONNECTED } LinGuiderConnection; LinGuider(); - ~LinGuider(); + virtual ~LinGuider() override = default; bool Connect() override; bool Disconnect() override; bool isConnected() override { return (connection == CONNECTED); } bool calibrate() override; bool guide() override; bool abort() override; bool suspend() override; bool resume() override; bool dither(double pixels) override; bool clearCalibration() override { return true;} private slots: void readLinGuider(); void onConnected(); void displayError(QAbstractSocket::SocketError socketError); private: void sendCommand(LinGuiderCommand command, const QString &args = QString()); void processResponse(LinGuiderCommand command, const QString &reply); QTcpSocket *tcpSocket { nullptr }; QByteArray rawBuffer; LinGuiderState state { IDLE }; LinGuiderConnection connection { DISCONNECTED }; QTimer deviationTimer; QString starCenter; }; } diff --git a/kstars/ekos/guide/guideinterface.cpp b/kstars/ekos/guide/guideinterface.cpp index 2c8905436..dccac03ab 100644 --- a/kstars/ekos/guide/guideinterface.cpp +++ b/kstars/ekos/guide/guideinterface.cpp @@ -1,77 +1,73 @@ /* Ekos Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "guideinterface.h" #include "guide.h" #define MAX_GUIDE_STARS 10 namespace Ekos { -GuideInterface::GuideInterface() -{ -} - bool GuideInterface::setGuiderParams(double ccdPixelSizeX, double ccdPixelSizeY, double mountAperture, double mountFocalLength) { this->ccdPixelSizeX = ccdPixelSizeX; this->ccdPixelSizeY = ccdPixelSizeY; this->mountAperture = mountAperture; this->mountFocalLength = mountFocalLength; return true; } bool GuideInterface::getGuiderParams(double *ccdPixelSizeX, double *ccdPixelSizeY, double *mountAperture, double *mountFocalLength) { *ccdPixelSizeX = this->ccdPixelSizeX; *ccdPixelSizeY = this->ccdPixelSizeY; *mountAperture = this->mountAperture; *mountFocalLength = this->mountFocalLength; return true; } bool GuideInterface::setFrameParams(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t binX, uint16_t binY) { if (w <= 0 || h <= 0) return false; subX = x; subY = y; subW = w; subH = h; subBinX = binX; subBinY = binY; return true; } bool GuideInterface::getFrameParams(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h, uint16_t *binX, uint16_t *binY) { *x = subX; *y = subY; *w = subW; *h = subH; *binX = subBinX; *binY = subBinY; return true; } void GuideInterface::setStarPosition(QVector3D& starCenter) { INDI_UNUSED(starCenter); } } diff --git a/kstars/ekos/guide/guideinterface.h b/kstars/ekos/guide/guideinterface.h index 75f04925b..f2ee051e3 100644 --- a/kstars/ekos/guide/guideinterface.h +++ b/kstars/ekos/guide/guideinterface.h @@ -1,85 +1,85 @@ /* Ekos guide class interface Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "ekos/ekos.h" #include #include #include class QString; namespace Ekos { /** * @class GuideInterface * @short Interface skeleton for implementation of different guiding applications and/or routines * * @author Jasem Mutlaq * @version 1.0 */ class GuideInterface : public QObject { Q_OBJECT public: - GuideInterface(); - virtual ~GuideInterface() {} + GuideInterface() = default; + virtual ~GuideInterface() override = default; virtual bool Connect() = 0; virtual bool Disconnect() = 0; virtual bool isConnected() = 0; virtual bool calibrate() = 0; virtual bool guide() = 0; virtual bool suspend() = 0; virtual bool resume() = 0; virtual bool abort() = 0; virtual bool dither(double pixels) = 0; virtual bool clearCalibration() = 0; virtual bool reacquire() { return false; } virtual bool setGuiderParams(double ccdPixelSizeX, double ccdPixelSizeY, double mountAperture, double mountFocalLength); virtual bool getGuiderParams(double *ccdPixelSizeX, double *ccdPixelSizeY, double *mountAperture, double *mountFocalLength); virtual bool setFrameParams(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t binX, uint16_t binY); virtual bool getFrameParams(uint16_t *x, uint16_t *y, uint16_t *w, uint16_t *h, uint16_t *binX, uint16_t *binY); virtual void setStarPosition(QVector3D& starCenter); signals: void newLog(const QString &); void newStatus(Ekos::GuideState); void newAxisDelta(double delta_ra, double delta_dec); void newAxisSigma(double sigma_ra, double sigma_dec); void newAxisPulse(double pulse_ra, double pulse_dec); void newStarPosition(QVector3D, bool); void newStarPixmap(QPixmap &); void frameCaptureRequested(); protected: Ekos::GuideState state { GUIDE_IDLE }; double ccdPixelSizeX { 0 }; double ccdPixelSizeY { 0 }; double mountAperture { 0 }; double mountFocalLength { 0 }; uint16_t subX { 0 }; uint16_t subY { 0 }; uint16_t subW { 0 }; uint16_t subH { 0 }; uint16_t subBinX { 1 }; uint16_t subBinY { 1 }; }; } diff --git a/kstars/ekos/guide/internalguide/gmath.cpp b/kstars/ekos/guide/internalguide/gmath.cpp index 2d1c02398..a53256e03 100644 --- a/kstars/ekos/guide/internalguide/gmath.cpp +++ b/kstars/ekos/guide/internalguide/gmath.cpp @@ -1,1796 +1,1796 @@ /* Ekos guide tool Copyright (C) 2012 Andrew Stepanenko Modified by Jasem Mutlaq for KStars. This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "gmath.h" #include "imageautoguiding.h" #include "Options.h" #include "fitsviewer/fitsdata.h" #include "fitsviewer/fitsview.h" #include "auxiliary/kspaths.h" #include "ekos_guide_debug.h" #include #include #define DEF_SQR_0 (8 - 0) #define DEF_SQR_1 (16 - 0) #define DEF_SQR_2 (32 - 0) #define DEF_SQR_3 (64 - 0) #define DEF_SQR_4 (128 - 0) const guide_square_t guide_squares[] = { { DEF_SQR_0, DEF_SQR_0 *DEF_SQR_0 * 1.0 }, { DEF_SQR_1, DEF_SQR_1 *DEF_SQR_1 * 1.0 }, { DEF_SQR_2, DEF_SQR_2 *DEF_SQR_2 * 1.0 }, { DEF_SQR_3, DEF_SQR_3 *DEF_SQR_3 * 1.0 }, { DEF_SQR_4, DEF_SQR_4 *DEF_SQR_4 * 1.0 }, { -1, -1 } }; const square_alg_t guide_square_alg[] = { { SMART_THRESHOLD, "Smart" }, { SEP_THRESHOLD, "SEP" }, { CENTROID_THRESHOLD, "Fast" }, { AUTO_THRESHOLD, "Auto" }, { NO_THRESHOLD, "No thresh." }, { -1, { 0 } } }; struct Peak { int x; int y; float val; - Peak() { } + Peak() = default; Peak(int x_, int y_, float val_) : x(x_), y(y_), val(val_) { } bool operator<(const Peak& rhs) const { return val < rhs.val; } }; // JM: Why not use QPoint? typedef struct { int x, y; } point_t; cgmath::cgmath() : QObject() { // sys... ROT_Z = Ekos::Matrix(0); // sky coord. system vars. star_pos = Vector(0); scr_star_pos = Vector(0); reticle_pos = Vector(0); reticle_orts[0] = Vector(0); reticle_orts[1] = Vector(0); reticle_angle = 0; ditherRate[0] = ditherRate[1] = -1; // processing in_params.reset(); out_params.reset(); channel_ticks[GUIDE_RA] = channel_ticks[GUIDE_DEC] = 0; accum_ticks[GUIDE_RA] = accum_ticks[GUIDE_DEC] = 0; drift[GUIDE_RA] = new double[MAX_ACCUM_CNT]; drift[GUIDE_DEC] = new double[MAX_ACCUM_CNT]; memset(drift[GUIDE_RA], 0, sizeof(double) * MAX_ACCUM_CNT); memset(drift[GUIDE_DEC], 0, sizeof(double) * MAX_ACCUM_CNT); drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0; QString logFileName = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "guide_log.txt"; logFile.setFileName(logFileName); } cgmath::~cgmath() { delete[] drift[GUIDE_RA]; delete[] drift[GUIDE_DEC]; foreach (float *region, referenceRegions) delete[] region; referenceRegions.clear(); } bool cgmath::setVideoParameters(int vid_wd, int vid_ht, int binX, int binY) { if (vid_wd <= 0 || vid_ht <= 0) return false; video_width = vid_wd / binX; video_height = vid_ht / binY; subBinX = binX; subBinY = binY; //set_reticle_params( video_width/2, video_height/2, -1 ); // keep orientation return true; } void cgmath::setGuideView(FITSView *image) { guideView = image; /*if (guideView) { FITSData *image_data = guideView->getImageData(); setDataBuffer(image_data->getImageBuffer()); setVideoParameters(image_data->getWidth(), image_data->getHeight()); }*/ } bool cgmath::setGuiderParameters(double ccd_pix_wd, double ccd_pix_ht, double guider_aperture, double guider_focal) { if (ccd_pix_wd < 0) ccd_pix_wd = 0; if (ccd_pix_ht < 0) ccd_pix_ht = 0; if (guider_focal <= 0) guider_focal = 1; ccd_pixel_width = ccd_pix_wd / 1000.0; // from mkm to mm ccd_pixel_height = ccd_pix_ht / 1000.0; // from mkm to mm aperture = guider_aperture; focal = guider_focal; return true; } void cgmath::getGuiderParameters(double *ccd_pix_wd, double *ccd_pix_ht, double *guider_aperture, double *guider_focal) { *ccd_pix_wd = ccd_pixel_width * 1000.0; *ccd_pix_ht = ccd_pixel_height * 1000.0; *guider_aperture = aperture; *guider_focal = focal; } void cgmath::createGuideLog() { logFile.close(); logFile.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&logFile); out << "Guiding rate,x15 arcsec/sec: " << Options::guidingRate() << endl; out << "Focal,mm: " << focal << endl; out << "Aperture,mm: " << aperture << endl; out << "F/D: " << focal / aperture << endl; out << "Frame #, Time Elapsed (ms), RA Error (arcsec), RA Correction (ms), RA Correction Direction, DEC Error " "(arcsec), DEC Correction (ms), DEC Correction Direction" << endl; logTime.restart(); } bool cgmath::setReticleParameters(double x, double y, double ang) { // check frame ranges if (x < 0) x = 0; if (y < 0) y = 0; if (x >= (double)video_width - 1) x = (double)video_width - 1; if (y >= (double)video_height - 1) y = (double)video_height - 1; reticle_pos = Vector(x, y, 0); if (ang >= 0) reticle_angle = ang; ROT_Z = Ekos::RotateZ(-M_PI * reticle_angle / 180.0); // NOTE!!! sing '-' derotates star coordinate system reticle_orts[0] = Vector(1, 0, 0) * 100; reticle_orts[1] = Vector(0, 1, 0) * 100; reticle_orts[0] = reticle_orts[0] * ROT_Z; reticle_orts[1] = reticle_orts[1] * ROT_Z; return true; } bool cgmath::getReticleParameters(double *x, double *y, double *ang) const { *x = reticle_pos.x; *y = reticle_pos.y; if (ang) *ang = reticle_angle; return true; } int cgmath::getSquareAlgorithmIndex(void) const { return square_alg_idx; } info_params_t cgmath::getInfoParameters(void) const { info_params_t ret; Vector p; ret.aperture = aperture; ret.focal = focal; ret.focal_ratio = focal / aperture; p = Vector(video_width, video_height, 0); p = point2arcsec(p); p /= 60; // convert to minutes ret.fov_wd = p.x; ret.fov_ht = p.y; return ret; } uint32_t cgmath::getTicks(void) const { return ticks; } void cgmath::getStarDrift(double *dx, double *dy) const { *dx = star_pos.x; *dy = star_pos.y; } void cgmath::getStarScreenPosition(double *dx, double *dy) const { *dx = scr_star_pos.x; *dy = scr_star_pos.y; } bool cgmath::reset(void) { square_alg_idx = AUTO_THRESHOLD; // sky coord. system vars. star_pos = Vector(0); scr_star_pos = Vector(0); setReticleParameters(video_width / 2, video_height / 2, 0.0); return true; } /*void cgmath::move_square( double newx, double newy ) { square_pos.x = newx; square_pos.y = newy; // check frame ranges if (lastBinX == subBinX) { if( square_pos.x < 0 ) square_pos.x = 0; if( square_pos.y < 0 ) square_pos.y = 0; if( square_pos.x+(double)square_size > (double)video_width ) square_pos.x = (double)(video_width - square_size); if( square_pos.y+(double)square_size > (double)video_height ) square_pos.y = (double)(video_height - square_size); } // FITS Image takes center coords if (guide_frame) { guide_frame->setTrackingBoxEnabled(true); //guide_frame->setTrackingBoxCenter(QPointF(square_pos.x+square_size/2, square_pos.y+square_size/2)); guide_frame->setTrackingBox(QRect(square_pos.x, square_pos.y, square_size/subBinX, square_size/subBinY)); } } void cgmath::resize_square( int size_idx ) { if( size_idx < 0 || size_idx >= (int)(sizeof(guide_squares)/sizeof(guide_square_t))-1) return; if (square_size != guide_squares[size_idx].size) { square_pos.x += (square_size-guide_squares[size_idx].size)/2; square_pos.y += (square_size-guide_squares[size_idx].size)/2; } square_size = guide_squares[size_idx].size; square_square = guide_squares[size_idx].square; square_idx = size_idx; // check position if (guide_frame) { guide_frame->setTrackingBoxEnabled(true); //guide_frame->setTrackingBoxSize(QSize(square_size,square_size)); guide_frame->setTrackingBox(QRect(square_pos.x/subBinX, square_pos.y/subBinY, square_size/subBinX, square_size/subBinY)); } }*/ void cgmath::setSquareAlgorithm(int alg_idx) { if (alg_idx < 0 || alg_idx >= (int)(sizeof(guide_square_alg) / sizeof(square_alg_t)) - 1) return; square_alg_idx = alg_idx; in_params.threshold_alg_idx = square_alg_idx; } Vector cgmath::point2arcsec(const Vector &p) const { Vector arcs; // arcs = 3600*180/pi * (pix*ccd_pix_sz) / focal_len arcs.x = 206264.8062470963552 * p.x * ccd_pixel_width / focal; arcs.y = 206264.8062470963552 * p.y * ccd_pixel_height / focal; return arcs; } bool cgmath::calculateAndSetReticle1D(double start_x, double start_y, double end_x, double end_y, int RATotalPulse) { double phi; phi = calculatePhi(start_x, start_y, end_x, end_y); if (phi < 0) return false; setReticleParameters(start_x, start_y, phi); if (RATotalPulse > 0) { double x = end_x - start_x; double y = end_y - start_y; double len = sqrt(x * x + y * y); // Total pulse includes start --> end --> start ditherRate[GUIDE_RA] = RATotalPulse / (2 * len); qCDebug(KSTARS_EKOS_GUIDE) << "Dither RA Rate " << ditherRate[GUIDE_RA] << " ms/Pixel"; } return true; } bool cgmath::calculateAndSetReticle2D(double start_ra_x, double start_ra_y, double end_ra_x, double end_ra_y, double start_dec_x, double start_dec_y, double end_dec_x, double end_dec_y, bool *swap_dec, int RATotalPulse, int DETotalPulse) { double phi_ra = 0; // angle calculated by GUIDE_RA drift double phi_dec = 0; // angle calculated by GUIDE_DEC drift double phi = 0; Vector ra_vect = Normalize(Vector(end_ra_x - start_ra_x, -(end_ra_y - start_ra_y), 0)); Vector dec_vect = Normalize(Vector(end_dec_x - start_dec_x, -(end_dec_y - start_dec_y), 0)); Vector try_increase = dec_vect * Ekos::RotateZ(M_PI / 2); Vector try_decrease = dec_vect * Ekos::RotateZ(-M_PI / 2); double cos_increase = try_increase & ra_vect; double cos_decrease = try_decrease & ra_vect; bool do_increase = cos_increase > cos_decrease ? true : false; phi_ra = calculatePhi(start_ra_x, start_ra_y, end_ra_x, end_ra_y); if (phi_ra < 0) return false; phi_dec = calculatePhi(start_dec_x, start_dec_y, end_dec_x, end_dec_y); if (phi_dec < 0) return false; if (do_increase) phi_dec += 90; else phi_dec -= 90; if (phi_dec > 360) phi_dec -= 360.0; if (phi_dec < 0) phi_dec += 360.0; if (fabs(phi_dec - phi_ra) > 180) { if (phi_ra > phi_dec) phi_ra -= 360; else phi_dec -= 360; } // average angles phi = (phi_ra + phi_dec) / 2; if (phi < 0) phi += 360.0; // check DEC if (swap_dec) *swap_dec = dec_swap = do_increase ? false : true; setReticleParameters(start_ra_x, start_ra_y, phi); if (RATotalPulse > 0) { double x = end_ra_x - start_ra_x; double y = end_ra_y - start_ra_y; double len = sqrt(x * x + y * y); ditherRate[GUIDE_RA] = RATotalPulse / (2 * len); qCDebug(KSTARS_EKOS_GUIDE) << "Dither RA Rate " << ditherRate[GUIDE_RA] << " ms/Pixel"; } if (DETotalPulse > 0) { double x = end_dec_x - start_dec_x; double y = end_dec_y - start_dec_y; double len = sqrt(x * x + y * y); ditherRate[GUIDE_DEC] = DETotalPulse / (2 * len); qCDebug(KSTARS_EKOS_GUIDE) << "Dither DEC Rate " << ditherRate[GUIDE_DEC] << " ms/Pixel"; } return true; } double cgmath::calculatePhi(double start_x, double start_y, double end_x, double end_y) const { double delta_x, delta_y; double phi; delta_x = end_x - start_x; delta_y = -(end_y - start_y); //if( (!Vector(delta_x, delta_y, 0)) < 2.5 ) // JM 2015-12-10: Lower threshold to 1 pixel if ((!Vector(delta_x, delta_y, 0)) < 1) return -1; // 90 or 270 degrees if (fabs(delta_x) < fabs(delta_y) / 1000000.0) { phi = delta_y > 0 ? 90.0 : 270; } else { phi = 180.0 / M_PI * atan2(delta_y, delta_x); if (phi < 0) phi += 360.0; } return phi; } void cgmath::do_ticks(void) { ticks++; channel_ticks[GUIDE_RA]++; channel_ticks[GUIDE_DEC]++; if (channel_ticks[GUIDE_RA] >= MAX_ACCUM_CNT) channel_ticks[GUIDE_RA] = 0; if (channel_ticks[GUIDE_DEC] >= MAX_ACCUM_CNT) channel_ticks[GUIDE_DEC] = 0; accum_ticks[GUIDE_RA]++; accum_ticks[GUIDE_DEC]++; if (accum_ticks[GUIDE_RA] >= in_params.accum_frame_cnt[GUIDE_RA]) accum_ticks[GUIDE_RA] = 0; if (accum_ticks[GUIDE_DEC] >= in_params.accum_frame_cnt[GUIDE_DEC]) accum_ticks[GUIDE_DEC] = 0; } //-------------------- Processing --------------------------- void cgmath::start(void) { ticks = 0; channel_ticks[GUIDE_RA] = channel_ticks[GUIDE_DEC] = 0; accum_ticks[GUIDE_RA] = accum_ticks[GUIDE_DEC] = 0; drift_integral[GUIDE_RA] = drift_integral[GUIDE_DEC] = 0; out_params.reset(); memset(drift[GUIDE_RA], 0, sizeof(double) * MAX_ACCUM_CNT); memset(drift[GUIDE_DEC], 0, sizeof(double) * MAX_ACCUM_CNT); // cleanup stat vars. sum = 0; preview_mode = false; if (focal > 0 && aperture > 0) createGuideLog(); // Create reference Image if (imageGuideEnabled) { foreach (float *region, referenceRegions) delete[] region; referenceRegions.clear(); referenceRegions = partitionImage(); reticle_pos = Vector(0, 0, 0); } } void cgmath::stop(void) { preview_mode = true; } void cgmath::suspend(bool mode) { suspended = mode; } bool cgmath::isSuspended(void) const { return suspended; } bool cgmath::isStarLost(void) const { return lost_star; } void cgmath::setLostStar(bool is_lost) { lost_star = is_lost; } float *cgmath::createFloatImage(FITSData *target) const { FITSData *imageData = target; if (imageData == nullptr) imageData = guideView->getImageData(); // #1 Convert to float array // We only process 1st plane if it is a color image uint32_t imgSize = imageData->getSize(); float *imgFloat = new float[imgSize]; if (imgFloat == nullptr) { qCritical() << "Not enough memory for float image array!"; return nullptr; } switch (imageData->getDataType()) { case TBYTE: { uint8_t *buffer = imageData->getImageBuffer(); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TSHORT: { int16_t *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TUSHORT: { uint16_t *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TLONG: { int32_t *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TULONG: { uint32_t *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TFLOAT: { float *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TLONGLONG: { int64_t *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; case TDOUBLE: { double *buffer = reinterpret_cast(imageData->getImageBuffer()); for (uint32_t i = 0; i < imgSize; i++) imgFloat[i] = buffer[i]; } break; default: delete[] imgFloat; return nullptr; } return imgFloat; } QVector cgmath::partitionImage() const { QVector regions; FITSData *imageData = guideView->getImageData(); float *imgFloat = createFloatImage(); if (imgFloat == nullptr) return regions; const uint32_t width = imageData->getWidth(); const uint32_t height = imageData->getHeight(); uint8_t xRegions = floor(width / regionAxis); uint8_t yRegions = floor(height / regionAxis); // Find number of regions to divide the image //uint8_t regions = xRegions * yRegions; float *regionPtr = imgFloat; for (uint8_t i = 0; i < yRegions; i++) { for (uint8_t j = 0; j < xRegions; j++) { // Allocate space for one region float *oneRegion = new float[regionAxis * regionAxis]; // Create points to region and current location of the source image in the desired region float *oneRegionPtr = oneRegion, *imgFloatPtr = regionPtr + j * regionAxis; // copy from image to region line by line for (uint32_t line = 0; line < regionAxis; line++) { memcpy(oneRegionPtr, imgFloatPtr, regionAxis); oneRegionPtr += regionAxis; imgFloatPtr += width; } regions.append(oneRegion); } // Move regionPtr block by (width * regionAxis) elements regionPtr += width * regionAxis; } // We're done with imgFloat delete[] imgFloat; return regions; } void cgmath::setRegionAxis(const uint32_t &value) { regionAxis = value; } Vector cgmath::findLocalStarPosition(void) const { if (useRapidGuide) { return Vector(rapidDX, rapidDY, 0); } FITSData *imageData = guideView->getImageData(); if (imageGuideEnabled) { float xshift = 0, yshift = 0; QVector shifts; float xsum = 0, ysum = 0; QVector imageParition = partitionImage(); if (imageParition.isEmpty()) { qWarning() << "Failed to partiion regions in image!"; return Vector(-1, -1, -1); } if (imageParition.count() != referenceRegions.count()) { qWarning() << "Mismatch between reference regions #" << referenceRegions.count() << "and image parition regions #" << imageParition.count(); // Clear memory in case of mis-match foreach (float *region, imageParition) { delete[] region; } return Vector(-1, -1, -1); } for (uint8_t i = 0; i < imageParition.count(); i++) { ImageAutoGuiding::ImageAutoGuiding1(referenceRegions[i], imageParition[i], regionAxis, &xshift, &yshift); Vector shift(xshift, yshift, -1); qCDebug(KSTARS_EKOS_GUIDE) << "Region #" << i << ": X-Shift=" << xshift << "Y-Shift=" << yshift; xsum += xshift; ysum += yshift; shifts.append(shift); } // Delete partitions foreach (float *region, imageParition) { delete[] region; } imageParition.clear(); float average_x = xsum / referenceRegions.count(); float average_y = ysum / referenceRegions.count(); float median_x = shifts[referenceRegions.count() / 2 - 1].x; float median_y = shifts[referenceRegions.count() / 2 - 1].y; qCDebug(KSTARS_EKOS_GUIDE) << "Average : X-Shift=" << average_x << "Y-Shift=" << average_y; qCDebug(KSTARS_EKOS_GUIDE) << "Median : X-Shift=" << median_x << "Y-Shift=" << median_y; return Vector(median_x, median_y, -1); } switch (imageData->getDataType()) { case TBYTE: return findLocalStarPosition(); break; case TSHORT: return findLocalStarPosition(); break; case TUSHORT: return findLocalStarPosition(); break; case TLONG: return findLocalStarPosition(); break; case TULONG: return findLocalStarPosition(); break; case TFLOAT: return findLocalStarPosition(); break; case TLONGLONG: return findLocalStarPosition(); break; case TDOUBLE: return findLocalStarPosition(); break; default: break; } return Vector(-1, -1, -1); } template Vector cgmath::findLocalStarPosition(void) const { static double P0 = 0.906, P1 = 0.584, P2 = 0.365, P3 = 0.117, P4 = 0.049, P5 = -0.05, P6 = -0.064, P7 = -0.074, P8 = -0.094; Vector ret; int i, j; double resx, resy, mass, threshold, pval; T *psrc = nullptr; T *porigin = nullptr; T *pptr; QRect trackingBox = guideView->getTrackingBox(); if (trackingBox.isValid() == false) return Vector(-1, -1, -1); FITSData *imageData = guideView->getImageData(); if (imageData == nullptr) { qCWarning(KSTARS_EKOS_GUIDE) << "Cannot process a nullptr image."; return Vector(-1, -1, -1); } if (square_alg_idx == SEP_THRESHOLD) { int count = imageData->findStars(ALGORITHM_SEP, trackingBox); if (count > 0) { imageData->getHFR(HFR_MAX); Edge *star = imageData->getMaxHFRStar(); if (star) ret = Vector(star->x, star->y, 0); else ret = Vector(-1, -1, -1); //ret = Vector(star->x, star->y, 0) - Vector(trackingBox.x(), trackingBox.y(), 0); } else ret = Vector(-1, -1, -1); return ret; } T *pdata = reinterpret_cast(imageData->getImageBuffer()); qCDebug(KSTARS_EKOS_GUIDE) << "Tracking Square " << trackingBox; double square_square = trackingBox.width() * trackingBox.width(); psrc = porigin = pdata + trackingBox.y() * video_width + trackingBox.x(); resx = resy = 0; threshold = mass = 0; // several threshold adaptive smart agorithms switch (square_alg_idx) { case CENTROID_THRESHOLD: { int width = trackingBox.width(); int height = trackingBox.width(); float i0, i1, i2, i3, i4, i5, i6, i7, i8; int ix = 0, iy = 0; int xM4; T *p; double average, fit, bestFit = 0; int minx = 0; int maxx = width; int miny = 0; int maxy = height; for (int x = minx; x < maxx; x++) for (int y = miny; y < maxy; y++) { i0 = i1 = i2 = i3 = i4 = i5 = i6 = i7 = i8 = 0; xM4 = x - 4; p = psrc + (y - 4) * video_width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y - 3) * video_width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y - 2) * video_width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y - 1) * video_width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y + 0) * video_width + xM4; i8 += *p++; i6 += *p++; i3 += *p++; i1 += *p++; i0 += *p++; i1 += *p++; i3 += *p++; i6 += *p++; i8 += *p++; p = psrc + (y + 1) * video_width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y + 2) * video_width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y + 3) * video_width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; p = psrc + (y + 4) * video_width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; average = (i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8) / 85.0; fit = P0 * (i0 - average) + P1 * (i1 - 4 * average) + P2 * (i2 - 4 * average) + P3 * (i3 - 4 * average) + P4 * (i4 - 8 * average) + P5 * (i5 - 4 * average) + P6 * (i6 - 4 * average) + P7 * (i7 - 8 * average) + P8 * (i8 - 48 * average); if (bestFit < fit) { bestFit = fit; ix = x; iy = y; } } if (bestFit > 50) { double sumX = 0; double sumY = 0; double total = 0; for (int y = iy - 4; y <= iy + 4; y++) { p = psrc + y * width + ix - 4; for (int x = ix - 4; x <= ix + 4; x++) { double w = *p++; sumX += x * w; sumY += y * w; total += w; } } if (total > 0) { ret = (Vector(trackingBox.x(), trackingBox.y(), 0) + Vector(sumX / total, sumY / total, 0)); return ret; } } return Vector(-1, -1, -1); } break; // Alexander's Stepanenko smart threshold algorithm case SMART_THRESHOLD: { point_t bbox_lt = { trackingBox.x() - SMART_FRAME_WIDTH, trackingBox.y() - SMART_FRAME_WIDTH }; point_t bbox_rb = { trackingBox.x() + trackingBox.width() + SMART_FRAME_WIDTH, trackingBox.y() + trackingBox.width() + SMART_FRAME_WIDTH }; int offset = 0; // clip frame if (bbox_lt.x < 0) bbox_lt.x = 0; if (bbox_lt.y < 0) bbox_lt.y = 0; if (bbox_rb.x > video_width) bbox_rb.x = video_width; if (bbox_rb.y > video_height) bbox_rb.y = video_height; // calc top bar int box_wd = bbox_rb.x - bbox_lt.x; int box_ht = trackingBox.y() - bbox_lt.y; int pix_cnt = 0; if (box_wd > 0 && box_ht > 0) { pix_cnt += box_wd * box_ht; for (j = bbox_lt.y; j < trackingBox.y(); ++j) { offset = j * video_width; for (i = bbox_lt.x; i < bbox_rb.x; ++i) { pptr = pdata + offset + i; threshold += *pptr; } } } // calc left bar box_wd = trackingBox.x() - bbox_lt.x; box_ht = trackingBox.width(); if (box_wd > 0 && box_ht > 0) { pix_cnt += box_wd * box_ht; for (j = trackingBox.y(); j < trackingBox.y() + box_ht; ++j) { offset = j * video_width; for (i = bbox_lt.x; i < trackingBox.x(); ++i) { pptr = pdata + offset + i; threshold += *pptr; } } } // calc right bar box_wd = bbox_rb.x - trackingBox.x() - trackingBox.width(); box_ht = trackingBox.width(); if (box_wd > 0 && box_ht > 0) { pix_cnt += box_wd * box_ht; for (j = trackingBox.y(); j < trackingBox.y() + box_ht; ++j) { offset = j * video_width; for (i = trackingBox.x() + trackingBox.width(); i < bbox_rb.x; ++i) { pptr = pdata + offset + i; threshold += *pptr; } } } // calc bottom bar box_wd = bbox_rb.x - bbox_lt.x; box_ht = bbox_rb.y - trackingBox.y() - trackingBox.width(); if (box_wd > 0 && box_ht > 0) { pix_cnt += box_wd * box_ht; for (j = trackingBox.y() + trackingBox.width(); j < bbox_rb.y; ++j) { offset = j * video_width; for (i = bbox_lt.x; i < bbox_rb.x; ++i) { pptr = pdata + offset + i; threshold += *pptr; } } } // find maximum double max_val = 0; for (j = 0; j < trackingBox.width(); ++j) { for (i = 0; i < trackingBox.width(); ++i) { pptr = psrc + i; if (*pptr > max_val) max_val = *pptr; } psrc += video_width; } if (pix_cnt != 0) threshold /= (double)pix_cnt; // cut by 10% higher then average threshold if (max_val > threshold) threshold += (max_val - threshold) * SMART_CUT_FACTOR; //log_i("smart thr. = %f cnt = %d", threshold, pix_cnt); break; } // simple adaptive threshold case AUTO_THRESHOLD: { for (j = 0; j < trackingBox.width(); ++j) { for (i = 0; i < trackingBox.width(); ++i) { pptr = psrc + i; threshold += *pptr; } psrc += video_width; } threshold /= square_square; break; } // no threshold subtracion default: { } } psrc = porigin; for (j = 0; j < trackingBox.width(); ++j) { for (i = 0; i < trackingBox.width(); ++i) { pptr = psrc + i; pval = *pptr - threshold; pval = pval < 0 ? 0 : pval; resx += (double)i * pval; resy += (double)j * pval; mass += pval; } psrc += video_width; } if (mass == 0) mass = 1; resx /= mass; resy /= mass; ret = Vector(trackingBox.x(), trackingBox.y(), 0) + Vector(resx, resy, 0); return ret; } void cgmath::process_axes(void) { int cnt = 0; double t_delta = 0; qCDebug(KSTARS_EKOS_GUIDE) << "Processing Axes"; in_params.proportional_gain[0] = Options::rAProportionalGain(); in_params.proportional_gain[1] = Options::dECProportionalGain(); in_params.integral_gain[0] = Options::rAIntegralGain(); in_params.integral_gain[1] = Options::rAIntegralGain(); in_params.derivative_gain[0] = Options::rADerivativeGain(); in_params.derivative_gain[1] = Options::dECDerivativeGain(); in_params.enabled[0] = Options::rAGuideEnabled(); in_params.enabled[1] = Options::dECGuideEnabled(); in_params.min_pulse_length[0] = Options::rAMinimumPulse(); in_params.min_pulse_length[1] = Options::dECMinimumPulse(); in_params.max_pulse_length[0] = Options::rAMaximumPulse(); in_params.max_pulse_length[1] = Options::dECMaximumPulse(); // RA W/E enable // East RA+ enabled? in_params.enabled_axis1[0] = Options::eastRAGuideEnabled(); // West RA- enabled? in_params.enabled_axis2[0] = Options::westRAGuideEnabled(); // DEC N/S enable // North DEC+ enabled? in_params.enabled_axis1[1] = Options::northDECGuideEnabled(); // South DEC- enabled? in_params.enabled_axis2[1] = Options::southDECGuideEnabled(); // process axes... for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) { // zero all out commands out_params.pulse_dir[k] = NO_DIR; if (accum_ticks[k] < in_params.accum_frame_cnt[k] - 1) continue; t_delta = 0; drift_integral[k] = 0; cnt = in_params.accum_frame_cnt[k]; for (int i = 0, idx = channel_ticks[k]; i < cnt; ++i) { t_delta += drift[k][idx]; qCDebug(KSTARS_EKOS_GUIDE) << "At #" << idx << "drift[" << k << "][" << idx << "] = " << drift[k][idx] << " , t_delta: " << t_delta; if (idx > 0) --idx; else idx = MAX_ACCUM_CNT - 1; } for (int i = 0; i < MAX_ACCUM_CNT; ++i) drift_integral[k] += drift[k][i]; out_params.delta[k] = t_delta / (double)cnt; drift_integral[k] /= (double)MAX_ACCUM_CNT; qCDebug(KSTARS_EKOS_GUIDE) << "delta [" << k << "]= " << out_params.delta[k]; qCDebug(KSTARS_EKOS_GUIDE) << "drift_integral[" << k << "]= " << drift_integral[k]; out_params.pulse_length[k] = fabs(out_params.delta[k] * in_params.proportional_gain[k] + drift_integral[k] * in_params.integral_gain[k]); out_params.pulse_length[k] = out_params.pulse_length[k] <= in_params.max_pulse_length[k] ? out_params.pulse_length[k] : in_params.max_pulse_length[k]; qCDebug(KSTARS_EKOS_GUIDE) << "pulse_length [" << k << "]= " << out_params.pulse_length[k]; // calc direction // We do not send pulse if direction is disabled completely, or if direction in a specific axis (e.g. N or S) is disabled if (!in_params.enabled[k] || (out_params.delta[k] > 0 && !in_params.enabled_axis1[k]) || (out_params.delta[k] < 0 && !in_params.enabled_axis2[k])) { out_params.pulse_dir[k] = NO_DIR; out_params.pulse_length[k] = 0; continue; } if (out_params.pulse_length[k] >= in_params.min_pulse_length[k]) { if (k == GUIDE_RA) out_params.pulse_dir[k] = out_params.delta[k] > 0 ? RA_DEC_DIR : RA_INC_DIR; // GUIDE_RA. right dir - decreases GUIDE_RA else { out_params.pulse_dir[k] = out_params.delta[k] > 0 ? DEC_INC_DIR : DEC_DEC_DIR; // GUIDE_DEC. // Reverse DEC direction if we are looking eastward //if (ROT_Z.x[0][0] > 0 || (ROT_Z.x[0][0] ==0 && ROT_Z.x[0][1] > 0)) //out_params.pulse_dir[k] = (out_params.pulse_dir[k] == DEC_INC_DIR) ? DEC_DEC_DIR : DEC_INC_DIR; } } else out_params.pulse_dir[k] = NO_DIR; qCDebug(KSTARS_EKOS_GUIDE) << "Direction : " << get_direction_string(out_params.pulse_dir[k]); } //emit newAxisDelta(out_params.delta[0], out_params.delta[1]); if (Options::guideLogging()) { QTextStream out(&logFile); out << ticks << "," << logTime.elapsed() << "," << out_params.delta[0] << "," << out_params.pulse_length[0] << "," << get_direction_string(out_params.pulse_dir[0]) << "," << out_params.delta[1] << "," << out_params.pulse_length[1] << "," << get_direction_string(out_params.pulse_dir[1]) << endl; } } void cgmath::performProcessing(void) { Vector arc_star_pos, arc_reticle_pos; // do nothing if suspended if (suspended) return; // find guiding star location in scr_star_pos = star_pos = findLocalStarPosition(); if (star_pos.x == -1 || std::isnan(star_pos.x)) { lost_star = true; return; } else lost_star = false; // move square overlay //TODO FIXME //moveSquare( round(star_pos.x) - (double)square_size/(2*subBinX), round(star_pos.y) - (double)square_size/(2*subBinY) ); QVector3D starCenter(star_pos.x, star_pos.y, 0); emit newStarPosition(starCenter, true); if (preview_mode) return; qCDebug(KSTARS_EKOS_GUIDE) << "################## BEGIN PROCESSING ##################"; // translate star coords into sky coord. system // convert from pixels into arcsecs arc_star_pos = point2arcsec(star_pos); arc_reticle_pos = point2arcsec(reticle_pos); qCDebug(KSTARS_EKOS_GUIDE) << "Star X : " << star_pos.x << " Y : " << star_pos.y; qCDebug(KSTARS_EKOS_GUIDE) << "Reticle X : " << reticle_pos.x << " Y :" << reticle_pos.y; qCDebug(KSTARS_EKOS_GUIDE) << "Star RA: " << arc_star_pos.x << " DEC: " << arc_star_pos.y; qCDebug(KSTARS_EKOS_GUIDE) << "Reticle RA: " << arc_reticle_pos.x << " DEC: " << arc_reticle_pos.y; // translate into sky coords. star_pos = arc_star_pos - arc_reticle_pos; star_pos.y = -star_pos.y; // invert y-axis as y picture axis is inverted qCDebug(KSTARS_EKOS_GUIDE) << "-------> BEFORE ROTATION Diff RA: " << star_pos.x << " DEC: " << star_pos.y; star_pos = star_pos * ROT_Z; // both coords are ready for math processing //put coord to drift list drift[GUIDE_RA][channel_ticks[GUIDE_RA]] = star_pos.x; drift[GUIDE_DEC][channel_ticks[GUIDE_DEC]] = star_pos.y; qCDebug(KSTARS_EKOS_GUIDE) << "-------> AFTER ROTATION Diff RA: " << star_pos.x << " DEC: " << star_pos.y; qCDebug(KSTARS_EKOS_GUIDE) << "RA channel ticks: " << channel_ticks[GUIDE_RA] << " DEC channel ticks: " << channel_ticks[GUIDE_DEC]; // make decision by axes process_axes(); // process statistics calc_square_err(); // finally process tickers do_ticks(); qCDebug(KSTARS_EKOS_GUIDE) << "################## FINISH PROCESSING ##################"; } void cgmath::calc_square_err(void) { if (!do_statistics) return; // through MAX_ACCUM_CNT values if (ticks == 0) return; for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) { double sqr_avg = 0; for (int i = 0; i < MAX_ACCUM_CNT; ++i) sqr_avg += drift[k][i] * drift[k][i]; out_params.sigma[k] = sqrt(sqr_avg / (double)MAX_ACCUM_CNT); } } void cgmath::setRapidGuide(bool enable) { useRapidGuide = enable; } double cgmath::getDitherRate(int axis) { if (axis < 0 || axis > 1) return -1; return ditherRate[axis]; } void cgmath::setRapidStarData(double dx, double dy) { rapidDX = dx; rapidDY = dy; } const char *cgmath::get_direction_string(GuideDirection dir) { switch (dir) { case RA_DEC_DIR: return "Decrease RA"; break; case RA_INC_DIR: return "Increase RA"; break; case DEC_DEC_DIR: return "Decrease DEC"; break; case DEC_INC_DIR: return "Increase DEC"; break; default: break; } return "NO DIR"; } bool cgmath::isImageGuideEnabled() const { return imageGuideEnabled; } void cgmath::setImageGuideEnabled(bool value) { imageGuideEnabled = value; } static void psf_conv(float *dst, const float *src, int width, int height) { //dst.Init(src.Size); // A B1 B2 C1 C2 C3 D1 D2 D3 const double PSF[] = { 0.906, 0.584, 0.365, .117, .049, -0.05, -.064, -.074, -.094 }; //memset(dst.px, 0, src.NPixels * sizeof(float)); /* PSF Grid is: D3 D3 D3 D3 D3 D3 D3 D3 D3 D3 D3 D3 D2 D1 D2 D3 D3 D3 D3 D3 C3 C2 C1 C2 C3 D3 D3 D3 D2 C2 B2 B1 B2 C2 D2 D3 D3 D1 C1 B1 A B1 C1 D1 D3 D3 D2 C2 B2 B1 B2 C2 D2 D3 D3 D3 C3 C2 C1 C2 C3 D3 D3 D3 D3 D3 D2 D1 D2 D3 D3 D3 D3 D3 D3 D3 D3 D3 D3 D3 D3 1@A 4@B1, B2, C1, C3, D1 8@C2, D2 44 * D3 */ int psf_size = 4; for (int y = psf_size; y < height - psf_size; y++) { for (int x = psf_size; x < width - psf_size; x++) { float A, B1, B2, C1, C2, C3, D1, D2, D3; #define PX(dx, dy) *(src + width * (y + (dy)) + x + (dx)) A = PX(+0, +0); B1 = PX(+0, -1) + PX(+0, +1) + PX(+1, +0) + PX(-1, +0); B2 = PX(-1, -1) + PX(+1, -1) + PX(-1, +1) + PX(+1, +1); C1 = PX(+0, -2) + PX(-2, +0) + PX(+2, +0) + PX(+0, +2); C2 = PX(-1, -2) + PX(+1, -2) + PX(-2, -1) + PX(+2, -1) + PX(-2, +1) + PX(+2, +1) + PX(-1, +2) + PX(+1, +2); C3 = PX(-2, -2) + PX(+2, -2) + PX(-2, +2) + PX(+2, +2); D1 = PX(+0, -3) + PX(-3, +0) + PX(+3, +0) + PX(+0, +3); D2 = PX(-1, -3) + PX(+1, -3) + PX(-3, -1) + PX(+3, -1) + PX(-3, +1) + PX(+3, +1) + PX(-1, +3) + PX(+1, +3); D3 = PX(-4, -2) + PX(-3, -2) + PX(+3, -2) + PX(+4, -2) + PX(-4, -1) + PX(+4, -1) + PX(-4, +0) + PX(+4, +0) + PX(-4, +1) + PX(+4, +1) + PX(-4, +2) + PX(-3, +2) + PX(+3, +2) + PX(+4, +2); #undef PX int i; const float *uptr; uptr = src + width * (y - 4) + (x - 4); for (i = 0; i < 9; i++) D3 += *uptr++; uptr = src + width * (y - 3) + (x - 4); for (i = 0; i < 3; i++) D3 += *uptr++; uptr += 3; for (i = 0; i < 3; i++) D3 += *uptr++; uptr = src + width * (y + 3) + (x - 4); for (i = 0; i < 3; i++) D3 += *uptr++; uptr += 3; for (i = 0; i < 3; i++) D3 += *uptr++; uptr = src + width * (y + 4) + (x - 4); for (i = 0; i < 9; i++) D3 += *uptr++; double mean = (A + B1 + B2 + C1 + C2 + C3 + D1 + D2 + D3) / 81.0; double PSF_fit = PSF[0] * (A - mean) + PSF[1] * (B1 - 4.0 * mean) + PSF[2] * (B2 - 4.0 * mean) + PSF[3] * (C1 - 4.0 * mean) + PSF[4] * (C2 - 8.0 * mean) + PSF[5] * (C3 - 4.0 * mean) + PSF[6] * (D1 - 4.0 * mean) + PSF[7] * (D2 - 8.0 * mean) + PSF[8] * (D3 - 44.0 * mean); dst[width * y + x] = (float) PSF_fit; } } } static void GetStats(double *mean, double *stdev, int width, const float *img, const QRect& win) { // Determine the mean and standard deviation double sum = 0.0; double a = 0.0; double q = 0.0; double k = 1.0; double km1 = 0.0; const float *p0 = img + win.top() * width + win.left(); for (int y = 0; y < win.height(); y++) { const float *end = p0 + win.height(); for (const float *p = p0; p < end; p++) { double const x = (double) *p; sum += x; double const a0 = a; a += (x - a) / k; q += (x - a0) * (x - a); km1 = k; k += 1.0; } p0 += width; } *mean = sum / km1; *stdev = sqrt(q / km1); } static void RemoveItems(std::set& stars, const std::set& to_erase) { int n = 0; for (std::set::iterator it = stars.begin(); it != stars.end(); n++) { if (to_erase.find(n) != to_erase.end()) { std::set::iterator next = it; ++next; stars.erase(it); it = next; } else ++it; } } // Based on PHD2 algorithm QList cgmath::PSFAutoFind(int extraEdgeAllowance) { //Debug.Write(wxString::Format("Star::AutoFind called with edgeAllowance = %d searchRegion = %d\n", extraEdgeAllowance, searchRegion)); // run a 3x3 median first to eliminate hot pixels //usImage smoothed; //smoothed.CopyFrom(image); //Median3(smoothed); FITSData *smoothed = new FITSData(guideView->getImageData()); smoothed->applyFilter(FITS_MEDIAN); int searchRegion = guideView->getTrackingBox().width(); int subW = smoothed->getWidth(); int subH = smoothed->getHeight(); // convert to floating point float *conv = createFloatImage(smoothed); // run the PSF convolution { float *tmp = new float[smoothed->getSize()]; memset(tmp, 0, smoothed->getSize()*sizeof(float)); psf_conv(tmp, conv, subW, subH); delete [] conv; // Swap conv = tmp; } enum { CONV_RADIUS = 4 }; int dw = subW; // width of the downsampled image int dh = subH; // height of the downsampled image QRect convRect(CONV_RADIUS, CONV_RADIUS, dw - 2 * CONV_RADIUS, dh - 2 * CONV_RADIUS); // region containing valid data enum { TOP_N = 100 }; // keep track of the brightest stars std::set stars; // sorted by ascending intensity double global_mean, global_stdev; GetStats(&global_mean, &global_stdev, subW, conv, convRect); //Debug.Write(wxString::Format("AutoFind: global mean = %.1f, stdev %.1f\n", global_mean, global_stdev)); const double threshold = 0.1; //Debug.Write(wxString::Format("AutoFind: using threshold = %.1f\n", threshold)); // find each local maximum int srch = 4; for (int y = convRect.top() + srch; y <= convRect.bottom() - srch; y++) { for (int x = convRect.left() + srch; x <= convRect.right() - srch; x++) { float val = conv[dw * y + x]; bool ismax = false; if (val > 0.0) { ismax = true; for (int j = -srch; j <= srch; j++) { for (int i = -srch; i <= srch; i++) { if (i == 0 && j == 0) continue; if (conv[dw * (y + j) + (x + i)] > val) { ismax = false; break; } } } } if (!ismax) continue; // compare local maximum to mean value of surrounding pixels const int local = 7; double local_mean, local_stdev; QRect localRect(x - local, y - local, 2 * local + 1, 2 * local + 1); localRect = localRect.intersected(convRect); GetStats(&local_mean, &local_stdev, subW, conv, localRect); // this is our measure of star intensity double h = (val - local_mean) / global_stdev; if (h < threshold) { // Debug.Write(wxString::Format("AG: local max REJECT [%d, %d] PSF %.1f SNR %.1f\n", imgx, imgy, val, SNR)); continue; } // coordinates on the original image int downsample =1; int imgx = x * downsample + downsample / 2; int imgy = y * downsample + downsample / 2; stars.insert(Peak(imgx, imgy, h)); if (stars.size() > TOP_N) stars.erase(stars.begin()); } } //for (std::set::const_reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) //qCDebug(KSTARS_EKOS_GUIDE) << "AutoFind: local max [" << it->x << "," << it->y << "]" << it->val; // merge stars that are very close into a single star { const int minlimitsq = 5 * 5; repeat: for (std::set::const_iterator a = stars.begin(); a != stars.end(); ++a) { std::set::const_iterator b = a; ++b; for (; b != stars.end(); ++b) { int dx = a->x - b->x; int dy = a->y - b->y; int d2 = dx * dx + dy * dy; if (d2 < minlimitsq) { // very close, treat as single star //Debug.Write(wxString::Format("AutoFind: merge [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); // erase the dimmer one stars.erase(a); goto repeat; } } } } // exclude stars that would fit within a single searchRegion box { // build a list of stars to be excluded std::set to_erase; const int extra = 5; // extra safety margin const int fullw = searchRegion + extra; for (std::set::const_iterator a = stars.begin(); a != stars.end(); ++a) { std::set::const_iterator b = a; ++b; for (; b != stars.end(); ++b) { int dx = abs(a->x - b->x); int dy = abs(a->y - b->y); if (dx <= fullw && dy <= fullw) { // stars closer than search region, exclude them both // but do not let a very dim star eliminate a very bright star if (b->val / a->val >= 5.0) { //Debug.Write(wxString::Format("AutoFind: close dim-bright [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); } else { //Debug.Write(wxString::Format("AutoFind: too close [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); to_erase.insert(std::distance(stars.begin(), a)); to_erase.insert(std::distance(stars.begin(), b)); } } } } RemoveItems(stars, to_erase); } // exclude stars too close to the edge { enum { MIN_EDGE_DIST = 40 }; int edgeDist = MIN_EDGE_DIST;//pConfig->Profile.GetInt("/StarAutoFind/MinEdgeDist", MIN_EDGE_DIST); if (edgeDist < searchRegion) edgeDist = searchRegion; edgeDist += extraEdgeAllowance; std::set::iterator it = stars.begin(); while (it != stars.end()) { std::set::iterator next = it; ++next; if (it->x <= edgeDist || it->x >= subW - edgeDist || it->y <= edgeDist || it->y >= subH - edgeDist) { //Debug.Write(wxString::Format("AutoFind: too close to edge [%d, %d] %.1f\n", it->x, it->y, it->val)); stars.erase(it); } it = next; } } QList centers; for (std::set::reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) { Edge *center = new Edge; center->x = it->x; center->y = it->y; center->val = it->val; centers.append(center); } delete [] conv; delete (smoothed); return centers; } //--------------------------------------------------------------------------------------- cproc_in_params::cproc_in_params() { reset(); } void cproc_in_params::reset(void) { threshold_alg_idx = CENTROID_THRESHOLD; average = true; for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) { enabled[k] = true; accum_frame_cnt[k] = 1; integral_gain[k] = 0; derivative_gain[k] = 0; max_pulse_length[k] = 5000; min_pulse_length[k] = 100; } } cproc_out_params::cproc_out_params() { reset(); } void cproc_out_params::reset(void) { for (int k = GUIDE_RA; k <= GUIDE_DEC; k++) { delta[k] = 0; pulse_dir[k] = NO_DIR; pulse_length[k] = 0; sigma[k] = 0; } } diff --git a/kstars/ekos/guide/internalguide/gmath.h b/kstars/ekos/guide/internalguide/gmath.h index 0c2aadada..a9f315553 100644 --- a/kstars/ekos/guide/internalguide/gmath.h +++ b/kstars/ekos/guide/internalguide/gmath.h @@ -1,259 +1,259 @@ /* Ekos guide tool Copyright (C) 2012 Andrew Stepanenko Modified by Jasem Mutlaq for KStars. This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once -#include -#include - #include "matr.h" #include "vect.h" #include "indi/indicommon.h" #include #include #include #include #include +#include +#include + class FITSView; class FITSData; class Edge; typedef struct { int size; double square; } guide_square_t; #define SMART_THRESHOLD 0 #define SEP_THRESHOLD 1 #define CENTROID_THRESHOLD 2 #define AUTO_THRESHOLD 3 #define NO_THRESHOLD 4 typedef struct { int idx; const char name[32]; } square_alg_t; // smart threshold algorithm param // width of outer frame for backgroung calculation #define SMART_FRAME_WIDTH 4 // cut-factor above avarage threshold #define SMART_CUT_FACTOR 0.1 #define GUIDE_RA 0 #define GUIDE_DEC 1 #define CHANNEL_CNT 2 #define MAX_ACCUM_CNT 50 extern const guide_square_t guide_squares[]; extern const square_alg_t guide_square_alg[]; // input params class cproc_in_params { public: cproc_in_params(); void reset(void); int threshold_alg_idx; bool enabled[CHANNEL_CNT]; bool enabled_axis1[CHANNEL_CNT]; bool enabled_axis2[CHANNEL_CNT]; bool average; uint32_t accum_frame_cnt[CHANNEL_CNT]; double proportional_gain[CHANNEL_CNT]; double integral_gain[CHANNEL_CNT]; double derivative_gain[CHANNEL_CNT]; int max_pulse_length[CHANNEL_CNT]; int min_pulse_length[CHANNEL_CNT]; }; //output params class cproc_out_params { public: cproc_out_params(); void reset(void); double delta[2]; GuideDirection pulse_dir[2]; int pulse_length[2]; double sigma[2]; }; typedef struct { double focal_ratio; double fov_wd, fov_ht; double focal, aperture; } info_params_t; class cgmath : public QObject { Q_OBJECT public: cgmath(); virtual ~cgmath(); // functions bool setVideoParameters(int vid_wd, int vid_ht, int binX, int binY); bool setGuiderParameters(double ccd_pix_wd, double ccd_pix_ht, double guider_aperture, double guider_focal); void getGuiderParameters(double *ccd_pix_wd, double *ccd_pix_ht, double *guider_aperture, double *guider_focal); bool setReticleParameters(double x, double y, double ang); bool getReticleParameters(double *x, double *y, double *ang) const; int getSquareAlgorithmIndex(void) const; void setSquareAlgorithm(int alg_idx); Ekos::Matrix getROTZ() { return ROT_Z; } const cproc_out_params *getOutputParameters() const { return &out_params; } info_params_t getInfoParameters(void) const; uint32_t getTicks(void) const; void setGuideView(FITSView *image); bool declinationSwapEnabled() { return dec_swap; } void setDeclinationSwapEnabled(bool enable) { dec_swap = enable; } FITSView *getGuideView() { return guideView; } void setPreviewMode(bool enable) { preview_mode = enable; } // Based on PHD2 algorithm QList PSFAutoFind(int extraEdgeAllowance=0); /*void moveSquare( double newx, double newy ); void resizeSquare( int size_idx ); Vector getSquarePosition() { return square_pos; }*/ // Rapid Guide void setRapidGuide(bool enable); void setRapidStarData(double dx, double dy); // Operations void start(void); void stop(void); bool reset(void); void suspend(bool mode); bool isSuspended(void) const; // Star tracking void getStarDrift(double *dx, double *dy) const; void getStarScreenPosition(double *dx, double *dy) const; Vector findLocalStarPosition(void) const; bool isStarLost(void) const; void setLostStar(bool is_lost); // Main processing function void performProcessing(void); // Math bool calculateAndSetReticle1D(double start_x, double start_y, double end_x, double end_y, int RATotalPulse = -1); bool calculateAndSetReticle2D(double start_ra_x, double start_ra_y, double end_ra_x, double end_ra_y, double start_dec_x, double start_dec_y, double end_dec_x, double end_dec_y, bool *swap_dec, int RATotalPulse = -1, int DETotalPulse = -1); double calculatePhi(double start_x, double start_y, double end_x, double end_y) const; // Dither double getDitherRate(int axis); bool isImageGuideEnabled() const; void setImageGuideEnabled(bool value); void setRegionAxis(const uint32_t &value); signals: void newAxisDelta(double delta_ra, double delta_dec); void newStarPosition(QVector3D, bool); private: // Templated functions template Vector findLocalStarPosition(void) const; // Creates a new float image from the guideView image data. The returned image MUST be deleted later or memory will leak. float *createFloatImage(FITSData *target=nullptr) const; void do_ticks(void); Vector point2arcsec(const Vector &p) const; void process_axes(void); void calc_square_err(void); const char *get_direction_string(GuideDirection dir); // Logging void createGuideLog(); /// Global channel ticker uint32_t ticks { 0 }; /// Pointer to image QPointer guideView; /// Video frame width int video_width { -1 }; /// Video frame height int video_height { -1 }; double ccd_pixel_width { 0 }; double ccd_pixel_height { 0 }; double aperture { 0 }; double focal { 0 }; Ekos::Matrix ROT_Z; bool preview_mode { true }; bool suspended { false }; bool lost_star { false }; bool dec_swap { false }; /// Index of threshold algorithm int square_alg_idx { SMART_THRESHOLD }; int subBinX { 1 }; int subBinY { 1 }; // sky coord. system vars. /// Star position in reticle coord. system Vector star_pos; /// Star position on the screen Vector scr_star_pos; Vector reticle_pos; Vector reticle_orts[2]; double reticle_angle { 0 }; // processing uint32_t channel_ticks[2]; uint32_t accum_ticks[2]; double *drift[2]; double drift_integral[2]; // overlays... cproc_in_params in_params; cproc_out_params out_params; // stat math... bool do_statistics { true }; double sum { 0 }; // rapid guide bool useRapidGuide { false }; double rapidDX { 0 }; double rapidDY { 0 }; // Image Guide bool imageGuideEnabled { false }; // Partition guideView image into NxN square regions each of size axis*axis. The returned vector contains pointers to // the newly allocated square images. It MUST be deleted later by delete[] or memory will leak. QVector partitionImage() const; uint32_t regionAxis { 64 }; QVector referenceRegions; // dithering double ditherRate[2]; QFile logFile; QTime logTime; }; diff --git a/kstars/ekos/guide/internalguide/vect.h b/kstars/ekos/guide/internalguide/vect.h index 1dc3610db..cd1cb193a 100644 --- a/kstars/ekos/guide/internalguide/vect.h +++ b/kstars/ekos/guide/internalguide/vect.h @@ -1,160 +1,159 @@ /* Ekos guide tool Copyright (C) 2012 Andrew Stepanenko Modified by Jasem Mutlaq for KStars. This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ //--------------------------------------------------------------------------- -#ifndef vectH -#define vectH + +#pragma once #include class Vector { public: double x, y, z; Vector() { x = y = z = 0.0; }; explicit Vector(double v) { x = y = z = v; }; Vector(const Vector &v) { x = v.x; y = v.y; z = v.z; }; Vector(double vx, double vy, double vz) { x = vx; y = vy; z = vz; }; - ~Vector(){}; + ~Vector() = default; Vector &operator=(const Vector &v) { x = v.x; y = v.y; z = v.z; return *this; }; Vector &operator=(double f) { x = y = z = f; return *this; }; Vector operator-() const; Vector &operator+=(const Vector &); Vector &operator-=(const Vector &); Vector &operator*=(const Vector &); Vector &operator*=(double); Vector &operator/=(double); friend Vector operator+(const Vector &, const Vector &); friend Vector operator-(const Vector &, const Vector &); friend Vector operator*(const Vector &, const Vector &); friend Vector operator*(double, const Vector &); friend Vector operator*(const Vector &, double); friend Vector operator/(const Vector &, double); friend Vector operator/(const Vector &, const Vector &); friend double operator&(const Vector &u, const Vector &v) { return u.x * v.x + u.y * v.y + u.z * v.z; }; friend Vector operator^(const Vector &, const Vector &); double operator!() const { return (double)sqrt(x * x + y * y + z * z); }; double &operator[](int n) { return *(&x + n); }; int operator<(double v) { return x < v && y < v && z < v; }; int operator>(double v) { return x > v && y > v && z > v; }; }; inline Vector Vector ::operator-() const { return Vector(-x, -y, -z); } inline Vector operator+(const Vector &u, const Vector &v) { return Vector(u.x + v.x, u.y + v.y, u.z + v.z); } inline Vector operator-(const Vector &u, const Vector &v) { return Vector(u.x - v.x, u.y - v.y, u.z - v.z); } inline Vector operator*(const Vector &u, const Vector &v) { return Vector(u.x * v.x, u.y * v.y, u.z * v.z); } inline Vector operator*(const Vector &u, double f) { return Vector(u.x * f, u.y * f, u.z * f); } inline Vector operator*(double f, const Vector &v) { return Vector(f * v.x, f * v.y, f * v.z); } inline Vector operator/(const Vector &v, double f) { return Vector(v.x / f, v.y / f, v.z / f); } inline Vector operator/(const Vector &u, const Vector &v) { return Vector(u.x / v.x, u.y / v.y, u.z / v.z); } inline Vector &Vector ::operator+=(const Vector &v) { x += v.x; y += v.y; z += v.z; return *this; } inline Vector &Vector ::operator-=(const Vector &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } inline Vector &Vector ::operator*=(double v) { x *= v; y *= v; z *= v; return *this; } inline Vector &Vector ::operator*=(const Vector &v) { x *= v.x; y *= v.y; z *= v.z; return *this; } inline Vector &Vector ::operator/=(double v) { x /= v; y /= v; z /= v; return *this; } //////// inline Vector Normalize(const Vector &v) { return v / !v; }; Vector RndVector(); Vector &Clip(Vector &); //--------------------------------------------------------------------------- -#endif diff --git a/kstars/ekos/guide/opscalibration.cpp b/kstars/ekos/guide/opscalibration.cpp index d7dd2d5a6..f2ab44df7 100644 --- a/kstars/ekos/guide/opscalibration.cpp +++ b/kstars/ekos/guide/opscalibration.cpp @@ -1,65 +1,61 @@ /* INDI Options Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ +#include "opscalibration.h" + +#include "guide.h" +#include "kstars.h" +#include "Options.h" +#include "internalguide/internalguider.h" + #include #include #include #include #include #include -#include "Options.h" -#include "opscalibration.h" -#include "guide.h" -#include "kstars.h" - -#include "internalguide/internalguider.h" - namespace Ekos { OpsCalibration::OpsCalibration(InternalGuider *guiderObject) : QFrame(KStars::Instance()) { setupUi(this); guider = guiderObject; //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("guidesettings"); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); } -OpsCalibration::~OpsCalibration() -{ -} - void OpsCalibration::showEvent(QShowEvent *) { double x, y, ang; guider->getReticleParameters(&x, &y, &ang); spinBox_ReticleX->setValue(x); spinBox_ReticleY->setValue(y); spinBox_ReticleAngle->setValue(ang); uint16_t subX, subY, subW, subH, subBinX, subBinY; guider->getFrameParams(&subX, &subY, &subW, &subH, &subBinX, &subBinY); spinBox_ReticleX->setMaximum(subW); spinBox_ReticleY->setMaximum(subH); } void OpsCalibration::slotApply() { guider->setReticleParameters(spinBox_ReticleX->value(), spinBox_ReticleY->value(), spinBox_ReticleAngle->value()); } } diff --git a/kstars/ekos/guide/opscalibration.h b/kstars/ekos/guide/opscalibration.h index 7aff7a65f..371486638 100644 --- a/kstars/ekos/guide/opscalibration.h +++ b/kstars/ekos/guide/opscalibration.h @@ -1,49 +1,46 @@ /* Ekos Options Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef OpsCalibration_H_ -#define OpsCalibration_H_ +#pragma once #include "ui_opscalibration.h" class KConfigDialog; namespace Ekos { class InternalGuider; /** * @class OpsCalibration * * Enables the user to set guide calibration options * * @author Jasem Mutlaq */ class OpsCalibration : public QFrame, public Ui::OpsCalibration { Q_OBJECT public: explicit OpsCalibration(InternalGuider *guiderObject); - ~OpsCalibration(); + virtual ~OpsCalibration() override = default; protected: void showEvent(QShowEvent *); private slots: void slotApply(); private: - KConfigDialog *m_ConfigDialog; - InternalGuider *guider; + KConfigDialog *m_ConfigDialog { nullptr }; + InternalGuider *guider { nullptr }; }; } - -#endif diff --git a/kstars/ekos/guide/opsguide.cpp b/kstars/ekos/guide/opsguide.cpp index fd517f6a1..a0e2ca786 100644 --- a/kstars/ekos/guide/opsguide.cpp +++ b/kstars/ekos/guide/opsguide.cpp @@ -1,48 +1,43 @@ /* INDI Options Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#include -#include -#include -#include -#include - -#include +#include "opsguide.h" #include "Options.h" -#include "opsguide.h" #include "kstars.h" #include "auxiliary/ksnotification.h" - #include "internalguide/internalguider.h" +#include + +#include +#include +#include +#include +#include + namespace Ekos { OpsGuide::OpsGuide() : QFrame(KStars::Instance()) { setupUi(this); //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("guidesettings"); connect(kcfg_GuideRemoteImagesEnabled, &QCheckBox::toggled, this, [this] () { if (Options::guideRemoteImagesEnabled() != kcfg_GuideRemoteImagesEnabled->isChecked()) KSNotification::info(i18n("You must restart KStars for this change to take effect.")); }); connect(m_ConfigDialog, SIGNAL(settingsChanged(QString)), this, SIGNAL(settingsUpdated())); } - -OpsGuide::~OpsGuide() -{ -} - } diff --git a/kstars/ekos/guide/opsguide.h b/kstars/ekos/guide/opsguide.h index c26c5adb4..7b7dac5b8 100644 --- a/kstars/ekos/guide/opsguide.h +++ b/kstars/ekos/guide/opsguide.h @@ -1,43 +1,40 @@ /* Ekos Options Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef OpsGuide_H_ -#define OpsGuide_H_ +#pragma once #include "ui_opsguide.h" #include "guide.h" class KConfigDialog; namespace Ekos { /** * @class OpsGuide * * Enables the user to set guide options * * @author Jasem Mutlaq */ class OpsGuide : public QFrame, public Ui::OpsGuide { Q_OBJECT public: explicit OpsGuide(); - ~OpsGuide(); + virtual ~OpsGuide() override = default; signals: void settingsUpdated(); private: - KConfigDialog *m_ConfigDialog; + KConfigDialog *m_ConfigDialog { nullptr }; }; } - -#endif diff --git a/kstars/ekos/profileeditor.cpp b/kstars/ekos/profileeditor.cpp index ec9abcad5..cb7bdef81 100644 --- a/kstars/ekos/profileeditor.cpp +++ b/kstars/ekos/profileeditor.cpp @@ -1,835 +1,831 @@ /* Profile Editor Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "profileeditor.h" #include "geolocation.h" #include "kstarsdata.h" #include "Options.h" #include "guide/guide.h" #include "indi/driverinfo.h" #include "indi/drivermanager.h" #include "oal/equipmentwriter.h" ProfileEditorUI::ProfileEditorUI(QWidget *p) : QFrame(p) { setupUi(this); } ProfileEditor::ProfileEditor(QWidget *w) : QDialog(w) { setObjectName("profileEditorDialog"); #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ui = new ProfileEditorUI(this); pi = nullptr; QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setLayout(mainLayout); setWindowTitle(i18n("Profile Editor")); // Create button box and link it to save and reject functions QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Close, this); buttonBox->setObjectName("dialogButtons"); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(saveProfile())); connect(ui->openWebManagerB, &QPushButton::clicked, this, [this]() { QUrl url(QString("http://" + ui->remoteHost->text() + ":8624")); QDesktopServices::openUrl(url); }); connect(ui->INDIWebManagerCheck, SIGNAL(toggled(bool)), ui->openWebManagerB, SLOT(setEnabled(bool))); connect(ui->guideTypeCombo, SIGNAL(activated(int)), this, SLOT(updateGuiderSelection(int))); ui->addScopeB->setIcon(QIcon::fromTheme("list-add")); connect(ui->addScopeB, &QPushButton::clicked, this, [this]() { QPointer equipmentdlg = new EquipmentWriter(); equipmentdlg->loadEquipment(); equipmentdlg->exec(); delete equipmentdlg; loadScopeEquipment(); }); #ifdef Q_OS_WIN ui->remoteMode->setChecked(true); ui->localMode->setEnabled(false); setRemoteMode(true); #else connect(ui->remoteMode, SIGNAL(toggled(bool)), this, SLOT(setRemoteMode(bool))); #endif // Load all drivers loadDrivers(); // Load scope equipment loadScopeEquipment(); } -ProfileEditor::~ProfileEditor() -{ -} - void ProfileEditor::loadScopeEquipment() { // Get all OAL equipment filter list KStarsData::Instance()->userdb()->GetAllScopes(m_scopeList); ui->primaryScopeCombo->clear(); ui->guideScopeCombo->clear(); ui->primaryScopeCombo->addItem(i18n("Default")); ui->primaryScopeCombo->setItemData(0, i18n("Use scope data from INDI"), Qt::ToolTipRole); ui->guideScopeCombo->addItem(i18n("Default")); ui->guideScopeCombo->setItemData(0, i18n("Use scope data from INDI"), Qt::ToolTipRole); int primaryScopeIndex=0; int guideScopeIndex=0; for (int i=0; i < m_scopeList.count(); i++) { OAL::Scope *oneScope = m_scopeList[i]; ui->primaryScopeCombo->addItem(oneScope->name()); if (pi && oneScope->id().toInt() == pi->primaryscope) primaryScopeIndex = i+1; ui->guideScopeCombo->addItem(oneScope->name()); if (pi && oneScope->id().toInt() == pi->guidescope) guideScopeIndex = i+1; double FocalLength = oneScope->focalLength(); double Aperture = oneScope->aperture(); ui->primaryScopeCombo->setItemData(i+1, i18nc("F-Number, Focal Length, Aperture", "F%1 Focal Length: %2 mm Aperture: %3 mm2", QString::number(FocalLength / Aperture, 'f', 1), QString::number(FocalLength, 'f', 2), QString::number(Aperture, 'f', 2)), Qt::ToolTipRole); ui->guideScopeCombo->setItemData(i+1, i18nc("F-Number, Focal Length, Aperture", "F%1 Focal Length: %2 mm Aperture: %3 mm2", QString::number(FocalLength / Aperture, 'f', 1), QString::number(FocalLength, 'f', 2), QString::number(Aperture, 'f', 2)), Qt::ToolTipRole); } ui->primaryScopeCombo->setCurrentIndex(primaryScopeIndex); ui->guideScopeCombo->setCurrentIndex(guideScopeIndex); } void ProfileEditor::saveProfile() { bool newProfile = (pi == nullptr); if (ui->profileIN->text().isEmpty()) { KMessageBox::error(this, i18n("Cannot save an empty profile!")); return; } if (newProfile) { int id = KStarsData::Instance()->userdb()->AddProfile(ui->profileIN->text()); pi = new ProfileInfo(id, ui->profileIN->text()); } else pi->name = ui->profileIN->text(); // Local Mode if (ui->localMode->isChecked()) { pi->host.clear(); pi->port = -1; pi->INDIWebManagerPort = -1; //pi->customDrivers = ui->customDriversIN->text(); } // Remote Mode else { pi->host = ui->remoteHost->text().trimmed(); pi->port = ui->remotePort->text().toInt(); if (ui->INDIWebManagerCheck->isChecked()) pi->INDIWebManagerPort = ui->INDIWebManagerPort->text().toInt(); else pi->INDIWebManagerPort = -1; //pi->customDrivers.clear(); } // City Info if (ui->loadSiteCheck->isEnabled() && ui->loadSiteCheck->isChecked()) { pi->city = KStarsData::Instance()->geo()->name(); pi->province = KStarsData::Instance()->geo()->province(); pi->country = KStarsData::Instance()->geo()->country(); } else { pi->city.clear(); pi->province.clear(); pi->country.clear(); } // Auto Connect pi->autoConnect = ui->autoConnectCheck->isChecked(); // Guider Type pi->guidertype = ui->guideTypeCombo->currentIndex(); if (pi->guidertype != Ekos::Guide::GUIDE_INTERNAL) { pi->guiderhost = ui->externalGuideHost->text(); pi->guiderport = ui->externalGuidePort->text().toInt(); if (pi->guidertype == Ekos::Guide::GUIDE_PHD2) { Options::setPHD2Host(pi->guiderhost); Options::setPHD2Port(pi->guiderport); } else if (pi->guidertype == Ekos::Guide::GUIDE_LINGUIDER) { Options::setLinGuiderHost(pi->guiderhost); Options::setLinGuiderPort(pi->guiderport); } } // Scope list pi->primaryscope=0; pi->guidescope=0; QString selectedScope = ui->primaryScopeCombo->currentText(); QString selectedGuide = ui->guideScopeCombo->currentText(); foreach(OAL::Scope *oneScope, m_scopeList) { if (selectedScope == oneScope->name()) pi->primaryscope = oneScope->id().toInt(); if (selectedGuide == oneScope->name()) pi->guidescope = oneScope->id().toInt(); } if (ui->mountCombo->currentText().isEmpty() || ui->mountCombo->currentText() == "--") pi->drivers.remove("Mount"); else pi->drivers["Mount"] = ui->mountCombo->currentText(); if (ui->ccdCombo->currentText().isEmpty() || ui->ccdCombo->currentText() == "--") pi->drivers.remove("CCD"); else pi->drivers["CCD"] = ui->ccdCombo->currentText(); if (ui->guiderCombo->currentText().isEmpty() || ui->guiderCombo->currentText() == "--") pi->drivers.remove("Guider"); else pi->drivers["Guider"] = ui->guiderCombo->currentText(); if (ui->focuserCombo->currentText().isEmpty() || ui->focuserCombo->currentText() == "--") pi->drivers.remove("Focuser"); else pi->drivers["Focuser"] = ui->focuserCombo->currentText(); if (ui->filterCombo->currentText().isEmpty() || ui->filterCombo->currentText() == "--") pi->drivers.remove("Filter"); else pi->drivers["Filter"] = ui->filterCombo->currentText(); if (ui->AOCombo->currentText().isEmpty() || ui->AOCombo->currentText() == "--") pi->drivers.remove("AO"); else pi->drivers["AO"] = ui->AOCombo->currentText(); if (ui->domeCombo->currentText().isEmpty() || ui->domeCombo->currentText() == "--") pi->drivers.remove("Dome"); else pi->drivers["Dome"] = ui->domeCombo->currentText(); if (ui->weatherCombo->currentText().isEmpty() || ui->weatherCombo->currentText() == "--") pi->drivers.remove("Weather"); else pi->drivers["Weather"] = ui->weatherCombo->currentText(); if (ui->aux1Combo->currentText().isEmpty() || ui->aux1Combo->currentText() == "--") pi->drivers.remove("Aux1"); else pi->drivers["Aux1"] = ui->aux1Combo->currentText(); if (ui->aux2Combo->currentText().isEmpty() || ui->aux2Combo->currentText() == "--") pi->drivers.remove("Aux2"); else pi->drivers["Aux2"] = ui->aux2Combo->currentText(); if (ui->aux3Combo->currentText().isEmpty() || ui->aux3Combo->currentText() == "--") pi->drivers.remove("Aux3"); else pi->drivers["Aux3"] = ui->aux3Combo->currentText(); if (ui->aux4Combo->currentText().isEmpty() || ui->aux4Combo->currentText() == "--") pi->drivers.remove("Aux4"); else pi->drivers["Aux4"] = ui->aux4Combo->currentText(); KStarsData::Instance()->userdb()->SaveProfile(pi); // Ekos manager will reload and new profiles will be created if (newProfile) delete (pi); accept(); } void ProfileEditor::setRemoteMode(bool enable) { loadDrivers(); //This is needed to reload the drivers because some may not be available locally ui->remoteHost->setEnabled(enable); ui->remoteHostLabel->setEnabled(enable); ui->remotePort->setEnabled(enable); ui->remotePortLabel->setEnabled(enable); //ui->customLabel->setEnabled(!enable); //ui->customDriversIN->setEnabled(!enable); ui->mountCombo->setEditable(enable); ui->ccdCombo->setEditable(enable); ui->guiderCombo->setEditable(enable); ui->focuserCombo->setEditable(enable); ui->filterCombo->setEditable(enable); ui->AOCombo->setEditable(enable); ui->domeCombo->setEditable(enable); ui->weatherCombo->setEditable(enable); ui->aux1Combo->setEditable(enable); ui->aux2Combo->setEditable(enable); ui->aux3Combo->setEditable(enable); ui->aux4Combo->setEditable(enable); ui->loadSiteCheck->setEnabled(enable); ui->INDIWebManagerCheck->setEnabled(enable); if (enable == false) ui->INDIWebManagerCheck->setChecked(false); ui->INDIWebManagerPort->setEnabled(enable); } void ProfileEditor::setPi(ProfileInfo *value) { pi = value; ui->profileIN->setText(pi->name); ui->loadSiteCheck->setChecked(!pi->city.isEmpty()); ui->autoConnectCheck->setChecked(pi->autoConnect); if (pi->city.isEmpty() == false) { if (pi->province.isEmpty()) ui->loadSiteCheck->setText(ui->loadSiteCheck->text() + QString(" (%1, %2)").arg(pi->country, pi->city)); else ui->loadSiteCheck->setText(ui->loadSiteCheck->text() + QString(" (%1, %2, %3)").arg(pi->country, pi->province, pi->city)); } if (pi->host.isEmpty() == false) { ui->remoteHost->setText(pi->host); ui->remotePort->setText(QString::number(pi->port)); ui->remoteMode->setChecked(true); if (pi->INDIWebManagerPort > 0) { ui->INDIWebManagerCheck->setChecked(true); ui->INDIWebManagerPort->setText(QString::number(pi->INDIWebManagerPort)); } else { ui->INDIWebManagerCheck->setChecked(false); ui->INDIWebManagerPort->setText("8624"); } } ui->guideTypeCombo->setCurrentIndex(pi->guidertype); updateGuiderSelection(ui->guideTypeCombo->currentIndex()); if (pi->guidertype == Ekos::Guide::GUIDE_PHD2) { Options::setPHD2Host(pi->guiderhost); Options::setPHD2Port(pi->guiderport); } else if (pi->guidertype == Ekos::Guide::GUIDE_LINGUIDER) { Options::setLinGuiderHost(pi->guiderhost); Options::setLinGuiderPort(pi->guiderport); } QMapIterator i(pi->drivers); int row = 0; while (i.hasNext()) { i.next(); QString key = i.key(); QString value = i.value(); if (key == "Mount") { // If driver doesn't exist, let's add it to the list if ((row = ui->mountCombo->findText(value)) == -1) { ui->mountCombo->addItem(value); row = ui->mountCombo->count() - 1; } // Set index to our driver ui->mountCombo->setCurrentIndex(row); } else if (key == "CCD") { if ((row = ui->ccdCombo->findText(value)) == -1) { ui->ccdCombo->addItem(value); row = ui->ccdCombo->count() - 1; } ui->ccdCombo->setCurrentIndex(row); } else if (key == "Guider") { if ((row = ui->guiderCombo->findText(value)) == -1) { ui->guiderCombo->addItem(value); row = ui->guiderCombo->count() - 1; } ui->guiderCombo->setCurrentIndex(row); } else if (key == "Focuser") { if ((row = ui->focuserCombo->findText(value)) == -1) { ui->focuserCombo->addItem(value); row = ui->focuserCombo->count() - 1; } ui->focuserCombo->setCurrentIndex(row); } else if (key == "Filter") { if ((row = ui->filterCombo->findText(value)) == -1) { ui->filterCombo->addItem(value); row = ui->filterCombo->count() - 1; } ui->filterCombo->setCurrentIndex(row); } else if (key == "AO") { if ((row = ui->AOCombo->findText(value)) == -1) { ui->AOCombo->addItem(value); row = ui->AOCombo->count() - 1; } ui->AOCombo->setCurrentIndex(row); } else if (key == "Dome") { if ((row = ui->domeCombo->findText(value)) == -1) { ui->domeCombo->addItem(value); row = ui->domeCombo->count() - 1; } ui->domeCombo->setCurrentIndex(row); } else if (key == "Weather") { if ((row = ui->weatherCombo->findText(value)) == -1) { ui->weatherCombo->addItem(value); row = ui->weatherCombo->count() - 1; } ui->weatherCombo->setCurrentIndex(row); } else if (key == "Aux1") { if ((row = ui->aux1Combo->findText(value)) == -1) { ui->aux1Combo->addItem(value); row = ui->aux1Combo->count() - 1; } ui->aux1Combo->setCurrentIndex(row); } else if (key == "Aux2") { if ((row = ui->aux2Combo->findText(value)) == -1) { ui->aux2Combo->addItem(value); row = ui->aux2Combo->count() - 1; } ui->aux2Combo->setCurrentIndex(row); } else if (key == "Aux3") { if ((row = ui->aux3Combo->findText(value)) == -1) { ui->aux3Combo->addItem(value); row = ui->aux3Combo->count() - 1; } ui->aux3Combo->setCurrentIndex(row); } else if (key == "Aux4") { if ((row = ui->aux4Combo->findText(value)) == -1) { ui->aux4Combo->addItem(value); row = ui->aux4Combo->count() - 1; } ui->aux4Combo->setCurrentIndex(row); } } loadScopeEquipment(); } void ProfileEditor::loadDrivers() { QVector boxes; boxes.append(ui->mountCombo); boxes.append(ui->ccdCombo); boxes.append(ui->guiderCombo); boxes.append(ui->AOCombo); boxes.append(ui->focuserCombo); boxes.append(ui->filterCombo); boxes.append(ui->domeCombo); boxes.append(ui->weatherCombo); boxes.append(ui->aux1Combo); boxes.append(ui->aux2Combo); boxes.append(ui->aux3Combo); boxes.append(ui->aux4Combo); QVector selectedItems; foreach (QComboBox *box, boxes) { selectedItems.append(box->currentText()); box->clear(); box->addItem("--"); box->setMaxVisibleItems(20); } QIcon remoteIcon = QIcon::fromTheme("network-modem"); foreach (DriverInfo *dv, DriverManager::Instance()->getDrivers()) { bool locallyAvailable = false; QIcon icon; if (dv->getAuxInfo().contains("LOCALLY_AVAILABLE")) locallyAvailable = dv->getAuxInfo().value("LOCALLY_AVAILABLE", false).toBool(); if (!locallyAvailable) { if (ui->localMode->isChecked()) continue; else icon = remoteIcon; } QString toolTipText; if (!locallyAvailable) toolTipText = i18n( "Available as Remote Driver. To use locally, install the corresponding driver."); else toolTipText = i18n("Label: %1 ━ Driver: %2 ━ Exec: %3", dv->getTreeLabel(), dv->getName(), dv->getDriver()); switch (dv->getType()) { case KSTARS_TELESCOPE: { ui->mountCombo->addItem(icon, dv->getTreeLabel()); ui->mountCombo->setItemData(ui->mountCombo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_CCD: { ui->ccdCombo->addItem(icon, dv->getTreeLabel()); ui->ccdCombo->setItemData(ui->ccdCombo->count() - 1, toolTipText, Qt::ToolTipRole); ui->guiderCombo->addItem(icon, dv->getTreeLabel()); ui->guiderCombo->setItemData(ui->guiderCombo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux1Combo->addItem(icon, dv->getTreeLabel()); ui->aux1Combo->setItemData(ui->aux1Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux2Combo->addItem(icon, dv->getTreeLabel()); ui->aux2Combo->setItemData(ui->aux2Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux3Combo->addItem(icon, dv->getTreeLabel()); ui->aux3Combo->setItemData(ui->aux3Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux4Combo->addItem(icon, dv->getTreeLabel()); ui->aux4Combo->setItemData(ui->aux4Combo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_ADAPTIVE_OPTICS: { ui->AOCombo->addItem(icon, dv->getTreeLabel()); ui->AOCombo->setItemData(ui->AOCombo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_FOCUSER: { ui->focuserCombo->addItem(icon, dv->getTreeLabel()); ui->focuserCombo->setItemData(ui->focuserCombo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux1Combo->addItem(icon, dv->getTreeLabel()); ui->aux1Combo->setItemData(ui->aux1Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux2Combo->addItem(icon, dv->getTreeLabel()); ui->aux2Combo->setItemData(ui->aux2Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux3Combo->addItem(icon, dv->getTreeLabel()); ui->aux3Combo->setItemData(ui->aux3Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux4Combo->addItem(icon, dv->getTreeLabel()); ui->aux4Combo->setItemData(ui->aux4Combo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_FILTER: { ui->filterCombo->addItem(icon, dv->getTreeLabel()); ui->filterCombo->setItemData(ui->filterCombo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux1Combo->addItem(icon, dv->getTreeLabel()); ui->aux1Combo->setItemData(ui->aux1Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux2Combo->addItem(icon, dv->getTreeLabel()); ui->aux2Combo->setItemData(ui->aux2Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux3Combo->addItem(icon, dv->getTreeLabel()); ui->aux3Combo->setItemData(ui->aux3Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux4Combo->addItem(icon, dv->getTreeLabel()); ui->aux4Combo->setItemData(ui->aux4Combo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_DOME: { ui->domeCombo->addItem(icon, dv->getTreeLabel()); ui->domeCombo->setItemData(ui->domeCombo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_WEATHER: { ui->weatherCombo->addItem(icon, dv->getTreeLabel()); ui->weatherCombo->setItemData(ui->weatherCombo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux1Combo->addItem(icon, dv->getTreeLabel()); ui->aux1Combo->setItemData(ui->aux1Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux2Combo->addItem(icon, dv->getTreeLabel()); ui->aux2Combo->setItemData(ui->aux2Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux3Combo->addItem(icon, dv->getTreeLabel()); ui->aux3Combo->setItemData(ui->aux3Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux4Combo->addItem(icon, dv->getTreeLabel()); ui->aux4Combo->setItemData(ui->aux4Combo->count() - 1, toolTipText, Qt::ToolTipRole); } break; case KSTARS_AUXILIARY: { ui->aux1Combo->addItem(icon, dv->getTreeLabel()); ui->aux1Combo->setItemData(ui->aux1Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux2Combo->addItem(icon, dv->getTreeLabel()); ui->aux2Combo->setItemData(ui->aux2Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux3Combo->addItem(icon, dv->getTreeLabel()); ui->aux3Combo->setItemData(ui->aux3Combo->count() - 1, toolTipText, Qt::ToolTipRole); ui->aux4Combo->addItem(icon, dv->getTreeLabel()); ui->aux4Combo->setItemData(ui->aux4Combo->count() - 1, toolTipText, Qt::ToolTipRole); } break; default: continue; break; } } //ui->mountCombo->setCurrentIndex(-1); for (int i = 0; i < boxes.count(); i++) { QComboBox *box = boxes.at(i); QString selectedItemText = selectedItems.at(i); int index = box->findText(selectedItemText); if (index == -1) { if (ui->localMode->isChecked()) box->setCurrentIndex(0); else box->addItem(remoteIcon, selectedItemText); } else { box->setCurrentIndex(index); } box->model()->sort(0); } } void ProfileEditor::setProfileName(const QString &name) { ui->profileIN->setText(name); } void ProfileEditor::setAuxDrivers(const QStringList &aux) { QStringList auxList(aux); if (auxList.isEmpty()) return; ui->aux1Combo->setCurrentText(auxList.first()); auxList.removeFirst(); if (auxList.isEmpty()) return; ui->aux2Combo->setCurrentText(auxList.first()); auxList.removeFirst(); if (auxList.isEmpty()) return; ui->aux3Combo->setCurrentText(auxList.first()); auxList.removeFirst(); if (auxList.isEmpty()) return; ui->aux4Combo->setCurrentText(auxList.first()); } void ProfileEditor::setHostPort(const QString &host, const QString &port) { ui->remoteMode->setChecked(true); ui->remoteHost->setText(host); ui->remotePort->setText(port); } void ProfileEditor::setWebManager(bool enabled, const QString &port) { ui->INDIWebManagerCheck->setChecked(enabled); ui->INDIWebManagerPort->setText(port); } void ProfileEditor::setGuiderType(int type) { ui->guideTypeCombo->setCurrentIndex(type); if (type != Ekos::Guide::GUIDE_INTERNAL) { ui->externalGuideHostLabel->setEnabled(true); ui->externalGuideHost->setEnabled(true); ui->externalGuidePortLabel->setEnabled(true); ui->externalGuidePort->setEnabled(true); } } void ProfileEditor::setConnectionOptionsEnabled(bool enable) { // Enable or disable connection related options ui->modeLabel->setEnabled(enable); ui->localMode->setEnabled(enable); ui->remoteMode->setEnabled(enable); ui->remoteHostLabel->setEnabled(enable); ui->remoteHost->setEnabled(enable); ui->remotePortLabel->setEnabled(enable); ui->remotePort->setEnabled(enable); ui->INDIWebManagerCheck->setEnabled(enable); ui->INDIWebManagerPort->setEnabled(enable); ui->INDIWebManagerPortLabel->setEnabled(enable); ui->guidingTypeLabel->setEnabled(enable); ui->guideTypeCombo->setEnabled(enable); updateGuiderSelection(ui->guideTypeCombo->currentIndex()); if (enable == false) ui->mountCombo->setFocus(); } void ProfileEditor::updateGuiderSelection(int id) { if (id == Ekos::Guide::GUIDE_INTERNAL) { ui->externalGuideHost->setText("localhost"); ui->externalGuidePort->clear(); ui->externalGuideHost->setEnabled(false); ui->externalGuideHostLabel->setEnabled(false); ui->externalGuidePort->setEnabled(false); ui->externalGuidePortLabel->setEnabled(false); return; } QString host; int port = -1; ui->externalGuideHost->setEnabled(true); ui->externalGuideHostLabel->setEnabled(true); ui->externalGuidePort->setEnabled(true); ui->externalGuidePortLabel->setEnabled(true); if (pi && pi->guidertype == id) { host = pi->guiderhost; port = pi->guiderport; } if (id == Ekos::Guide::GUIDE_PHD2) { if (host.isEmpty()) host = Options::pHD2Host(); if (port < 0) port = Options::pHD2Port(); } else if (id == Ekos::Guide::GUIDE_LINGUIDER) { if (host.isEmpty()) host = Options::linGuiderHost(); if (port < 0) port = Options::linGuiderPort(); } ui->externalGuideHost->setText(host); ui->externalGuidePort->setText(QString::number(port)); } diff --git a/kstars/ekos/profileeditor.h b/kstars/ekos/profileeditor.h index a35681051..4ca127e93 100644 --- a/kstars/ekos/profileeditor.h +++ b/kstars/ekos/profileeditor.h @@ -1,64 +1,64 @@ /* Profile Editor Copyright (C) 2016 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "oal/scope.h" #include "ui_profileeditor.h" #include #include #include #include class ProfileInfo; class ProfileEditorUI : public QFrame, public Ui::ProfileEditorUI { Q_OBJECT public: /** @short Constructor */ explicit ProfileEditorUI(QWidget *parent); }; class ProfileEditor : public QDialog { Q_OBJECT public: /** @short Constructor */ explicit ProfileEditor(QWidget *ks); /** @short Destructor */ - ~ProfileEditor(); + virtual ~ProfileEditor() override = default; void setPi(ProfileInfo *value); void loadDrivers(); void loadScopeEquipment(); void setProfileName(const QString &name); void setAuxDrivers(const QStringList &aux); void setHostPort(const QString &host, const QString &port); void setWebManager(bool enabled, const QString &port = "8624"); void setGuiderType(int type); void setConnectionOptionsEnabled(bool enable); public slots: void saveProfile(); void setRemoteMode(bool enable); private slots: void updateGuiderSelection(int id); private: ProfileEditorUI *ui { nullptr }; ProfileInfo *pi { nullptr }; QList m_scopeList; }; diff --git a/kstars/ekos/scheduler/scheduler.cpp b/kstars/ekos/scheduler/scheduler.cpp index edbde212b..16e92b9ae 100644 --- a/kstars/ekos/scheduler/scheduler.cpp +++ b/kstars/ekos/scheduler/scheduler.cpp @@ -1,5509 +1,5574 @@ /* Ekos Scheduler Module Copyright (C) 2015 Jasem Mutlaq DBus calls from GSoC 2015 Ekos Scheduler project by Daniel Leu This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "scheduler.h" #include "ksalmanac.h" #include "ksnotification.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include "mosaic.h" #include "Options.h" #include "scheduleradaptor.h" #include "schedulerjob.h" #include "skymapcomposite.h" #include "auxiliary/QProgressIndicator.h" #include "dialogs/finddialog.h" #include "ekos/ekosmanager.h" #include "ekos/capture/sequencejob.h" #include "skyobjects/starobject.h" #include #include #define BAD_SCORE -1000 #define MAX_FAILURE_ATTEMPTS 5 #define UPDATE_PERIOD_MS 1000 #define SETTING_ALTITUDE_CUTOFF 3 #define DEFAULT_CULMINATION_TIME -60 #define DEFAULT_MIN_ALTITUDE 15 #define DEFAULT_MIN_MOON_SEPARATION 0 namespace Ekos { Scheduler::Scheduler() { setupUi(this); new SchedulerAdaptor(this); QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Scheduler", this); dirPath = QUrl::fromLocalFile(QDir::homePath()); // Get current KStars time and set seconds to zero QDateTime currentDateTime = KStarsData::Instance()->lt(); QTime currentTime = currentDateTime.time(); currentTime.setHMS(currentTime.hour(), currentTime.minute(), 0); currentDateTime.setTime(currentTime); // Set initial time for startup and completion times startupTimeEdit->setDateTime(currentDateTime); completionTimeEdit->setDateTime(currentDateTime); // Set up DBus interfaces QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Scheduler", this); ekosInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos", QDBusConnection::sessionBus(), this); focusInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Focus", "org.kde.kstars.Ekos.Focus", QDBusConnection::sessionBus(), this); captureInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Capture", "org.kde.kstars.Ekos.Capture", QDBusConnection::sessionBus(), this); mountInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Mount", "org.kde.kstars.Ekos.Mount", QDBusConnection::sessionBus(), this); alignInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Align", "org.kde.kstars.Ekos.Align", QDBusConnection::sessionBus(), this); guideInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Guide", "org.kde.kstars.Ekos.Guide", QDBusConnection::sessionBus(), this); domeInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Dome", "org.kde.kstars.Ekos.Dome", QDBusConnection::sessionBus(), this); weatherInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/Weather", "org.kde.kstars.Ekos.Weather", QDBusConnection::sessionBus(), this); capInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos/DustCap", "org.kde.kstars.Ekos.DustCap", QDBusConnection::sessionBus(), this); moon = dynamic_cast(KStarsData::Instance()->skyComposite()->findByName("Moon")); sleepLabel->setPixmap( QIcon::fromTheme("chronometer").pixmap(QSize(32, 32))); sleepLabel->hide(); connect(&sleepTimer, SIGNAL(timeout()), this, SLOT(wakeUpScheduler())); schedulerTimer.setInterval(UPDATE_PERIOD_MS); jobTimer.setInterval(UPDATE_PERIOD_MS); connect(&schedulerTimer, SIGNAL(timeout()), this, SLOT(checkStatus())); connect(&jobTimer, SIGNAL(timeout()), this, SLOT(checkJobStage())); pi = new QProgressIndicator(this); bottomLayout->addWidget(pi, 0, 0); geo = KStarsData::Instance()->geo(); raBox->setDegType(false); //RA box should be HMS-style addToQueueB->setIcon(QIcon::fromTheme("list-add")); addToQueueB->setToolTip(i18n("Add observation job to list.")); addToQueueB->setAttribute(Qt::WA_LayoutUsesWidgetRect); removeFromQueueB->setIcon(QIcon::fromTheme("list-remove")); removeFromQueueB->setToolTip(i18n("Remove observation job from list.")); removeFromQueueB->setAttribute(Qt::WA_LayoutUsesWidgetRect); evaluateOnlyB->setIcon(QIcon::fromTheme("tools-wizard")); evaluateOnlyB->setAttribute(Qt::WA_LayoutUsesWidgetRect); mosaicB->setIcon(QIcon::fromTheme("zoom-draw")); mosaicB->setAttribute(Qt::WA_LayoutUsesWidgetRect); queueSaveAsB->setIcon(QIcon::fromTheme("document-save-as")); queueSaveAsB->setAttribute(Qt::WA_LayoutUsesWidgetRect); queueSaveB->setIcon(QIcon::fromTheme("document-save")); queueSaveB->setAttribute(Qt::WA_LayoutUsesWidgetRect); queueLoadB->setIcon(QIcon::fromTheme("document-open")); queueLoadB->setAttribute(Qt::WA_LayoutUsesWidgetRect); loadSequenceB->setIcon(QIcon::fromTheme("document-open")); loadSequenceB->setAttribute(Qt::WA_LayoutUsesWidgetRect); selectStartupScriptB->setIcon(QIcon::fromTheme("document-open")); selectStartupScriptB->setAttribute(Qt::WA_LayoutUsesWidgetRect); selectShutdownScriptB->setIcon( QIcon::fromTheme("document-open")); selectShutdownScriptB->setAttribute(Qt::WA_LayoutUsesWidgetRect); selectFITSB->setIcon(QIcon::fromTheme("document-open")); selectFITSB->setAttribute(Qt::WA_LayoutUsesWidgetRect); startupB->setIcon( QIcon::fromTheme("media-playback-start")); startupB->setAttribute(Qt::WA_LayoutUsesWidgetRect); shutdownB->setIcon( QIcon::fromTheme("media-playback-start")); shutdownB->setAttribute(Qt::WA_LayoutUsesWidgetRect); connect(startupB, SIGNAL(clicked()), this, SLOT(runStartupProcedure())); connect(shutdownB, SIGNAL(clicked()), this, SLOT(runShutdownProcedure())); selectObjectB->setIcon(QIcon::fromTheme("edit-find")); connect(selectObjectB, SIGNAL(clicked()), this, SLOT(selectObject())); connect(selectFITSB, SIGNAL(clicked()), this, SLOT(selectFITS())); connect(loadSequenceB, SIGNAL(clicked()), this, SLOT(selectSequence())); connect(selectStartupScriptB, SIGNAL(clicked()), this, SLOT(selectStartupScript())); connect(selectShutdownScriptB, SIGNAL(clicked()), this, SLOT(selectShutdownScript())); connect(mosaicB, SIGNAL(clicked()), this, SLOT(startMosaicTool())); connect(addToQueueB, SIGNAL(clicked()), this, SLOT(addJob())); connect(removeFromQueueB, SIGNAL(clicked()), this, SLOT(removeJob())); connect(evaluateOnlyB, SIGNAL(clicked()), this, SLOT(startJobEvaluation())); connect(queueTable, SIGNAL(clicked(QModelIndex)), this, SLOT(loadJob(QModelIndex))); connect(queueTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(resetJobState(QModelIndex))); startB->setIcon(QIcon::fromTheme("media-playback-start")); startB->setAttribute(Qt::WA_LayoutUsesWidgetRect); pauseB->setIcon(QIcon::fromTheme("media-playback-pause")); pauseB->setAttribute(Qt::WA_LayoutUsesWidgetRect); connect(startB, SIGNAL(clicked()), this, SLOT(toggleScheduler())); connect(pauseB, SIGNAL(clicked()), this, SLOT(pause())); connect(queueSaveAsB, SIGNAL(clicked()), this, SLOT(saveAs())); connect(queueSaveB, SIGNAL(clicked()), this, SLOT(save())); connect(queueLoadB, SIGNAL(clicked()), this, SLOT(load())); connect(twilightCheck, SIGNAL(toggled(bool)), this, SLOT(checkTwilightWarning(bool))); loadProfiles(); } QString Scheduler::getCurrentJobName() { return (currentJob != nullptr ? currentJob->getName() : ""); } void Scheduler::watchJobChanges(bool enable) { if (enable) { connect(nameEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(fitsEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(sequenceEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(startupScript, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(shutdownScript, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(schedulerProfileCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setDirty())); connect(stepsButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); connect(startupButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); connect(constraintButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); connect(completionButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); connect(startupProcedureButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); connect(shutdownProcedureGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); connect(culminationOffset, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); connect(startupTimeEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(minAltitude, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); connect(repeatsSpin, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); connect(minMoonSeparation, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); connect(completionTimeEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); connect(prioritySpin, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); } else { //disconnect(this, SLOT(setDirty())); disconnect(nameEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(fitsEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(sequenceEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(startupScript, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(shutdownScript, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(schedulerProfileCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setDirty())); disconnect(stepsButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); disconnect(startupButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); disconnect(constraintButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); disconnect(completionButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); disconnect(startupProcedureButtonGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); disconnect(shutdownProcedureGroup, SIGNAL(buttonToggled(int, bool)), this, SLOT(setDirty())); disconnect(culminationOffset, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); disconnect(startupTimeEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(minAltitude, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); disconnect(repeatsSpin, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); disconnect(minMoonSeparation, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); disconnect(completionTimeEdit, SIGNAL(editingFinished()), this, SLOT(setDirty())); disconnect(prioritySpin, SIGNAL(valueChanged(int)), this, SLOT(setDirty())); } } void Scheduler::appendLogText(const QString &text) { /* FIXME: user settings for log length */ int const max_log_count = 2000; if (logText.size() > max_log_count) logText.removeLast(); logText.prepend(i18nc("log entry; %1 is the date, %2 is the text", "%1 %2", QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss"), text)); qCInfo(KSTARS_EKOS_SCHEDULER) << text; emit newLog(); } void Scheduler::clearLog() { logText.clear(); emit newLog(); } void Scheduler::selectObject() { QPointer fd = new FindDialog(this); if (fd->exec() == QDialog::Accepted) { SkyObject *object = fd->targetObject(); addObject(object); } delete fd; } void Scheduler::addObject(SkyObject *object) { if (object != nullptr) { QString finalObjectName(object->name()); if (object->name() == "star") { StarObject *s = (StarObject *)object; if (s->getHDIndex() != 0) finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex())); } nameEdit->setText(finalObjectName); raBox->setText(object->ra0().toHMSString()); decBox->setText(object->dec0().toDMSString()); addToQueueB->setEnabled(sequenceEdit->text().isEmpty() == false); mosaicB->setEnabled(sequenceEdit->text().isEmpty() == false); } } void Scheduler::selectFITS() { fitsURL = QFileDialog::getOpenFileUrl(this, i18n("Select FITS Image"), dirPath, "FITS (*.fits *.fit)"); if (fitsURL.isEmpty()) return; dirPath = QUrl(fitsURL.url(QUrl::RemoveFilename)); fitsEdit->setText(fitsURL.toLocalFile()); if (nameEdit->text().isEmpty()) nameEdit->setText(fitsURL.fileName()); addToQueueB->setEnabled(sequenceEdit->text().isEmpty() == false); mosaicB->setEnabled(sequenceEdit->text().isEmpty() == false); setDirty(); } void Scheduler::selectSequence() { sequenceURL = QFileDialog::getOpenFileUrl(this, i18n("Select Sequence Queue"), dirPath, i18n("Ekos Sequence Queue (*.esq)")); if (sequenceURL.isEmpty()) return; dirPath = QUrl(sequenceURL.url(QUrl::RemoveFilename)); sequenceEdit->setText(sequenceURL.toLocalFile()); // For object selection, all fields must be filled if ((raBox->isEmpty() == false && decBox->isEmpty() == false && nameEdit->text().isEmpty() == false) // For FITS selection, only the name and fits URL should be filled. || (nameEdit->text().isEmpty() == false && fitsURL.isEmpty() == false)) { addToQueueB->setEnabled(true); mosaicB->setEnabled(true); } setDirty(); } void Scheduler::selectStartupScript() { startupScriptURL = QFileDialog::getOpenFileUrl(this, i18n("Select Startup Script"), dirPath, i18n("Script (*)")); if (startupScriptURL.isEmpty()) return; dirPath = QUrl(startupScriptURL.url(QUrl::RemoveFilename)); mDirty = true; startupScript->setText(startupScriptURL.toLocalFile()); } void Scheduler::selectShutdownScript() { shutdownScriptURL = QFileDialog::getOpenFileUrl(this, i18n("Select Shutdown Script"), dirPath, i18n("Script (*)")); if (shutdownScriptURL.isEmpty()) return; dirPath = QUrl(shutdownScriptURL.url(QUrl::RemoveFilename)); mDirty = true; shutdownScript->setText(shutdownScriptURL.toLocalFile()); } void Scheduler::addJob() { if (jobUnderEdit >= 0) { resetJobEdit(); return; } //jobUnderEdit = false; saveJob(); jobEvaluationOnly = true; evaluateJobs(); } void Scheduler::saveJob() { if (state == SCHEDULER_RUNNIG) { appendLogText(i18n("You cannot add or modify a job while the scheduler is running.")); return; } watchJobChanges(false); /* Warn if appending a job after infinite repeat */ /* FIXME: alter looping job priorities so that they are rescheduled later */ foreach(SchedulerJob * job, jobs) if(SchedulerJob::FINISH_LOOP == job->getCompletionCondition()) appendLogText(i18n("Warning! Job '%1' has completion condition set to infinite repeat, other jobs may not execute.",job->getName())); if (nameEdit->text().isEmpty()) { appendLogText(i18n("Target name is required.")); return; } if (sequenceEdit->text().isEmpty()) { appendLogText(i18n("Sequence file is required.")); return; } // Coordinates are required unless it is a FITS file if ((raBox->isEmpty() || decBox->isEmpty()) && fitsURL.isEmpty()) { appendLogText(i18n("Target coordinates are required.")); return; } // Create or Update a scheduler job SchedulerJob *job = nullptr; if (jobUnderEdit >= 0) job = jobs.at(queueTable->currentRow()); else job = new SchedulerJob(); job->setName(nameEdit->text()); job->setPriority(prioritySpin->value()); bool raOk = false, decOk = false; dms ra(raBox->createDms(false, &raOk)); //false means expressed in hours dms dec(decBox->createDms(true, &decOk)); if (raOk == false) { if(jobUnderEdit < 0) delete job; appendLogText(i18n("RA value %1 is invalid.", raBox->text())); return; } if (decOk == false) { if(jobUnderEdit < 0) delete job; appendLogText(i18n("DEC value %1 is invalid.", decBox->text())); return; } job->setTargetCoords(ra, dec); job->setDateTimeDisplayFormat(startupTimeEdit->displayFormat()); job->setSequenceFile(sequenceURL); fitsURL = QUrl::fromLocalFile(fitsEdit->text()); job->setFITSFile(fitsURL); // #1 Startup conditions if (asapConditionR->isChecked()) { job->setStartupCondition(SchedulerJob::START_ASAP); } else if (culminationConditionR->isChecked()) { job->setStartupCondition(SchedulerJob::START_CULMINATION); job->setCulminationOffset(culminationOffset->value()); } else { job->setStartupCondition(SchedulerJob::START_AT); job->setStartupTime(startupTimeEdit->dateTime()); } /* Store the original startup condition */ job->setFileStartupCondition(job->getStartupCondition()); job->setFileStartupTime(job->getStartupTime()); // #2 Constraints // Do we have minimum altitude constraint? if (altConstraintCheck->isChecked()) job->setMinAltitude(minAltitude->value()); else job->setMinAltitude(-1); // Do we have minimum moon separation constraint? if (moonSeparationCheck->isChecked()) job->setMinMoonSeparation(minMoonSeparation->value()); else job->setMinMoonSeparation(-1); // Check enforce weather constraints job->setEnforceWeather(weatherCheck->isChecked()); // twilight constraints job->setEnforceTwilight(twilightCheck->isChecked()); /* Verifications */ /* FIXME: perhaps use a method more visible to the end-user */ if (SchedulerJob::START_AT == job->getFileStartupCondition()) { /* Warn if appending a job which startup time doesn't allow proper score */ if (calculateJobScore(job, job->getStartupTime()) < 0) appendLogText(i18n("Warning! Job '%1' has startup time %1 resulting in a negative score, and will be marked invalid when processed.", job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()))); /* Warn if appending a job with a startup time that is in the past */ if (job->getStartupTime() < KStarsData::Instance()->lt()) appendLogText(i18n("Warning! Job '%1' has fixed startup time %1 set in the past, and will be marked invalid when evaluated.", job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()))); } // #3 Completion conditions if (sequenceCompletionR->isChecked()) { job->setCompletionCondition(SchedulerJob::FINISH_SEQUENCE); } else if (repeatCompletionR->isChecked()) { job->setCompletionCondition(SchedulerJob::FINISH_REPEAT); job->setRepeatsRequired(repeatsSpin->value()); job->setRepeatsRemaining(repeatsSpin->value()); } else if (loopCompletionR->isChecked()) { job->setCompletionCondition(SchedulerJob::FINISH_LOOP); } else { job->setCompletionCondition(SchedulerJob::FINISH_AT); job->setCompletionTime(completionTimeEdit->dateTime()); } // Job steps job->setStepPipeline(SchedulerJob::USE_NONE); if (trackStepCheck->isChecked()) job->setStepPipeline(static_cast(job->getStepPipeline() | SchedulerJob::USE_TRACK)); if (focusStepCheck->isChecked()) job->setStepPipeline(static_cast(job->getStepPipeline() | SchedulerJob::USE_FOCUS)); if (alignStepCheck->isChecked()) job->setStepPipeline(static_cast(job->getStepPipeline() | SchedulerJob::USE_ALIGN)); if (guideStepCheck->isChecked()) job->setStepPipeline(static_cast(job->getStepPipeline() | SchedulerJob::USE_GUIDE)); // Add job to queue if it is new if (jobUnderEdit == -1) jobs.append(job); int currentRow = 0; if (jobUnderEdit == -1) { currentRow = queueTable->rowCount(); queueTable->insertRow(currentRow); } else currentRow = queueTable->currentRow(); // Warn user if a duplicated job is in the list - same target, same sequence foreach (SchedulerJob *a_job, jobs) { if(a_job == job) { break; } else if(a_job->getName() == job->getName() && a_job->getSequenceFile() == job->getSequenceFile()) { appendLogText(i18n("Warning! Job '%1' at row %2 has a duplicate at row %3 (same target, same sequence file), " "the scheduler will consider the same storage for captures!", job->getName(), currentRow, a_job->getNameCell()? a_job->getNameCell()->row()+1 : 0)); appendLogText(i18n("Make sure job '%1' at row %2 has a specific startup time or a different priority, " "and a greater repeat count (or disable option 'Remember job progress')", job->getName(), currentRow)); } } /* Reset job state to evaluate the changes - so this is equivalent to double-clicking the job */ /* FIXME: should we do that if no change was done to the job? */ /* FIXME: move this to SchedulerJob as a "reset" method */ job->setState(SchedulerJob::JOB_IDLE); job->setStage(SchedulerJob::STAGE_IDLE); job->setEstimatedTime(-1); QTableWidgetItem *nameCell = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_NAME) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_NAME, nameCell); nameCell->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); nameCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setNameCell(nameCell); QTableWidgetItem *statusCell = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_STATUS) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_STATUS, statusCell); statusCell->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); statusCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setStatusCell(statusCell); QTableWidgetItem *captureCount = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_CAPTURES) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_CAPTURES, captureCount); captureCount->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); captureCount->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setCaptureCountCell(captureCount); QTableWidgetItem *scoreValue = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_SCORE) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_SCORE, scoreValue); scoreValue->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); scoreValue->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setScoreCell(scoreValue); QTableWidgetItem *startupCell = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_STARTTIME) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_STARTTIME, startupCell); startupCell->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); startupCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setStartupCell(startupCell); QTableWidgetItem *completionCell = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_ENDTIME) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_ENDTIME, completionCell); completionCell->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); completionCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setCompletionCell(completionCell); QTableWidgetItem *estimatedTimeCell = (jobUnderEdit >= 0) ? queueTable->item(currentRow, (int)SCHEDCOL_DURATION) : new QTableWidgetItem(); if (jobUnderEdit == -1) queueTable->setItem(currentRow, (int)SCHEDCOL_DURATION, estimatedTimeCell); estimatedTimeCell->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); estimatedTimeCell->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); job->setEstimatedTimeCell(estimatedTimeCell); if (queueTable->rowCount() > 0) { queueSaveAsB->setEnabled(true); queueSaveB->setEnabled(true); startB->setEnabled(true); mDirty = true; } removeFromQueueB->setEnabled(true); if (jobUnderEdit == -1) { startB->setEnabled(true); evaluateOnlyB->setEnabled(true); } watchJobChanges(true); } void Scheduler::resetJobState(QModelIndex i) { if (state == SCHEDULER_RUNNIG) { appendLogText(i18n("You cannot reset a job while the scheduler is running.")); return; } SchedulerJob *job = jobs.at(i.row()); if (job == nullptr) return; job->setState(SchedulerJob::JOB_IDLE); job->setStage(SchedulerJob::STAGE_IDLE); job->setEstimatedTime(-1); appendLogText(i18n("Job '%1' status is reset.", job->getName())); } void Scheduler::loadJob(QModelIndex i) { if (jobUnderEdit == i.row()) return; if (state == SCHEDULER_RUNNIG) { appendLogText(i18n("Warning! You cannot add or modify a job while the scheduler is running.")); return; } SchedulerJob *job = jobs.at(i.row()); if (job == nullptr) return; watchJobChanges(false); //job->setState(SchedulerJob::JOB_IDLE); //job->setStage(SchedulerJob::STAGE_IDLE); nameEdit->setText(job->getName()); prioritySpin->setValue(job->getPriority()); raBox->setText(job->getTargetCoords().ra0().toHMSString()); decBox->setText(job->getTargetCoords().dec0().toDMSString()); if (job->getFITSFile().isEmpty() == false) { fitsEdit->setText(job->getFITSFile().toLocalFile()); fitsURL = job->getFITSFile(); } else { fitsEdit->clear(); fitsURL = QUrl(); } sequenceEdit->setText(job->getSequenceFile().toLocalFile()); sequenceURL = job->getSequenceFile(); trackStepCheck->setChecked(job->getStepPipeline() & SchedulerJob::USE_TRACK); focusStepCheck->setChecked(job->getStepPipeline() & SchedulerJob::USE_FOCUS); alignStepCheck->setChecked(job->getStepPipeline() & SchedulerJob::USE_ALIGN); guideStepCheck->setChecked(job->getStepPipeline() & SchedulerJob::USE_GUIDE); switch (job->getFileStartupCondition()) { case SchedulerJob::START_ASAP: asapConditionR->setChecked(true); culminationOffset->setValue(DEFAULT_CULMINATION_TIME); break; case SchedulerJob::START_CULMINATION: culminationConditionR->setChecked(true); culminationOffset->setValue(job->getCulminationOffset()); break; case SchedulerJob::START_AT: startupTimeConditionR->setChecked(true); startupTimeEdit->setDateTime(job->getStartupTime()); culminationOffset->setValue(DEFAULT_CULMINATION_TIME); break; } if (job->getMinAltitude() >= 0) { altConstraintCheck->setChecked(true); minAltitude->setValue(job->getMinAltitude()); } else { altConstraintCheck->setChecked(false); minAltitude->setValue(DEFAULT_MIN_ALTITUDE); } if (job->getMinMoonSeparation() >= 0) { moonSeparationCheck->setChecked(true); minMoonSeparation->setValue(job->getMinMoonSeparation()); } else { moonSeparationCheck->setChecked(false); minMoonSeparation->setValue(DEFAULT_MIN_MOON_SEPARATION); } weatherCheck->setChecked(job->getEnforceWeather()); twilightCheck->blockSignals(true); twilightCheck->setChecked(job->getEnforceTwilight()); twilightCheck->blockSignals(false); switch (job->getCompletionCondition()) { case SchedulerJob::FINISH_SEQUENCE: sequenceCompletionR->setChecked(true); break; case SchedulerJob::FINISH_REPEAT: repeatCompletionR->setChecked(true); repeatsSpin->setValue(job->getRepeatsRequired()); break; case SchedulerJob::FINISH_LOOP: loopCompletionR->setChecked(true); break; case SchedulerJob::FINISH_AT: timeCompletionR->setChecked(true); completionTimeEdit->setDateTime(job->getCompletionTime()); break; } appendLogText(i18n("Editing job #%1...", i.row() + 1)); addToQueueB->setIcon(QIcon::fromTheme("edit-undo")); addToQueueB->setStyleSheet("background-color:orange;}"); addToQueueB->setEnabled(true); startB->setEnabled(false); evaluateOnlyB->setEnabled(false); addToQueueB->setToolTip(i18n("Exit edit mode")); jobUnderEdit = i.row(); watchJobChanges(true); } void Scheduler::resetJobEdit() { if (jobUnderEdit == -1) return; /* appendLogText(i18n("Edit mode cancelled.")); */ jobUnderEdit = -1; watchJobChanges(false); addToQueueB->setIcon(QIcon::fromTheme("list-add")); addToQueueB->setStyleSheet(QString()); addToQueueB->setToolTip(i18n("Add observation job to list.")); queueTable->clearSelection(); evaluateOnlyB->setEnabled(true); startB->setEnabled(true); //removeFromQueueB->setToolTip(i18n("Remove observation job from list.")); jobEvaluationOnly = true; evaluateJobs(); } void Scheduler::removeJob() { /*if (jobUnderEdit) { resetJobEdit(); return; }*/ int currentRow = queueTable->currentRow(); if (currentRow < 0) { currentRow = queueTable->rowCount() - 1; if (currentRow < 0) return; } queueTable->removeRow(currentRow); queueTable->resizeColumnsToContents(); SchedulerJob *job = jobs.at(currentRow); jobs.removeOne(job); delete (job); if (queueTable->rowCount() == 0) { removeFromQueueB->setEnabled(false); evaluateOnlyB->setEnabled(false); } queueTable->selectRow(queueTable->currentRow()); if (queueTable->rowCount() == 0) { queueSaveAsB->setEnabled(false); queueSaveB->setEnabled(false); startB->setEnabled(false); pauseB->setEnabled(false); if (jobUnderEdit >= 0) resetJobEdit(); } else loadJob(queueTable->currentIndex()); mDirty = true; } void Scheduler::toggleScheduler() { if (state == SCHEDULER_RUNNIG) { preemptiveShutdown = false; stop(); } else start(); } void Scheduler::stop() { if (state != SCHEDULER_RUNNIG) return; qCInfo(KSTARS_EKOS_SCHEDULER) << "Stopped."; // Stop running job and abort all others // in case of soft shutdown we skip this if (preemptiveShutdown == false) { bool wasAborted = false; foreach (SchedulerJob *job, jobs) { if (job == currentJob) { stopCurrentJobAction(); stopGuiding(); } if (job->getState() <= SchedulerJob::JOB_BUSY) { appendLogText(i18n("Job '%1' has not been processed upon scheduler stop, marking aborted.", job->getName())); job->setState(SchedulerJob::JOB_ABORTED); wasAborted = true; } } if (wasAborted) KNotification::event(QLatin1String("SchedulerAborted"), i18n("Scheduler aborted.")); } schedulerTimer.stop(); jobTimer.stop(); state = SCHEDULER_IDLE; ekosState = EKOS_IDLE; indiState = INDI_IDLE; parkWaitState = PARKWAIT_IDLE; // Only reset startup state to idle if the startup procedure was interrupted before it had the chance to complete. // Or if we're doing a soft shutdown if (startupState != STARTUP_COMPLETE || preemptiveShutdown) { if (startupState == STARTUP_SCRIPT) { scriptProcess.disconnect(); scriptProcess.terminate(); } startupState = STARTUP_IDLE; } // Reset startup state to unparking phase (dome -> mount -> cap) // We do not want to run the startup script again but unparking should be checked // whenever the scheduler is running again. else if (startupState == STARTUP_COMPLETE) { if (unparkDomeCheck->isChecked()) startupState = STARTUP_UNPARKING_DOME; else if (unparkMountCheck->isChecked()) startupState = STARTUP_UNPARKING_MOUNT; else if (uncapCheck->isChecked()) startupState = STARTUP_UNPARKING_CAP; } shutdownState = SHUTDOWN_IDLE; setCurrentJob(nullptr); captureBatch = 0; indiConnectFailureCount = 0; focusFailureCount = 0; guideFailureCount = 0; alignFailureCount = 0; captureFailureCount = 0; jobEvaluationOnly = false; loadAndSlewProgress = false; autofocusCompleted = false; startupB->setEnabled(true); shutdownB->setEnabled(true); // If soft shutdown, we return for now if (preemptiveShutdown) { sleepLabel->setToolTip(i18n("Scheduler is in shutdown until next job is ready")); sleepLabel->show(); return; } // Clear target name in capture interface upon stopping captureInterface->call(QDBus::AutoDetect, "setTargetName", QString()); if (scriptProcess.state() == QProcess::Running) scriptProcess.terminate(); sleepTimer.stop(); //sleepTimer.disconnect(); sleepLabel->hide(); pi->stopAnimation(); startB->setIcon(QIcon::fromTheme("media-playback-start")); startB->setToolTip(i18n("Start Scheduler")); pauseB->setEnabled(false); //startB->setText("Start Scheduler"); queueLoadB->setEnabled(true); addToQueueB->setEnabled(true); removeFromQueueB->setEnabled(true); mosaicB->setEnabled(true); evaluateOnlyB->setEnabled(true); } void Scheduler::start() { if (state == SCHEDULER_RUNNIG) return; else if (state == SCHEDULER_PAUSED) { state = SCHEDULER_RUNNIG; appendLogText(i18n("Scheduler resumed.")); startB->setIcon( QIcon::fromTheme("media-playback-stop")); startB->setToolTip(i18n("Stop Scheduler")); return; } startupScriptURL = QUrl::fromUserInput(startupScript->text()); if (startupScript->text().isEmpty() == false && startupScriptURL.isValid() == false) { appendLogText(i18n("Startup script URL %1 is not valid.", startupScript->text())); return; } shutdownScriptURL = QUrl::fromUserInput(shutdownScript->text()); if (shutdownScript->text().isEmpty() == false && shutdownScriptURL.isValid() == false) { appendLogText(i18n("Shutdown script URL %1 is not valid.", shutdownScript->text())); return; } qCInfo(KSTARS_EKOS_SCHEDULER) << "Starting..."; pi->startAnimation(); sleepLabel->hide(); //startB->setText("Stop Scheduler"); startB->setIcon(QIcon::fromTheme("media-playback-stop")); startB->setToolTip(i18n("Stop Scheduler")); pauseB->setEnabled(true); /* Recalculate dawn and dusk astronomical times - unconditionally in case date changed */ calculateDawnDusk(); state = SCHEDULER_RUNNIG; setCurrentJob(nullptr); jobEvaluationOnly = false; /* Reset all aborted jobs when starting the Scheduler. * When the Scheduler is stopped manually, all scheduled and running jobs do abort. * This snippet essentially has the same effect as double-clicking all aborted jobs before restarting. */ foreach (SchedulerJob *job, jobs) if (job->getState() == SchedulerJob::JOB_ABORTED) job->reset(); queueLoadB->setEnabled(false); addToQueueB->setEnabled(false); removeFromQueueB->setEnabled(false); mosaicB->setEnabled(false); evaluateOnlyB->setEnabled(false); startupB->setEnabled(false); shutdownB->setEnabled(false); schedulerTimer.start(); } void Scheduler::pause() { state = SCHEDULER_PAUSED; appendLogText(i18n("Scheduler paused.")); pauseB->setEnabled(false); startB->setIcon(QIcon::fromTheme("media-playback-start")); startB->setToolTip(i18n("Resume Scheduler")); } void Scheduler::setCurrentJob(SchedulerJob *job) { /* Reset job widgets */ if (currentJob) { currentJob->setStageLabel(nullptr); } /* Set current job */ currentJob = job; /* Reassign job widgets, or reset to defaults */ if (currentJob) { currentJob->setStageLabel(jobStatus); queueTable->selectRow(currentJob->getStartupCell()->row()); } else { jobStatus->setText(i18n("No job running")); queueTable->clearSelection(); } } void Scheduler::evaluateJobs() { /* FIXME: it is possible to evaluate jobs while KStars has a time offset, so warn the user about this */ QDateTime const now = KStarsData::Instance()->lt(); /* Start by refreshing the number of captures already present */ updateCompletedJobsCount(); /* Update dawn and dusk astronomical times - unconditionally in case date changed */ calculateDawnDusk(); /* First, filter out non-schedulable jobs */ /* FIXME: jobs in state JOB_ERROR should not be in the list, reorder states */ QList sortedJobs = jobs; sortedJobs.erase(std::remove_if(sortedJobs.begin(), sortedJobs.end(),[](SchedulerJob* job) { return SchedulerJob::JOB_ABORTED < job->getState(); }), sortedJobs.end()); /* Then reorder jobs by priority */ /* FIXME: refactor so all sorts are using the same predicates */ /* FIXME: use std::sort as qSort is deprecated */ if (Options::sortSchedulerJobs()) qSort(sortedJobs.begin(), sortedJobs.end(), SchedulerJob::increasingPriorityOrder); /* Then enumerate SchedulerJobs, scheduling only what is required */ foreach (SchedulerJob *job, sortedJobs) { /* Let aborted jobs be rescheduled later instead of forgetting them */ /* FIXME: minimum altitude and altitude cutoff may cause loops here */ switch (job->getState()) { /* If job is idle, set it for evaluation */ case SchedulerJob::JOB_IDLE: job->setState(SchedulerJob::JOB_EVALUATION); job->setEstimatedTime(-1); break; /* If job is aborted, reset it for evaluation */ case SchedulerJob::JOB_ABORTED: job->setState(SchedulerJob::JOB_EVALUATION); break; /* If job is scheduled, quick-check startup and bypass evaluation if in future */ case SchedulerJob::JOB_SCHEDULED: if (job->getStartupTime() < now) break; continue; /* If job is in error, invalid or complete, bypass evaluation */ case SchedulerJob::JOB_ERROR: case SchedulerJob::JOB_INVALID: case SchedulerJob::JOB_COMPLETE: continue; /* If job is busy, edge case, bypass evaluation */ case SchedulerJob::JOB_BUSY: continue; /* Else evaluate */ case SchedulerJob::JOB_EVALUATION: default: break; } // In case of a repeating jobs, let's make sure we have more runs left to go - /* FIXME: if finding a repeated job with no repeats remaining, state this is a safeguard - Is it really? Set complete? */ if (job->getCompletionCondition() == SchedulerJob::FINISH_REPEAT) { if (job->getRepeatsRemaining() == 0) { appendLogText(i18n("Job '%1' has no more batches remaining.", job->getName())); - job->setState(SchedulerJob::JOB_INVALID); - continue; + job->setState(SchedulerJob::JOB_EVALUATION); } } // -1 = Job is not estimated yet // -2 = Job is estimated but time is unknown // > 0 Job is estimated and time is known if (job->getEstimatedTime() == -1) { if (estimateJobTime(job) == false) { job->setState(SchedulerJob::JOB_INVALID); continue; } } if (job->getEstimatedTime() == 0) { + job->setRepeatsRemaining(0); job->setState(SchedulerJob::JOB_COMPLETE); continue; } // #1 Check startup conditions switch (job->getStartupCondition()) { // #1.1 ASAP? case SchedulerJob::START_ASAP: { /* Job is to be started as soon as possible, so check its current score */ int16_t const score = calculateJobScore(job, now); /* If it's not possible to run the job now, find proper altitude time */ if (score < 0) { // If Altitude or Dark score are negative, we try to schedule a better time for altitude and dark sky period. if (calculateAltitudeTime(job, job->getMinAltitude() > 0 ? job->getMinAltitude() : 0, job->getMinMoonSeparation())) { //appendLogText(i18n("%1 observation job is scheduled at %2", job->getName(), job->getStartupTime().toString())); job->setState(SchedulerJob::JOB_SCHEDULED); // Since it's scheduled, we need to skip it now and re-check it later since its startup condition changed to START_AT /*job->setScore(BAD_SCORE); continue;*/ } else { job->setState(SchedulerJob::JOB_INVALID); appendLogText(i18n("Ekos failed to schedule %1.", job->getName())); } /* Keep the job score for current time, score will refresh as scheduler progresses */ /* score = calculateJobScore(job, job->getStartupTime()); */ job->setScore(score); } /* If it's possible to run the job now, check weather */ else if (isWeatherOK(job) == false) { appendLogText(i18n("Job '%1' cannot run now because of bad weather.", job->getName())); job->setState(SchedulerJob::JOB_ABORTED); job->setScore(BAD_SCORE); } /* If weather is ok, schedule the job to run now */ else { appendLogText(i18n("Job '%1' is due to run as soon as possible.", job->getName())); /* Give a proper start time, so that job can be rescheduled if others also start asap */ job->setStartupTime(now); job->setState(SchedulerJob::JOB_SCHEDULED); job->setScore(score); } } break; // #1.2 Culmination? case SchedulerJob::START_CULMINATION: { if (calculateCulmination(job)) { appendLogText(i18n("Job '%1' is scheduled at %2 for culmination.", job->getName(), job->getStartupTime().toString())); job->setState(SchedulerJob::JOB_SCHEDULED); // Since it's scheduled, we need to skip it now and re-check it later since its startup condition changed to START_AT /*job->setScore(BAD_SCORE); continue;*/ } else { appendLogText(i18n("Job '%1' culmination cannot be scheduled, marking invalid.", job->getName())); job->setState(SchedulerJob::JOB_INVALID); } } break; // #1.3 Start at? case SchedulerJob::START_AT: { if (job->getCompletionCondition() == SchedulerJob::FINISH_AT) { if (job->getCompletionTime() <= job->getStartupTime()) { appendLogText(i18n("Job '%1' completion time (%2) could not be achieved before start up time (%3), marking invalid", job->getName(), job->getCompletionTime().toString(), job->getStartupTime().toString())); job->setState(SchedulerJob::JOB_INVALID); continue; } } int const timeUntil = now.secsTo(job->getStartupTime()); // If starting time already passed by 5 minutes (default), we mark the job as invalid or aborted if (timeUntil < (-1 * Options::leadTime() * 60)) { dms const passedUp(-timeUntil / 3600.0); /* Mark the job invalid only if its startup time was a user request, else just abort it for later reschedule */ if (job->getFileStartupCondition() == SchedulerJob::START_AT) { appendLogText(i18n("Job '%1' startup time was fixed at %2, and is already passed by %3, marking invalid.", job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()), passedUp.toHMSString())); job->setState(SchedulerJob::JOB_INVALID); } else { appendLogText(i18n("Job '%1' startup time was %2, and is already passed by %3, marking aborted.", job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()), passedUp.toHMSString())); job->setState(SchedulerJob::JOB_ABORTED); } - - continue; } // Start scoring once we reach startup time else if (timeUntil <= 0) { /* Consolidate altitude, moon separation and sky darkness scores */ int16_t const score = calculateJobScore(job, now); if (score < 0) { /* If job score is already negative, silently abort the job to avoid spamming the user */ if (0 < job->getScore()) { if (job->getState() == SchedulerJob::JOB_EVALUATION) appendLogText(i18n("Job '%1' evaluation failed with a score of %2, marking aborted.", job->getName(), score)); else if (timeUntil == 0) appendLogText(i18n("Job '%1' updated score is %2 at startup time, marking aborted.", job->getName(), score)); else appendLogText(i18n("Job '%1' updated score is %2 %3 seconds after startup time, marking aborted.", job->getName(), score, abs(timeUntil))); } job->setState(SchedulerJob::JOB_ABORTED); job->setScore(score); - - continue; } /* Positive score means job is already scheduled, so we check the weather, and if it is not OK, we set bad score until weather improves. */ else if (isWeatherOK(job) == false) { appendLogText(i18n("Job '%1' cannot run now because of bad weather.", job->getName())); job->setState(SchedulerJob::JOB_ABORTED); job->setScore(BAD_SCORE); } /* Else record current score */ - else job->setScore(score); + else + { + appendLogText(i18n("Job '%1' will be run at %2.", job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()))); + job->setState(SchedulerJob::JOB_SCHEDULED); + job->setScore(score); + } } #if 0 // If it is in the future and originally was designated as ASAP job // Job must be less than 12 hours away to be considered for re-evaluation else if (timeUntil > (Options::leadTime() * 60) && (timeUntil < 12 * 3600) && job->getFileStartupCondition() == SchedulerJob::START_ASAP) { QDateTime nextJobTime = now.addSecs(Options::leadTime() * 60); if (job->getEnforceTwilight() == false || (now > duskDateTime && now < preDawnDateTime)) { appendLogText(i18n("Job '%1' can be scheduled under 12 hours, but will be re-evaluated at %2.", job->getName(), nextJobTime.toString(job->getDateTimeDisplayFormat()))); job->setStartupTime(nextJobTime); } job->setScore(BAD_SCORE); } // If time is far in the future, we make the score negative else { if (job->getState() == SchedulerJob::JOB_EVALUATION && calculateJobScore(job, job->getStartupTime()) < 0) { appendLogText(i18n("Job '%1' can only be scheduled in more than 12 hours, marking aborted.", job->getName())); job->setState(SchedulerJob::JOB_ABORTED); continue; } /*score += BAD_SCORE;*/ } #endif + /* Else simply refresh job score */ + else + { + appendLogText(i18n("Job '%1' unmodified, will be run at %2.", job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()))); + job->setState(SchedulerJob::JOB_SCHEDULED); + job->setScore(calculateJobScore(job, now)); + } } break; } - //if (score > 0 && job->getState() == SchedulerJob::JOB_EVALUATION) if (job->getState() == SchedulerJob::JOB_EVALUATION) - job->setState(SchedulerJob::JOB_SCHEDULED); + { + qCDebug(KSTARS_EKOS_SCHEDULER) << "BUGBUG! Job '" << job->getName() << "' was unexpectedly not scheduled by evaluation."; + } } /* * At this step, we scheduled all jobs that had to be scheduled because they could not start as soon as possible. * Now we check the amount of jobs we have to run. */ int invalidJobs = 0, completedJobs = 0, abortedJobs = 0, upcomingJobs = 0; /* Partition jobs into invalid/aborted/completed/upcoming jobs */ foreach (SchedulerJob *job, jobs) { switch (job->getState()) { case SchedulerJob::JOB_INVALID: invalidJobs++; break; case SchedulerJob::JOB_ERROR: case SchedulerJob::JOB_ABORTED: abortedJobs++; break; case SchedulerJob::JOB_COMPLETE: completedJobs++; break; case SchedulerJob::JOB_SCHEDULED: case SchedulerJob::JOB_BUSY: upcomingJobs++; break; default: break; } } - if (upcomingJobs == 0) + /* And render some statistics */ + if (upcomingJobs == 0 && jobEvaluationOnly == false) { - if (jobEvaluationOnly == false) - { - if (invalidJobs == jobs.count()) - { - appendLogText(i18n("No valid jobs found, aborting schedule...")); - stop(); - return; - } - - if (invalidJobs > 0) - appendLogText(i18np("%1 job is invalid.", "%1 jobs are invalid.", invalidJobs)); + if (invalidJobs > 0) + appendLogText(i18np("%1 job is invalid.", "%1 jobs are invalid.", invalidJobs)); - if (abortedJobs > 0) - appendLogText(i18np("%1 job aborted.", "%1 jobs aborted", abortedJobs)); - - if (completedJobs > 0) - appendLogText(i18np("%1 job completed.", "%1 jobs completed.", completedJobs)); - - if (startupState == STARTUP_COMPLETE) - { - appendLogText(i18n("Scheduler complete. Starting shutdown procedure...")); - // Let's start shutdown procedure - checkShutdownState(); - } - else - stop(); + if (abortedJobs > 0) + appendLogText(i18np("%1 job aborted.", "%1 jobs aborted", abortedJobs)); - return; - } - else if (jobs.isEmpty()) - return; + if (completedJobs > 0) + appendLogText(i18np("%1 job completed.", "%1 jobs completed.", completedJobs)); } /* * At this step, we still have jobs to run. * We filter out jobs that won't run now, and make sure jobs are not all starting at the same time. */ updatePreDawn(); /* Remove complete and invalid jobs that could have appeared during the last evaluation */ sortedJobs.erase(std::remove_if(sortedJobs.begin(), sortedJobs.end(),[](SchedulerJob* job) { return SchedulerJob::JOB_ABORTED < job->getState(); }), sortedJobs.end()); - /* If there are no jobs left to run in the filtered list, shutdown scheduler */ + /* If there are no jobs left to run in the filtered list, shutdown scheduler and stop evaluation now */ if (sortedJobs.isEmpty()) { - if (startupState == STARTUP_COMPLETE) + if (!jobEvaluationOnly) { - appendLogText(i18n("No jobs left in the scheduler queue, starting shutdown procedure...")); - // Let's start shutdown procedure - checkShutdownState(); + if (startupState == STARTUP_COMPLETE) + { + appendLogText(i18n("No jobs left in the scheduler queue, starting shutdown procedure...")); + // Let's start shutdown procedure + checkShutdownState(); + } + else + { + appendLogText(i18n("No jobs left in the scheduler queue, scheduler is stopping.")); + stop(); + } } - else stop(); + else jobEvaluationOnly = false; return; } /* Now that jobs are scheduled, possibly at the same time, reorder by altitude and priority again */ if (Options::sortSchedulerJobs()) { qSort(sortedJobs.begin(), sortedJobs.end(), SchedulerJob::decreasingAltitudeOrder); qSort(sortedJobs.begin(), sortedJobs.end(), SchedulerJob::increasingPriorityOrder); } // Our first job now takes priority over ALL others. // So if any other jobs conflicts with ours, we re-schedule that job to another time. SchedulerJob *firstJob = sortedJobs.first(); QDateTime firstStartTime = firstJob->getStartupTime(); QDateTime lastStartTime = firstJob->getStartupTime(); double lastJobEstimatedTime = firstJob->getEstimatedTime(); int daysCount = 0; // Make sure no two jobs have the same scheduled time or overlap with other jobs foreach (SchedulerJob *job, sortedJobs) { // If this job is not scheduled, continue // If this job startup conditon is not to start at a specific time, continue if (job == firstJob || job->getState() != SchedulerJob::JOB_SCHEDULED || job->getStartupCondition() != SchedulerJob::START_AT) continue; double timeBetweenJobs = (double)std::abs(firstStartTime.secsTo(job->getStartupTime())); // If there are within 5 minutes of each other, try to advance scheduling time of the lower altitude one if (timeBetweenJobs < (Options::leadTime()) * 60) { double delayJob = timeBetweenJobs + lastJobEstimatedTime; if (delayJob < (Options::leadTime() * 60)) delayJob = Options::leadTime() * 60; QDateTime otherjob_time = lastStartTime.addSecs(delayJob); QDateTime nextPreDawnTime = preDawnDateTime.addDays(daysCount); // If other jobs starts after pre-dawn limit, then we schedule it to the next day. // But we only take this action IF the job we are checking against starts _before_ dawn and our // job therefore carry us after down, then there is an actual need to schedule it next day. // FIXME: After changing time we are not evaluating job again when we should. if (job->getEnforceTwilight() && lastStartTime < nextPreDawnTime && otherjob_time >= nextPreDawnTime) { QDateTime date; daysCount++; lastStartTime = job->getStartupTime().addDays(daysCount); job->setStartupTime(lastStartTime); date = lastStartTime.addSecs(delayJob); } else { lastStartTime = lastStartTime.addSecs(delayJob); job->setStartupTime(lastStartTime); } job->setState(SchedulerJob::JOB_SCHEDULED); /* Kept the informative log now that aborted jobs are rescheduled */ appendLogText(i18n("Jobs '%1' and '%2' have close start up times, job '%2' is rescheduled to %3.", firstJob->getName(), job->getName(), job->getStartupTime().toString(job->getDateTimeDisplayFormat()))); } lastJobEstimatedTime = job->getEstimatedTime(); } - if (jobEvaluationOnly) + if (jobEvaluationOnly || state != SCHEDULER_RUNNIG) { appendLogText(i18n("Job evaluation complete.")); jobEvaluationOnly = false; return; } /* * At this step, we finished evaluating jobs. * We select the first job that has to be run, per schedule. */ // Sort again by schedule, sooner first, as some jobs may have shifted during the last step qSort(sortedJobs.begin(), sortedJobs.end(), SchedulerJob::increasingStartupTimeOrder); setCurrentJob(sortedJobs.first()); /* Check if job can be processed right now */ if (currentJob->getFileStartupCondition() == SchedulerJob::START_ASAP) if( 0 < calculateJobScore(currentJob, now)) currentJob->setStartupTime(now); int const nextObservationTime = now.secsTo(currentJob->getStartupTime()); appendLogText(i18n("Job '%1' is selected for next observation with priority #%2 and score %3.", currentJob->getName(), currentJob->getPriority(), currentJob->getScore())); // If mount was previously parked awaiting job activation, we unpark it. if (parkWaitState == PARKWAIT_PARKED) { parkWaitState = PARKWAIT_UNPARK; return; } // If we already started, we check when the next object is scheduled at. // If it is more than 30 minutes in the future, we park the mount if that is supported // and we unpark when it is due to start. // If start up procedure is complete and the user selected pre-emptive shutdown, let us check if the next observation time exceed // the pre-emptive shutdown time in hours (default 2). If it exceeds that, we perform complete shutdown until next job is ready if (startupState == STARTUP_COMPLETE && Options::preemptiveShutdown() && nextObservationTime > (Options::preemptiveShutdownTime() * 3600)) { appendLogText(i18n("Job '%1' scheduled for execution at %2. Observatory scheduled for " "shutdown until next job is ready.", currentJob->getName(), currentJob->getStartupTime().toString())); preemptiveShutdown = true; weatherCheck->setEnabled(false); weatherLabel->hide(); checkShutdownState(); // Wake up when job is due //sleepTimer.setInterval((nextObservationTime * 1000 - (1000 * Options::leadTime() * 60))); sleepTimer.setInterval(( (nextObservationTime+1) * 1000)); //connect(&sleepTimer, SIGNAL(timeout()), this, SLOT(wakeUpScheduler())); sleepTimer.start(); } // Otherise, sleep until job is ready /* FIXME: if not parking, stop tracking maybe? this would prevent crashes or scheduler stops from leaving the mount to track and bump the pier */ //else if (nextObservationTime > (Options::leadTime() * 60)) else if (nextObservationTime > 1) { // If start up procedure is already complete, and we didn't issue any parking commands before and parking is checked and enabled // Then we park the mount until next job is ready. But only if the job uses TRACK as its first step, otherwise we cannot get into position again. // This is also only performed if next job is due more than the default lead time (5 minutes). // If job is due sooner than that is not worth parking and we simply go into sleep or wait modes. if ((nextObservationTime > (Options::leadTime() * 60)) && startupState == STARTUP_COMPLETE && parkWaitState == PARKWAIT_IDLE && (currentJob->getStepPipeline() & SchedulerJob::USE_TRACK) && parkMountCheck->isEnabled() && parkMountCheck->isChecked()) { appendLogText(i18n("Job '%1' scheduled for execution at %2. Parking the mount until " "the job is ready.", currentJob->getName(), currentJob->getStartupTime().toString())); parkWaitState = PARKWAIT_PARK; } // If mount was pre-emptivally parked OR if parking is not supported or if start up procedure is IDLE then go into // sleep mode until next job is ready. #if 0 else if ((nextObservationTime > (Options::leadTime() * 60)) && (parkWaitState == PARKWAIT_PARKED || parkMountCheck->isEnabled() == false || parkMountCheck->isChecked() == false || startupState == STARTUP_IDLE)) { appendLogText(i18n("Sleeping until observation job %1 is ready at %2...", currentJob->getName(), now.addSecs(nextObservationTime+1).toString())); sleepLabel->setToolTip(i18n("Scheduler is in sleep mode")); schedulerTimer.stop(); sleepLabel->show(); // Wake up when job is ready. // N.B. Waking 5 minutes before is useless now because we evaluate ALL scheduled jobs each second // So just wake it up when it is exactly due sleepTimer.setInterval(( (nextObservationTime+1) * 1000)); sleepTimer.start(); } #endif // The only difference between sleep and wait modes is the time. If the time more than lead time (5 minutes by default) // then we sleep, otherwise we wait. It's the same thing, just different labels. else { appendLogText(i18n("Sleeping until observation job %1 is ready at %2...", currentJob->getName(), now.addSecs(nextObservationTime+1).toString())); sleepLabel->setToolTip(i18n("Scheduler is in sleep mode")); schedulerTimer.stop(); sleepLabel->show(); /* FIXME: stop tracking now */ // Wake up when job is ready. // N.B. Waking 5 minutes before is useless now because we evaluate ALL scheduled jobs each second // So just wake it up when it is exactly due sleepTimer.setInterval(( (nextObservationTime+1) * 1000)); //connect(&sleepTimer, SIGNAL(timeout()), this, SLOT(wakeUpScheduler())); sleepTimer.start(); } } } void Scheduler::wakeUpScheduler() { sleepLabel->hide(); sleepTimer.stop(); if (preemptiveShutdown) { preemptiveShutdown = false; appendLogText(i18n("Scheduler is awake.")); start(); } else { if (state == SCHEDULER_RUNNIG) appendLogText(i18n("Scheduler is awake. Jobs shall be started when ready...")); else appendLogText(i18n("Scheduler is awake. Jobs shall be started when scheduler is resumed.")); schedulerTimer.start(); } } double Scheduler::findAltitude(const SkyPoint &target, const QDateTime &when) { // Make a copy /*SkyPoint p = target; QDateTime lt(when.date(), QTime()); KStarsDateTime ut = KStarsData::Instance()->geo()->LTtoUT(KStarsDateTime(lt)); KStarsDateTime myUT = ut.addSecs(when.time().msecsSinceStartOfDay() / 1000); CachingDms LST = KStarsData::Instance()->geo()->GSTtoLST(myUT.gst()); p.EquatorialToHorizontal(&LST, KStarsData::Instance()->geo()->lat()); return p.alt().Degrees();*/ SkyPoint p = target; KStarsDateTime lt(when); CachingDms LST = KStarsData::Instance()->geo()->GSTtoLST(lt.gst()); p.EquatorialToHorizontal(&LST, KStarsData::Instance()->geo()->lat()); return p.alt().Degrees(); } bool Scheduler::calculateAltitudeTime(SchedulerJob *job, double minAltitude, double minMoonAngle) { // We wouldn't stat observation 30 mins (default) before dawn. double const earlyDawn = Dawn - Options::preDawnTime() / (60.0 * 24.0); /* Compute UTC for beginning of today */ QDateTime const lt(KStarsData::Instance()->lt().date(), QTime()); KStarsDateTime const ut = geo->LTtoUT(KStarsDateTime(lt)); /* Retrieve target coordinates to be converted to horizontal to determine altitude */ SkyPoint target = job->getTargetCoords(); /* Retrieve the current fraction of the day */ QTime const now = KStarsData::Instance()->lt().time(); double const fraction = now.hour() + now.minute() / 60.0 + now.second() / 3600; /* This attempts to locate the first minute of the next 24 hours when the job target matches the altitude and moon constraints */ for (double hour = fraction; hour < (fraction + 24); hour += 1.0 / 60.0) { double const rawFrac = (hour > 24 ? (hour - 24) : hour) / 24.0; /* Test twilight enforcement, and if enforced, bail out if start time is during day */ /* FIXME: rework day fraction loop to shift to dusk directly */ if (job->getEnforceTwilight() && Dawn <= rawFrac && rawFrac <= Dusk) continue; /* Compute altitude of target for the current fraction of the day */ KStarsDateTime const myUT = ut.addSecs(hour * 3600.0); CachingDms const LST = geo->GSTtoLST(myUT.gst()); target.EquatorialToHorizontal(&LST, geo->lat()); double const altitude = target.alt().Degrees(); if (altitude > minAltitude) { QDateTime const startTime = geo->UTtoLT(myUT); /* Test twilight enforcement, and if enforced, bail out if start time is too close to dawn */ if (job->getEnforceTwilight() && earlyDawn < rawFrac && rawFrac < Dawn) { appendLogText(i18n("Job '%1' reaches an altitude of %2 degrees at %3 but will not be scheduled due to " "close proximity to astronomical twilight rise.", job->getName(), QString::number(minAltitude, 'g', 3), startTime.toString(job->getDateTimeDisplayFormat()))); return false; } /* Continue searching if Moon separation is not good enough */ if (minMoonAngle > 0 && getMoonSeparationScore(job, startTime) < 0) continue; /* FIXME: the name of the function doesn't suggest the job can be modified */ job->setStartupTime(startTime); /* Kept the informative log because of the reschedule of aborted jobs */ appendLogText(i18n("Job '%1' is scheduled to start at %2 where its altitude is %3 degrees.", job->getName(), startTime.toString(job->getDateTimeDisplayFormat()), QString::number(altitude, 'g', 3))); return true; } } /* FIXME: move this to the caller too to comment the decision to reject the job */ if (minMoonAngle == -1) { if (job->getEnforceTwilight()) { appendLogText(i18n("Job '%1' has no night time with an altitude above %2 degrees during the next 24 hours, marking invalid.", job->getName(), QString::number(minAltitude, 'g', 3))); } else appendLogText(i18n("Job '%1' can't rise to an altitude above %2 degrees in the next 24 hours, marking invalid.", job->getName(), QString::number(minAltitude, 'g', 3))); } else appendLogText(i18n("Job '%1' can't be scheduled with an altitude above %2 degrees with minimum moon " "separation of %3 degrees in the next 24 hours, marking invalid.", job->getName(), QString::number(minAltitude, 'g', 3), QString::number(minMoonAngle, 'g', 3))); return false; } bool Scheduler::calculateCulmination(SchedulerJob *job) { SkyPoint target = job->getTargetCoords(); SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); o.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QDateTime midnight(KStarsData::Instance()->lt().date(), QTime()); KStarsDateTime dt = geo->LTtoUT(KStarsDateTime(midnight)); QTime transitTime = o.transitTime(dt, geo); appendLogText(i18n("%1 Transit time is %2", job->getName(), transitTime.toString(job->getDateTimeDisplayFormat()))); int dayOffset = 0; if (KStarsData::Instance()->lt().time() > transitTime) dayOffset = 1; QDateTime observationDateTime(QDate::currentDate().addDays(dayOffset), transitTime.addSecs(job->getCulminationOffset() * 60)); appendLogText(i18np("%1 Observation time is %2 adjusted for %3 minute.", "%1 Observation time is %2 adjusted for %3 minutes.", job->getName(), observationDateTime.toString(job->getDateTimeDisplayFormat()), job->getCulminationOffset())); if (job->getEnforceTwilight() && getDarkSkyScore(observationDateTime) < 0) { appendLogText(i18n("%1 culminates during the day and cannot be scheduled for observation.", job->getName())); return false; } if (observationDateTime < (static_cast(KStarsData::Instance()->lt()))) { appendLogText(i18n("Observation time for %1 already passed.", job->getName())); return false; } job->setStartupTime(observationDateTime); return true; } void Scheduler::checkWeather() { if (weatherCheck->isEnabled() == false || weatherCheck->isChecked() == false) return; IPState newStatus; QString statusString; QDBusReply weatherReply = weatherInterface->call(QDBus::AutoDetect, "getWeatherStatus"); if (weatherReply.error().type() == QDBusError::NoError) { newStatus = (IPState)weatherReply.value(); switch (newStatus) { case IPS_OK: statusString = i18n("Weather conditions are OK."); break; case IPS_BUSY: statusString = i18n("Warning! Weather conditions are in the WARNING zone."); break; case IPS_ALERT: statusString = i18n("Caution! Weather conditions are in the DANGER zone!"); break; default: if (noWeatherCounter++ >= MAX_FAILURE_ATTEMPTS) { noWeatherCounter = 0; appendLogText(i18n("Warning: Ekos did not receive any weather updates for the last %1 minutes.", weatherTimer.interval() / (60000.0))); } break; } if (newStatus != weatherStatus) { weatherStatus = newStatus; qCDebug(KSTARS_EKOS_SCHEDULER) << statusString; if (weatherStatus == IPS_OK) weatherLabel->setPixmap( QIcon::fromTheme("security-high") .pixmap(QSize(32, 32))); else if (weatherStatus == IPS_BUSY) { weatherLabel->setPixmap( QIcon::fromTheme("security-medium") .pixmap(QSize(32, 32))); KNotification::event(QLatin1String("WeatherWarning"), i18n("Weather conditions in warning zone")); } else if (weatherStatus == IPS_ALERT) { weatherLabel->setPixmap( QIcon::fromTheme("security-low") .pixmap(QSize(32, 32))); KNotification::event(QLatin1String("WeatherAlert"), i18n("Weather conditions are critical. Observatory shutdown is imminent")); } else weatherLabel->setPixmap(QIcon::fromTheme("chronometer") .pixmap(QSize(32, 32))); weatherLabel->show(); weatherLabel->setToolTip(statusString); appendLogText(statusString); emit weatherChanged(weatherStatus); } if (weatherStatus == IPS_ALERT) { appendLogText(i18n("Starting shutdown procedure due to severe weather.")); if (currentJob) { stopCurrentJobAction(); stopGuiding(); jobTimer.stop(); currentJob->setState(SchedulerJob::JOB_ABORTED); currentJob->setStage(SchedulerJob::STAGE_IDLE); } checkShutdownState(); //connect(KStars::Instance()->data()->clock(), SIGNAL(timeAdvanced()), this, SLOT(checkStatus()), Qt::UniqueConnection); } } } int16_t Scheduler::getWeatherScore() { if (weatherCheck->isEnabled() == false || weatherCheck->isChecked() == false) return 0; if (weatherStatus == IPS_BUSY) return BAD_SCORE / 2; else if (weatherStatus == IPS_ALERT) return BAD_SCORE; return 0; } int16_t Scheduler::getDarkSkyScore(const QDateTime &observationDateTime) { // if (job->getStartingCondition() == SchedulerJob::START_CULMINATION) // return -1000; int16_t score = 0; double dayFraction = 0; // Anything half an hour before dawn shouldn't be a good candidate double earlyDawn = Dawn - Options::preDawnTime() / (60.0 * 24.0); dayFraction = observationDateTime.time().msecsSinceStartOfDay() / (24.0 * 60.0 * 60.0 * 1000.0); // The farther the target from dawn, the better. if (dayFraction > earlyDawn && dayFraction < Dawn) score = BAD_SCORE / 50; else if (dayFraction < Dawn) score = (Dawn - dayFraction) * 100; else if (dayFraction > Dusk) { score = (dayFraction - Dusk) * 100; } else score = BAD_SCORE; qCDebug(KSTARS_EKOS_SCHEDULER) << "Dark sky score is" << score << "for time" << observationDateTime.toString(); return score; } int16_t Scheduler::calculateJobScore(SchedulerJob *job, QDateTime when) { /* Only consolidate the score if light frames are required, calibration frames can run whenever needed */ if (!job->getLightFramesRequired()) return 1000; int16_t total = 0; /* As soon as one score is negative, it's a no-go and other scores are unneeded */ if (job->getEnforceTwilight()) total += getDarkSkyScore(when); if (0 <= total && job->getStepPipeline() != SchedulerJob::USE_NONE) total += getAltitudeScore(job, when); if (0 <= total) total += getMoonSeparationScore(job, when); appendLogText(i18n("Job '%1' has a total score of %2", job->getName(), total)); return total; } int16_t Scheduler::getAltitudeScore(SchedulerJob *job, QDateTime when) { int16_t score = 0; double currentAlt = findAltitude(job->getTargetCoords(), when); if (currentAlt < 0) { score = BAD_SCORE; } // If minimum altitude is specified else if (job->getMinAltitude() > 0) { // if current altitude is lower that's not good if (currentAlt < job->getMinAltitude()) score = BAD_SCORE; else { // Get HA of actual object, and not of the mount as was done below double HA = KStars::Instance()->data()->lst()->Hours() - job->getTargetCoords().ra().Hours(); #if 0 if (indiState == INDI_READY) { QDBusReply haReply = mountInterface->call(QDBus::AutoDetect, "getHourAngle"); if (haReply.error().type() == QDBusError::NoError) HA = haReply.value(); } #endif // If already passed the merdian and setting we check if it is within setting alttidue cut off value (3 degrees default) // If it is within that value then it is useless to start the job which will end very soon so we better look for a better job. /* FIXME: don't use BAD_SCORE/2, a negative result implies the job has to be aborted - we'd be annoyed if that score became positive again */ /* FIXME: bug here, raising target will get a negative score if under cutoff, issue mitigated by aborted jobs getting rescheduled */ if (HA > 0 && (currentAlt - SETTING_ALTITUDE_CUTOFF) < job->getMinAltitude()) score = BAD_SCORE / 2.0; else // Otherwise, adjust score and add current altitude to score weight score = (1.5 * pow(1.06, currentAlt)) - (minAltitude->minimum() / 10.0); } } // If it's below minimum hard altitude (15 degrees now), set score to 10% of altitude value else if (currentAlt < minAltitude->minimum()) { score = currentAlt / 10.0; } // If no minimum altitude, then adjust altitude score to account for current target altitude else { score = (1.5 * pow(1.06, currentAlt)) - (minAltitude->minimum() / 10.0); } /* Kept the informative log now that scores are displayed */ appendLogText(i18n("Job '%1' target altitude is %3 degrees at %2 (score %4).", job->getName(), when.toString(job->getDateTimeDisplayFormat()), QString::number(currentAlt, 'g', 3), QString::asprintf("%+d", score))); return score; } double Scheduler::getCurrentMoonSeparation(SchedulerJob *job) { // Get target altitude given the time SkyPoint p = job->getTargetCoords(); QDateTime midnight(KStarsData::Instance()->lt().date(), QTime()); KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight)); KStarsDateTime myUT = ut.addSecs(KStarsData::Instance()->lt().time().msecsSinceStartOfDay() / 1000); CachingDms LST = geo->GSTtoLST(myUT.gst()); p.EquatorialToHorizontal(&LST, geo->lat()); // Update moon ut = geo->LTtoUT(KStarsData::Instance()->lt()); KSNumbers ksnum(ut.djd()); LST = geo->GSTtoLST(ut.gst()); moon->updateCoords(&ksnum, true, geo->lat(), &LST, true); // Moon/Sky separation p return moon->angularDistanceTo(&p).Degrees(); } int16_t Scheduler::getMoonSeparationScore(SchedulerJob *job, QDateTime when) { int16_t score = 0; // Get target altitude given the time SkyPoint p = job->getTargetCoords(); QDateTime midnight(when.date(), QTime()); KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight)); KStarsDateTime myUT = ut.addSecs(when.time().msecsSinceStartOfDay() / 1000); CachingDms LST = geo->GSTtoLST(myUT.gst()); p.EquatorialToHorizontal(&LST, geo->lat()); double currentAlt = p.alt().Degrees(); // Update moon ut = geo->LTtoUT(KStarsDateTime(when)); KSNumbers ksnum(ut.djd()); LST = geo->GSTtoLST(ut.gst()); moon->updateCoords(&ksnum, true, geo->lat(), &LST, true); double moonAltitude = moon->alt().Degrees(); // Lunar illumination % double illum = moon->illum() * 100.0; // Moon/Sky separation p double separation = moon->angularDistanceTo(&p).Degrees(); // Zenith distance of the moon double zMoon = (90 - moonAltitude); // Zenith distance of target double zTarget = (90 - currentAlt); // If target = Moon, or no illuminiation, or moon below horizon, return static score. if (zMoon == zTarget || illum == 0 || zMoon >= 90) score = 100; else { // JM: Some magic voodoo formula I came up with! double moonEffect = (pow(separation, 1.7) * pow(zMoon, 0.5)) / (pow(zTarget, 1.1) * pow(illum, 0.5)); // Limit to 0 to 100 range. moonEffect = KSUtils::clamp(moonEffect, 0.0, 100.0); if (job->getMinMoonSeparation() > 0) { if (separation < job->getMinMoonSeparation()) score = BAD_SCORE * 5; else score = moonEffect; } else score = moonEffect; } // Limit to 0 to 20 score /= 5.0; /* Kept the informative log now that score is displayed */ appendLogText(i18n("Job '%1' target is %3 degrees from Moon (score %2).", job->getName(), QString::asprintf("%+d", score), separation)); return score; } void Scheduler::calculateDawnDusk() { KSAlmanac ksal; Dawn = ksal.getDawnAstronomicalTwilight(); Dusk = ksal.getDuskAstronomicalTwilight(); QTime now = KStarsData::Instance()->lt().time(); QTime dawn = QTime(0, 0, 0).addSecs(Dawn * 24 * 3600); QTime dusk = QTime(0, 0, 0).addSecs(Dusk * 24 * 3600); duskDateTime.setDate(KStars::Instance()->data()->lt().date()); duskDateTime.setTime(dusk); appendLogText(i18n("Astronomical twilight rise is at %1, set is at %2, and current time is %3", dawn.toString(), dusk.toString(), now.toString())); } void Scheduler::executeJob(SchedulerJob *job) { + setCurrentJob(job); + + /* If job schedule isn't now, postpone - else this will cancel a parking attempt */ + if (0 < KStarsData::Instance()->lt().secsTo(currentJob->getStartupTime())) + return; + if (job->getCompletionCondition() == SchedulerJob::FINISH_SEQUENCE && Options::rememberJobProgress()) { QString targetName = job->getName().replace(' ', ""); QList targetArgs; targetArgs.append(targetName); captureInterface->callWithArgumentList(QDBus::AutoDetect, "setTargetName", targetArgs); } - setCurrentJob(job); + updatePreDawn(); qCInfo(KSTARS_EKOS_SCHEDULER) << "Executing Job " << currentJob->getName(); + currentJob->setState(SchedulerJob::JOB_BUSY); + KNotification::event(QLatin1String("EkosSchedulerJobStart"), i18n("Ekos job started (%1)", currentJob->getName())); - /* If job schedule allows it, start the job right now */ - if (KStarsData::Instance()->lt().secsTo(currentJob->getStartupTime()) <= 0) - currentJob->setState(SchedulerJob::JOB_BUSY); - - updatePreDawn(); - // No need to continue evaluating jobs as we already have one. schedulerTimer.stop(); jobTimer.start(); } bool Scheduler::checkEkosState() { if (state == SCHEDULER_PAUSED) return false; switch (ekosState) { case EKOS_IDLE: { // Even if state is IDLE, check if Ekos is already started. If not, start it. QDBusReply isEkosStarted; isEkosStarted = ekosInterface->call(QDBus::AutoDetect, "getEkosStartingStatus"); if (isEkosStarted.value() == EkosManager::EKOS_STATUS_SUCCESS) { ekosState = EKOS_READY; return true; } else { ekosInterface->call(QDBus::AutoDetect, "start"); ekosState = EKOS_STARTING; currentOperationTime.start(); return false; } } break; case EKOS_STARTING: { QDBusReply isEkosStarted; isEkosStarted = ekosInterface->call(QDBus::AutoDetect, "getEkosStartingStatus"); if (isEkosStarted.value() == EkosManager::EKOS_STATUS_SUCCESS) { appendLogText(i18n("Ekos started.")); ekosState = EKOS_READY; return true; } else if (isEkosStarted.value() == EkosManager::EKOS_STATUS_ERROR) { appendLogText(i18n("Ekos failed to start.")); stop(); return false; } // If a minute passed, give up else if (currentOperationTime.elapsed() > (60 * 1000)) { appendLogText(i18n("Ekos timed out.")); stop(); return false; } } break; case EKOS_STOPPING: { QDBusReply isEkosStarted; isEkosStarted = ekosInterface->call(QDBus::AutoDetect, "getEkosStartingStatus"); if (isEkosStarted.value() == EkosManager::EKOS_STATUS_IDLE) { appendLogText(i18n("Ekos stopped.")); ekosState = EKOS_IDLE; return true; } } break; case EKOS_READY: return true; break; } return false; } bool Scheduler::checkINDIState() { if (state == SCHEDULER_PAUSED) return false; qCDebug(KSTARS_EKOS_SCHEDULER) << "Checking INDI State..."; switch (indiState) { case INDI_IDLE: { // Even in idle state, we make sure that INDI is not already connected. QDBusReply isINDIConnected = ekosInterface->call(QDBus::AutoDetect, "getINDIConnectionStatus"); if (isINDIConnected.value() == EkosManager::EKOS_STATUS_SUCCESS) { indiState = INDI_PROPERTY_CHECK; qCDebug(KSTARS_EKOS_SCHEDULER) << "Checking INDI Properties..."; return false; } else { ekosInterface->call(QDBus::AutoDetect, "connectDevices"); indiState = INDI_CONNECTING; currentOperationTime.start(); qCDebug(KSTARS_EKOS_SCHEDULER) << "Connecting INDI Devices"; return false; } } break; case INDI_CONNECTING: { QDBusReply isINDIConnected = ekosInterface->call(QDBus::AutoDetect, "getINDIConnectionStatus"); if (isINDIConnected.value() == EkosManager::EKOS_STATUS_SUCCESS) { appendLogText(i18n("INDI devices connected.")); indiState = INDI_PROPERTY_CHECK; return false; } else if (isINDIConnected.value() == EkosManager::EKOS_STATUS_ERROR) { if (indiConnectFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("One or more INDI devices failed to connect. Retrying...")); ekosInterface->call(QDBus::AutoDetect, "connectDevices"); return false; } appendLogText(i18n("INDI devices failed to connect. Check INDI control panel for details.")); stop(); return false; } // If 30 seconds passed, we retry else if (currentOperationTime.elapsed() > (30 * 1000)) { if (indiConnectFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("One or more INDI devices failed to connect. Retrying...")); ekosInterface->call(QDBus::AutoDetect, "connectDevices"); return false; } appendLogText(i18n("INDI devices connection timed out. Check INDI control panel for details.")); stop(); return false; } else return false; } break; case INDI_DISCONNECTING: { QDBusReply isINDIConnected = ekosInterface->call(QDBus::AutoDetect, "getINDIConnectionStatus"); if (isINDIConnected.value() == EkosManager::EKOS_STATUS_IDLE) { appendLogText(i18n("INDI devices disconnected.")); indiState = INDI_IDLE; return true; } } break; case INDI_PROPERTY_CHECK: { // Check if mount and dome support parking or not. QDBusReply boolReply = mountInterface->call(QDBus::AutoDetect, "canPark"); unparkMountCheck->setEnabled(boolReply.value()); parkMountCheck->setEnabled(boolReply.value()); //qDebug() << "Mount can park " << boolReply.value(); boolReply = domeInterface->call(QDBus::AutoDetect, "canPark"); unparkDomeCheck->setEnabled(boolReply.value()); parkDomeCheck->setEnabled(boolReply.value()); boolReply = captureInterface->call(QDBus::AutoDetect, "hasCoolerControl"); warmCCDCheck->setEnabled(boolReply.value()); QDBusReply updateReply = weatherInterface->call(QDBus::AutoDetect, "getUpdatePeriod"); if (updateReply.error().type() == QDBusError::NoError) { weatherCheck->setEnabled(true); if (updateReply.value() > 0) { weatherTimer.setInterval(updateReply.value() * 1000); connect(&weatherTimer, SIGNAL(timeout()), this, SLOT(checkWeather())); weatherTimer.start(); // Check weather initially checkWeather(); } } else weatherCheck->setEnabled(false); QDBusReply capReply = capInterface->call(QDBus::AutoDetect, "canPark"); if (capReply.error().type() == QDBusError::NoError) { capCheck->setEnabled(capReply.value()); uncapCheck->setEnabled(capReply.value()); } else { capCheck->setEnabled(false); uncapCheck->setEnabled(false); } indiState = INDI_READY; return true; } break; case INDI_READY: return true; } return false; } bool Scheduler::checkStartupState() { if (state == SCHEDULER_PAUSED) return false; qCDebug(KSTARS_EKOS_SCHEDULER) << "Checking Startup State..."; switch (startupState) { case STARTUP_IDLE: { KNotification::event(QLatin1String("ObservatoryStartup"), i18n("Observatory is in the startup process")); qCDebug(KSTARS_EKOS_SCHEDULER) << "Startup Idle. Starting startup process..."; // If Ekos is already started, we skip the script and move on to dome unpark step // unless we do not have light frames, then we skip all QDBusReply isEkosStarted; isEkosStarted = ekosInterface->call(QDBus::AutoDetect, "getEkosStartingStatus"); if (isEkosStarted.value() == EkosManager::EKOS_STATUS_SUCCESS) { if (startupScriptURL.isEmpty() == false) appendLogText(i18n("Ekos is already started, skipping startup script...")); if (currentJob->getLightFramesRequired()) startupState = STARTUP_UNPARK_DOME; else startupState = STARTUP_COMPLETE; return true; } if (schedulerProfileCombo->currentText() != i18n("Default")) { QList profile; profile.append(schedulerProfileCombo->currentText()); ekosInterface->callWithArgumentList(QDBus::AutoDetect, "setProfile", profile); } if (startupScriptURL.isEmpty() == false) { startupState = STARTUP_SCRIPT; executeScript(startupScriptURL.toString(QUrl::PreferLocalFile)); return false; } startupState = STARTUP_UNPARK_DOME; return false; } break; case STARTUP_SCRIPT: return false; break; case STARTUP_UNPARK_DOME: // If there is no job in case of manual startup procedure, // or if the job requires light frames, let's proceed with // unparking the dome, otherwise startup process is complete. if (currentJob == nullptr || currentJob->getLightFramesRequired()) { if (unparkDomeCheck->isEnabled() && unparkDomeCheck->isChecked()) unParkDome(); else startupState = STARTUP_UNPARK_MOUNT; } else { startupState = STARTUP_COMPLETE; return true; } break; case STARTUP_UNPARKING_DOME: checkDomeParkingStatus(); break; case STARTUP_UNPARK_MOUNT: if (unparkMountCheck->isEnabled() && unparkMountCheck->isChecked()) unParkMount(); else startupState = STARTUP_UNPARK_CAP; break; case STARTUP_UNPARKING_MOUNT: checkMountParkingStatus(); break; case STARTUP_UNPARK_CAP: if (uncapCheck->isEnabled() && uncapCheck->isChecked()) unParkCap(); else startupState = STARTUP_COMPLETE; break; case STARTUP_UNPARKING_CAP: checkCapParkingStatus(); break; case STARTUP_COMPLETE: return true; case STARTUP_ERROR: stop(); return true; break; } return false; } bool Scheduler::checkShutdownState() { if (state == SCHEDULER_PAUSED) return false; qCDebug(KSTARS_EKOS_SCHEDULER) << "Checking shutown state..."; switch (shutdownState) { case SHUTDOWN_IDLE: KNotification::event(QLatin1String("ObservatoryShutdown"), i18n("Observatory is in the shutdown process")); qCInfo(KSTARS_EKOS_SCHEDULER) << "Starting shutdown process..."; weatherTimer.stop(); weatherTimer.disconnect(); weatherLabel->hide(); jobTimer.stop(); setCurrentJob(nullptr); if (state == SCHEDULER_RUNNIG) schedulerTimer.start(); if (preemptiveShutdown == false) { sleepTimer.stop(); //sleepTimer.disconnect(); } if (warmCCDCheck->isEnabled() && warmCCDCheck->isChecked()) { appendLogText(i18n("Warming up CCD...")); // Turn it off QVariant arg(false); captureInterface->call(QDBus::AutoDetect, "setCoolerControl", arg); } if (capCheck->isEnabled() && capCheck->isChecked()) { shutdownState = SHUTDOWN_PARK_CAP; return false; } if (parkMountCheck->isEnabled() && parkMountCheck->isChecked()) { shutdownState = SHUTDOWN_PARK_MOUNT; return false; } if (parkDomeCheck->isEnabled() && parkDomeCheck->isChecked()) { shutdownState = SHUTDOWN_PARK_DOME; return false; } if (shutdownScriptURL.isEmpty() == false) { shutdownState = SHUTDOWN_SCRIPT; return false; } shutdownState = SHUTDOWN_COMPLETE; return true; break; case SHUTDOWN_PARK_CAP: if (capCheck->isEnabled() && capCheck->isChecked()) parkCap(); else shutdownState = SHUTDOWN_PARK_MOUNT; break; case SHUTDOWN_PARKING_CAP: checkCapParkingStatus(); break; case SHUTDOWN_PARK_MOUNT: if (parkMountCheck->isEnabled() && parkMountCheck->isChecked()) parkMount(); else shutdownState = SHUTDOWN_PARK_DOME; break; case SHUTDOWN_PARKING_MOUNT: checkMountParkingStatus(); break; case SHUTDOWN_PARK_DOME: if (parkDomeCheck->isEnabled() && parkDomeCheck->isChecked()) parkDome(); else shutdownState = SHUTDOWN_SCRIPT; break; case SHUTDOWN_PARKING_DOME: checkDomeParkingStatus(); break; case SHUTDOWN_SCRIPT: if (shutdownScriptURL.isEmpty() == false) { // Need to stop Ekos now before executing script if it happens to stop INDI if (ekosState != EKOS_IDLE && Options::shutdownScriptTerminatesINDI()) { stopEkos(); return false; } shutdownState = SHUTDOWN_SCRIPT_RUNNING; executeScript(shutdownScriptURL.toString(QUrl::PreferLocalFile)); } else shutdownState = SHUTDOWN_COMPLETE; break; case SHUTDOWN_SCRIPT_RUNNING: return false; case SHUTDOWN_COMPLETE: return true; case SHUTDOWN_ERROR: stop(); return true; break; } return false; } bool Scheduler::checkParkWaitState() { if (state == SCHEDULER_PAUSED) return false; qCDebug(KSTARS_EKOS_SCHEDULER) << "Checking Park Wait State..."; switch (parkWaitState) { case PARKWAIT_IDLE: return true; case PARKWAIT_PARK: parkMount(); break; case PARKWAIT_PARKING: checkMountParkingStatus(); break; case PARKWAIT_PARKED: return true; case PARKWAIT_UNPARK: unParkMount(); break; case PARKWAIT_UNPARKING: checkMountParkingStatus(); break; case PARKWAIT_UNPARKED: return true; case PARKWAIT_ERROR: appendLogText(i18n("park/unpark wait procedure failed, aborting...")); stop(); return true; break; } return false; } void Scheduler::executeScript(const QString &filename) { appendLogText(i18n("Executing script %1 ...", filename)); connect(&scriptProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(readProcessOutput())); connect(&scriptProcess, SIGNAL(finished(int)), this, SLOT(checkProcessExit(int))); scriptProcess.start(filename); } void Scheduler::readProcessOutput() { appendLogText(scriptProcess.readAllStandardOutput().simplified()); } void Scheduler::checkProcessExit(int exitCode) { scriptProcess.disconnect(); if (exitCode == 0) { if (startupState == STARTUP_SCRIPT) startupState = STARTUP_UNPARK_DOME; else if (shutdownState == SHUTDOWN_SCRIPT_RUNNING) shutdownState = SHUTDOWN_COMPLETE; return; } if (startupState == STARTUP_SCRIPT) { appendLogText(i18n("Startup script failed, aborting...")); startupState = STARTUP_ERROR; } else if (shutdownState == SHUTDOWN_SCRIPT_RUNNING) { appendLogText(i18n("Shutdown script failed, aborting...")); shutdownState = SHUTDOWN_ERROR; } } void Scheduler::checkStatus() { if (state == SCHEDULER_PAUSED) return; // #1 If no current job selected, let's check if we need to shutdown or evaluate jobs if (currentJob == nullptr) { // #2.1 If shutdown is already complete or in error, we need to stop if (shutdownState == SHUTDOWN_COMPLETE || shutdownState == SHUTDOWN_ERROR) { // If INDI is not done disconnecting, try again later if (indiState == INDI_DISCONNECTING && checkINDIState() == false) return; // Disconnect INDI if required first if (indiState != INDI_IDLE && Options::stopEkosAfterShutdown()) { disconnectINDI(); return; } // If Ekos is not done stopping, try again later if (ekosState == EKOS_STOPPING && checkEkosState() == false) return; // Stop Ekos if required. if (ekosState != EKOS_IDLE && Options::stopEkosAfterShutdown()) { stopEkos(); return; } if (shutdownState == SHUTDOWN_COMPLETE) appendLogText(i18n("Shutdown complete.")); else appendLogText(i18n("Shutdown procedure failed, aborting...")); // Stop Scheduler stop(); return; } // #2.2 Check if shutdown is in progress if (shutdownState > SHUTDOWN_IDLE) { // If Ekos is not done stopping, try again later if (ekosState == EKOS_STOPPING && checkEkosState() == false) return; checkShutdownState(); return; } // #2.3 Check if park wait procedure is in progress if (checkParkWaitState() == false) return; // #2.4 If not in shutdown state, evaluate the jobs evaluateJobs(); } else { // #3 Check if startup procedure has failed. if (startupState == STARTUP_ERROR) { // Stop Scheduler stop(); return; } // #4 Check if startup procedure Phase #1 is complete (Startup script) if ((startupState == STARTUP_IDLE && checkStartupState() == false) || startupState == STARTUP_SCRIPT) return; // #5 Check if Ekos is started if (checkEkosState() == false) return; // #6 Check if INDI devices are connected. if (checkINDIState() == false) return; + // #6.1 Check if park wait procedure is in progress - in the case we're waiting for a distant job + if (checkParkWaitState() == false) + return; + // #7 Check if startup procedure Phase #2 is complete (Unparking phase) if (startupState > STARTUP_SCRIPT && startupState < STARTUP_ERROR && checkStartupState() == false) return; // #8 Execute the job executeJob(currentJob); } } void Scheduler::checkJobStage() { if (state == SCHEDULER_PAUSED) return; Q_ASSERT_X(currentJob, __FUNCTION__, "Actual current job is required to check job stage"); if (!currentJob) return; + qCDebug(KSTARS_EKOS_SCHEDULER) << "Checking job stage for" << currentJob->getName() << "startup" << currentJob->getStartupCondition() << currentJob->getStartupTime().toString(currentJob->getDateTimeDisplayFormat()) << "state" << currentJob->getState(); + QDateTime const now = KStarsData::Instance()->lt(); /* Refresh the score of the current job */ /* currentJob->setScore(calculateJobScore(currentJob, now)); */ /* If current job is scheduled and has not started yet, wait */ if (SchedulerJob::JOB_SCHEDULED == currentJob->getState()) if (now < currentJob->getStartupTime()) return; // #1 Check if we need to stop at some point if (currentJob->getCompletionCondition() == SchedulerJob::FINISH_AT && currentJob->getState() == SchedulerJob::JOB_BUSY) { // If the job reached it COMPLETION time, we stop it. if (now.secsTo(currentJob->getCompletionTime()) <= 0) { appendLogText(i18n("Job '%1' reached completion time %2, stopping.", currentJob->getName(), currentJob->getCompletionTime().toString(currentJob->getDateTimeDisplayFormat()))); findNextJob(); return; } } // #2 Check if altitude restriction still holds true if (currentJob->getMinAltitude() > 0) { SkyPoint p = currentJob->getTargetCoords(); p.EquatorialToHorizontal(KStarsData::Instance()->lst(), geo->lat()); /* FIXME: find a way to use altitude cutoff here, because the job can be scheduled when evaluating, then aborted when running */ if (p.alt().Degrees() < currentJob->getMinAltitude()) { // Only terminate job due to altitude limitation if mount is NOT parked. if (isMountParked() == false) { appendLogText(i18n("Job '%1' current altitude (%2 degrees) crossed minimum constraint altitude (%3 degrees), " "marking aborted.", currentJob->getName(), p.alt().Degrees(), currentJob->getMinAltitude())); currentJob->setState(SchedulerJob::JOB_ABORTED); stopCurrentJobAction(); stopGuiding(); findNextJob(); return; } } } // #3 Check if moon separation is still valid if (currentJob->getMinMoonSeparation() > 0) { SkyPoint p = currentJob->getTargetCoords(); p.EquatorialToHorizontal(KStarsData::Instance()->lst(), geo->lat()); double moonSeparation = getCurrentMoonSeparation(currentJob); if (moonSeparation < currentJob->getMinMoonSeparation()) { // Only terminate job due to moon separation limitation if mount is NOT parked. if (isMountParked() == false) { appendLogText(i18n("Job '%2' current moon separation (%1 degrees) is lower than minimum constraint (%3 " "degrees), marking aborted.", moonSeparation, currentJob->getName(), currentJob->getMinMoonSeparation())); currentJob->setState(SchedulerJob::JOB_ABORTED); stopCurrentJobAction(); stopGuiding(); findNextJob(); return; } } } // #4 Check if we're not at dawn if (currentJob->getEnforceTwilight() && now > KStarsDateTime(preDawnDateTime)) { // If either mount or dome are not parked, we shutdown if we approach dawn if (isMountParked() == false || (parkDomeCheck->isEnabled() && isDomeParked() == false)) { // Minute is a DOUBLE value, do not use i18np appendLogText(i18n( "Job '%3' is now approaching astronomical twilight rise limit at %1 (%2 minutes safety margin), marking aborted.", preDawnDateTime.toString(), Options::preDawnTime(), currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); stopCurrentJobAction(); stopGuiding(); checkShutdownState(); //disconnect(KStars::Instance()->data()->clock(), SIGNAL(timeAdvanced()), this, SLOT(checkJobStage()), Qt::UniqueConnection); //connect(KStars::Instance()->data()->clock(), SIGNAL(timeAdvanced()), this, SLOT(checkStatus()), Qt::UniqueConnection); return; } } switch (currentJob->getStage()) { case SchedulerJob::STAGE_IDLE: getNextAction(); break; case SchedulerJob::STAGE_SLEWING: { QDBusReply slewStatus = mountInterface->call(QDBus::AutoDetect, "getSlewStatus"); bool isDomeMoving = false; if (parkDomeCheck->isEnabled()) { QDBusReply domeReply = domeInterface->call(QDBus::AutoDetect, "isMoving"); if (domeReply.error().type() == QDBusError::NoError && domeReply.value() == true) isDomeMoving = true; } if (slewStatus.error().type() == QDBusError::UnknownObject) { appendLogText(i18n("Warning! Job '%1' lost connection to INDI while slewing, marking aborted.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); checkShutdownState(); return; } qCDebug(KSTARS_EKOS_SCHEDULER) << "Slewing Stage... Slew Status is " << pstateStr(static_cast(slewStatus.value())); if (slewStatus.value() == IPS_OK && isDomeMoving == false) { appendLogText(i18n("Job '%1' slew is complete.", currentJob->getName())); currentJob->setStage(SchedulerJob::STAGE_SLEW_COMPLETE); getNextAction(); } else if (slewStatus.value() == IPS_ALERT) { appendLogText(i18n("Warning! Job '%1' slew failed, marking terminated due to errors.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ERROR); findNextJob(); } else if (slewStatus.value() == IPS_IDLE) { appendLogText(i18n("Warning! Job '%1' found not slewing, restarting.", currentJob->getName())); currentJob->setStage(SchedulerJob::STAGE_IDLE); getNextAction(); } } break; case SchedulerJob::STAGE_FOCUSING: { QDBusReply focusReply = focusInterface->call(QDBus::AutoDetect, "getStatus"); if (focusReply.error().type() == QDBusError::UnknownObject) { appendLogText(i18n("Warning! Job '%1' lost connection to INDI server while focusing, marking aborted.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); checkShutdownState(); return; } qCDebug(KSTARS_EKOS_SCHEDULER) << "Focus stage..."; Ekos::FocusState focusStatus = static_cast(focusReply.value()); // Is focus complete? if (focusStatus == Ekos::FOCUS_COMPLETE) { appendLogText(i18n("Job '%1' focusing is complete.", currentJob->getName())); autofocusCompleted = true; currentJob->setStage(SchedulerJob::STAGE_FOCUS_COMPLETE); getNextAction(); } else if (focusStatus == Ekos::FOCUS_FAILED || focusStatus == Ekos::FOCUS_ABORTED) { appendLogText(i18n("Warning! Job '%1' focusing failed.", currentJob->getName())); if (focusFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("Job '%1' is restarting its focusing procedure.", currentJob->getName())); // Reset frame to original size. focusInterface->call(QDBus::AutoDetect, "resetFrame"); // Restart focusing startFocusing(); } else { appendLogText(i18n("Warning! Job '%1' focusing procedure failed, marking terminated due to errors.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ERROR); findNextJob(); } } } break; /*case SchedulerJob::STAGE_POSTALIGN_FOCUSING: focusInterface->call(QDBus::AutoDetect,"resetFrame"); currentJob->setStage(SchedulerJob::STAGE_POSTALIGN_FOCUSING_COMPLETE); getNextAction(); break;*/ case SchedulerJob::STAGE_ALIGNING: { QDBusReply alignReply; qCDebug(KSTARS_EKOS_SCHEDULER) << "Alignment stage..."; alignReply = alignInterface->call(QDBus::AutoDetect, "getStatus"); if (alignReply.error().type() == QDBusError::UnknownObject) { appendLogText(i18n("Warning! Job '%1' lost connection to INDI server while aligning, marking aborted.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); checkShutdownState(); return; } Ekos::AlignState alignStatus = static_cast(alignReply.value()); // Is solver complete? if (alignStatus == Ekos::ALIGN_COMPLETE) { appendLogText(i18n("Job '%1' alignment is complete.", currentJob->getName())); alignFailureCount = 0; currentJob->setStage(SchedulerJob::STAGE_ALIGN_COMPLETE); getNextAction(); } else if (alignStatus == Ekos::ALIGN_FAILED || alignStatus == Ekos::ALIGN_ABORTED) { appendLogText(i18n("Warning! Job '%1' alignment failed.", currentJob->getName())); if (alignFailureCount++ < MAX_FAILURE_ATTEMPTS) { if (Options::resetMountModelOnAlignFail() && MAX_FAILURE_ATTEMPTS-1 < alignFailureCount) { appendLogText(i18n("Warning! Job '%1' forcing mount model reset after failing alignment #%2.", currentJob->getName(), alignFailureCount)); mountInterface->call(QDBus::AutoDetect, "resetModel"); } appendLogText(i18n("Restarting %1 alignment procedure...", currentJob->getName())); startAstrometry(); } else { appendLogText(i18n("Warning! Job '%1' alignment procedure failed, aborting job.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); findNextJob(); } } } break; case SchedulerJob::STAGE_RESLEWING: { QDBusReply slewStatus = mountInterface->call(QDBus::AutoDetect, "getSlewStatus"); bool isDomeMoving = false; qCDebug(KSTARS_EKOS_SCHEDULER) << "Re-slewing stage..."; if (parkDomeCheck->isEnabled()) { QDBusReply domeReply = domeInterface->call(QDBus::AutoDetect, "isMoving"); if (domeReply.error().type() == QDBusError::NoError && domeReply.value() == true) isDomeMoving = true; } if (slewStatus.error().type() == QDBusError::UnknownObject) { appendLogText(i18n("Warning! Job '%1' lost connection to INDI server while reslewing, marking aborted.",currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); checkShutdownState(); } else if (slewStatus.value() == IPS_OK && isDomeMoving == false) { appendLogText(i18n("Job '%1' repositioning is complete.", currentJob->getName())); currentJob->setStage(SchedulerJob::STAGE_RESLEWING_COMPLETE); getNextAction(); } else if (slewStatus.value() == IPS_ALERT) { appendLogText(i18n("Warning! Job '%1' repositioning failed, marking terminated due to errors.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ERROR); findNextJob(); } else if (slewStatus.value() == IPS_IDLE) { appendLogText(i18n("Warning! Job '%1' found not repositioning, restarting.", currentJob->getName())); currentJob->setStage(SchedulerJob::STAGE_IDLE); getNextAction(); } } break; case SchedulerJob::STAGE_GUIDING: { QDBusReply guideReply = guideInterface->call(QDBus::AutoDetect, "getStatus"); qCDebug(KSTARS_EKOS_SCHEDULER) << "Calibration & Guide stage..."; if (guideReply.error().type() == QDBusError::UnknownObject) { appendLogText(i18n("Warning! Job '%1' lost connection to INDI server while guiding, marking aborted.",currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); checkShutdownState(); return; } Ekos::GuideState guideStatus = static_cast(guideReply.value()); // If calibration stage complete? if (guideStatus == Ekos::GUIDE_GUIDING) { appendLogText(i18n("Job '%1' guiding is in progress.", currentJob->getName())); guideFailureCount = 0; currentJob->setStage(SchedulerJob::STAGE_GUIDING_COMPLETE); getNextAction(); } else if (guideStatus == Ekos::GUIDE_CALIBRATION_ERROR || guideStatus == Ekos::GUIDE_ABORTED) { if (guideStatus == Ekos::GUIDE_ABORTED) appendLogText(i18n("Warning! Job '%1' guiding failed.", currentJob->getName())); else appendLogText(i18n("Warning! Job '%1' calibration failed.", currentJob->getName())); if (guideFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("Job '%1' is guiding, and is restarting its guiding procedure.", currentJob->getName())); startGuiding(true); } else { appendLogText(i18n("Warning! Job '%1' guiding procedure failed, marking terminated due to errors.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ERROR); findNextJob(); } } } break; case SchedulerJob::STAGE_CAPTURING: { QDBusReply captureReply = captureInterface->call(QDBus::AutoDetect, "getSequenceQueueStatus"); if (captureReply.error().type() == QDBusError::UnknownObject) { appendLogText(i18n("Warning! Job '%1' lost connection to INDI server while capturing, marking aborted.",currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); checkShutdownState(); } else if (captureReply.value().toStdString() == "Aborted" || captureReply.value().toStdString() == "Error") { appendLogText(i18n("Warning! Job '%1' failed to capture target (%2).", currentJob->getName(), captureReply.value())); if (captureFailureCount++ < MAX_FAILURE_ATTEMPTS) { // If capture failed due to guiding error, let's try to restart that if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) { // Check if it is guiding related. QDBusReply guideReply = guideInterface->call(QDBus::AutoDetect, "getStatus"); if (guideReply.value() == Ekos::GUIDE_ABORTED || guideReply.value() == Ekos::GUIDE_CALIBRATION_ERROR || guideReply.value() == GUIDE_DITHERING_ERROR) // If guiding failed, let's restart it //if(guideReply.value() == false) { appendLogText(i18n("Job '%1' is capturing, and is restarting its guiding procedure.", currentJob->getName())); //currentJob->setStage(SchedulerJob::STAGE_GUIDING); startGuiding(true); return; } } /* FIXME: it's not clear whether it is actually possible to continue capturing when capture fails this way */ appendLogText(i18n("Warning! Job '%1' failed its capture procedure, restarting capture.", currentJob->getName())); startCapture(); } else { /* FIXME: it's not clear whether this situation can be recovered at all */ appendLogText(i18n("Warning! Job '%1' failed its capture procedure, marking aborted.", currentJob->getName())); currentJob->setState(SchedulerJob::JOB_ABORTED); findNextJob(); } } else if (captureReply.value().toStdString() == "Complete") { KNotification::event(QLatin1String("EkosScheduledImagingFinished"), i18n("Ekos job (%1) - Capture finished", currentJob->getName())); - /* Set evaluation state so that job is reevaluated for repeats or other things */ - currentJob->setState(SchedulerJob::JOB_EVALUATION); captureInterface->call(QDBus::AutoDetect, "clearSequenceQueue"); + currentJob->setState(SchedulerJob::JOB_COMPLETE); findNextJob(); } else { captureFailureCount = 0; /* currentJob->setCompletedCount(currentJob->getCompletedCount() + 1); */ } } break; default: break; } } void Scheduler::getNextAction() { qCDebug(KSTARS_EKOS_SCHEDULER) << "Get next action..."; switch (currentJob->getStage()) { case SchedulerJob::STAGE_IDLE: if (currentJob->getLightFramesRequired()) { if (currentJob->getStepPipeline() & SchedulerJob::USE_TRACK) startSlew(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_FOCUS && autofocusCompleted == false) startFocusing(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_ALIGN) startAstrometry(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) startGuiding(); else startCapture(); } else { if (currentJob->getStepPipeline()) appendLogText( i18n("Job '%1' is proceeding directly to capture stage because only calibration frames are pending.", currentJob->getName())); startCapture(); } break; case SchedulerJob::STAGE_SLEW_COMPLETE: if (currentJob->getStepPipeline() & SchedulerJob::USE_FOCUS && autofocusCompleted == false) startFocusing(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_ALIGN) startAstrometry(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) startGuiding(); else startCapture(); break; case SchedulerJob::STAGE_FOCUS_COMPLETE: if (currentJob->getStepPipeline() & SchedulerJob::USE_ALIGN) startAstrometry(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) startGuiding(); else startCapture(); break; case SchedulerJob::STAGE_ALIGN_COMPLETE: currentJob->setStage(SchedulerJob::STAGE_RESLEWING); break; case SchedulerJob::STAGE_RESLEWING_COMPLETE: // If we have in-sequence-focus in the sequence file then we perform post alignment focusing so that the focus // frame is ready for the capture module in-sequence-focus procedure. if ((currentJob->getStepPipeline() & SchedulerJob::USE_FOCUS) && currentJob->getInSequenceFocus()) // Post alignment re-focusing startFocusing(); else if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) startGuiding(); else startCapture(); break; case SchedulerJob::STAGE_POSTALIGN_FOCUSING_COMPLETE: if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) startGuiding(); else startCapture(); break; case SchedulerJob::STAGE_GUIDING_COMPLETE: startCapture(); break; default: break; } } void Scheduler::stopCurrentJobAction() { if (currentJob) { qCDebug(KSTARS_EKOS_SCHEDULER) << "Job '" << currentJob->getName() << "' is stopping current action..." << currentJob->getStage(); switch (currentJob->getStage()) { case SchedulerJob::STAGE_IDLE: break; case SchedulerJob::STAGE_SLEWING: mountInterface->call(QDBus::AutoDetect, "abort"); break; case SchedulerJob::STAGE_FOCUSING: focusInterface->call(QDBus::AutoDetect, "abort"); break; case SchedulerJob::STAGE_ALIGNING: alignInterface->call(QDBus::AutoDetect, "abort"); break; //case SchedulerJob::STAGE_CALIBRATING: // guideInterface->call(QDBus::AutoDetect,"stopCalibration"); // break; case SchedulerJob::STAGE_GUIDING: stopGuiding(); break; case SchedulerJob::STAGE_CAPTURING: captureInterface->call(QDBus::AutoDetect, "abort"); //stopGuiding(); break; default: break; } /* Reset interrupted job stage */ currentJob->setStage(SchedulerJob::STAGE_IDLE); } } void Scheduler::load() { QUrl fileURL = QFileDialog::getOpenFileUrl(this, i18n("Open Ekos Scheduler List"), dirPath, "Ekos Scheduler List (*.esl)"); if (fileURL.isEmpty()) return; if (fileURL.isValid() == false) { QString message = i18n("Invalid URL: %1", fileURL.toLocalFile()); KMessageBox::sorry(0, message, i18n("Invalid URL")); return; } dirPath = QUrl(fileURL.url(QUrl::RemoveFilename)); /* Run a job idle evaluation after a successful load */ if (loadScheduler(fileURL.toLocalFile())) startJobEvaluation(); } bool Scheduler::loadScheduler(const QString &fileURL) { QFile sFile; sFile.setFileName(fileURL); if (!sFile.open(QIODevice::ReadOnly)) { QString message = i18n("Unable to open file %1", fileURL); KMessageBox::sorry(0, message, i18n("Could Not Open File")); return false; } if (jobUnderEdit >= 0) resetJobEdit(); qDeleteAll(jobs); jobs.clear(); while (queueTable->rowCount() > 0) queueTable->removeRow(0); LilXML *xmlParser = newLilXML(); char errmsg[MAXRBUF]; XMLEle *root = nullptr; XMLEle *ep = nullptr; char c; while (sFile.getChar(&c)) { root = readXMLEle(xmlParser, c, errmsg); if (root) { for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) { const char *tag = tagXMLEle(ep); if (!strcmp(tag, "Job")) processJobInfo(ep); else if (!strcmp(tag, "Profile")) { schedulerProfileCombo->setCurrentText(pcdataXMLEle(ep)); } else if (!strcmp(tag, "StartupProcedure")) { XMLEle *procedure; startupScript->clear(); unparkDomeCheck->setChecked(false); unparkMountCheck->setChecked(false); uncapCheck->setChecked(false); for (procedure = nextXMLEle(ep, 1); procedure != nullptr; procedure = nextXMLEle(ep, 0)) { const char *proc = pcdataXMLEle(procedure); if (!strcmp(proc, "StartupScript")) { startupScript->setText(findXMLAttValu(procedure, "value")); startupScriptURL = QUrl::fromUserInput(startupScript->text()); } else if (!strcmp(proc, "UnparkDome")) unparkDomeCheck->setChecked(true); else if (!strcmp(proc, "UnparkMount")) unparkMountCheck->setChecked(true); else if (!strcmp(proc, "UnparkCap")) uncapCheck->setChecked(true); } } else if (!strcmp(tag, "ShutdownProcedure")) { XMLEle *procedure; shutdownScript->clear(); warmCCDCheck->setChecked(false); parkDomeCheck->setChecked(false); parkMountCheck->setChecked(false); capCheck->setChecked(false); for (procedure = nextXMLEle(ep, 1); procedure != nullptr; procedure = nextXMLEle(ep, 0)) { const char *proc = pcdataXMLEle(procedure); if (!strcmp(proc, "ShutdownScript")) { shutdownScript->setText(findXMLAttValu(procedure, "value")); shutdownScriptURL = QUrl::fromUserInput(shutdownScript->text()); } else if (!strcmp(proc, "ParkDome")) parkDomeCheck->setChecked(true); else if (!strcmp(proc, "ParkMount")) parkMountCheck->setChecked(true); else if (!strcmp(proc, "ParkCap")) capCheck->setChecked(true); else if (!strcmp(proc, "WarmCCD")) warmCCDCheck->setChecked(true); } } } delXMLEle(root); } else if (errmsg[0]) { appendLogText(QString(errmsg)); delLilXML(xmlParser); return false; } } schedulerURL = QUrl::fromLocalFile(fileURL); mosaicB->setEnabled(true); mDirty = false; delLilXML(xmlParser); return true; } bool Scheduler::processJobInfo(XMLEle *root) { XMLEle *ep; XMLEle *subEP; altConstraintCheck->setChecked(false); moonSeparationCheck->setChecked(false); weatherCheck->setChecked(false); twilightCheck->blockSignals(true); twilightCheck->setChecked(false); twilightCheck->blockSignals(false); minAltitude->setValue(minAltitude->minimum()); minMoonSeparation->setValue(minMoonSeparation->minimum()); for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) { if (!strcmp(tagXMLEle(ep), "Name")) nameEdit->setText(pcdataXMLEle(ep)); else if (!strcmp(tagXMLEle(ep), "Priority")) prioritySpin->setValue(atoi(pcdataXMLEle(ep))); else if (!strcmp(tagXMLEle(ep), "Coordinates")) { subEP = findXMLEle(ep, "J2000RA"); if (subEP) raBox->setDMS(pcdataXMLEle(subEP)); subEP = findXMLEle(ep, "J2000DE"); if (subEP) decBox->setDMS(pcdataXMLEle(subEP)); } else if (!strcmp(tagXMLEle(ep), "Sequence")) { sequenceEdit->setText(pcdataXMLEle(ep)); sequenceURL = QUrl::fromUserInput(sequenceEdit->text()); } else if (!strcmp(tagXMLEle(ep), "FITS")) { fitsEdit->setText(pcdataXMLEle(ep)); fitsURL.setPath(fitsEdit->text()); } else if (!strcmp(tagXMLEle(ep), "StartupCondition")) { for (subEP = nextXMLEle(ep, 1); subEP != nullptr; subEP = nextXMLEle(ep, 0)) { if (!strcmp("ASAP", pcdataXMLEle(subEP))) asapConditionR->setChecked(true); else if (!strcmp("Culmination", pcdataXMLEle(subEP))) { culminationConditionR->setChecked(true); culminationOffset->setValue(atof(findXMLAttValu(subEP, "value"))); } else if (!strcmp("At", pcdataXMLEle(subEP))) { startupTimeConditionR->setChecked(true); startupTimeEdit->setDateTime(QDateTime::fromString(findXMLAttValu(subEP, "value"), Qt::ISODate)); } } } else if (!strcmp(tagXMLEle(ep), "Constraints")) { for (subEP = nextXMLEle(ep, 1); subEP != nullptr; subEP = nextXMLEle(ep, 0)) { if (!strcmp("MinimumAltitude", pcdataXMLEle(subEP))) { altConstraintCheck->setChecked(true); minAltitude->setValue(atof(findXMLAttValu(subEP, "value"))); } else if (!strcmp("MoonSeparation", pcdataXMLEle(subEP))) { moonSeparationCheck->setChecked(true); minMoonSeparation->setValue(atof(findXMLAttValu(subEP, "value"))); } else if (!strcmp("EnforceWeather", pcdataXMLEle(subEP))) weatherCheck->setChecked(true); else if (!strcmp("EnforceTwilight", pcdataXMLEle(subEP))) twilightCheck->setChecked(true); } } else if (!strcmp(tagXMLEle(ep), "CompletionCondition")) { for (subEP = nextXMLEle(ep, 1); subEP != nullptr; subEP = nextXMLEle(ep, 0)) { if (!strcmp("Sequence", pcdataXMLEle(subEP))) sequenceCompletionR->setChecked(true); else if (!strcmp("Repeat", pcdataXMLEle(subEP))) { repeatCompletionR->setChecked(true); repeatsSpin->setValue(atoi(findXMLAttValu(subEP, "value"))); } else if (!strcmp("Loop", pcdataXMLEle(subEP))) loopCompletionR->setChecked(true); else if (!strcmp("At", pcdataXMLEle(subEP))) { timeCompletionR->setChecked(true); completionTimeEdit->setDateTime(QDateTime::fromString(findXMLAttValu(subEP, "value"), Qt::ISODate)); } } } else if (!strcmp(tagXMLEle(ep), "Steps")) { XMLEle *module; trackStepCheck->setChecked(false); focusStepCheck->setChecked(false); alignStepCheck->setChecked(false); guideStepCheck->setChecked(false); for (module = nextXMLEle(ep, 1); module != nullptr; module = nextXMLEle(ep, 0)) { const char *proc = pcdataXMLEle(module); if (!strcmp(proc, "Track")) trackStepCheck->setChecked(true); else if (!strcmp(proc, "Focus")) focusStepCheck->setChecked(true); else if (!strcmp(proc, "Align")) alignStepCheck->setChecked(true); else if (!strcmp(proc, "Guide")) guideStepCheck->setChecked(true); } } } addToQueueB->setEnabled(true); saveJob(); return true; } void Scheduler::saveAs() { schedulerURL.clear(); save(); } void Scheduler::save() { QUrl backupCurrent = schedulerURL; if (schedulerURL.toLocalFile().startsWith(QLatin1String("/tmp/")) || schedulerURL.toLocalFile().contains("/Temp")) schedulerURL.clear(); // If no changes made, return. if (mDirty == false && !schedulerURL.isEmpty()) return; if (schedulerURL.isEmpty()) { schedulerURL = QFileDialog::getSaveFileUrl(this, i18n("Save Ekos Scheduler List"), dirPath, "Ekos Scheduler List (*.esl)"); // if user presses cancel if (schedulerURL.isEmpty()) { schedulerURL = backupCurrent; return; } dirPath = QUrl(schedulerURL.url(QUrl::RemoveFilename)); if (schedulerURL.toLocalFile().contains('.') == 0) schedulerURL.setPath(schedulerURL.toLocalFile() + ".esl"); } if (schedulerURL.isValid()) { if ((saveScheduler(schedulerURL)) == false) { KMessageBox::error(KStars::Instance(), i18n("Failed to save scheduler list"), i18n("Save")); return; } mDirty = false; } else { QString message = i18n("Invalid URL: %1", schedulerURL.url()); KMessageBox::sorry(KStars::Instance(), message, i18n("Invalid URL")); } } bool Scheduler::saveScheduler(const QUrl &fileURL) { QFile file; file.setFileName(fileURL.toLocalFile()); if (!file.open(QIODevice::WriteOnly)) { QString message = i18n("Unable to write to file %1", fileURL.toLocalFile()); KMessageBox::sorry(0, message, i18n("Could Not Open File")); return false; } QTextStream outstream(&file); outstream << "" << endl; outstream << "" << endl; outstream << "" << schedulerProfileCombo->currentText() << "" << endl; foreach (SchedulerJob *job, jobs) { outstream << "" << endl; outstream << "" << job->getName() << "" << endl; outstream << "" << job->getPriority() << "" << endl; outstream << "" << endl; outstream << "" << job->getTargetCoords().ra0().Hours() << "" << endl; outstream << "" << job->getTargetCoords().dec0().Degrees() << "" << endl; outstream << "" << endl; if (job->getFITSFile().isValid() && job->getFITSFile().isEmpty() == false) outstream << "" << job->getFITSFile().toLocalFile() << "" << endl; outstream << "" << job->getSequenceFile().toLocalFile() << "" << endl; outstream << "" << endl; if (job->getFileStartupCondition() == SchedulerJob::START_ASAP) outstream << "ASAP" << endl; else if (job->getFileStartupCondition() == SchedulerJob::START_CULMINATION) outstream << "Culmination" << endl; else if (job->getFileStartupCondition() == SchedulerJob::START_AT) outstream << "At" << endl; outstream << "" << endl; outstream << "" << endl; if (job->getMinAltitude() > 0) outstream << "MinimumAltitude" << endl; if (job->getMinMoonSeparation() > 0) outstream << "MoonSeparation" << endl; if (job->getEnforceWeather()) outstream << "EnforceWeather" << endl; if (job->getEnforceTwilight()) outstream << "EnforceTwilight" << endl; outstream << "" << endl; outstream << "" << endl; if (job->getCompletionCondition() == SchedulerJob::FINISH_SEQUENCE) outstream << "Sequence" << endl; else if (job->getCompletionCondition() == SchedulerJob::FINISH_REPEAT) outstream << "Repeat" << endl; else if (job->getCompletionCondition() == SchedulerJob::FINISH_LOOP) outstream << "Loop" << endl; else if (job->getCompletionCondition() == SchedulerJob::FINISH_AT) outstream << "At" << endl; outstream << "" << endl; outstream << "" << endl; if (job->getStepPipeline() & SchedulerJob::USE_TRACK) outstream << "Track" << endl; if (job->getStepPipeline() & SchedulerJob::USE_FOCUS) outstream << "Focus" << endl; if (job->getStepPipeline() & SchedulerJob::USE_ALIGN) outstream << "Align" << endl; if (job->getStepPipeline() & SchedulerJob::USE_GUIDE) outstream << "Guide" << endl; outstream << "" << endl; outstream << "" << endl; } outstream << "" << endl; if (startupScript->text().isEmpty() == false) outstream << "StartupScript" << endl; if (unparkDomeCheck->isChecked()) outstream << "UnparkDome" << endl; if (unparkMountCheck->isChecked()) outstream << "UnparkMount" << endl; if (uncapCheck->isChecked()) outstream << "UnparkCap" << endl; outstream << "" << endl; outstream << "" << endl; if (warmCCDCheck->isChecked()) outstream << "WarmCCD" << endl; if (capCheck->isChecked()) outstream << "ParkCap" << endl; if (parkMountCheck->isChecked()) outstream << "ParkMount" << endl; if (parkDomeCheck->isChecked()) outstream << "ParkDome" << endl; if (shutdownScript->text().isEmpty() == false) outstream << "ShutdownScript" << endl; outstream << "" << endl; outstream << "" << endl; appendLogText(i18n("Scheduler list saved to %1", fileURL.toLocalFile())); file.close(); return true; } void Scheduler::startSlew() { Q_ASSERT(currentJob != nullptr); if (isMountParked()) { appendLogText(i18n("Warning! Job '%1' found mount parked unexpectedly, attempting to unpark.", currentJob->getName())); startupState = STARTUP_UNPARK_MOUNT; unParkMount(); return; } if (Options::resetMountModelBeforeJob()) mountInterface->call(QDBus::AutoDetect, "resetModel"); SkyPoint target = currentJob->getTargetCoords(); //target.EquatorialToHorizontal(KStarsData::Instance()->lst(), geo->lat()); QList telescopeSlew; telescopeSlew.append(target.ra().Hours()); telescopeSlew.append(target.dec().Degrees()); appendLogText(i18n("Job '%1' is slewing to target.", currentJob->getName())); QDBusReply const slewModeReply = mountInterface->callWithArgumentList(QDBus::AutoDetect, "slew", telescopeSlew); if (slewModeReply.error().type() != QDBusError::NoError) { /* FIXME: manage error */ appendLogText(i18n("Warning! Job '%1' slew request received DBUS error: %2", currentJob->getName(), QDBusError::errorString(slewModeReply.error().type()))); return; } currentJob->setStage(SchedulerJob::STAGE_SLEWING); } void Scheduler::startFocusing() { // 2017-09-30 Jasem: We're skipping post align focusing now as it can be performed // when first focus request is made in capture module if (currentJob->getStage() == SchedulerJob::STAGE_RESLEWING_COMPLETE || currentJob->getStage() == SchedulerJob::STAGE_POSTALIGN_FOCUSING) { // Clear the HFR limit value set in the capture module captureInterface->call(QDBus::AutoDetect, "clearAutoFocusHFR"); // Reset Focus frame so that next frame take a full-resolution capture first. focusInterface->call(QDBus::AutoDetect,"resetFrame"); currentJob->setStage(SchedulerJob::STAGE_POSTALIGN_FOCUSING_COMPLETE); getNextAction(); return; } // Check if autofocus is supported QDBusReply focusModeReply; focusModeReply = focusInterface->call(QDBus::AutoDetect, "canAutoFocus"); if (focusModeReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Job '%1' canAutoFocus request received DBUS error: %2", currentJob->getName(), QDBusError::errorString(focusModeReply.error().type()))); return; } if (focusModeReply.value() == false) { appendLogText(i18n("Warning! Job '%1' is unable to proceed with autofocus, not supported.", currentJob->getName())); currentJob->setStepPipeline( static_cast(currentJob->getStepPipeline() & ~SchedulerJob::USE_FOCUS)); currentJob->setStage(SchedulerJob::STAGE_FOCUS_COMPLETE); getNextAction(); return; } // Clear the HFR limit value set in the capture module captureInterface->call(QDBus::AutoDetect, "clearAutoFocusHFR"); QDBusMessage reply; // We always need to reset frame first if ((reply = focusInterface->call(QDBus::AutoDetect, "resetFrame")).type() == QDBusMessage::ErrorMessage) { appendLogText(i18n("Warning! Job '%1' resetFrame request received DBUS error: %2", currentJob->getName(), reply.errorMessage())); return; } // Set autostar if full field option is false if (Options::focusUseFullField() == false) { QList autoStar; autoStar.append(true); if ((reply = focusInterface->callWithArgumentList(QDBus::AutoDetect, "setAutoStarEnabled", autoStar)).type() == QDBusMessage::ErrorMessage) { appendLogText(i18n("Warning! Job '%1' setAutoFocusStar request received DBUS error: %1", currentJob->getName(), reply.errorMessage())); return; } } // Start auto-focus if ((reply = focusInterface->call(QDBus::AutoDetect, "start")).type() == QDBusMessage::ErrorMessage) { appendLogText(i18n("Warning! Job '%1' startFocus request received DBUS error: %2", currentJob->getName(), reply.errorMessage())); return; } /*if (currentJob->getStage() == SchedulerJob::STAGE_RESLEWING_COMPLETE || currentJob->getStage() == SchedulerJob::STAGE_POSTALIGN_FOCUSING) { currentJob->setStage(SchedulerJob::STAGE_POSTALIGN_FOCUSING); appendLogText(i18n("Post-alignment focusing for %1 ...", currentJob->getName())); } else { currentJob->setStage(SchedulerJob::STAGE_FOCUSING); appendLogText(i18n("Focusing %1 ...", currentJob->getName())); }*/ currentJob->setStage(SchedulerJob::STAGE_FOCUSING); appendLogText(i18n("Job '%1' is focusing.", currentJob->getName())); } void Scheduler::findNextJob() { + Q_ASSERT_X(currentJob->getState() == SchedulerJob::JOB_ERROR || + currentJob->getState() == SchedulerJob::JOB_ABORTED || + currentJob->getState() == SchedulerJob::JOB_COMPLETE, + __FUNCTION__, "Finding next job requires current to be in error, aborted or complete"); + jobTimer.stop(); /* FIXME: Other debug logs in that function probably */ qCDebug(KSTARS_EKOS_SCHEDULER) << "Find next job..."; if (currentJob->getState() == SchedulerJob::JOB_ERROR) { captureBatch = 0; // Stop Guiding if it was used stopGuiding(); appendLogText(i18n("Job '%1' is terminated due to errors.", currentJob->getName())); setCurrentJob(nullptr); schedulerTimer.start(); } else if (currentJob->getState() == SchedulerJob::JOB_ABORTED) { // Stop Guiding if it was used stopGuiding(); appendLogText(i18n("Job '%1' is aborted.", currentJob->getName())); setCurrentJob(nullptr); schedulerTimer.start(); } - // Check completion criteria - // We're done whether the job completed successfully or not. + // Job is complete, so check completion criteria to optimize processing + // In any case, we're done whether the job completed successfully or not. else if (currentJob->getCompletionCondition() == SchedulerJob::FINISH_SEQUENCE) { /* Mark the job idle as well as all its duplicates for re-evaluation */ foreach(SchedulerJob *a_job, jobs) if (a_job == currentJob || a_job->isDuplicateOf(currentJob)) a_job->setState(SchedulerJob::JOB_IDLE); + captureBatch = 0; // Stop Guiding if it was used stopGuiding(); appendLogText(i18n("Job '%1' is complete.", currentJob->getName())); setCurrentJob(nullptr); schedulerTimer.start(); } else if (currentJob->getCompletionCondition() == SchedulerJob::FINISH_REPEAT) { - currentJob->setRepeatsRemaining(currentJob->getRepeatsRemaining() - 1); + if (0 < currentJob->getRepeatsRemaining()) + currentJob->setRepeatsRemaining(currentJob->getRepeatsRemaining() - 1); - // If we're done + /* Mark the job idle as well as all its duplicates for re-evaluation */ + foreach(SchedulerJob *a_job, jobs) + if (a_job == currentJob || a_job->isDuplicateOf(currentJob)) + a_job->setState(SchedulerJob::JOB_IDLE); + + /* Re-evaluate all jobs, without selecting a new job */ + jobEvaluationOnly = true; + evaluateJobs(); + + /* If current job is actually complete because of previous duplicates, prepare for next job */ if (currentJob->getRepeatsRemaining() == 0) { - /* Mark the job idle as well as all its duplicates for re-evaluation */ - foreach(SchedulerJob *a_job, jobs) - if (a_job == currentJob || a_job->isDuplicateOf(currentJob)) - a_job->setState(SchedulerJob::JOB_IDLE); stopCurrentJobAction(); stopGuiding(); appendLogText(i18np("Job '%1' is complete after #%2 batch.", "Job '%1' is complete after #%2 batches.", currentJob->getName(), currentJob->getRepeatsRequired())); setCurrentJob(nullptr); schedulerTimer.start(); } + /* If job requires more work, continue current observation */ else { /* FIXME: raise priority to allow other jobs to schedule in-between */ + executeJob(currentJob); /* If we are guiding, continue capturing */ if (currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) { currentJob->setStage(SchedulerJob::STAGE_CAPTURING); startCapture(); } - /* If we are using alignment, realign */ + /* If we are not guiding, but using alignment, realign */ else if (currentJob->getStepPipeline() & SchedulerJob::USE_ALIGN) { currentJob->setStage(SchedulerJob::STAGE_ALIGNING); startAstrometry(); } /* Else just slew back to target - no-op probably, but having only 'track' checked is an edge case */ else { currentJob->setStage(SchedulerJob::STAGE_SLEWING); startSlew(); } appendLogText(i18np("Job '%1' is repeating, #%2 batch remaining.", "Job '%1' is repeating, #%2 batches remaining.", currentJob->getName(), currentJob->getRepeatsRemaining())); /* currentJob remains the same */ jobTimer.start(); } } else if (currentJob->getCompletionCondition() == SchedulerJob::FINISH_LOOP) { + executeJob(currentJob); + currentJob->setStage(SchedulerJob::STAGE_CAPTURING); captureBatch++; startCapture(); appendLogText(i18n("Job '%1' is repeating, looping indefinitely.", currentJob->getName())); /* currentJob remains the same */ jobTimer.start(); } else if (currentJob->getCompletionCondition() == SchedulerJob::FINISH_AT) { if (KStarsData::Instance()->lt().secsTo(currentJob->getCompletionTime()) <= 0) { /* Mark the job idle as well as all its duplicates for re-evaluation */ foreach(SchedulerJob *a_job, jobs) if (a_job == currentJob || a_job->isDuplicateOf(currentJob)) a_job->setState(SchedulerJob::JOB_IDLE); stopCurrentJobAction(); stopGuiding(); captureBatch = 0; appendLogText(i18np("Job '%1' stopping, reached completion time with #%2 batch done.", "Job '%1' stopping, reached completion time with #%2 batches done.", currentJob->getName(), captureBatch + 1)); setCurrentJob(nullptr); schedulerTimer.start(); } else { + executeJob(currentJob); + currentJob->setStage(SchedulerJob::STAGE_CAPTURING); captureBatch++; startCapture(); appendLogText(i18np("Job '%1' completed #%2 batch before completion time, restarted.", "Job '%1' completed #%2 batches before completion time, restarted.", currentJob->getName(), captureBatch)); /* currentJob remains the same */ jobTimer.start(); } } else { /* Unexpected situation, mitigate by resetting the job and restarting the scheduler timer */ - appendLogText(i18n("BUGBUG: Job '%1' timer elapsed, but no action to be taken.", currentJob->getName())); + qCDebug(KSTARS_EKOS_SCHEDULER) << "BUGBUG! Job '" << currentJob->getName() << "' timer elapsed, but no action to be taken."; setCurrentJob(nullptr); schedulerTimer.start(); } } void Scheduler::startAstrometry() { QDBusMessage reply; setSolverAction(Align::GOTO_SLEW); // Always turn update coords on QVariant arg(true); alignInterface->call(QDBus::AutoDetect, "setUpdateCoords", arg); // If FITS file is specified, then we use load and slew if (currentJob->getFITSFile().isEmpty() == false) { QList solveArgs; solveArgs.append(currentJob->getFITSFile().toString(QUrl::PreferLocalFile)); if ((reply = alignInterface->callWithArgumentList(QDBus::AutoDetect, "loadAndSlew", solveArgs)).type() == QDBusMessage::ErrorMessage) { appendLogText(i18n("Warning! Job '%1' loadAndSlew request received DBUS error: %2", currentJob->getName(), reply.errorMessage())); return; } loadAndSlewProgress = true; appendLogText(i18n("Job '%1' is plate solving capture %2.", currentJob->getName(), currentJob->getFITSFile().fileName())); } else { if ((reply = alignInterface->call(QDBus::AutoDetect, "captureAndSolve")).type() == QDBusMessage::ErrorMessage) { appendLogText(i18n("Warning! Job '%1' captureAndSolve request received DBUS error: %2", currentJob->getName(), reply.errorMessage())); return; } appendLogText(i18n("Job '%1' is capturing and plate solving.", currentJob->getName())); } /* FIXME: not supposed to modify the job */ currentJob->setStage(SchedulerJob::STAGE_ALIGNING); } void Scheduler::startGuiding(bool resetCalibration) { // Make sure calibration is auto //QVariant arg(true); //guideInterface->call(QDBus::AutoDetect,"setCalibrationAutoStar", arg); if (resetCalibration) guideInterface->call(QDBus::AutoDetect, "clearCalibration"); //QDBusReply guideReply = guideInterface->call(QDBus::AutoDetect,"startAutoCalibrateGuide"); guideInterface->call(QDBus::AutoDetect, "startAutoCalibrateGuide"); /*if (guideReply.value() == false) { appendLogText(i18n("Starting guide calibration failed. If using external guide application, ensure it is up and running.")); currentJob->setState(SchedulerJob::JOB_ERROR); } else {*/ currentJob->setStage(SchedulerJob::STAGE_GUIDING); appendLogText(i18n("Starting guiding procedure for %1 ...", currentJob->getName())); //} } void Scheduler::startCapture() { captureInterface->call(QDBus::AutoDetect, "clearSequenceQueue"); QString targetName = currentJob->getName().replace(' ', ""); QList targetArgs; targetArgs.append(targetName); captureInterface->callWithArgumentList(QDBus::AutoDetect, "setTargetName", targetArgs); QString url = currentJob->getSequenceFile().toLocalFile(); QList dbusargs; dbusargs.append(url); captureInterface->callWithArgumentList(QDBus::AutoDetect, "loadSequenceQueue", dbusargs); QMap fMap = currentJob->getCapturedFramesMap(); for (auto e : fMap.keys()) { QList dbusargs; QDBusMessage reply; dbusargs.append(e); dbusargs.append(fMap.value(e)); if ((reply = captureInterface->callWithArgumentList(QDBus::AutoDetect, "setCapturedFramesMap", dbusargs)).type() == QDBusMessage::ErrorMessage) { appendLogText(i18n("Warning! Job '%1' setCapturedFramesCount request received DBUS error: %1", currentJob->getName(), reply.errorMessage())); return; } } // If sequence is a loop, ignore sequence history if (currentJob->getCompletionCondition() != SchedulerJob::FINISH_SEQUENCE) captureInterface->call(QDBus::AutoDetect, "ignoreSequenceHistory"); // Start capture process captureInterface->call(QDBus::AutoDetect, "start"); currentJob->setStage(SchedulerJob::STAGE_CAPTURING); KNotification::event(QLatin1String("EkosScheduledImagingStart"), i18n("Ekos job (%1) - Capture started", currentJob->getName())); if (captureBatch > 0) appendLogText(i18n("Job '%1' capture is in progress (batch #%2)...", currentJob->getName(), captureBatch + 1)); else appendLogText(i18n("Job '%1' capture is in progress...", currentJob->getName())); } void Scheduler::stopGuiding() { if ((currentJob->getStepPipeline() & SchedulerJob::USE_GUIDE) && (currentJob->getStage() == SchedulerJob::STAGE_GUIDING_COMPLETE || currentJob->getStage() == SchedulerJob::STAGE_CAPTURING)) { qCInfo(KSTARS_EKOS_SCHEDULER) << "Stopping guiding..."; guideInterface->call(QDBus::AutoDetect, "abort"); guideFailureCount = 0; } } void Scheduler::setSolverAction(Align::GotoMode mode) { QVariant gotoMode(static_cast(mode)); alignInterface->call(QDBus::AutoDetect, "setSolverAction", gotoMode); } void Scheduler::disconnectINDI() { qCInfo(KSTARS_EKOS_SCHEDULER) << "Disconnecting INDI..."; indiState = INDI_DISCONNECTING; indiConnectFailureCount = 0; ekosInterface->call(QDBus::AutoDetect, "disconnectDevices"); } void Scheduler::stopEkos() { qCInfo(KSTARS_EKOS_SCHEDULER) << "Stopping Ekos..."; ekosState = EKOS_STOPPING; ekosInterface->call(QDBus::AutoDetect, "stop"); } void Scheduler::setDirty() { mDirty = true; if (sender() == startupProcedureButtonGroup || sender() == shutdownProcedureGroup) return; if (jobUnderEdit >= 0 && state != SCHEDULER_RUNNIG && queueTable->selectedItems().isEmpty() == false) saveJob(); } void Scheduler::updateCompletedJobsCount() { /* Use a temporary map in order to limit the number of file searches */ QMap newFramesCount; /* Enumerate SchedulerJobs to count captures that are already stored */ for (SchedulerJob *oneJob : jobs) { QList seqjobs; bool hasAutoFocus = false; /* Look into the sequence requirements, bypass if invalid */ if (loadSequenceQueue(oneJob->getSequenceFile().toLocalFile(), oneJob, seqjobs, hasAutoFocus) == false) { appendLogText(i18n("Warning! Job '%1' has inaccessible sequence '%2', marking invalid.", oneJob->getName(), oneJob->getSequenceFile().toLocalFile())); oneJob->setState(SchedulerJob::JOB_INVALID); continue; } /* Enumerate the SchedulerJob's SequenceJobs to count captures stored for each */ foreach (SequenceJob *oneSeqJob, seqjobs) { /* Only consider captures stored on client (Ekos) side */ /* FIXME: ask the remote for the file count */ if (oneSeqJob->getUploadMode() == ISD::CCD::UPLOAD_LOCAL) continue; /* FIXME: refactor signature determination in a separate function in order to support multiple backends */ /* FIXME: this signature path is incoherent when there is no filter wheel on the setup - bugfix should be elsewhere though */ QString const signature = oneSeqJob->getLocalDir() + oneSeqJob->getDirectoryPostfix(); /* Bypass this SchedulerJob if we already checked its signature */ switch(oneJob->getState()) { case SchedulerJob::JOB_IDLE: case SchedulerJob::JOB_EVALUATION: /* We recount idle/evaluated jobs systematically */ break; default: /* We recount other jobs if somehow we don't have any count for their signature, else we reuse the previous count */ QMap::iterator const sigCount = capturedFramesCount.find(signature); if (capturedFramesCount.end() != sigCount) { newFramesCount[signature] = sigCount.value(); continue; } } /* Count captures already stored */ newFramesCount[signature] = getCompletedFiles(signature, oneSeqJob->getFullPrefix()); } } capturedFramesCount = newFramesCount; } bool Scheduler::estimateJobTime(SchedulerJob *schedJob) { /* updateCompletedJobsCount(); */ - QList jobs; + QList seqJobs; bool hasAutoFocus = false; - if (loadSequenceQueue(schedJob->getSequenceFile().toLocalFile(), schedJob, jobs, hasAutoFocus) == false) + if (loadSequenceQueue(schedJob->getSequenceFile().toLocalFile(), schedJob, seqJobs, hasAutoFocus) == false) return false; schedJob->setInSequenceFocus(hasAutoFocus); bool lightFramesRequired = false; int totalSequenceCount = 0, totalCompletedCount = 0; double totalImagingTime = 0; bool rememberJobProgress = Options::rememberJobProgress(); - foreach (SequenceJob *job, jobs) + foreach (SequenceJob *seqJob, seqJobs) { /* FIXME: find a way to actually display the filter name */ - QString seqName = i18n("Job '%1' %2x%3\" %4", schedJob->getName(), job->getCount(), job->getExposure(), job->getFilterName()); + QString seqName = i18n("Job '%1' %2x%3\" %4", schedJob->getName(), seqJob->getCount(), seqJob->getExposure(), seqJob->getFilterName()); - if (job->getUploadMode() == ISD::CCD::UPLOAD_LOCAL) + if (seqJob->getUploadMode() == ISD::CCD::UPLOAD_LOCAL) { appendLogText(i18n("%1 duration cannot be estimated time since the sequence saves the files remotely.", seqName)); schedJob->setEstimatedTime(-2); - // Iterate over all jobs, if just one requires FRAME_LIGHT then we set it as is and return - foreach (SequenceJob *oneJob, jobs) + // Iterate over all sequence jobs, if just one requires FRAME_LIGHT then we set it as is and return + foreach (SequenceJob *oneJob, seqJobs) { if (oneJob->getFrameType() == FRAME_LIGHT) { lightFramesRequired = true; break; } } schedJob->setLightFramesRequired(lightFramesRequired); - qDeleteAll(jobs); + qDeleteAll(seqJobs); return true; } - int completed = 0; + int const captures_required = seqJob->getCount()*schedJob->getRepeatsRequired(); + + int captures_completed = 0; if (rememberJobProgress) { - // Retrieve cached count of completed captures for the output folder of this job - QString signature = job->getLocalDir() + job->getDirectoryPostfix(); - completed = capturedFramesCount[signature]; - appendLogText(i18n("%1 matches %2 captures in output folder '%3'.", seqName, completed, signature)); - - // If we have multiple jobs storing their captures in the same output folder (duplicated jobs), we need to recalculate - // the completion count for the current scheduler job using sequence jobs which had the same signature earlier in the list - // This DOES NOT handle the case of duplicated Scheduler jobs having the same output folder - //int const overallCompleted = completed; - foreach (SequenceJob *prevJob, jobs) + /* Enumerate sequence jobs associated to this scheduler job, and assign them a completed count. + * + * The objective of this block is to fill the storage map of the scheduler job with completed counts for each capture storage. + * + * Sequence jobs capture to a storage folder, and are given a count of captures to store at that location. + * The tricky part is to make sure the repeat count of the scheduler job is properly transferred to each sequence job. + * + * For instance, a scheduler job repeated three times must execute the full list of sequence jobs three times, thus + * has to tell each sequence job it misses all captures, three times. It cannot tell the sequence job three captures are + * missing, first because that's not how the sequence job is designed (completed count, not required count), and second + * because this would make the single sequence job repeat three times, instead of repeating the full list of sequence + * jobs three times. + * + * The consolidated storage map will be assigned to each sequence job based on their signature when the scheduler job executes them. + * + * For instance, consider a RGBL sequence of single captures. The map will store completed captures for R, G, B and L storages. + * If R and G have 1 file each, and B and L have no files, map[storage(R)] = map[storage(G)] = 1 and map[storage(B)] = map[storage(L)] = 0. + * When that scheduler job executes, only B and L captures will be processed. + * + * In the case of a RGBLRGB sequence of single captures, the second R, G and B map items will count one less capture than what is really in storage. + * If R and G have 1 file each, and B and L have no files, map[storage(R1)] = map[storage(B1)] = 1, and all others will be 0. + * When that scheduler job executes, B1, L, R2, G2 and B2 will be processed. + * + * This doesn't handle the case of duplicated scheduler jobs, that is, scheduler jobs with the same storage for capture sets. + * Those scheduler jobs will all change state to completion at the same moment as they all target the same storage. + * This is why it is important to manage the repeat count of the scheduler job, as stated earlier. + */ + + // Retrieve cached count of captures_completed captures for the output folder of this seqJob + QString const signature = seqJob->getLocalDir() + seqJob->getDirectoryPostfix(); + captures_completed = capturedFramesCount[signature]; + + appendLogText(i18n("%1 sees %2 captures in output folder '%3'.", seqName, captures_completed, signature)); + + // Enumerate sequence jobs to check how many captures are completed overall in the same storage as the current one + foreach (SequenceJob *prevSeqJob, seqJobs) { - // Enumerate jobs up to the current one - if (job == prevJob) + // Enumerate seqJobs up to the current one + if (seqJob == prevSeqJob) break; - // If the previous job signature matches the current, reduce completion count to compare duplicates - if (!signature.compare(prevJob->getLocalDir() + prevJob->getDirectoryPostfix())) + // If the previous sequence signature matches the current, reduce completion count to take duplicates into account + if (!signature.compare(prevSeqJob->getLocalDir() + prevSeqJob->getDirectoryPostfix())) { - appendLogText(i18n("%1 has a previous duplicate sequence requiring %2 captures.", seqName, prevJob->getCount())); - completed -= prevJob->getCount(); + int const previous_captures_required = prevSeqJob->getCount()*schedJob->getRepeatsRequired(); + appendLogText(i18n("%1 has a previous duplicate sequence job requiring %2 captures.", seqName, previous_captures_required)); + captures_completed -= previous_captures_required; } + + // Now completed count can be needlessly negative for this job, so clamp to zero + if (captures_completed < 0) + captures_completed = 0; + + // And break if no captures remain, this job has to execute + if (captures_completed == 0) + break; } - // Now completed count can be needlessly negative for this job, so clamp to zero - if (completed < 0) - completed = 0; + // Finally we're only interested in the number of captures required for this sequence item + if (captures_required < captures_completed) + captures_completed = captures_required; + + appendLogText(i18n("%1 has completed %2/%3 of its required captures in output folder '%4'.", seqName, captures_completed, captures_required, signature)); // Update the completion count for this signature if we still have captures to take + // FIXME: setting the whole capture map each time is not very optimal QMap fMap = schedJob->getCapturedFramesMap(); - fMap[signature] = completed < job->getCount() ? completed : job->getCount(); - schedJob->setCapturedFramesMap(fMap); + if (fMap[signature] != captures_completed) + { + fMap[signature] = captures_completed; + schedJob->setCapturedFramesMap(fMap); + } - // From now on, 'completed' is the number of frames completed for the *current* sequence job + // From now on, 'captures_completed' is the number of frames completed for the *current* sequence job } // Check if we still need any light frames. Because light frames changes the flow of the observatory startup // Without light frames, there is no need to do focusing, alignment, guiding...etc - // We check if the frame type is LIGHT and if either the number of completed frames is less than required + // We check if the frame type is LIGHT and if either the number of captures_completed frames is less than required // OR if the completion condition is set to LOOP so it is never complete due to looping. - bool const areJobCapturesComplete = !(completed < job->getCount()*schedJob->getRepeatsRequired() || schedJob->getCompletionCondition() == SchedulerJob::FINISH_LOOP); - if (job->getFrameType() == FRAME_LIGHT) + // FIXME: As it is implemented now, FINISH_LOOP may loop over a capture-complete, therefore inoperant, scheduler job. + bool const areJobCapturesComplete = !(captures_completed < captures_required || schedJob->getCompletionCondition() == SchedulerJob::FINISH_LOOP); + if (seqJob->getFrameType() == FRAME_LIGHT) { if(areJobCapturesComplete) { - appendLogText(i18n("%1 completed its sequence of %2 light frames.", seqName, job->getCount()*schedJob->getRepeatsRequired())); + appendLogText(i18n("%1 completed its sequence of %2 light frames.", seqName, captures_required)); } else { lightFramesRequired = true; // In some cases we do not need to calculate time we just need to know // if light frames are required or not. So we break out /* if (schedJob->getCompletionCondition() == SchedulerJob::FINISH_LOOP || (schedJob->getStartupCondition() == SchedulerJob::START_AT && schedJob->getCompletionCondition() == SchedulerJob::FINISH_AT)) break; */ } } else { appendLogText(i18n("%1 captures calibration frames.", seqName)); } - totalSequenceCount += job->getCount()*schedJob->getRepeatsRequired(); - totalCompletedCount += rememberJobProgress ? completed : 0; + totalSequenceCount += captures_required; + totalCompletedCount += rememberJobProgress ? captures_completed : 0; /* If captures are not complete, we have imaging time left */ if (!areJobCapturesComplete) { /* if looping, consider we always have one capture left - currently this is discarded afterwards as -2 */ if (schedJob->getCompletionCondition() == SchedulerJob::FINISH_LOOP) - totalImagingTime += fabs((job->getExposure() + job->getDelay()) * 1); + totalImagingTime += fabs((seqJob->getExposure() + seqJob->getDelay()) * 1); else - totalImagingTime += fabs((job->getExposure() + job->getDelay()) * (job->getCount()*schedJob->getRepeatsRequired() - completed)); + totalImagingTime += fabs((seqJob->getExposure() + seqJob->getDelay()) * (captures_required - captures_completed)); /* If we have light frames to process, add focus/dithering delay */ - if (job->getFrameType() == FRAME_LIGHT) + if (seqJob->getFrameType() == FRAME_LIGHT) { // If inSequenceFocus is true if (hasAutoFocus) { // Wild guess that each in sequence auto focus takes an average of 30 seconds. It can take any where from 2 seconds to 2+ minutes. appendLogText(i18n("%1 requires a focus procedure.", seqName)); - totalImagingTime += (job->getCount()*schedJob->getRepeatsRequired() - completed) * 30; + totalImagingTime += (captures_required - captures_completed) * 30; } // If we're dithering after each exposure, that's another 10-20 seconds if (schedJob->getStepPipeline() & SchedulerJob::USE_GUIDE && Options::ditherEnabled()) { appendLogText(i18n("%1 requires a dither procedure.", seqName)); - totalImagingTime += ((job->getCount()*schedJob->getRepeatsRequired() - completed) * 15) / Options::ditherFrames(); + totalImagingTime += ((captures_required - captures_completed) * 15) / Options::ditherFrames(); } } } } schedJob->setLightFramesRequired(lightFramesRequired); schedJob->setSequenceCount(totalSequenceCount); schedJob->setCompletedCount(totalCompletedCount); - qDeleteAll(jobs); + qDeleteAll(seqJobs); // We can't estimate times that do not finish when sequence is done if (schedJob->getCompletionCondition() == SchedulerJob::FINISH_LOOP) { // We can't know estimated time if it is looping indefinitely appendLogText(i18n("Warning! Job '%1' will be looping until Scheduler is stopped manually.", schedJob->getName())); schedJob->setEstimatedTime(-2); } // If we know startup and finish times, we can estimate time right away else if (schedJob->getStartupCondition() == SchedulerJob::START_AT && schedJob->getCompletionCondition() == SchedulerJob::FINISH_AT) { qint64 const diff = schedJob->getStartupTime().secsTo(schedJob->getCompletionTime()); appendLogText(i18n("Job '%1' will run for %2.", schedJob->getName(), dms(diff / 3600.0f).toHMSString())); schedJob->setEstimatedTime(diff); } // Rely on the estimated imaging time to determine whether this job is complete or not - this makes the estimated time null else if (totalImagingTime <= 0) { appendLogText(i18n("Job '%1' will not run, complete with %2/%3 captures.", schedJob->getName(), totalCompletedCount, totalSequenceCount)); schedJob->setEstimatedTime(0); } else { if (lightFramesRequired) { + /* FIXME: estimation doesn't need to consider repeats, those will be optimized away by findNextJob (this is a regression) */ + /* FIXME: estimation should base on actual measure of each step, eventually with preliminary data as what it used now */ // Are we doing tracking? It takes about 30 seconds if (schedJob->getStepPipeline() & SchedulerJob::USE_TRACK) totalImagingTime += 30*schedJob->getRepeatsRequired(); // Are we doing initial focusing? That can take about 2 minutes if (schedJob->getStepPipeline() & SchedulerJob::USE_FOCUS) totalImagingTime += 120*schedJob->getRepeatsRequired(); // Are we doing astrometry? That can take about 30 seconds if (schedJob->getStepPipeline() & SchedulerJob::USE_ALIGN) totalImagingTime += 30*schedJob->getRepeatsRequired(); // Are we doing guiding? Calibration process can take about 2 mins if (schedJob->getStepPipeline() & SchedulerJob::USE_GUIDE) totalImagingTime += 120*schedJob->getRepeatsRequired(); } dms estimatedTime; estimatedTime.setH(totalImagingTime / 3600.0); /* Kept the informative log because the estimation is displayed */ appendLogText(i18n("Job '%1' estimated to take %2 to complete.", schedJob->getName(), estimatedTime.toHMSString())); schedJob->setEstimatedTime(totalImagingTime); } return true; } void Scheduler::parkMount() { QDBusReply MountReply = mountInterface->call(QDBus::AutoDetect, "getParkingStatus"); Mount::ParkingStatus status = (Mount::ParkingStatus)MountReply.value(); if (status != Mount::PARKING_OK) { if (status == Mount::PARKING_BUSY) { appendLogText(i18n("Parking mount in progress...")); } else { mountInterface->call(QDBus::AutoDetect, "park"); appendLogText(i18n("Parking mount...")); currentOperationTime.start(); } if (shutdownState == SHUTDOWN_PARK_MOUNT) shutdownState = SHUTDOWN_PARKING_MOUNT; else if (parkWaitState == PARKWAIT_PARK) parkWaitState = PARKWAIT_PARKING; } else { appendLogText(i18n("Mount already parked.")); if (shutdownState == SHUTDOWN_PARK_MOUNT) shutdownState = SHUTDOWN_PARK_DOME; else if (parkWaitState == PARKWAIT_PARK) parkWaitState = PARKWAIT_PARKED; } } void Scheduler::unParkMount() { QDBusReply const mountReply = mountInterface->call(QDBus::AutoDetect, "getParkingStatus"); Mount::ParkingStatus status = (Mount::ParkingStatus)mountReply.value(); if (mountReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Mount getParkingStatus request received DBUS error: %1", QDBusError::errorString(mountReply.error().type()))); status = Mount::PARKING_ERROR; } if (status != Mount::UNPARKING_OK) { if (status == Mount::UNPARKING_BUSY) appendLogText(i18n("Unparking mount in progress...")); else { mountInterface->call(QDBus::AutoDetect, "unpark"); appendLogText(i18n("Unparking mount...")); currentOperationTime.start(); } if (startupState == STARTUP_UNPARK_MOUNT) startupState = STARTUP_UNPARKING_MOUNT; else if (parkWaitState == PARKWAIT_UNPARK) parkWaitState = PARKWAIT_UNPARKING; } else { appendLogText(i18n("Mount already unparked.")); if (startupState == STARTUP_UNPARK_MOUNT) startupState = STARTUP_UNPARK_CAP; else if (parkWaitState == PARKWAIT_UNPARK) parkWaitState = PARKWAIT_UNPARKED; } } void Scheduler::checkMountParkingStatus() { static int parkingFailureCount = 0; QDBusReply const mountReply = mountInterface->call(QDBus::AutoDetect, "getParkingStatus"); Mount::ParkingStatus status = (Mount::ParkingStatus)mountReply.value(); if (mountReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Mount getParkingStatus request received DBUS error: %1", QDBusError::errorString(mountReply.error().type()))); status = Mount::PARKING_ERROR; } switch (status) { case Mount::PARKING_OK: appendLogText(i18n("Mount parked.")); if (shutdownState == SHUTDOWN_PARKING_MOUNT) shutdownState = SHUTDOWN_PARK_DOME; else if (parkWaitState == PARKWAIT_PARKING) parkWaitState = PARKWAIT_PARKED; parkingFailureCount = 0; break; case Mount::UNPARKING_OK: appendLogText(i18n("Mount unparked.")); if (startupState == STARTUP_UNPARKING_MOUNT) startupState = STARTUP_UNPARK_CAP; else if (parkWaitState == PARKWAIT_UNPARKING) parkWaitState = PARKWAIT_UNPARKED; parkingFailureCount = 0; break; case Mount::PARKING_BUSY: case Mount::UNPARKING_BUSY: // TODO make the timeouts configurable by the user if (currentOperationTime.elapsed() > (60 * 1000)) { if (parkingFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("Operation timeout. Restarting operation...")); if (status == Mount::PARKING_BUSY) parkMount(); else unParkMount(); break; } } break; case Mount::PARKING_ERROR: if (startupState == STARTUP_UNPARKING_MOUNT) { appendLogText(i18n("Mount unparking error.")); startupState = STARTUP_ERROR; } else if (shutdownState == SHUTDOWN_PARKING_MOUNT) { appendLogText(i18n("Mount parking error.")); shutdownState = SHUTDOWN_ERROR; } else if (parkWaitState == PARKWAIT_PARKING) { appendLogText(i18n("Mount parking error.")); parkWaitState = PARKWAIT_ERROR; } else if (parkWaitState == PARKWAIT_UNPARK) { appendLogText(i18n("Mount unparking error.")); parkWaitState = PARKWAIT_ERROR; } parkingFailureCount = 0; break; default: break; } } bool Scheduler::isMountParked() { QDBusReply const mountReply = mountInterface->call(QDBus::AutoDetect, "getParkingStatus"); Mount::ParkingStatus status = (Mount::ParkingStatus)mountReply.value(); if (mountReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Mount getParkingStatus request received DBUS error: %1", QDBusError::errorString(mountReply.error().type()))); status = Mount::PARKING_ERROR; } return status == Mount::PARKING_OK || status == Mount::PARKING_IDLE; } void Scheduler::parkDome() { QDBusReply const domeReply = domeInterface->call(QDBus::AutoDetect, "getParkingStatus"); Dome::ParkingStatus status = (Dome::ParkingStatus)domeReply.value(); if (domeReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Dome getParkingStatus request received DBUS error: %1", QDBusError::errorString(domeReply.error().type()))); status = Dome::PARKING_ERROR; } if (status != Dome::PARKING_OK) { shutdownState = SHUTDOWN_PARKING_DOME; domeInterface->call(QDBus::AutoDetect, "park"); appendLogText(i18n("Parking dome...")); currentOperationTime.start(); } else { appendLogText(i18n("Dome already parked.")); shutdownState = SHUTDOWN_SCRIPT; } } void Scheduler::unParkDome() { QDBusReply const domeReply = domeInterface->call(QDBus::AutoDetect, "getParkingStatus"); Dome::ParkingStatus status = (Dome::ParkingStatus)domeReply.value(); if (domeReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Dome getParkingStatus request received DBUS error: %1", QDBusError::errorString(domeReply.error().type()))); status = Dome::PARKING_ERROR; } if (status != Dome::UNPARKING_OK) { startupState = STARTUP_UNPARKING_DOME; domeInterface->call(QDBus::AutoDetect, "unpark"); appendLogText(i18n("Unparking dome...")); currentOperationTime.start(); } else { appendLogText(i18n("Dome already unparked.")); startupState = STARTUP_UNPARK_MOUNT; } } void Scheduler::checkDomeParkingStatus() { /* FIXME: move this elsewhere */ static int parkingFailureCount = 0; QDBusReply const domeReply = domeInterface->call(QDBus::AutoDetect, "getParkingStatus"); Dome::ParkingStatus status = (Dome::ParkingStatus)domeReply.value(); if (domeReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Dome getParkingStatus request received DBUS error: %1", QDBusError::errorString(domeReply.error().type()))); status = Dome::PARKING_ERROR; } switch (status) { case Dome::PARKING_OK: if (shutdownState == SHUTDOWN_PARKING_DOME) { appendLogText(i18n("Dome parked.")); shutdownState = SHUTDOWN_SCRIPT; } parkingFailureCount = 0; break; case Dome::UNPARKING_OK: if (startupState == STARTUP_UNPARKING_DOME) { startupState = STARTUP_UNPARK_MOUNT; appendLogText(i18n("Dome unparked.")); } parkingFailureCount = 0; break; case Dome::PARKING_BUSY: case Dome::UNPARKING_BUSY: // TODO make the timeouts configurable by the user if (currentOperationTime.elapsed() > (120 * 1000)) { if (parkingFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("Operation timeout. Restarting operation...")); if (status == Dome::PARKING_BUSY) parkDome(); else unParkDome(); break; } } break; case Dome::PARKING_ERROR: if (shutdownState == SHUTDOWN_PARKING_DOME) { appendLogText(i18n("Dome parking error.")); shutdownState = SHUTDOWN_ERROR; } else if (startupState == STARTUP_UNPARKING_DOME) { appendLogText(i18n("Dome unparking error.")); startupState = STARTUP_ERROR; } parkingFailureCount = 0; break; default: break; } } bool Scheduler::isDomeParked() { QDBusReply const domeReply = domeInterface->call(QDBus::AutoDetect, "getParkingStatus"); Dome::ParkingStatus status = (Dome::ParkingStatus)domeReply.value(); if (domeReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Dome getParkingStatus request received DBUS error: %1", QDBusError::errorString(domeReply.error().type()))); status = Dome::PARKING_ERROR; } return status == Dome::PARKING_OK || status == Dome::PARKING_IDLE; } void Scheduler::parkCap() { QDBusReply const capReply = capInterface->call(QDBus::AutoDetect, "getParkingStatus"); DustCap::ParkingStatus status = (DustCap::ParkingStatus)capReply.value(); if (capReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Cap getParkingStatus request received DBUS error: %1", QDBusError::errorString(capReply.error().type()))); status = DustCap::PARKING_ERROR; } if (status != DustCap::PARKING_OK) { shutdownState = SHUTDOWN_PARKING_CAP; capInterface->call(QDBus::AutoDetect, "park"); appendLogText(i18n("Parking Cap...")); currentOperationTime.start(); } else { appendLogText(i18n("Cap already parked.")); shutdownState = SHUTDOWN_PARK_MOUNT; } } void Scheduler::unParkCap() { QDBusReply const capReply = capInterface->call(QDBus::AutoDetect, "getParkingStatus"); DustCap::ParkingStatus status = (DustCap::ParkingStatus)capReply.value(); if (capReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Cap getParkingStatus request received DBUS error: %1", QDBusError::errorString(capReply.error().type()))); status = DustCap::PARKING_ERROR; } if (status != DustCap::UNPARKING_OK) { startupState = STARTUP_UNPARKING_CAP; capInterface->call(QDBus::AutoDetect, "unpark"); appendLogText(i18n("Unparking cap...")); currentOperationTime.start(); } else { appendLogText(i18n("Cap already unparked.")); startupState = STARTUP_COMPLETE; } } void Scheduler::checkCapParkingStatus() { /* FIXME: move this elsewhere */ static int parkingFailureCount = 0; QDBusReply const capReply = capInterface->call(QDBus::AutoDetect, "getParkingStatus"); DustCap::ParkingStatus status = (DustCap::ParkingStatus)capReply.value(); if (capReply.error().type() != QDBusError::NoError) { appendLogText(i18n("Warning! Cap getParkingStatus request received DBUS error: %1", QDBusError::errorString(capReply.error().type()))); status = DustCap::PARKING_ERROR; } switch (status) { case DustCap::PARKING_OK: if (shutdownState == SHUTDOWN_PARKING_CAP) { appendLogText(i18n("Cap parked.")); shutdownState = SHUTDOWN_PARK_MOUNT; } parkingFailureCount = 0; break; case DustCap::UNPARKING_OK: if (startupState == STARTUP_UNPARKING_CAP) { startupState = STARTUP_COMPLETE; appendLogText(i18n("Cap unparked.")); } parkingFailureCount = 0; break; case DustCap::PARKING_BUSY: case DustCap::UNPARKING_BUSY: // TODO make the timeouts configurable by the user if (currentOperationTime.elapsed() > (60 * 1000)) { if (parkingFailureCount++ < MAX_FAILURE_ATTEMPTS) { appendLogText(i18n("Operation timeout. Restarting operation...")); if (status == DustCap::PARKING_BUSY) parkCap(); else unParkCap(); break; } } break; case DustCap::PARKING_ERROR: if (shutdownState == SHUTDOWN_PARKING_CAP) { appendLogText(i18n("Cap parking error.")); shutdownState = SHUTDOWN_ERROR; } else if (startupState == STARTUP_UNPARKING_CAP) { appendLogText(i18n("Cap unparking error.")); startupState = STARTUP_ERROR; } parkingFailureCount = 0; break; default: break; } } void Scheduler::startJobEvaluation() { // Reset ALL scheduler jobs to IDLE and re-evalute them all again for(SchedulerJob *job : jobs) job->reset(); // Now evaluate all pending jobs per the conditions set in each jobEvaluationOnly = true; evaluateJobs(); } void Scheduler::updatePreDawn() { double earlyDawn = Dawn - Options::preDawnTime() / (60.0 * 24.0); int dayOffset = 0; QTime dawn = QTime(0, 0, 0).addSecs(Dawn * 24 * 3600); if (KStarsData::Instance()->lt().time() >= dawn) dayOffset = 1; preDawnDateTime.setDate(KStarsData::Instance()->lt().date().addDays(dayOffset)); preDawnDateTime.setTime(QTime::fromMSecsSinceStartOfDay(earlyDawn * 24 * 3600 * 1000)); } bool Scheduler::isWeatherOK(SchedulerJob *job) { if (weatherStatus == IPS_OK || weatherCheck->isChecked() == false) return true; else if (weatherStatus == IPS_IDLE) { if (indiState == INDI_READY) appendLogText(i18n("Weather information is pending...")); return true; } // Temporary BUSY is ALSO accepted for now // TODO Figure out how to exactly handle this if (weatherStatus == IPS_BUSY) return true; if (weatherStatus == IPS_ALERT) { job->setState(SchedulerJob::JOB_ABORTED); appendLogText(i18n("Job '%1' suffers from bad weather, marking aborted.", job->getName())); } /*else if (weatherStatus == IPS_BUSY) { appendLogText(i18n("%1 observation job delayed due to bad weather.", job->getName())); schedulerTimer.stop(); connect(this, SIGNAL(weatherChanged(IPState)), this, SLOT(resumeCheckStatus())); }*/ return false; } void Scheduler::resumeCheckStatus() { disconnect(this, SIGNAL(weatherChanged(IPState)), this, SLOT(resumeCheckStatus())); schedulerTimer.start(); } void Scheduler::startMosaicTool() { bool raOk = false, decOk = false; dms ra(raBox->createDms(false, &raOk)); //false means expressed in hours dms dec(decBox->createDms(true, &decOk)); if (raOk == false) { appendLogText(i18n("RA value %1 is invalid.", raBox->text())); return; } if (decOk == false) { appendLogText(i18n("DEC value %1 is invalid.", decBox->text())); return; } Mosaic mosaicTool; SkyPoint center; center.setRA0(ra); center.setDec0(dec); mosaicTool.setCenter(center); mosaicTool.calculateFOV(); mosaicTool.adjustSize(); if (mosaicTool.exec() == QDialog::Accepted) { // #1 Edit Sequence File ---> Not needed as of 2016-09-12 since Scheduler can send Target Name to Capture module it will append it to root dir // #1.1 Set prefix to Target-Part# // #1.2 Set directory to output/Target-Part# // #2 Save all sequence files in Jobs dir // #3 Set as currnet Sequence file // #4 Change Target name to Target-Part# // #5 Update J2000 coords // #6 Repeat and save Ekos Scheduler List in the output directory qCDebug(KSTARS_EKOS_SCHEDULER) << "Job accepted with # " << mosaicTool.getJobs().size() << " jobs and fits dir " << mosaicTool.getJobsDir(); QString outputDir = mosaicTool.getJobsDir(); QString targetName = nameEdit->text().simplified().remove(' '); int batchCount = 1; XMLEle *root = getSequenceJobRoot(); if (root == nullptr) return; int currentJobsCount = jobs.count(); foreach (OneTile *oneJob, mosaicTool.getJobs()) { QString prefix = QString("%1-Part%2").arg(targetName).arg(batchCount++); prefix.replace(' ', '-'); nameEdit->setText(prefix); if (createJobSequence(root, prefix, outputDir) == false) return; QString filename = QString("%1/%2.esq").arg(outputDir).arg(prefix); sequenceEdit->setText(filename); sequenceURL = QUrl::fromLocalFile(filename); raBox->setText(oneJob->skyCenter.ra0().toHMSString()); decBox->setText(oneJob->skyCenter.dec0().toDMSString()); saveJob(); } delXMLEle(root); // Delete any prior jobs before saving for (int i = 0; i < currentJobsCount; i++) { delete (jobs.takeFirst()); queueTable->removeRow(0); } QUrl mosaicURL = QUrl::fromLocalFile((QString("%1/%2_mosaic.esl").arg(outputDir).arg(targetName))); if (saveScheduler(mosaicURL)) { appendLogText(i18n("Mosaic file %1 saved successfully.", mosaicURL.toLocalFile())); } else { appendLogText(i18n("Error saving mosaic file %1. Please reload job.", mosaicURL.toLocalFile())); } } } XMLEle *Scheduler::getSequenceJobRoot() { QFile sFile; sFile.setFileName(sequenceURL.toLocalFile()); if (!sFile.open(QIODevice::ReadOnly)) { KMessageBox::sorry(KStars::Instance(), i18n("Unable to open file %1", sFile.fileName()), i18n("Could Not Open File")); return nullptr; } LilXML *xmlParser = newLilXML(); char errmsg[MAXRBUF]; XMLEle *root = nullptr; char c; while (sFile.getChar(&c)) { root = readXMLEle(xmlParser, c, errmsg); if (root) break; } delLilXML(xmlParser); sFile.close(); return root; } bool Scheduler::createJobSequence(XMLEle *root, const QString &prefix, const QString &outputDir) { QFile sFile; sFile.setFileName(sequenceURL.toLocalFile()); if (!sFile.open(QIODevice::ReadOnly)) { KMessageBox::sorry(KStars::Instance(), i18n("Unable to open file %1", sFile.fileName()), i18n("Could Not Open File")); return false; } XMLEle *ep = nullptr; XMLEle *subEP = nullptr; for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) { if (!strcmp(tagXMLEle(ep), "Job")) { for (subEP = nextXMLEle(ep, 1); subEP != nullptr; subEP = nextXMLEle(ep, 0)) { if (!strcmp(tagXMLEle(subEP), "Prefix")) { XMLEle *rawPrefix = findXMLEle(subEP, "RawPrefix"); if (rawPrefix) { editXMLEle(rawPrefix, prefix.toLatin1().constData()); } } else if (!strcmp(tagXMLEle(subEP), "FITSDirectory")) { editXMLEle(subEP, QString("%1/%2").arg(outputDir).arg(prefix).toLatin1().constData()); } } } } QDir().mkpath(outputDir); QString filename = QString("%1/%2.esq").arg(outputDir).arg(prefix); FILE *outputFile = fopen(filename.toLatin1().constData(), "w"); if (outputFile == nullptr) { QString message = i18n("Unable to write to file %1", filename); KMessageBox::sorry(0, message, i18n("Could Not Open File")); return false; } fprintf(outputFile, ""); prXMLEle(outputFile, root, 0); fclose(outputFile); return true; } void Scheduler::resetAllJobs() { if (state == SCHEDULER_RUNNIG) return; foreach (SchedulerJob *job, jobs) job->reset(); } void Scheduler::checkTwilightWarning(bool enabled) { if (enabled) return; if (KMessageBox::warningContinueCancel( NULL, i18n("Warning! Turning off astronomial twilight check may cause the observatory " "to run during daylight. This can cause irreversible damage to your equipment!"), i18n("Astronomial Twilight Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "astronomical_twilight_warning") == KMessageBox::Cancel) { twilightCheck->setChecked(true); } } void Scheduler::checkStartupProcedure() { if (checkStartupState() == false) QTimer::singleShot(1000, this, SLOT(checkStartupProcedure())); else { if (startupState == STARTUP_COMPLETE) appendLogText(i18n("Manual startup procedure completed successfully.")); else if (startupState == STARTUP_ERROR) appendLogText(i18n("Manual startup procedure terminated due to errors.")); startupB->setIcon( QIcon::fromTheme("media-playback-start")); } } void Scheduler::runStartupProcedure() { if (startupState == STARTUP_IDLE || startupState == STARTUP_ERROR || startupState == STARTUP_COMPLETE) { /* FIXME: Probably issue a warning only, in case the user wants to run the startup script alone */ if (indiState == INDI_IDLE) { KSNotification::sorry(i18n("Cannot run startup procedure while INDI devices are not online.")); return; } if (KMessageBox::questionYesNo( nullptr, i18n("Are you sure you want to execute the startup procedure manually?")) == KMessageBox::Yes) { appendLogText(i18n("Warning! Executing startup procedure manually...")); startupB->setIcon( QIcon::fromTheme("media-playback-stop")); startupState = STARTUP_IDLE; checkStartupState(); QTimer::singleShot(1000, this, SLOT(checkStartupProcedure())); } } else { switch (startupState) { case STARTUP_IDLE: break; case STARTUP_SCRIPT: scriptProcess.terminate(); break; case STARTUP_UNPARK_DOME: break; case STARTUP_UNPARKING_DOME: domeInterface->call(QDBus::AutoDetect, "abort"); break; case STARTUP_UNPARK_MOUNT: break; case STARTUP_UNPARKING_MOUNT: mountInterface->call(QDBus::AutoDetect, "abort"); break; case STARTUP_UNPARK_CAP: break; case STARTUP_UNPARKING_CAP: break; case STARTUP_COMPLETE: break; case STARTUP_ERROR: break; } startupState = STARTUP_IDLE; appendLogText(i18n("Startup procedure terminated.")); } } void Scheduler::checkShutdownProcedure() { // If shutdown procedure is not finished yet, let's check again in 1 second. if (checkShutdownState() == false) QTimer::singleShot(1000, this, SLOT(checkShutdownProcedure())); else { if (shutdownState == SHUTDOWN_COMPLETE) { appendLogText(i18n("Manual shutdown procedure completed successfully.")); // Stop Ekos if (Options::stopEkosAfterShutdown()) stopEkos(); } else if (shutdownState == SHUTDOWN_ERROR) appendLogText(i18n("Manual shutdown procedure terminated due to errors.")); shutdownState = SHUTDOWN_IDLE; shutdownB->setIcon( QIcon::fromTheme("media-playback-start")); } } void Scheduler::runShutdownProcedure() { if (shutdownState == SHUTDOWN_IDLE || shutdownState == SHUTDOWN_ERROR || shutdownState == SHUTDOWN_COMPLETE) { if (KMessageBox::questionYesNo( nullptr, i18n("Are you sure you want to execute the shutdown procedure manually?")) == KMessageBox::Yes) { appendLogText(i18n("Warning! Executing shutdown procedure manually...")); shutdownB->setIcon( QIcon::fromTheme("media-playback-stop")); shutdownState = SHUTDOWN_IDLE; checkShutdownState(); QTimer::singleShot(1000, this, SLOT(checkShutdownProcedure())); } } else { switch (shutdownState) { case SHUTDOWN_IDLE: break; case SHUTDOWN_SCRIPT: break; case SHUTDOWN_SCRIPT_RUNNING: scriptProcess.terminate(); break; case SHUTDOWN_PARK_DOME: break; case SHUTDOWN_PARKING_DOME: domeInterface->call(QDBus::AutoDetect, "abort"); break; case SHUTDOWN_PARK_MOUNT: break; case SHUTDOWN_PARKING_MOUNT: mountInterface->call(QDBus::AutoDetect, "abort"); break; case SHUTDOWN_PARK_CAP: break; case SHUTDOWN_PARKING_CAP: break; case SHUTDOWN_COMPLETE: break; case SHUTDOWN_ERROR: break; } shutdownState = SHUTDOWN_IDLE; appendLogText(i18n("Shutdown procedure terminated.")); } } void Scheduler::loadProfiles() { QString currentProfile = schedulerProfileCombo->currentText(); QDBusReply profiles = ekosInterface->call(QDBus::AutoDetect, "getProfiles"); if (profiles.error().type() == QDBusError::NoError) { schedulerProfileCombo->blockSignals(true); schedulerProfileCombo->clear(); schedulerProfileCombo->addItem(i18n("Default")); schedulerProfileCombo->addItems(profiles); schedulerProfileCombo->setCurrentText(currentProfile); schedulerProfileCombo->blockSignals(false); } } bool Scheduler::loadSequenceQueue(const QString &fileURL, SchedulerJob *schedJob, QList &jobs, bool &hasAutoFocus) { QFile sFile; sFile.setFileName(fileURL); if (!sFile.open(QIODevice::ReadOnly)) { appendLogText(i18n("Unable to open file %1", fileURL)); return false; } LilXML *xmlParser = newLilXML(); char errmsg[MAXRBUF]; XMLEle *root = nullptr; XMLEle *ep = nullptr; char c; while (sFile.getChar(&c)) { root = readXMLEle(xmlParser, c, errmsg); if (root) { for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) { if (!strcmp(tagXMLEle(ep), "Autofocus")) hasAutoFocus = (!strcmp(findXMLAttValu(ep, "enabled"), "true")); else if (!strcmp(tagXMLEle(ep), "Job")) jobs.append(processJobInfo(ep, schedJob)); } delXMLEle(root); } else if (errmsg[0]) { appendLogText(QString(errmsg)); delLilXML(xmlParser); qDeleteAll(jobs); return false; } } return true; } SequenceJob *Scheduler::processJobInfo(XMLEle *root, SchedulerJob *schedJob) { XMLEle *ep = nullptr; XMLEle *subEP = nullptr; const QMap frameTypes = { { "Light", FRAME_LIGHT }, { "Dark", FRAME_DARK }, { "Bias", FRAME_BIAS }, { "Flat", FRAME_FLAT } }; SequenceJob *job = new SequenceJob(); QString rawPrefix, frameType, filterType; double exposure = 0; bool filterEnabled = false, expEnabled = false, tsEnabled = false; for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) { if (!strcmp(tagXMLEle(ep), "Exposure")) { exposure = atof(pcdataXMLEle(ep)); job->setExposure(exposure); } else if (!strcmp(tagXMLEle(ep), "Filter")) { filterType = QString(pcdataXMLEle(ep)); } else if (!strcmp(tagXMLEle(ep), "Type")) { frameType = QString(pcdataXMLEle(ep)); job->setFrameType(frameTypes[frameType]); } else if (!strcmp(tagXMLEle(ep), "Prefix")) { subEP = findXMLEle(ep, "RawPrefix"); if (subEP) rawPrefix = QString(pcdataXMLEle(subEP)); subEP = findXMLEle(ep, "FilterEnabled"); if (subEP) filterEnabled = !strcmp("1", pcdataXMLEle(subEP)); subEP = findXMLEle(ep, "ExpEnabled"); if (subEP) expEnabled = (!strcmp("1", pcdataXMLEle(subEP))); subEP = findXMLEle(ep, "TimeStampEnabled"); if (subEP) tsEnabled = (!strcmp("1", pcdataXMLEle(subEP))); job->setPrefixSettings(rawPrefix, filterEnabled, expEnabled, tsEnabled); } else if (!strcmp(tagXMLEle(ep), "Count")) { job->setCount(atoi(pcdataXMLEle(ep))); } else if (!strcmp(tagXMLEle(ep), "Delay")) { job->setDelay(atoi(pcdataXMLEle(ep))); } else if (!strcmp(tagXMLEle(ep), "FITSDirectory")) { job->setLocalDir(pcdataXMLEle(ep)); } else if (!strcmp(tagXMLEle(ep), "RemoteDirectory")) { job->setRemoteDir(pcdataXMLEle(ep)); } else if (!strcmp(tagXMLEle(ep), "UploadMode")) { job->setUploadMode(static_cast(atoi(pcdataXMLEle(ep)))); } } // Make full prefix QString imagePrefix = rawPrefix; if (imagePrefix.isEmpty() == false) imagePrefix += '_'; imagePrefix += frameType; if (filterEnabled && filterType.isEmpty() == false && (job->getFrameType() == FRAME_LIGHT || job->getFrameType() == FRAME_FLAT)) { imagePrefix += '_'; imagePrefix += filterType; } if (expEnabled) { imagePrefix += '_'; imagePrefix += QString::number(exposure, 'd', 0) + QString("_secs"); } job->setFullPrefix(imagePrefix); QString targetName = schedJob->getName().remove(' '); // Directory postfix QString directoryPostfix; directoryPostfix = QLatin1Literal("/") + targetName + QLatin1Literal("/") + frameType; if ((job->getFrameType() == FRAME_LIGHT || job->getFrameType() == FRAME_FLAT) && filterType.isEmpty() == false) directoryPostfix += QLatin1Literal("/") + filterType; job->setDirectoryPostfix(directoryPostfix); return job; } int Scheduler::getCompletedFiles(const QString &path, const QString &seqPrefix) { int seqFileCount = 0; qCDebug(KSTARS_EKOS_SCHEDULER) << QString("Searching in '%1' for prefix '%2'...").arg(path).arg(seqPrefix); QDirIterator it(path, QDir::Files); /* FIXME: this counts all files with prefix in the storage location, not just captures. DSS analysis files are counted in, for instance. */ while (it.hasNext()) { QString const fileName = QFileInfo(it.next()).baseName(); if (fileName.startsWith(seqPrefix)) { qCDebug(KSTARS_EKOS_SCHEDULER) << QString("> Found '%1'").arg(fileName); seqFileCount++; } } return seqFileCount; } } diff --git a/kstars/ekos/scheduler/schedulerjob.cpp b/kstars/ekos/scheduler/schedulerjob.cpp index 740119019..93b6cf7d6 100644 --- a/kstars/ekos/scheduler/schedulerjob.cpp +++ b/kstars/ekos/scheduler/schedulerjob.cpp @@ -1,531 +1,527 @@ /* Ekos Scheduler Job Copyright (C) Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "schedulerjob.h" #include "scheduler.h" #include "dms.h" #include "kstarsdata.h" #include #include -SchedulerJob::SchedulerJob() -{ -} - void SchedulerJob::setName(const QString &value) { name = value; updateJobCell(); } void SchedulerJob::setStartupCondition(const StartupCondition &value) { startupCondition = value; if (value == START_ASAP) startupTime = QDateTime(); updateJobCell(); } void SchedulerJob::setStartupTime(const QDateTime &value) { startupTime = value; if (value.isValid()) startupCondition = START_AT; /* Refresh estimated time - which update job cells */ setEstimatedTime(estimatedTime); } void SchedulerJob::setSequenceFile(const QUrl &value) { sequenceFile = value; } void SchedulerJob::setFITSFile(const QUrl &value) { fitsFile = value; } void SchedulerJob::setMinAltitude(const double &value) { minAltitude = value; } void SchedulerJob::setMinMoonSeparation(const double &value) { minMoonSeparation = value; } void SchedulerJob::setEnforceWeather(bool value) { enforceWeather = value; } void SchedulerJob::setCompletionTime(const QDateTime &value) { /* If argument completion time is valid, automatically switch condition to FINISH_AT */ if (value.isValid()) { setCompletionCondition(FINISH_AT); completionTime = value; } /* If completion time is not valid, but startup time is, deduce completion from startup and duration */ else if (startupTime.isValid()) { completionTime = startupTime.addSecs(estimatedTime); } /* Refresh estimated time - which update job cells */ setEstimatedTime(estimatedTime); } void SchedulerJob::setCompletionCondition(const CompletionCondition &value) { completionCondition = value; updateJobCell(); } void SchedulerJob::setStepPipeline(const StepPipeline &value) { stepPipeline = value; } void SchedulerJob::setState(const JOBStatus &value) { state = value; /* FIXME: move this to Scheduler, SchedulerJob is mostly a model */ if (JOB_ERROR == state) KNotification::event(QLatin1String("EkosSchedulerJobFail"), i18n("Ekos job failed (%1)", getName())); /* If job becomes invalid, automatically reset its startup characteristics, and force its duration to be reestimated */ if (JOB_INVALID == value) { setStartupCondition(fileStartupCondition); setStartupTime(fileStartupTime); setEstimatedTime(-1); } /* If job is aborted, automatically reset its startup characteristics */ if (JOB_ABORTED == value) { setStartupCondition(fileStartupCondition); /* setStartupTime(fileStartupTime); */ } updateJobCell(); } void SchedulerJob::setScore(int value) { score = value; updateJobCell(); } void SchedulerJob::setCulminationOffset(const int16_t &value) { culminationOffset = value; } void SchedulerJob::setSequenceCount(const int count) { sequenceCount = count; updateJobCell(); } void SchedulerJob::setNameCell(QTableWidgetItem *value) { nameCell = value; updateJobCell(); } void SchedulerJob::setCompletedCount(const int count) { completedCount = count; updateJobCell(); } void SchedulerJob::setStatusCell(QTableWidgetItem *value) { statusCell = value; updateJobCell(); if (statusCell) statusCell->setToolTip(i18n("Current status of job '%1', managed by the Scheduler.\n" "If invalid, the Scheduler was not able to find a proper observation time for the target.\n" "If aborted, the Scheduler missed the scheduled time or encountered transitory issues and will reschedule the job.\n" "If complete, the Scheduler verified that all sequence captures requested were stored, including repeats.", name)); } void SchedulerJob::setStartupCell(QTableWidgetItem *value) { startupCell = value; updateJobCell(); if (startupCell) startupCell->setToolTip(i18n("Startup time for job '%1', as estimated by the Scheduler.\n" "Fixed time from user or culmination time is marked with a chronometer symbol. ", name)); } void SchedulerJob::setCompletionCell(QTableWidgetItem *value) { completionCell = value; updateJobCell(); if (completionCell) completionCell->setToolTip(i18n("Completion time for job '%1', as estimated by the Scheduler.\n" "Can be specified by the user to limit duration of looping jobs.\n" "Fixed time from user is marked with a chronometer symbol. ", name)); } void SchedulerJob::setCaptureCountCell(QTableWidgetItem *value) { captureCountCell = value; updateJobCell(); if (captureCountCell) captureCountCell->setToolTip(i18n("Count of captures stored for job '%1', based on its sequence job.\n" "This is a summary, additional specific frame types may be required to complete the job.", name)); } void SchedulerJob::setScoreCell(QTableWidgetItem *value) { scoreCell = value; updateJobCell(); if (scoreCell) scoreCell->setToolTip(i18n("Current score for job '%1', from its altitude, moon separation and sky darkness.\n" "Negative if adequate altitude is not achieved yet or if there is no proper observation time today.\n" "The Scheduler will refresh scores when picking a new candidate job.", name)); } void SchedulerJob::setDateTimeDisplayFormat(const QString &value) { dateTimeDisplayFormat = value; updateJobCell(); } void SchedulerJob::setStage(const JOBStage &value) { stage = value; updateJobCell(); } void SchedulerJob::setStageCell(QTableWidgetItem *cell) { stageCell = cell; updateJobCell(); } void SchedulerJob::setStageLabel(QLabel *label) { stageLabel = label; updateJobCell(); } void SchedulerJob::setFileStartupCondition(const StartupCondition &value) { fileStartupCondition = value; } void SchedulerJob::setFileStartupTime(const QDateTime &value) { fileStartupTime = value; } void SchedulerJob::setEstimatedTime(const int64_t &value) { /* If startup and completion times are fixed, estimated time cannot change */ if (START_AT == startupCondition && FINISH_AT == completionCondition) { estimatedTime = startupTime.secsTo(completionTime); } /* If startup time is fixed but not completion time, estimated time adjusts completion time */ else if (START_AT == startupCondition) { estimatedTime = value; completionTime = startupTime.addSecs(value); } /* If completion time is fixed but not startup time, estimated time adjusts startup time */ /* FIXME: adjusting startup time will probably not work, because jobs are scheduled from first available altitude */ else if (FINISH_AT == completionCondition) { estimatedTime = value; startupTime = completionTime.addSecs(-value); } /* Else estimated time is simply stored as is */ else estimatedTime = value; updateJobCell(); } void SchedulerJob::setInSequenceFocus(bool value) { inSequenceFocus = value; } void SchedulerJob::setPriority(const uint8_t &value) { priority = value; } void SchedulerJob::setEnforceTwilight(bool value) { enforceTwilight = value; } void SchedulerJob::setEstimatedTimeCell(QTableWidgetItem *value) { estimatedTimeCell = value; updateJobCell(); if (estimatedTimeCell) estimatedTimeCell->setToolTip(i18n("Duration job '%1' will take to complete when started, as estimated by the Scheduler.\n" "Depends on the actions to be run, and the sequence job to be processed.", name)); } void SchedulerJob::setLightFramesRequired(bool value) { lightFramesRequired = value; } void SchedulerJob::setRepeatsRequired(const uint16_t &value) { repeatsRequired = value; updateJobCell(); } void SchedulerJob::setRepeatsRemaining(const uint16_t &value) { repeatsRemaining = value; updateJobCell(); } void SchedulerJob::setCapturedFramesMap(const QMap &value) { capturedFramesMap = value; } void SchedulerJob::setTargetCoords(dms& ra, dms& dec) { targetCoords.setRA0(ra); targetCoords.setDec0(dec); targetCoords.updateCoordsNow(KStarsData::Instance()->updateNum()); } void SchedulerJob::updateJobCell() { if (nameCell) { nameCell->setText(name); nameCell->tableWidget()->resizeColumnToContents(nameCell->column()); } if (nameLabel) { nameLabel->setText(name + QString(":")); } if (statusCell) { static QMap stateStrings; static QString stateStringUnknown; if (stateStrings.isEmpty()) { stateStrings[JOB_IDLE] = i18n("Idle"); stateStrings[JOB_EVALUATION] = i18n("Evaluating"); stateStrings[JOB_SCHEDULED] = i18n("Scheduled"); stateStrings[JOB_BUSY] = i18n("Running"); stateStrings[JOB_INVALID] = i18n("Invalid"); stateStrings[JOB_COMPLETE] = i18n("Complete"); stateStrings[JOB_ABORTED] = i18n("Aborted"); stateStrings[JOB_ERROR] = i18n("Error"); stateStringUnknown = i18n("Unknown"); } statusCell->setText(stateStrings.value(state, stateStringUnknown)); statusCell->tableWidget()->resizeColumnToContents(statusCell->column()); } if (stageCell || stageLabel) { /* Translated string cache - overkill, probably, and doesn't warn about missing enums like switch/case should ; also, not thread-safe */ /* FIXME: this should work with a static initializer in C++11, but QT versions are touchy on this, and perhaps i18n can't be used? */ static QMap stageStrings; static QString stageStringUnknown; if (stageStrings.isEmpty()) { stageStrings[STAGE_IDLE] = i18n("Idle"); stageStrings[STAGE_SLEWING] = i18n("Slewing"); stageStrings[STAGE_SLEW_COMPLETE] = i18n("Slew complete"); stageStrings[STAGE_FOCUSING] = stageStrings[STAGE_POSTALIGN_FOCUSING] = i18n("Focusing"); stageStrings[STAGE_FOCUS_COMPLETE] = stageStrings[STAGE_POSTALIGN_FOCUSING_COMPLETE ] = i18n("Focus complete"); stageStrings[STAGE_ALIGNING] = i18n("Aligning"); stageStrings[STAGE_ALIGN_COMPLETE] = i18n("Align complete"); stageStrings[STAGE_RESLEWING] = i18n("Repositioning"); stageStrings[STAGE_RESLEWING_COMPLETE] = i18n("Repositioning complete"); /*stageStrings[STAGE_CALIBRATING] = i18n("Calibrating");*/ stageStrings[STAGE_GUIDING] = i18n("Guiding"); stageStrings[STAGE_GUIDING_COMPLETE] = i18n("Guiding complete"); stageStrings[STAGE_CAPTURING] = i18n("Capturing"); stageStringUnknown = i18n("Unknown"); } if (stageCell) { stageCell->setText(stageStrings.value(stage, stageStringUnknown)); stageCell->tableWidget()->resizeColumnToContents(stageCell->column()); } if (stageLabel) { stageLabel->setText(QString("%1: %2").arg(name).arg(stageStrings.value(stage, stageStringUnknown))); } } if (startupCell && startupCell->tableWidget()) { /* Display a startup time if job is running, scheduled to run or about to be re-scheduled */ if (JOB_SCHEDULED == state || JOB_BUSY == state || JOB_ABORTED == state) switch (fileStartupCondition) { /* If the original condition is START_AT/START_CULMINATION, startup time is fixed */ case START_AT: case START_CULMINATION: startupCell->setText(startupTime.toString(dateTimeDisplayFormat)); startupCell->setIcon(QIcon::fromTheme("chronometer")); break; /* If the original condition is START_ASAP, startup time is informational */ case START_ASAP: startupCell->setText(startupTime.toString(dateTimeDisplayFormat)); startupCell->setIcon(QIcon()); break; /* Else do not display any startup time */ default: startupCell->setText(QString()); startupCell->setIcon(QIcon()); break; } /* Display a missed startup time if job is invalid */ else if (JOB_INVALID == state && START_AT == fileStartupCondition) { startupCell->setText(startupTime.toString(dateTimeDisplayFormat)); startupCell->setIcon(QIcon::fromTheme("chronometer")); } /* Else do not display any startup time */ else { startupCell->setText(QString()); startupCell->setIcon(QIcon()); } startupCell->tableWidget()->resizeColumnToContents(startupCell->column()); } if (completionCell && completionCell->tableWidget()) { /* Display a completion time if job is running, scheduled to run or about to be re-scheduled */ if (JOB_SCHEDULED == state || JOB_BUSY == state || JOB_ABORTED == state) switch (completionCondition) { case FINISH_LOOP: completionCell->setText(QString("-")); completionCell->setIcon(QIcon()); break; case FINISH_AT: completionCell->setText(completionTime.toString(dateTimeDisplayFormat)); completionCell->setIcon(QIcon::fromTheme("chronometer")); break; case FINISH_SEQUENCE: case FINISH_REPEAT: default: completionCell->setText(completionTime.toString(dateTimeDisplayFormat)); completionCell->setIcon(QIcon()); break; } /* Else do not display any completion time */ else { completionCell->setText(QString()); completionCell->setIcon(QIcon()); } completionCell->tableWidget()->resizeColumnToContents(completionCell->column()); } if (estimatedTimeCell && estimatedTimeCell->tableWidget()) { if (0 < estimatedTime) /* Seconds to ms - this doesn't follow dateTimeDisplayFormat, which renders YMD too */ estimatedTimeCell->setText(QTime::fromMSecsSinceStartOfDay(estimatedTime*1000).toString("HH:mm:ss")); else if(0 == estimatedTime) /* FIXME: this special case could be merged with the previous, kept for future to indicate actual duration */ estimatedTimeCell->setText("00:00:00"); else /* Invalid marker */ estimatedTimeCell->setText("-"); estimatedTimeCell->tableWidget()->resizeColumnToContents(estimatedTimeCell->column()); } if (captureCountCell && captureCountCell->tableWidget()) { captureCountCell->setText(QString("%1/%2").arg(completedCount).arg(sequenceCount)); captureCountCell->tableWidget()->resizeColumnToContents(captureCountCell->column()); } if (scoreCell && scoreCell->tableWidget()) { if (0 <= score) scoreCell->setText(QString("%1").arg(score)); else /* FIXME: negative scores are just weird for the end-user */ scoreCell->setText(QString("<0")); scoreCell->tableWidget()->resizeColumnToContents(scoreCell->column()); } } void SchedulerJob::reset() { state = JOB_IDLE; stage = STAGE_IDLE; estimatedTime = -1; startupCondition = fileStartupCondition; startupTime = fileStartupCondition == START_AT ? fileStartupTime : QDateTime(); /* No change to culmination offset */ repeatsRemaining = repeatsRequired; } bool SchedulerJob::decreasingScoreOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return job1->getScore() > job2->getScore(); } bool SchedulerJob::increasingPriorityOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return job1->getPriority() < job2->getPriority(); } bool SchedulerJob::decreasingAltitudeOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return Ekos::Scheduler::findAltitude(job1->getTargetCoords(), job1->getStartupTime()) > Ekos::Scheduler::findAltitude(job2->getTargetCoords(), job2->getStartupTime()); } bool SchedulerJob::increasingStartupTimeOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return job1->getStartupTime() < job2->getStartupTime(); } diff --git a/kstars/ekos/scheduler/schedulerjob.h b/kstars/ekos/scheduler/schedulerjob.h index 10328a4af..0a8f235c2 100644 --- a/kstars/ekos/scheduler/schedulerjob.h +++ b/kstars/ekos/scheduler/schedulerjob.h @@ -1,414 +1,414 @@ /* Ekos Scheduler Job Copyright (C) Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "skypoint.h" #include #include class QTableWidgetItem; class QLabel; class dms; class SchedulerJob { public: - SchedulerJob(); + SchedulerJob() = default; /** @brief States of a SchedulerJob. */ typedef enum { JOB_IDLE, /**< Job was just created, and is not evaluated yet */ JOB_EVALUATION, /**< Job is being evaluated */ JOB_SCHEDULED, /**< Job was evaluated, and has a schedule */ JOB_BUSY, /**< Job is being processed */ JOB_ERROR, /**< Job encountered a fatal issue while processing, and must be reset manually */ JOB_ABORTED, /**< Job encountered a transitory issue while processing, and will be rescheduled */ JOB_INVALID, /**< Job has an incorrect configuration, and cannot proceed */ JOB_COMPLETE /**< Job finished all required captures */ } JOBStatus; /** @brief Running stages of a SchedulerJob. */ typedef enum { STAGE_IDLE, STAGE_SLEWING, STAGE_SLEW_COMPLETE, STAGE_FOCUSING, STAGE_FOCUS_COMPLETE, STAGE_ALIGNING, STAGE_ALIGN_COMPLETE, STAGE_RESLEWING, STAGE_RESLEWING_COMPLETE, STAGE_POSTALIGN_FOCUSING, STAGE_POSTALIGN_FOCUSING_COMPLETE, STAGE_GUIDING, STAGE_GUIDING_COMPLETE, STAGE_CAPTURING, STAGE_COMPLETE } JOBStage; /** @brief Conditions under which a SchedulerJob may start. */ typedef enum { START_ASAP, START_CULMINATION, START_AT } StartupCondition; /** @brief Conditions under which a SchedulerJob may complete. */ typedef enum { FINISH_SEQUENCE, FINISH_REPEAT, FINISH_LOOP, FINISH_AT } CompletionCondition; /** @brief Actions that may be processed when running a SchedulerJob. * FIXME: StepPipeLine is actually a mask, change this into a bitfield. */ typedef enum { USE_NONE = 0, USE_TRACK = 1 << 0, USE_FOCUS = 1 << 1, USE_ALIGN = 1 << 2, USE_GUIDE = 1 << 3 } StepPipeline; /** @brief Coordinates of the target of this job. */ /** @{ */ SkyPoint const & getTargetCoords() const { return targetCoords; } void setTargetCoords(dms& ra, dms& dec); /** @} */ /** @brief Capture sequence this job uses while running. */ /** @{ */ QUrl getSequenceFile() const { return sequenceFile; } void setSequenceFile(const QUrl &value); /** @} */ /** @brief FITS file whose plate solve produces target coordinates. */ /** @{ */ QUrl getFITSFile() const { return fitsFile; } void setFITSFile(const QUrl &value); /** @} */ /** @brief Minimal target altitude to process this job */ /** @{ */ double getMinAltitude() const { return minAltitude; } void setMinAltitude(const double &value); /** @} */ /** @brief Minimal Moon separation to process this job. */ /** @{ */ double getMinMoonSeparation() const { return minMoonSeparation; } void setMinMoonSeparation(const double &value); /** @} */ /** @brief Whether to restrict this job to good weather. */ /** @{ */ bool getEnforceWeather() const { return enforceWeather; } void setEnforceWeather(bool value); /** @} */ /** @brief Mask of actions to process for this job. */ /** @{ */ StepPipeline getStepPipeline() const { return stepPipeline; } void setStepPipeline(const StepPipeline &value); /** @} */ /** @brief Condition under which this job starts. */ /** @{ */ StartupCondition getStartupCondition() const { return startupCondition; } void setStartupCondition(const StartupCondition &value); /** @} */ /** @brief Condition under which this job completes. */ /** @{ */ CompletionCondition getCompletionCondition() const { return completionCondition; } void setCompletionCondition(const CompletionCondition &value); /** @} */ /** @brief Target culmination proximity under which this job starts. */ /** @{ */ int16_t getCulminationOffset() const { return culminationOffset; } void setCulminationOffset(const int16_t &value); /** @} */ /** @brief Timestamp format to use when displaying information about this job. */ /** @{ */ QString const & getDateTimeDisplayFormat() const { return dateTimeDisplayFormat; } void setDateTimeDisplayFormat(const QString &value); /** @} */ /** @brief Original startup condition, as entered by the user. */ /** @{ */ StartupCondition getFileStartupCondition() const { return fileStartupCondition; } void setFileStartupCondition(const StartupCondition &value); /** @} */ /** @brief Original time at which the job must start, as entered by the user. */ /** @{ */ QDateTime getFileStartupTime() const { return fileStartupTime; } void setFileStartupTime(const QDateTime &value); /** @} */ /** @brief Whether this job requires re-focus while running its capture sequence. */ /** @{ */ bool getInSequenceFocus() const { return inSequenceFocus; } void setInSequenceFocus(bool value); /** @} */ /** @brief Job priority, low priority value means most prioritary. */ /** @{ */ uint8_t getPriority() const { return priority; } void setPriority(const uint8_t &value); /** @} */ /** @brief Whether to restrict job to night time. */ /** @{ */ bool getEnforceTwilight() const { return enforceTwilight; } void setEnforceTwilight(bool value); /** @} */ /** @brief Current name of the scheduler job. */ /** @{ */ QString getName() const { return name; } void setName(const QString &value); /** @} */ /** @brief Shortcut to widget cell for job name in the job queue table. */ /** @{ */ QTableWidgetItem *getNameCell() const { return nameCell; } void setNameCell(QTableWidgetItem *cell); /** @} */ /** @brief Current state of the scheduler job. * Setting state to JOB_ABORTED automatically resets the startup characteristics. * Setting state to JOB_INVALID automatically resets the startup characteristics and the duration estimation. * @see SchedulerJob::setStartupCondition, SchedulerJob::setFileStartupCondition, SchedulerJob::setStartupTime * and SchedulerJob::setFileStartupTime. */ /** @{ */ JOBStatus getState() const { return state; } void setState(const JOBStatus &value); /** @} */ /** @brief Shortcut to widget cell for job state in the job queue table. */ /** @{ */ QTableWidgetItem *getStatusCell() const { return statusCell; } void setStatusCell(QTableWidgetItem *cell); /** @} */ /** @brief Current stage of the scheduler job. */ /** @{ */ JOBStage getStage() const { return stage; } void setStage(const JOBStage &value); /** @} */ /** @brief Shortcut to widget cell for job stage in the job queue table. */ /** @{ */ QTableWidgetItem *getStageCell() const { return stageCell; } void setStageCell(QTableWidgetItem *cell); QLabel *getStageLabel() const { return stageLabel; } void setStageLabel(QLabel *label); /** @} */ /** @brief Number of captures required in the associated sequence. */ /** @{ */ int getSequenceCount() const { return sequenceCount; } void setSequenceCount(const int count); /** @} */ /** @brief Number of captures completed in the associated sequence. */ /** @{ */ int getCompletedCount() const { return completedCount; } void setCompletedCount(const int count); /** @} */ /** @brief Shortcut to widget cell for captures in the job queue table. */ /** @{ */ QTableWidgetItem *getCaptureCountCell() const { return captureCountCell; } void setCaptureCountCell(QTableWidgetItem *value); /** @} */ /** @brief Time at which the job must start. */ /** @{ */ QDateTime getStartupTime() const { return startupTime; } void setStartupTime(const QDateTime &value); /** @} */ /** @brief Shortcut to widget cell for startup time in the job queue table. */ /** @{ */ QTableWidgetItem *getStartupCell() const { return startupCell; } void setStartupCell(QTableWidgetItem *value); /** @} */ /** @brief Time after which the job is considered complete. */ /** @{ */ QDateTime getCompletionTime() const { return completionTime; } void setCompletionTime(const QDateTime &value); /** @} */ /** @brief Shortcut to widget cell for completion time in the job queue table. */ /** @{ */ QTableWidgetItem *getCompletionCell() const { return completionCell; } void setCompletionCell(QTableWidgetItem *value); /** @} */ /** @brief Estimation of the time the job will take to process. */ /** @{ */ int64_t getEstimatedTime() const { return estimatedTime; } void setEstimatedTime(const int64_t &value); /** @} */ /** @brief Shortcut to widget cell for estimated time in the job queue table. */ /** @{ */ QTableWidgetItem *getEstimatedTimeCell() const { return estimatedTimeCell; } void setEstimatedTimeCell(QTableWidgetItem *value); /** @} */ /** @brief Current score of the scheduler job. */ /** @{ */ int getScore() const { return score; } void setScore(int value); /** @} */ /** @brief Shortcut to widget cell for job score in the job queue table. */ /** @{ */ QTableWidgetItem *getScoreCell() const { return scoreCell; } void setScoreCell(QTableWidgetItem *value); /** @} */ /** @brief Whether this job requires light frames, or only calibration frames. */ /** @{ */ bool getLightFramesRequired() const { return lightFramesRequired; } void setLightFramesRequired(bool value); /** @} */ /** @brief Number of times this job must be repeated (in terms of capture count). */ /** @{ */ uint16_t getRepeatsRequired() const { return repeatsRequired; } void setRepeatsRequired(const uint16_t &value); /** @} */ /** @brief Number of times this job still has to be repeated (in terms of capture count). */ /** @{ */ uint16_t getRepeatsRemaining() const { return repeatsRemaining; } void setRepeatsRemaining(const uint16_t &value); /** @} */ /** @brief The map of capture counts for this job, keyed by its capture storage signatures. */ /** @{ */ QMap getCapturedFramesMap() const { return capturedFramesMap; } void setCapturedFramesMap(const QMap &value); /** @} */ /** @brief Resetting a job to original values: * - idle state and stage * - original startup, none if asap, else user original setting * - duration not estimated * - full repeat count */ void reset(); /** @brief Determining whether a SchedulerJob is a duplicate of another. * @param a_job is the other SchedulerJob to test duplication against. * @return True if objects are different, but name and sequence file are identical, else false. * @fixme This is a weak comparison, but that's what the scheduler looks at to decide completion. */ bool isDuplicateOf(SchedulerJob const *a_job) const { return this != a_job && name == a_job->name && sequenceFile == a_job->sequenceFile; } /** @brief Compare ::SchedulerJob instances based on score. This is a qSort predicate, deprecated in QT5. * @arg a, b are ::SchedulerJob instances to compare. * @return true if the score of b is lower than the score of a. * @return false if the score of b is higher than or equal to the score of a. */ static bool decreasingScoreOrder(SchedulerJob const *a, SchedulerJob const *b); /** @brief Compare ::SchedulerJob instances based on priority. This is a qSort predicate, deprecated in QT5. * @arg a, b are ::SchedulerJob instances to compare. * @return true if the priority of a is lower than the priority of b. * @return false if the priority of a is higher than or equal to the priority of b. */ static bool increasingPriorityOrder(SchedulerJob const *a, SchedulerJob const *b); /** @brief Compare ::SchedulerJob instances based on altitude. This is a qSort predicate, deprecated in QT5. * @arg a, b are ::SchedulerJob instances to compare. * @return true if the altitude of b is lower than the altitude of a. * @return false if the altitude of b is higher than or equal to the altitude of a. */ static bool decreasingAltitudeOrder(SchedulerJob const *a, SchedulerJob const *b); /** @brief Compare ::SchedulerJob instances based on startup time. This is a qSort predicate, deprecated in QT5. * @arg a, b are ::SchedulerJob instances to compare. * @return true if the startup time of a is sooner than the priority of b. * @return false if the startup time of a is later than or equal to the priority of b. */ static bool increasingStartupTimeOrder(SchedulerJob const *a, SchedulerJob const *b); private: QString name; SkyPoint targetCoords; JOBStatus state { JOB_IDLE }; JOBStage stage { STAGE_IDLE }; StartupCondition fileStartupCondition { START_ASAP }; StartupCondition startupCondition { START_ASAP }; CompletionCondition completionCondition { FINISH_SEQUENCE }; int sequenceCount { 0 }; int completedCount { 0 }; QDateTime fileStartupTime; QDateTime startupTime; QDateTime completionTime; QUrl sequenceFile; QUrl fitsFile; double minAltitude { -1 }; double minMoonSeparation { -1 }; bool enforceWeather { false }; bool enforceTwilight { false }; StepPipeline stepPipeline { USE_NONE }; /** @internal Widget cell/label shortcuts. */ /** @{ */ QTableWidgetItem *nameCell { nullptr }; QLabel *nameLabel { nullptr }; QTableWidgetItem *statusCell { nullptr }; QTableWidgetItem *stageCell { nullptr }; QLabel *stageLabel { nullptr }; QTableWidgetItem *startupCell { nullptr }; QTableWidgetItem *completionCell { nullptr }; QTableWidgetItem *estimatedTimeCell { nullptr }; QTableWidgetItem *captureCountCell { nullptr }; QTableWidgetItem *scoreCell { nullptr }; /** @} */ /** @internal General cell refresh. */ void updateJobCell(); int score { 0 }; int16_t culminationOffset { 0 }; uint8_t priority { 10 }; int64_t estimatedTime { -1 }; uint16_t repeatsRequired { 1 }; uint16_t repeatsRemaining { 1 }; bool inSequenceFocus { false }; QString dateTimeDisplayFormat; bool lightFramesRequired { false }; QMap capturedFramesMap; }; diff --git a/kstars/fitsviewer/fitsdebayer.cpp b/kstars/fitsviewer/fitsdebayer.cpp index 1f5b90b68..d25a493a6 100644 --- a/kstars/fitsviewer/fitsdebayer.cpp +++ b/kstars/fitsviewer/fitsdebayer.cpp @@ -1,80 +1,76 @@ /* FITS Debayer class Copyright (C) 2015 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "fitsdebayer.h" #include "fitsdata.h" #include "fitsview.h" #include "fitsviewer.h" debayerUI::debayerUI(QDialog *parent) : QDialog(parent) { setupUi(parent); setModal(false); } FITSDebayer::FITSDebayer(FITSViewer *parent) : QDialog(parent) { ui = new debayerUI(this); viewer = parent; connect(ui->buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(applyDebayer())); } -FITSDebayer::~FITSDebayer() -{ -} - void FITSDebayer::applyDebayer() { FITSView *view = viewer->getCurrentView(); if (view) { FITSData *image_data = view->getImageData(); dc1394bayer_method_t method = (dc1394bayer_method_t)ui->methodCombo->currentIndex(); dc1394color_filter_t filter = (dc1394color_filter_t)(ui->filterCombo->currentIndex() + 512); int offsetX = ui->XOffsetSpin->value(); int offsetY = ui->YOffsetSpin->value(); BayerParams param; param.method = method; param.filter = filter; param.offsetX = offsetX; param.offsetY = offsetY; image_data->setBayerParams(¶m); ui->statusEdit->setText(i18n("Processing...")); qApp->processEvents(); if (image_data->debayer()) { ui->statusEdit->setText(i18n("Complete.")); view->rescale(ZOOM_KEEP_LEVEL); view->updateFrame(); } else ui->statusEdit->setText(i18n("Debayer failed.")); } } void FITSDebayer::setBayerParams(BayerParams *param) { ui->methodCombo->setCurrentIndex(param->method); ui->filterCombo->setCurrentIndex(param->filter - 512); ui->XOffsetSpin->setValue(param->offsetX); ui->YOffsetSpin->setValue(param->offsetY); } diff --git a/kstars/fitsviewer/fitsdebayer.h b/kstars/fitsviewer/fitsdebayer.h index 7d9dc335e..3a20692a8 100644 --- a/kstars/fitsviewer/fitsdebayer.h +++ b/kstars/fitsviewer/fitsdebayer.h @@ -1,43 +1,43 @@ /* FITS Debayer class Copyright (C) 2015 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "bayer.h" #include "ui_fitsdebayer.h" class FITSViewer; class debayerUI : public QDialog, public Ui::FITSDebayerDialog { Q_OBJECT public: explicit debayerUI(QDialog *parent = 0); }; class FITSDebayer : public QDialog { Q_OBJECT public: explicit FITSDebayer(FITSViewer *parent); - ~FITSDebayer(); + virtual ~FITSDebayer() override = default; void setBayerParams(BayerParams *param); public slots: void applyDebayer(); private: FITSViewer *viewer { nullptr }; debayerUI *ui { nullptr }; }; diff --git a/kstars/fitsviewer/fitslabel.cpp b/kstars/fitsviewer/fitslabel.cpp index 7699475f1..8ecd9212b 100644 --- a/kstars/fitsviewer/fitslabel.cpp +++ b/kstars/fitsviewer/fitslabel.cpp @@ -1,371 +1,367 @@ /* FITS Label Copyright (C) 2003-2017 Jasem Mutlaq Copyright (C) 2016-2017 Robert Lancaster This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "fitslabel.h" #include "config-kstars.h" #include "fitsdata.h" #include "fitsview.h" #include "kspopupmenu.h" #include "kstars.h" #include "kstarsdata.h" #include "Options.h" #include "skymap.h" #ifdef HAVE_INDI #include "basedevice.h" #include "indi/indilistener.h" #endif #include #define BASE_OFFSET 50 #define ZOOM_DEFAULT 100.0 #define ZOOM_MIN 10 #define ZOOM_MAX 400 #define ZOOM_LOW_INCR 10 #define ZOOM_HIGH_INCR 50 FITSLabel::FITSLabel(FITSView *view, QWidget *parent) : QLabel(parent) { this->view = view; } -FITSLabel::~FITSLabel() -{ -} - void FITSLabel::setSize(double w, double h) { width = w; height = h; size = w * h; } bool FITSLabel::getMouseButtonDown() { return mouseButtonDown; } /** This method was added to make the panning function work. If the mouse button is released, it resets mouseButtonDown variable and the mouse cursor. */ void FITSLabel::mouseReleaseEvent(QMouseEvent *e) { Q_UNUSED(e); if (view->getCursorMode() == FITSView::dragCursor) { mouseButtonDown = false; view->updateMouseCursor(); } } /** I added some things to the top of this method to allow panning and Scope slewing to function. If you are in the dragMouse mode and the mousebutton is pressed, The method checks the difference between the location of the last point stored and the current event point to see how the mouse has moved. Then it moves the scrollbars and thus the view to the right location. Then it stores the current point so next time it can do it again. */ void FITSLabel::mouseMoveEvent(QMouseEvent *e) { float scale = (view->getCurrentZoom() / ZOOM_DEFAULT); if (view->getCursorMode() == FITSView::dragCursor && mouseButtonDown) { QPoint newPoint = e->globalPos(); int dx = newPoint.x() - lastMousePoint.x(); int dy = newPoint.y() - lastMousePoint.y(); view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() - dx); view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() - dy); lastMousePoint = newPoint; } double x, y; FITSData *view_data = view->getImageData(); uint8_t *buffer = view_data->getImageBuffer(); if (buffer == nullptr) return; x = round(e->x() / scale); y = round(e->y() / scale); 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; QString stringValue; switch (view_data->getDataType()) { case TBYTE: stringValue = QLocale().toString(buffer[(int)(y * width + x)]); break; case TSHORT: stringValue = QLocale().toString((reinterpret_cast(buffer))[(int)(y * width + x)]); break; case TUSHORT: stringValue = QLocale().toString((reinterpret_cast(buffer))[(int)(y * width + x)]); break; case TLONG: stringValue = QLocale().toString((reinterpret_cast(buffer))[(int)(y * width + x)]); break; case TULONG: stringValue = QLocale().toString((reinterpret_cast(buffer))[(int)(y * width + x)]); break; case TFLOAT: stringValue = QLocale().toString((reinterpret_cast(buffer))[(int)(y * width + x)], 'f', 5); break; case TLONGLONG: stringValue = QLocale().toString(static_cast((reinterpret_cast(buffer))[(int)(y * width + x)])); break; case TDOUBLE: stringValue = QLocale().toString((reinterpret_cast(buffer))[(int)(y * width + x)], 'f', 5); break; default: break; } emit newStatus(stringValue, FITS_VALUE); if (view_data->hasWCS() && view->getCursorMode() != FITSView::selectCursor) { int index = x + y * width; wcs_point *wcs_coord = view_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(), dec.toDMSString()), FITS_WCS); } bool objFound = false; foreach (FITSSkyObject *listObject, view_data->objList) { if ((std::abs(listObject->x() - x) < 5 / scale) && (std::abs(listObject->y() - y) < 5 / scale)) { QToolTip::showText(e->globalPos(), listObject->skyObject()->name() + '\n' + listObject->skyObject()->longname(), this); objFound = true; break; } } if (!objFound) QToolTip::hideText(); } double HFR = view->getImageData()->getHFR(x, y); if (HFR > 0) QToolTip::showText(e->globalPos(), i18nc("Half Flux Radius", "HFR: %1", QString::number(HFR, 'g', 3)), this); //setCursor(Qt::CrossCursor); e->accept(); } /** I added some things to the top of this method to allow panning and Scope slewing to function. If in dragMouse mode, the Panning function works by storing the cursor position when the mouse was pressed and setting the mouseButtonDown variable to true. If in ScopeMouse mode and the mouse is clicked, if there is WCS data and a scope is available, the method will verify that you actually do want to slew to the WCS coordinates associated with the click location. If so, it calls the centerTelescope function. */ void FITSLabel::mousePressEvent(QMouseEvent *e) { float scale = (view->getCurrentZoom() / ZOOM_DEFAULT); if (view->getCursorMode() == FITSView::dragCursor) { mouseButtonDown = true; lastMousePoint = e->globalPos(); view->updateMouseCursor(); } else if (e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::scopeCursor) { #ifdef HAVE_INDI FITSData *view_data = view->getImageData(); if (view_data->hasWCS()) { wcs_point *wcs_coord = view_data->getWCSCoord(); if (wcs_coord) { double x, y; x = round(e->x() / scale); y = round(e->y() / scale); x = KSUtils::clamp(x, 1.0, width); y = KSUtils::clamp(y, 1.0, height); int index = x + y * width; if (KMessageBox::Continue == KMessageBox::warningContinueCancel( nullptr, "Slewing to Coordinates: \nRA: " + dms(wcs_coord[index].ra).toHMSString() + "\nDec: " + dms(wcs_coord[index].dec).toDMSString(), i18n("Continue Slew"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "continue_slew_warning")) { centerTelescope(wcs_coord[index].ra / 15.0, wcs_coord[index].dec); view->setCursorMode(view->lastMouseMode); view->updateScopeButton(); } } } #endif } double x, y; x = round(e->x() / scale); y = round(e->y() / scale); x = KSUtils::clamp(x, 1.0, width); y = KSUtils::clamp(y, 1.0, height); #ifdef HAVE_INDI FITSData *view_data = view->getImageData(); if (e->buttons() & Qt::RightButton && view->getCursorMode() != FITSView::scopeCursor) { mouseReleaseEvent(e); if (view_data->hasWCS()) { foreach (FITSSkyObject *listObject, view_data->objList) { if ((std::abs(listObject->x() - x) < 5 / scale) && (std::abs(listObject->y() - y) < 5 / scale)) { SkyObject *object = listObject->skyObject(); KSPopupMenu *pmenu; pmenu = new KSPopupMenu(); object->initPopupMenu(pmenu); QList actions = pmenu->actions(); foreach (QAction *action, actions) { if (action->text().left(7) == "Starhop") pmenu->removeAction(action); if (action->text().left(7) == "Angular") pmenu->removeAction(action); if (action->text().left(8) == "Add flag") pmenu->removeAction(action); if (action->text().left(12) == "Attach Label") pmenu->removeAction(action); } pmenu->popup(e->globalPos()); KStars::Instance()->map()->setClickedObject(object); break; } } } if (fabs(view->markerCrosshair.x() - x) <= 15 && fabs(view->markerCrosshair.y() - y) <= 15) emit markerSelected(0, 0); } #endif if (e->buttons() & Qt::LeftButton) { if (view->getCursorMode() == FITSView::selectCursor) emit pointSelected(x, y); else if (view->getCursorMode() == FITSView::crosshairCursor) emit pointSelected(x+5/scale, y+5/scale); } } void FITSLabel::mouseDoubleClickEvent(QMouseEvent *e) { double x, y; x = round(e->x() / (view->getCurrentZoom() / ZOOM_DEFAULT)); y = round(e->y() / (view->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 == nullptr) 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 } diff --git a/kstars/fitsviewer/fitslabel.h b/kstars/fitsviewer/fitslabel.h index d24d192d5..a6667431c 100644 --- a/kstars/fitsviewer/fitslabel.h +++ b/kstars/fitsviewer/fitslabel.h @@ -1,55 +1,55 @@ /* FITS Label Copyright (C) 2003-2017 Jasem Mutlaq Copyright (C) 2016-2017 Robert Lancaster This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "dms.h" #include "fitscommon.h" #include #include class FITSView; class QMouseEvent; class QString; class FITSLabel : public QLabel { Q_OBJECT public: explicit FITSLabel(FITSView *img, QWidget *parent = nullptr); - virtual ~FITSLabel(); + virtual ~FITSLabel() override = default; void setSize(double w, double h); void centerTelescope(double raJ2000, double decJ2000); bool getMouseButtonDown(); protected: - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e) override; + virtual void mousePressEvent(QMouseEvent *e) override; + virtual void mouseReleaseEvent(QMouseEvent *e) override; + virtual void mouseDoubleClickEvent(QMouseEvent *e) override; private: bool mouseButtonDown { false }; QPoint lastMousePoint; FITSView *view { nullptr }; dms ra; dms dec; double width { 0 }; double height { 0 }; double size { 0 }; signals: void newStatus(const QString &msg, FITSBar id); void pointSelected(int x, int y); void markerSelected(int x, int y); }; diff --git a/kstars/hips/healpix.cpp b/kstars/hips/healpix.cpp index 0d1c47ce5..bca73b277 100644 --- a/kstars/hips/healpix.cpp +++ b/kstars/hips/healpix.cpp @@ -1,447 +1,443 @@ /* ----------------------------------------------------------------------------- * * Copyright (C) 1997-2016 Krzysztof M. Gorski, Eric Hivon, Martin Reinecke, * Benjamin D. Wandelt, Anthony J. Banday, * Matthias Bartelmann, * Reza Ansari & Kenneth M. Ganga * * * This file is part of HEALPix. * * Based on work by Pavel Mraz from SkyTechX. * * Adapted to KStars by Jasem Mutlaq. * * HEALPix 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. * * HEALPix is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HEALPix; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * For more information about HEALPix see http://healpix.sourceforge.net * *---------------------------------------------------------------------------*/ #include "healpix.h" #include #include "hipsmanager.h" #include "skypoint.h" #include "kstarsdata.h" #include "geolocation.h" static const double twothird = 2.0/3.0; static const double twopi = 6.283185307179586476925286766559005768394; static const double inv_halfpi = 0.6366197723675813430755350534900574; static const int jrll[] = { 2,2,2,2,3,3,3,3,4,4,4,4 }; static const int jpll[] = { 1,3,5,7,0,2,4,6,1,3,5,7 }; static const short ctab[] = { #define Z(a) a,a+1,a+256,a+257 #define Y(a) Z(a),Z(a+2),Z(a+512),Z(a+514) #define X(a) Y(a),Y(a+4),Y(a+1024),Y(a+1028) X(0),X(8),X(2048),X(2056) #undef X #undef Y #undef Z }; static const short utab[]={ #define Z(a) 0x##a##0, 0x##a##1, 0x##a##4, 0x##a##5 #define Y(a) Z(a##0), Z(a##1), Z(a##4), Z(a##5) #define X(a) Y(a##0), Y(a##1), Y(a##4), Y(a##5) X(0),X(1),X(4),X(5) #undef X #undef Y #undef Z }; static short xoffset[] = { -1,-1, 0, 1, 1, 1, 0,-1 }; static short yoffset[] = { 0, 1, 1, 1, 0,-1,-1,-1 }; static short facearray[9][12] = { { 8, 9,10,11,-1,-1,-1,-1,10,11, 8, 9 }, // S { 5, 6, 7, 4, 8, 9,10,11, 9,10,11, 8 }, // SE { -1,-1,-1,-1, 5, 6, 7, 4,-1,-1,-1,-1 }, // E { 4, 5, 6, 7,11, 8, 9,10,11, 8, 9,10 }, // SW { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 }, // center { 1, 2, 3, 0, 0, 1, 2, 3, 5, 6, 7, 4 }, // NE { -1,-1,-1,-1, 7, 4, 5, 6,-1,-1,-1,-1 }, // W { 3, 0, 1, 2, 3, 0, 1, 2, 4, 5, 6, 7 }, // NW { 2, 3, 0, 1,-1,-1,-1,-1, 0, 1, 2, 3 } }; // N static uchar swaparray[9][12] = { { 0,0,3 }, // S { 0,0,6 }, // SE { 0,0,0 }, // E { 0,0,5 }, // SW { 0,0,0 }, // center { 5,0,0 }, // NE { 0,0,0 }, // W { 6,0,0 }, // NW { 3,0,0 } }; // N static double fmodulo(double v1, double v2) { if (v1>=0) { return (v1getCurrentFrame() == HIPSManager::HIPS_EQUATORIAL_FRAME) { skyCoords[i].setRA0(ra/15.0); skyCoords[i].setDec0(de); } else { dms galacticLong(ra); dms galacticLat(de); skyCoords[i].GalacticToEquatorial1950(&galacticLong, &galacticLat); skyCoords[i].B1950ToJ2000(); skyCoords[i].setRA0(skyCoords[i].ra()); skyCoords[i].setDec0(skyCoords[i].dec()); } skyCoords[i].updateCoords(KStarsData::Instance()->updateNum(), false); skyCoords[i].EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); } } void HEALPix::boundaries(qint32 nside, qint32 pix, int step, QVector3D *out) { int ix, iy, fn; nest2xyf(nside, pix, &ix, &iy, &fn); double dc = 0.5 / nside; double xc = (ix + 0.5) / nside; double yc = (iy + 0.5) / nside; double d = 1. / (step * nside); for (int i = 0; i < step; ++i) { out[0] = toVec3(xc+dc-i*d, yc+dc, fn); out[1] = toVec3(xc-dc, yc+dc-i*d, fn); out[2] = toVec3(xc-dc+i*d, yc-dc, fn); out[3] = toVec3(xc+dc, yc-dc+i*d, fn); } } QVector3D HEALPix::toVec3(double fx, double fy, int face) { double jr = jrll[face] - fx - fy; double locz; double locsth; double locphi; bool lochave_sth = false; double nr; if (jr<1) { nr = jr; double tmp = nr*nr/3.; locz = 1 - tmp; if (locz >0.99) { locsth = sqrt(tmp*(2.-tmp)); lochave_sth=true; } } else if (jr>3) { nr = 4-jr; double tmp = nr*nr/3.; locz = tmp - 1; if (locz<-0.99) { locsth=sqrt(tmp*(2.-tmp)); lochave_sth=true; } } else { nr = 1; locz = (2-jr)*2./3.; } double tmp=jpll[face]*nr+fx-fy; if (tmp<0) tmp+=8; if (tmp>=8) tmp-=8; locphi = (nr<1e-15) ? 0 : (0.5* dms::PI/2.0 *tmp)/nr; double st = lochave_sth ? locsth : sqrt((1.0-locz)*(1.0+locz)); QVector3D out; out.setX(st * cos(locphi)); out.setY(st * sin(locphi)); out.setZ(locz); return out; } void HEALPix::nest2xyf(int nside, int pix, int *ix, int *iy, int *face_num) { int npface_ = nside * nside, raw; *face_num = pix / npface_; pix &= (npface_ - 1); raw = (pix&0x5555) | ((pix&0x55550000)>>15); *ix = ctab[raw&0xff] | (ctab[raw>>8]<<4); pix >>= 1; raw = (pix&0x5555) | ((pix&0x55550000)>>15); *iy = ctab[raw&0xff] | (ctab[raw>>8]<<4); } static int64_t spread_bits64 (int v) { return (int64_t)(utab[ v &0xff]) | ((int64_t)(utab[(v>> 8)&0xff])<<16) | ((int64_t)(utab[(v>>16)&0xff])<<32) | ((int64_t)(utab[(v>>24)&0xff])<<48); } static int xyf2nest (int nside, int ix, int iy, int face_num) { return (face_num*nside*nside) + (utab[ix&0xff] | (utab[ix>>8]<<16) | (utab[iy&0xff]<<1) | (utab[iy>>8]<<17)); } /* int static leadingZeros(qint32 value) { int leadingZeros = 0; while(value != 0) { value = value >> 1; leadingZeros++; } return (32 - leadingZeros); } */ /* static int ilog2(qint32 arg) { return 32 - leadingZeros(qMax(arg, 1)); } */ static int nside2order(qint32 nside) { { int i=0; while((nside>>(++i))>0); return --i; } } /** Returns the neighboring pixels of ipix. This method works in both RING and NEST schemes, but is considerably faster in the NEST scheme. @param ipix the requested pixel number. @return array with indices of the neighboring pixels. The returned array contains (in this order) the pixel numbers of the SW, W, NW, N, NE, E, SE and S neighbor of ipix. If a neighbor does not exist (this can only happen for the W, N, E and S neighbors), its entry is set to -1. */ void HEALPix::neighbours(int nside, qint32 ipix, int *result) { int ix, iy, face_num; int order = nside2order(nside); nest2xyf(nside, ipix, &ix, &iy, &face_num); qint32 nsm1 = nside-1; if ((ix>0)&&(ix0)&&(iy=nside) { x-=nside; nbnum+=1; } if (y<0) { y+=nside; nbnum-=3; } else if (y>=nside) { y-=nside; nbnum+=3; } int f = facearray[nbnum][face_num]; if (f>=0) { int bits = swaparray[nbnum][face_num>>2]; if ((bits&1)>0) x=(int)(nside-x-1); if ((bits&2)>0) y=(int)(nside-y-1); if ((bits&4)>0) { int tint=x; x=y; y=tint; } result[i] = xyf2nest(nside, x,y,f); } else result[i]=-1; } } } int HEALPix::ang2pix_nest_z_phi (qint32 nside_, double z, double phi) { double za = fabs(z); double tt = fmodulo(phi,twopi) * inv_halfpi; /* in [0,4) */ int face_num, ix, iy; if (za<=twothird) /* Equatorial region */ { double temp1 = nside_*(0.5+tt); double temp2 = nside_*(z*0.75); int jp = (int)(temp1-temp2); /* index of ascending edge line */ int jm = (int)(temp1+temp2); /* index of descending edge line */ int ifp = jp/nside_; /* in {0,4} */ int ifm = jm/nside_; face_num = (ifp==ifm) ? (ifp|4) : ((ifp 2/3 */ { int ntt = (int)tt, jp, jm; double tp, tmp; if (ntt>=4) ntt=3; tp = tt-ntt; tmp = nside_*sqrt(3*(1-za)); jp = (int)(tp*tmp); /* increasing edge line index */ jm = (int)((1.0-tp)*tmp); /* decreasing edge line index */ if (jp>=nside_) jp = nside_-1; /* for points too close to the boundary */ if (jm>=nside_) jm = nside_-1; if (z >= 0) { face_num = ntt; /* in {0,3} */ ix = nside_ - jm - 1; iy = nside_ - jp - 1; } else { face_num = ntt + 8; /* in {8,11} */ ix = jp; iy = jm; } } return xyf2nest(nside_,ix,iy,face_num); } int HEALPix::getPix(int level, double ra, double dec) { int nside = 1 << level; double polar[2] = { 0, 0 }; if (HIPSManager::Instance()->getCurrentFrame() == HIPSManager::HIPS_EQUATORIAL_FRAME) { polar[0] = dec; polar[1] = ra; } else if (HIPSManager::Instance()->getCurrentFrame() == HIPSManager::HIPS_GALACTIC_FRAME) { static QMatrix4x4 gl(-0.0548762f, -0.873437f, -0.483835f, 0, 0.4941100f, -0.444830f, 0.746982f, 0, -0.8676660f, -0.198076f, 0.455984f, 0, 0, 0, 0, 1); double rcb = cos(dec); QVector3D xyz = QVector3D(rcb * cos(ra), rcb * sin(ra), sin(dec)); xyz = gl.mapVector(xyz); xyz2sph(xyz, polar[1], polar[0]); } return ang2pix_nest_z_phi(nside, sin(polar[0]), polar[1]); } void HEALPix::getPixChilds(int pix, int *childs) { childs[0] = pix * 4 + 0; childs[1] = pix * 4 + 1; childs[2] = pix * 4 + 2; childs[3] = pix * 4 + 3; } void HEALPix::xyz2sph(const QVector3D &vec, double &l, double &b) { double rho = vec.x()*vec.x() + vec.y()*vec.y(); if (rho > 0) { l = atan2(vec.y(), vec.x()); l -= floor(l / (M_PI*2)) * (M_PI*2); b = atan2(vec.z(), sqrt(rho)); } else { l = 0.0; if (vec.z() == 0.0) { b = 0.0; } else { b = (vec.z() > 0.0) ? M_PI/2. : -dms::PI/2.; } } } diff --git a/kstars/hips/healpix.h b/kstars/hips/healpix.h index 861ee50ad..6bf177a71 100644 --- a/kstars/hips/healpix.h +++ b/kstars/hips/healpix.h @@ -1,58 +1,58 @@ /* ----------------------------------------------------------------------------- * * Copyright (C) 1997-2016 Krzysztof M. Gorski, Eric Hivon, Martin Reinecke, * Benjamin D. Wandelt, Anthony J. Banday, * Matthias Bartelmann, * Reza Ansari & Kenneth M. Ganga * * * This file is part of HEALPix. * * Based on work by Pavel Mraz from SkyTechX. * * Adapted to KStars by Jasem Mutlaq. * * HEALPix 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. * * HEALPix is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HEALPix; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * For more information about HEALPix see http://healpix.sourceforge.net * *---------------------------------------------------------------------------*/ #pragma once #include "hips.h" #include class SkyPoint; class HEALPix { public: - HEALPix(); + HEALPix() = default; void getCornerPoints(int level, int pix, SkyPoint *skyCoords); void neighbours(int nside, qint32 ipix, int *result); int getPix(int level, double ra, double dec); void getPixChilds(int pix, int *childs); private: void nest2xyf(int nside, int pix, int *ix, int *iy, int *face_num); QVector3D toVec3(double fx, double fy, int face); void boundaries(qint32 nside, qint32 pix, int step, QVector3D *out); int ang2pix_nest_z_phi(qint32 nside_, double z, double phi); void xyz2sph(const QVector3D &vec, double &l, double &b); }; diff --git a/kstars/hips/opships.cpp b/kstars/hips/opships.cpp index 26faac21c..7b1cfff3d 100644 --- a/kstars/hips/opships.cpp +++ b/kstars/hips/opships.cpp @@ -1,207 +1,205 @@ /* HiPS Options Copyright (C) 2017 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#include -#include -#include -#include -#include - -#include -#include "Options.h" #include "opships.h" + #include "kstars.h" +#include "hipsmanager.h" +#include "Options.h" +#include "skymap.h" #include "auxiliary/ksnotification.h" #include "auxiliary/filedownloader.h" #include "auxiliary/kspaths.h" -#include "skymap.h" -#include "hipsmanager.h" + +#include + +#include +#include +#include +#include +#include static const QStringList hipsKeys = { "ID", "obs_title", "obs_description", "hips_order", "hips_frame", "hips_tile_width", "hips_tile_format", "hips_service_url", "moc_sky_fraction"}; OpsHIPSDisplay::OpsHIPSDisplay() : QFrame(KStars::Instance()) { setupUi(this); } OpsHIPSCache::OpsHIPSCache() : QFrame(KStars::Instance()) { setupUi(this); } OpsHIPS::OpsHIPS() : QFrame(KStars::Instance()) { setupUi(this); //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("hipssettings"); QString path = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Literal("hips_previews/"); QDir dir; dir.mkpath(path); connect(refreshSourceB, SIGNAL(clicked()), this, SLOT(slotRefresh())); connect(sourcesList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(slotItemUpdated(QListWidgetItem*))); connect(sourcesList, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotItemClicked(QListWidgetItem*))); if (sourcesList->count() == 0) slotRefresh(); } -OpsHIPS::~OpsHIPS() -{ -} - void OpsHIPS::slotRefresh() { downloadJob = new FileDownloader(); downloadJob->setProgressDialogEnabled(true, i18n("HiPS Update"), i18n("Downloading HiPS sources...")); QObject::connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); QObject::connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); downloadJob->get(QUrl("http://alasky.unistra.fr/MocServer/query?hips_service_url=*&dataproduct_type=!catalog&dataproduct_type=!cube&&moc_sky_fraction=1&get=record")); } void OpsHIPS::downloadReady() { sources.clear(); QTextStream stream(downloadJob->downloadedData()); QStringList hipsTitles; QMap oneSource; while (stream.atEnd() == false) { QString line = stream.readLine(); if (line.isEmpty()) { sources.append(oneSource); oneSource.clear(); continue; } QStringList keyvalue = line.split("=", QString::KeepEmptyParts); QString key = keyvalue[0].simplified(); if (hipsKeys.contains(key) == false) continue; QString value = keyvalue[1].simplified(); oneSource[key] = value; if (key == "obs_title") hipsTitles << value; } // Get existing database sources QList> dbSources; KStarsData::Instance()->userdb()->GetAllHIPSSources(dbSources); // Get existing database titles QStringList dbTitles; for (QMap oneSource : dbSources) dbTitles << oneSource["obs_title"]; // Add all titiles to list widget sourcesList->addItems(hipsTitles); QListWidgetItem* item = nullptr; // Make sources checkable and check sources that already exist in the database sourcesList->blockSignals(true); for(int i = 0; i < sourcesList->count(); ++i) { item = sourcesList->item(i); item->setFlags(item->flags() | Qt::ItemIsUserCheckable); item->setCheckState(dbTitles.contains(item->text()) ? Qt::Checked : Qt::Unchecked); if (item->text() == Options::hIPSSource()) { item->setSelected(true); sourcesList->scrollToItem(item); slotItemClicked(item); } } sourcesList->blockSignals(false); // Delete job later downloadJob->deleteLater(); } void OpsHIPS::downloadError(const QString &errorString) { KSNotification::error(i18n("Error downloading HiPS sources: %1", errorString)); downloadJob->deleteLater(); } void OpsHIPS::slotItemUpdated(QListWidgetItem *item) { for(QMap &oneSource: sources) { if (oneSource.value("obs_title") == item->text()) { if (item->checkState() == Qt::Checked) KStarsData::Instance()->userdb()->AddHIPSSource(oneSource); else KStarsData::Instance()->userdb()->DeleteHIPSSource(oneSource.value("ID")); break; } } } void OpsHIPS::slotItemClicked(QListWidgetItem *item) { for(QMap &oneSource: sources) { if (oneSource.value("obs_title") == item->text()) { sourceDescription->setText(oneSource.value("obs_description")); // Get stored preview, if not found, it will be downloaded. setPreview(oneSource.value("ID"), oneSource.value("hips_service_url")); break; } } } void OpsHIPS::setPreview(const QString &id, const QString &url) { uint hash = qHash(id); QString previewName = QString("%1.jpg").arg(hash); QString currentPreviewPath = KSPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal("hips_previews/") + previewName); if (currentPreviewPath.isEmpty() == false) sourceImage->setPixmap(QPixmap(currentPreviewPath)); else { currentPreviewPath = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Literal("hips_previews/") + previewName; previewJob = new FileDownloader(); connect(previewJob, SIGNAL(downloaded()), this, SLOT(previewReady())); previewJob->setDownloadedFileURL(QUrl::fromLocalFile(currentPreviewPath)); previewJob->get(QUrl(url + QLatin1Literal("/preview.jpg"))); } } void OpsHIPS::previewReady() { QString previewFile = previewJob->getDownloadedFileURL().toLocalFile(); QFileInfo previewInfo(previewFile); // If less than 1kb then it's junk if (previewInfo.size() < 1024) { sourceImage->setPixmap(QPixmap(":/images/noimage.png")); QFile::remove(previewFile); } else sourceImage->setPixmap(QPixmap(previewFile)); } diff --git a/kstars/hips/opships.h b/kstars/hips/opships.h index b8b4703a8..9bda30fc4 100644 --- a/kstars/hips/opships.h +++ b/kstars/hips/opships.h @@ -1,70 +1,70 @@ /* HiPS Options Copyright (C) 2017 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "ui_opships.h" #include "ui_opshipsdisplay.h" #include "ui_opshipscache.h" class KConfigDialog; class FileDownloader; class OpsHIPSDisplay : public QFrame, public Ui::OpsHIPSDisplay { Q_OBJECT public: explicit OpsHIPSDisplay(); }; class OpsHIPSCache : public QFrame, public Ui::OpsHIPSCache { Q_OBJECT public: explicit OpsHIPSCache(); }; /** * @class OpsHIPS * * HIPS Settings including download of external sources and enabling/disabling them accordingly. * * @author Jasem Mutlaq */ class OpsHIPS : public QFrame, public Ui::OpsHIPS { Q_OBJECT public: explicit OpsHIPS(); - ~OpsHIPS() override; + virtual ~OpsHIPS() override = default; public slots: void slotRefresh(); protected slots: void downloadReady(); void downloadError(const QString &errorString); void previewReady(); void slotItemUpdated(QListWidgetItem *item); void slotItemClicked(QListWidgetItem *item); private: void setPreview(const QString &id, const QString &url); - KConfigDialog *m_ConfigDialog = nullptr; - FileDownloader *downloadJob = nullptr; - FileDownloader *previewJob = nullptr; + KConfigDialog *m_ConfigDialog { nullptr }; + FileDownloader *downloadJob { nullptr }; + FileDownloader *previewJob { nullptr }; QList> sources; - bool dirty=false; + bool dirty { false }; }; diff --git a/kstars/hips/pixcache.cpp b/kstars/hips/pixcache.cpp index 52b86fc89..7a63c84ce 100644 --- a/kstars/hips/pixcache.cpp +++ b/kstars/hips/pixcache.cpp @@ -1,76 +1,72 @@ /* Copyright (C) 2015-2017, Pavel Mraz 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "pixcache.h" static int qHash(const pixCacheKey_t &key, uint seed) { return qHash(QString("%1_%2_%3").arg(key.level).arg(key.pix).arg(key.uid), seed); } inline bool operator<(const pixCacheKey_t &k1, const pixCacheKey_t &k2) { if (k1.uid != k2.uid) { return k1.uid < k2.uid; } if (k1.level != k2.level) { return k1.level < k2.level; } return k1.pix < k2.pix; } inline bool operator==(const pixCacheKey_t &k1, const pixCacheKey_t &k2) { return (k1.uid == k2.uid) && (k1.level == k2.level) && (k1.pix == k2.pix); } -PixCache::PixCache() -{ -} - void PixCache::add(pixCacheKey_t &key, pixCacheItem_t *item, int cost) { Q_ASSERT(cost < m_cache.maxCost()); m_cache.insert(key, item, cost); } pixCacheItem_t *PixCache::get(pixCacheKey_t &key) { return m_cache.object(key); } void PixCache::setMaxCost(int maxCost) { m_cache.setMaxCost(maxCost); } void PixCache::printCache() { qDebug() << " -- cache ---------------"; qDebug() << m_cache.size() << m_cache.totalCost() << m_cache.maxCost(); } int PixCache::used() { return m_cache.totalCost(); } diff --git a/kstars/hips/pixcache.h b/kstars/hips/pixcache.h index 089897793..f603b1e49 100644 --- a/kstars/hips/pixcache.h +++ b/kstars/hips/pixcache.h @@ -1,39 +1,39 @@ /* Copyright (C) 2015-2017, Pavel Mraz 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include "hips.h" #include class PixCache { public: - PixCache(); + PixCache() = default; void add(pixCacheKey_t &key, pixCacheItem_t *item, int cost); pixCacheItem_t *get(pixCacheKey_t &key); void setMaxCost(int maxCost); void printCache(); int used(); private: QCache m_cache; }; diff --git a/kstars/indi/clientmanager.cpp b/kstars/indi/clientmanager.cpp index 6db4747f7..2d266bc5e 100644 --- a/kstars/indi/clientmanager.cpp +++ b/kstars/indi/clientmanager.cpp @@ -1,260 +1,252 @@ /* INDI Client Manager Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "clientmanager.h" #include "deviceinfo.h" #include "drivermanager.h" #include "guimanager.h" #include "indilistener.h" #include "Options.h" #include "servermanager.h" #include -ClientManager::ClientManager() -{ -} - -ClientManager::~ClientManager() -{ -} - bool ClientManager::isDriverManaged(DriverInfo *di) { foreach (DriverInfo *dv, managedDrivers) { if (dv == di) return true; } return false; } void ClientManager::newDevice(INDI::BaseDevice *dp) { setBLOBMode(B_ALSO, dp->getDeviceName()); DriverInfo *deviceDriver = nullptr; if (QString(dp->getDeviceName()).isEmpty()) { qCWarning(KSTARS_INDI) << "Received invalid device with empty name! Ignoring the device..."; return; } qCDebug(KSTARS_INDI) << "Received new device" << dp->getDeviceName(); // First iteration find unique matches foreach (DriverInfo *dv, managedDrivers) { if (dv->getUniqueLabel() == QString(dp->getDeviceName())) { deviceDriver = dv; break; } } // Second iteration find partial matches if (deviceDriver == nullptr) { foreach (DriverInfo *dv, managedDrivers) { QString dvName = dv->getName(); dvName = dv->getName().split(' ').first(); if (dvName.isEmpty()) dvName = dv->getName(); if (/*dv->getUniqueLabel() == dp->getDeviceName() ||*/ QString(dp->getDeviceName()).startsWith(dvName, Qt::CaseInsensitive) || ((dv->getDriverSource() == HOST_SOURCE || dv->getDriverSource() == GENERATED_SOURCE))) { deviceDriver = dv; break; } } } if (deviceDriver == nullptr) return; deviceDriver->setUniqueLabel(dp->getDeviceName()); DeviceInfo *devInfo = new DeviceInfo(deviceDriver, dp); deviceDriver->addDevice(devInfo); emit newINDIDevice(devInfo); return; } void ClientManager::newProperty(INDI::Property *prop) { //IDLog("Received new property %s for device %s\n", prop->getName(), prop->getgetDeviceName()); emit newINDIProperty(prop); } void ClientManager::removeProperty(INDI::Property *prop) { emit removeINDIProperty(prop); } void ClientManager::removeDevice(INDI::BaseDevice *dp) { foreach (DriverInfo *driverInfo, managedDrivers) { foreach (DeviceInfo *deviceInfo, driverInfo->getDevices()) { if (deviceInfo->getBaseDevice() == dp) { //GUIManager::Instance()->removeDevice(deviceInfo); //INDIListener::Instance()->removeDevice(deviceInfo); qCDebug(KSTARS_INDI) << "Removing device" << dp->getDeviceName(); emit removeINDIDevice(deviceInfo); driverInfo->removeDevice(deviceInfo); if (driverInfo->isEmpty()) managedDrivers.removeOne(driverInfo); return; } } } } void ClientManager::newBLOB(IBLOB *bp) { emit newINDIBLOB(bp); } void ClientManager::newSwitch(ISwitchVectorProperty *svp) { emit newINDISwitch(svp); } void ClientManager::newNumber(INumberVectorProperty *nvp) { emit newINDINumber(nvp); } void ClientManager::newText(ITextVectorProperty *tvp) { emit newINDIText(tvp); } void ClientManager::newLight(ILightVectorProperty *lvp) { emit newINDILight(lvp); } void ClientManager::newMessage(INDI::BaseDevice *dp, int messageID) { emit newINDIMessage(dp, messageID); } #if INDI_VERSION_MAJOR >= 1 && INDI_VERSION_MINOR >= 5 void ClientManager::newUniversalMessage(std::string message) { emit newINDIUniversalMessage(QString::fromStdString(message)); } #endif void ClientManager::appendManagedDriver(DriverInfo *dv) { qCDebug(KSTARS_INDI) << "Adding managed driver" << dv->getName(); managedDrivers.append(dv); dv->setClientManager(this); sManager = dv->getServerManager(); } void ClientManager::removeManagedDriver(DriverInfo *dv) { qCDebug(KSTARS_INDI) << "Removing managed driver" << dv->getName(); dv->setClientState(false); foreach (DeviceInfo *di, dv->getDevices()) { //emit removeINDIDevice(di); INDIListener::Instance()->removeDevice(di); GUIManager::Instance()->removeDevice(di); dv->removeDevice(di); } /*foreach(DriverInfo *dv, managedDrivers) { if (dv->getDriverSource() == GENERATED_SOURCE) { managedDrivers.removeOne(dv); delete (dv); return; } }*/ managedDrivers.removeOne(dv); if (dv->getDriverSource() == GENERATED_SOURCE) delete (dv); } void ClientManager::serverConnected() { qCDebug(KSTARS_INDI) << "INDI server connected."; foreach (DriverInfo *device, managedDrivers) { device->setClientState(true); if (sManager) device->setHostParameters(sManager->getHost(), sManager->getPort()); } } void ClientManager::serverDisconnected(int exit_code) { qCDebug(KSTARS_INDI) << "INDI server disconnected. Exit code:" << exit_code; foreach (DriverInfo *device, managedDrivers) { device->setClientState(false); device->reset(); } if (exit_code < 0) emit connectionFailure(this); } QList ClientManager::getManagedDrivers() const { return managedDrivers; } DriverInfo *ClientManager::findDriverInfoByName(const QString &name) { foreach (DriverInfo *dv, managedDrivers) { if (dv->getName() == name) return dv; } return nullptr; } DriverInfo *ClientManager::findDriverInfoByLabel(const QString &label) { foreach (DriverInfo *dv, managedDrivers) { if (dv->getTreeLabel() == label) return dv; } return nullptr; } diff --git a/kstars/indi/clientmanager.h b/kstars/indi/clientmanager.h index 399f0a325..2a5caf7fb 100644 --- a/kstars/indi/clientmanager.h +++ b/kstars/indi/clientmanager.h @@ -1,117 +1,117 @@ /* INDI Client Manager Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #ifdef USE_QT5_INDI #include #else #include #include #endif class DeviceInfo; class DriverInfo; class ServerManager; /** * @class ClientManager * ClientManager manages connection to INDI server, creation of devices, and receiving/sending properties. * * ClientManager is a subclass of INDI::BaseClient class part of the INDI Library. * This enables the class to communicate with INDI server and to receive notification of devices, properties, and messages. * * @author Jasem Mutlaq * @version 1.1 */ #ifdef USE_QT5_INDI class ClientManager : public INDI::BaseClientQt #else class ClientManager : public QObject, public INDI::BaseClient #endif { Q_OBJECT public: - ClientManager(); - virtual ~ClientManager(); + ClientManager() = default; + virtual ~ClientManager() override = default; /** * @brief appendManagedDriver Add driver to pool of managed drivers by this client manager. * @param dv pointer to driver info instance. * @note This function is ALWAYS called from the main KStars thread. */ void appendManagedDriver(DriverInfo *dv); /** * @brief removeManagedDriver Remove managed driver from pool of drivers managed by this client manager. * @param dv pointer to driver info instance. * @note This function is ALWAYS called from the main KStars thread. */ void removeManagedDriver(DriverInfo *dv); int count() { return managedDrivers.count(); } ServerManager *getServerManager() { return sManager; } DriverInfo *findDriverInfoByName(const QString &name); DriverInfo *findDriverInfoByLabel(const QString &label); bool isDriverManaged(DriverInfo *); QList getManagedDrivers() const; protected: - virtual void newDevice(INDI::BaseDevice *dp); - virtual void newProperty(INDI::Property *prop); - virtual void removeProperty(INDI::Property *prop); - virtual void removeDevice(INDI::BaseDevice *dp); - virtual void newBLOB(IBLOB *bp); - virtual void newSwitch(ISwitchVectorProperty *svp); - virtual void newNumber(INumberVectorProperty *); - virtual void newText(ITextVectorProperty *); - virtual void newLight(ILightVectorProperty *); - virtual void newMessage(INDI::BaseDevice *dp, int messageID); + virtual void newDevice(INDI::BaseDevice *dp) override; + virtual void newProperty(INDI::Property *prop) override; + virtual void removeProperty(INDI::Property *prop) override; + virtual void removeDevice(INDI::BaseDevice *dp) override; + virtual void newBLOB(IBLOB *bp) override; + virtual void newSwitch(ISwitchVectorProperty *svp) override; + virtual void newNumber(INumberVectorProperty *) override; + virtual void newText(ITextVectorProperty *) override; + virtual void newLight(ILightVectorProperty *) override; + virtual void newMessage(INDI::BaseDevice *dp, int messageID) override; #if INDI_VERSION_MAJOR >= 1 && INDI_VERSION_MINOR >= 5 - virtual void newUniversalMessage(std::string message); + virtual void newUniversalMessage(std::string message) override; #endif - virtual void serverConnected(); - virtual void serverDisconnected(int exit_code); + virtual void serverConnected() override; + virtual void serverDisconnected(int exit_code) override; private: QList managedDrivers; ServerManager *sManager { nullptr }; signals: void connectionSuccessful(); void connectionFailure(ClientManager *); // @note If using INDI Posix client, the following newINDIDevice/Property and removeINDIDevice/Property signals // must be connected to slots using Qt::BlockingQueuedConnection to ensure operation is fully completed before // resuming the INDI client thread. For Qt Based INDI client, Qt::DirectConnection should be used. void newINDIDevice(DeviceInfo *dv); void removeINDIDevice(DeviceInfo *dv); void newINDIProperty(INDI::Property *prop); void removeINDIProperty(INDI::Property *prop); void newINDIBLOB(IBLOB *bp); void newINDISwitch(ISwitchVectorProperty *svp); void newINDINumber(INumberVectorProperty *nvp); void newINDIText(ITextVectorProperty *tvp); void newINDILight(ILightVectorProperty *lvp); void newINDIMessage(INDI::BaseDevice *dp, int messageID); #if INDI_VERSION_MAJOR >= 1 && INDI_VERSION_MINOR >= 5 void newINDIUniversalMessage(const QString &message); #endif }; diff --git a/kstars/indi/indistd.cpp b/kstars/indi/indistd.cpp index 91b87df6f..8234b12da 100644 --- a/kstars/indi/indistd.cpp +++ b/kstars/indi/indistd.cpp @@ -1,1026 +1,1017 @@ /* INDI STD Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Handle INDI Standard properties. */ #include "indistd.h" #include "clientmanager.h" #include "driverinfo.h" #include "deviceinfo.h" #include "imageviewer.h" +#include "indi_debug.h" #include "kstars.h" #include "kstarsdata.h" #include "Options.h" #include "skymap.h" #include #include -#include "indi_debug.h" - namespace ISD { GDSetCommand::GDSetCommand(INDI_PROPERTY_TYPE inPropertyType, const QString &inProperty, const QString &inElement, QVariant qValue, QObject *parent) : QObject(parent) { propType = inPropertyType; indiProperty = inProperty; indiElement = inElement; elementValue = qValue; } GenericDevice::GenericDevice(DeviceInfo &idv) { deviceInfo = &idv; driverInfo = idv.getDriverInfo(); baseDevice = idv.getBaseDevice(); clientManager = driverInfo->getClientManager(); dType = KSTARS_UNKNOWN; } -GenericDevice::~GenericDevice() -{ -} - const char *GenericDevice::getDeviceName() { return baseDevice->getDeviceName(); } void GenericDevice::registerProperty(INDI::Property *prop) { foreach (INDI::Property *pp, properties) { if (pp == prop) return; } properties.append(prop); emit propertyDefined(prop); // In case driver already started if (!strcmp(prop->getName(), "CONNECTION")) { ISwitchVectorProperty *svp = prop->getSwitch(); if (svp == nullptr) return; // Still connecting/disconnecting... if (svp->s == IPS_BUSY) return; ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP == nullptr) return; if (svp->s == IPS_OK && conSP->s == ISS_ON) { connected = true; emit Connected(); createDeviceInit(); } } if (!strcmp(prop->getName(), "DRIVER_INFO")) { ITextVectorProperty *tvp = prop->getText(); if (tvp) { IText *tp = IUFindText(tvp, "DRIVER_INTERFACE"); if (tp) driverInterface = static_cast(atoi(tp->text)); } } else if (!strcmp(prop->getName(), "TIME_UTC") && Options::useTimeUpdate() && Options::useKStarsSource()) { ITextVectorProperty *tvp = prop->getText(); if (tvp && tvp->p != IP_RO) updateTime(); } else if (!strcmp(prop->getName(), "GEOGRAPHIC_COORD") && Options::useGeographicUpdate() && Options::useKStarsSource()) { INumberVectorProperty *nvp = prop->getNumber(); if (nvp && nvp->p != IP_RO) updateLocation(); } else if (!strcmp(prop->getName(), "WATCHDOG_HEARTBEAT")) { INumberVectorProperty *nvp = prop->getNumber(); if (nvp) { if (watchDogTimer == nullptr) { watchDogTimer = new QTimer(this); connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(resetWatchdog())); } if (connected && nvp->np[0].value > 0) { // Send immediately a heart beat clientManager->sendNewNumber(nvp); //watchDogTimer->start(0); } } } } void GenericDevice::removeProperty(INDI::Property *prop) { properties.removeOne(prop); emit propertyDeleted(prop); } void GenericDevice::processSwitch(ISwitchVectorProperty *svp) { if (!strcmp(svp->name, "CONNECTION")) { ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP == nullptr) return; // Still connecting/disconnecting... if (svp->s == IPS_BUSY) return; if (svp->s == IPS_OK && conSP->s == ISS_ON) { connected = true; emit Connected(); createDeviceInit(); if (watchDogTimer != nullptr) { INumberVectorProperty *nvp = baseDevice->getNumber("WATCHDOG_HEARTBEAT"); if (nvp && nvp->np[0].value > 0) { // Send immediately clientManager->sendNewNumber(nvp); //watchDogTimer->start(0); } } } else { connected = false; emit Disconnected(); } } emit switchUpdated(svp); } void GenericDevice::processNumber(INumberVectorProperty *nvp) { QString deviceName = getDeviceName(); uint32_t interface = getDriverInterface(); Q_UNUSED(interface); if (!strcmp(nvp->name, "GEOGRAPHIC_COORD") && nvp->s == IPS_OK && ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) || (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)))) { // Update KStars Location once we receive update from INDI, if the source is set to DEVICE dms lng, lat; double elev=0; INumber *np = nullptr; np = IUFindNumber(nvp, "LONG"); if (!np) return; // INDI Longitude convention is 0 to 360. We need to turn it back into 0 to 180 EAST, 0 to -180 WEST if (np->value < 180) lng.setD(np->value); else lng.setD(np->value - 360.0); np = IUFindNumber(nvp, "LAT"); if (!np) return; lat.setD(np->value); // Double check we have valid values if (lng.Degrees() == 0 && lat.Degrees() ==0) { qCWarning(KSTARS_INDI) << "Ignoring invalid device coordinates."; return; } np = IUFindNumber(nvp, "ELEV"); if (np) elev = np->value; GeoLocation *geo = KStars::Instance()->data()->geo(); QString newLocationName; if (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE) newLocationName = i18n("GPS Location"); else newLocationName = i18n("Mount Location"); if (geo->name() != newLocationName) { double TZ0 = geo->TZ0(); TimeZoneRule *rule = geo->tzrule(); geo = new GeoLocation(lng, lat, newLocationName, "", "", TZ0, rule, elev); } else { geo->setLong(lng); geo->setLat(lat); } qCInfo(KSTARS_INDI) << "Setting location from device:" << getDeviceName() << "Longitude:" << lng.toDMSString() << "Latitude:" << lat.toDMSString(); KStars::Instance()->data()->setLocation(*geo); } else if (!strcmp(nvp->name, "WATCHDOG_HEARTBEAT")) { if (watchDogTimer == nullptr) { watchDogTimer = new QTimer(this); connect(watchDogTimer, SIGNAL(timeout()), this, SLOT(resetWatchdog())); } if (connected && nvp->np[0].value > 0) { // Reset timer 15 seconds before it is due watchDogTimer->start(nvp->np[0].value * 60 * 1000 - 15 * 1000); } else if (nvp->np[0].value == 0) watchDogTimer->stop(); } emit numberUpdated(nvp); } void GenericDevice::processText(ITextVectorProperty *tvp) { // Update KStars time once we receive update from INDI, if the source is set to DEVICE if (!strcmp(tvp->name, "TIME_UTC") && tvp->s == IPS_OK && ( (Options::useMountSource() && (getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) || (Options::useGPSSource() && (getDriverInterface() & INDI::BaseDevice::GPS_INTERFACE)))) { IText *tp = nullptr; int d, m, y, min, sec, hour; float utcOffset; QDate indiDate; QTime indiTime; KStarsDateTime indiDateTime; tp = IUFindText(tvp, "UTC"); if (!tp) return; sscanf(tp->text, "%d%*[^0-9]%d%*[^0-9]%dT%d%*[^0-9]%d%*[^0-9]%d", &y, &m, &d, &hour, &min, &sec); indiDate.setDate(y, m, d); indiTime.setHMS(hour, min, sec); indiDateTime.setDate(indiDate); indiDateTime.setTime(indiTime); tp = IUFindText(tvp, "OFFSET"); if (!tp) return; sscanf(tp->text, "%f", &utcOffset); qCInfo(KSTARS_INDI) << "Setting UTC time from device:" << getDeviceName() << indiDateTime.toString(); KStars::Instance()->data()->changeDateTime(indiDateTime); KStars::Instance()->data()->syncLST(); GeoLocation *geo = KStars::Instance()->data()->geo(); if (geo->tzrule()) utcOffset -= geo->tzrule()->deltaTZ(); // TZ0 is the timezone WTIHOUT any DST offsets. Above, we take INDI UTC Offset (with DST already included) // and subtract from it the deltaTZ from the current TZ rule. geo->setTZ0(utcOffset); } emit textUpdated(tvp); } void GenericDevice::processLight(ILightVectorProperty *lvp) { emit lightUpdated(lvp); } void GenericDevice::processMessage(int messageID) { emit messageUpdated(messageID); } void GenericDevice::processBLOB(IBLOB *bp) { // Ignore write-only BLOBs since we only receive it for state-change if (bp->bvp->p == IP_WO) return; QFile *data_file = nullptr; INDIDataTypes dataType; if (!strcmp(bp->format, ".ascii")) dataType = DATA_ASCII; else dataType = DATA_OTHER; QString currentDir = Options::fitsDir(); int nr, n = 0; if (currentDir.endsWith('/')) currentDir.truncate(sizeof(currentDir) - 1); QString filename(currentDir + '/'); QString ts = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss"); filename += QString("%1_").arg(bp->label) + ts + QString(bp->format).trimmed(); strncpy(BLOBFilename, filename.toLatin1(), MAXINDIFILENAME); bp->aux2 = BLOBFilename; if (dataType == DATA_ASCII) { if (bp->aux0 == nullptr) { bp->aux0 = new int(); QFile *ascii_data_file = new QFile(); ascii_data_file->setFileName(filename); if (!ascii_data_file->open(QIODevice::WriteOnly)) { qCCritical(KSTARS_INDI) << "GenericDevice Error: Unable to open " << ascii_data_file->fileName() << endl; return; } bp->aux1 = ascii_data_file; } data_file = (QFile *)bp->aux1; QDataStream out(data_file); for (nr = 0; nr < (int)bp->size; nr += n) n = out.writeRawData(static_cast(bp->blob) + nr, bp->size - nr); out.writeRawData((const char *)"\n", 1); data_file->flush(); } else { QFile fits_temp_file(filename); if (!fits_temp_file.open(QIODevice::WriteOnly)) { qCCritical(KSTARS_INDI) << "GenericDevice Error: Unable to open " << fits_temp_file.fileName() << endl; return; } QDataStream out(&fits_temp_file); for (nr = 0; nr < (int)bp->size; nr += n) n = out.writeRawData(static_cast(bp->blob) + nr, bp->size - nr); fits_temp_file.close(); QByteArray fmt = QString(bp->format).toLower().remove('.').toUtf8(); if (QImageReader::supportedImageFormats().contains(fmt)) { QUrl url(filename); url.setScheme("file"); ImageViewer *iv = new ImageViewer(url, QString(), KStars::Instance()); if (iv) iv->show(); } } if (dataType == DATA_OTHER) KStars::Instance()->statusBar()->showMessage(i18n("Data file saved to %1", filename), 0); emit BLOBUpdated(bp); } bool GenericDevice::setConfig(INDIConfig tConfig) { ISwitchVectorProperty *svp = baseDevice->getSwitch("CONFIG_PROCESS"); if (svp == nullptr) return false; ISwitch *sp = nullptr; IUResetSwitch(svp); switch (tConfig) { case LOAD_LAST_CONFIG: sp = IUFindSwitch(svp, "CONFIG_LOAD"); if (sp == nullptr) return false; IUResetSwitch(svp); sp->s = ISS_ON; break; case SAVE_CONFIG: sp = IUFindSwitch(svp, "CONFIG_SAVE"); if (sp == nullptr) return false; IUResetSwitch(svp); sp->s = ISS_ON; break; case LOAD_DEFAULT_CONFIG: sp = IUFindSwitch(svp, "CONFIG_DEFAULT"); if (sp == nullptr) return false; IUResetSwitch(svp); sp->s = ISS_ON; break; } clientManager->sendNewSwitch(svp); return true; } void GenericDevice::createDeviceInit() { if (Options::showINDIMessages()) KStars::Instance()->statusBar()->showMessage(i18n("%1 is online.", baseDevice->getDeviceName()), 0); KStars::Instance()->map()->forceUpdateNow(); } /*********************************************************************************/ /* Update the Driver's Time */ /*********************************************************************************/ void GenericDevice::updateTime() { QString offset, isoTS; offset = QString().setNum(KStars::Instance()->data()->geo()->TZ(), 'g', 2); //QTime newTime( KStars::Instance()->data()->ut().time()); //QDate newDate( KStars::Instance()->data()->ut().date()); //isoTS = QString("%1-%2-%3T%4:%5:%6").arg(newDate.year()).arg(newDate.month()).arg(newDate.day()).arg(newTime.hour()).arg(newTime.minute()).arg(newTime.second()); isoTS = KStars::Instance()->data()->ut().toString(Qt::ISODate).remove('Z'); /* Update Date/Time */ ITextVectorProperty *timeUTC = baseDevice->getText("TIME_UTC"); if (timeUTC) { IText *timeEle = IUFindText(timeUTC, "UTC"); if (timeEle) IUSaveText(timeEle, isoTS.toLatin1().constData()); IText *offsetEle = IUFindText(timeUTC, "OFFSET"); if (offsetEle) IUSaveText(offsetEle, offset.toLatin1().constData()); if (timeEle && offsetEle) clientManager->sendNewText(timeUTC); } } /*********************************************************************************/ /* Update the Driver's Geographical Location */ /*********************************************************************************/ void GenericDevice::updateLocation() { GeoLocation *geo = KStars::Instance()->data()->geo(); double longNP; if (geo->lng()->Degrees() >= 0) longNP = geo->lng()->Degrees(); else longNP = dms(geo->lng()->Degrees() + 360.0).Degrees(); INumberVectorProperty *nvp = baseDevice->getNumber("GEOGRAPHIC_COORD"); if (nvp == nullptr) return; INumber *np = IUFindNumber(nvp, "LONG"); if (np == nullptr) return; np->value = longNP; np = IUFindNumber(nvp, "LAT"); if (np == nullptr) return; np->value = geo->lat()->Degrees(); np = IUFindNumber(nvp, "ELEV"); if (np == nullptr) return; np->value = geo->elevation(); clientManager->sendNewNumber(nvp); } bool GenericDevice::Connect() { return runCommand(INDI_CONNECT, nullptr); } bool GenericDevice::Disconnect() { return runCommand(INDI_DISCONNECT, nullptr); } bool GenericDevice::runCommand(int command, void *ptr) { switch (command) { case INDI_CONNECT: clientManager->connectDevice(baseDevice->getDeviceName()); break; case INDI_DISCONNECT: clientManager->disconnectDevice(baseDevice->getDeviceName()); break; case INDI_SET_PORT: { if (ptr == nullptr) return false; ITextVectorProperty *tvp = baseDevice->getText("DEVICE_PORT"); if (tvp == nullptr) return false; IText *tp = IUFindText(tvp, "PORT"); IUSaveText(tp, (static_cast(ptr))->toLatin1().constData()); clientManager->sendNewText(tvp); } break; // We do it here because it could be either CCD or FILTER interfaces, so no need to duplicate code case INDI_SET_FILTER: { if (ptr == nullptr) return false; INumberVectorProperty *nvp = baseDevice->getNumber("FILTER_SLOT"); if (nvp == nullptr) return false; int requestedFilter = *((int *)ptr); if (requestedFilter == nvp->np[0].value) break; nvp->np[0].value = requestedFilter; clientManager->sendNewNumber(nvp); } break; // We do it here because it could be either FOCUSER or ROTATOR interfaces, so no need to duplicate code case INDI_SET_ROTATOR_ANGLE: { if (ptr == nullptr) return false; INumberVectorProperty *nvp = baseDevice->getNumber("ABS_ROTATOR_ANGLE"); if (nvp == nullptr) return false; double requestedAngle = *((double *)ptr); if (requestedAngle == nvp->np[0].value) break; nvp->np[0].value = requestedAngle; clientManager->sendNewNumber(nvp); } break; // We do it here because it could be either FOCUSER or ROTATOR interfaces, so no need to duplicate code case INDI_SET_ROTATOR_TICKS: { if (ptr == nullptr) return false; INumberVectorProperty *nvp = baseDevice->getNumber("ABS_ROTATOR_POSITION"); if (nvp == nullptr) return false; int32_t requestedTicks = *((int32_t *)ptr); if (requestedTicks == nvp->np[0].value) break; nvp->np[0].value = requestedTicks; clientManager->sendNewNumber(nvp); } break; } return true; } bool GenericDevice::setProperty(QObject *setPropCommand) { GDSetCommand *indiCommand = static_cast(setPropCommand); //qDebug() << "We are trying to set value for property " << indiCommand->indiProperty << " and element" << indiCommand->indiElement << " and value " << indiCommand->elementValue << endl; INDI::Property *pp = baseDevice->getProperty(indiCommand->indiProperty.toLatin1().constData()); if (pp == nullptr) return false; switch (indiCommand->propType) { case INDI_SWITCH: { ISwitchVectorProperty *svp = pp->getSwitch(); if (svp == nullptr) return false; ISwitch *sp = IUFindSwitch(svp, indiCommand->indiElement.toLatin1().constData()); if (sp == nullptr) return false; if (svp->r == ISR_1OFMANY || svp->r == ISR_ATMOST1) IUResetSwitch(svp); sp->s = indiCommand->elementValue.toInt() == 0 ? ISS_OFF : ISS_ON; //qDebug() << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off") << endl; clientManager->sendNewSwitch(svp); return true; } break; case INDI_NUMBER: { INumberVectorProperty *nvp = pp->getNumber(); if (nvp == nullptr) return false; INumber *np = IUFindNumber(nvp, indiCommand->indiElement.toLatin1().constData()); if (np == nullptr) return false; double value = indiCommand->elementValue.toDouble(); if (value == np->value) return true; np->value = value; //qDebug() << "Sending switch " << sp->name << " with status " << ((sp->s == ISS_ON) ? "On" : "Off") << endl; clientManager->sendNewNumber(nvp); } break; // TODO: Add set property for other types of properties default: break; } return true; } bool GenericDevice::getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, double *step) { INumberVectorProperty *nvp = baseDevice->getNumber(propName.toLatin1()); if (nvp == nullptr) return false; INumber *np = IUFindNumber(nvp, elementName.toLatin1()); if (np == nullptr) return false; *min = np->min; *max = np->max; *step = np->step; return true; } IPState GenericDevice::getState(const QString &propName) { return baseDevice->getPropertyState(propName.toLatin1().constData()); } IPerm GenericDevice::getPermission(const QString &propName) { return baseDevice->getPropertyPermission(propName.toLatin1().constData()); } INDI::Property *GenericDevice::getProperty(const QString &propName) { for (auto &oneProp : properties) { if (propName == QString(oneProp->getName())) return oneProp; } return nullptr; } void GenericDevice::resetWatchdog() { INumberVectorProperty *nvp = baseDevice->getNumber("WATCHDOG_HEARTBEAT"); if (nvp) // Send heartbeat to driver clientManager->sendNewNumber(nvp); } DeviceDecorator::DeviceDecorator(GDInterface *iPtr) { interfacePtr = iPtr; connect(iPtr, SIGNAL(Connected()), this, SIGNAL(Connected())); connect(iPtr, SIGNAL(Disconnected()), this, SIGNAL(Disconnected())); connect(iPtr, SIGNAL(propertyDefined(INDI::Property*)), this, SIGNAL(propertyDefined(INDI::Property*))); connect(iPtr, SIGNAL(propertyDeleted(INDI::Property*)), this, SIGNAL(propertyDeleted(INDI::Property*))); connect(iPtr, SIGNAL(messageUpdated(int)), this, SIGNAL(messageUpdated(int))); connect(iPtr, SIGNAL(switchUpdated(ISwitchVectorProperty*)), this, SIGNAL(switchUpdated(ISwitchVectorProperty*))); connect(iPtr, SIGNAL(numberUpdated(INumberVectorProperty*)), this, SIGNAL(numberUpdated(INumberVectorProperty*))); connect(iPtr, SIGNAL(textUpdated(ITextVectorProperty*)), this, SIGNAL(textUpdated(ITextVectorProperty*))); connect(iPtr, SIGNAL(BLOBUpdated(IBLOB*)), this, SIGNAL(BLOBUpdated(IBLOB*))); connect(iPtr, SIGNAL(lightUpdated(ILightVectorProperty*)), this, SIGNAL(lightUpdated(ILightVectorProperty*))); baseDevice = interfacePtr->getBaseDevice(); clientManager = interfacePtr->getDriverInfo()->getClientManager(); } DeviceDecorator::~DeviceDecorator() { delete (interfacePtr); } bool DeviceDecorator::runCommand(int command, void *ptr) { return interfacePtr->runCommand(command, ptr); } bool DeviceDecorator::setProperty(QObject *setPropCommand) { return interfacePtr->setProperty(setPropCommand); } void DeviceDecorator::processBLOB(IBLOB *bp) { interfacePtr->processBLOB(bp); } void DeviceDecorator::processLight(ILightVectorProperty *lvp) { interfacePtr->processLight(lvp); } void DeviceDecorator::processNumber(INumberVectorProperty *nvp) { interfacePtr->processNumber(nvp); } void DeviceDecorator::processSwitch(ISwitchVectorProperty *svp) { interfacePtr->processSwitch(svp); } void DeviceDecorator::processText(ITextVectorProperty *tvp) { interfacePtr->processText(tvp); } void DeviceDecorator::processMessage(int messageID) { interfacePtr->processMessage(messageID); } void DeviceDecorator::registerProperty(INDI::Property *prop) { interfacePtr->registerProperty(prop); } void DeviceDecorator::removeProperty(INDI::Property *prop) { interfacePtr->removeProperty(prop); } bool DeviceDecorator::setConfig(INDIConfig tConfig) { return interfacePtr->setConfig(tConfig); } DeviceFamily DeviceDecorator::getType() { return interfacePtr->getType(); } DriverInfo *DeviceDecorator::getDriverInfo() { return interfacePtr->getDriverInfo(); } DeviceInfo *DeviceDecorator::getDeviceInfo() { return interfacePtr->getDeviceInfo(); } const char *DeviceDecorator::getDeviceName() { return interfacePtr->getDeviceName(); } INDI::BaseDevice *DeviceDecorator::getBaseDevice() { return interfacePtr->getBaseDevice(); } uint32_t DeviceDecorator::getDriverInterface() { return interfacePtr->getDriverInterface(); } QList DeviceDecorator::getProperties() { return interfacePtr->getProperties(); } INDI::Property *DeviceDecorator::getProperty(const QString &propName) { return interfacePtr->getProperty(propName); } bool DeviceDecorator::isConnected() { return interfacePtr->isConnected(); } bool DeviceDecorator::Connect() { return interfacePtr->Connect(); } bool DeviceDecorator::Disconnect() { return interfacePtr->Disconnect(); } bool DeviceDecorator::getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, double *step) { return interfacePtr->getMinMaxStep(propName, elementName, min, max, step); } IPState DeviceDecorator::getState(const QString &propName) { return interfacePtr->getState(propName); } IPerm DeviceDecorator::getPermission(const QString &propName) { return interfacePtr->getPermission(propName); } ST4::ST4(INDI::BaseDevice *bdv, ClientManager *cm) { baseDevice = bdv; clientManager = cm; } -ST4::~ST4() -{ -} - const char *ST4::getDeviceName() { return baseDevice->getDeviceName(); } void ST4::setDECSwap(bool enable) { swapDEC = enable; } bool ST4::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs) { bool raOK = false, decOK = false; raOK = doPulse(ra_dir, ra_msecs); decOK = doPulse(dec_dir, dec_msecs); if (raOK && decOK) return true; else return false; } bool ST4::doPulse(GuideDirection dir, int msecs) { INumberVectorProperty *raPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_WE"); INumberVectorProperty *decPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_NS"); INumberVectorProperty *npulse = nullptr; INumber *dirPulse = nullptr; if (raPulse == nullptr || decPulse == nullptr) return false; if (dir == RA_INC_DIR || dir == RA_DEC_DIR) raPulse->np[0].value = raPulse->np[1].value = 0; else decPulse->np[0].value = decPulse->np[1].value = 0; switch (dir) { case RA_INC_DIR: dirPulse = IUFindNumber(raPulse, "TIMED_GUIDE_W"); if (dirPulse == nullptr) return false; npulse = raPulse; break; case RA_DEC_DIR: dirPulse = IUFindNumber(raPulse, "TIMED_GUIDE_E"); if (dirPulse == nullptr) return false; npulse = raPulse; break; case DEC_INC_DIR: if (swapDEC) dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_S"); else dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_N"); if (dirPulse == nullptr) return false; npulse = decPulse; break; case DEC_DEC_DIR: if (swapDEC) dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_N"); else dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_S"); if (dirPulse == nullptr) return false; npulse = decPulse; break; default: return false; } dirPulse->value = msecs; clientManager->sendNewNumber(npulse); //qDebug() << "Sending pulse for " << npulse->name << " in direction " << dirPulse->name << " for " << msecs << " ms " << endl; return true; } } diff --git a/kstars/indi/indistd.h b/kstars/indi/indistd.h index 9026e6202..6ec8e67fa 100644 --- a/kstars/indi/indistd.h +++ b/kstars/indi/indistd.h @@ -1,259 +1,259 @@ /* INDI STD Copyright (C) 2012 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Handle INDI Standard properties. */ #pragma once #include "indicommon.h" #include #include #include #define MAXINDIFILENAME 512 class ClientManager; class DriverInfo; class DeviceInfo; class QTimer; // INDI Standard Device Namespace namespace ISD { class GDSetCommand : public QObject { Q_OBJECT public: GDSetCommand(INDI_PROPERTY_TYPE inPropertyType, const QString &inProperty, const QString &inElement, QVariant qValue, QObject *parent); INDI_PROPERTY_TYPE propType; QString indiProperty; QString indiElement; QVariant elementValue; }; /** * @class GDInterface * GDInterface is the Generic Device Interface for INDI devices. It is used as part of the Decorater Pattern when initially a new INDI device is created as a * Generic Device in INDIListener. If the device registers an INDI Standard Property belonging to one specific device type (e.g. Telescope), then the device functionality * is extended to the particular device type. * * DeviceDecorator subclasses GDInterface and calls concrete decorators methods. * * @author Jasem Mutlaq */ class GDInterface : public QObject { Q_OBJECT public: // Property handling virtual void registerProperty(INDI::Property *prop) = 0; virtual void removeProperty(INDI::Property *prop) = 0; virtual void processSwitch(ISwitchVectorProperty *svp) = 0; virtual void processText(ITextVectorProperty *tvp) = 0; virtual void processNumber(INumberVectorProperty *nvp) = 0; virtual void processLight(ILightVectorProperty *lvp) = 0; virtual void processBLOB(IBLOB *bp) = 0; virtual void processMessage(int messageID) = 0; // Accessors virtual QList getProperties() = 0; virtual DeviceFamily getType() = 0; virtual DriverInfo *getDriverInfo() = 0; virtual DeviceInfo *getDeviceInfo() = 0; virtual INDI::BaseDevice *getBaseDevice() = 0; virtual uint32_t getDriverInterface() = 0; // Convenience functions virtual bool setConfig(INDIConfig tConfig) = 0; virtual const char *getDeviceName() = 0; virtual bool isConnected() = 0; virtual bool getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, double *step) = 0; virtual IPState getState(const QString &propName) = 0; virtual IPerm getPermission(const QString &propName) = 0; virtual INDI::Property *getProperty(const QString &propName) = 0; - virtual ~GDInterface() {} + virtual ~GDInterface() = default; public slots: virtual bool Connect() = 0; virtual bool Disconnect() = 0; virtual bool runCommand(int command, void *ptr = nullptr) = 0; virtual bool setProperty(QObject *) = 0; protected: DeviceFamily dType { KSTARS_CCD }; uint32_t driverInterface { 0 }; QList properties; signals: void Connected(); void Disconnected(); void switchUpdated(ISwitchVectorProperty *svp); void textUpdated(ITextVectorProperty *tvp); void numberUpdated(INumberVectorProperty *nvp); void lightUpdated(ILightVectorProperty *lvp); void BLOBUpdated(IBLOB *bp); void messageUpdated(int messageID); void propertyDefined(INDI::Property *prop); void propertyDeleted(INDI::Property *prop); }; /** * @class GenericDevice * GenericDevice is the Generic Device for INDI devices. When a new INDI device is created in INDIListener, it gets created as a GenericDevice initially. If the device * registers a standard property that is a key property to a device type family (e.g. Number property EQUATORIAL_EOD_COORD signifies a Telescope device), then the specialized version of * the device is exnteded via the Decorater Pattern. * * GenericDevice handles common functions shared across many devices such as time and location handling, configuration processing, retrieving information about properties, driver info..etc. * * @author Jasem Mutlaq */ class GenericDevice : public GDInterface { Q_OBJECT public: explicit GenericDevice(DeviceInfo &idv); - ~GenericDevice(); - - virtual void registerProperty(INDI::Property *prop); - virtual void removeProperty(INDI::Property *prop); - virtual void processSwitch(ISwitchVectorProperty *svp); - virtual void processText(ITextVectorProperty *tvp); - virtual void processNumber(INumberVectorProperty *nvp); - virtual void processLight(ILightVectorProperty *lvp); - virtual void processBLOB(IBLOB *bp); - virtual void processMessage(int messageID); - - virtual DeviceFamily getType() { return dType; } - virtual const char *getDeviceName(); - virtual DriverInfo *getDriverInfo() { return driverInfo; } - virtual DeviceInfo *getDeviceInfo() { return deviceInfo; } - virtual QList getProperties() { return properties; } - virtual uint32_t getDriverInterface() { return driverInterface; } - - virtual bool setConfig(INDIConfig tConfig); - virtual bool isConnected() { return connected; } - virtual INDI::BaseDevice *getBaseDevice() { return baseDevice; } + virtual ~GenericDevice() override = default; + + virtual void registerProperty(INDI::Property *prop) override; + virtual void removeProperty(INDI::Property *prop) override; + virtual void processSwitch(ISwitchVectorProperty *svp) override; + virtual void processText(ITextVectorProperty *tvp) override; + virtual void processNumber(INumberVectorProperty *nvp) override; + virtual void processLight(ILightVectorProperty *lvp) override; + virtual void processBLOB(IBLOB *bp) override; + virtual void processMessage(int messageID) override; + + virtual DeviceFamily getType() override { return dType; } + virtual const char *getDeviceName() override; + virtual DriverInfo *getDriverInfo() override { return driverInfo; } + virtual DeviceInfo *getDeviceInfo() override { return deviceInfo; } + virtual QList getProperties() override { return properties; } + virtual uint32_t getDriverInterface() override { return driverInterface; } + + virtual bool setConfig(INDIConfig tConfig) override; + virtual bool isConnected() override { return connected; } + virtual INDI::BaseDevice *getBaseDevice() override { return baseDevice; } virtual bool getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, - double *step); - virtual IPState getState(const QString &propName); - virtual IPerm getPermission(const QString &propName); - virtual INDI::Property *getProperty(const QString &propName); + double *step) override; + virtual IPState getState(const QString &propName) override; + virtual IPerm getPermission(const QString &propName) override; + virtual INDI::Property *getProperty(const QString &propName) override; public slots: - virtual bool Connect(); - virtual bool Disconnect(); - virtual bool runCommand(int command, void *ptr = nullptr); - virtual bool setProperty(QObject *); + virtual bool Connect() override; + virtual bool Disconnect() override; + virtual bool runCommand(int command, void *ptr = nullptr) override; + virtual bool setProperty(QObject *) override; protected slots: virtual void resetWatchdog(); protected: void createDeviceInit(); void updateTime(); void updateLocation(); private: bool connected { false }; DriverInfo *driverInfo { nullptr }; DeviceInfo *deviceInfo { nullptr }; INDI::BaseDevice *baseDevice { nullptr }; ClientManager *clientManager { nullptr }; QTimer *watchDogTimer { nullptr }; char BLOBFilename[MAXINDIFILENAME+1]; }; /** * @class DeviceDecorator * DeviceDecorator is the base decorater for all specialized devices. It extends the functionality of GenericDevice. * * @author Jasem Mutlaq */ class DeviceDecorator : public GDInterface { Q_OBJECT public: explicit DeviceDecorator(GDInterface *iPtr); ~DeviceDecorator(); virtual void registerProperty(INDI::Property *prop); virtual void removeProperty(INDI::Property *prop); virtual void processSwitch(ISwitchVectorProperty *svp); virtual void processText(ITextVectorProperty *tvp); virtual void processNumber(INumberVectorProperty *nvp); virtual void processLight(ILightVectorProperty *lvp); virtual void processBLOB(IBLOB *bp); virtual void processMessage(int messageID); virtual DeviceFamily getType(); virtual bool setConfig(INDIConfig tConfig); virtual bool isConnected(); const char *getDeviceName(); DriverInfo *getDriverInfo(); DeviceInfo *getDeviceInfo(); QList getProperties(); uint32_t getDriverInterface(); virtual INDI::BaseDevice *getBaseDevice(); bool getMinMaxStep(const QString &propName, const QString &elementName, double *min, double *max, double *step); IPState getState(const QString &propName); IPerm getPermission(const QString &propName); INDI::Property *getProperty(const QString &propName); public slots: virtual bool Connect(); virtual bool Disconnect(); virtual bool runCommand(int command, void *ptr = nullptr); virtual bool setProperty(QObject *); protected: INDI::BaseDevice *baseDevice { nullptr }; ClientManager *clientManager { nullptr }; GDInterface *interfacePtr { nullptr }; }; /** * @class ST4 * ST4 is a special class that handles ST4 commands. Since ST4 functionalty can be part of a stand alone ST4 device, * or as part of a larger device as CCD or Telescope, it is handled separately to enable one ST4 device regardless of the parent device type. * * ST4 is a hardware port dedicated to sending guiding correction pulses to the mount. * * @author Jasem Mutlaq */ class ST4 { public: ST4(INDI::BaseDevice *bdv, ClientManager *cm); - ~ST4(); + ~ST4() = default; bool doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs); bool doPulse(GuideDirection dir, int msecs); void setDECSwap(bool enable); const char *getDeviceName(); private: INDI::BaseDevice *baseDevice { nullptr }; ClientManager *clientManager { nullptr }; bool swapDEC { false }; }; } diff --git a/kstars/indi/inditelescope.cpp b/kstars/indi/inditelescope.cpp index c8b7ba87e..fef67dc2b 100644 --- a/kstars/indi/inditelescope.cpp +++ b/kstars/indi/inditelescope.cpp @@ -1,1192 +1,1188 @@ /* INDI CCD Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "inditelescope.h" #include "clientmanager.h" #include "driverinfo.h" #include "indidevice.h" #include "kstars.h" #include "Options.h" #include "skymap.h" #include "skymapcomposite.h" #include #include #include #include namespace ISD { Telescope::Telescope(GDInterface *iPtr) : DeviceDecorator(iPtr) { dType = KSTARS_TELESCOPE; minAlt = -1; maxAlt = -1; EqCoordPreviousState = IPS_IDLE; centerLockTimer = new QTimer(this); // Set it for 5 seconds for now as not to spam the display update centerLockTimer->setInterval(5000); centerLockTimer->setSingleShot(true); connect(centerLockTimer, &QTimer::timeout, this, [this]() { runCommand(INDI_CENTER_LOCK); }); } -Telescope::~Telescope() -{ -} - void Telescope::registerProperty(INDI::Property *prop) { if (!strcmp(prop->getName(), "TELESCOPE_INFO")) { INumberVectorProperty *ti = prop->getNumber(); if (ti == nullptr) return; bool aperture_ok = false, focal_ok = false; double temp = 0; INumber *aperture = IUFindNumber(ti, "TELESCOPE_APERTURE"); if (aperture && aperture->value == 0) { if (getDriverInfo()->getAuxInfo().contains("TELESCOPE_APERTURE")) { temp = getDriverInfo()->getAuxInfo().value("TELESCOPE_APERTURE").toDouble(&aperture_ok); if (aperture_ok) { aperture->value = temp; INumber *g_aperture = IUFindNumber(ti, "GUIDER_APERTURE"); if (g_aperture && g_aperture->value == 0) g_aperture->value = aperture->value; } } } INumber *focal_length = IUFindNumber(ti, "TELESCOPE_FOCAL_LENGTH"); if (focal_length && focal_length->value == 0) { if (getDriverInfo()->getAuxInfo().contains("TELESCOPE_FOCAL_LENGTH")) { temp = getDriverInfo()->getAuxInfo().value("TELESCOPE_FOCAL_LENGTH").toDouble(&focal_ok); if (focal_ok) { focal_length->value = temp; INumber *g_focal = IUFindNumber(ti, "GUIDER_FOCAL_LENGTH"); if (g_focal && g_focal->value == 0) g_focal->value = focal_length->value; } } } if (aperture_ok && focal_ok) clientManager->sendNewNumber(ti); } if (!strcmp(prop->getName(), "TELESCOPE_PARK")) { ISwitchVectorProperty *svp = prop->getSwitch(); if (svp) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if ((sp->s == ISS_ON) && svp->s == IPS_OK) { parkStatus = PARK_PARKED; QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark"); if (unParkAction) unParkAction->setEnabled(true); } else if ((sp->s == ISS_OFF) && svp->s == IPS_OK) { parkStatus = PARK_UNPARKED; QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark"); if (unParkAction) unParkAction->setEnabled(false); } } } } if (!strcmp(prop->getName(), "ALIGNMENT_POINTSET_ACTION") || !strcmp(prop->getName(), "ALIGNLIST")) m_hasAlignmentModel = true; else if (!strcmp(prop->getName(), "TELESCOPE_TRACK_STATE")) m_canControlTrack = true; else if (!strcmp(prop->getName(), "TELESCOPE_TRACK_MODE")) m_hasTrackModes = true; else if (!strcmp(prop->getName(), "TELESCOPE_TRACK_RATE")) m_hasCustomTrackRate = true; else if (!strcmp(prop->getName(), "TELESCOPE_PARK_OPTION")) m_hasCustomParking = true; DeviceDecorator::registerProperty(prop); } void Telescope::processNumber(INumberVectorProperty *nvp) { if (!strcmp(nvp->name, "EQUATORIAL_EOD_COORD")) { INumber *RA = IUFindNumber(nvp, "RA"); INumber *DEC = IUFindNumber(nvp, "DEC"); if (RA == nullptr || DEC == nullptr) return; currentCoord.setRA(RA->value); currentCoord.setDec(DEC->value); currentCoord.EquatorialToHorizontal(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); if (nvp->s == IPS_BUSY && EqCoordPreviousState != IPS_BUSY) { if (getStatus() != MOUNT_PARKING) KNotification::event(QLatin1String("SlewStarted"), i18n("Mount is slewing to target location")); } else if (EqCoordPreviousState == IPS_BUSY && nvp->s == IPS_OK) { KNotification::event(QLatin1String("SlewCompleted"), i18n("Mount arrived at target location")); double maxrad = 1000.0 / Options::zoomFactor(); currentObject = KStarsData::Instance()->skyComposite()->objectNearest(¤tCoord, maxrad); if (currentObject != nullptr) emit newTarget(currentObject->name()); } EqCoordPreviousState = nvp->s; KStars::Instance()->map()->update(); } else if (!strcmp(nvp->name, "HORIZONTAL_COORD")) { INumber *Az = IUFindNumber(nvp, "AZ"); INumber *Alt = IUFindNumber(nvp, "ALT"); if (Az == nullptr || Alt == nullptr) return; currentCoord.setAz(Az->value); currentCoord.setAlt(Alt->value); currentCoord.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); KStars::Instance()->map()->update(); } DeviceDecorator::processNumber(nvp); } void Telescope::processSwitch(ISwitchVectorProperty *svp) { bool manualMotionChanged = false; if (!strcmp(svp->name, "CONNECTION")) { ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP) { // TODO We must allow for multiple mount drivers to be online, not just one // For the actions taken, the user should be able to specifiy which mounts shall receive the commands. It could be one // or more. For now, we enable/disable telescope group on the assumption there is only one mount present. if (isConnected() == false && conSP->s == ISS_ON) KStars::Instance()->slotSetTelescopeEnabled(true); else if (isConnected() && conSP->s == ISS_OFF) { KStars::Instance()->slotSetTelescopeEnabled(false); centerLockTimer->stop(); } } } else if (!strcmp(svp->name, "TELESCOPE_PARK")) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if (svp->s == IPS_ALERT) { // If alert, set park status to whatever it was opposite to. That is, if it was parking and failed // then we set status to unparked since it did not successfully complete parking. if (parkStatus == PARK_PARKING) parkStatus = PARK_UNPARKED; else if (parkStatus == PARK_UNPARKING) parkStatus = PARK_PARKED; KNotification::event(QLatin1String("MountParkingFailed"), i18n("Mount parking failed")); } else if (svp->s == IPS_BUSY && sp->s == ISS_ON && parkStatus != PARK_PARKING) { parkStatus = PARK_PARKING; KNotification::event(QLatin1String("MountParking"), i18n("Mount parking is in progress")); currentObject = nullptr; } else if (svp->s == IPS_BUSY && sp->s == ISS_OFF && parkStatus != PARK_UNPARKING) { parkStatus = PARK_UNPARKING; KNotification::event(QLatin1String("MountUnParking"), i18n("Mount unparking is in progress")); } else if (svp->s == IPS_OK && sp->s == ISS_ON && parkStatus != PARK_PARKED) { parkStatus = PARK_PARKED; KNotification::event(QLatin1String("MountParked"), i18n("Mount parked")); currentObject = nullptr; QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark"); if (unParkAction) unParkAction->setEnabled(true); } else if ( (svp->s == IPS_OK || svp->s == IPS_IDLE) && sp->s == ISS_OFF && parkStatus != PARK_UNPARKED) { parkStatus = PARK_UNPARKED; KNotification::event(QLatin1String("MountUnparked"), i18n("Mount unparked")); currentObject = nullptr; QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark"); if (unParkAction) unParkAction->setEnabled(false); } } } else if (!strcmp(svp->name, "TELESCOPE_ABORT_MOTION")) { if (svp->s == IPS_OK) { inCustomParking = false; KNotification::event(QLatin1String("MountAborted"), i18n("Mount motion was aborted")); } } else if (!strcmp(svp->name, "TELESCOPE_MOTION_NS")) manualMotionChanged = true; else if (!strcmp(svp->name, "TELESCOPE_MOTION_WE")) manualMotionChanged = true; if (manualMotionChanged) { IPState NSCurrentMotion, WECurrentMotion; NSCurrentMotion = baseDevice->getSwitch("TELESCOPE_MOTION_NS")->s; WECurrentMotion = baseDevice->getSwitch("TELESCOPE_MOTION_WE")->s; inCustomParking = false; if (NSCurrentMotion == IPS_BUSY || WECurrentMotion == IPS_BUSY || NSPreviousState == IPS_BUSY || WEPreviousState == IPS_BUSY) { if (inManualMotion == false && ((NSCurrentMotion == IPS_BUSY && NSPreviousState != IPS_BUSY) || (WECurrentMotion == IPS_BUSY && WEPreviousState != IPS_BUSY))) { inManualMotion = true; KNotification::event(QLatin1String("MotionStarted"), i18n("Mount is manually moving")); } else if (inManualMotion && ((NSCurrentMotion != IPS_BUSY && NSPreviousState == IPS_BUSY) || (WECurrentMotion != IPS_BUSY && WEPreviousState == IPS_BUSY))) { inManualMotion = false; KNotification::event(QLatin1String("MotionStopped"), i18n("Mount is manually moving")); } NSPreviousState = NSCurrentMotion; WEPreviousState = WECurrentMotion; } } DeviceDecorator::processSwitch(svp); } void Telescope::processText(ITextVectorProperty *tvp) { DeviceDecorator::processText(tvp); } bool Telescope::canGuide() { INumberVectorProperty *raPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_WE"); INumberVectorProperty *decPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_NS"); if (raPulse && decPulse) return true; else return false; } bool Telescope::canSync() { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("ON_COORD_SET"); if (motionSP == nullptr) return false; ISwitch *syncSW = IUFindSwitch(motionSP, "SYNC"); return (syncSW != nullptr); } bool Telescope::canPark() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("TELESCOPE_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "PARK"); return (parkSW != nullptr); } bool Telescope::isSlewing() { INumberVectorProperty *EqProp = baseDevice->getNumber("EQUATORIAL_EOD_COORD"); if (EqProp == nullptr) return false; return (EqProp->s == IPS_BUSY); } bool Telescope::isInMotion() { return (isSlewing() || inManualMotion); } bool Telescope::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs) { if (canGuide() == false) return false; bool raOK = false, decOK = false; raOK = doPulse(ra_dir, ra_msecs); decOK = doPulse(dec_dir, dec_msecs); if (raOK && decOK) return true; else return false; } bool Telescope::doPulse(GuideDirection dir, int msecs) { INumberVectorProperty *raPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_WE"); INumberVectorProperty *decPulse = baseDevice->getNumber("TELESCOPE_TIMED_GUIDE_NS"); INumberVectorProperty *npulse = nullptr; INumber *dirPulse = nullptr; if (raPulse == nullptr || decPulse == nullptr) return false; switch (dir) { case RA_INC_DIR: dirPulse = IUFindNumber(raPulse, "TIMED_GUIDE_W"); if (dirPulse == nullptr) return false; npulse = raPulse; break; case RA_DEC_DIR: dirPulse = IUFindNumber(raPulse, "TIMED_GUIDE_E"); if (dirPulse == nullptr) return false; npulse = raPulse; break; case DEC_INC_DIR: dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_N"); if (dirPulse == nullptr) return false; npulse = decPulse; break; case DEC_DEC_DIR: dirPulse = IUFindNumber(decPulse, "TIMED_GUIDE_S"); if (dirPulse == nullptr) return false; npulse = decPulse; break; default: return false; } dirPulse->value = msecs; clientManager->sendNewNumber(npulse); //qDebug() << "Sending pulse for " << npulse->name << " in direction " << dirPulse->name << " for " << msecs << " ms " << endl; return true; } bool Telescope::runCommand(int command, void *ptr) { //qDebug() << "Telescope run command is called!!!" << endl; switch (command) { // set pending based on the outcome of send coords case INDI_CUSTOM_PARKING: { bool rc = false; if (ptr == nullptr) rc = sendCoords(KStars::Instance()->map()->clickedPoint()); else rc = sendCoords(static_cast(ptr)); inCustomParking = rc; } break; case INDI_SEND_COORDS: if (ptr == nullptr) sendCoords(KStars::Instance()->map()->clickedPoint()); else sendCoords(static_cast(ptr)); break; case INDI_ENGAGE_TRACKING: { SkyPoint J2000Coord(currentCoord.ra(), currentCoord.dec()); J2000Coord.apparentCoord(KStars::Instance()->data()->ut().djd(), (long double)J2000); currentCoord.setRA0(J2000Coord.ra()); currentCoord.setDec0(J2000Coord.dec()); KStars::Instance()->map()->setDestination(currentCoord); } break; case INDI_CENTER_LOCK: { //if (currentObject == nullptr || KStars::Instance()->map()->focusObject() != currentObject) if (Options::isTracking() == false || currentCoord.angularDistanceTo(KStars::Instance()->map()->focus()).Degrees() > 0.5) { SkyPoint J2000Coord(currentCoord.ra(), currentCoord.dec()); J2000Coord.apparentCoord(KStars::Instance()->data()->ut().djd(), (long double)J2000); currentCoord.setRA0(J2000Coord.ra()); currentCoord.setDec0(J2000Coord.dec()); //KStars::Instance()->map()->setClickedPoint(¤tCoord); //KStars::Instance()->map()->slotCenter(); KStars::Instance()->map()->setDestination(currentCoord); KStars::Instance()->map()->setFocusPoint(¤tCoord); //KStars::Instance()->map()->setFocusObject(currentObject); KStars::Instance()->map()->setFocusObject(nullptr); Options::setIsTracking(true); } centerLockTimer->start(); } break; case INDI_CENTER_UNLOCK: KStars::Instance()->map()->stopTracking(); centerLockTimer->stop(); break; default: return DeviceDecorator::runCommand(command, ptr); break; } return true; } bool Telescope::sendCoords(SkyPoint *ScopeTarget) { INumber *RAEle = nullptr; INumber *DecEle = nullptr; INumber *AzEle = nullptr; INumber *AltEle = nullptr; INumberVectorProperty *EqProp = nullptr; INumberVectorProperty *HorProp = nullptr; double currentRA = 0, currentDEC = 0, currentAlt = 0, currentAz = 0, targetAlt = 0; bool useJ2000(false); EqProp = baseDevice->getNumber("EQUATORIAL_EOD_COORD"); if (EqProp == nullptr) { // J2000 Property EqProp = baseDevice->getNumber("EQUATORIAL_COORD"); if (EqProp) useJ2000 = true; } HorProp = baseDevice->getNumber("HORIZONTAL_COORD"); if (EqProp && EqProp->p == IP_RO) EqProp = nullptr; if (HorProp && HorProp->p == IP_RO) HorProp = nullptr; //qDebug() << "Skymap click - RA: " << scope_target->ra().toHMSString() << " DEC: " << scope_target->dec().toDMSString(); if (EqProp) { RAEle = IUFindNumber(EqProp, "RA"); if (!RAEle) return false; DecEle = IUFindNumber(EqProp, "DEC"); if (!DecEle) return false; if (useJ2000) ScopeTarget->apparentCoord(KStars::Instance()->data()->ut().djd(), (long double)J2000); currentRA = RAEle->value; currentDEC = DecEle->value; ScopeTarget->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); } if (HorProp) { AzEle = IUFindNumber(HorProp, "AZ"); if (!AzEle) return false; AltEle = IUFindNumber(HorProp, "ALT"); if (!AltEle) return false; currentAz = AzEle->value; currentAlt = AltEle->value; } /* Could not find either properties! */ if (EqProp == nullptr && HorProp == nullptr) return false; //targetAz = ScopeTarget->az().Degrees(); targetAlt = ScopeTarget->altRefracted().Degrees(); if (minAlt != -1 && maxAlt != -1) { if (targetAlt < minAlt || targetAlt > maxAlt) { KMessageBox::error(nullptr, i18n("Requested altitude %1 is outside the specified altitude limit boundary (%2,%3).", QString::number(targetAlt, 'g', 3), QString::number(minAlt, 'g', 3), QString::number(maxAlt, 'g', 3)), i18n("Telescope Motion")); return false; } } if (targetAlt < 0) { if (KMessageBox::warningContinueCancel( nullptr, 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) { if (EqProp) { RAEle->value = currentRA; DecEle->value = currentDEC; } if (HorProp) { AzEle->value = currentAz; AltEle->value = currentAlt; } return false; } } if (EqProp) { RAEle->value = ScopeTarget->ra().Hours(); DecEle->value = ScopeTarget->dec().Degrees(); clientManager->sendNewNumber(EqProp); qCDebug(KSTARS_INDI) << "ISD:Telescope sending coords RA:" << ScopeTarget->ra().toHMSString() << "(" << RAEle->value << ") DE:" << ScopeTarget->dec().toDMSString() << "(" << DecEle->value << ")"; RAEle->value = currentRA; DecEle->value = currentDEC; } // Only send Horizontal Coord property if Equatorial is not available. else if (HorProp) { AzEle->value = ScopeTarget->az().Degrees(); AltEle->value = ScopeTarget->alt().Degrees(); clientManager->sendNewNumber(HorProp); AzEle->value = currentAz; AltEle->value = currentAlt; } double maxrad = 1000.0 / Options::zoomFactor(); currentObject = KStarsData::Instance()->skyComposite()->objectNearest(ScopeTarget, maxrad); if (currentObject) emit newTarget(currentObject->name()); return true; } bool Telescope::Slew(double ra, double dec) { SkyPoint target; target.setRA(ra); target.setDec(dec); return Slew(&target); } bool Telescope::Slew(SkyPoint *ScopeTarget) { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("ON_COORD_SET"); if (motionSP == nullptr) return false; ISwitch *slewSW = IUFindSwitch(motionSP, "TRACK"); if (slewSW == nullptr) slewSW = IUFindSwitch(motionSP, "SLEW"); if (slewSW == nullptr) return false; if (slewSW->s != ISS_ON) { IUResetSwitch(motionSP); slewSW->s = ISS_ON; clientManager->sendNewSwitch(motionSP); qCDebug(KSTARS_INDI) << "ISD:Telescope: " << slewSW->name; } return sendCoords(ScopeTarget); } bool Telescope::Sync(double ra, double dec) { SkyPoint target; target.setRA(ra); target.setDec(dec); return Sync(&target); } bool Telescope::Sync(SkyPoint *ScopeTarget) { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("ON_COORD_SET"); if (motionSP == nullptr) return false; ISwitch *syncSW = IUFindSwitch(motionSP, "SYNC"); if (syncSW == nullptr) return false; if (syncSW->s != ISS_ON) { IUResetSwitch(motionSP); syncSW->s = ISS_ON; clientManager->sendNewSwitch(motionSP); qCDebug(KSTARS_INDI) << "ISD:Telescope: Syncing..."; } return sendCoords(ScopeTarget); } bool Telescope::Abort() { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("TELESCOPE_ABORT_MOTION"); if (motionSP == nullptr) return false; ISwitch *abortSW = IUFindSwitch(motionSP, "ABORT"); if (abortSW == nullptr) return false; qCDebug(KSTARS_INDI) << "ISD:Telescope: Aborted." << endl; abortSW->s = ISS_ON; clientManager->sendNewSwitch(motionSP); inCustomParking = false; return true; } bool Telescope::Park() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("TELESCOPE_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "PARK"); if (parkSW == nullptr) return false; qCDebug(KSTARS_INDI) << "ISD:Telescope: Parking..." << endl; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Telescope::UnPark() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("TELESCOPE_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "UNPARK"); if (parkSW == nullptr) return false; qCDebug(KSTARS_INDI) << "ISD:Telescope: UnParking..." << endl; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Telescope::getEqCoords(double *ra, double *dec) { INumberVectorProperty *EqProp = nullptr; INumber *RAEle = nullptr; INumber *DecEle = nullptr; EqProp = baseDevice->getNumber("EQUATORIAL_EOD_COORD"); if (EqProp == nullptr) return false; RAEle = IUFindNumber(EqProp, "RA"); if (!RAEle) return false; DecEle = IUFindNumber(EqProp, "DEC"); if (!DecEle) return false; *ra = RAEle->value; *dec = DecEle->value; return true; } bool Telescope::MoveNS(TelescopeMotionNS dir, TelescopeMotionCommand cmd) { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("TELESCOPE_MOTION_NS"); if (motionSP == nullptr) return false; ISwitch *motionNorth = IUFindSwitch(motionSP, "MOTION_NORTH"); ISwitch *motionSouth = IUFindSwitch(motionSP, "MOTION_SOUTH"); if (motionNorth == nullptr || motionSouth == nullptr) return false; // If same direction, return if (dir == MOTION_NORTH && motionNorth->s == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF)) return true; if (dir == MOTION_SOUTH && motionSouth->s == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF)) return true; IUResetSwitch(motionSP); if (cmd == MOTION_START) { if (dir == MOTION_NORTH) motionNorth->s = ISS_ON; else motionSouth->s = ISS_ON; } clientManager->sendNewSwitch(motionSP); return true; } bool Telescope::MoveWE(TelescopeMotionWE dir, TelescopeMotionCommand cmd) { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("TELESCOPE_MOTION_WE"); if (motionSP == nullptr) return false; ISwitch *motionWest = IUFindSwitch(motionSP, "MOTION_WEST"); ISwitch *motionEast = IUFindSwitch(motionSP, "MOTION_EAST"); if (motionWest == nullptr || motionEast == nullptr) return false; // If same direction, return if (dir == MOTION_WEST && motionWest->s == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF)) return true; if (dir == MOTION_EAST && motionEast->s == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF)) return true; IUResetSwitch(motionSP); if (cmd == MOTION_START) { if (dir == MOTION_WEST) motionWest->s = ISS_ON; else motionEast->s = ISS_ON; } clientManager->sendNewSwitch(motionSP); return true; } bool Telescope::setSlewRate(int index) { ISwitchVectorProperty *slewRateSP = baseDevice->getSwitch("TELESCOPE_SLEW_RATE"); if (slewRateSP == nullptr) return false; if (index < 0 || index > slewRateSP->nsp) return false; IUResetSwitch(slewRateSP); slewRateSP->sp[index].s = ISS_ON; clientManager->sendNewSwitch(slewRateSP); return true; } void Telescope::setAltLimits(double minAltitude, double maxAltitude) { minAlt = minAltitude; maxAlt = maxAltitude; } bool Telescope::setAlignmentModelEnabled(bool enable) { bool wasExecuted = false; ISwitchVectorProperty *alignSwitch = nullptr; // For INDI Alignment Subsystem alignSwitch = baseDevice->getSwitch("ALIGNMENT_SUBSYSTEM_ACTIVE"); if (alignSwitch) { alignSwitch->sp[0].s = enable ? ISS_ON : ISS_OFF; clientManager->sendNewSwitch(alignSwitch); wasExecuted = true; } // For EQMod Alignment --- Temporary until all drivers switch fully to INDI Alignment Subsystem alignSwitch = baseDevice->getSwitch("ALIGNMODE"); if (alignSwitch) { IUResetSwitch(alignSwitch); // For now, always set alignment mode to NSTAR on enable. if (enable) alignSwitch->sp[2].s = ISS_ON; // Otherwise, set to NO ALIGN else alignSwitch->sp[0].s = ISS_ON; clientManager->sendNewSwitch(alignSwitch); wasExecuted = true; } return wasExecuted; } bool Telescope::clearAlignmentModel() { bool wasExecuted = false; // Note: Should probably use INDI Alignment Subsystem Client API in the future? ISwitchVectorProperty *clearSwitch = baseDevice->getSwitch("ALIGNMENT_POINTSET_ACTION"); ISwitchVectorProperty *commitSwitch = baseDevice->getSwitch("ALIGNMENT_POINTSET_COMMIT"); if (clearSwitch && commitSwitch) { IUResetSwitch(clearSwitch); // ALIGNMENT_POINTSET_ACTION.CLEAR clearSwitch->sp[4].s = ISS_ON; clientManager->sendNewSwitch(clearSwitch); commitSwitch->sp[0].s = ISS_ON; clientManager->sendNewSwitch(commitSwitch); wasExecuted = true; } // For EQMod Alignment --- Temporary until all drivers switch fully to INDI Alignment Subsystem clearSwitch = baseDevice->getSwitch("ALIGNLIST"); if (clearSwitch) { // ALIGNLISTCLEAR IUResetSwitch(clearSwitch); clearSwitch->sp[1].s = ISS_ON; clientManager->sendNewSwitch(clearSwitch); wasExecuted = true; } return wasExecuted; } Telescope::TelescopeStatus Telescope::getStatus() { INumberVectorProperty *EqProp = nullptr; EqProp = baseDevice->getNumber("EQUATORIAL_EOD_COORD"); if (EqProp == nullptr) return MOUNT_ERROR; switch (EqProp->s) { case IPS_IDLE: if (inManualMotion) return MOUNT_MOVING; else if (isParked()) return MOUNT_PARKED; else return MOUNT_IDLE; break; case IPS_OK: if (inManualMotion) return MOUNT_MOVING; else if (inCustomParking) { inCustomParking = false; // set CURRENT position as the desired parking position sendParkingOptionCommand(PARK_OPTION_CURRENT); // Write data to disk sendParkingOptionCommand(PARK_OPTION_WRITE_DATA); return MOUNT_TRACKING; } else return MOUNT_TRACKING; break; case IPS_BUSY: { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("TELESCOPE_PARK"); if (parkSP && parkSP->s == IPS_BUSY) return MOUNT_PARKING; else return MOUNT_SLEWING; } break; case IPS_ALERT: inCustomParking = false; return MOUNT_ERROR; break; } return MOUNT_ERROR; } const QString Telescope::getStatusString(Telescope::TelescopeStatus status) { switch (status) { case ISD::Telescope::MOUNT_IDLE: return i18n("Idle"); break; case ISD::Telescope::MOUNT_PARKED: return i18n("Parked"); break; case ISD::Telescope::MOUNT_PARKING: return i18n("Parking"); break; case ISD::Telescope::MOUNT_SLEWING: return i18n("Slewing"); break; case ISD::Telescope::MOUNT_MOVING: return i18n("Moving %1", getManualMotionString()); break; case ISD::Telescope::MOUNT_TRACKING: return i18n("Tracking"); break; case ISD::Telescope::MOUNT_ERROR: return i18n("Error"); break; } return i18n("Error"); } QString Telescope::getManualMotionString() const { ISwitchVectorProperty *movementSP = nullptr; QString NSMotion, WEMotion; movementSP = baseDevice->getSwitch("TELESCOPE_MOTION_NS"); if (movementSP) { if (movementSP->sp[MOTION_NORTH].s == ISS_ON) NSMotion = 'N'; else if (movementSP->sp[MOTION_SOUTH].s == ISS_ON) NSMotion = 'S'; } movementSP = baseDevice->getSwitch("TELESCOPE_MOTION_WE"); if (movementSP) { if (movementSP->sp[MOTION_WEST].s == ISS_ON) WEMotion = 'W'; else if (movementSP->sp[MOTION_EAST].s == ISS_ON) WEMotion = 'E'; } return QString("%1%2").arg(NSMotion, WEMotion); } bool Telescope::setTrackEnabled(bool enable) { ISwitchVectorProperty *trackSP = baseDevice->getSwitch("TELESCOPE_TRACK_STATE"); if (trackSP == nullptr) return false; ISwitch *trackON = IUFindSwitch(trackSP, "TRACK_ON"); ISwitch *trackOFF = IUFindSwitch(trackSP, "TRACK_OFF"); if (trackON == nullptr || trackOFF == nullptr) return false; trackON->s = enable ? ISS_ON : ISS_OFF; trackOFF->s= enable ? ISS_OFF : ISS_ON; clientManager->sendNewSwitch(trackSP); return true; } bool Telescope::isTracking() { return (getStatus() == MOUNT_TRACKING); } bool Telescope::setTrackMode(uint8_t index) { ISwitchVectorProperty *trackModeSP = baseDevice->getSwitch("TELESCOPE_TRACK_MODE"); if (trackModeSP == nullptr) return false; if (index >= trackModeSP->nsp) return false; IUResetSwitch(trackModeSP); trackModeSP->sp[index].s = ISS_ON; clientManager->sendNewSwitch(trackModeSP); return true; } bool Telescope::getTrackMode(uint8_t &index) { ISwitchVectorProperty *trackModeSP = baseDevice->getSwitch("TELESCOPE_TRACK_MODE"); if (trackModeSP == nullptr) return false; index = IUFindOnSwitchIndex(trackModeSP); return true; } bool Telescope::setCustomTrackRate(double raRate, double deRate) { INumberVectorProperty *trackRateNP = baseDevice->getNumber("TELESCOPE_TRACK_RATE"); if (trackRateNP == nullptr) return false; INumber *raRateN = IUFindNumber(trackRateNP, "TRACK_RATE_RA"); INumber *deRateN = IUFindNumber(trackRateNP, "TRACK_RATE_DE"); if (raRateN == nullptr || deRateN == nullptr) return false; raRateN->value = raRate; deRateN->value = deRate; clientManager->sendNewNumber(trackRateNP); return true; } bool Telescope::getCustomTrackRate(double &raRate, double &deRate) { INumberVectorProperty *trackRateNP = baseDevice->getNumber("TELESCOPE_TRACK_RATE"); if (trackRateNP == nullptr) return false; INumber *raRateN = IUFindNumber(trackRateNP, "TRACK_RATE_RA"); INumber *deRateN = IUFindNumber(trackRateNP, "TRACK_RATE_DE"); if (raRateN == nullptr || deRateN == nullptr) return false; raRate = raRateN->value; deRate = deRateN->value; return true; } bool Telescope::sendParkingOptionCommand(ParkOptionCommand command) { ISwitchVectorProperty *parkOptionsSP = baseDevice->getSwitch("TELESCOPE_PARK_OPTION"); if (parkOptionsSP == nullptr) return false; IUResetSwitch(parkOptionsSP); parkOptionsSP->sp[command].s = ISS_ON; clientManager->sendNewSwitch(parkOptionsSP); return true; } } diff --git a/kstars/indi/inditelescope.h b/kstars/indi/inditelescope.h index 1abe3b273..e3f2f5112 100644 --- a/kstars/indi/inditelescope.h +++ b/kstars/indi/inditelescope.h @@ -1,147 +1,147 @@ /* INDI CCD Copyright (C) 2012 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "indistd.h" #include "skypoint.h" class SkyObject; namespace ISD { /** * @class Telescope * device handle controlling telescope. It can slew and sync to a specific sky point and supports all standard propreties with INDI * telescope device. * * @author Jasem Mutlaq */ class Telescope : public DeviceDecorator { Q_OBJECT public: explicit Telescope(GDInterface *iPtr); - ~Telescope(); + virtual ~Telescope() override = default; typedef enum { MOTION_NORTH, MOTION_SOUTH } TelescopeMotionNS; typedef enum { MOTION_WEST, MOTION_EAST } TelescopeMotionWE; typedef enum { MOTION_START, MOTION_STOP } TelescopeMotionCommand; typedef enum { MOUNT_IDLE, MOUNT_MOVING, MOUNT_SLEWING, MOUNT_TRACKING, MOUNT_PARKING, MOUNT_PARKED, MOUNT_ERROR } TelescopeStatus; typedef enum { PARK_UNKNOWN, PARK_PARKED, PARK_PARKING, PARK_UNPARKING, PARK_UNPARKED } ParkStatus; typedef enum { PARK_OPTION_CURRENT, PARK_OPTION_DEFAULT, PARK_OPTION_WRITE_DATA } ParkOptionCommand; - void registerProperty(INDI::Property *prop); - void processSwitch(ISwitchVectorProperty *svp); - void processText(ITextVectorProperty *tvp); - void processNumber(INumberVectorProperty *nvp); + void registerProperty(INDI::Property *prop) override; + void processSwitch(ISwitchVectorProperty *svp) override; + void processText(ITextVectorProperty *tvp) override; + void processNumber(INumberVectorProperty *nvp) override; - DeviceFamily getType() { return dType; } + DeviceFamily getType() override { return dType; } // Coordinates bool getEqCoords(double *ra, double *dec); // Slew bool Slew(SkyPoint *ScopeTarget); bool Slew(double ra, double dec); // Sync bool Sync(SkyPoint *ScopeTarget); bool Sync(double ra, double dec); bool canSync(); // Tracking - bool canControlTrack() const { return m_canControlTrack; } + bool canControlTrack() const { return m_canControlTrack; } bool isTracking(); // Track Mode bool hasTrackModes() const { return m_hasTrackModes; } bool getTrackMode(uint8_t &index); // Custom Track Rate bool hasCustomTrackRate() const { return m_hasTrackModes; } bool getCustomTrackRate(double &raRate, double &deRate); // Motion bool MoveNS(TelescopeMotionNS dir, TelescopeMotionCommand cmd); bool MoveWE(TelescopeMotionWE dir, TelescopeMotionCommand cmd); bool isSlewing(); bool isInMotion(); QString getManualMotionString() const; // Guiding bool canGuide(); bool doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs); bool doPulse(GuideDirection dir, int msecs); // Parking bool canPark(); bool isParked() { return parkStatus == PARK_PARKED; } bool canCustomPark() { return m_hasCustomParking; } bool sendParkingOptionCommand(ParkOptionCommand command); // Status ParkStatus getParkStatus() { return parkStatus; } TelescopeStatus getStatus(); const QString getStatusString(TelescopeStatus status); // Altitude Limits void setAltLimits(double minAltitude, double maxAltitude); // Alignment Model bool setAlignmentModelEnabled(bool enable); bool clearAlignmentModel(); bool hasAlignmentModel() { return m_hasAlignmentModel; } protected: bool sendCoords(SkyPoint *ScopeTarget); public slots: - virtual bool runCommand(int command, void *ptr = nullptr); + virtual bool runCommand(int command, void *ptr = nullptr) override; bool Abort(); bool Park(); bool UnPark(); bool setSlewRate(int index); bool setTrackEnabled(bool enable); bool setCustomTrackRate(double raRate, double deRate); bool setTrackMode(uint8_t index); signals: void newTarget(const QString &); private: SkyPoint currentCoord; double minAlt, maxAlt; ParkStatus parkStatus = PARK_UNKNOWN; IPState EqCoordPreviousState; QTimer *centerLockTimer = nullptr; SkyObject *currentObject = nullptr; bool inManualMotion = false; bool inCustomParking = false; IPState NSPreviousState = IPS_IDLE; IPState WEPreviousState = IPS_IDLE; bool m_hasAlignmentModel = { false }; bool m_canControlTrack = { false }; bool m_hasTrackModes { false}; bool m_hasCustomTrackRate { false}; bool m_hasCustomParking { false }; }; } diff --git a/kstars/indi/opsindi.cpp b/kstars/indi/opsindi.cpp index 4af91c579..b62660b0d 100644 --- a/kstars/indi/opsindi.cpp +++ b/kstars/indi/opsindi.cpp @@ -1,133 +1,126 @@ /* INDI Options Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "opsindi.h" -#include -#include -#include +#include "ksnotification.h" +#include "kstars.h" +#include "Options.h" #include #include -#include #include - -#include "ksnotification.h" - -#include "Options.h" - -#include "kstars.h" +#include +#include +#include +#include OpsINDI::OpsINDI() : QFrame(KStars::Instance()) { setupUi(this); //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("settings"); selectFITSDirB->setIcon( QIcon::fromTheme("document-open-folder")); selectFITSDirB->setAttribute(Qt::WA_LayoutUsesWidgetRect); selectDriversDirB->setIcon( QIcon::fromTheme("document-open-folder")); selectDriversDirB->setAttribute(Qt::WA_LayoutUsesWidgetRect); #ifdef Q_OS_OSX connect(kcfg_indiServerIsInternal, SIGNAL(clicked()), this, SLOT(toggleINDIInternal())); kcfg_indiServerIsInternal->setToolTip(i18n("Internal or External INDI Server?")); connect(kcfg_indiDriversAreInternal, SIGNAL(clicked()), this, SLOT(toggleDriversInternal())); kcfg_indiDriversAreInternal->setToolTip(i18n("Internal or External INDI Drivers?")); if (Options::indiServerIsInternal()) kcfg_indiServer->setEnabled(false); if (Options::indiDriversAreInternal()) kcfg_indiDriversDir->setEnabled(false); #else kcfg_indiServerIsInternal->setVisible(false); kcfg_indiDriversAreInternal->setVisible(false); #endif connect(selectFITSDirB, SIGNAL(clicked()), this, SLOT(saveFITSDirectory())); connect(selectDriversDirB, SIGNAL(clicked()), this, SLOT(saveDriversDirectory())); connect(showLogsB, SIGNAL(clicked()), this, SLOT(slotShowLogFiles())); connect(kcfg_indiServer, SIGNAL(editingFinished()), this, SLOT(verifyINDIServer())); #ifdef Q_OS_WIN kcfg_indiServer->setEnabled(false); #endif } -OpsINDI::~OpsINDI() -{ -} - void OpsINDI::toggleINDIInternal() { kcfg_indiServer->setEnabled(!kcfg_indiServerIsInternal->isChecked()); if (kcfg_indiServerIsInternal->isChecked()) kcfg_indiServer->setText("*Internal INDI Server*"); else kcfg_indiServer->setText(KSUtils::getDefaultPath("indiServer")); } void OpsINDI::toggleDriversInternal() { kcfg_indiDriversDir->setEnabled(!kcfg_indiDriversAreInternal->isChecked()); if (kcfg_indiDriversAreInternal->isChecked()) kcfg_indiDriversDir->setText("*Internal INDI Drivers*"); else kcfg_indiDriversDir->setText(KSUtils::getDefaultPath("indiDriversDir")); KSNotification::info(i18n("You need to restart KStars for this change to take effect.")); } void OpsINDI::saveFITSDirectory() { QString dir = QFileDialog::getExistingDirectory(KStars::Instance(), i18n("FITS Default Directory"), kcfg_fitsDir->text()); if (!dir.isEmpty()) kcfg_fitsDir->setText(dir); } void OpsINDI::saveDriversDirectory() { QString dir = QFileDialog::getExistingDirectory(KStars::Instance(), i18n("INDI Drivers Directory"), kcfg_indiDriversDir->text()); if (!dir.isEmpty()) { kcfg_indiDriversDir->setText(dir); KSNotification::info(i18n("You need to restart KStars for this change to take effect.")); } } void OpsINDI::slotShowLogFiles() { QUrl path = QUrl::fromLocalFile(QDir::homePath() + "/.indi/logs"); QDesktopServices::openUrl(path); } void OpsINDI::verifyINDIServer() { // Do not verify internal if (kcfg_indiServerIsInternal->isChecked()) return; QFileInfo indiserver(kcfg_indiServer->text()); if (indiserver.exists() && indiserver.isFile() && indiserver.baseName() == "indiserver") return; KSNotification::error(i18n("%1 is not a valid INDI server binary!", kcfg_indiServer->text())); } diff --git a/kstars/indi/opsindi.h b/kstars/indi/opsindi.h index 14f6208a9..385058d90 100644 --- a/kstars/indi/opsindi.h +++ b/kstars/indi/opsindi.h @@ -1,45 +1,43 @@ /* INDI Options (subclass) Copyright (C) 2005 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef OPSINDI_H_ -#define OPSINDI_H_ +#pragma once #include "ui_opsindi.h" class KStars; class KConfigDialog; /** * @class OpsINDI * - * Enables the user to change several INDI options including default ports for common devices, time and location source, and options pertnaning to FITSViewer tool. + * Enables the user to change several INDI options including default ports for common devices, + * time and location source, and options pertnaning to FITSViewer tool. * * @author Jasem Mutlaq */ class OpsINDI : public QFrame, public Ui::OpsINDI { Q_OBJECT public: OpsINDI(); - ~OpsINDI(); + virtual ~OpsINDI() override = default; private slots: void saveFITSDirectory(); void saveDriversDirectory(); void slotShowLogFiles(); void toggleINDIInternal(); void toggleDriversInternal(); void verifyINDIServer(); private: - KConfigDialog *m_ConfigDialog; + KConfigDialog *m_ConfigDialog { nullptr }; }; - -#endif diff --git a/kstars/indi/streamwg.cpp b/kstars/indi/streamwg.cpp index b26b6da79..de0f710e8 100644 --- a/kstars/indi/streamwg.cpp +++ b/kstars/indi/streamwg.cpp @@ -1,254 +1,251 @@ /* Stream Widget Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 2004-03-16: A class to handle video streaming. */ -#include +#include "streamwg.h" + +#include "indistd.h" +#include "kstars.h" +#include "Options.h" + #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "streamwg.h" -#include "indistd.h" -#include "kstars.h" -#include "Options.h" - RecordOptions::RecordOptions(QWidget *parent) : QDialog(parent) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif setupUi(this); dirPath = QUrl::fromLocalFile(QDir::homePath()); selectDirB->setIcon( QIcon::fromTheme("document-open-folder")); connect(selectDirB, SIGNAL(clicked()), this, SLOT(selectRecordDirectory())); } void RecordOptions::selectRecordDirectory() { QString dir = QFileDialog::getExistingDirectory(KStars::Instance(), i18n("SER Record Directory"), dirPath.toLocalFile()); if (dir.isEmpty()) return; recordDirectoryEdit->setText(dir); } StreamWG::StreamWG(ISD::CCD *ccd) : QDialog(KStars::Instance()) { setupUi(this); currentCCD = ccd; streamWidth = streamHeight = -1; processStream = colorFrame = isRecording = false; options = new RecordOptions(this); connect(optionsB, SIGNAL(clicked()), options, SLOT(show())); QString filename, directory; ccd->getSERNameDirectory(filename, directory); options->recordFilenameEdit->setText(filename); options->recordDirectoryEdit->setText(directory); setWindowTitle(i18n("%1 Live Video", ccd->getDeviceName())); #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif recordIcon = QIcon::fromTheme("media-record"); stopIcon = QIcon::fromTheme("media-playback-stop"); optionsB->setIcon(QIcon::fromTheme("run-build")); resetFrameB->setIcon(QIcon::fromTheme("view-restore")); connect(resetFrameB, SIGNAL(clicked()), this, SLOT(resetFrame())); recordB->setIcon(recordIcon); connect(recordB, SIGNAL(clicked()), this, SLOT(toggleRecord())); connect(ccd, SIGNAL(videoRecordToggled(bool)), this, SLOT(updateRecordStatus(bool))); connect(videoFrame, SIGNAL(newSelection(QRect)), this, SLOT(setStreamingFrame(QRect))); resize(Options::streamWindowWidth(), Options::streamWindowHeight()); connect(currentCCD, SIGNAL(newFPS(double,double)), this, SLOT(updateFPS(double,double))); } -StreamWG::~StreamWG() -{ -} - QSize StreamWG::sizeHint() const { QSize size(Options::streamWindowWidth(), Options::streamWindowHeight()); return size; } void StreamWG::closeEvent(QCloseEvent *ev) { processStream = false; Options::setStreamWindowWidth(width()); Options::setStreamWindowHeight(height()); ev->accept(); emit hidden(); } void StreamWG::setColorFrame(bool color) { colorFrame = color; } void StreamWG::enableStream(bool enable) { if (enable) { processStream = true; show(); } else { processStream = false; instFPS->setText("--"); avgFPS->setText("--"); hide(); } } void StreamWG::setSize(int wd, int ht) { // Initial resize /*if (streamWidth == -1) resize(Options::streamWindowWidth() + layout()->margin() * 2, Options::streamWindowHeight()+ playB->height() + layout()->margin() * 4 + layout()->spacing());*/ streamWidth = wd; streamHeight = ht; videoFrame->setSize(wd, ht); } /*void StreamWG::resizeEvent(QResizeEvent *ev) { streamFrame->resize(ev->size().width() - layout()->margin() * 2, ev->size().height() - playB->height() - layout()->margin() * 4 - layout()->spacing()); }*/ void StreamWG::updateRecordStatus(bool enabled) { if ((enabled && isRecording) || (!enabled && !isRecording)) return; isRecording = enabled; if (isRecording) { recordB->setIcon(stopIcon); recordB->setToolTip(i18n("Stop recording")); } else { recordB->setIcon(recordIcon); recordB->setToolTip(i18n("Start recording")); } } void StreamWG::toggleRecord() { if (isRecording) { recordB->setIcon(recordIcon); isRecording = false; recordB->setToolTip(i18n("Start recording")); currentCCD->stopRecording(); } else { currentCCD->setSERNameDirectory(options->recordFilenameEdit->text(), options->recordDirectoryEdit->text()); // Save config in INDI so the filename and directory templates are reloaded next time currentCCD->setConfig(SAVE_CONFIG); if (options->recordUntilStoppedR->isChecked()) { isRecording = currentCCD->startRecording(); } else if (options->recordDurationR->isChecked()) { isRecording = currentCCD->startDurationRecording(options->durationSpin->value()); } else { isRecording = currentCCD->startFramesRecording(options->framesSpin->value()); } if (isRecording) { recordB->setIcon(stopIcon); recordB->setToolTip(i18n("Stop recording")); } } } void StreamWG::newFrame(IBLOB *bp) { bool rc = videoFrame->newFrame(bp); if (rc == false) qWarning() << "Failed to load video frame."; } void StreamWG::resetFrame() { currentCCD->resetStreamingFrame(); } void StreamWG::setStreamingFrame(QRect newFrame) { int w = newFrame.width(); // Must be divisable by 4 while (w % 4) { w++; } currentCCD->setStreamingFrame(newFrame.x(), newFrame.y(), w, newFrame.height()); } void StreamWG::updateFPS(double instantFPS, double averageFPS) { instFPS->setText(QString::number(instantFPS, 'f', 1)); avgFPS->setText(QString::number(averageFPS, 'f', 1)); } diff --git a/kstars/indi/streamwg.h b/kstars/indi/streamwg.h index f6ab36625..8de5c86f7 100644 --- a/kstars/indi/streamwg.h +++ b/kstars/indi/streamwg.h @@ -1,92 +1,88 @@ /* Stream Widget Copyright (C) 2003-2017 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef STREAMWG_H -#define STREAMWG_H +#pragma once + +#include "indi/indiccd.h" +#include "ui_streamform.h" +#include "ui_recordingoptions.h" + +#include -#include -#include -#include -#include #include -#include #include #include #include - -#include - -#include "ui_streamform.h" -#include "ui_recordingoptions.h" - -#include "indi/indiccd.h" +#include +#include +#include +#include +#include class RecordOptions : public QDialog, public Ui::recordingOptions { Q_OBJECT public: explicit RecordOptions(QWidget *parent); public slots: void selectRecordDirectory(); private: QUrl dirPath; friend class StreamWG; }; class StreamWG : public QDialog, public Ui::streamForm { Q_OBJECT public: explicit StreamWG(ISD::CCD *ccd); - ~StreamWG(); + virtual ~StreamWG() override = default; void setColorFrame(bool color); void setSize(int wd, int ht); void enableStream(bool enable); bool isStreamEnabled() { return processStream; } void newFrame(IBLOB *bp); int getStreamWidth() { return streamWidth; } int getStreamHeight() { return streamHeight; } protected: - void closeEvent(QCloseEvent *ev); - QSize sizeHint() const; + void closeEvent(QCloseEvent *ev) override; + QSize sizeHint() const override; public slots: void toggleRecord(); void updateRecordStatus(bool enabled); void resetFrame(); protected slots: void setStreamingFrame(QRect newFrame); void updateFPS(double instantFPS, double averageFPS); signals: void hidden(); private: bool processStream; int streamWidth, streamHeight; bool colorFrame, isRecording; QIcon recordIcon, stopIcon; ISD::CCD *currentCCD; RecordOptions *options; }; - -#endif diff --git a/kstars/indi/videowg.cpp b/kstars/indi/videowg.cpp index e7b182f4e..956f5a944 100644 --- a/kstars/indi/videowg.cpp +++ b/kstars/indi/videowg.cpp @@ -1,118 +1,114 @@ /* Video Stream Frame Copyright (C) 2017 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "videowg.h" #include #include #include #include VideoWG::VideoWG(QWidget *parent) : QLabel(parent) { streamImage.reset(new QImage()); grayTable.resize(256); for (int i = 0; i < 256; i++) grayTable[i] = qRgb(i, i, i); } -VideoWG::~VideoWG() -{ -} - bool VideoWG::newFrame(IBLOB *bp) { if (bp->size <= 0) return false; QString format(bp->format); format.remove('.'); format.remove("stream_"); bool rc = false; if (QImageReader::supportedImageFormats().contains(format.toLatin1())) rc = streamImage->loadFromData(static_cast(bp->blob), bp->size); else if (static_cast(bp->size) == totalBaseCount) { streamImage.reset(new QImage(static_cast(bp->blob), streamW, streamH, QImage::Format_Indexed8)); streamImage->setColorTable(grayTable); rc = !streamImage->isNull(); } else if (static_cast(bp->size) == totalBaseCount*3) { streamImage.reset(new QImage(static_cast(bp->blob), streamW, streamH, QImage::Format_RGB888)); rc = !streamImage->isNull(); } if (rc) { kPix = QPixmap::fromImage(streamImage->scaled(size(), Qt::KeepAspectRatio)); setPixmap(kPix); } return rc; } bool VideoWG::save(const QString &filename, const char *format) { return kPix.save(filename, format); } void VideoWG::setSize(uint16_t w, uint16_t h) { streamW = w; streamH = h; totalBaseCount = w * h; } void VideoWG::resizeEvent(QResizeEvent *ev) { setPixmap(QPixmap::fromImage(streamImage->scaled(ev->size(), Qt::KeepAspectRatio))); ev->accept(); } void VideoWG::mousePressEvent(QMouseEvent *event) { origin = event->pos(); if (!rubberBand) rubberBand = new QRubberBand(QRubberBand::Rectangle, this); rubberBand->setGeometry(QRect(origin, QSize())); rubberBand->show(); } void VideoWG::mouseMoveEvent(QMouseEvent *event) { rubberBand->setGeometry(QRect(origin, event->pos()).normalized()); } void VideoWG::mouseReleaseEvent(QMouseEvent *) { rubberBand->hide(); QRect rawSelection = rubberBand->geometry(); int pixmapX = (width() - kPix.width()) / 2; int pixmapY = (height() - kPix.height()) / 2; QRect finalSelection; double scaleX = static_cast(streamImage->width()) / kPix.width(); double scaleY = static_cast(streamImage->height()) / kPix.height(); finalSelection.setX((rawSelection.x() - pixmapX) * scaleX); finalSelection.setY((rawSelection.y() - pixmapY) * scaleY); finalSelection.setWidth(rawSelection.width() * scaleX); finalSelection.setHeight(rawSelection.height() * scaleY); emit newSelection(finalSelection); // determine selection, for example using QRect::intersects() // and QRect::contains(). } diff --git a/kstars/indi/videowg.h b/kstars/indi/videowg.h index 9bc4d6e25..d329566fb 100644 --- a/kstars/indi/videowg.h +++ b/kstars/indi/videowg.h @@ -1,57 +1,57 @@ /* Video Stream Frame Copyright (C) 2017 Jasem Mutlaq (mutlaqja@ikarustech.com) This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include #include #include #include #include #include class QImage; class QRubberBand; class VideoWG : public QLabel { Q_OBJECT public: explicit VideoWG(QWidget *parent = nullptr); - ~VideoWG(); + virtual ~VideoWG() override = default; bool newFrame(IBLOB *bp); bool save(const QString &filename, const char *format); void setSize(uint16_t w, uint16_t h); protected: - virtual void resizeEvent(QResizeEvent *ev); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *); + virtual void resizeEvent(QResizeEvent *ev) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *) override; signals: void newSelection(QRect); private: uint16_t streamW { 0 }; uint16_t streamH { 0 }; uint32_t totalBaseCount { 0 }; QVector grayTable; std::unique_ptr streamImage; QPixmap kPix; QRubberBand *rubberBand { nullptr }; QPoint origin; }; diff --git a/kstars/ksnumbers.cpp b/kstars/ksnumbers.cpp index f2dc8bab2..1be25fc89 100644 --- a/kstars/ksnumbers.cpp +++ b/kstars/ksnumbers.cpp @@ -1,383 +1,380 @@ /*************************************************************************** ksnumbers.cpp - description ------------------- begin : Sun Jan 13 2002 copyright : (C) 2002-2005 by Jason Harris email : kstars@30doradus.org copyright : (C) 2004-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 "ksnumbers.h" + #include "kstarsdatetime.h" //for J2000 define // 63 elements const int KSNumbers::arguments[NUTTERMS][5] = { { 0, 0, 0, 0, 1 }, { -2, 0, 0, 2, 2 }, { 0, 0, 0, 2, 2 }, { 0, 0, 0, 0, 2 }, { 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0 }, { -2, 1, 0, 2, 2 }, { 0, 0, 0, 2, 1 }, { 0, 0, 1, 2, 2 }, { -2, -1, 0, 2, 2 }, { -2, 0, 1, 0, 0 }, { -2, 0, 0, 2, 1 }, { 0, 0, -1, 2, 2 }, { 2, 0, 0, 0, 0 }, { 0, 0, 1, 0, 1 }, { 2, 0, -1, 2, 2 }, { 0, 0, -1, 0, 1 }, { 0, 0, 1, 2, 1 }, { -2, 0, 2, 0, 0 }, { 0, 0, -2, 2, 1 }, { 2, 0, 0, 2, 2 }, { 0, 0, 2, 2, 2 }, { 0, 0, 2, 0, 0 }, { -2, 0, 1, 2, 2 }, { 0, 0, 0, 2, 0 }, { -2, 0, 0, 2, 0 }, { 0, 0, -1, 2, 1 }, { 0, 2, 0, 0, 0 }, { 2, 0, -1, 0, 1 }, { -2, 2, 0, 2, 2 }, { 0, 1, 0, 0, 1 }, { -2, 0, 1, 0, 1 }, { 0, -1, 0, 0, 1 }, { 0, 0, 2, -2, 0 }, { 2, 0, -1, 2, 1 }, { 2, 0, 1, 2, 2 }, { 0, 1, 0, 2, 2 }, { -2, 1, 1, 0, 0 }, { 0, -1, 0, 2, 2 }, { 2, 0, 0, 2, 1 }, { 2, 0, 1, 0, 0 }, { -2, 0, 2, 2, 2 }, { -2, 0, 1, 2, 1 }, { 2, 0, -2, 0, 1 }, { 2, 0, 0, 0, 1 }, { 0, -1, 1, 0, 0 }, { -2, -1, 0, 2, 1 }, { -2, 0, 0, 0, 1 }, { 0, 0, 2, 2, 1 }, { -2, 0, 2, 0, 1 }, { -2, 1, 0, 2, 1 }, { 0, 0, 1, -2, 0 }, { -1, 0, 1, 0, 0 }, { -2, 1, 0, 0, 0 }, { 1, 0, 0, 0, 0 }, { 0, 0, 1, 2, 0 }, { 0, 0, -2, 2, 2 }, { -1, -1, 1, 0, 0 }, { 0, 1, 1, 0, 0 }, { 0, -1, 1, 2, 2 }, { 2, -1, -1, 2, 2 }, { 0, 0, 3, 2, 2 }, { 2, -1, 0, 2, 2 } }; const int KSNumbers::amp[NUTTERMS][4] = { { -171996, -1742, 92025, 89 }, { -13187, -16, 5736, -31 }, { -2274, -2, 977, -5 }, { 2062, 2, -895, 5 }, { 1426, -34, 54, -1 }, { 712, 1, -7, 0 }, { -517, 12, 224, -6 }, { -386, -4, 200, 0 }, { -301, 0, 129, -1 }, { 217, -5, -95, 3 }, { -158, 0, 0, 0 }, { 129, 1, -70, 0 }, { 123, 0, -53, 0 }, { 63, 0, 0, 0 }, { 63, 1, -33, 0 }, { -59, 0, 26, 0 }, { -58, -1, 32, 0 }, { -51, 0, 27, 0 }, { 48, 0, 0, 0 }, { 46, 0, -24, 0 }, { -38, 0, 16, 0 }, { -31, 0, 13, 0 }, { 29, 0, 0, 0 }, { 29, 0, -12, 0 }, { 26, 0, 0, 0 }, { -22, 0, 0, 0 }, { 21, 0, -10, 0 }, { 17, -1, 0, 0 }, { 16, 0, -8, 0 }, { -16, 1, 7, 0 }, { -15, 0, 9, 0 }, { -13, 0, 7, 0 }, { -12, 0, 6, 0 }, { 11, 0, 0, 0 }, { -10, 0, 5, 0 }, { -8, 0, 3, 0 }, { 7, 0, -3, 0 }, { -7, 0, 0, 0 }, { -7, 0, 3, 0 }, { -7, 0, 3, 0 }, { 6, 0, 0, 0 }, { 6, 0, -3, 0 }, { 6, 0, -3, 0 }, { -6, 0, 3, 0 }, { -6, 0, 3, 0 }, { 5, 0, 0, 0 }, { -5, 0, 3, 0 }, { -5, 0, 3, 0 }, { -5, 0, 3, 0 }, { 4, 0, 0, 0 }, { 4, 0, 0, 0 }, { 4, 0, 0, 0 }, { -4, 0, 0, 0 }, { -4, 0, 0, 0 }, { -4, 0, 0, 0 }, { 3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 }, { -3, 0, 0, 0 } }; KSNumbers::KSNumbers(long double jd) { K.setD(20.49552 / 3600.); //set the constant of aberration // ecliptic longitude of earth's perihelion, source: http://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html; FIXME: We should correct this, as it changes with time. See the commit log for an order of magnitude estimate of the error. // FIXME: FIXME above seems to have been addressed? What is deltaEcLong? -- asimha P.setD(102.94719); // JM 2017-09-04: Yes the longitude of the perihelion changes with time. The value below is the one for 04-09-2017 obtained from JPL Horizons system. // But some Earth orbital elements MUST be updated periodically like any other solar system body. // This is an important FIXME! // deltaEcLong is used in nutation calculations. Can P be obtained computationally? //P.setD(104.8089842092676); computeConstantValues(); updateValues(jd); } void KSNumbers::computeConstantValues() { // Compute those numbers that need to be computed only // once. // // Ideally, these should be computed at compile-time. When we // upgrade to C++11, we can make this function static and // constexpr (maybe?) But even otherwise, the overhead is very // negligible. //Compute Precession Matrices from B1950 to 1984 using Newcomb formulae XB.setD(0.217697); YB.setD(0.189274); ZB.setD(0.217722); XB.SinCos(SXB, CXB); YB.SinCos(SYB, CYB); ZB.SinCos(SZB, CZB); //P1B is used to precess from 1984 to B1950: P1B(0, 0) = CXB * CYB * CZB - SXB * SZB; P1B(1, 0) = CXB * CYB * SZB + SXB * CZB; P1B(2, 0) = CXB * SYB; P1B(0, 1) = -1.0 * SXB * CYB * CZB - CXB * SZB; P1B(1, 1) = -1.0 * SXB * CYB * SZB + CXB * CZB; P1B(2, 1) = -1.0 * SXB * SYB; P1B(0, 2) = -1.0 * SYB * CZB; P1B(1, 2) = -1.0 * SYB * SZB; P1B(2, 2) = CYB; //P2 is used to precess from B1950 to 1984 (it is the transpose of P1) // FIXME: This can be optimized by taking the transpose of P1 instead of recomputing it from scratch P2B(0, 0) = CXB * CYB * CZB - SXB * SZB; P2B(1, 0) = -1.0 * SXB * CYB * CZB - CXB * SZB; P2B(2, 0) = -1.0 * SYB * CZB; P2B(0, 1) = CXB * CYB * SZB + SXB * CZB; P2B(1, 1) = -1.0 * SXB * CYB * SZB + CXB * CZB; P2B(2, 1) = -1.0 * SYB * SZB; P2B(0, 2) = CXB * SYB; P2B(1, 2) = -1.0 * SXB * SYB; P2B(2, 2) = CYB; } -KSNumbers::~KSNumbers() -{ -} - void KSNumbers::updateValues(long double jd) { dms arg; double args, argc; days = jd; // FIXME: What is the source for these algorithms / polynomials / numbers? -- asimha //Julian Centuries since J2000.0 T = (jd - J2000) / 36525.; // Julian Millenia since J2000.0 jm = T / 10.0; double T2 = T * T; double T3 = T2 * T; //Sun's Mean Longitude L.setD(280.46645 + 36000.76983 * T + 0.0003032 * T2); //Mean elongation of the Moon from the Sun D.setD(297.85036 + 445267.111480 * T - 0.0019142 * T2 + T3 / 189474.); //Sun's Mean Anomaly M.setD(357.52910 + 35999.05030 * T - 0.0001559 * T2 - 0.00000048 * T3); //Moon's Mean Anomaly MM.setD(134.96298 + 477198.867398 * T + 0.0086972 * T2 + T3 / 56250.0); //Moon's Mean Longitude LM.setD(218.3164591 + 481267.88134236 * T - 0.0013268 * T2 + T3 / 538841. - T * T * T * T / 6519400.); //Moon's argument of latitude F.setD(93.27191 + 483202.017538 * T - 0.0036825 * T2 + T3 / 327270.); //Longitude of Moon's Ascending Node O.setD(125.04452 - 1934.136261 * T + 0.0020708 * T2 + T3 / 450000.0); //Earth's orbital eccentricity e = 0.016708617 - 0.000042037 * T - 0.0000001236 * T2; double C = (1.914600 - 0.004817 * T - 0.000014 * T2) * sin(M.radians()) + (0.019993 - 0.000101 * T) * sin(2.0 * M.radians()) + 0.000290 * sin(3.0 * M.radians()); //Sun's True Longitude L0.setD(L.Degrees() + C); //Sun's True Anomaly M0.setD(M.Degrees() + C); //Obliquity of the Ecliptic // FIXME: This can be optimized by using the fact that U^3 = U^2 * U, so we can reduce the number of multiplications double U = T / 100.0; double dObliq = -4680.93 * U - 1.55 * U * U + 1999.25 * U * U * U - 51.38 * U * U * U * U - 249.67 * U * U * U * U * U - 39.05 * U * U * U * U * U * U + 7.12 * U * U * U * U * U * U * U + 27.87 * U * U * U * U * U * U * U * U + 5.79 * U * U * U * U * U * U * U * U * U + 2.45 * U * U * U * U * U * U * U * U * U * U; Obliquity.setD(23.43929111 + dObliq / 3600.0); //Nutation parameters dms L2, M2, O2; double sin2L, cos2L, sin2M, cos2M; double sinO, cosO, sin2O, cos2O; O2.setD(2.0 * O.Degrees()); L2.setD(2.0 * L.Degrees()); //twice mean ecl. long. of Sun M2.setD(2.0 * LM.Degrees()); //twice mean ecl. long. of Moon O.SinCos(sinO, cosO); O2.SinCos(sin2O, cos2O); L2.SinCos(sin2L, cos2L); M2.SinCos(sin2M, cos2M); // deltaEcLong = ( -17.2*sinO - 1.32*sin2L - 0.23*sin2M + 0.21*sin2O)/3600.0; //Ecl. long. correction // deltaObliquity = ( 9.2*cosO + 0.57*cos2L + 0.10*cos2M - 0.09*cos2O)/3600.0; //Obliq. correction deltaEcLong = 0.; deltaObliquity = 0.; for (unsigned int i = 0; i < NUTTERMS; i++) { arg.setD(arguments[i][0] * D.Degrees() + arguments[i][1] * M.Degrees() + arguments[i][2] * MM.Degrees() + arguments[i][3] * F.Degrees() + arguments[i][4] * O.Degrees()); arg.SinCos(args, argc); deltaEcLong += (amp[i][0] + amp[i][1] / 10. * T) * args * 1e-4; deltaObliquity += (amp[i][2] + amp[i][3] / 10. * T) * argc * 1e-4; } deltaEcLong /= 3600.0; deltaObliquity /= 3600.0; //Compute Precession Matrices: XP.setD(0.6406161 * T + 0.0000839 * T2 + 0.0000050 * T3); YP.setD(0.5567530 * T - 0.0001185 * T2 - 0.0000116 * T3); ZP.setD(0.6406161 * T + 0.0003041 * T2 + 0.0000051 * T3); XP.SinCos(SX, CX); YP.SinCos(SY, CY); ZP.SinCos(SZ, CZ); //P1 is used to precess from any epoch to J2000 // Note: P1 is a rotation matrix, and P2 is its transpose = inverse (also a rotation matrix) P1(0, 0) = CX * CY * CZ - SX * SZ; P1(1, 0) = CX * CY * SZ + SX * CZ; P1(2, 0) = CX * SY; P1(0, 1) = -1.0 * SX * CY * CZ - CX * SZ; P1(1, 1) = -1.0 * SX * CY * SZ + CX * CZ; P1(2, 1) = -1.0 * SX * SY; P1(0, 2) = -1.0 * SY * CZ; P1(1, 2) = -1.0 * SY * SZ; P1(2, 2) = CY; //P2 is used to precess from J2000 to any other epoch (it is the transpose of P1) // FIXME: More optimization -- just use P1(j, i) instead of P2(i, j) in code P2(0, 0) = P1(0, 0); P2(1, 0) = P1(0, 1); P2(2, 0) = P1(0, 2); P2(0, 1) = P1(1, 0); P2(1, 1) = P1(1, 1); P2(2, 1) = P1(1, 2); P2(0, 2) = P1(2, 0); P2(1, 2) = P1(2, 1); P2(2, 2) = P1(2, 2); // Mean longitudes for the planets. radians // // TODO Pasar a grados [Google Translate says "Jump to Degrees". --asimha] double LVenus = 3.1761467 + 1021.3285546 * T; // Venus double LMars = 1.7534703 + 628.3075849 * T; // Mars double LEarth = 6.2034809 + 334.0612431 * T; // Earth double LJupiter = 0.5995465 + 52.9690965 * T; // Jupiter double LSaturn = 0.8740168 + 21.3299095 * T; // Saturn double LNeptune = 5.3118863 + 3.8133036 * T; // Neptune double LUranus = 5.4812939 + 7.4781599 * T; // Uranus double LMRad = 3.8103444 + 8399.6847337 * T; // Moon double DRad = 5.1984667 + 7771.3771486 * T; double MMRad = 2.3555559 + 8328.6914289 * T; // Moon double FRad = 1.6279052 + 8433.4661601 * T; /** Contibutions to the velocity of the Earth referred to the barycenter of the solar system in the J2000 equatorial system Velocities 10^{-8} AU/day Ron & Vondrak method **/ double vondrak[36][7] = { { LMars, -1719914 - 2 * T, -25, 25 - 13 * T, 1578089 + 156 * T, 10 + 32 * T, 684185 - 358 * T }, { 2 * LMars, 6434 + 141 * T, 28007 - 107 * T, 25697 - 95 * T, -5904 - 130 * T, 11141 - 48 * T, -2559 - 55 * T }, { LJupiter, 715, 0, 6, -657, -15, -282 }, { LMRad, 715, 0, 0, -656, 0, -285 }, { 3 * LMars, 486 - 5 * T, -236 - 4 * T, -216 - 4 * T, -446 + 5 * T, -94, -193 }, { LSaturn, 159, 0, 2, -147, -6, -61 }, { FRad, 0, 0, 0, 26, 0, -59 }, { LMRad + MMRad, 39, 0, 0, -36, 0, -16 }, { 2 * LJupiter, 33, -10, -9, -30, -5, -13 }, { 2 * LMars - LJupiter, 31, 1, 1, -28, 0, -12 }, { 3 * LMars - 8 * LEarth + 3 * LJupiter, 8, -28, 25, 8, 11, 3 }, { 5 * LMars - 8 * LEarth + 3 * LJupiter, 8, -28, -25, -8, -11, -3 }, { 2 * LVenus - LMars, 21, 0, 0, -19, 0, -8 }, { LVenus, -19, 0, 0, 17, 0, 8 }, { LNeptune, 17, 0, 0, -16, 0, -7 }, { LMars - 2 * LJupiter, 16, 0, 0, 15, 1, 7 }, { LUranus, 16, 0, 1, -15, -3, -6 }, { LMars + LJupiter, 11, -1, -1, -10, -1, -5 }, { 2 * LVenus - 2 * LMars, 0, -11, -10, 0, -4, 0 }, { LMars - LJupiter, -11, -2, -2, 9, -1, 4 }, { 4 * LMars, -7, -8, -8, 6, -3, 3 }, { 3 * LMars - 2 * LJupiter, -10, 0, 0, 9, 0, 4 }, { LVenus - 2 * LMars, -9, 0, 0, -9, 0, -4 }, { 2 * LVenus - 3 * LMars, -9, 0, 0, -8, 0, -4 }, { 2 * LSaturn, 0, -9, -8, 0, -3, 0 }, { 2 * LVenus - 4 * LMars, 0, -9, 8, 0, 3, 0 }, { 3 * LMars - 2 * LEarth, 8, 0, 0, -8, 0, -3 }, { LMRad + 2 * DRad - MMRad, 8, 0, 0, -7, 0, -3 }, { 8 * LVenus - 12 * LMars, -4, -7, -6, 4, -3, 2 }, { 8 * LVenus - 14 * LMars, -4, -7, 6, -4, 3, -2 }, { 2 * LEarth, -6, -5, -4, 5, -2, 2 }, { 3 * LVenus - 4 * LMars, -1, -1, -2, -7, 1, -4 }, { 2 * LMars - 2 * LJupiter, 4, -6, -5, -4, -2, -2 }, { 3 * LVenus - 3 * LMars, 0, -7, -6, 0, -3, 0 }, { 2 * LMars - 2 * LEarth, 5, -5, -4, -5, -2, -2 }, { LMRad - 2 * DRad, 5, 0, 0, -5, 0, -2 } }; dms anglev; double sa, ca; // Vearth X component vearth[0] = 0.; // Vearth Y component vearth[1] = 0.; // Vearth Z component vearth[2] = 0.; for (unsigned int i = 0; i < 36; i++) { anglev.setRadians(vondrak[i][0]); anglev.SinCos(sa, ca); for (unsigned int j = 0; j < 3; j++) { vearth[j] += vondrak[i][2 * j + 1] * sa + vondrak[i][2 * j + 2] * ca; } } const double UA2km = 1.49597870 / 86400.; // 10^{-8}*UA/dia -> km/s for (unsigned int j = 0; j < 3; j++) { vearth[j] = vearth[j] * UA2km; } } diff --git a/kstars/ksnumbers.h b/kstars/ksnumbers.h index 9e9519bd4..573925249 100644 --- a/kstars/ksnumbers.h +++ b/kstars/ksnumbers.h @@ -1,158 +1,158 @@ /*************************************************************************** ksnumbers.h - description ------------------- begin : Sun Jan 13 2002 copyright : (C) 2002-2005 by Jason Harris email : kstars@30doradus.org copyright : (C) 2004-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "cachingdms.h" #if __GNUC__ > 5 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-attributes" #endif #if __GNUC__ > 6 #pragma GCC diagnostic ignored "-Wint-in-bool-context" #endif #include #if __GNUC__ > 5 #pragma GCC diagnostic pop #endif #define NUTTERMS 63 /** @class KSNumbers * *There are several time-dependent values used in position calculations, *that are not specific to an object. This class provides *storage for these values, and methods for calculating them for a given date. *The numbers include solar data like the true/mean solar anomalies *and longitudes, the longitude of the Earth's perihelion, the *eccentricity of Earth's orbit, the *constant of aberration, the obliquity of the Ecliptic, the effects of *Nutation (delta Obliquity and delta Ecliptic longitude), *the Julian Day/Century/Millenium, and arrays for computing the precession. *@short Store several time-dependent astronomical quantities. *@author Jason Harris *@version 1.0 */ class KSNumbers { public: /** - * Constructor. - *@param jd Julian Day for which the new instance is initialized - */ + * Constructor. + * @param jd Julian Day for which the new instance is initialized + */ explicit KSNumbers(long double jd); + ~KSNumbers() = default; - /**Destructor (empty). */ - ~KSNumbers(); - - /** @return the current Obliquity (the angle of inclination between - *the celestial equator and the ecliptic) - */ + /** + * @return the current Obliquity (the angle of inclination between + * the celestial equator and the ecliptic) + */ inline const CachingDms *obliquity() const { return &Obliquity; } /** @return the constant of aberration (20.49 arcsec). */ inline dms constAberr() const { return K; } /** @return the mean solar anomaly. */ inline dms sunMeanAnomaly() const { return M; } /** @return the mean solar longitude. */ inline dms sunMeanLongitude() const { return L; } /** @return the true solar anomaly. */ inline dms sunTrueAnomaly() const { return M0; } /** @return the true solar longitude. */ inline CachingDms sunTrueLongitude() const { return L0; } /** @return the longitude of the Earth's perihelion point. */ inline CachingDms earthPerihelionLongitude() const { return P; } /** @return eccentricity of Earth's orbit.*/ inline double earthEccentricity() const { return e; } /** @return the change in obliquity due to the nutation of * Earth's orbit. Value is in degrees */ inline double dObliq() const { return deltaObliquity; } /** @return the change in Ecliptic Longitude due to nutation. * Value is in degrees. */ inline double dEcLong() const { return deltaEcLong; } /** @return Julian centuries since J2000*/ inline double julianCenturies() const { return T; } /** @return Julian Day*/ inline long double julianDay() const { return days; } /** @return Julian Millenia since J2000*/ inline double julianMillenia() const { return jm; } /** @return element of P1 precession array at position (i1, i2) */ inline double p1(int i1, int i2) const { return P1(i1, i2); } /** @return element of P2 precession array at position (i1, i2) */ inline double p2(int i1, int i2) const { return P2(i1, i2); } /** @return element of P1B precession array at position (i1, i2) */ inline double p1b(int i1, int i2) const { return P1B(i1, i2); } /** @return element of P2B precession array at position (i1, i2) */ inline double p2b(int i1, int i2) const { return P2B(i1, i2); } /** @return the precession matrix directly **/ inline const Eigen::Matrix3d &p2() const { return P1; } inline const Eigen::Matrix3d &p1() const { return P2; } inline const Eigen::Matrix3d &p1b() const { return P1B; } inline const Eigen::Matrix3d &p2b() const { return P2B; } /** - *@short compute constant values that need to be computed only once per instance of the application - */ + * @short compute constant values that need to be computed only once per instance of the application + */ void computeConstantValues(); - /** @short update all values for the date given as an argument. - *@param jd the Julian date for which to compute values - */ + /** + * @short update all values for the date given as an argument. + * @param jd the Julian date for which to compute values + */ void updateValues(long double jd); /** - *@return the JD for which these values hold (i.e. the last updated JD) - */ + * @return the JD for which these values hold (i.e. the last updated JD) + */ inline long double getJD() const { return days; } inline double vEarth(int i) const { return vearth[i]; } private: CachingDms Obliquity, L0, P; dms K, L, LM, M, M0, O, D, MM, F; dms XP, YP, ZP, XB, YB, ZB; double CX, SX, CY, SY, CZ, SZ; double CXB, SXB, CYB, SYB, CZB, SZB; Eigen::Matrix3d P1, P2, P1B, P2B; double deltaObliquity, deltaEcLong; double e, T; long double days; // JD for which the last update was called double jm; static const int arguments[NUTTERMS][5]; static const int amp[NUTTERMS][4]; double vearth[3]; }; diff --git a/kstars/kstarssplash.cpp b/kstars/kstarssplash.cpp index ec857d059..c08adacfb 100644 --- a/kstars/kstarssplash.cpp +++ b/kstars/kstarssplash.cpp @@ -1,39 +1,35 @@ /*************************************************************************** kstarssplash.cpp - description ------------------- begin : Thu Jul 26 2001 copyright : (C) 2001 by Heiko Evermann email : heiko@evermann.de ***************************************************************************/ /*************************************************************************** * * * 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 "kstarssplash.h" #include #include #include "kspaths.h" KStarsSplash::KStarsSplash(const QString &customMessage) : QSplashScreen(QPixmap()) { /*Background for kstars.png is called "Venus and The Night Sky Over Mammoth"(https://www.flickr.com/photos/newdimensionfilms/7108632527) *It was provided by John Lemieux (https://www.flickr.com/photos/newdimensionfilms/) and is licensed under CC BY 2.0 (http://creativecommons.org/licenses/by/2.0/)*/ setPixmap(KSPaths::locate(QStandardPaths::GenericDataLocation, "kstars.png")); setMessage(customMessage.isEmpty() ? i18n("Welcome to KStars. Please stand by while loading...") : customMessage); } -KStarsSplash::~KStarsSplash() -{ -} - void KStarsSplash::setMessage(const QString &message) { showMessage(message, Qt::AlignBottom | Qt::AlignHCenter, Qt::lightGray); } diff --git a/kstars/kstarssplash.h b/kstars/kstarssplash.h index e5414f1ee..8d87e309f 100644 --- a/kstars/kstarssplash.h +++ b/kstars/kstarssplash.h @@ -1,49 +1,50 @@ /*************************************************************************** KStarsSplash.h - description ------------------- begin : Thu Jul 26 2001 copyright : (C) 2001 by Heiko Evermann email : heiko@evermann.de ***************************************************************************/ /*************************************************************************** * * * 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 KSTARSSPLASH_H__ -#define KSTARSSPLASH_H__ +#pragma once #include -/** @class KStarsSplash +/** + * @class KStarsSplash * The KStars Splash Screen. The splash screen shows the KStars logo and * progress messages while data files are parsed and objects are initialized. + * * @short the KStars Splash Screen. * @author Heiko Evermann * @version 1.0 */ class KStarsSplash : public QSplashScreen { Q_OBJECT public: - /** Constructor. Create widgets. Load KStars logo. Start load timer. - * A non-empty customMessage will replace "Welcome to KStars [...]". - */ + /** + * Constructor. Create widgets. Load KStars logo. Start load timer. + * A non-empty customMessage will replace "Welcome to KStars [...]". + */ explicit KStarsSplash(const QString &customMessage = ""); - /** Destructor */ - ~KStarsSplash() override; + virtual ~KStarsSplash() override = default; public slots: - /** Display the text argument in the Splash Screen's status label. - * This is connected to KStarsData::progressText(QString)*/ + /** + * Display the text argument in the Splash Screen's status label. + * This is connected to KStarsData::progressText(QString) + */ void setMessage(const QString &s); }; - -#endif diff --git a/kstars/options/opsadvanced.cpp b/kstars/options/opsadvanced.cpp index d03863b4d..30a94a824 100644 --- a/kstars/options/opsadvanced.cpp +++ b/kstars/options/opsadvanced.cpp @@ -1,106 +1,102 @@ /*************************************************************************** opsadvanced.cpp - K Desktop Planetarium ------------------- begin : Sun 14 Mar 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "opsadvanced.h" #include "kspaths.h" #include "kstars.h" #include "ksutils.h" #include "Options.h" #include "widgets/timespinbox.h" #include #include #include #include #include OpsAdvanced::OpsAdvanced() : QFrame(KStars::Instance()) { setupUi(this); //Initialize the timestep value SlewTimeScale->tsbox()->changeScale(Options::slewTimeScale()); connect(SlewTimeScale, SIGNAL(scaleChanged(float)), this, SLOT(slotChangeTimeScale(float))); connect(kcfg_HideOnSlew, SIGNAL(clicked()), this, SLOT(slotToggleHideOptions())); connect(kcfg_VerboseLogging, SIGNAL(toggled(bool)), this, SLOT(slotToggleVerbosityOptions())); connect(kcfg_LogToFile, SIGNAL(toggled(bool)), this, SLOT(slotToggleOutputOptions())); connect(showLogsB, SIGNAL(clicked()), this, SLOT(slotShowLogFiles())); connect(kcfg_ObsListDemoteHole, &QCheckBox::toggled, [this](bool state) { kcfg_ObsListHoleSize->setEnabled(state); }); connect(zoomScrollSlider, &QSlider::valueChanged, [&](int value) { kcfg_ZoomScrollFactor->setValue(value / 100.0); }); //Get a pointer to the KConfigDialog KConfigDialog *m_ConfigDialog = KConfigDialog::exists("settings"); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); } -OpsAdvanced::~OpsAdvanced() -{ -} - void OpsAdvanced::slotChangeTimeScale(float newScale) { Options::setSlewTimeScale(newScale); } void OpsAdvanced::slotToggleHideOptions() { textLabelHideTimeStep->setEnabled(kcfg_HideOnSlew->isChecked()); SlewTimeScale->setEnabled(kcfg_HideOnSlew->isChecked()); HideBox->setEnabled(kcfg_HideOnSlew->isChecked()); } void OpsAdvanced::slotToggleVerbosityOptions() { if (kcfg_DisableLogging->isChecked()) KSUtils::Logging::Disable(); } void OpsAdvanced::slotToggleOutputOptions() { if (kcfg_LogToDefault->isChecked()) { if (kcfg_DisableLogging->isChecked() == false) KSUtils::Logging::UseDefault(); } else KSUtils::Logging::UseFile(); } void OpsAdvanced::slotShowLogFiles() { QUrl path = QUrl::fromLocalFile(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "logs"); QDesktopServices::openUrl(path); } void OpsAdvanced::slotApply() { KSUtils::Logging::SyncFilterRules(); } diff --git a/kstars/options/opsadvanced.h b/kstars/options/opsadvanced.h index 259e7e611..acf0c784e 100644 --- a/kstars/options/opsadvanced.h +++ b/kstars/options/opsadvanced.h @@ -1,51 +1,51 @@ /*************************************************************************** opsadvanced.h - K Desktop Planetarium ------------------- begin : Sun 14 Mar 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_opsadvanced.h" /** * @class OpsAdvanced * The Advanced Tab of the Options window. In this Tab the user can configure * advanced behaviors of the program, including: * @li Whether some objects are hidden when the map is moving (and which objects) * @li Whether positions are corrected for atmospheric refraction * @li Whether a slewing animation is used to move the Focus position * @li Whether centered objects are automatically labeled * @li whether a "transient" label is attached when the mouse "hovers" at an object. * @li whether to enable verbose debug output to a file which could be useful in troubleshooting any issues in KStars. * * @author Jason Harris, Jasem Mutlaq * @version 1.1 */ class OpsAdvanced : public QFrame, public Ui::OpsAdvanced { Q_OBJECT public: OpsAdvanced(); - ~OpsAdvanced() override; + virtual ~OpsAdvanced() override = default; private slots: void slotChangeTimeScale(float newScale); void slotToggleHideOptions(); void slotToggleVerbosityOptions(); void slotToggleOutputOptions(); void slotShowLogFiles(); void slotApply(); }; diff --git a/kstars/options/opscatalog.cpp b/kstars/options/opscatalog.cpp index 0888b77e4..2f79120ab 100644 --- a/kstars/options/opscatalog.cpp +++ b/kstars/options/opscatalog.cpp @@ -1,394 +1,389 @@ /*************************************************************************** opscatalog.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 29 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "opscatalog.h" -#include -#include -#include -#include - -#include -#include - -#include "Options.h" #include "kstars.h" #include "kstarsdata.h" +#include "Options.h" #include "skymap.h" #include "dialogs/addcatdialog.h" -#include "widgets/magnitudespinbox.h" #include "skycomponents/catalogcomponent.h" #include "skycomponents/skymapcomposite.h" +#include "widgets/magnitudespinbox.h" + +#include +#include + +#include +#include +#include +#include OpsCatalog::OpsCatalog() : QFrame(KStars::Instance()) { setupUi(this); //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("settings"); //Populate CatalogList populateInbuiltCatalogs(); m_ShowMessier = Options::showMessier(); m_ShowMessImages = Options::showMessierImages(); m_ShowNGC = Options::showNGC(); m_ShowIC = Options::showIC(); // kcfg_MagLimitDrawStar->setValue( Options::magLimitDrawStar() ); kcfg_StarDensity->setValue(Options::starDensity()); // kcfg_MagLimitDrawStarZoomOut->setValue( Options::magLimitDrawStarZoomOut() ); // m_MagLimitDrawStar = kcfg_MagLimitDrawStar->value(); m_StarDensity = kcfg_StarDensity->value(); // m_MagLimitDrawStarZoomOut = kcfg_MagLimitDrawStarZoomOut->value(); // kcfg_MagLimitDrawStar->setMinimum( Options::magLimitDrawStarZoomOut() ); // kcfg_MagLimitDrawStarZoomOut->setMaximum( 12.0 ); kcfg_MagLimitDrawDeepSky->setMaximum(16.0); kcfg_MagLimitDrawDeepSkyZoomOut->setMaximum(16.0); //disable star-related widgets if not showing stars if (!kcfg_ShowStars->isChecked()) slotStarWidgets(false); //Add custom catalogs, if necessary /* * 1) Get the list from DB and add it as unchecked * 2) If the showCatalogNames list has any of the items, check it */ m_CustomCatalogFile = KStars::Instance()->data()->catalogdb()->Catalogs(); m_CheckedCatalogNames = Options::showCatalogNames(); populateCustomCatalogs(); connect(CatalogList, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(updateCustomCatalogs())); connect(CatalogList, SIGNAL(itemSelectionChanged()), this, SLOT(selectCatalog())); connect(AddCatalog, SIGNAL(clicked()), this, SLOT(slotAddCatalog())); connect(LoadCatalog, SIGNAL(clicked()), this, SLOT(slotLoadCatalog())); connect(RemoveCatalog, SIGNAL(clicked()), this, SLOT(slotRemoveCatalog())); /* connect( kcfg_MagLimitDrawStar, SIGNAL( valueChanged(double) ), SLOT( slotSetDrawStarMagnitude(double) ) ); connect( kcfg_MagLimitDrawStarZoomOut, SIGNAL( valueChanged(double) ), SLOT( slotSetDrawStarZoomOutMagnitude(double) ) ); */ connect(kcfg_ShowStars, SIGNAL(toggled(bool)), SLOT(slotStarWidgets(bool))); connect(kcfg_ShowDeepSky, SIGNAL(toggled(bool)), SLOT(slotDeepSkyWidgets(bool))); connect(kcfg_ShowDeepSkyNames, SIGNAL(toggled(bool)), kcfg_DeepSkyLongLabels, SLOT(setEnabled(bool))); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), SLOT(slotCancel())); // Keep track of changes connect(CatalogList, &QListWidget::itemChanged, this, [&]() { isDirty = true; }); connect(catalogButtonGroup, static_cast(&QButtonGroup::buttonPressed), this, [&]() { isDirty = true; }); isDirty = false; } -//empty destructor -OpsCatalog::~OpsCatalog() -{ -} - void OpsCatalog::updateCustomCatalogs() { m_ShowMessier = showMessier->checkState(); m_ShowMessImages = showMessImages->checkState(); m_ShowNGC = showNGC->checkState(); m_ShowIC = showIC->checkState(); int limit = m_CustomCatalogFile->size(); for (int i = 0; i < limit; ++i) { QString name = m_CustomCatalogFile->at(i); QList l = CatalogList->findItems(name, Qt::MatchExactly); /* * Options::CatalogNames contains the list of those custom catalog * names which are to be checked. * For every checked item, we check if the option CatalogNames has * the name. If not, we add it. * For every unchecked item, we check if the option CatalogNames does * not contain the name. If it does, we remove it. */ if (l.count() == 0) continue; // skip the name if no match found if (l[0]->checkState() == Qt::Checked) { if (!m_CheckedCatalogNames.contains(name)) { m_CheckedCatalogNames.append(name); //isDirty = true; } } else if (l[0]->checkState() == Qt::Unchecked) { if (m_CheckedCatalogNames.contains(name)) { m_CheckedCatalogNames.removeAll(name); //isDirty = true; } } } m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false); } void OpsCatalog::selectCatalog() { //If selected item is a custom catalog, enable the remove button (otherwise, disable it) RemoveCatalog->setEnabled(false); if (!CatalogList->currentItem()) return; //isDirty = true; foreach (SkyComponent *sc, KStars::Instance()->data()->skyComposite()->customCatalogs()) { CatalogComponent *cc = (CatalogComponent *)sc; if (CatalogList->currentItem()->text() == cc->name()) { RemoveCatalog->setEnabled(true); break; } } } void OpsCatalog::slotAddCatalog() { QPointer ac = new AddCatDialog(KStars::Instance()); if (ac->exec() == QDialog::Accepted) { KStars::Instance()->data()->catalogdb()->AddCatalogContents(ac->filename()); refreshCatalogList(); isDirty = true; } delete ac; } void OpsCatalog::slotLoadCatalog() { //Get the filename from the user QString filename = QFileDialog::getOpenFileName(KStars::Instance(), QString(), QDir::homePath(), "*"); if (!filename.isEmpty()) { KStars::Instance()->data()->catalogdb()->AddCatalogContents(filename); isDirty = true; refreshCatalogList(); } m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false); } void OpsCatalog::refreshCatalogList() { KStars::Instance()->data()->catalogdb()->Catalogs(); populateCustomCatalogs(); } void OpsCatalog::slotRemoveCatalog() { if (KMessageBox::warningYesNo( nullptr, i18n("The selected database will be removed. This action cannot be reversed! Delete Catalog?"), i18n("Delete Catalog?")) == KMessageBox::No) { KMessageBox::information(nullptr, "Catalog deletion cancelled."); return; } isDirty = true; //Ask DB to remove catalog KStars::Instance()->data()->catalogdb()->RemoveCatalog(CatalogList->currentItem()->text()); // Remove from Options if it exists in it (i.e. was marked as visible) // This does not remove it from the database or from the widget in Options QList checkedlist = Options::showCatalogNames(); if (checkedlist.contains(CatalogList->currentItem()->text())) { checkedlist.removeAll(CatalogList->currentItem()->text()); Options::setShowCatalogNames(checkedlist); } //Remove entry in the QListView QListWidgetItem *todelete = CatalogList->takeItem(CatalogList->row(CatalogList->currentItem())); delete todelete; refreshCatalogList(); m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false); KStars::Instance()->data()->skyComposite()->reloadDeepSky(); } /* void OpsCatalog::slotSetDrawStarMagnitude(double newValue) { m_MagLimitDrawStar = newValue; kcfg_MagLimitDrawStarZoomOut->setMaximum( newValue ); m_ConfigDialog->enableButtonApply( true ); } void OpsCatalog::slotSetDrawStarZoomOutMagnitude(double newValue) { m_MagLimitDrawStarZoomOut = newValue; kcfg_MagLimitDrawStar->setMinimum( newValue ); m_ConfigDialog->enableButtonApply( true ); } */ void OpsCatalog::slotApply() { if (isDirty == false) return; isDirty = false; refreshCatalogList(); Options::setStarDensity(kcfg_StarDensity->value()); // Options::setMagLimitDrawStarZoomOut( kcfg_MagLimitDrawStarZoomOut->value() ); //FIXME: need to add the ShowDeepSky meta-option to the config dialog! //For now, I'll set showDeepSky to true if any catalog options changed if (m_ShowMessier != Options::showMessier() || m_ShowMessImages != Options::showMessierImages() || m_ShowNGC != Options::showNGC() || m_ShowIC != Options::showIC()) { Options::setShowDeepSky(true); } updateCustomCatalogs(); KStars::Instance()->data()->skyComposite()->reloadDeepSky(); // update time for all objects because they might be not initialized // it's needed when using horizontal coordinates KStars::Instance()->data()->setFullTimeUpdate(); KStars::Instance()->updateTime(); KStars::Instance()->map()->forceUpdate(); Options::setShowCatalogNames(m_CheckedCatalogNames); Options::setShowMessier(m_ShowMessier); Options::setShowMessierImages(m_ShowMessImages); Options::setShowNGC(m_ShowNGC); Options::setShowIC(m_ShowIC); } void OpsCatalog::slotCancel() { //Revert all local option placeholders to the values in the global config object // m_MagLimitDrawStar = Options::magLimitDrawStar(); m_StarDensity = Options::starDensity(); // m_MagLimitDrawStarZoomOut = Options::magLimitDrawStarZoomOut(); m_ShowMessier = Options::showMessier(); m_ShowMessImages = Options::showMessierImages(); m_ShowNGC = Options::showNGC(); m_ShowIC = Options::showIC(); m_ShowCustomCatalog = Options::showCatalog(); } void OpsCatalog::slotStarWidgets(bool on) { // LabelMagStars->setEnabled(on); LabelStarDensity->setEnabled(on); // LabelMagStarsZoomOut->setEnabled(on); LabelDensity->setEnabled(on); // LabelMag1->setEnabled(on); // LabelMag2->setEnabled(on); // kcfg_MagLimitDrawStar->setEnabled(on); kcfg_StarDensity->setEnabled(on); LabelStarDensity->setEnabled(on); // kcfg_MagLimitDrawStarZoomOut->setEnabled(on); kcfg_StarLabelDensity->setEnabled(on); kcfg_ShowStarNames->setEnabled(on); kcfg_ShowStarMagnitudes->setEnabled(on); } void OpsCatalog::slotDeepSkyWidgets(bool on) { CatalogList->setEnabled(on); AddCatalog->setEnabled(on); LoadCatalog->setEnabled(on); LabelMagDeepSky->setEnabled(on); LabelMagDeepSkyZoomOut->setEnabled(on); kcfg_MagLimitDrawDeepSky->setEnabled(on); kcfg_MagLimitDrawDeepSkyZoomOut->setEnabled(on); kcfg_ShowDeepSkyNames->setEnabled(on); kcfg_ShowDeepSkyMagnitudes->setEnabled(on); kcfg_DeepSkyLabelDensity->setEnabled(on); kcfg_DeepSkyLongLabels->setEnabled(on); LabelMag3->setEnabled(on); LabelMag4->setEnabled(on); if (on) { //Enable RemoveCatalog if the selected catalog is custom selectCatalog(); } else { RemoveCatalog->setEnabled(on); } } void OpsCatalog::populateInbuiltCatalogs() { showIC = new QListWidgetItem(i18n("Index Catalog (IC)"), CatalogList); showIC->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); showIC->setCheckState(Options::showIC() ? Qt::Checked : Qt::Unchecked); showNGC = new QListWidgetItem(i18n("New General Catalog (NGC)"), CatalogList); showNGC->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); showNGC->setCheckState(Options::showNGC() ? Qt::Checked : Qt::Unchecked); showMessImages = new QListWidgetItem(i18n("Messier Catalog (images)"), CatalogList); showMessImages->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); showMessImages->setCheckState(Options::showMessierImages() ? Qt::Checked : Qt::Unchecked); showMessier = new QListWidgetItem(i18n("Messier Catalog (symbols)"), CatalogList); showMessier->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); showMessier->setCheckState(Options::showMessier() ? Qt::Checked : Qt::Unchecked); } void OpsCatalog::populateCustomCatalogs() { QStringList toggleNames = Options::showCatalogNames(); QStringList customList = *m_CustomCatalogFile; // Create a copy QStringListIterator catalogIter(customList); while (catalogIter.hasNext()) { QString catalogname = catalogIter.next(); //Skip already existing items if (CatalogList->findItems(catalogname, Qt::MatchExactly).length() > 0) continue; //Allocate new catalog list item QListWidgetItem *newItem = new QListWidgetItem(catalogname, CatalogList); newItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); if (toggleNames.contains(catalogname)) { newItem->setCheckState(Qt::Checked); } else { newItem->setCheckState(Qt::Unchecked); } } } diff --git a/kstars/options/opscatalog.h b/kstars/options/opscatalog.h index bf56e0a2f..8de92a053 100644 --- a/kstars/options/opscatalog.h +++ b/kstars/options/opscatalog.h @@ -1,80 +1,83 @@ /*************************************************************************** opscatalog.h - K Desktop Planetarium ------------------- begin : Sun Feb 29 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 OPSCATALOG_H_ -#define OPSCATALOG_H_ +#pragma once #include "ui_opscatalog.h" class KStars; class QListWidgetItem; class KConfigDialog; /** - *@class OpsCatalog - *The Catalog page for the Options window. This page allows the user - *to modify display of the major object catalogs in KStars: - *@li Hipparcos/Tycho Star Catalog - *@li Messier Catalog - *@li NGC/IC Catalogs - *@li Any Custom catalogs added by the user. + * @class OpsCatalog + * The Catalog page for the Options window. This page allows the user + * to modify display of the major object catalogs in KStars: + * @li Hipparcos/Tycho Star Catalog + * @li Messier Catalog + * @li NGC/IC Catalogs + * @li Any Custom catalogs added by the user. * - *@short Catalog page of the Options window. - *@author Jason Harris - *@version 1.0 + * @short Catalog page of the Options window. + * @author Jason Harris + * @version 1.0 */ class OpsCatalog : public QFrame, public Ui::OpsCatalog { Q_OBJECT public: explicit OpsCatalog(); - ~OpsCatalog() override; + virtual ~OpsCatalog() override = default; private slots: void updateCustomCatalogs(); void selectCatalog(); void slotAddCatalog(); void slotLoadCatalog(); void slotRemoveCatalog(); /* void slotSetDrawStarMagnitude(double newValue); void slotSetDrawStarZoomOutMagnitude(double newValue); */ void slotStarWidgets(bool on); void slotDeepSkyWidgets(bool on); void slotApply(); void slotCancel(); private: void insertCatalog(const QString &filename); void refreshCatalogList(); void populateInbuiltCatalogs(); void populateCustomCatalogs(); QString getCatalogName(const QString &filename); - QListWidgetItem *showMessier, *showMessImages, *showNGC, *showIC; + QListWidgetItem *showMessier { nullptr }; + QListWidgetItem *showMessImages { nullptr }; + QListWidgetItem *showNGC { nullptr }; + QListWidgetItem *showIC { nullptr }; - KConfigDialog *m_ConfigDialog; - QStringList *m_CustomCatalogFile; + KConfigDialog *m_ConfigDialog { nullptr }; + QStringList *m_CustomCatalogFile { nullptr }; QStringList m_CheckedCatalogNames; QList m_ShowCustomCatalog; - float m_StarDensity; - bool m_ShowMessier, m_ShowMessImages, m_ShowNGC, m_ShowIC; - bool isDirty = false; + float m_StarDensity { 0 }; + bool m_ShowMessier { false }; + bool m_ShowMessImages { false }; + bool m_ShowNGC { false }; + bool m_ShowIC { false }; + bool isDirty { false }; }; - -#endif //OPSCATALOG_H_ diff --git a/kstars/options/opscolors.cpp b/kstars/options/opscolors.cpp index 668773b34..889a0aa86 100644 --- a/kstars/options/opscolors.cpp +++ b/kstars/options/opscolors.cpp @@ -1,338 +1,330 @@ /*************************************************************************** opscolors.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 29 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "opscolors.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "thememanager.h" - -#include -#include -#include - +#include "colorscheme.h" +#include "config-kstars.h" #include "kspaths.h" #include "kstars.h" #include "kstarsdata.h" #include "skymap.h" -#include "colorscheme.h" - +#include "thememanager.h" #ifdef HAVE_CFITSIO #include "fitsviewer/fitsviewer.h" #include "fitsviewer/fitsview.h" #endif - #include "skyobjects/starobject.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + static int ItemColorData = Qt::UserRole + 1; OpsColors::OpsColors() : QFrame(KStars::Instance()) { setupUi(this); //Populate list of Application Themes KSTheme::Manager::instance()->populateThemeQListWidget(themesWidget); connect(themesWidget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotChangeTheme(QListWidgetItem*))); //Populate list of adjustable colors for (unsigned int i = 0; i < KStarsData::Instance()->colorScheme()->numberOfColors(); ++i) { QPixmap col(30, 20); QColor itemColor(KStarsData::Instance()->colorScheme()->colorAt(i)); col.fill(itemColor); QListWidgetItem *item = new QListWidgetItem(KStarsData::Instance()->colorScheme()->nameAt(i), ColorPalette); item->setData(Qt::DecorationRole, col); item->setData(ItemColorData, itemColor); } PresetBox->addItem(i18nc("use default color scheme", "Default Colors")); PresetBox->addItem(i18nc("use 'star chart' color scheme", "Star Chart")); PresetBox->addItem(i18nc("use 'night vision' color scheme", "Night Vision")); PresetBox->addItem(i18nc("use 'moonless night' color scheme", "Moonless Night")); PresetFileList.append("classic.colors"); PresetFileList.append("chart.colors"); PresetFileList.append("night.colors"); PresetFileList.append("moonless-night.colors"); QFile file; QString line, schemeName, filename; file.setFileName(KSPaths::locate(QStandardPaths::GenericDataLocation, "colors.dat")); if (file.exists() && file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); while (!stream.atEnd()) { line = stream.readLine(); schemeName = line.left(line.indexOf(':')); filename = line.mid(line.indexOf(':') + 1, line.length()); PresetBox->addItem(schemeName); PresetFileList.append(filename); } file.close(); } kcfg_StarColorIntensity->setValue(KStarsData::Instance()->colorScheme()->starColorIntensity()); kcfg_StarColorMode->addItem(i18nc("use realistic star colors", "Real Colors")); kcfg_StarColorMode->addItem(i18nc("show stars as red circles", "Solid Red")); kcfg_StarColorMode->addItem(i18nc("show stars as black circles", "Solid Black")); kcfg_StarColorMode->addItem(i18nc("show stars as white circles", "Solid White")); kcfg_StarColorMode->addItem(i18nc("show stars as colored circles", "Solid Colors")); kcfg_StarColorMode->setCurrentIndex(KStarsData::Instance()->colorScheme()->starColorMode()); if (KStarsData::Instance()->colorScheme()->starColorMode() != 0) //mode is not "Real Colors" kcfg_StarColorIntensity->setEnabled(false); else kcfg_StarColorIntensity->setEnabled(true); connect(ColorPalette, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(newColor(QListWidgetItem*))); connect(kcfg_StarColorIntensity, SIGNAL(valueChanged(int)), this, SLOT(slotStarColorIntensity(int))); connect(kcfg_StarColorMode, SIGNAL(activated(int)), this, SLOT(slotStarColorMode(int))); connect(PresetBox, SIGNAL(currentRowChanged(int)), this, SLOT(slotPreset(int))); connect(AddPreset, SIGNAL(clicked()), this, SLOT(slotAddPreset())); connect(RemovePreset, SIGNAL(clicked()), this, SLOT(slotRemovePreset())); connect(SavePreset, SIGNAL(clicked()), this, SLOT(slotSavePreset())); RemovePreset->setEnabled(false); SavePreset->setEnabled(false); } -//empty destructor -OpsColors::~OpsColors() -{ -} - void OpsColors::slotChangeTheme(QListWidgetItem *item) { KSTheme::Manager::instance()->setCurrentTheme(item->text()); } void OpsColors::newColor(QListWidgetItem *item) { if (!item) return; QPixmap pixmap(30, 20); QColor NewColor; int index = ColorPalette->row(item); if (index < 0 || index >= ColorPalette->count()) return; QColor col = item->data(ItemColorData).value(); NewColor = QColorDialog::getColor(col); //NewColor will only be valid if the above if statement was found to be true during one of the for loop iterations if (NewColor.isValid()) { pixmap.fill(NewColor); item->setData(Qt::DecorationRole, pixmap); item->setData(ItemColorData, NewColor); KStarsData::Instance()->colorScheme()->setColor(KStarsData::Instance()->colorScheme()->keyAt(index), NewColor.name()); } if (PresetBox->currentRow() > 3) SavePreset->setEnabled(true); KStars::Instance()->map()->forceUpdate(); #ifdef HAVE_CFITSIO QList viewers = KStars::Instance()->findChildren(); foreach (FITSViewer *viewer, viewers) viewer->getCurrentView()->updateFrame(); #endif } void OpsColors::slotPreset(int index) { QString sPreset = PresetFileList.at(index); setColors(sPreset); } bool OpsColors::setColors(const QString &filename) { QPixmap temp(30, 20); //check if colorscheme is removable... QFile test; //try filename in local user KDE directory tree. test.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + filename); if (test.exists()) { RemovePreset->setEnabled(true); SavePreset->setEnabled(true); } else { RemovePreset->setEnabled(false); SavePreset->setEnabled(false); } test.close(); QString actionName = QString("cs_" + filename.left(filename.indexOf(".colors"))).toUtf8(); QAction *a = KStars::Instance()->actionCollection()->action(actionName); if (a) a->setChecked(true); qApp->processEvents(); kcfg_StarColorMode->setCurrentIndex(KStarsData::Instance()->colorScheme()->starColorMode()); kcfg_StarColorIntensity->setValue(KStarsData::Instance()->colorScheme()->starColorIntensity()); for (unsigned int i = 0; i < KStarsData::Instance()->colorScheme()->numberOfColors(); ++i) { QColor itemColor(KStarsData::Instance()->colorScheme()->colorAt(i)); temp.fill(itemColor); ColorPalette->item(i)->setData(Qt::DecorationRole, temp); ColorPalette->item(i)->setData(ItemColorData, itemColor); } KStars::Instance()->map()->forceUpdate(); #ifdef HAVE_CFITSIO QList viewers = KStars::Instance()->findChildren(); foreach (FITSViewer *viewer, viewers) viewer->getCurrentView()->updateFrame(); #endif return true; } void OpsColors::slotAddPreset() { bool okPressed = false; QString schemename = QInputDialog::getText(nullptr, i18n("New Color Scheme"), i18n("Enter a name for the new color scheme:"), QLineEdit::Normal, QString(), &okPressed, nullptr); if (okPressed && !schemename.isEmpty()) { if (KStarsData::Instance()->colorScheme()->save(schemename)) { QListWidgetItem *item = new QListWidgetItem(schemename, PresetBox); QString fname = KStarsData::Instance()->colorScheme()->fileName(); PresetFileList.append(fname); QString actionName = QString("cs_" + fname.left(fname.indexOf(".colors"))).toUtf8(); KStars::Instance()->addColorMenuItem(schemename, actionName); QAction *a = KStars::Instance()->actionCollection()->action(actionName); if (a) a->setChecked(true); PresetBox->setCurrentItem(item); } } } void OpsColors::slotSavePreset() { QString schemename = PresetBox->currentItem()->text(); KStarsData::Instance()->colorScheme()->save(schemename); SavePreset->setEnabled(false); } void OpsColors::slotRemovePreset() { QListWidgetItem *current = PresetBox->currentItem(); if (!current) return; QString name = current->text(); QString filename = PresetFileList[PresetBox->currentRow()]; QFile cdatFile; cdatFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "colors.dat"); //determine filename in local user KDE directory tree. //Remove action from color-schemes menu KStars::Instance()->removeColorMenuItem(QString("cs_" + filename.left(filename.indexOf(".colors"))).toUtf8()); if (!cdatFile.exists() || !cdatFile.open(QIODevice::ReadWrite)) { QString message = i18n("Local color scheme index file could not be opened.\nScheme cannot be removed."); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); } else { //Remove entry from the ListBox and from the QStringList holding filenames. //There seems to be no way to set no item selected, so select // the first item. PresetBox->setCurrentRow(0); PresetFileList.removeOne(filename); delete current; RemovePreset->setEnabled(false); //Read the contents of colors.dat into a QStringList, except for the entry to be removed. QTextStream stream(&cdatFile); QStringList slist; bool removed = false; while (!stream.atEnd()) { QString line = stream.readLine(); if (line.left(line.indexOf(':')) != name) slist.append(line); else removed = true; } if (removed) //Entry was removed; delete the corresponding .colors file. { QFile colorFile; colorFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + filename); //determine filename in local user KDE directory tree. if (!colorFile.remove()) { QString message = i18n("Could not delete the file: %1", colorFile.fileName()); KMessageBox::sorry(nullptr, message, i18n("Error Deleting File")); } //remove the old colors.dat file, and rebuild it with the modified string list. cdatFile.remove(); cdatFile.open(QIODevice::ReadWrite); QTextStream stream2(&cdatFile); for (int i = 0; i < slist.count(); ++i) stream << slist[i] << endl; } else { QString message = i18n("Could not find an entry named %1 in colors.dat.", name); KMessageBox::sorry(nullptr, message, i18n("Scheme Not Found")); } cdatFile.close(); } } void OpsColors::slotStarColorMode(int i) { KStarsData::Instance()->colorScheme()->setStarColorMode(i); if (KStarsData::Instance()->colorScheme()->starColorMode() != 0) //mode is not "Real Colors" kcfg_StarColorIntensity->setEnabled(false); else kcfg_StarColorIntensity->setEnabled(true); } void OpsColors::slotStarColorIntensity(int i) { KStarsData::Instance()->colorScheme()->setStarColorIntensity(i); } diff --git a/kstars/options/opscolors.h b/kstars/options/opscolors.h index ec26309e8..0901cfe88 100644 --- a/kstars/options/opscolors.h +++ b/kstars/options/opscolors.h @@ -1,64 +1,63 @@ /*************************************************************************** opscolors.h - K Desktop Planetarium ------------------- begin : Sun Feb 29 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 OPSCOLORS_H_ -#define OPSCOLORS_H_ - -#include +#pragma once #include "ui_opscolors.h" -/** @class OpsColors - *The Colors page allows the user to adjust all of the colors used to - *display the night sky. The colors are presented as a list of - *colored rectangles and a description of its assignment in the map. - *Clicking on any color opens a KColorDialog for selecting a new color. - * - *The user can also load preset color schemes, or create new schemes - *from the current set of colors. - * - *@short The Colors page of the Options window. - *@author Jason Harris - *@author Jasem Mutlaq - *@version 1.1 - */ +#include class KStars; +/** + * @class OpsColors + * + * The Colors page allows the user to adjust all of the colors used to + * display the night sky. The colors are presented as a list of + * colored rectangles and a description of its assignment in the map. + * Clicking on any color opens a KColorDialog for selecting a new color. + * + * The user can also load preset color schemes, or create new schemes + * from the current set of colors. + * + * @short The Colors page of the Options window. + * @author Jason Harris + * @author Jasem Mutlaq + * @version 1.1 + */ class OpsColors : public QFrame, public Ui::OpsColors { Q_OBJECT public: explicit OpsColors(); - ~OpsColors() override; + virtual ~OpsColors() override = default; private slots: void newColor(QListWidgetItem *item); void slotPreset(int i); void slotAddPreset(); void slotSavePreset(); void slotRemovePreset(); void slotStarColorMode(int); void slotStarColorIntensity(int); void slotChangeTheme(QListWidgetItem *item); private: bool setColors(const QString &filename); + QStringList PresetFileList; }; - -#endif //OPSCOLORS_H_ diff --git a/kstars/options/opsguides.cpp b/kstars/options/opsguides.cpp index e038adc3e..eea090c87 100644 --- a/kstars/options/opsguides.cpp +++ b/kstars/options/opsguides.cpp @@ -1,126 +1,124 @@ /*************************************************************************** opsguides.cpp - K Desktop Planetarium ------------------- begin : Sun 6 Feb 2005 copyright : (C) 2005 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 -#include - #include "opsguides.h" + #include "ksfilereader.h" #include "kstars.h" #include "kstarsdata.h" +#include "Options.h" #include "skymap.h" #include "skycomponents/skymapcomposite.h" -#include "Options.h" + +#include + +#include OpsGuides::OpsGuides() : QFrame(KStars::Instance()) { setupUi(this); foreach (const QString &item, KStarsData::Instance()->skyComposite()->getCultureNames()) kcfg_SkyCulture->addItem(i18nc("Sky Culture", item.toUtf8().constData())); m_ConfigDialog = KConfigDialog::exists("settings"); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); // When setting up the widget, update the enabled status of the // checkboxes depending on the options. slotToggleOpaqueGround(Options::showGround()); slotToggleConstellOptions(Options::showCNames()); slotToggleConstellationArt(Options::showConstellationArt()); slotToggleMilkyWayOptions(Options::showMilkyWay()); slotToggleAutoSelectGrid(Options::autoSelectGrid()); connect(kcfg_ShowCNames, SIGNAL(toggled(bool)), this, SLOT(slotToggleConstellOptions(bool))); connect(kcfg_ShowConstellationArt, SIGNAL(toggled(bool)), this, SLOT(slotToggleConstellationArt(bool))); connect(kcfg_ShowMilkyWay, SIGNAL(toggled(bool)), this, SLOT(slotToggleMilkyWayOptions(bool))); connect(kcfg_ShowGround, SIGNAL(toggled(bool)), this, SLOT(slotToggleOpaqueGround(bool))); connect(kcfg_AutoSelectGrid, SIGNAL(toggled(bool)), this, SLOT(slotToggleAutoSelectGrid(bool))); // Track changes to apply settings connect(constellationButtonGroup, static_cast(&QButtonGroup::buttonPressed), this, [&]() { isDirty = true; }); connect(nameButtonGroup, static_cast(&QButtonGroup::buttonPressed), this, [&]() { isDirty = true; }); connect(kcfg_SkyCulture, static_cast(&QComboBox::activated), this, [&]() { isDirty = true; }); isDirty = false; } -OpsGuides::~OpsGuides() -{ -} - void OpsGuides::slotToggleConstellOptions(bool state) { ConstellOptions->setEnabled(state); } void OpsGuides::slotToggleConstellationArt(bool state) { kcfg_ShowConstellationArt->setEnabled(state); } void OpsGuides::slotToggleMilkyWayOptions(bool state) { kcfg_FillMilkyWay->setEnabled(state); } void OpsGuides::slotToggleOpaqueGround(bool state) { kcfg_ShowHorizon->setEnabled(!state); } void OpsGuides::slotToggleAutoSelectGrid(bool state) { kcfg_ShowEquatorialGrid->setEnabled(!state); kcfg_ShowHorizontalGrid->setEnabled(!state); } void OpsGuides::slotApply() { if (isDirty == false) return; isDirty = false; KStarsData *data = KStarsData::Instance(); SkyMap *map = SkyMap::Instance(); // If the focus object was a constellation and the sky culture has changed, remove the focus object if (map->focusObject() && map->focusObject()->type() == SkyObject::CONSTELLATION) { if (data->skyComposite()->currentCulture() != data->skyComposite()->getCultureName(kcfg_SkyCulture->currentIndex()) || data->skyComposite()->isLocalCNames() != Options::useLocalConstellNames()) { map->setClickedObject(nullptr); map->setFocusObject(nullptr); } } data->skyComposite()->setCurrentCulture( KStarsData::Instance()->skyComposite()->getCultureName(kcfg_SkyCulture->currentIndex())); data->skyComposite()->reloadCLines(); data->skyComposite()->reloadCNames(); data->skyComposite()->reloadConstellationArt(); data->setFullTimeUpdate(); KStars::Instance()->updateTime(); map->forceUpdate(); } diff --git a/kstars/options/opsguides.h b/kstars/options/opsguides.h index ea698eecb..0e9f1d113 100644 --- a/kstars/options/opsguides.h +++ b/kstars/options/opsguides.h @@ -1,48 +1,48 @@ /*************************************************************************** opsguides.h - K Desktop Planetarium ------------------- begin : Sun 6 Feb 2005 copyright : (C) 2005 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_opsguides.h" class KConfigDialog; /** * @class OpsGuides * The guide page enables to user to select to turn on and off guide overlays such as * constellation lines, boundaries, flags..etc. */ class OpsGuides : public QFrame, public Ui::OpsGuides { Q_OBJECT public: explicit OpsGuides(); - ~OpsGuides() override; + virtual ~OpsGuides() override = default; private slots: void slotApply(); void slotToggleConstellOptions(bool state); void slotToggleConstellationArt(bool state); void slotToggleMilkyWayOptions(bool state); void slotToggleOpaqueGround(bool state); void slotToggleAutoSelectGrid(bool state); private: - KConfigDialog *m_ConfigDialog; - bool isDirty = false; + KConfigDialog *m_ConfigDialog { nullptr }; + bool isDirty { false }; }; diff --git a/kstars/options/opssatellites.cpp b/kstars/options/opssatellites.cpp index bf57d8f4c..bd58a2ad2 100644 --- a/kstars/options/opssatellites.cpp +++ b/kstars/options/opssatellites.cpp @@ -1,299 +1,295 @@ /*************************************************************************** opssatellites.cpp - K Desktop Planetarium ------------------- begin : Mon 21 Mar 2011 copyright : (C) 2011 by Jérôme SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 "opssatellites.h" -#include -#include - #include "kstars.h" #include "kstarsdata.h" #include "Options.h" #include "satellite.h" #include "skymapcomposite.h" #include "skycomponents/satellitescomponent.h" #include "skymap.h" +#include +#include + static const char *satgroup_strings_context = "Satellite group name"; SatelliteSortFilterProxyModel::SatelliteSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { } bool SatelliteSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); if (sourceModel()->hasChildren(index)) { for (int i = 0; i < sourceModel()->rowCount(index); ++i) if (filterAcceptsRow(i, index)) return true; return false; } return sourceModel()->data(index).toString().contains(filterRegExp()); } OpsSatellites::OpsSatellites() : QFrame(KStars::Instance()) { setupUi(this); m_ConfigDialog = KConfigDialog::exists("settings"); //Set up the Table Views m_Model = new QStandardItemModel(0, 1, this); m_SortModel = new SatelliteSortFilterProxyModel(this); m_SortModel->setSourceModel(m_Model); SatListTreeView->setModel(m_SortModel); SatListTreeView->setEditTriggers(QTreeView::NoEditTriggers); SatListTreeView->setSortingEnabled(false); // Populate satellites list updateListView(); // Signals and slots connections connect(UpdateTLEButton, SIGNAL(clicked()), this, SLOT(slotUpdateTLEs())); connect(kcfg_ShowSatellites, SIGNAL(toggled(bool)), SLOT(slotShowSatellites(bool))); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), SLOT(slotCancel())); connect(FilterEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterReg(QString))); connect(m_Model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(slotItemChanged(QStandardItem*))); connect(satelliteButtonGroup, static_cast(&QButtonGroup::buttonPressed), this, [&]() { isDirty = true; }); isDirty = false; } -OpsSatellites::~OpsSatellites() -{ -} - void OpsSatellites::slotUpdateTLEs() { // Save existing satellites saveSatellitesList(); // Get new data files KStarsData::Instance()->skyComposite()->satellites()->updateTLEs(); isDirty = true; // Refresh satellites list updateListView(); } void OpsSatellites::updateListView() { KStarsData *data = KStarsData::Instance(); // Clear satellites list m_Model->clear(); SatListTreeView->reset(); m_Model->setHorizontalHeaderLabels(QStringList(i18n("Satellite name"))); // Add each groups and satellites in the list foreach (SatelliteGroup *sat_group, data->skyComposite()->satellites()->groups()) { QStandardItem *group_item; QStandardItem *sat_item; bool all_sat_checked = true; bool all_sat_unchecked = true; // Add the group group_item = new QStandardItem(i18nc(satgroup_strings_context, sat_group->name().toUtf8())); group_item->setCheckable(true); m_Model->appendRow(group_item); // Add all satellites of the group for (int i = 0; i < sat_group->count(); ++i) { sat_item = new QStandardItem(sat_group->at(i)->name()); sat_item->setCheckable(true); if (Options::selectedSatellites().contains(sat_group->at(i)->name())) { sat_item->setCheckState(Qt::Checked); all_sat_unchecked = false; } else all_sat_checked = false; group_item->setChild(i, sat_item); } // If all satellites of the group are selected, select the group if (all_sat_checked) group_item->setCheckState(Qt::Checked); else if (all_sat_unchecked) group_item->setCheckState(Qt::Unchecked); else group_item->setCheckState(Qt::PartiallyChecked); } } void OpsSatellites::saveSatellitesList() { KStarsData *data = KStarsData::Instance(); QString sat_name; QStringList selected_satellites; QModelIndex group_index, sat_index; QStandardItem *group_item; QStandardItem *sat_item; // Retrieve each satellite in the list and select it if checkbox is checked for (int i = 0; i < m_Model->rowCount(SatListTreeView->rootIndex()); ++i) { group_index = m_Model->index(i, 0, SatListTreeView->rootIndex()); group_item = m_Model->itemFromIndex(group_index); for (int j = 0; j < m_Model->rowCount(group_item->index()); ++j) { sat_index = m_Model->index(j, 0, group_index); sat_item = m_Model->itemFromIndex(sat_index); sat_name = sat_item->data(0).toString(); Satellite *sat = data->skyComposite()->satellites()->findSatellite(sat_name); if (sat) { if (sat_item->checkState() == Qt::Checked) { int rc = sat->updatePos(); // If position calculation fails, unselect it if (rc == 0) { sat->setSelected(true); selected_satellites.append(sat_name); } else { KStars::Instance()->statusBar()->showMessage( i18n("%1 position calculation error: %2.", sat->name(), sat->sgp4ErrorString(rc)), 0); sat->setSelected(false); sat_item->setCheckState(Qt::Unchecked); } } else { sat->setSelected(false); } } } } Options::setSelectedSatellites(selected_satellites); } void OpsSatellites::slotApply() { if (isDirty == false) return; isDirty = false; saveSatellitesList(); // update time for all objects because they might be not initialized // it's needed when using horizontal coordinates KStars::Instance()->data()->setFullTimeUpdate(); KStars::Instance()->updateTime(); KStars::Instance()->map()->forceUpdate(); } void OpsSatellites::slotCancel() { // Update satellites list updateListView(); } void OpsSatellites::slotShowSatellites(bool on) { isDirty = true; kcfg_ShowVisibleSatellites->setEnabled(on); kcfg_ShowSatellitesLabels->setEnabled(on); kcfg_DrawSatellitesLikeStars->setEnabled(on); } void OpsSatellites::slotFilterReg(const QString &filter) { m_SortModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::RegExp)); m_SortModel->setFilterKeyColumn(-1); isDirty = true; // Expand all categories when the user use filter if (filter.length() > 0) SatListTreeView->expandAll(); else SatListTreeView->collapseAll(); } void OpsSatellites::slotItemChanged(QStandardItem *item) { if (item->parent() == nullptr && !item->hasChildren()) { return; } isDirty = true; QModelIndex sat_index; QStandardItem *sat_item; disconnect(m_Model, SIGNAL(itemChanged(QStandardItem *)), this, SLOT(slotItemChanged(QStandardItem *))); // If a group has been (un)checked, (un)check all satellites of the group // else a satellite has been (un)checked, (un)check his group if (item->hasChildren()) { for (int i = 0; i < m_Model->rowCount(item->index()); ++i) { sat_index = m_Model->index(i, 0, item->index()); sat_item = m_Model->itemFromIndex(sat_index); if (item->checkState() == Qt::Checked) sat_item->setCheckState(Qt::Checked); else sat_item->setCheckState(Qt::Unchecked); } } else { bool all_sat_checked = true; bool all_sat_unchecked = true; for (int i = 0; i < item->parent()->model()->rowCount(item->parent()->index()); ++i) { sat_index = m_Model->index(i, 0, item->parent()->index()); sat_item = m_Model->itemFromIndex(sat_index); if (sat_item->checkState() == Qt::Checked) all_sat_unchecked = false; else all_sat_checked = false; } if (all_sat_checked) item->parent()->setCheckState(Qt::Checked); else if (all_sat_unchecked) item->parent()->setCheckState(Qt::Unchecked); else item->parent()->setCheckState(Qt::PartiallyChecked); } connect(m_Model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(slotItemChanged(QStandardItem*))); } diff --git a/kstars/options/opssatellites.h b/kstars/options/opssatellites.h index 4a676549f..2edf7ac73 100644 --- a/kstars/options/opssatellites.h +++ b/kstars/options/opssatellites.h @@ -1,78 +1,80 @@ /*************************************************************************** opssatellites.h - K Desktop Planetarium ------------------- begin : Mon 21 Mar 2011 copyright : (C) 2011 by Jérôme SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_opssatellites.h" #include #include #include class QStandardItem; class QStandardItemModel; class KStars; class SatelliteSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit SatelliteSortFilterProxyModel(QObject *parent); bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; }; /** * @class OpsSatellites + * * The Satellites Tab of the Options window. In this Tab the user can configure * satellites options and select satellites that should be draw + * * @author Jérôme SONRIER * @version 1.0 */ class OpsSatellites : public QFrame, public Ui::OpsSatellites { Q_OBJECT public: /** Constructor */ OpsSatellites(); - /** Destructor */ - ~OpsSatellites() override; + virtual ~OpsSatellites() override = default; private: /** Refresh satellites list */ void updateListView(); /** * @brief saveSatellitesList Saves list of checked satellites in the configuration file */ void saveSatellitesList(); - KConfigDialog *m_ConfigDialog; - QStandardItemModel *m_Model; - QSortFilterProxyModel *m_SortModel; - bool isDirty = false; - private slots: void slotUpdateTLEs(); void slotShowSatellites(bool on); void slotApply(); void slotCancel(); void slotFilterReg(const QString &); void slotItemChanged(QStandardItem *); + +private: + KConfigDialog *m_ConfigDialog { nullptr }; + QStandardItemModel *m_Model { nullptr }; + QSortFilterProxyModel *m_SortModel { nullptr }; + bool isDirty { false }; }; diff --git a/kstars/options/opssolarsystem.cpp b/kstars/options/opssolarsystem.cpp index a6bc36eef..f5a670de2 100644 --- a/kstars/options/opssolarsystem.cpp +++ b/kstars/options/opssolarsystem.cpp @@ -1,128 +1,125 @@ /*************************************************************************** opssolarsystem.cpp - K Desktop Planetarium ------------------- begin : Sun 22 Aug 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 -#include - #include "opssolarsystem.h" + #include "kstars.h" #include "kstarsdata.h" #include "skymap.h" +#include +#include + OpsSolarSystem::OpsSolarSystem() : QFrame(KStars::Instance()) { setupUi(this); connect(kcfg_ShowSolarSystem, SIGNAL(toggled(bool)), SLOT(slotAllWidgets(bool))); connect(kcfg_ShowAsteroids, SIGNAL(toggled(bool)), SLOT(slotAsteroidWidgets(bool))); connect(kcfg_MagLimitAsteroidDownload, SIGNAL(valueChanged(double)), this, SLOT(slotChangeMagDownload(double))); connect(kcfg_ShowComets, SIGNAL(toggled(bool)), SLOT(slotCometWidgets(bool))); connect(ClearAllTrails, SIGNAL(clicked()), KStars::Instance(), SLOT(slotClearAllTrails())); connect(showAllPlanets, SIGNAL(clicked()), this, SLOT(slotSelectPlanets())); connect(showNonePlanets, SIGNAL(clicked()), this, SLOT(slotSelectPlanets())); MagLimitAsteroidDownloadWarning->setVisible(false); kcfg_MagLimitAsteroid->setMinimum(0.0); kcfg_MagLimitAsteroid->setMaximum(30.0); kcfg_MaxRadCometName->setMaximum(100.0); kcfg_MagLimitAsteroidDownload->setMinimum(0.0); kcfg_MagLimitAsteroidDownload->setMaximum(30.0); slotAsteroidWidgets(kcfg_ShowAsteroids->isChecked()); slotCometWidgets(kcfg_ShowComets->isChecked()); //Get a pointer to the KConfigDialog m_ConfigDialog = KConfigDialog::exists("settings"); connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply())); connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply())); //connect( m_ConfigDialog->button(QDialogButtonBox::Cancel), SIGNAL( clicked() ), SLOT( slotCancel() ) ); connect(solarButtonGroup, static_cast(&QButtonGroup::buttonPressed), this, [&]() { isDirty = true; }); } -OpsSolarSystem::~OpsSolarSystem() -{ -} - void OpsSolarSystem::slotChangeMagDownload(double mag) { if (mag > 12) MagLimitAsteroidDownloadWarning->setVisible(true); else MagLimitAsteroidDownloadWarning->setVisible(false); } void OpsSolarSystem::slotAllWidgets(bool on) { MajorBodiesBox->setEnabled(on); MinorBodiesBox->setEnabled(on); TrailsBox->setEnabled(on); } void OpsSolarSystem::slotAsteroidWidgets(bool on) { kcfg_MagLimitAsteroid->setEnabled(on); kcfg_ShowAsteroidNames->setEnabled(on); kcfg_AsteroidLabelDensity->setEnabled(on); textLabel3->setEnabled(on); textLabel6->setEnabled(on); LabelDensity->setEnabled(on); } void OpsSolarSystem::slotCometWidgets(bool on) { kcfg_ShowCometNames->setEnabled(on); kcfg_MaxRadCometName->setEnabled(on); textLabel4->setEnabled(on); kcfg_ShowCometComas->setEnabled(on); } void OpsSolarSystem::slotSelectPlanets() { bool b = true; if (QString(sender()->objectName()) == "showNonePlanets") b = false; kcfg_ShowSun->setChecked(b); kcfg_ShowMoon->setChecked(b); kcfg_ShowMercury->setChecked(b); kcfg_ShowVenus->setChecked(b); kcfg_ShowMars->setChecked(b); kcfg_ShowJupiter->setChecked(b); kcfg_ShowSaturn->setChecked(b); kcfg_ShowUranus->setChecked(b); kcfg_ShowNeptune->setChecked(b); //kcfg_ShowPluto->setChecked( b ); } void OpsSolarSystem::slotApply() { if (isDirty == false) return; isDirty = false; // update time for all objects because they might be not initialized // it's needed when using horizontal coordinates KStars::Instance()->data()->setFullTimeUpdate(); KStars::Instance()->updateTime(); KStars::Instance()->map()->forceUpdate(); } diff --git a/kstars/options/opssolarsystem.h b/kstars/options/opssolarsystem.h index c13934f5d..176a629ac 100644 --- a/kstars/options/opssolarsystem.h +++ b/kstars/options/opssolarsystem.h @@ -1,53 +1,50 @@ /*************************************************************************** opssolarsystem.h - K Desktop Planetarium ------------------- begin : Sun 22 Aug 2004 copyright : (C) 2004 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 OPSSOLARSYSTEM_H_ -#define OPSSOLARSYSTEM_H_ +#pragma once #include "ui_opssolarsystem.h" class KConfigDialog; /** @class OpsSolarSystem *The Solar System page for the Options window. This page allows the user *to modify display of solar system bodies in KStars, including the *major planets, the Sun and Moon, and the comets and asteroids. *@short The Solar System page of the Options window. *@author Jason Harris *@version 1.0 */ class OpsSolarSystem : public QFrame, public Ui::OpsSolarSystem { Q_OBJECT public: explicit OpsSolarSystem(); - ~OpsSolarSystem() override; + virtual ~OpsSolarSystem() override = default; private slots: void slotChangeMagDownload(double mag); void slotAllWidgets(bool on); void slotAsteroidWidgets(bool on); void slotCometWidgets(bool on); void slotSelectPlanets(); void slotApply(); private: - bool isDirty = false; - KConfigDialog *m_ConfigDialog; + bool isDirty { false }; + KConfigDialog *m_ConfigDialog { nullptr }; }; - -#endif diff --git a/kstars/options/opssupernovae.h b/kstars/options/opssupernovae.h index 826a7ad8a..aec1134bb 100644 --- a/kstars/options/opssupernovae.h +++ b/kstars/options/opssupernovae.h @@ -1,50 +1,44 @@ /*************************************************************************** opssupernovae.h - K Desktop Planetarium ------------------- begin : Thu, 25 Aug 2011 copyright : (C) 2011 by Samikshan Bairagya email : samikshan@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 OPSSUPERNOVAE_H_ -#define OPSSUPERNOVAE_H_ +#pragma once #include "ui_opssupernovae.h" #include class KStars; -/** @class OpsSupernovae - *The Supernovae Tab of the Options window. In this Tab the user can configure - *supernovae options and select if supernovae should be drawn on the skymap. - *Also the user is given the option to check for updates on startup. And whether - *to be alerted on startup. - *@author Samikshan Bairagya - *@version 1.0 +/** + * @class OpsSupernovae + * + * The Supernovae Tab of the Options window. In this Tab the user can configure + * supernovae options and select if supernovae should be drawn on the skymap. + * Also the user is given the option to check for updates on startup. And whether + * to be alerted on startup. + * + * @author Samikshan Bairagya + * @version 1.0 */ class OpsSupernovae : public QFrame, public Ui::OpsSupernovae { Q_OBJECT public: - /** - * Constructor - */ explicit OpsSupernovae(); - /** - * Destructor - */ - ~OpsSupernovae() override {} + ~OpsSupernovae() override = default; }; - -#endif //OPSSUPERNOVAE_H_ diff --git a/kstars/printing/loggingform.cpp b/kstars/printing/loggingform.cpp index 75e7da4b3..11e17168e 100644 --- a/kstars/printing/loggingform.cpp +++ b/kstars/printing/loggingform.cpp @@ -1,74 +1,70 @@ /*************************************************************************** loggingform.cpp - K Desktop Planetarium ------------------- begin : Wed Jul 20 2011 copyright : (C) 2011 by RafaÅ‚ KuÅ‚aga email : rl.kulaga@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 "loggingform.h" #include #include #include -LoggingForm::LoggingForm() -{ -} - void LoggingForm::createFinderChartLogger() { QTextCursor cursor(m_Document.get()); cursor.movePosition(QTextCursor::Start); QTextTableFormat tableFormat; tableFormat.setAlignment(Qt::AlignHCenter); tableFormat.setBorder(2); tableFormat.setCellPadding(2); tableFormat.setCellSpacing(4); QTextCharFormat fieldCharFmt; QFont font("Times", 10); fieldCharFmt.setFont(font); QVector constraints; constraints << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25); tableFormat.setColumnWidthConstraints(constraints); QTextTable *table = cursor.insertTable(5, 4, tableFormat); table->mergeCells(0, 0, 1, 4); table->cellAt(0, 0).firstCursorPosition().insertText(i18n("Observer:"), fieldCharFmt); table->mergeCells(1, 0, 1, 2); table->cellAt(1, 0).firstCursorPosition().insertText(i18n("Date:"), fieldCharFmt); table->mergeCells(1, 2, 1, 2); table->cellAt(1, 2).firstCursorPosition().insertText(i18n("Time:"), fieldCharFmt); table->mergeCells(2, 0, 1, 2); table->cellAt(2, 0).firstCursorPosition().insertText(i18n("Site:"), fieldCharFmt); table->cellAt(2, 2).firstCursorPosition().insertText(i18n("Seeing:"), fieldCharFmt); table->cellAt(2, 3).firstCursorPosition().insertText(i18n("Trans:"), fieldCharFmt); table->mergeCells(3, 0, 1, 4); table->cellAt(3, 0).firstCursorPosition().insertText(i18n("Telescope:"), fieldCharFmt); table->mergeCells(4, 0, 1, 3); table->cellAt(4, 0).firstCursorPosition().insertText(i18n("Eyepiece:"), fieldCharFmt); table->cellAt(4, 3).firstCursorPosition().insertText(i18n("Power:"), fieldCharFmt); } QTextDocument *LoggingForm::getDocument() { return m_Document.get(); } diff --git a/kstars/printing/loggingform.h b/kstars/printing/loggingform.h index baf11bcf0..a3082720d 100644 --- a/kstars/printing/loggingform.h +++ b/kstars/printing/loggingform.h @@ -1,47 +1,47 @@ /*************************************************************************** loggingform.h - K Desktop Planetarium ------------------- begin : Wed Jul 20 2011 copyright : (C) 2011 by RafaÅ‚ KuÅ‚aga email : rl.kulaga@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. * * * ***************************************************************************/ #pragma once #include "kstarsdocument.h" class QTextDocument; /** * @class LoggingForm * @brief Class that represents logging form. * Currently, LoggingForm class is used to create logging forms for finder charts. * * @author RafaÅ‚ KuÅ‚aga */ class LoggingForm : public KStarsDocument { public: /** Constructor */ - LoggingForm(); + LoggingForm() = default; /** Create simple logging form for finder charts. */ void createFinderChartLogger(); /** * @brief Get logging form internal QTextDocument. * This method is used to enable inserting of LoggingForm objects into QTextDocument * instances. * @return QTextDocument that contains logging form. */ QTextDocument *getDocument(); }; diff --git a/kstars/projections/lambertprojector.h b/kstars/projections/lambertprojector.h index ffa5296ab..3663d9f85 100644 --- a/kstars/projections/lambertprojector.h +++ b/kstars/projections/lambertprojector.h @@ -1,42 +1,42 @@ /* Copyright (C) 2010 Henry de Valence 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef LAMBERTPROJECTOR_H #define LAMBERTPROJECTOR_H #include "projector.h" /** * @class LambertProjector * * Implememntation of Lambert azimuthal equal-area projection * */ class LambertProjector : public Projector { public: explicit LambertProjector(const ViewParams &p); - ~LambertProjector() override {} + ~LambertProjector() override = default; Projection type() const override; double radius() const override; double projectionK(double x) const override; double projectionL(double x) const override; }; #endif // LAMBERTPROJECTOR_H diff --git a/kstars/projections/projector.cpp b/kstars/projections/projector.cpp index df0413f51..8a34b0342 100644 --- a/kstars/projections/projector.cpp +++ b/kstars/projections/projector.cpp @@ -1,564 +1,560 @@ /* Copyright (C) 2010 Henry de Valence 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "projector.h" #include "ksutils.h" #ifdef KSTARS_LITE #include "skymaplite.h" #endif #include "skycomponents/skylabeler.h" namespace { void toXYZ(const SkyPoint *p, double *x, double *y, double *z) { double sinRa, sinDec, cosRa, cosDec; p->ra().SinCos(sinRa, cosRa); p->dec().SinCos(sinDec, cosDec); *x = cosDec * cosRa; *y = cosDec * sinRa; *z = sinDec; } } SkyPoint Projector::pointAt(double az) { SkyPoint p; p.setAz(az); p.setAlt(0.0); p.HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); return p; } Projector::Projector(const ViewParams &p) { m_data = KStarsData::Instance(); setViewParams(p); // Force clip polygon update updateClipPoly(); } -Projector::~Projector() -{ -} - void Projector::setViewParams(const ViewParams &p) { m_vp = p; /** Precompute cached values */ //Find Sin/Cos for focus point m_sinY0 = 0; m_cosY0 = 0; if (m_vp.useAltAz) { m_vp.focus->alt().SinCos(m_sinY0, m_cosY0); } else { m_vp.focus->dec().SinCos(m_sinY0, m_cosY0); } double currentFOV = m_fov; //Find FOV in radians m_fov = sqrt(m_vp.width * m_vp.width + m_vp.height * m_vp.height) / (2 * m_vp.zoomFactor * dms::DegToRad); //Set checkVisibility variables double Ymax; if (m_vp.useAltAz) { m_xrange = 1.2 * m_fov / cos(m_vp.focus->alt().radians()); Ymax = fabs(m_vp.focus->alt().Degrees()) + m_fov; } else { m_xrange = 1.2 * m_fov / cos(m_vp.focus->dec().radians()); Ymax = fabs(m_vp.focus->dec().Degrees()) + m_fov; } m_isPoleVisible = (Ymax >= 90.0); // Only update clipping polygon if there is an FOV change if (currentFOV != m_fov) updateClipPoly(); } double Projector::fov() const { return m_fov; } QPointF Projector::toScreen(const SkyPoint *o, bool oRefract, bool *onVisibleHemisphere) const { return KSUtils::vecToPoint(toScreenVec(o, oRefract, onVisibleHemisphere)); } bool Projector::onScreen(const QPointF &p) const { return (0 <= p.x() && p.x() <= m_vp.width && 0 <= p.y() && p.y() <= m_vp.height); } bool Projector::onScreen(const Vector2f &p) const { return onScreen(QPointF(p.x(), p.y())); } QPointF Projector::clipLine(SkyPoint *p1, SkyPoint *p2) const { return KSUtils::vecToPoint(clipLineVec(p1, p2)); } Vector2f Projector::clipLineVec(SkyPoint *p1, SkyPoint *p2) const { /* ASSUMES p1 was not clipped but p2 was. * Return the QPoint that barely clips in the line twixt p1 and p2. */ //TODO: iteration = ceil( 0.5*log2( w^2 + h^2) )?? // also possibly rewrite this // --hdevalence int iteration = 15; // For "perfect" clipping: // 2^interations should be >= max pixels/line bool isVisible = true; // so we start at midpoint SkyPoint mid; Vector2f oMid; double x, y, z, dx, dy, dz, ra, dec; int newx, newy, oldx, oldy; oldx = oldy = -10000; // any old value that is not the first omid toXYZ(p1, &x, &y, &z); // -jbb printf("\np1: %6.4f %6.4f %6.4f\n", x, y, z); toXYZ(p2, &dx, &dy, &dz); // -jbb printf("p2: %6.4f %6.4f %6.4f\n", dx, dy, dz); dx -= x; dy -= y; dz -= z; // Successive approximation to point on line that just clips. while (iteration-- > 0) { dx *= .5; dy *= .5; dz *= .5; if (!isVisible) // move back toward visible p1 { x -= dx; y -= dy; z -= dz; } else // move out toward clipped p2 { x += dx; y += dy; z += dz; } // -jbb printf(" : %6.4f %6.4f %6.4f\n", x, y, z); // [x, y, z] => [ra, dec] ra = atan2(y, x); dec = asin(z / sqrt(x * x + y * y + z * z)); mid = SkyPoint(ra * 12. / dms::PI, dec * 180. / dms::PI); mid.EquatorialToHorizontal(m_data->lst(), m_data->geo()->lat()); oMid = toScreenVec(&mid, false, &isVisible); //AND the result with checkVisibility to clip things going below horizon isVisible &= checkVisibility(&mid); newx = (int)oMid.x(); newy = (int)oMid.y(); // -jbb printf("new x/y: %4d %4d", newx, newy); if ((oldx == newx) && (oldy == newy)) { break; } oldx = newx; oldy = newy; } return oMid; } bool Projector::checkVisibility(const SkyPoint *p) const { //TODO deal with alternate projections //not clear how this depends on projection //FIXME do these heuristics actually work? double dX, dY; //Skip objects below the horizon if the ground is drawn /* Is the cost of this conversion actually less than drawing it anyways? * EquatorialToHorizontal takes 3 SinCos calls -- so 6 trig calls if not using GNU exts. */ /* if( m_vp.fillGround ) { if( !m_vp.useAltAz ) p->EquatorialToHorizontal( m_data->lst(), m_data->geo()->lat() ); if( p->alt().Degrees() < -1.0 ) return false; } */ //Here we hope that the point has already been 'synchronized' if (m_vp.fillGround /*&& m_vp.useAltAz*/ && p->alt().Degrees() < -1.0) return false; if (m_vp.useAltAz) { /** To avoid calculating refraction, we just use the unrefracted altitude and add a 2-degree 'safety factor' */ dY = fabs(p->alt().Degrees() - m_vp.focus->alt().Degrees()) - 2.; } else { dY = fabs(p->dec().Degrees() - m_vp.focus->dec().Degrees()); } if (m_isPoleVisible) dY *= 0.75; //increase effective FOV when pole visible. if (dY > m_fov) return false; if (m_isPoleVisible) return true; if (m_vp.useAltAz) { dX = fabs(p->az().Degrees() - m_vp.focus->az().Degrees()); } else { dX = fabs(p->ra().Degrees() - m_vp.focus->ra().Degrees()); } if (dX > 180.0) dX = 360.0 - dX; // take shorter distance around sky return dX < m_xrange; } // FIXME: There should be a MUCH more efficient way to do this (see EyepieceField for example) double Projector::findNorthPA(SkyPoint *o, float x, float y) const { //Find position angle of North using a test point displaced to the north //displace by 100/zoomFactor radians (so distance is always 100 pixels) //this is 5730/zoomFactor degrees KStarsData *data = KStarsData::Instance(); double newDec = o->dec().Degrees() + 5730.0 / m_vp.zoomFactor; if (newDec > 90.0) newDec = 90.0; SkyPoint test(o->ra().Hours(), newDec); if (m_vp.useAltAz) test.EquatorialToHorizontal(data->lst(), data->geo()->lat()); Vector2f t = toScreenVec(&test); float dx = t.x() - x; float dy = y - t.y(); //backwards because QWidget Y-axis increases to the bottom float north; if (dy) { north = atan2f(dx, dy) * 180.0 / dms::PI; } else { north = (dx > 0.0 ? -90.0 : 90.0); } return north; } double Projector::findPA(SkyObject *o, float x, float y) const { return (findNorthPA(o, x, y) + o->pa()); } QVector Projector::groundPoly(SkyPoint *labelpoint, bool *drawLabel) const { QVector ground; static const QString horizonLabel = i18n("Horizon"); float marginLeft, marginRight, marginTop, marginBot; SkyLabeler::Instance()->getMargins(horizonLabel, &marginLeft, &marginRight, &marginTop, &marginBot); //daz is 1/2 the width of the sky in degrees double daz = 90.; if (m_vp.useAltAz) { daz = 0.5 * m_vp.width * 57.3 / m_vp.zoomFactor; //center to edge, in degrees if (type() == Projector::Orthographic) { daz = daz * 1.4; } daz = qMin(qreal(90.0), daz); } double faz = m_vp.focus->az().Degrees(); double az1 = faz - daz; double az2 = faz + daz; bool allGround = true; bool allSky = true; double inc = 1.0; //Add points along horizon for (double az = az1; az <= az2 + inc; az += inc) { SkyPoint p = pointAt(az); bool visible = false; Vector2f o = toScreenVec(&p, false, &visible); if (visible) { ground.append(o); //Set the label point if this point is onscreen if (labelpoint && o.x() < marginRight && o.y() > marginTop && o.y() < marginBot) *labelpoint = p; if (o.y() > 0.) allGround = false; if (o.y() < m_vp.height) allSky = false; } } if (allSky) { if (drawLabel) *drawLabel = false; return QVector(); } if (allGround) { ground.clear(); ground.append(Vector2f(-10., -10.)); ground.append(Vector2f(m_vp.width + 10., -10.)); ground.append(Vector2f(m_vp.width + 10., m_vp.height + 10.)); ground.append(Vector2f(-10., m_vp.height + 10.)); if (drawLabel) *drawLabel = false; return ground; } //In Gnomonic projection, or if sufficiently zoomed in, we can complete //the ground polygon by simply adding offscreen points //FIXME: not just gnomonic if (daz < 25.0 || type() == Projector::Gnomonic) { ground.append(Vector2f(m_vp.width + 10.f, ground.last().y())); ground.append(Vector2f(m_vp.width + 10.f, m_vp.height + 10.f)); ground.append(Vector2f(-10.f, m_vp.height + 10.f)); ground.append(Vector2f(-10.f, ground.first().y())); } else { double r = m_vp.zoomFactor * radius(); double t1 = atan2(-1. * (ground.last().y() - 0.5 * m_vp.height), ground.last().x() - 0.5 * m_vp.width) / dms::DegToRad; double t2 = t1 - 180.; for (double t = t1; t >= t2; t -= inc) //step along circumference { dms a(t); double sa(0.), ca(0.); a.SinCos(sa, ca); ground.append(Vector2f(0.5 * m_vp.width + r * ca, 0.5 * m_vp.height - r * sa)); } } if (drawLabel) *drawLabel = true; return ground; } void Projector::updateClipPoly() { m_clipPolygon.clear(); double r = m_vp.zoomFactor * radius(); double t1 = 0; double t2 = 360; double inc = 1.0; for (double t = t1; t <= t2; t += inc) { //step along circumference dms a(t); double sa(0.), ca(0.); a.SinCos(sa, ca); m_clipPolygon << QPointF(0.5 * m_vp.width + r * ca, 0.5 * m_vp.height - r * sa); } } QPolygonF Projector::clipPoly() const { return m_clipPolygon; } bool Projector::unusablePoint(const QPointF &p) const { //r0 is the angular size of the sky horizon, in radians double r0 = radius(); //If the zoom is high enough, all points are usable //The center-to-corner distance, in radians double r = 0.5 * 1.41421356 * m_vp.width / m_vp.zoomFactor; if (r < r0) return false; //At low zoom, we have to determine whether the point is beyond the sky horizon //Convert pixel position to x and y offsets in radians double dx = (0.5 * m_vp.width - p.x()) / m_vp.zoomFactor; double dy = (0.5 * m_vp.height - p.y()) / m_vp.zoomFactor; return (dx * dx + dy * dy) > r0 * r0; } SkyPoint Projector::fromScreen(const QPointF &p, dms *LST, const dms *lat) const { dms c; double sinc, cosc; /** N.B. We don't cache these sin/cos values in the inverse * projection because it causes 'shaking' when moving the sky. */ double sinY0, cosY0; //Convert pixel position to x and y offsets in radians double dx = (0.5 * m_vp.width - p.x()) / m_vp.zoomFactor; double dy = (0.5 * m_vp.height - p.y()) / m_vp.zoomFactor; double r = sqrt(dx * dx + dy * dy); c.setRadians(projectionL(r)); c.SinCos(sinc, cosc); if (m_vp.useAltAz) { dx = -1.0 * dx; //Azimuth goes in opposite direction compared to RA m_vp.focus->alt().SinCos(sinY0, cosY0); } else { m_vp.focus->dec().SinCos(sinY0, cosY0); } double Y = asin(cosc * sinY0 + (r == 0 ? 0 : (dy * sinc * cosY0) / r)); double atop = dx * sinc; double abot = r * cosY0 * cosc - dy * sinY0 * sinc; double A = atan2(atop, abot); SkyPoint result; if (m_vp.useAltAz) { dms alt, az; alt.setRadians(Y); az.setRadians(A + m_vp.focus->az().radians()); if (m_vp.useRefraction) alt = SkyPoint::unrefract(alt); result.setAlt(alt); result.setAz(az); result.HorizontalToEquatorial(LST, lat); } else { dms ra, dec; dec.setRadians(Y); ra.setRadians(A + m_vp.focus->ra().radians()); result.set(ra.reduce(), dec); result.EquatorialToHorizontal(LST, lat); } return result; } Vector2f Projector::toScreenVec(const SkyPoint *o, bool oRefract, bool *onVisibleHemisphere) const { double Y, dX; double sindX, cosdX, sinY, cosY; oRefract &= m_vp.useRefraction; if (m_vp.useAltAz) { if (oRefract) Y = SkyPoint::refract(o->alt()).radians(); //account for atmospheric refraction else Y = o->alt().radians(); dX = m_vp.focus->az().radians() - o->az().radians(); } else { dX = o->ra().radians() - m_vp.focus->ra().radians(); Y = o->dec().radians(); } if (!(std::isfinite(Y) && std::isfinite(dX))) { return Vector2f(0, 0); // JM: Enable this again later when trying to find a solution for it // As it is now creating too much noise in the log file. /* qDebug() << "Assert in Projector::toScreenVec failed!"; qDebug() << "using AltAz?" << m_vp.useAltAz << " Refract? " << oRefract; const SkyObject *obj; qDebug() << "Point supplied has RA0 = " << o->ra0().toHMSString() << " Dec0 = " << o->dec0().toDMSString() << "; alt = " << o->alt().toDMSString() << "; az = " << o->az().toDMSString(); if ( (obj = dynamic_cast(o) ) ) { qDebug() << "Point is object with name = " << obj->name() << " longname = " << obj->longname(); } qDebug() << "dX = " << dX << " and isfinite(dX) is" << std::isfinite(dX); qDebug() << "Y = " << Y << " and isfinite(Y) is" << std::isfinite(Y); //Q_ASSERT( false ); */ } dX = KSUtils::reduceAngle(dX, -dms::PI, dms::PI); //Convert dX, Y coords to screen pixel coords, using GNU extension if available #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) sincos(dX, &sindX, &cosdX); sincos(Y, &sinY, &cosY); #else sindX = sin(dX); cosdX = cos(dX); sinY = sin(Y); cosY = cos(Y); #endif //c is the cosine of the angular distance from the center double c = m_sinY0 * sinY + m_cosY0 * cosY * cosdX; //If c is less than 0.0, then the "field angle" (angular distance from the focus) //is more than 90 degrees. This is on the "back side" of the celestial sphere //and should not be drawn. if (onVisibleHemisphere) *onVisibleHemisphere = (c > cosMaxFieldAngle()); // TODO: Isn't it more efficient to bypass the final calculation below if the object is not visible? double k = projectionK(c); double origX = m_vp.width / 2; double origY = m_vp.height / 2; double x = origX - m_vp.zoomFactor * k * cosY * sindX; double y = origY - m_vp.zoomFactor * k * (m_cosY0 * sinY - m_sinY0 * cosY * cosdX); #ifdef KSTARS_LITE double skyRotation = SkyMapLite::Instance()->getSkyRotation(); if (skyRotation != 0) { dms rotation(skyRotation); double cosT, sinT; rotation.SinCos(sinT, cosT); double newX = origX + (x - origX) * cosT - (y - origY) * sinT; double newY = origY + (x - origX) * sinT + (y - origY) * cosT; x = newX; y = newY; } #endif return Vector2f(x, y); } diff --git a/kstars/projections/projector.h b/kstars/projections/projector.h index 72583a340..3d70b7be4 100644 --- a/kstars/projections/projector.h +++ b/kstars/projections/projector.h @@ -1,296 +1,297 @@ /* Copyright (C) 2010 Henry de Valence 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "skymap.h" #endif #include "skyobjects/skypoint.h" #if __GNUC__ > 5 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-attributes" #endif #if __GNUC__ > 6 #pragma GCC diagnostic ignored "-Wint-in-bool-context" #endif #include #if __GNUC__ > 5 #pragma GCC diagnostic pop #endif #include #include #include using namespace Eigen; class KStarsData; /** This is just a container that holds infromation needed to do projections. */ class ViewParams { public: float width, height; float zoomFactor; bool useRefraction; bool useAltAz; bool fillGround; /// groundPoly(SkyPoint *labelpoint = nullptr, bool *drawLabel = nullptr) const; /** * @brief updateClipPoly calculate the clipping polygen given the current FOV. */ virtual void updateClipPoly(); /** * @return the clipping polygen covering the visible sky area. Anything outside this polygon is * clipped by QPainter. */ virtual QPolygonF clipPoly() const; protected: /** * Get the radius of this projection's sky circle. * @return the radius in radians */ virtual double radius() const { return 2 * M_PI; } /** * This function handles some of the projection-specific code. * @see toScreen() */ virtual double projectionK(double x) const { return x; } /** * This function handles some of the projection-specific code. * @see toScreen() */ virtual double projectionL(double x) const { return x; } /** * This function returns the cosine of the maximum field angle, i.e., the maximum angular * distance from the focus for which a point should be projected. Default is 0, i.e., * 90 degrees. */ virtual double cosMaxFieldAngle() const { return 0; } /** * Helper function for drawing ground. * @return the point with Alt = 0, az = @p az */ static SkyPoint pointAt(double az); KStarsData *m_data { nullptr }; ViewParams m_vp; double m_sinY0 { 0 }; double m_cosY0 { 0 }; double m_fov { 0 }; QPolygonF m_clipPolygon; private: //Used by CheckVisibility double m_xrange { 0 }; bool m_isPoleVisible { false }; }; diff --git a/kstars/skycomponents/artificialhorizoncomponent.cpp b/kstars/skycomponents/artificialhorizoncomponent.cpp index 816ce2dc8..a7e2e9ce9 100644 --- a/kstars/skycomponents/artificialhorizoncomponent.cpp +++ b/kstars/skycomponents/artificialhorizoncomponent.cpp @@ -1,179 +1,175 @@ /* Artificial Horizon Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "artificialhorizoncomponent.h" #include "kstarsdata.h" #include "linelist.h" #include "Options.h" #include "skymap.h" #include "skymapcomposite.h" #include "skypainter.h" #include "projections/projector.h" -ArtificialHorizonEntity::ArtificialHorizonEntity() -{ -} - ArtificialHorizonEntity::~ArtificialHorizonEntity() { clearList(); } QString ArtificialHorizonEntity::region() const { return m_Region; } void ArtificialHorizonEntity::setRegion(const QString &Region) { m_Region = Region; } bool ArtificialHorizonEntity::enabled() const { return m_Enabled; } void ArtificialHorizonEntity::setEnabled(bool Enabled) { m_Enabled = Enabled; } void ArtificialHorizonEntity::setList(const std::shared_ptr &list) { m_List = list; } std::shared_ptr ArtificialHorizonEntity::list() { return m_List; } void ArtificialHorizonEntity::clearList() { m_List.reset(); } ArtificialHorizonComponent::ArtificialHorizonComponent(SkyComposite *parent) : NoPrecessIndex(parent, i18n("Artificial Horizon")) { load(); } ArtificialHorizonComponent::~ArtificialHorizonComponent() { qDeleteAll(m_HorizonList); m_HorizonList.clear(); } bool ArtificialHorizonComponent::load() { m_HorizonList = KStarsData::Instance()->userdb()->GetAllHorizons(); foreach (ArtificialHorizonEntity *horizon, m_HorizonList) appendLine(horizon->list()); return true; } void ArtificialHorizonComponent::save() { KStarsData::Instance()->userdb()->DeleteAllHorizons(); foreach (ArtificialHorizonEntity *horizon, m_HorizonList) KStarsData::Instance()->userdb()->AddHorizon(horizon); } bool ArtificialHorizonComponent::selected() { return Options::showGround(); } void ArtificialHorizonComponent::preDraw(SkyPainter *skyp) { QColor color(KStarsData::Instance()->colorScheme()->colorNamed("ArtificialHorizonColor")); color.setAlpha(40); skyp->setBrush(QBrush(color)); skyp->setPen(Qt::NoPen); } void ArtificialHorizonComponent::draw(SkyPainter *skyp) { if (!selected()) return; if (livePreview.get()) { skyp->setPen(QPen(Qt::white, 3)); skyp->drawSkyPolyline(livePreview.get()); return; } preDraw(skyp); DrawID drawID = skyMesh()->drawID(); //UpdateID updateID = KStarsData::Instance()->updateID(); //foreach ( LineList* lineList, listList() ) for (int i = 0; i < listList().count(); i++) { std::shared_ptr lineList = listList().at(i); if (lineList->drawID == drawID || m_HorizonList.at(i)->enabled() == false) continue; lineList->drawID = drawID; /*if ( lineList->updateID != updateID ) JITupdate( lineList );*/ skyp->drawSkyPolygon(lineList.get(), false); } } void ArtificialHorizonComponent::removeRegion(const QString ®ionName, bool lineOnly) { ArtificialHorizonEntity *regionHorizon = nullptr; foreach (ArtificialHorizonEntity *horizon, m_HorizonList) { if (horizon->region() == regionName) { regionHorizon = horizon; break; } } if (regionHorizon == nullptr) return; if (regionHorizon->list()) removeLine(regionHorizon->list()); if (lineOnly) regionHorizon->clearList(); else { m_HorizonList.removeOne(regionHorizon); delete (regionHorizon); } } void ArtificialHorizonComponent::addRegion(const QString ®ionName, bool enabled, const std::shared_ptr &list) { ArtificialHorizonEntity *horizon = new ArtificialHorizonEntity; horizon->setRegion(regionName); horizon->setEnabled(enabled); horizon->setList(list); m_HorizonList.append(horizon); appendLine(list); } diff --git a/kstars/skycomponents/artificialhorizoncomponent.h b/kstars/skycomponents/artificialhorizoncomponent.h index 39f569f98..4e62e1c4f 100644 --- a/kstars/skycomponents/artificialhorizoncomponent.h +++ b/kstars/skycomponents/artificialhorizoncomponent.h @@ -1,76 +1,77 @@ /* Artificial Horizon Component Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "noprecessindex.h" #include class ArtificialHorizonEntity { public: - ArtificialHorizonEntity(); + ArtificialHorizonEntity() = default; ~ArtificialHorizonEntity(); QString region() const; void setRegion(const QString &Region); bool enabled() const; void setEnabled(bool Enabled); void clearList(); void setList(const std::shared_ptr &list); std::shared_ptr list(); private: QString m_Region; bool m_Enabled { false }; std::shared_ptr m_List; }; /** * @class ArtificialHorizon * Represents custom area from the horizon upwards which represent blocked views from the vantage point of the user. * Such blocked views could stem for example from tall trees or buildings. The user can define one or more polygons to * represent the blocked areas. * * @author Jasem Mutlaq * @version 0.1 */ class ArtificialHorizonComponent : public NoPrecessIndex { public: /** * @short Constructor + * * @p parent pointer to the parent SkyComposite object * name is the name of the subclass */ explicit ArtificialHorizonComponent(SkyComposite *parent); - ~ArtificialHorizonComponent() override; + virtual ~ArtificialHorizonComponent() override; bool selected() override; void draw(SkyPainter *skyp) override; void setLivePreview(const std::shared_ptr &preview) { livePreview = preview; } void addRegion(const QString ®ionName, bool enabled, const std::shared_ptr &list); void removeRegion(const QString ®ionName, bool lineOnly = false); inline QList *horizonList() { return &m_HorizonList; } bool load(); void save(); protected: void preDraw(SkyPainter *skyp) override; private: QList m_HorizonList; std::shared_ptr livePreview; }; diff --git a/kstars/skycomponents/asteroidscomponent.cpp b/kstars/skycomponents/asteroidscomponent.cpp index ce2eb1f88..13c733217 100644 --- a/kstars/skycomponents/asteroidscomponent.cpp +++ b/kstars/skycomponents/asteroidscomponent.cpp @@ -1,307 +1,304 @@ /*************************************************************************** asteroidscomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/30/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 -#include -#include -#include -#include - -#include - #include "asteroidscomponent.h" #ifndef KSTARS_LITE #include "kstars.h" #endif -#include "auxiliary/filedownloader.h" -#include "projections/projector.h" +#include "ksfilereader.h" +#include "kstarsdata.h" +#include "Options.h" #include "solarsystemcomposite.h" #include "skycomponent.h" #include "skylabeler.h" #ifndef KSTARS_LITE #include "skymap.h" #else #include "kstarslite.h" #endif #include "skypainter.h" -#include "Options.h" -#include "kstarsdata.h" -#include "ksfilereader.h" #include "auxiliary/kspaths.h" #include "auxiliary/ksnotification.h" +#include "auxiliary/filedownloader.h" +#include "projections/projector.h" + +#include + +#include +#include +#include +#include + +#include AsteroidsComponent::AsteroidsComponent(SolarSystemComposite *parent) : BinaryListComponent(this, "asteroids"), SolarSystemListComponent(parent) { loadData(); } -AsteroidsComponent::~AsteroidsComponent() -{ -} - bool AsteroidsComponent::selected() { return Options::showAsteroids(); } /* * @short Initialize the asteroids list. * Reads in the asteroids data from the asteroids.dat file * and writes it into the Binary File; * * The data file is a CSV file with the following columns : * @li 1 full name [string] * @li 2 Modified Julian Day of orbital elements [int] * @li 3 perihelion distance in AU [double] * @li 4 semi-major axis * @li 5 eccentricity of orbit [double] * @li 6 inclination angle of orbit in degrees [double] * @li 7 argument of perihelion in degrees [double] * @li 8 longitude of the ascending node in degrees [double] * @li 9 mean anomaly * @li 10 time of perihelion passage (YYYYMMDD.DDD) [double] * @li 11 orbit solution ID [string] * @li 12 absolute magnitude [float] * @li 13 slope parameter [float] * @li 14 Near-Earth Object (NEO) flag [bool] * @li 15 comet total magnitude parameter [float] (we should remove this column) * @li 16 comet nuclear magnitude parameter [float] (we should remove this column) * @li 17 object diameter (from equivalent sphere) [float] * @li 18 object bi/tri-axial ellipsoid dimensions [string] * @li 19 geometric albedo [float] * @li 20 rotation period [float] * @li 21 orbital period [float] * @li 22 earth minimum orbit intersection distance [double] * @li 23 orbit classification [string] */ void AsteroidsComponent::loadDataFromText() { QString name, full_name, orbit_id, orbit_class, dimensions; int mJD; double q, a, e, dble_i, dble_w, dble_N, dble_M, H, G, earth_moid; long double JD; float diameter, albedo, rot_period, period; bool neo; emitProgressText(i18n("Loading asteroids")); QList> sequence; sequence.append(qMakePair(QString("full name"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("epoch_mjd"), KSParser::D_INT)); sequence.append(qMakePair(QString("q"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("a"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("e"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("i"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("w"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("om"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("ma"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("tp_calc"), KSParser::D_SKIP)); sequence.append(qMakePair(QString("orbit_id"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("H"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("G"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("neo"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("tp_calc"), KSParser::D_SKIP)); sequence.append(qMakePair(QString("M2"), KSParser::D_SKIP)); sequence.append(qMakePair(QString("diameter"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("extent"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("albedo"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("rot_period"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("per_y"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("moid"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("class"), KSParser::D_QSTRING)); KSParser asteroid_parser(filepath_txt, '#', sequence); QHash row_content; while (asteroid_parser.HasNextRow()) { row_content = asteroid_parser.ReadNextRow(); full_name = row_content["full name"].toString(); full_name = full_name.trimmed(); int catN = full_name.section(' ', 0, 0).toInt(); name = full_name.section(' ', 1, -1); //JM temporary hack to avoid Europa,Io, and Asterope duplication if (name == i18nc("Asteroid name (optional)", "Europa") || name == i18nc("Asteroid name (optional)", "Io") || name == i18nc("Asteroid name (optional)", "Asterope")) name += i18n(" (Asteroid)"); mJD = row_content["epoch_mjd"].toInt(); q = row_content["q"].toDouble(); a = row_content["a"].toDouble(); e = row_content["e"].toDouble(); dble_i = row_content["i"].toDouble(); dble_w = row_content["w"].toDouble(); dble_N = row_content["om"].toDouble(); dble_M = row_content["ma"].toDouble(); orbit_id = row_content["orbit_id"].toString(); H = row_content["H"].toDouble(); G = row_content["G"].toDouble(); neo = row_content["neo"].toString() == "Y"; diameter = row_content["diameter"].toFloat(); dimensions = row_content["extent"].toString(); albedo = row_content["albedo"].toFloat(); rot_period = row_content["rot_period"].toFloat(); period = row_content["per_y"].toFloat(); earth_moid = row_content["moid"].toDouble(); orbit_class = row_content["class"].toString(); JD = static_cast(mJD) + 2400000.5; KSAsteroid *new_asteroid = nullptr; // Diameter is missing from JPL data if (name == i18nc("Asteroid name (optional)", "Pluto")) diameter = 2390; new_asteroid = new KSAsteroid(catN, name, QString(), JD, a, e, dms(dble_i), dms(dble_w), dms(dble_N), dms(dble_M), H, G); new_asteroid->setPerihelion(q); new_asteroid->setOrbitID(orbit_id); new_asteroid->setNEO(neo); new_asteroid->setDiameter(diameter); new_asteroid->setDimensions(dimensions); new_asteroid->setAlbedo(albedo); new_asteroid->setRotationPeriod(rot_period); new_asteroid->setPeriod(period); new_asteroid->setEarthMOID(earth_moid); new_asteroid->setOrbitClass(orbit_class); new_asteroid->setPhysicalSize(diameter); //new_asteroid->setAngularSize(0.005); appendListObject(new_asteroid); // Add name to the list of object names objectNames(SkyObject::ASTEROID).append(name); objectLists(SkyObject::ASTEROID).append(QPair(name, new_asteroid)); } } void AsteroidsComponent::draw(SkyPainter *skyp) { Q_UNUSED(skyp) #ifndef KSTARS_LITE if (!selected()) return; bool hideLabels = !Options::showAsteroidNames() || (SkyMap::Instance()->isSlewing() && Options::hideLabels()); double lgmin = log10(MINZOOM); double lgmax = log10(MAXZOOM); double lgz = log10(Options::zoomFactor()); double labelMagLimit = 2.5 + Options::asteroidLabelDensity() / 5.0; labelMagLimit += (15.0 - labelMagLimit) * (lgz - lgmin) / (lgmax - lgmin); if (labelMagLimit > 10.0) labelMagLimit = 10.0; //printf("labelMagLim = %.1f\n", labelMagLimit ); skyp->setBrush(QBrush(QColor("gray"))); foreach (SkyObject *so, m_ObjectList) { // FIXME: God help us! KSAsteroid *ast = (KSAsteroid *)so; if (!ast->toDraw()) continue; bool drawn = false; if (ast->image().isNull() == false) drawn = skyp->drawPlanet(ast); else drawn = skyp->drawPointSource(ast, ast->mag()); if (drawn && !(hideLabels || ast->mag() >= labelMagLimit)) SkyLabeler::AddLabel(ast, SkyLabeler::ASTEROID_LABEL); } #endif } SkyObject *AsteroidsComponent::objectNearest(SkyPoint *p, double &maxrad) { SkyObject *oBest = nullptr; if (!selected()) return nullptr; foreach (SkyObject *o, m_ObjectList) { if (!(((KSAsteroid*)o)->toDraw())) continue; double r = o->angularDistanceTo(p).Degrees(); if (r < maxrad) { oBest = o; maxrad = r; } } return oBest; } void AsteroidsComponent::updateDataFile() { downloadJob = new FileDownloader(); downloadJob->setProgressDialogEnabled(true, i18n("Asteroid Update"), i18n("Downloading asteroids updates...")); downloadJob->registerDataVerification([&](const QByteArray &data) { return data.startsWith("full_name");}); QObject::connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); QObject::connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); QUrl url = QUrl("https://ssd.jpl.nasa.gov/sbdb_query.cgi"); QByteArray mag = QString::number(Options::magLimitAsteroidDownload()).toUtf8(); QByteArray post_data = KSUtils::getJPLQueryString("ast", "AcBdBiBhBgBjBlBkBmBqBbAiAjAgAkAlApAqArAsBsBtCh", QVector{ { "Ai", "<", mag } }); downloadJob->post(url, post_data); } void AsteroidsComponent::downloadReady() { // Comment the first line QByteArray data = downloadJob->downloadedData(); data.insert(0, '#'); // Write data to asteroids.dat QFile file(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "asteroids.dat"); file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); file.write(data); file.close(); // Reload asteroids loadData(true); #ifdef KSTARS_LITE KStarsLite::Instance()->data()->setFullTimeUpdate(); #else KStars::Instance()->data()->setFullTimeUpdate(); #endif downloadJob->deleteLater(); } void AsteroidsComponent::downloadError(const QString &errorString) { KSNotification::error(i18n("Error downloading asteroids data: %1", errorString)); qDebug() << i18n("Error downloading asteroids data: %1", errorString); downloadJob->deleteLater(); } diff --git a/kstars/skycomponents/asteroidscomponent.h b/kstars/skycomponents/asteroidscomponent.h index dd6126f4f..e0700f0c1 100644 --- a/kstars/skycomponents/asteroidscomponent.h +++ b/kstars/skycomponents/asteroidscomponent.h @@ -1,68 +1,69 @@ /*************************************************************************** asteroidscomponent.h - K Desktop Planetarium ------------------- begin : 2005/30/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 ASTEROIDSCOMPONENT_H -#define ASTEROIDSCOMPONENT_H - -#include -#include +#pragma once #include "binarylistcomponent.h" #include "ksparser.h" #include "typedef.h" #include "skyobjects/ksasteroid.h" #include "solarsystemlistcomponent.h" +#include +#include + class FileDownloader; -/** @class AsteroidsComponent +/** + * @class AsteroidsComponent * Represents the asteroids on the sky map. * * @author Thomas Kabelmann * @version 0.1 */ class AsteroidsComponent : public QObject, public SolarSystemListComponent, virtual public BinaryListComponent { Q_OBJECT friend class BinaryListComponent; public: - /** @short Default constructor. - * @p parent pointer to the parent SolarSystemComposite - */ + /** + * @short Default constructor. + * + * @p parent pointer to the parent SolarSystemComposite + */ explicit AsteroidsComponent(SolarSystemComposite *parent); + virtual ~AsteroidsComponent() override = default; - ~AsteroidsComponent() override; void draw(SkyPainter *skyp) override; bool selected() override; SkyObject *objectNearest(SkyPoint *p, double &maxrad) override; void updateDataFile() override; QString ans(); protected slots: void downloadReady(); void downloadError(const QString &errorString); private: void loadDataFromText() override; - FileDownloader *downloadJob; -}; -#endif + FileDownloader *downloadJob { nullptr }; +}; diff --git a/kstars/skycomponents/cometscomponent.cpp b/kstars/skycomponents/cometscomponent.cpp index 91d395a91..b37d052b2 100644 --- a/kstars/skycomponents/cometscomponent.cpp +++ b/kstars/skycomponents/cometscomponent.cpp @@ -1,265 +1,261 @@ /*************************************************************************** cometscomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/24/09 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 -#include -#include -#include -#include -#include - #include "cometscomponent.h" -#include "solarsystemcomposite.h" -#include "Options.h" -#include "skyobjects/kscomet.h" -#include "ksutils.h" #ifndef KSTARS_LITE #include "kstars.h" #endif -#include "kstarsdata.h" #include "ksfilereader.h" -#include "auxiliary/kspaths.h" +#include "kspaths.h" +#include "kstarsdata.h" +#include "ksutils.h" #ifndef KSTARS_LITE #include "skymap.h" #else #include "kstarslite.h" #endif +#include "Options.h" #include "skylabeler.h" #include "skypainter.h" -#include "projections/projector.h" +#include "solarsystemcomposite.h" #include "auxiliary/filedownloader.h" -#include "kspaths.h" -#include "ksutils.h" +#include "auxiliary/kspaths.h" +#include "projections/projector.h" +#include "skyobjects/kscomet.h" + +#include +#include +#include +#include +#include + +#include CometsComponent::CometsComponent(SolarSystemComposite *parent) : SolarSystemListComponent(parent) { loadData(); } -CometsComponent::~CometsComponent() -{ -} - bool CometsComponent::selected() { return Options::showComets(); } /* * @short Initialize the comets list. * Reads in the comets data from the comets.dat file. * * Populate the list of Comets from the data file. * The data file is a CSV file with the following columns : * @li 1 full name [string] * @li 2 modified julian day of orbital elements [int] * @li 3 perihelion distance in AU [double] * @li 4 eccentricity of orbit [double] * @li 5 inclination angle of orbit in degrees [double] * @li 6 argument of perihelion in degrees [double] * @li 7 longitude of the ascending node in degrees [double] * @li 8 time of perihelion passage (YYYYMMDD.DDD) [double] * @li 9 orbit solution ID [string] * @li 10 Near-Earth Object (NEO) flag [bool] * @li 11 comet total magnitude parameter [float] * @li 12 comet nuclear magnitude parameter [float] * @li 13 object diameter (from equivalent sphere) [float] * @li 14 object bi/tri-axial ellipsoid dimensions [string] * @li 15 geometric albedo [float] * @li 16 rotation period [float] * @li 17 orbital period [float] * @li 18 earth minimum orbit intersection distance [double] * @li 19 orbit classification [string] * @li 20 comet total magnitude slope parameter * @li 21 comet nuclear magnitude slope parameter * @note See KSComet constructor for more details. */ void CometsComponent::loadData() { QString name, orbit_id, orbit_class, dimensions; bool neo; double q, e, dble_i, dble_w, dble_N, Tp, earth_moid; float M1, M2, K1, K2, diameter, albedo, rot_period, period; emitProgressText(i18n("Loading comets")); qDeleteAll(m_ObjectList); m_ObjectList.clear(); objectNames(SkyObject::COMET).clear(); objectLists(SkyObject::COMET).clear(); QList> sequence; sequence.append(qMakePair(QString("full name"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("epoch_mjd"), KSParser::D_INT)); sequence.append(qMakePair(QString("q"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("e"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("i"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("w"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("om"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("tp_calc"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("orbit_id"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("neo"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("M1"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("M2"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("diameter"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("extent"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("albedo"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("rot_period"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("per_y"), KSParser::D_FLOAT)); sequence.append(qMakePair(QString("moid"), KSParser::D_DOUBLE)); sequence.append(qMakePair(QString("class"), KSParser::D_QSTRING)); sequence.append(qMakePair(QString("H"), KSParser::D_SKIP)); sequence.append(qMakePair(QString("G"), KSParser::D_SKIP)); QString file_name = KSPaths::locate(QStandardPaths::GenericDataLocation, QString("comets.dat")); KSParser cometParser(file_name, '#', sequence); QHash row_content; while (cometParser.HasNextRow()) { KSComet *com = nullptr; row_content = cometParser.ReadNextRow(); name = row_content["full name"].toString(); name = name.trimmed(); q = row_content["q"].toDouble(); e = row_content["e"].toDouble(); dble_i = row_content["i"].toDouble(); dble_w = row_content["w"].toDouble(); dble_N = row_content["om"].toDouble(); Tp = row_content["tp_calc"].toDouble(); orbit_id = row_content["orbit_id"].toString(); neo = row_content["neo"] == "Y"; if (row_content["M1"].toFloat() == 0.0) M1 = 101.0; else M1 = row_content["M1"].toFloat(); if (row_content["M2"].toFloat() == 0.0) M2 = 101.0; else M2 = row_content["M2"].toFloat(); diameter = row_content["diameter"].toFloat(); dimensions = row_content["extent"].toString(); albedo = row_content["albedo"].toFloat(); rot_period = row_content["rot_period"].toFloat(); period = row_content["per_y"].toFloat(); earth_moid = row_content["moid"].toDouble(); orbit_class = row_content["class"].toString(); K1 = row_content["H"].toFloat(); K2 = row_content["G"].toFloat(); com = new KSComet(name, QString(), q, e, dms(dble_i), dms(dble_w), dms(dble_N), Tp, M1, M2, K1, K2); com->setOrbitID(orbit_id); com->setNEO(neo); com->setDiameter(diameter); com->setDimensions(dimensions); com->setAlbedo(albedo); com->setRotationPeriod(rot_period); com->setPeriod(period); com->setEarthMOID(earth_moid); com->setOrbitClass(orbit_class); com->setAngularSize(0.005); appendListObject(com); // Add *short* name to the list of object names objectNames(SkyObject::COMET).append(com->name()); objectLists(SkyObject::COMET).append(QPair(com->name(), com)); } } void CometsComponent::draw(SkyPainter *skyp) { Q_UNUSED(skyp) #ifndef KSTARS_LITE if (!selected() || Options::zoomFactor() < 10 * MINZOOM) return; bool hideLabels = !Options::showCometNames() || (SkyMap::Instance()->isSlewing() && Options::hideLabels()); double rsunLabelLimit = Options::maxRadCometName(); //FIXME: Should these be config'able? skyp->setPen(QPen(QColor("transparent"))); skyp->setBrush(QBrush(QColor("white"))); foreach (SkyObject *so, m_ObjectList) { KSComet *com = (KSComet *)so; double mag = com->mag(); if (std::isnan(mag) == 0) { bool drawn = skyp->drawComet(com); if (drawn && !(hideLabels || com->rsun() >= rsunLabelLimit)) SkyLabeler::AddLabel(com, SkyLabeler::COMET_LABEL); } } #endif } void CometsComponent::updateDataFile() { downloadJob = new FileDownloader(); downloadJob->setProgressDialogEnabled(true, i18n("Comets Update"), i18n("Downloading comets updates...")); downloadJob->registerDataVerification([&](const QByteArray &data) { return data.startsWith("full_name");}); connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); QUrl url = QUrl("https://ssd.jpl.nasa.gov/sbdb_query.cgi"); QByteArray post_data = KSUtils::getJPLQueryString("com", "AcBdBiBgBjBlBkBqBbAgAkAlApAqArAsBsBtChAmAn", QVector{ { "Af", "!=", "D" } }); downloadJob->post(url, post_data); } void CometsComponent::downloadReady() { // Comment the first line QByteArray data = downloadJob->downloadedData(); data.insert(0, '#'); // Write data to asteroids.dat QFile file(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "comets.dat"); file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); file.write(data); file.close(); // Reload asteroids loadData(); #ifdef KSTARS_LITE KStarsLite::Instance()->data()->setFullTimeUpdate(); #else KStars::Instance()->data()->setFullTimeUpdate(); #endif downloadJob->deleteLater(); } void CometsComponent::downloadError(const QString &errorString) { #ifndef KSTARS_LITE KMessageBox::error(nullptr, i18n("Error downloading asteroids data: %1", errorString)); #else qDebug() << i18n("Error downloading comets data: %1", errorString); #endif downloadJob->deleteLater(); } diff --git a/kstars/skycomponents/cometscomponent.h b/kstars/skycomponents/cometscomponent.h index 931b83810..3dbbea5d6 100644 --- a/kstars/skycomponents/cometscomponent.h +++ b/kstars/skycomponents/cometscomponent.h @@ -1,59 +1,62 @@ /*************************************************************************** cometscomponent.h - K Desktop Planetarium ------------------- begin : 2005/24/09 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 COMETSCOMPONENT_H -#define COMETSCOMPONENT_H +#pragma once -class SkyLabeler; - -#include "solarsystemlistcomponent.h" #include "ksparser.h" +#include "solarsystemlistcomponent.h" + #include class FileDownloader; +class SkyLabeler; -/** @class CometsComponent +/** + * @class CometsComponent + * * This class encapsulates the Comets * * @author Jason Harris * @version 0.1 */ class CometsComponent : public QObject, public SolarSystemListComponent { Q_OBJECT public: - /** @short Default constructor. - * @p parent pointer to the parent SolarSystemComposite - */ + /** + * @short Default constructor. + * + * @p parent pointer to the parent SolarSystemComposite + */ explicit CometsComponent(SolarSystemComposite *parent); - ~CometsComponent() override; + virtual ~CometsComponent() override = default; + bool selected() override; void draw(SkyPainter *skyp) override; void updateDataFile(); protected slots: void downloadReady(); void downloadError(const QString &errorString); private: void loadData(); - FileDownloader *downloadJob; -}; -#endif + FileDownloader *downloadJob { nullptr }; +}; diff --git a/kstars/skycomponents/constellationboundarylines.cpp b/kstars/skycomponents/constellationboundarylines.cpp index 214262615..e2f14bda3 100644 --- a/kstars/skycomponents/constellationboundarylines.cpp +++ b/kstars/skycomponents/constellationboundarylines.cpp @@ -1,291 +1,287 @@ /*************************************************************************** constellationboundarylines.cpp - K Desktop Planetarium ------------------- begin : 25 Oct. 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "constellationboundarylines.h" #include "ksfilereader.h" #include "kstarsdata.h" #include "linelist.h" #include "Options.h" #include "polylist.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "skymap.h" #endif #include "skypainter.h" #include "htmesh/MeshIterator.h" #include "skycomponents/skymapcomposite.h" #include ConstellationBoundaryLines::ConstellationBoundaryLines(SkyComposite *parent) : NoPrecessIndex(parent, i18n("Constellation Boundaries")) { m_skyMesh = SkyMesh::Instance(); m_polyIndexCnt = 0; for (int i = 0; i < m_skyMesh->size(); i++) { m_polyIndex.append(std::shared_ptr(new PolyListList())); } KStarsData *data = KStarsData::Instance(); int verbose = 0; // -1 => create cbounds-$x.idx on stdout // 0 => normal const char *fname = "cbounds.dat"; int flag = 0; double ra, dec = 0, lastRa, lastDec; std::shared_ptr lineList; std::shared_ptr polyList; bool ok = false; intro(); // Open the .idx file and skip past the first line KSFileReader idxReader, *idxFile = nullptr; QString idxFname = QString("cbounds-%1.idx").arg(SkyMesh::Instance()->level()); if (idxReader.open(idxFname)) { idxReader.readLine(); idxFile = &idxReader; } // now open the file that contains the points KSFileReader fileReader; if (!fileReader.open(fname)) return; fileReader.setProgress(i18n("Loading Constellation Boundaries"), 13124, 10); lastRa = lastDec = -1000.0; while (fileReader.hasMoreLines()) { QString line = fileReader.readLine(); fileReader.showProgress(); if (line.at(0) == '#') continue; // ignore comments if (line.at(0) == ':') // :constellation line { if (lineList.get()) appendLine(lineList); lineList.reset(); if (polyList.get()) appendPoly(polyList, idxFile, verbose); QString cName = line.mid(1); polyList.reset(new PolyList(cName)); if (verbose == -1) printf(":\n"); lastRa = lastDec = -1000.0; continue; } // read in the data from the line ra = line.midRef(0, 12).toDouble(&ok); if (ok) dec = line.midRef(13, 12).toDouble(&ok); if (ok) flag = line.midRef(26, 1).toInt(&ok); if (!ok) { fprintf(stderr, "%s: conversion error on line: %d\n", fname, fileReader.lineNumber()); continue; } if (ra == lastRa && dec == lastDec) { fprintf(stderr, "%s: tossing dupe on line %4d: (%f, %f)\n", fname, fileReader.lineNumber(), ra, dec); continue; } // always add the point to the boundary (and toss dupes) // By the time we come here, we should have polyList. Else we aren't doing good Q_ASSERT(polyList); // Is this the right fix? polyList->append(QPointF(ra, dec)); if (ra < 0) polyList->setWrapRA(true); if (flag) { if (!lineList.get()) lineList.reset(new LineList()); std::shared_ptr point(new SkyPoint(ra, dec)); point->EquatorialToHorizontal(data->lst(), data->geo()->lat()); lineList->append(std::move(point)); lastRa = ra; lastDec = dec; } else { if (lineList.get()) appendLine(lineList); lineList.reset(); lastRa = lastDec = -1000.0; } } if (lineList.get()) appendLine(lineList); if (polyList.get()) appendPoly(polyList, idxFile, verbose); } -ConstellationBoundaryLines::~ConstellationBoundaryLines() -{ -} - bool ConstellationBoundaryLines::selected() { #ifndef KSTARS_LITE return Options::showCBounds() && !(Options::hideOnSlew() && Options::hideCBounds() && SkyMap::IsSlewing()); #else return Options::showCBounds() && !(Options::hideOnSlew() && Options::hideCBounds() && SkyMapLite::IsSlewing()); #endif } void ConstellationBoundaryLines::preDraw(SkyPainter *skyp) { QColor color = KStarsData::Instance()->colorScheme()->colorNamed("CBoundColor"); skyp->setPen(QPen(QBrush(color), 1, Qt::SolidLine)); } void ConstellationBoundaryLines::appendPoly(std::shared_ptr &polyList, KSFileReader *file, int debug) { if (!file || debug == -1) return appendPoly(polyList, debug); while (file->hasMoreLines()) { QString line = file->readLine(); if (line.at(0) == ':') return; Trixel trixel = line.toInt(); m_polyIndex[trixel]->append(polyList); } } void ConstellationBoundaryLines::appendPoly(const std::shared_ptr &polyList, int debug) { if (debug >= 0 && debug < m_skyMesh->debug()) debug = m_skyMesh->debug(); const IndexHash &indexHash = m_skyMesh->indexPoly(polyList->poly()); IndexHash::const_iterator iter = indexHash.constBegin(); while (iter != indexHash.constEnd()) { Trixel trixel = iter.key(); iter++; if (debug == -1) printf("%d\n", trixel); m_polyIndex[trixel]->append(polyList); } if (debug > 9) printf("PolyList: %3d: %d\n", ++m_polyIndexCnt, indexHash.size()); } PolyList *ConstellationBoundaryLines::ContainingPoly(SkyPoint *p) { //printf("called ContainingPoly(p)\n"); // we save the pointers in a hash because most often there is only one // constellation and we can avoid doing the expensive boundary calculations // and just return it if we know it is unique. We can avoid this minor // complication entirely if we use index(p) instead of aperture(p, r) // because index(p) always returns a single trixel index. QHash polyHash; QHash::const_iterator iter; //printf("\n"); // the boundaries don't precess so we use index() not aperture() m_skyMesh->index(p, 1.0, IN_CONSTELL_BUF); MeshIterator region(m_skyMesh, IN_CONSTELL_BUF); while (region.hasNext()) { Trixel trixel = region.next(); //printf("Trixel: %4d %s\n", trixel, m_skyMesh->indexToName( trixel ) ); std::shared_ptr polyListList = m_polyIndex[trixel]; //printf(" size: %d\n", polyListList->size() ); for (int i = 0; i < polyListList->size(); i++) { polyHash.insert(polyListList->at(i).get(), true); } } iter = polyHash.constBegin(); // Don't bother with boundaries if there is only one if (polyHash.size() == 1) return iter.key(); QPointF point(p->ra().Hours(), p->dec().Degrees()); QPointF wrapPoint(p->ra().Hours() - 24.0, p->dec().Degrees()); bool wrapRA = p->ra().Hours() > 12.0; while (iter != polyHash.constEnd()) { PolyList *polyList = iter.key(); iter++; //qDebug() << QString("checking %1 boundary\n").arg( polyList->name() ); const QPolygonF *poly = polyList->poly(); if (wrapRA && polyList->wrapRA()) { if (poly->containsPoint(wrapPoint, Qt::OddEvenFill)) return polyList; } else { if (poly->containsPoint(point, Qt::OddEvenFill)) return polyList; } } return nullptr; } //------------------------------------------------------------------- // The routines for providing public access to the boundary index // start here. (Some of them may not be needed (or working)). //------------------------------------------------------------------- QString ConstellationBoundaryLines::constellationName(SkyPoint *p) { PolyList *polyList = ContainingPoly(p); if (polyList) { return (Options::useLocalConstellNames() ? i18nc("Constellation name (optional)", polyList->name().toUpper().toLocal8Bit().data()) : polyList->name()); } return i18n("Unknown"); } diff --git a/kstars/skycomponents/constellationboundarylines.h b/kstars/skycomponents/constellationboundarylines.h index 881672e86..2170e9513 100644 --- a/kstars/skycomponents/constellationboundarylines.h +++ b/kstars/skycomponents/constellationboundarylines.h @@ -1,76 +1,76 @@ /*************************************************************************** constellationboundary.h - K Desktop Planetarium ------------------- begin : 25 Oct. 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "noprecessindex.h" #include #include class PolyList; class ConstellationBoundary; class KSFileReader; typedef QVector> PolyListList; typedef QVector> PolyIndex; /** * @class ConstellationBoundary * Collection of lines comprising the borders between constellations * * @author Jason Harris * @version 0.1 */ class ConstellationBoundaryLines : public NoPrecessIndex { public: /** * @short Constructor * Simply adds all of the coordinate grid circles (meridians and parallels) * @p parent Pointer to the parent SkyComposite object * * Reads the constellation boundary data from cbounds.dat. The boundary data is defined by * a series of RA,Dec coordinate pairs defining the "nodes" of the boundaries. The nodes are * organized into "segments", such that each segment represents a continuous series * of boundary-line intervals that divide two particular constellations. */ explicit ConstellationBoundaryLines(SkyComposite *parent); - ~ConstellationBoundaryLines() override; + virtual ~ConstellationBoundaryLines() override = default; QString constellationName(SkyPoint *p); bool selected() override; void preDraw(SkyPainter *skyp) override; private: void appendPoly(const std::shared_ptr &polyList, int debug = 0); /** * @short reads the indices from the KSFileReader instead of using * the SkyMesh to create them. If the file pointer is null or if * debug == -1 then we fall back to using the index. */ void appendPoly(std::shared_ptr &polyList, KSFileReader *file, int debug); PolyList *ContainingPoly(SkyPoint *p); SkyMesh *m_skyMesh { nullptr }; PolyIndex m_polyIndex; int m_polyIndexCnt { 0 }; }; diff --git a/kstars/skycomponents/constellationnamescomponent.cpp b/kstars/skycomponents/constellationnamescomponent.cpp index 9d9589923..92b8a0746 100644 --- a/kstars/skycomponents/constellationnamescomponent.cpp +++ b/kstars/skycomponents/constellationnamescomponent.cpp @@ -1,163 +1,159 @@ /*************************************************************************** constellationnamescomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/10/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "constellationnamescomponent.h" #include "ksfilereader.h" #include "kstarsdata.h" #include "Options.h" #include "skylabeler.h" #ifndef KSTARS_LITE #include "skymap.h" #endif #include "projections/projector.h" #include "skycomponents/culturelist.h" #include ConstellationNamesComponent::ConstellationNamesComponent(SkyComposite *parent, CultureList *cultures) : ListComponent(parent) { QtConcurrent::run(this, &ConstellationNamesComponent::loadData, cultures); } -ConstellationNamesComponent::~ConstellationNamesComponent() -{ -} - void ConstellationNamesComponent::loadData(CultureList *cultures) { uint i = 0; bool culture = false; KSFileReader fileReader; QString cultureName; if (!fileReader.open("cnames.dat")) return; emitProgressText(i18n("Loading constellation names")); localCNames = Options::useLocalConstellNames(); while (fileReader.hasMoreLines()) { QString line, name, abbrev; int rah, ram, ras, dd, dm, ds; QChar sgn, mode; line = fileReader.readLine(); mode = line.at(0); if (mode == 'C') { cultureName = line.mid(2).trimmed(); culture = cultureName == cultures->current(); i++; continue; } if (culture) { rah = line.midRef(0, 2).toInt(); ram = line.midRef(2, 2).toInt(); ras = line.midRef(4, 2).toInt(); sgn = line.at(6); dd = line.midRef(7, 2).toInt(); dm = line.midRef(9, 2).toInt(); ds = line.midRef(11, 2).toInt(); abbrev = line.mid(13, 3); name = line.mid(17).trimmed(); if (Options::useLocalConstellNames()) name = i18nc("Constellation name (optional)", name.toLocal8Bit().data()); dms r; r.setH(rah, ram, ras); dms d(dd, dm, ds); if (sgn == '-') d.setD(-1.0 * d.Degrees()); SkyObject *o = new SkyObject(SkyObject::CONSTELLATION, r, d, 0.0, name, abbrev); o->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); appendListObject(o); //Add name to the list of object names objectNames(SkyObject::CONSTELLATION).append(name); objectLists(SkyObject::CONSTELLATION).append(QPair(name, o)); } } } bool ConstellationNamesComponent::selected() { #ifndef KSTARS_LITE return Options::showCNames() && !(Options::hideOnSlew() && Options::hideCNames() && SkyMap::IsSlewing()); #else return Options::showCNames() && !(Options::hideOnSlew() && Options::hideCNames() && SkyMapLite::IsSlewing()); #endif } // Don't precess the location of the names void ConstellationNamesComponent::update(KSNumbers *) { if (!selected()) return; KStarsData *data = KStarsData::Instance(); foreach (SkyObject *o, m_ObjectList) o->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } void ConstellationNamesComponent::draw(SkyPainter *skyp) { Q_UNUSED(skyp); #ifndef KSTARS_LITE if (!selected()) return; const Projector *proj = SkyMap::Instance()->projector(); SkyLabeler *skyLabeler = SkyLabeler::Instance(); //skyLabeler->useStdFont(); // Subjective change, but constellation names really need to stand out against everything else skyLabeler->setFont(QFont("Arial", 14)); skyLabeler->setPen(QColor(KStarsData::Instance()->colorScheme()->colorNamed("CNameColor"))); QString name; foreach (SkyObject *p, m_ObjectList) { if (!proj->checkVisibility(p)) continue; bool visible = false; QPointF o = proj->toScreen(p, false, &visible); if (!visible || !proj->onScreen(o)) continue; if (Options::useLatinConstellNames() || Options::useLocalConstellNames()) name = p->name(); else name = p->name2(); o.setX(o.x() - 5.0 * name.length()); skyLabeler->drawGuideLabel(o, name, 0.0); } skyLabeler->resetFont(); #endif } diff --git a/kstars/skycomponents/constellationnamescomponent.h b/kstars/skycomponents/constellationnamescomponent.h index 34acbbcf7..e6dc87869 100644 --- a/kstars/skycomponents/constellationnamescomponent.h +++ b/kstars/skycomponents/constellationnamescomponent.h @@ -1,79 +1,78 @@ /*************************************************************************** constellationnamescomponent.h - K Desktop Planetarium ------------------- begin : 2005/10/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "listcomponent.h" class CultureList; class KSNumbers; class SkyComposite; class SkyPainter; /** * @class ConstellationNamesComponent * Represents the constellation names on the sky map. * * @author Thomas Kabelmann * @version 0.1 */ class ConstellationNamesComponent : public ListComponent { public: /** * @short Constructor * @p parent Pointer to the parent SkyComposite object * * Reads the constellation names data from cnames.dat * Each line in the file is parsed according to column position: * @li 0-1 RA hours [int] * @li 2-3 RA minutes [int] * @li 4-5 RA seconds [int] * @li 6 Dec sign [char; '+' or '-'] * @li 7-8 Dec degrees [int] * @li 9-10 Dec minutes [int] * @li 11-12 Dec seconds [int] * @li 13-15 IAU Abbreviation [string] e.g., 'Ori' == Orion * @li 17- Constellation name [string] */ ConstellationNamesComponent(SkyComposite *parent, CultureList *cultures); - /** @short Destructor. Delete list members */ - ~ConstellationNamesComponent() override; + virtual ~ConstellationNamesComponent() override = default; /** * @short Draw constellation names on the sky map. * @p psky Reference to the QPainter on which to paint */ void draw(SkyPainter *skyp) override; /** * @short we need a custom routine (for now) so we don't * precess the locations of the names. */ void update(KSNumbers *num) override; /** @short Return true if we are using localized constellation names */ inline bool isLocalCNames() { return localCNames; } bool selected() override; void loadData(CultureList *cultures); private: bool localCNames { false }; }; diff --git a/kstars/skycomponents/flagcomponent.cpp b/kstars/skycomponents/flagcomponent.cpp index b9d1fc84c..515ae0094 100644 --- a/kstars/skycomponents/flagcomponent.cpp +++ b/kstars/skycomponents/flagcomponent.cpp @@ -1,398 +1,394 @@ /*************************************************************************** flagcomponent.cpp - K Desktop Planetarium ------------------- begin : Fri 16 Jan 2009 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 "flagcomponent.h" #include "ksfilereader.h" #include "kstarsdata.h" #include "Options.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "skymap.h" #endif #include "skypainter.h" #include "auxiliary/kspaths.h" #include "projections/projector.h" #include "skyobjects/skypoint.h" #include #include #include #include FlagComponent::FlagComponent(SkyComposite *parent) : PointListComponent(parent) { // Add the default flag images to available images list m_Names.append(i18n("No icon")); m_Images.append(QImage()); m_Names.append(i18n("Default")); m_Images.append(QImage(KSPaths::locate(QStandardPaths::GenericDataLocation, "defaultflag.gif"))); QDir appDir(KSPaths::writableLocation(QStandardPaths::GenericDataLocation)); appDir.setNameFilters(QStringList() << "flag*"); // Add all other images found in user appdata directory for (auto &item : appDir.entryList()) { QString path = appDir.absoluteFilePath(item); m_Images.append(QImage(path)); QString fileName = item.replace(QRegExp("\\.[^.]*$"), QString()).replace(QRegExp("^flag"), QString()).replace('_', ' '); m_Names.append(fileName); } loadFromFile(); } -FlagComponent::~FlagComponent() -{ -} - void FlagComponent::draw(SkyPainter *skyp) { // Return if flags must not be draw if (!selected()) return; // Return if no images are available if (m_Names.size() < 1) return; // Draw all flags skyp->drawFlags(); } bool FlagComponent::selected() { return Options::showFlags(); } void FlagComponent::loadFromFile() { bool imageFound = false; QList flagList = KStarsData::Instance()->userdb()->GetAllFlags(); for (int i = 0; i < flagList.size(); ++i) { QStringList flagEntry = flagList.at(i); // Read coordinates dms r(flagEntry.at(0)); dms d(flagEntry.at(1)); m_EpochCoords.append(qMakePair(r.Degrees(), d.Degrees())); std::shared_ptr flagPoint(new SkyPoint(r, d)); // Convert to JNow toJ2000(flagPoint.get(), flagEntry.at(2)); flagPoint->updateCoordsNow(KStarsData::Instance()->updateNum()); pointList().append(std::move(flagPoint)); // Read epoch m_Epoch.append(flagEntry.at(2)); // Read image name QString str = flagEntry.at(3); str = str.replace('_', ' '); for (int i = 0; i < m_Names.size(); ++i) { if (str == m_Names.at(i)) { m_FlagImages.append(i); imageFound = true; } } // If the image sprecified in db does not exist, // use the default one if (!imageFound) m_FlagImages.append(0); imageFound = false; // If there is no label, use an empty string, red color and continue. m_Labels.append(flagEntry.at(4)); // color label QRegExp rxLabelColor("^#[a-fA-F0-9]{6}$"); if (rxLabelColor.exactMatch(flagEntry.at(5))) { m_LabelColors.append(QColor(flagEntry.at(5))); } else { m_LabelColors.append(QColor("red")); } } } void FlagComponent::saveToFile() { /* TODO: This is a really bad way of storing things. Adding one flag shouldn't involve writing a new file/table every time. Needs fixing. */ KStarsData::Instance()->userdb()->DeleteAllFlags(); for (int i = 0; i < size(); ++i) { KStarsData::Instance()->userdb()->AddFlag(QString::number(epochCoords(i).first), QString::number(epochCoords(i).second), epoch(i), imageName(i).replace(' ', '_'), label(i), labelColor(i).name()); } } void FlagComponent::add(const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor) { //JM 2015-02-21: Insert original coords in list and convert skypint to JNow // JM 2017-02-07: Discard above! We add RAW epoch coordinates to list. // If not J2000, we convert to J2000 m_EpochCoords.append(qMakePair(flagPoint.ra().Degrees(), flagPoint.dec().Degrees())); std::shared_ptr newFlagPoint(new SkyPoint(flagPoint.ra(), flagPoint.dec())); toJ2000(newFlagPoint.get(), epoch); newFlagPoint->updateCoordsNow(KStarsData::Instance()->updateNum()); pointList().append(std::move(newFlagPoint)); m_Epoch.append(epoch); for (int i = 0; i < m_Names.size(); i++) { if (image == m_Names.at(i)) m_FlagImages.append(i); } m_Labels.append(label); m_LabelColors.append(labelColor); } void FlagComponent::remove(int index) { // check if flag of required index exists if (index > pointList().size() - 1) { return; } pointList().removeAt(index); m_EpochCoords.removeAt(index); m_Epoch.removeAt(index); m_FlagImages.removeAt(index); m_Labels.removeAt(index); m_LabelColors.removeAt(index); // request SkyMap update #ifndef KSTARS_LITE SkyMap::Instance()->forceUpdate(); #endif } void FlagComponent::updateFlag(int index, const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor) { if (index < 0 || index > pointList().size() - 1) return; std::shared_ptr existingFlag = pointList().at(index); existingFlag->setRA0(flagPoint.ra()); existingFlag->setDec0(flagPoint.dec()); // If epoch not J2000, to convert to J2000 toJ2000(existingFlag.get(), epoch); existingFlag->updateCoordsNow(KStarsData::Instance()->updateNum()); m_EpochCoords.replace(index, qMakePair(flagPoint.ra().Degrees(), flagPoint.dec().Degrees())); m_Epoch.replace(index, epoch); for (int i = 0; i < m_Names.size(); i++) { if (image == m_Names.at(i)) m_FlagImages.replace(index, i); } m_Labels.replace(index, label); m_LabelColors.replace(index, labelColor); } QStringList FlagComponent::getNames() { return m_Names; } int FlagComponent::size() { return pointList().size(); } QString FlagComponent::epoch(int index) { if (index > m_Epoch.size() - 1) { return QString(); } return m_Epoch.at(index); } QString FlagComponent::label(int index) { if (index > m_Labels.size() - 1) { return QString(); } return m_Labels.at(index); } QColor FlagComponent::labelColor(int index) { if (index > m_LabelColors.size() - 1) { return QColor(); } return m_LabelColors.at(index); } QImage FlagComponent::image(int index) { if (index > m_FlagImages.size() - 1) { return QImage(); } if (m_FlagImages.at(index) > m_Images.size() - 1) { return QImage(); } return m_Images.at(m_FlagImages.at(index)); } QString FlagComponent::imageName(int index) { if (index > m_FlagImages.size() - 1) { return QString(); } if (m_FlagImages.at(index) > m_Names.size() - 1) { return QString(); } return m_Names.at(m_FlagImages.at(index)); } QList FlagComponent::imageList() { return m_Images; } QList FlagComponent::getFlagsNearPix(SkyPoint *point, int pixelRadius) { #ifdef KSTARS_LITE const Projector *proj = SkyMapLite::Instance()->projector(); #else const Projector *proj = SkyMap::Instance()->projector(); #endif QPointF pos = proj->toScreen(point); QList retVal; int ptr = 0; for (auto &cp : pointList()) { if (std::isnan(cp->ra().Degrees()) || std::isnan(cp->dec().Degrees())) continue; cp->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF pos2 = proj->toScreen(cp.get()); int dx = (pos2 - pos).x(); int dy = (pos2 - pos).y(); if (qSqrt(dx * dx + dy * dy) <= pixelRadius) { //point is inside pixelRadius circle retVal.append(ptr); } ptr++; } return retVal; } QImage FlagComponent::imageList(int index) { if (index < 0 || index > m_Images.size() - 1) { return QImage(); } return m_Images.at(index); } void FlagComponent::toJ2000(SkyPoint *p, QString epoch) { KStarsDateTime dt; dt.setFromEpoch(epoch); if (dt.djd() == J2000) return; p->apparentCoord(dt.djd(), J2000); // Store J2000 coords in RA0, DEC0 p->setRA0(p->ra()); p->setDec0(p->dec()); } QPair FlagComponent::epochCoords(int index) { if (index > m_FlagImages.size() - 1) { QPair coord = qMakePair(0, 0); return coord; } return m_EpochCoords.at(index); } void FlagComponent::update(KSNumbers *num) { if (!selected()) return; KStarsData *data = KStarsData::Instance(); for (auto &p : pointList()) { if (num) p->updateCoordsNow(num); p->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } } diff --git a/kstars/skycomponents/flagcomponent.h b/kstars/skycomponents/flagcomponent.h index 79104c583..650afaffd 100644 --- a/kstars/skycomponents/flagcomponent.h +++ b/kstars/skycomponents/flagcomponent.h @@ -1,185 +1,184 @@ /*************************************************************************** flagcomponent.h - K Desktop Planetarium ------------------- begin : Fri 16 Jan 2009 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "pointlistcomponent.h" #include #include #include #include class SkyPainter; /** * @class FlagComponent * @short Represents a flag on the sky map. * Each flag is composed by a SkyPoint where coordinates are stored, an * epoch and a label. This class also stores flag images and associates * each flag with an image. * When FlagComponent is created, it seeks all file names beginning with * "_flag" in the user directory and *considere them as flag images. * * The file flags.dat stores coordinates, epoch, image name and label of each * flags and is read to init FlagComponent * * @author Jerome SONRIER * @version 1.1 */ class FlagComponent : public QObject, public PointListComponent { Q_OBJECT public: /** @short Constructor. */ explicit FlagComponent(SkyComposite *); - /** @short Destructor. */ - ~FlagComponent() override; + virtual ~FlagComponent() override = default; void draw(SkyPainter *skyp) override; bool selected() override; void update(KSNumbers *num = nullptr) override; /** * @short Add a flag. * @param SkyPoint Sky point in epoch coordinates * @param epoch Moment for which celestial coordinates are specified * @param image Image name * @param label Label of the flag */ void add(const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor); /** * @short Remove a flag. * @param index Index of the flag to be remove. */ void remove(int index); /** * @short Update a flag. * @param index index of the flag to be updated. * @param epoch new flag epoch. * @param image new flag image. * @param label new flag label. * @param labelColor new flag label color. */ void updateFlag(int index, const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor); /** * @short Return image names. * @return the list of all image names */ QStringList getNames(); /** * @short Return the numbers of flags. * @return the size of m_PointList */ int size(); /** * @short Get epoch. * @return the epoch as a string * @param index Index of the flag */ QString epoch(int index); /** * @short Get label. * @return the label as a string * @param index Index of the flag */ QString label(int index); /** * @short Get label color. * @return the label color * @param index Index of the flag */ QColor labelColor(int index); /** * @short Get image. * @return the image associated with the flag * @param index Index of the flag */ QImage image(int index); /** * @short Get image name. * @return the name of the image associated with the flag * @param index Index of the flag */ QString imageName(int index); /** * @short Get images. * @return all images that can be use */ QList imageList(); /** * @short Get image. * @param index Index of the image in m_Images * @return an image from m_Images */ QImage imageList(int index); /** * @brief epochCoords return coordinates recorded in original epoch * @param index index of the flag * @return pair of RA/DEC in original epoch */ QPair epochCoords(int index); /** * @short Get list of flag indexes near specified SkyPoint with radius specified in pixels. * @param point central SkyPoint. * @param pixelRadius radius in pixels. */ QList getFlagsNearPix(SkyPoint *point, int pixelRadius); /** @short Load flags from flags.dat file. */ void loadFromFile(); /** @short Save flags to flags.dat file. */ void saveToFile(); private: // Convert from given epoch to J2000. If epoch is already J2000, do nothing void toJ2000(SkyPoint *p, QString epoch); /// List of epochs QStringList m_Epoch; /// RA/DEC stored in original epoch QList> m_EpochCoords; /// List of image index QList m_FlagImages; /// List of label QStringList m_Labels; /// List of label colors QList m_LabelColors; /// List of image names QStringList m_Names; /// List of flag images QList m_Images; }; diff --git a/kstars/skycomponents/hipscomponent.cpp b/kstars/skycomponents/hipscomponent.cpp index b892163fa..77ef45ff2 100644 --- a/kstars/skycomponents/hipscomponent.cpp +++ b/kstars/skycomponents/hipscomponent.cpp @@ -1,43 +1,37 @@ /* HiPS : Hierarchical Progressive Surveys HiPS is the hierarchical tiling mechanism which allows one to access, visualize and browse seamlessly image, catalogue and cube data. The KStars HiPS compoenent is used to load and overlay progress surverys from various online catalogs. Copyright (C) 2017 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "hipscomponent.h" +#include "Options.h" #include "skypainter.h" #include "skymap.h" -#include "Options.h" - HIPSComponent::HIPSComponent(SkyComposite *parent) : SkyComponent(parent) { } -HIPSComponent::~HIPSComponent() -{ -} - bool HIPSComponent::selected() { return Options::showHIPS(); } void HIPSComponent::draw(SkyPainter *skyp) { #if !defined(KSTARS_LITE) if (SkyMap::IsSlewing() == false && selected()) skyp->drawHips(); #else Q_UNUSED(skyp); - #endif } diff --git a/kstars/skycomponents/hipscomponent.h b/kstars/skycomponents/hipscomponent.h index 4b9166272..97f541810 100644 --- a/kstars/skycomponents/hipscomponent.h +++ b/kstars/skycomponents/hipscomponent.h @@ -1,36 +1,35 @@ /* HiPS : Hierarchical Progressive Surveys HiPS is the hierarchical tiling mechanism which allows one to access, visualize and browse seamlessly image, catalogue and cube data. The KStars HiPS compoenent is used to load and overlay progress surverys from various online catalogs. Copyright (C) 2017 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "hipscomponent.h" #include "skycomponent.h" /** * @class HIPSComponent * Represents the HIPS progress survey overlay * @author Jasem Mutlaq * @version 1.0 */ class HIPSComponent : public SkyComponent { public: /** Constructor */ explicit HIPSComponent(SkyComposite *); - /** Destructor */ - ~HIPSComponent() override; + virtual ~HIPSComponent() override = default; bool selected() override; void draw(SkyPainter *skyp) override; }; diff --git a/kstars/skycomponents/horizoncomponent.cpp b/kstars/skycomponents/horizoncomponent.cpp index a7bdafffc..65bc3edd3 100644 --- a/kstars/skycomponents/horizoncomponent.cpp +++ b/kstars/skycomponents/horizoncomponent.cpp @@ -1,152 +1,148 @@ /*************************************************************************** horizoncomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "horizoncomponent.h" #include "dms.h" #include "kstarsdata.h" #include "Options.h" #include "skylabeler.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "skymap.h" #endif #include "skypainter.h" #include "projections/projector.h" #include #define NCIRCLE 360 //number of points used to define equator, ecliptic and horizon HorizonComponent::HorizonComponent(SkyComposite *parent) : PointListComponent(parent) { KStarsData *data = KStarsData::Instance(); emitProgressText(i18n("Creating horizon")); //Define Horizon for (unsigned int i = 0; i < NCIRCLE; ++i) { std::shared_ptr o(new SkyPoint()); o->setAz(i * 360. / NCIRCLE); o->setAlt(0.0); o->HorizontalToEquatorial(data->lst(), data->geo()->lat()); pointList().append(o); } } -HorizonComponent::~HorizonComponent() -{ -} - bool HorizonComponent::selected() { return Options::showHorizon() || Options::showGround(); } void HorizonComponent::update(KSNumbers *) { if (!selected()) return; KStarsData *data = KStarsData::Instance(); for (auto &p : pointList()) { p->HorizontalToEquatorial(data->lst(), data->geo()->lat()); } } //Only half of the Horizon circle is ever valid, the invalid half is "behind" the observer. //To select the valid half, we start with the azimuth of the central focus point. //The valid horizon points have azimuth between this az +- 90 //This is true for Equatorial or Horizontal coordinates void HorizonComponent::draw(SkyPainter *skyp) { if (!selected()) return; KStarsData *data = KStarsData::Instance(); skyp->setPen(QPen(QColor(data->colorScheme()->colorNamed("HorzColor")), 2, Qt::SolidLine)); if (Options::showGround()) skyp->setBrush(QColor(data->colorScheme()->colorNamed("HorzColor"))); else skyp->setBrush(Qt::NoBrush); SkyPoint labelPoint; bool drawLabel; skyp->drawHorizon(Options::showGround(), &labelPoint, &drawLabel); if (drawLabel) { SkyPoint labelPoint2; labelPoint2.setAlt(0.0); labelPoint2.setAz(labelPoint.az().Degrees() + 1.0); labelPoint2.HorizontalToEquatorial(data->lst(), data->geo()->lat()); } drawCompassLabels(); } void HorizonComponent::drawCompassLabels() { #ifndef KSTARS_LITE SkyPoint c; QPointF cpoint; bool visible; const Projector *proj = SkyMap::Instance()->projector(); KStarsData *data = KStarsData::Instance(); SkyLabeler *skyLabeler = SkyLabeler::Instance(); // Set proper color for labels QColor color(data->colorScheme()->colorNamed("CompassColor")); skyLabeler->setPen(QPen(QBrush(color), 1, Qt::SolidLine)); double az = -0.01; static QString name[8]; name[0] = i18nc("Northeast", "NE"); name[1] = i18nc("East", "E"); name[2] = i18nc("Southeast", "SE"); name[3] = i18nc("South", "S"); name[4] = i18nc("Southwest", "SW"); name[5] = i18nc("West", "W"); name[6] = i18nc("Northwest", "NW"); name[7] = i18nc("North", "N"); for (int i = 0; i < 8; i++) { az += 45.0; c.setAz(az); c.setAlt(0.0); if (!Options::useAltAz()) { c.HorizontalToEquatorial(data->lst(), data->geo()->lat()); } cpoint = proj->toScreen(&c, false, &visible); if (visible && proj->onScreen(cpoint)) { skyLabeler->drawGuideLabel(cpoint, name[i], 0.0); } } #endif } diff --git a/kstars/skycomponents/horizoncomponent.h b/kstars/skycomponents/horizoncomponent.h index a21c03648..e84da985b 100644 --- a/kstars/skycomponents/horizoncomponent.h +++ b/kstars/skycomponents/horizoncomponent.h @@ -1,63 +1,60 @@ /*************************************************************************** horizoncomponent.h - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 HORIZONCOMPONENT_H -#define HORIZONCOMPONENT_H +#pragma once #include "pointlistcomponent.h" class SkyComposite; class SkyMap; class KSNumbers; /** - *@class HorizonComponent - *Represents the horizon on the sky map. - - *@author Thomas Kabelmann - *@version 0.1 - */ + * @class HorizonComponent + * + * Represents the horizon on the sky map. + * + * @author Thomas Kabelmann + * @version 0.1 + */ class HorizonComponent : public PointListComponent { public: /** - *@short Constructor - *@p parent Pointer to the parent SkyComposite object - */ + * @short Constructor + * + * @p parent Pointer to the parent SkyComposite object + */ explicit HorizonComponent(SkyComposite *parent); - /** - *@short Destructor - */ - ~HorizonComponent() override; + virtual ~HorizonComponent() override = default; /** - *@short Draw the Horizon on the Sky map - *@p map Pointer to the SkyMap object - *@p psky Reference to the QPainter on which to paint - */ + * @short Draw the Horizon on the Sky map + * + * @p map Pointer to the SkyMap object + * @p psky Reference to the QPainter on which to paint + */ void draw(SkyPainter *skyp) override; void update(KSNumbers *) override; bool selected() override; private: void drawCompassLabels(); }; - -#endif diff --git a/kstars/skycomponents/linelist.h b/kstars/skycomponents/linelist.h index 763f59bb8..a610557af 100644 --- a/kstars/skycomponents/linelist.h +++ b/kstars/skycomponents/linelist.h @@ -1,61 +1,61 @@ /*************************************************************************** linelist.h - K Desktop Planetarium ------------------- begin : 2007-07-06 copyright : (C) 2007 by James B. Bowlin email : bowlin@mindspring.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. * * * ***************************************************************************/ #pragma once #include "typedef.h" #include class SkyPoint; class KSNumbers; /** * @class LineList * A simple data container used by LineListIndex. It contains a list of * SkyPoints and integer drawID, updateID and updateNumID. * * @author James B. Bowlin * @version 0.2 */ class LineList { public: LineList() : drawID(0), updateID(0), updateNumID(0) {} - virtual ~LineList() { } + virtual ~LineList() = default; /** * @short return the list of points for iterating or appending (or whatever). */ SkyList *points() { return &pointList; } std::shared_ptr at(int i) { return pointList.at(i); } void append(std::shared_ptr p) { pointList.append(p); } /** * A global drawID (in SkyMesh) is updated at the start of each draw * cycle. Since an extended object is often covered by more than one * trixel, the drawID is used to make sure each object gets drawn at * most once per draw cycle. It is public because it is both set and * read by the LineListIndex class. */ DrawID drawID; UpdateID updateID; UpdateID updateNumID; private: SkyList pointList; }; diff --git a/kstars/skycomponents/linelistindex.cpp b/kstars/skycomponents/linelistindex.cpp index 75449912a..ba0a68f01 100644 --- a/kstars/skycomponents/linelistindex.cpp +++ b/kstars/skycomponents/linelistindex.cpp @@ -1,269 +1,265 @@ /*************************************************************************** linelistindex.cpp - K Desktop Planetarium ------------------- begin : 2007-07-04 copyright : (C) 2007 by James B. Bowlin email : bowlin@mindspring.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. * * * ***************************************************************************/ /**************************************************************************** * The filled polygon code in the innermost loops below in drawFilled*() below * implements the Sutherland-Hodgman's Polygon clipping algorithm. Please * don't mess with it unless you ensure that the Milky Way clipping continues * to work. The line clipping uses a similar but slightly less complicated * algorithm. * * Since the clipping code is a bit messy, there are four versions of the * inner loop for Filled/Outline * Integer/Float. This moved these two * decisions out of the inner loops to make them a bit faster and less * messy. * * -- James B. Bowlin * ****************************************************************************/ #include "linelistindex.h" #include "Options.h" #include "kstarsdata.h" #include "linelist.h" #ifndef KSTARS_LITE #include "skymap.h" #endif #include "skypainter.h" #include "htmesh/MeshIterator.h" LineListIndex::LineListIndex(SkyComposite *parent, const QString &name) : SkyComponent(parent), m_name(name) { m_skyMesh = SkyMesh::Instance(); m_lineIndex.reset(new LineListHash()); m_polyIndex.reset(new LineListHash()); } -LineListIndex::~LineListIndex() -{ -} - // This is a callback for the indexLines() function below const IndexHash &LineListIndex::getIndexHash(LineList *lineList) { return skyMesh()->indexLine(lineList->points()); } void LineListIndex::removeLine(const std::shared_ptr &lineList) { const IndexHash &indexHash = getIndexHash(lineList.get()); IndexHash::const_iterator iter = indexHash.constBegin(); while (iter != indexHash.constEnd()) { Trixel trixel = iter.key(); iter++; if (m_lineIndex->contains(trixel)) m_lineIndex->value(trixel)->removeOne(lineList); } m_listList.removeOne(lineList); } void LineListIndex::appendLine(const std::shared_ptr &lineList) { const IndexHash &indexHash = getIndexHash(lineList.get()); IndexHash::const_iterator iter = indexHash.constBegin(); while (iter != indexHash.constEnd()) { Trixel trixel = iter.key(); iter++; if (!m_lineIndex->contains(trixel)) { m_lineIndex->insert(trixel, std::shared_ptr(new LineListList())); } m_lineIndex->value(trixel)->append(lineList); } m_listList.append(lineList); } void LineListIndex::appendPoly(const std::shared_ptr &lineList) { const IndexHash &indexHash = skyMesh()->indexPoly(lineList->points()); IndexHash::const_iterator iter = indexHash.constBegin(); while (iter != indexHash.constEnd()) { Trixel trixel = iter.key(); iter++; if (!m_polyIndex->contains(trixel)) { m_polyIndex->insert(trixel, std::shared_ptr(new LineListList())); } m_polyIndex->value(trixel)->append(lineList); } } void LineListIndex::appendBoth(const std::shared_ptr &lineList) { QMutexLocker m1(&mutex); appendLine(lineList); appendPoly(lineList); } void LineListIndex::reindexLines() { LineListHash *oldIndex = m_lineIndex.release(); DrawID drawID = skyMesh()->incDrawID(); m_lineIndex.reset(new LineListHash()); for (auto &listList : *oldIndex) { for (auto &item : *listList) { if (item->drawID == drawID) continue; item->drawID = drawID; appendLine(item); } listList.reset(); } delete oldIndex; } void LineListIndex::JITupdate(LineList *lineList) { KStarsData *data = KStarsData::Instance(); lineList->updateID = data->updateID(); SkyList *points = lineList->points(); if (lineList->updateNumID != data->updateNumID()) { lineList->updateNumID = data->updateNumID(); KSNumbers *num = data->updateNum(); for (int i = 0; i < points->size(); i++) { points->at(i)->updateCoords(num); } } for (int i = 0; i < points->size(); i++) { points->at(i)->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } } // This is a callback used in draw() below void LineListIndex::preDraw(SkyPainter *skyp) { skyp->setPen(QPen(QBrush(QColor("white")), 1, Qt::SolidLine)); } void LineListIndex::draw(SkyPainter *skyp) { if (!selected()) return; preDraw(skyp); drawLines(skyp); } #ifdef KSTARS_LITE MeshIterator LineListIndex::visibleTrixels() { return MeshIterator(skyMesh(), drawBuffer()); } #endif // This is a callback used int drawLinesInt() and drawLinesFloat() SkipHashList *LineListIndex::skipList(LineList *lineList) { Q_UNUSED(lineList) return nullptr; } void LineListIndex::drawLines(SkyPainter *skyp) { DrawID drawID = skyMesh()->drawID(); UpdateID updateID = KStarsData::Instance()->updateID(); for (auto &lineListList : m_lineIndex->values()) { for (int i = 0; i < lineListList->size(); i++) { std::shared_ptr lineList = lineListList->at(i); if (lineList->drawID == drawID) continue; lineList->drawID = drawID; if (lineList->updateID != updateID) JITupdate(lineList.get()); skyp->drawSkyPolyline(lineList.get(), skipList(lineList.get()), label()); } } } void LineListIndex::drawFilled(SkyPainter *skyp) { DrawID drawID = skyMesh()->drawID(); UpdateID updateID = KStarsData::Instance()->updateID(); MeshIterator region(skyMesh(), drawBuffer()); while (region.hasNext()) { std::shared_ptr lineListList = m_polyIndex->value(region.next()); if (lineListList == nullptr) continue; for (int i = 0; i < lineListList->size(); i++) { std::shared_ptr lineList = lineListList->at(i); // draw each Linelist at most once if (lineList->drawID == drawID) continue; lineList->drawID = drawID; if (lineList->updateID != updateID) JITupdate(lineList.get()); skyp->drawSkyPolygon(lineList.get()); } } } void LineListIndex::intro() { emitProgressText(i18n("Loading %1", m_name)); if (skyMesh()->debug() >= 1) qDebug() << QString("Loading %1 ...").arg(m_name); } void LineListIndex::summary() { if (skyMesh()->debug() < 2) return; int total = skyMesh()->size(); int polySize = m_polyIndex->size(); int lineSize = m_lineIndex->size(); if (lineSize > 0) printf("%4d out of %4d trixels in line index %3d%%\n", lineSize, total, 100 * lineSize / total); if (polySize > 0) printf("%4d out of %4d trixels in poly index %3d%%\n", polySize, total, 100 * polySize / total); } diff --git a/kstars/skycomponents/linelistindex.h b/kstars/skycomponents/linelistindex.h index 8f012ba3c..fef9086d7 100644 --- a/kstars/skycomponents/linelistindex.h +++ b/kstars/skycomponents/linelistindex.h @@ -1,188 +1,187 @@ /*************************************************************************** linelistindex.h - K Desktop Planetarium ------------------- begin : 2007-07-04 copyright : (C) 2007 James B. Bowlin email : bowlin@mindspring.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. * * * ***************************************************************************/ #pragma once #include "skycomponent.h" #include "skymesh.h" #include #include #include class LineList; class LineListLabel; class SkipHashList; class SkyPainter; /** * @class LineListIndex * Contains almost all the code needed for indexing and drawing and clipping * lines and polygons. * * @author James B. Bowlin @version 0.1 */ class LineListIndex : public SkyComponent { friend class LinesItem; //Needs access to reindexLines public: /** * @short Constructor * Simply set the internal skyMesh, parent, and name. * @param parent Pointer to the parent SkyComponent object * @param mesh Pointer to the universal SkyMesh instance * @param name name of the subclass used for debugging */ explicit LineListIndex(SkyComposite *parent, const QString &name = ""); - /** @short Destructor */ - ~LineListIndex() override; + virtual ~LineListIndex() override = default; /** * @short The top level draw routine. Draws all the LineLists for any * subclass in one fell swoop which minimizes some of the loop overhead. * Overridden by MilkWay so it can decide whether to draw outlines or * filled. Therefore MilkyWay does not need to override preDraw(). The * MilkyWay draw() routine calls all of the more specific draw() * routines below. */ void draw(SkyPainter *skyp) override; #ifdef KSTARS_LITE /** * @short KStars Lite needs direct access to m_lineIndex for drawing the lines */ inline LineListHash *lineIndex() const { return m_lineIndex.get(); } inline LineListHash *polyIndex() const { return m_polyIndex.get(); } /** @short returns MeshIterator for currently visible trixels */ MeshIterator visibleTrixels(); #endif //Moved to public because KStars Lite uses it /** * @short this is called from within the draw routines when the updateID * of the lineList is stale. It is virtual because different subclasses * have different update routines. NoPrecessIndex doesn't precess in * the updates and ConstellationLines must update its points as stars, * not points. that doesn't precess the points. */ virtual void JITupdate(LineList *lineList); protected: /** * @short as the name says, recreates the lineIndex using the LineLists * in the previous index. Since we are indexing everything at J2000 * this is only used by ConstellationLines which needs to reindex * because of the proper motion of the stars. */ void reindexLines(); /** @short retrieve name of object */ QString name() const { return m_name; } /** * @short displays a message that we are loading m_name. Also prints * out the message if skyMesh debug is greater than zero. */ void intro(); /** * @short prints out some summary statistics if the skyMesh debug is * greater than 1. */ void summary(); /** @short Returns the SkyMesh object. */ SkyMesh *skyMesh() { return m_skyMesh; } /** * @short Typically called from within a subclasses constructors. * Adds the trixels covering the outline of lineList to the lineIndex. */ void appendLine(const std::shared_ptr &lineList); void removeLine(const std::shared_ptr &lineList); /** * @short Typically called from within a subclasses constructors. * Adds the trixels covering the full lineList to the polyIndex. */ void appendPoly(const std::shared_ptr &lineList); /** * @short a convenience method that adds a lineList to both the lineIndex and the polyIndex. */ void appendBoth(const std::shared_ptr &lineList); /** * @short Draws all the lines in m_listList as simple lines in float mode. */ void drawLines(SkyPainter *skyp); /** * @short Draws all the lines in m_listList as filled polygons in float * mode. */ void drawFilled(SkyPainter *skyp); /** * @short Gives the subclasses access to the top of the draw() method. * Typically used for setting the QPen, etc. in the QPainter being * passed in. Defaults to setting a thin white pen. */ virtual void preDraw(SkyPainter *skyp); /** * @short a callback overridden by NoPrecessIndex so it can use the * drawing code with the non-reverse-precessed mesh buffer. */ virtual MeshBufNum_t drawBuffer() { return DRAW_BUF; } /** * @short Returns an IndexHash from the SkyMesh that contains the set of * trixels that cover lineList. Overridden by SkipListIndex so it can * pass SkyMesh an IndexHash indicating which line segments should not * be indexed @param lineList contains the list of points to be covered. */ virtual const IndexHash &getIndexHash(LineList *lineList); /** * @short Also overridden by SkipListIndex. * Controls skipping inside of the draw() routines. The default behavior * is to simply return a null pointer. * * FIXME: I don't think that the SkipListIndex class even exists -- hdevalence */ virtual SkipHashList *skipList(LineList *lineList); virtual LineListLabel *label() { return nullptr; } inline LineListList listList() const { return m_listList; } private: QString m_name; SkyMesh *m_skyMesh { nullptr }; std::unique_ptr m_lineIndex; std::unique_ptr m_polyIndex; LineListList m_listList; QMutex mutex; }; diff --git a/kstars/skycomponents/planetmoonscomponent.cpp b/kstars/skycomponents/planetmoonscomponent.cpp index 3b6946bdd..e78e5e8d4 100644 --- a/kstars/skycomponents/planetmoonscomponent.cpp +++ b/kstars/skycomponents/planetmoonscomponent.cpp @@ -1,174 +1,170 @@ /*************************************************************************** planetmoonscomponent.cpp - K Desktop Planetarium ------------------- begin : Sat Mar 13 2009 copyright : (C) 2009 by Vipul Kumar Singh, Médéric Boquien email : vipulkrsingh@gmail.com, mboquien@free.fr ***************************************************************************/ /*************************************************************************** * * * 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 "planetmoonscomponent.h" -#include "Options.h" #include "kstarsdata.h" +#include "Options.h" #include "skylabeler.h" #include "skypainter.h" #include "solarsystemcomposite.h" #include "solarsystemsinglecomponent.h" #include "projections/projector.h" #include "skyobjects/jupitermoons.h" PlanetMoonsComponent::PlanetMoonsComponent(SkyComposite *p, SolarSystemSingleComponent *planetComponent, KSPlanetBase::Planets& _planet) : SkyComponent(p), planet(_planet), m_Planet(planetComponent) { /* if (planet == KSPlanetBase::JUPITER) pmoons = new JupiterMoons(); else pmoons = new SaturnMoons(); */ Q_ASSERT(planet == KSPlanetBase::JUPITER); // delete pmoons; // pmoons = new JupiterMoons(); // int nmoons = pmoons->nMoons(); // for (int i = 0; i < nmoons; ++i) // { // objectNames(SkyObject::MOON).append( pmoons->name(i) ); // objectLists(SkyObject::MOON).append( QPair(pmoons->name(i),pmoons->moon(i)) ); // } } -PlanetMoonsComponent::~PlanetMoonsComponent() -{ -} - bool PlanetMoonsComponent::selected() { return m_Planet->selected(); } #ifndef KSTARS_LITE void PlanetMoonsComponent::update(KSNumbers *) { KStarsData *data = KStarsData::Instance(); if (selected()) pmoons->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } #endif void PlanetMoonsComponent::updateMoons(KSNumbers *num) { //FIXME: evil cast if (selected()) pmoons->findPosition(num, m_Planet->planet(), (KSSun *)(parent()->findByName("Sun"))); } SkyObject *PlanetMoonsComponent::findByName(const QString &name) { int nmoons = pmoons->nMoons(); for (int i = 0; i < nmoons; ++i) { TrailObject *moon = pmoons->moon(i); if (QString::compare(moon->name(), name, Qt::CaseInsensitive) == 0 || QString::compare(moon->longname(), name, Qt::CaseInsensitive) == 0 || QString::compare(moon->name2(), name, Qt::CaseInsensitive) == 0) return moon; } return nullptr; } #ifdef KSTARS_LITE KSPlanetBase *PlanetMoonsComponent::getPlanet() const { return m_Planet->planet(); } #endif SkyObject *PlanetMoonsComponent::objectNearest(SkyPoint *p, double &maxrad) { SkyObject *oBest = nullptr; int nmoons = pmoons->nMoons(); if (Options::zoomFactor() < 3000) return nullptr; for (int i = 0; i < nmoons; ++i) { SkyObject *moon = pmoons->moon(i); double r = moon->angularDistanceTo(p).Degrees(); if (r < maxrad) { maxrad = r; oBest = moon; } } return oBest; } void PlanetMoonsComponent::draw(SkyPainter *skyp) { if (!(planet == KSPlanetBase::JUPITER && Options::showJupiter())) return; //In order to get the z-order right for the moons and the planet, //we need to first draw the moons that are further away than the planet, //then re-draw the planet, then draw the moons nearer than the planet. QList frontMoons; int nmoons = pmoons->nMoons(); for (int i = 0; i < nmoons; ++i) { if (pmoons->z(i) < 0.0) //Moon is nearer than the planet { frontMoons.append(pmoons->moon(i)); } else { //Draw Moons that are further than the planet skyp->drawPointSource(pmoons->moon(i), pmoons->moon(i)->mag()); } } //Now redraw the planet m_Planet->draw(skyp); //Now draw the remaining moons, as stored in frontMoons foreach (TrailObject *moon, frontMoons) { skyp->drawPointSource(moon, moon->mag()); } //Draw Moon name labels if at high zoom if (!(Options::showPlanetNames() && Options::zoomFactor() > 50. * MINZOOM)) return; for (int i = 0; i < nmoons; ++i) { /* if (planet ==KSPlanetBase::SATURN) SkyLabeler::AddLabel( pmoons->moon(i), SkyLabeler::SATURN_MOON_LABEL ); else */ SkyLabeler::AddLabel(pmoons->moon(i), SkyLabeler::JUPITER_MOON_LABEL); } } void PlanetMoonsComponent::drawTrails(SkyPainter *skyp) { if (!selected()) return; int nmoons = pmoons->nMoons(); for (int i = 0; i < nmoons; ++i) pmoons->moon(i)->drawTrail(skyp); } diff --git a/kstars/skycomponents/planetmoonscomponent.h b/kstars/skycomponents/planetmoonscomponent.h index 4e93e1a64..44610ed6c 100644 --- a/kstars/skycomponents/planetmoonscomponent.h +++ b/kstars/skycomponents/planetmoonscomponent.h @@ -1,81 +1,81 @@ /*************************************************************************** planetmoonscomponent.h - K Desktop Planetarium ------------------- begin : Sat Mar 13 2009 : by Vipul Kumar Singh, Médéric Boquien email : vipulkrsingh@gmail.com, mboquien@free.fr ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "skycomponent.h" #include "skyobjects/ksplanetbase.h" #include class KSNumbers; class PlanetMoons; class SkyComposite; class SolarSystemSingleComponent; /** * @class PlanetMoonsComponent * Represents the planetmoons on the sky map. * * @author Vipul Kumar Singh * @author Médéric boquien * @version 0.1 */ class PlanetMoonsComponent : public SkyComponent { public: /** * @short Constructor + * * @p parent pointer to the parent SkyComposite */ PlanetMoonsComponent(SkyComposite *parent, SolarSystemSingleComponent *pla, KSPlanetBase::Planets& planet); - /** @short Destructor */ - ~PlanetMoonsComponent() override; + virtual ~PlanetMoonsComponent() override = default; bool selected() override; void draw(SkyPainter *skyp) override; #ifndef KSTARS_LITE void update(KSNumbers *num) override; #endif void updateMoons(KSNumbers *num) override; SkyObject *objectNearest(SkyPoint *p, double &maxrad) override; /** * @return a pointer to a moon if its name matches the argument * * @p name the name to be matched * @return a SkyObject pointer to the moon whose name matches * the argument, or a nullptr pointer if no match was found. */ SkyObject *findByName(const QString &name) override; /** Return pointer to stored planet object. */ KSPlanetBase *getPlanet() const; /** Return pointer to stored moons object. */ inline PlanetMoons *getMoons() const { return pmoons.get(); } protected: void drawTrails(SkyPainter *skyp) override; private: KSPlanetBase::Planets planet; std::unique_ptr pmoons; SolarSystemSingleComponent *m_Planet { nullptr }; }; diff --git a/kstars/skycomponents/pointlistcomponent.cpp b/kstars/skycomponents/pointlistcomponent.cpp index bde675a81..8d2c31d65 100644 --- a/kstars/skycomponents/pointlistcomponent.cpp +++ b/kstars/skycomponents/pointlistcomponent.cpp @@ -1,45 +1,41 @@ /*************************************************************************** listcomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/10/01 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "pointlistcomponent.h" #include "kstarsdata.h" #include "skyobjects/skypoint.h" PointListComponent::PointListComponent(SkyComposite *parent) : SkyComponent(parent) { } -PointListComponent::~PointListComponent() -{ -} - void PointListComponent::update(KSNumbers *num) { if (!selected()) return; KStarsData *data = KStarsData::Instance(); for (auto &p : pointList()) { if (num) p->updateCoords(num); p->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } } diff --git a/kstars/skycomponents/pointlistcomponent.h b/kstars/skycomponents/pointlistcomponent.h index 5e36d4821..afba6a051 100644 --- a/kstars/skycomponents/pointlistcomponent.h +++ b/kstars/skycomponents/pointlistcomponent.h @@ -1,62 +1,63 @@ /*************************************************************************** pointlistcomponent.h - K Desktop Planetarium ------------------- begin : 2005/10/01 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once -#define NCIRCLE 360 //number of points used to define equator, ecliptic and horizon - #include "skycomponent.h" #include #include +#define NCIRCLE 360 //number of points used to define equator, ecliptic and horizon + class SkyPoint; /** * @class PointListComponent + * * An abstract parent class, to be inherited by SkyComponents that store a QList of SkyPoints. * * @author Jason Harris * @version 0.1 */ class PointListComponent : public SkyComponent { public: explicit PointListComponent(SkyComposite *parent); - ~PointListComponent() override; + virtual ~PointListComponent() override = default; /** * @short Update the sky positions of this component. * * This function usually just updates the Horizontal (Azimuth/Altitude) * coordinates of the objects in this component. However, the precession * and nutation must also be recomputed periodically. Requests to do * so are sent through the doPrecess parameter. * @p num Pointer to the KSNumbers object * @note By default, the num parameter is nullptr, indicating that * Precession/Nutation computation should be skipped; this computation * is only occasionally required. */ void update(KSNumbers *num = nullptr) override; QList> &pointList() { return m_PointList; } private: QList> m_PointList; }; diff --git a/kstars/skycomponents/skycomponent.cpp b/kstars/skycomponents/skycomponent.cpp index 7c65045fc..a92a6de47 100644 --- a/kstars/skycomponents/skycomponent.cpp +++ b/kstars/skycomponents/skycomponent.cpp @@ -1,104 +1,100 @@ /*************************************************************************** skycomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "skycomponent.h" #include "Options.h" #include "skycomposite.h" #include "skyobjects/skyobject.h" SkyComponent::SkyComponent(SkyComposite *parent) : m_parent(parent) { } -SkyComponent::~SkyComponent() -{ -} - //Hand the message up to SkyMapComposite void SkyComponent::emitProgressText(const QString &message) { parent()->emitProgressText(message); } SkyObject *SkyComponent::findByName(const QString &) { return nullptr; } SkyObject *SkyComponent::objectNearest(SkyPoint *, double &) { return nullptr; } void SkyComponent::drawTrails(SkyPainter *) { } void SkyComponent::objectsInArea(QList &, const SkyRegion &) { } QHash &SkyComponent::getObjectNames() { if (!parent()) { // Use a fake list if there is no parent object static QHash temp; return temp; } return parent()->objectNames(); } QHash>> &SkyComponent::getObjectLists() { if (!parent()) { // Use a fake list if there is no parent object static QHash>> temp; return temp; } return parent()->objectLists(); } void SkyComponent::removeFromNames(const SkyObject *obj) { QStringList &names = getObjectNames()[obj->type()]; int i; i = names.indexOf(obj->name()); if (i >= 0) names.removeAt(i); i = names.indexOf(obj->longname()); if (i >= 0) names.removeAt(i); } void SkyComponent::removeFromLists(const SkyObject *obj) { QVector> &names = getObjectLists()[obj->type()]; int i; i = names.indexOf(QPair(obj->name(), obj)); if (i >= 0) names.removeAt(i); i = names.indexOf(QPair(obj->longname(), obj)); if (i >= 0) names.removeAt(i); } diff --git a/kstars/skycomponents/skycomponent.h b/kstars/skycomponents/skycomponent.h index 76f9af48e..e6ad10f2c 100644 --- a/kstars/skycomponents/skycomponent.h +++ b/kstars/skycomponents/skycomponent.h @@ -1,213 +1,212 @@ /*************************************************************************** skycomponent.h - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "skypoint.h" #include "typedef.h" class QString; class SkyObject; class SkyPoint; class SkyComposite; class SkyPainter; /** * @class SkyComponent * SkyComponent represents an object on the sky map. This may be a star, a planet or an imaginary * line like the equator. * The SkyComponent uses the composite design pattern. So if a new object type is needed, it has to * be derived from this class and must be added into the hierarchy of components. * * \section Overview Overview * KStars utilizes the Composite/Component design pattern to store all elements that must be * drawn on the SkyMap. In this design pattern, elements to be drawn in the map are called * "Components", and they are hierarchically organized into groups called "Composites". * A Composite can contain other Composites, as well as Components. From an organizational * point of view, you can think of Composites and Components as being analogous to * "Directories" and "Files" in a computer filesystem. * * The advantage of this design pattern is that it minimizes code duplication and maximizes * modularity. There is a set of operations which all Components must perform periodically * (such as drawing on screen, or updating position), but each Component may perform its task * slightly differently. Thus, each Component can implement its own version of the functions, * and each Composite calls on its child Components to execute the desired function. For * example, if we wanted to draw all Components in the SkyMap, we would simly need to call * "skyComposite()->draw()". SkyComposite is the "top-level" Composite; its "draw()" function * simply calls draw() on each of its children. Its child Components will draw themselves, * while its child Composites will call "draw()" for each of *their* children. Note that * we don't need to know *any* implementation details; when we need to draw the * elements, we just tell the top-level Composite to spread the word. * * \section ClassInheritance Class Inheritance * The base class in this directory is SkyComponent; all other classes are derived from it * (directly or indirectly). Its most important derivative is SkyComposite, the base classes * for all Composites. * * From SkyComponent, we derive three important subclasses: SingleComponent, ListComponent, * and PointListComponent. SingleComponent represents a sky element consisting of a single * SkyObject, such as the Sun. ListComponent represents a list of SkyObjects, such as the * Asteroids. PointListComponent represents a list of SkyPoints (not SkyObjects), such as * the Ecliptic. Almost all other Components are derived from one of these three classes * (two Components are derived directly from SkyComponent). * * The Solar System bodies require some special treatment. For example, only solar system * bodies may have attached trails, and they move across the sky, so their positions must * be updated frequently. To handle these features, we derive SolarSystemComposite from * SkyComposite to be the container for all solar system Components. In addition, we derive * SolarSystemSingleComponent from SingleComponent, and SolarSystemListComponent from * ListComponent. These classes simply add functions to handle Trails, and to update the * positions of the bodies. Also, they contain KSPlanetBase objects, instead of SkyObjects. * * The Sun, Moon and Pluto each get a unique Component class, derived from * SolarSystemSingleComponent (this is needed because each of these bodies uses a unique * class derived from KSPlanetBase: KSSun, KSMoon, and KSPluto). The remaining major * planets are each represented with a PlanetComponent object, which is also derived from * SolarSystemSingleComponent. Finally, we have AsteroidsComponent and CometsComponent, * each of which is derived from SolarSystemListComponent. * * Next, we have the Components derived from PointListComponent. These Components represent * imaginary guide lines in the sky, such as constellation boundaries or the coordinate grid. * They are listed above, and most of them don't require further comment. The GuidesComposite * contains two sub-Composites: CoordinateGridComposite, and ConstellationLinesComposite. * Both of these contain a number of PointListComponents: CoordinateGridComposite contains one * PointsListComponent for each circle in the grid, and ConstellationLineComposite contains * one PointsListComponent for each constellation (representing the "stick figure" of the * constellation). * * Finally, we have the Components which don't inherit from either SingleComponent, * ListComponent, or PointListComponent: PlanetMoonsComponent inherits from SkyComponent * directly, because the planet moons have their own class (PlanetMoons) not derived * directly from a SkyPoint. * * \section StarComponent DeepStarComponent and StarComponent * StarComponent and DeepStarComponent manage stars in KStars. StarComponent maintains a * QVector of instances of DeepStarComponents and takes care of calling their draw routines * etc. The machinery for handling stars is documented in great detail in \ref Stars * * @author Thomas Kabelmann */ class SkyComponent { public: /** * @short Constructor * @p parent pointer to the parent SkyComposite */ explicit SkyComponent(SkyComposite *parent = nullptr); - /** @short Destructor */ - virtual ~SkyComponent(); + virtual ~SkyComponent() = default; /** * @short Draw the object on the SkyMap * @p skyp a pointer to the SkyPainter to use */ virtual void draw(SkyPainter *skyp) = 0; /** @short Draw trails for objects. */ virtual void drawTrails(SkyPainter *skyp); /** * @short Update the sky position(s) of this component. * * This function usually just updates the Horizontal (Azimuth/Altitude) * coordinates of its member object(s). However, the precession and * nutation must also be recomputed periodically. * @p num Pointer to the KSNumbers object * @sa SingleComponent::update() * @sa ListComponent::update() * @sa ConstellationBoundaryComponent::update() */ virtual void update(KSNumbers *) {} virtual void updateSolarSystemBodies(KSNumbers *) {} virtual void updateMoons(KSNumbers *) {} /** @return true if component is to be drawn on the map. */ virtual bool selected() { return true; } /** @return Parent of component. If there is no parent returns nullptr. */ SkyComposite *parent() { return m_parent; } /** * @short Search the children of this SkyComponent for * a SkyObject whose name matches the argument * @p name the name to be matched * @return a pointer to the SkyObject whose name matches * the argument, or a nullptr pointer if no match was found. * @note This function simply returns the nullptr pointer; it * is reimplemented in various sub-classes */ virtual SkyObject *findByName(const QString &name); /** * @short Searches the region(s) and appends the SkyObjects found to the list of sky objects * * Look for a SkyObject that is in one of the regions * If found, then append to the list of sky objects * @p list list of SkyObject to which matching list has to be appended to * @p region defines the regions in which the search for SkyObject should be done within * @return void * @note This function simply returns; it is * reimplemented in various sub-classes. */ virtual void objectsInArea(QList &list, const SkyRegion ®ion); /** * @short Find the SkyObject nearest the given SkyPoint * * Look for a SkyObject that is nearer to point p than maxrad. * If one is found, then maxrad is reset to the separation of the new nearest object. * @p p pointer to the SkyPoint to search around * @p maxrad reference to current search radius in degrees * @return a pointer to the nearest SkyObject * @note This function simply returns a nullptr pointer; it is * reimplemented in various sub-classes. */ virtual SkyObject *objectNearest(SkyPoint *p, double &maxrad); /** * @short Emit signal about progress. * * @sa SkyMapComposite::emitProgressText */ virtual void emitProgressText(const QString &message); inline QHash &objectNames() { return getObjectNames(); } inline QStringList &objectNames(int type) { return getObjectNames()[type]; } inline QHash>> &objectLists() { return getObjectLists(); } inline QVector> &objectLists(int type) { return getObjectLists()[type]; } protected: void removeFromNames(const SkyObject *obj); void removeFromLists(const SkyObject *obj); private: virtual QHash &getObjectNames(); virtual QHash>> &getObjectLists(); // Disallow copying and assignment SkyComponent(const SkyComponent &); SkyComponent &operator=(const SkyComponent &); /// Parent of sky component. SkyComposite *m_parent; }; diff --git a/kstars/skycomponents/skycomposite.h b/kstars/skycomponents/skycomposite.h index eab366d59..4ca54de14 100644 --- a/kstars/skycomponents/skycomposite.h +++ b/kstars/skycomponents/skycomposite.h @@ -1,112 +1,112 @@ /*************************************************************************** skycomposite.h - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once +#include "skycomponent.h" + #include #include -#include "skycomponent.h" - class KSNumbers; /** * @class SkyComposite * SkyComposite is a kind of container class for SkyComponent objects. The SkyComposite is * responsible for distributing calls to functions like draw() and update() to its children, * which can be SkyComponents or other SkyComposites with their own children. This is based * on the "composite/component" design pattern. * * Performance tuning: Sometimes it will be better to override a virtual function and do * the work in the composite instead of delegating the request to all sub components. * * @author Thomas Kabelmann * @version 0.1 */ class SkyComposite : public SkyComponent { public: /** * @short Constructor * @p parent pointer to the parent SkyComponent */ explicit SkyComposite(SkyComposite *parent = nullptr); /** *@short Destructor */ ~SkyComposite() override; /** * @short Delegate draw requests to all sub components * @p psky Reference to the QPainter on which to paint */ void draw(SkyPainter *skyp) override; /** * @short Delegate update-position requests to all sub components * * This function usually just updates the Horizontal (Azimuth/Altitude) coordinates. * However, the precession and nutation must also be recomputed periodically. Requests to * do so are sent through the doPrecess parameter. * @p num Pointer to the KSNumbers object * @sa updatePlanets() * @sa updateMoons() * @note By default, the num parameter is nullptr, indicating that Precession/Nutation * computation should be skipped; this computation is only occasionally required. */ void update(KSNumbers *num = nullptr) override; /** * @short Add a new sub component to the composite * @p comp Pointer to the SkyComponent to be added * @p priority A priority ordering for various operations on the list of all sky components * (notably objectNearest()) */ void addComponent(SkyComponent *comp, int priority = 1024); /** * @short Remove a sub component from the composite * @p comp Pointer to the SkyComponent to be removed. */ void removeComponent(SkyComponent *const comp); /** * @short Search the children of this SkyComposite for a SkyObject whose name matches * the argument. * * The objects' primary, secondary and long-form names will all be checked for a match. * @p name the name to be matched * @return a pointer to the SkyObject whose name matches * the argument, or a nullptr pointer if no match was found. */ SkyObject *findByName(const QString &name) override; /** * @short Identify the nearest SkyObject to the given SkyPoint, among the children of * this SkyComposite * @p p pointer to the SkyPoint around which to search. * @p maxrad reference to current search radius * @return a pointer to the nearest SkyObject */ SkyObject *objectNearest(SkyPoint *p, double &maxrad) override; QList components() { return m_Components.values(); } QMap &componentsWithPriorities() { return m_Components; } private: QMap m_Components; }; diff --git a/kstars/skycomponents/skymapcomposite.cpp b/kstars/skycomponents/skymapcomposite.cpp index 36ecccebc..a6c42f7ac 100644 --- a/kstars/skycomponents/skymapcomposite.cpp +++ b/kstars/skycomponents/skymapcomposite.cpp @@ -1,944 +1,940 @@ /*************************************************************************** skymapcomposite.cpp - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "skymapcomposite.h" #include "artificialhorizoncomponent.h" #include "catalogcomponent.h" #include "constellationartcomponent.h" #include "constellationboundarylines.h" #include "constellationlines.h" #include "constellationnamescomponent.h" #include "culturelist.h" #include "deepskycomponent.h" #include "deepstarcomponent.h" #include "ecliptic.h" #include "equator.h" #include "equatorialcoordinategrid.h" #include "horizoncomponent.h" #include "horizontalcoordinategrid.h" #include "localmeridiancomponent.h" #include "ksasteroid.h" #include "kscomet.h" #ifndef KSTARS_LITE #include "kstars.h" #endif #include "kstarsdata.h" #include "milkyway.h" #include "satellitescomponent.h" #include "skylabeler.h" #include "skypainter.h" #include "solarsystemcomposite.h" #include "starcomponent.h" #include "supernovaecomponent.h" #include "syncedcatalogcomponent.h" #include "targetlistcomponent.h" #include "projections/projector.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksplanet.h" #include "skyobjects/constellationsart.h" #ifndef KSTARS_LITE #include "flagcomponent.h" #include "ksutils.h" #include "observinglist.h" #include "skymap.h" #include "hipscomponent.h" #endif #include #include SkyMapComposite::SkyMapComposite(SkyComposite *parent) : SkyComposite(parent), m_reindexNum(J2000) { m_skyLabeler.reset(SkyLabeler::Instance()); m_skyMesh.reset(SkyMesh::Create(3)); // level 5 mesh = 8192 trixels m_skyMesh->debug(0); // 1 => print "indexing ..." // 2 => prints totals too // 10 => prints detailed lists // You can also set the debug level of individual // appendLine() and appendPoly() calls. //Add all components //Stars must come before constellation lines #ifdef KSTARS_LITE addComponent(m_MilkyWay = new MilkyWay(this), 50); addComponent(m_Stars = StarComponent::Create(this), 10); addComponent(m_EquatorialCoordinateGrid = new EquatorialCoordinateGrid(this)); addComponent(m_HorizontalCoordinateGrid = new HorizontalCoordinateGrid(this)); // Do add to components. addComponent(m_CBoundLines = new ConstellationBoundaryLines(this), 80); m_Cultures.reset(new CultureList()); addComponent(m_CLines = new ConstellationLines(this, m_Cultures.get()), 85); addComponent(m_CNames = new ConstellationNamesComponent(this, m_Cultures.get()), 90); addComponent(m_Equator = new Equator(this), 95); addComponent(m_Ecliptic = new Ecliptic(this), 95); addComponent(m_Horizon = new HorizonComponent(this), 100); addComponent(m_DeepSky = new DeepSkyComponent(this), 5); addComponent(m_ConstellationArt = new ConstellationArtComponent(this, m_Cultures.get()), 100); addComponent(m_ArtificialHorizon = new ArtificialHorizonComponent(this), 110); m_internetResolvedCat = "_Internet_Resolved"; m_manualAdditionsCat = "_Manual_Additions"; addComponent(m_internetResolvedComponent = new SyncedCatalogComponent(this, m_internetResolvedCat, true, 0), 6); addComponent(m_manualAdditionsComponent = new SyncedCatalogComponent(this, m_manualAdditionsCat, true, 0), 6); m_CustomCatalogs.reset(new SkyComposite(this)); QStringList allcatalogs = Options::showCatalogNames(); if (!allcatalogs.contains(m_internetResolvedCat)) { allcatalogs.append(m_internetResolvedCat); } if (!allcatalogs.contains(m_manualAdditionsCat)) { allcatalogs.append(m_manualAdditionsCat); } Options::setShowCatalogNames(allcatalogs); for (int i = 0; i < allcatalogs.size(); ++i) { if (allcatalogs.at(i) == m_internetResolvedCat || allcatalogs.at(i) == m_manualAdditionsCat) // This is a special catalog continue; m_CustomCatalogs->addComponent(new CatalogComponent(this, allcatalogs.at(i), false, i), 6); // FIXME: Should this be 6 or 5? See SkyMapComposite::reloadDeepSky() } addComponent(m_SolarSystem = new SolarSystemComposite(this), 2); //addComponent( m_ObservingList = new TargetListComponent( this , 0, QPen(), // &Options::obsListSymbol, &Options::obsListText ), 120 ); addComponent(m_StarHopRouteList = new TargetListComponent(this, 0, QPen()), 130); addComponent(m_Satellites = new SatellitesComponent(this), 7); addComponent(m_Supernovae = new SupernovaeComponent(this), 7); SkyMapLite::Instance()->loadingFinished(); #else addComponent(m_MilkyWay = new MilkyWay(this), 50); addComponent(m_Stars = StarComponent::Create(this), 10); addComponent(m_EquatorialCoordinateGrid = new EquatorialCoordinateGrid(this)); addComponent(m_HorizontalCoordinateGrid = new HorizontalCoordinateGrid(this)); addComponent(m_LocalMeridianComponent = new LocalMeridianComponent(this)); // Do add to components. addComponent(m_CBoundLines = new ConstellationBoundaryLines(this), 80); m_Cultures.reset(new CultureList()); addComponent(m_CLines = new ConstellationLines(this, m_Cultures.get()), 85); addComponent(m_CNames = new ConstellationNamesComponent(this, m_Cultures.get()), 90); addComponent(m_Equator = new Equator(this), 95); addComponent(m_Ecliptic = new Ecliptic(this), 95); addComponent(m_Horizon = new HorizonComponent(this), 100); addComponent(m_DeepSky = new DeepSkyComponent(this), 5); addComponent(m_ConstellationArt = new ConstellationArtComponent(this, m_Cultures.get()), 100); // Hips addComponent(m_HiPS = new HIPSComponent(this)); addComponent(m_ArtificialHorizon = new ArtificialHorizonComponent(this), 110); m_internetResolvedCat = "_Internet_Resolved"; m_manualAdditionsCat = "_Manual_Additions"; addComponent(m_internetResolvedComponent = new SyncedCatalogComponent(this, m_internetResolvedCat, true, 0), 6); addComponent(m_manualAdditionsComponent = new SyncedCatalogComponent(this, m_manualAdditionsCat, true, 0), 6); m_CustomCatalogs.reset(new SkyComposite(this)); QStringList allcatalogs = Options::showCatalogNames(); for (int i = 0; i < allcatalogs.size(); ++i) { if (allcatalogs.at(i) == m_internetResolvedCat || allcatalogs.at(i) == m_manualAdditionsCat) // This is a special catalog continue; m_CustomCatalogs->addComponent(new CatalogComponent(this, allcatalogs.at(i), false, i), 6); // FIXME: Should this be 6 or 5? See SkyMapComposite::reloadDeepSky() } addComponent(m_SolarSystem = new SolarSystemComposite(this), 2); addComponent(m_Flags = new FlagComponent(this), 4); addComponent(m_ObservingList = new TargetListComponent(this, nullptr, QPen(), &Options::obsListSymbol, &Options::obsListText), 120); addComponent(m_StarHopRouteList = new TargetListComponent(this, nullptr, QPen()), 130); addComponent(m_Satellites = new SatellitesComponent(this), 7); addComponent(m_Supernovae = new SupernovaeComponent(this), 7); #endif connect(this, SIGNAL(progressText(QString)), KStarsData::Instance(), SIGNAL(progressText(QString))); } -SkyMapComposite::~SkyMapComposite() -{ -} - void SkyMapComposite::update(KSNumbers *num) { //printf("updating SkyMapComposite\n"); //1. Milky Way //m_MilkyWay->update( data, num ); //2. Coordinate grid //m_EquatorialCoordinateGrid->update( num ); m_HorizontalCoordinateGrid->update(num); #ifndef KSTARS_LITE m_LocalMeridianComponent->update(num); #endif //3. Constellation boundaries //m_CBounds->update( data, num ); //4. Constellation lines //m_CLines->update( data, num ); //5. Constellation names if (m_CNames) m_CNames->update(num); //6. Equator //m_Equator->update( data, num ); //7. Ecliptic //m_Ecliptic->update( data, num ); //8. Deep sky //m_DeepSky->update( data, num ); //9. Custom catalogs m_CustomCatalogs->update(num); m_internetResolvedComponent->update(num); m_manualAdditionsComponent->update(num); //10. Stars //m_Stars->update( data, num ); //m_CLines->update( data, num ); // MUST follow stars. //12. Solar system m_SolarSystem->update(num); //13. Satellites m_Satellites->update(num); //14. Supernovae m_Supernovae->update(num); //15. Horizon m_Horizon->update(num); #ifndef KSTARS_LITE //16. Flags m_Flags->update(num); #endif } void SkyMapComposite::updateSolarSystemBodies(KSNumbers *num) { m_SolarSystem->updateSolarSystemBodies(num); } /* void SkyMapComposite::updateMoons(KSNumbers *num ) { m_SolarSystem->updateMoons( num ); } */ //Reimplement draw function so that we have control over the order of //elements, and we can add object labels // //The order in which components are drawn naturally determines the //z-ordering (the layering) of the components. Objects which //should appear "behind" others should be drawn first. void SkyMapComposite::draw(SkyPainter *skyp) { Q_UNUSED(skyp) #ifndef KSTARS_LITE SkyMap *map = SkyMap::Instance(); KStarsData *data = KStarsData::Instance(); // We delay one draw cycle before re-indexing // we MUST ensure CLines do not get re-indexed while we use DRAW_BUF // so we do it here. m_CLines->reindex(&m_reindexNum); // This queues re-indexing for the next draw cycle m_reindexNum = KSNumbers(data->updateNum()->julianDay()); // This ensures that the JIT updates are synchronized for the entire draw // cycle so the sky moves as a single sheet. May not be needed. data->syncUpdateIDs(); // prepare the aperture // FIXME_FOV: We may want to rejigger this to allow // wide-angle views --hdevalence float radius = map->projector()->fov(); if (radius > 180.0) radius = 180.0; if (m_skyMesh->inDraw()) { printf("Warning: aborting concurrent SkyMapComposite::draw()\n"); return; } m_skyMesh->inDraw(true); SkyPoint *focus = map->focus(); m_skyMesh->aperture(focus, radius + 1.0, DRAW_BUF); // divide by 2 for testing // create the no-precess aperture if needed if (Options::showEquatorialGrid() || Options::showHorizontalGrid() || Options::showCBounds() || Options::showEquator()) { m_skyMesh->index(focus, radius + 1.0, NO_PRECESS_BUF); } // clear marks from old labels and prep fonts m_skyLabeler->reset(map); m_skyLabeler->useStdFont(); // info boxes have highest label priority // FIXME: REGRESSION. Labeler now know nothing about infoboxes // map->infoBoxes()->reserveBoxes( psky ); // JM 2016-12-01: Why is this done this way?!! It's too inefficient if (KStars::Instance()) { auto &obsList = KStarsData::Instance()->observingList()->sessionList(); if (Options::obsListText()) for (auto &obj_clone : obsList) { // Find the "original" obj SkyObject *o = findByName(obj_clone->name()); // FIXME: This is sloww.... and can also fail!!! if (!o) continue; SkyLabeler::AddLabel(o, SkyLabeler::RUDE_LABEL); } } m_MilkyWay->draw(skyp); // Draw HIPS after milky way but before everything else m_HiPS->draw(skyp); m_EquatorialCoordinateGrid->draw(skyp); m_HorizontalCoordinateGrid->draw(skyp); m_LocalMeridianComponent->draw(skyp); //Draw constellation boundary lines only if we draw western constellations if (m_Cultures->current() == "Western") { m_CBoundLines->draw(skyp); m_ConstellationArt->draw(skyp); } else if (m_Cultures->current() == "Inuit") { m_ConstellationArt->draw(skyp); } m_CLines->draw(skyp); m_Equator->draw(skyp); m_Ecliptic->draw(skyp); m_DeepSky->draw(skyp); m_CustomCatalogs->draw(skyp); m_internetResolvedComponent->draw(skyp); m_manualAdditionsComponent->draw(skyp); m_Stars->draw(skyp); m_SolarSystem->drawTrails(skyp); m_SolarSystem->draw(skyp); m_Satellites->draw(skyp); m_Supernovae->draw(skyp); map->drawObjectLabels(labelObjects()); m_skyLabeler->drawQueuedLabels(); m_CNames->draw(skyp); m_Stars->drawLabels(); m_DeepSky->drawLabels(); m_ObservingList->pen = QPen(QColor(data->colorScheme()->colorNamed("ObsListColor")), 1.); if (KStars::Instance() && !m_ObservingList->list) m_ObservingList->list.reset(new SkyObjectList(KSUtils::makeVanillaPointerList( KStarsData::Instance() ->observingList() ->sessionList()))); // Make sure we never delete the pointers in m_ObservingList->list! m_ObservingList->draw(skyp); m_Flags->draw(skyp); m_StarHopRouteList->pen = QPen(QColor(data->colorScheme()->colorNamed("StarHopRouteColor")), 1.); m_StarHopRouteList->draw(skyp); m_ArtificialHorizon->draw(skyp); m_Horizon->draw(skyp); m_skyMesh->inDraw(false); // DEBUG Edit. Keywords: Trixel boundaries. Currently works only in QPainter mode // -jbb uncomment these to see trixel outlines: /* QPainter *psky = dynamic_cast< QPainter *>( skyp ); if( psky ) { qCDebug(KSTARS) << "Drawing trixel boundaries for debugging."; psky->setPen( QPen( QBrush( QColor( "yellow" ) ), 1, Qt::SolidLine ) ); m_skyMesh->draw( *psky, OBJ_NEAREST_BUF ); SkyMesh *p; if( p = SkyMesh::Instance( 6 ) ) { qCDebug(KSTARS) << "We have a deep sky mesh to draw"; p->draw( *psky, OBJ_NEAREST_BUF ); } psky->setPen( QPen( QBrush( QColor( "green" ) ), 1, Qt::SolidLine ) ); m_skyMesh->draw( *psky, NO_PRECESS_BUF ); if( p ) p->draw( *psky, NO_PRECESS_BUF ); } */ #endif } //Select nearest object to the given skypoint, but give preference //to certain object types. //we multiply each object type's smallest angular distance by the //following factors before selecting the final nearest object: // faint stars > 12th mag = 1.75 // stars > 4 and < 12 = 1.5 // stars brighter than 4th mag = 0.75 // IC catalog = 0.8 // NGC catalog = 0.6 // "other" catalog = 0.6 // Messier object = 0.5 // custom object = 0.5 // Solar system = 0.25 SkyObject *SkyMapComposite::objectNearest(SkyPoint *p, double &maxrad) { double rTry = maxrad; double rBest = maxrad; SkyObject *oTry = nullptr; SkyObject *oBest = nullptr; //printf("%.1f %.1f\n", p->ra().Degrees(), p->dec().Degrees() ); m_skyMesh->aperture(p, maxrad + 1.0, OBJ_NEAREST_BUF); oBest = m_Stars->objectNearest(p, rBest); //reduce rBest by 0.75 for stars brighter than 4th mag if (oBest && oBest->mag() < 4.0) rBest *= 0.75; // For stars fainter than 12th mag else if (oBest && oBest->mag() > 12.0) rBest *= 1.75; // For stars between 4th and 12th mag else if (oBest) rBest *= 1.5; oTry = m_Satellites->objectNearest(p, rTry); if (rTry < rBest) { rBest = rTry; oBest = oTry; } for (int i = 0; i < m_DeepStars.size(); ++i) { rTry = maxrad; oTry = m_DeepStars.at(i)->objectNearest(p, rTry); if (rTry < rBest) { rBest = rTry; oBest = oTry; } } // TODO: Add support for deep star catalogs //m_DeepSky internally discriminates among deepsky catalogs //and renormalizes rTry oTry = m_DeepSky->objectNearest(p, rTry); if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_CustomCatalogs->objectNearest(p, rTry); rTry *= 0.5; if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_internetResolvedComponent->objectNearest(p, rTry); rTry *= 0.5; if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_manualAdditionsComponent->objectNearest(p, rTry); rTry *= 0.5; if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_Supernovae->objectNearest(p, rTry); //qCDebug(KSTARS)<objectNearest(p, rTry); if (!dynamic_cast(oTry) && !dynamic_cast( oTry)) // There are gazillions of faint asteroids and comets; we want to prevent them from getting precedence { rTry *= 0.25; // this is either sun, moon, or one of the major planets or their moons. } else { if (std::isfinite(oTry->mag()) && oTry->mag() < 12.0) { rTry *= 0.75; // Bright comets / asteroids get some precedence. } } if (rTry < rBest) { rBest = rTry; oBest = oTry; } //if ( oBest && Options::verboseLogging()) //qCDebug(KSTARS) << "OBEST=" << oBest->name() << " - " << oBest->name2(); maxrad = rBest; return oBest; //will be 0 if no object nearer than maxrad was found } SkyObject *SkyMapComposite::starNearest(SkyPoint *p, double &maxrad) { double rtry = maxrad; SkyObject *star = nullptr; m_skyMesh->aperture(p, maxrad + 1.0, OBJ_NEAREST_BUF); star = m_Stars->objectNearest(p, rtry); //reduce rBest by 0.75 for stars brighter than 4th mag if (star && star->mag() < 4.0) rtry *= 0.75; // TODO: Add Deep Star Catalog support maxrad = rtry; return star; } bool SkyMapComposite::addNameLabel(SkyObject *o) { if (!o) return false; labelObjects().append(o); return true; } bool SkyMapComposite::removeNameLabel(SkyObject *o) { if (!o) return false; int index = labelObjects().indexOf(o); if (index < 0) return false; labelObjects().removeAt(index); return true; } QHash &SkyMapComposite::getObjectNames() { return m_ObjectNames; } QHash>> &SkyMapComposite::getObjectLists() { return m_ObjectLists; } QList SkyMapComposite::findObjectsInArea(const SkyPoint &p1, const SkyPoint &p2) { const SkyRegion ®ion = m_skyMesh->skyRegion(p1, p2); QList list; // call objectsInArea( QList&, const SkyRegion& ) for each of the // components of the SkyMapComposite if (m_Stars->selected()) m_Stars->objectsInArea(list, region); if (m_DeepSky->selected()) m_DeepSky->objectsInArea(list, region); return list; } SkyObject *SkyMapComposite::findByName(const QString &name) { #ifndef KSTARS_LITE if (KStars::Closing) return nullptr; #endif //We search the children in an "intelligent" order (most-used //object types first), in order to avoid wasting too much time //looking for a match. The most important part of this ordering //is that stars should be last (because the stars list is so long) SkyObject *o = nullptr; o = m_SolarSystem->findByName(name); if (o) return o; o = m_DeepSky->findByName(name); if (o) return o; o = m_CustomCatalogs->findByName(name); if (o) return o; o = m_internetResolvedComponent->findByName(name); if (o) return o; o = m_manualAdditionsComponent->findByName(name); if (o) return o; o = m_CNames->findByName(name); if (o) return o; o = m_Stars->findByName(name); if (o) return o; o = m_Supernovae->findByName(name); if (o) return o; o = m_Satellites->findByName(name); if (o) return o; return nullptr; } SkyObject *SkyMapComposite::findStarByGenetiveName(const QString name) { return m_Stars->findStarByGenetiveName(name); } KSPlanetBase *SkyMapComposite::planet(int n) { if (n == KSPlanetBase::SUN) return (KSPlanetBase *)(m_SolarSystem->findByName("Sun")); if (n == KSPlanetBase::MERCURY) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Mercury"))); if (n == KSPlanetBase::VENUS) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Venus"))); if (n == KSPlanetBase::MOON) return (KSPlanetBase *)(m_SolarSystem->findByName("Moon")); if (n == KSPlanetBase::MARS) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Mars"))); if (n == KSPlanetBase::JUPITER) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Jupiter"))); if (n == KSPlanetBase::SATURN) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Saturn"))); if (n == KSPlanetBase::URANUS) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Uranus"))); if (n == KSPlanetBase::NEPTUNE) return (KSPlanetBase *)(m_SolarSystem->findByName(i18n("Neptune"))); //if ( n == KSPlanetBase::PLUTO ) return (KSPlanetBase*)(m_SolarSystem->findByName( i18n( "Pluto" ) ) ); return nullptr; } void SkyMapComposite::addCustomCatalog(const QString &filename, int index) { CatalogComponent *cc = new CatalogComponent(this, filename, false, index); if (cc->objectList().size()) { m_CustomCatalogs->addComponent(cc); } else { delete cc; } } void SkyMapComposite::removeCustomCatalog(const QString &name) { foreach (SkyComponent *sc, m_CustomCatalogs->components()) { CatalogComponent *ccc = (CatalogComponent *)sc; if (ccc->name() == name) { m_CustomCatalogs->removeComponent(ccc); return; } } qWarning() << i18n("Could not find custom catalog component named %1.", name); } void SkyMapComposite::reloadCLines() { #ifndef KSTARS_LITE Q_ASSERT(!SkyMapDrawAbstract::drawLock()); SkyMapDrawAbstract::setDrawLock( true); // This is not (yet) multithreaded, so I think we don't have to worry about overwriting the state of an existing lock --asimha removeComponent(m_CLines); delete m_CLines; addComponent(m_CLines = new ConstellationLines(this, m_Cultures.get())); SkyMapDrawAbstract::setDrawLock(false); #endif } void SkyMapComposite::reloadCNames() { // Q_ASSERT( !SkyMapDrawAbstract::drawLock() ); // SkyMapDrawAbstract::setDrawLock( true ); // This is not (yet) multithreaded, so I think we don't have to worry about overwriting the state of an existing lock --asimha // objectNames(SkyObject::CONSTELLATION).clear(); // delete m_CNames; // m_CNames = 0; // m_CNames = new ConstellationNamesComponent( this, m_Cultures.get() ); // SkyMapDrawAbstract::setDrawLock( false ); objectNames(SkyObject::CONSTELLATION).clear(); removeComponent(m_CNames); delete m_CNames; addComponent(m_CNames = new ConstellationNamesComponent(this, m_Cultures.get())); } void SkyMapComposite::reloadConstellationArt() { #ifndef KSTARS_LITE Q_ASSERT(!SkyMapDrawAbstract::drawLock()); SkyMapDrawAbstract::setDrawLock(true); removeComponent(m_ConstellationArt); delete m_ConstellationArt; addComponent(m_ConstellationArt = new ConstellationArtComponent(this, m_Cultures.get())); SkyMapDrawAbstract::setDrawLock(false); #endif } void SkyMapComposite::reloadDeepSky() { #ifndef KSTARS_LITE Q_ASSERT(!SkyMapDrawAbstract::drawLock()); // Deselect object if selected! If not deselected then InfoBox tries to // get the name of an object which may not exist (getLongName) // FIXME (spacetime): Is there a better way? // Current Solution: Look for the nearest star in the region and select it. SkyMap *current_map = KStars::Instance()->map(); double maxrad = 30.0; SkyPoint center_point = current_map->getCenterPoint(); current_map->setClickedObject(KStars::Instance()->data()->skyComposite()->starNearest(¢er_point, maxrad)); current_map->setClickedPoint(current_map->clickedObject()); current_map->slotCenter(); // Remove and Regenerate set of catalog objects // // FIXME: Why should we do this? Because it messes up observing // list really bad to delete and regenerate SkyObjects. SkyMapDrawAbstract::setDrawLock(true); m_CustomCatalogs.reset(new SkyComposite(this)); removeComponent(m_internetResolvedComponent); delete m_internetResolvedComponent; addComponent(m_internetResolvedComponent = new SyncedCatalogComponent(this, m_internetResolvedCat, true, 0), 6); removeComponent(m_manualAdditionsComponent); delete m_manualAdditionsComponent; addComponent(m_manualAdditionsComponent = new SyncedCatalogComponent(this, m_manualAdditionsCat, true, 0), 6); QStringList allcatalogs = Options::showCatalogNames(); for (int i = 0; i < allcatalogs.size(); ++i) { if (allcatalogs.at(i) == m_internetResolvedCat || allcatalogs.at(i) == m_manualAdditionsCat) // These are special catalogs continue; m_CustomCatalogs->addComponent(new CatalogComponent(this, allcatalogs.at(i), false, i), 5); // FIXME: Should this be 6 or 5? See SkyMapComposite::SkyMapComposite() } // FIXME: We should also reload anything that refers to SkyObject // * in memory, because all the old SkyObjects are now gone! This // includes the observing list. Otherwise, expect a bad, bad crash // that is hard to debug! -- AS SkyMapDrawAbstract::setDrawLock(false); #endif } bool SkyMapComposite::isLocalCNames() { return m_CNames->isLocalCNames(); } void SkyMapComposite::emitProgressText(const QString &message) { emit progressText(message); #ifndef Q_OS_ANDROID //Can cause crashes on Android, investigate it qApp->processEvents(); // -jbb: this seemed to make it work. #endif //qCDebug(KSTARS) << QString("PROGRESS TEXT: %1\n").arg( message ); } const QList &SkyMapComposite::deepSkyObjects() const { return m_DeepSky->objectList(); } const QList &SkyMapComposite::constellationNames() const { return m_CNames->objectList(); } // Returns only named stars, and should not be used const QList &SkyMapComposite::stars() const { return m_Stars->objectList(); } const QList &SkyMapComposite::asteroids() const { return m_SolarSystem->asteroids(); } const QList &SkyMapComposite::comets() const { return m_SolarSystem->comets(); } const QList &SkyMapComposite::supernovae() const { return m_Supernovae->objectList(); } QList SkyMapComposite::planets() { return solarSystemComposite()->planetObjects(); } //Store it permanently /* QList SkyMapComposite::moons() { QList skyObjects; foreach(PlanetMoonsComponent *pMoons, m_SolarSystem->planetMoonsComponent()) { PlanetMoons *moons = pMoons->getMoons(); for(int i = 0; i < moons->nMoons(); ++i) { skyObjects.append(moons->moon(i)); } } return skyObjects; } */ const QList *SkyMapComposite::getSkyObjectsList(SkyObject::TYPE t) { switch (t) { case SkyObject::STAR: return &m_Stars->objectList(); break; case SkyObject::CATALOG_STAR: return nullptr; break; case SkyObject::PLANET: return &m_SolarSystem->planetObjects(); break; case SkyObject::COMET: return &comets(); break; case SkyObject::ASTEROID: return &asteroids(); break; case SkyObject::MOON: return &m_SolarSystem->moons(); break; case SkyObject::GALAXY: case SkyObject::PLANETARY_NEBULA: case SkyObject::GASEOUS_NEBULA: case SkyObject::GLOBULAR_CLUSTER: case SkyObject::OPEN_CLUSTER: return nullptr; break; case SkyObject::CONSTELLATION: return &constellationNames(); break; case SkyObject::SUPERNOVA: return &supernovae(); break; default: return nullptr; } //return nullptr; } KSPlanet *SkyMapComposite::earth() { return m_SolarSystem->earth(); } QList SkyMapComposite::customCatalogs() { return m_CustomCatalogs->components(); } QStringList SkyMapComposite::getCultureNames() { return m_Cultures->getNames(); } QString SkyMapComposite::getCultureName(int index) { return m_Cultures->getName(index); } void SkyMapComposite::setCurrentCulture(QString culture) { m_Cultures->setCurrent(culture); } QString SkyMapComposite::currentCulture() { return m_Cultures->current(); } #ifndef KSTARS_LITE FlagComponent *SkyMapComposite::flags() { return m_Flags; } #endif SatellitesComponent *SkyMapComposite::satellites() { return m_Satellites; } SupernovaeComponent *SkyMapComposite::supernovaeComponent() { return m_Supernovae; } ArtificialHorizonComponent *SkyMapComposite::artificialHorizon() { return m_ArtificialHorizon; } diff --git a/kstars/skycomponents/skymapcomposite.h b/kstars/skycomponents/skymapcomposite.h index cd8706a18..a9cdf54bd 100644 --- a/kstars/skycomponents/skymapcomposite.h +++ b/kstars/skycomponents/skymapcomposite.h @@ -1,283 +1,284 @@ /*************************************************************************** skymapcomposite.h - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once -#include "skycomposite.h" +#include "culturelist.h" #include "ksnumbers.h" +#include "skycomposite.h" +#include "skylabeler.h" +#include "skymesh.h" #include "skyobject.h" #include #include class QPolygonF; class ArtificialHorizonComponent; class ConstellationArtComponent; class ConstellationBoundaryLines; class ConstellationLines; class ConstellationNamesComponent; class ConstellationsArt; -class CultureList; class DeepSkyComponent; class DeepSkyObject; class DeepStarComponent; class Ecliptic; class Equator; class EquatorialCoordinateGrid; class FlagComponent; class HorizontalCoordinateGrid; class LocalMeridianComponent; class HorizonComponent; class KSPlanet; class KSPlanetBase; class MilkyWay; class SatellitesComponent; -class SkyLabeler; class SkyMap; -class SkyMesh; class SkyObject; class SolarSystemComposite; class StarComponent; class SupernovaeComponent; class SyncedCatalogComponent; class TargetListComponent; class HIPSComponent; /** * @class SkyMapComposite + * * SkyMapComposite is the root object in the object hierarchy of the sky map. * All requests to update, init, draw etc. will be done with this class. * The requests will be delegated to it's children. * The object hierarchy will created by adding new objects via addComponent(). * * @author Thomas Kabelmann * @version 0.1 */ class SkyMapComposite : public QObject, public SkyComposite { Q_OBJECT public: /** * Constructor * @p parent pointer to the parent SkyComponent */ explicit SkyMapComposite(SkyComposite *parent = nullptr); - ~SkyMapComposite() override; + virtual ~SkyMapComposite() override = default; void update(KSNumbers *num = nullptr) override; /** * @short Delegate planet position updates to the SolarSystemComposite * * Planet positions change over time, so they need to be recomputed * periodically, but not on every call to update(). This function * will recompute the positions of all solar system bodies except the * Earth's Moon, Jupiter's Moons AND Saturn Moons (because these objects' positions * change on a much more rapid timescale). * @p num Pointer to the KSNumbers object * @sa update() * @sa updateMoons() * @sa SolarSystemComposite::updatePlanets() */ void updateSolarSystemBodies(KSNumbers *num) override; /** * @short Delegate moon position updates to the SolarSystemComposite * * Planet positions change over time, so they need to be recomputed * periodically, but not on every call to update(). This function * will recompute the positions of the Earth's Moon and Jupiter's four * Galilean moons. These objects are done separately from the other * solar system bodies, because their positions change more rapidly, * and so updateMoons() must be called more often than updatePlanets(). * @p num Pointer to the KSNumbers object * @sa update() * @sa updatePlanets() * @sa SolarSystemComposite::updateMoons() */ // virtual void updateMoons( KSNumbers *num ); /** * @short Delegate draw requests to all sub components * @p psky Reference to the QPainter on which to paint */ void draw(SkyPainter *skyp) override; /** * @return the object nearest a given point in the sky. * @param p The point to find an object near * @param maxrad The maximum search radius, in Degrees * @note the angular separation to the matched object is returned * through the maxrad variable. */ SkyObject *objectNearest(SkyPoint *p, double &maxrad) override; /** * @return the star nearest a given point in the sky. * @param p The point to find a star near * @param maxrad The maximum search radius, in Degrees * @note the angular separation to the matched star is returned * through the maxrad variable. */ SkyObject *starNearest(SkyPoint *p, double &maxrad); /** * @short Search the children of this SkyMapComposite for * a SkyObject whose name matches the argument. * * The objects' primary, secondary and long-form names will * all be checked for a match. * @note Overloaded from SkyComposite. In this version, we search * the most likely object classes first to be more efficient. * @p name the name to be matched * @return a pointer to the SkyObject whose name matches * the argument, or a nullptr pointer if no match was found. */ SkyObject *findByName(const QString &name) override; /** * @return the list of objects in the region defined by skypoints * @param p1 first sky point (top-left vertex of rectangular region) * @param p2 second sky point (bottom-right vertex of rectangular region) */ QList findObjectsInArea(const SkyPoint &p1, const SkyPoint &p2); void addCustomCatalog(const QString &filename, int index); void removeCustomCatalog(const QString &name); bool addNameLabel(SkyObject *o); bool removeNameLabel(SkyObject *o); void reloadDeepSky(); void reloadAsteroids(); void reloadComets(); void reloadCLines(); void reloadCNames(); void reloadConstellationArt(); #ifndef KSTARS_LITE FlagComponent *flags(); #endif SatellitesComponent *satellites(); SupernovaeComponent *supernovaeComponent(); ArtificialHorizonComponent *artificialHorizon(); /** Getters for SkyComponents **/ inline HorizonComponent *horizon() { return m_Horizon; } inline ConstellationBoundaryLines *constellationBoundary() { return m_CBoundLines; } inline ConstellationLines *constellationLines() { return m_CLines; } inline Ecliptic *ecliptic() { return m_Ecliptic; } inline Equator *equator() { return m_Equator; } inline EquatorialCoordinateGrid *equatorialCoordGrid() { return m_EquatorialCoordinateGrid; } inline HorizontalCoordinateGrid *horizontalCoordGrid() { return m_HorizontalCoordinateGrid; } inline LocalMeridianComponent *localMeridianComponent() { return m_LocalMeridianComponent; } inline ConstellationArtComponent *constellationArt() { return m_ConstellationArt; } inline SolarSystemComposite *solarSystemComposite() { return m_SolarSystem; } inline ConstellationNamesComponent *constellationNamesComponent() { return m_CNames; } inline DeepSkyComponent *deepSkyComponent() { return m_DeepSky; } inline MilkyWay *milkyWay() { return m_MilkyWay; } inline SyncedCatalogComponent *internetResolvedComponent() { return m_internetResolvedComponent; } inline SyncedCatalogComponent *manualAdditionsComponent() { return m_manualAdditionsComponent; } //Accessors for StarComponent SkyObject *findStarByGenetiveName(const QString name); void emitProgressText(const QString &message) override; QList &labelObjects() { return m_LabeledObjects; } const QList &deepSkyObjects() const; const QList &constellationNames() const; const QList &stars() const; const QList &asteroids() const; const QList &comets() const; const QList &supernovae() const; QList planets(); // QList moons(); const QList *getSkyObjectsList(SkyObject::TYPE t); KSPlanet *earth(); KSPlanetBase *planet(int n); QStringList getCultureNames(); QString getCultureName(int index); QString currentCulture(); void setCurrentCulture(QString culture); bool isLocalCNames(); QList customCatalogs(); inline TargetListComponent *getStarHopRouteList() { return m_StarHopRouteList; } signals: void progressText(const QString &message); private: QHash &getObjectNames() override; QHash>> &getObjectLists() override; std::unique_ptr m_Cultures; ConstellationBoundaryLines *m_CBoundLines { nullptr }; ConstellationNamesComponent *m_CNames { nullptr }; ConstellationLines *m_CLines { nullptr }; ConstellationArtComponent *m_ConstellationArt { nullptr }; EquatorialCoordinateGrid *m_EquatorialCoordinateGrid { nullptr }; HorizontalCoordinateGrid *m_HorizontalCoordinateGrid { nullptr }; LocalMeridianComponent *m_LocalMeridianComponent { nullptr }; DeepSkyComponent *m_DeepSky { nullptr }; Equator *m_Equator { nullptr }; ArtificialHorizonComponent *m_ArtificialHorizon { nullptr }; Ecliptic *m_Ecliptic { nullptr }; HorizonComponent *m_Horizon { nullptr }; MilkyWay *m_MilkyWay { nullptr }; SolarSystemComposite *m_SolarSystem { nullptr }; std::unique_ptr m_CustomCatalogs; StarComponent *m_Stars { nullptr }; #ifndef KSTARS_LITE FlagComponent *m_Flags { nullptr }; HIPSComponent *m_HiPS { nullptr }; #endif TargetListComponent *m_ObservingList { nullptr }; TargetListComponent *m_StarHopRouteList { nullptr }; SatellitesComponent *m_Satellites { nullptr }; SupernovaeComponent *m_Supernovae { nullptr }; SyncedCatalogComponent *m_internetResolvedComponent { nullptr }; SyncedCatalogComponent *m_manualAdditionsComponent { nullptr }; std::unique_ptr m_skyMesh; std::unique_ptr m_skyLabeler; KSNumbers m_reindexNum; QList m_DeepStars; QList m_LabeledObjects; QHash m_ObjectNames; QHash>> m_ObjectLists; QHash m_ConstellationNames; QString m_internetResolvedCat; // Holds the name of the internet resolved catalog QString m_manualAdditionsCat; }; diff --git a/kstars/skycomponents/starblock.cpp b/kstars/skycomponents/starblock.cpp index 5e82ffba4..fbf35ab3e 100644 --- a/kstars/skycomponents/starblock.cpp +++ b/kstars/skycomponents/starblock.cpp @@ -1,127 +1,123 @@ /*************************************************************************** starblock.cpp - K Desktop Planetarium ------------------- begin : 9 Jun 2008 copyright : (C) 2008 by Akarsh Simha email : akarshsimha@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 #include "starblock.h" #include "skyobjects/starobject.h" #include "starcomponent.h" #include "skyobjects/stardata.h" #include "skyobjects/deepstardata.h" #ifdef KSTARS_LITE #include "skymaplite.h" #include "kstarslite/skyitems/skynodes/pointsourcenode.h" StarNode::StarNode() : starNode(0) { } StarNode::~StarNode() { if (starNode) { SkyMapLite::Instance()->deleteSkyNode(starNode); qDebug() << "REAL NODE DESTRUCTOR"; } } #endif StarBlock::StarBlock(int nstars) : faintMag(-5), brightMag(35), parent(nullptr), prev(nullptr), next(nullptr), drawID(0), nStars(0), #ifdef KSTARS_LITE stars(nstars, StarNode()) #else stars(nstars, StarObject()) #endif { } void StarBlock::reset() { if (parent) parent->releaseBlock(this); parent = nullptr; faintMag = -5.0; brightMag = 35.0; nStars = 0; } -StarBlock::~StarBlock() -{ -} - #ifdef KSTARS_LITE StarNode *StarBlock::addStar(const StarData &data) { if (isFull()) return 0; StarNode &node = stars[nStars++]; StarObject &star = node.star; star.init(&data); if (star.mag() > faintMag) faintMag = star.mag(); if (star.mag() < brightMag) brightMag = star.mag(); return &node; } StarNode *StarBlock::addStar(const DeepStarData &data) { if (isFull()) return 0; StarNode &node = stars[nStars++]; StarObject &star = node.star; star.init(&data); if (star.mag() > faintMag) faintMag = star.mag(); if (star.mag() < brightMag) brightMag = star.mag(); return &node; } #else StarObject *StarBlock::addStar(const StarData &data) { if (isFull()) return nullptr; StarObject &star = stars[nStars++]; star.init(&data); if (star.mag() > faintMag) faintMag = star.mag(); if (star.mag() < brightMag) brightMag = star.mag(); return ☆ } StarObject *StarBlock::addStar(const DeepStarData &data) { if (isFull()) return nullptr; StarObject &star = stars[nStars++]; star.init(&data); if (star.mag() > faintMag) faintMag = star.mag(); if (star.mag() < brightMag) brightMag = star.mag(); return ☆ } #endif diff --git a/kstars/skycomponents/starblock.h b/kstars/skycomponents/starblock.h index 6881736fd..758f90312 100644 --- a/kstars/skycomponents/starblock.h +++ b/kstars/skycomponents/starblock.h @@ -1,164 +1,162 @@ /*************************************************************************** starblock.h - K Desktop Planetarium ------------------- begin : 8 Jun 2008 copyright : (C) 2008 by Akarsh Simha email : akarshsimha@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 STARBLOCK_H -#define STARBLOCK_H +#pragma once #include "typedef.h" #include "starblocklist.h" #include class StarObject; class StarBlockList; class PointSourceNode; struct StarData; struct DeepStarData; #ifdef KSTARS_LITE #include "starobject.h" struct StarNode { StarNode(); ~StarNode(); StarObject star; PointSourceNode *starNode; }; #endif /** - *@class StarBlock - *Holds a block of stars and various peripheral variables to mark its place in data structures + * @class StarBlock * - *@author Akarsh Simha - *@version 1.0 + * Holds a block of stars and various peripheral variables to mark its place in data structures + * + * @author Akarsh Simha + * @version 1.0 */ class StarBlock { public: // StarBlockEntry is the data type held by the StarBlock's QVector #ifdef KSTARS_LITE typedef StarNode StarBlockEntry; #else typedef StarObject StarBlockEntry; #endif - /** Constructor - * Initializes values of various parameters and creates nstars number of stars - * @param nstars Number of stars to hold in this StarBlock - */ + /** + * Constructor + * + * Initializes values of various parameters and creates nstars number of stars + * + * @param nstars Number of stars to hold in this StarBlock + */ explicit StarBlock(int nstars = 100); + ~StarBlock() = default; + /** - * Destructor - * Deletes all stored stars - */ - ~StarBlock(); - - /** @short Initialize another star with data. - * - * FIXME: StarObject::init doesn't reset object name(s). It - * shouldn't be issue since stars which are swapped in/out do not - * have names. - * - *@param data data to initialize star with. - *@return pointer to star initialized with data. nullptr if block is full. - */ + * @short Initialize another star with data. + * + * FIXME: StarObject::init doesn't reset object name(s). It + * shouldn't be issue since stars which are swapped in/out do not + * have names. + * + * @param data data to initialize star with. + * @return pointer to star initialized with data. nullptr if block is full. + */ StarBlockEntry *addStar(const StarData &data); StarBlockEntry *addStar(const DeepStarData &data); /** - *@short Returns true if the StarBlock is full - * - *@return true if full, false if not full - */ + * @short Returns true if the StarBlock is full + * + * @return true if full, false if not full + */ inline bool isFull() const { return nStars == size(); } /** - *@short Return the capacity of this StarBlock - * - *This is different from nStars. While nStars indicates the number of stars that this StarBlock - *actually holds, this method returns the number of stars for which we have allocated memory. - *Thus, this method should return a number >= nStars. - * - *@return The number of stars that this StarBlock can hold - */ + * @short Return the capacity of this StarBlock + * + * This is different from nStars. While nStars indicates the number of stars that this StarBlock + * actually holds, this method returns the number of stars for which we have allocated memory. + * Thus, this method should return a number >= nStars. + * + * @return The number of stars that this StarBlock can hold + */ inline int size() const { return stars.size(); } /** - *@short Return the i-th star in this StarBlock - * - *@param Index of StarBlock to return - *@return A pointer to the i-th StarObject - */ + * @short Return the i-th star in this StarBlock + * + * @param Index of StarBlock to return + * @return A pointer to the i-th StarObject + */ inline StarBlockEntry *star(int i) { return &stars[i]; } /** - *@return a reference to the internal container of this - *@note This is bad -- is there a way of providing non-const access to the list's elements without allowing altering of the list alone? - */ + * @return a reference to the internal container of this + * @note This is bad -- is there a way of providing non-const access to the list's elements + * without allowing altering of the list alone? + */ inline QVector &contents() { return stars; } // These methods are there because we might want to make faintMag and brightMag private at some point /** - *@short Return the magnitude of the brightest star in this StarBlock - * - *@return Magnitude of the brightest star - */ + * @short Return the magnitude of the brightest star in this StarBlock + * + * @return Magnitude of the brightest star + */ inline float getBrightMag() const { return brightMag; } /** - *@short Return the magnitude of the faintest star in this StarBlock - * - *@return Magnitude of the faintest star - */ + * @short Return the magnitude of the faintest star in this StarBlock + * + * @return Magnitude of the faintest star + */ inline float getFaintMag() const { return faintMag; } /** - *@short Return the number of stars currently filled in this StarBlock - *@return Number of stars filled in this StarBlock - */ + * @short Return the number of stars currently filled in this StarBlock + * + * @return Number of stars filled in this StarBlock + */ inline int getStarCount() const { return nStars; } - /** - *@short Reset this StarBlock's data, for reuse of the StarBl - */ + /** @short Reset this StarBlock's data, for reuse of the StarBlock */ void reset(); - float faintMag; - float brightMag; + float faintMag { 0 }; + float brightMag { 0 }; StarBlockList *parent; std::shared_ptr prev; std::shared_ptr next; - quint32 drawID; + quint32 drawID { 0 }; private: // Disallow copying and assignment. Just in case. StarBlock(const StarBlock &); StarBlock &operator=(const StarBlock &); /** Number of initialized stars in StarBlock. */ - int nStars; + int nStars { 0 }; /** Array of stars. */ QVector stars; }; - -#endif diff --git a/kstars/skycomponents/supernovaecomponent.cpp b/kstars/skycomponents/supernovaecomponent.cpp index 4fbeafd25..67fab074f 100644 --- a/kstars/skycomponents/supernovaecomponent.cpp +++ b/kstars/skycomponents/supernovaecomponent.cpp @@ -1,265 +1,257 @@ /* Supernova Component Copyright (C) 2016 Jasem Mutlaq Based on Samikshan Bairagya GSoC work. This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "supernovaecomponent.h" -#include -#include -#include - #ifndef KSTARS_LITE +#include "kstars.h" #include "skymap.h" #else #include "kstarslite.h" #endif -#include "skypainter.h" -#include "skymesh.h" -#include "skylabeler.h" -#include "projections/projector.h" #include "dms.h" -#include "Options.h" -#include "auxiliary/filedownloader.h" #include "ksnotification.h" -//#include "notifyupdatesui.h" - -#ifndef KSTARS_LITE -#include "kstars.h" -#endif #include "kstarsdata.h" +#include "Options.h" +#include "skylabeler.h" +#include "skymesh.h" +#include "skypainter.h" +#include "auxiliary/filedownloader.h" +#include "projections/projector.h" #include "auxiliary/kspaths.h" +#include +#include +#include + SupernovaeComponent::SupernovaeComponent(SkyComposite *parent) : ListComponent(parent) { QtConcurrent::run(this, &SupernovaeComponent::loadData); //loadData(); } -SupernovaeComponent::~SupernovaeComponent() -{ -} - void SupernovaeComponent::update(KSNumbers *num) { if (!selected()) return; KStarsData *data = KStarsData::Instance(); foreach (SkyObject *so, m_ObjectList) { if (num) so->updateCoords(num); so->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } } bool SupernovaeComponent::selected() { return Options::showSupernovae(); } void SupernovaeComponent::loadData() { qDeleteAll(m_ObjectList); m_ObjectList.clear(); objectNames(SkyObject::SUPERNOVA).clear(); objectLists(SkyObject::SUPERNOVA).clear(); QString name, type, host, date, ra, de; float z, mag; QString sFileName = KSPaths::locate(QStandardPaths::GenericDataLocation, QString("catalog.min.json")); QFile sNovaFile(sFileName); if (sNovaFile.open(QIODevice::ReadOnly) == false) { qCritical() << "Unable to open supernova file" << sFileName; return; } QJsonParseError pError; QJsonDocument sNova = QJsonDocument::fromJson(sNovaFile.readAll(), &pError); if (pError.error != QJsonParseError::NoError) { qCritical() << "Error parsing json document" << pError.errorString(); return; } if (sNova.isArray() == false) { qCritical() << "Invalid document format! No JSON array."; return; } QJsonArray sArray = sNova.array(); bool ok = false; foreach (const QJsonValue &snValue, sArray) { const QJsonObject propObject = snValue.toObject(); mag = 99.9; z = 0; name.clear(); type.clear(); host.clear(); date.clear(); if (propObject.contains("ra") == false || propObject.contains("dec") == false) continue; ra = ((propObject["ra"].toArray()[0]).toObject()["value"]).toString(); de = ((propObject["dec"].toArray()[0]).toObject()["value"]).toString(); name = propObject["name"].toString(); if (propObject.contains("claimedtype")) type = ((propObject["claimedtype"].toArray()[0]).toObject()["value"]).toString(); if (propObject.contains("host")) host = ((propObject["host"].toArray()[0]).toObject()["value"]).toString(); if (propObject.contains("discoverdate")) date = ((propObject["discoverdate"].toArray()[0]).toObject()["value"]).toString(); if (propObject.contains("redshift")) z = ((propObject["redshift"].toArray()[0]).toObject()["value"]).toString().toDouble(&ok); if (ok == false) z = 99.9; if (propObject.contains("maxappmag")) mag = ((propObject["maxappmag"].toArray()[0]).toObject()["value"]).toString().toDouble(&ok); if (ok == false) mag = 99.9; Supernova *sup = new Supernova(name, dms::fromString(ra, false), dms::fromString(de, true), type, host, date, z, mag); objectNames(SkyObject::SUPERNOVA).append(name); appendListObject(sup); objectLists(SkyObject::SUPERNOVA).append(QPair(name, sup)); } } SkyObject *SupernovaeComponent::objectNearest(SkyPoint *p, double &maxrad) { if (!selected()) return nullptr; SkyObject *oBest = nullptr; double rBest = maxrad; foreach (SkyObject *so, m_ObjectList) { double r = so->angularDistanceTo(p).Degrees(); //qDebug()<(Options::starDensity())); } void SupernovaeComponent::draw(SkyPainter *skyp) { //qDebug()<<"selected()="<mag(); if (mag > float(Options::magnitudeLimitShowSupernovae())) continue; //Do not draw if mag>maglim if (mag > maglim) continue; skyp->drawSupernova(sup); } } #if 0 void SupernovaeComponent::notifyNewSupernovae() { #ifndef KSTARS_LITE //qDebug()<<"New Supernovae discovered"; QList latestList; foreach (SkyObject * so, latest) { Supernova * sup = (Supernova *)so; if (sup->getMagnitude() > float(Options::magnitudeLimitAlertSupernovae())) { //qDebug()<<"Not Bright enough to be notified"; continue; } //qDebug()<<"Bright enough to be notified"; latestList.append(so); } if (!latestList.empty()) { NotifyUpdatesUI * ui = new NotifyUpdatesUI(0); ui->addItems(latestList); ui->show(); } // if (!latest.empty()) // KMessageBox::informationList(0, i18n("New Supernovae discovered!"), latestList, i18n("New Supernovae discovered!")); #endif } #endif void SupernovaeComponent::slotTriggerDataFileUpdate() { downloadJob = new FileDownloader(); downloadJob->setProgressDialogEnabled(true, i18n("Supernovae Update"), i18n("Downloading Supernovae updates...")); QObject::connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); QObject::connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); QString output = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "catalog.min.json"; downloadJob->setDownloadedFileURL(QUrl::fromLocalFile(output)); downloadJob->get(QUrl("https://sne.space/astrocats/astrocats/supernovae/output/catalog.min.json")); } void SupernovaeComponent::downloadReady() { // Reload Supernova loadData(); #ifdef KSTARS_LITE KStarsLite::Instance()->data()->setFullTimeUpdate(); #else KStars::Instance()->data()->setFullTimeUpdate(); #endif downloadJob->deleteLater(); } void SupernovaeComponent::downloadError(const QString &errorString) { KSNotification::error(i18n("Error downloading asteroids data: %1", errorString)); downloadJob->deleteLater(); } diff --git a/kstars/skycomponents/supernovaecomponent.h b/kstars/skycomponents/supernovaecomponent.h index 8e79fc432..23ee9c66d 100644 --- a/kstars/skycomponents/supernovaecomponent.h +++ b/kstars/skycomponents/supernovaecomponent.h @@ -1,72 +1,65 @@ /* Supernova Component Copyright (C) 2016 Jasem Mutlaq Based on Samikshan Bairagya GSoC work. This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -#ifndef SUPERNOVAE_COMPONENT_H -#define SUPERNOVAE_COMPONENT_H +#pragma once +#include "ksnumbers.h" #include "listcomponent.h" #include "skyobjects/supernova.h" -#include "ksnumbers.h" - #include /** * @class SupernovaeComponent This class encapsulates Supernovae. * * @author Jasem Mutlaq, Samikshan Bairagya * * @version 0.2 */ class Supernova; class FileDownloader; class SupernovaeComponent : public QObject, public ListComponent { Q_OBJECT public: explicit SupernovaeComponent(SkyComposite *parent); - ~SupernovaeComponent() override; + virtual ~SupernovaeComponent() override = default; + bool selected() override; void update(KSNumbers *num = nullptr) override; SkyObject *objectNearest(SkyPoint *p, double &maxrad) override; /** - * @note This should actually be implemented in a better manner. - * Possibly by checking if the host galaxy for the supernova is drawn. - */ + * @note This should actually be implemented in a better manner. + * Possibly by checking if the host galaxy for the supernova is drawn. + */ void draw(SkyPainter *skyp) override; //virtual void notifyNewSupernovae(); - /** - * @note Basically copy pasted from StarComponent::zoomMagnitudeLimit() - */ + /** @note Basically copy pasted from StarComponent::zoomMagnitudeLimit() */ static float zoomMagnitudeLimit(); public slots: - /** - * @short This initiates updating of the data file - */ + /** @short This initiates updating of the data file */ void slotTriggerDataFileUpdate(); protected slots: void downloadReady(); void downloadError(const QString &errorString); private: void loadData(); - FileDownloader *downloadJob = nullptr; + FileDownloader *downloadJob { nullptr }; }; - -#endif diff --git a/kstars/skymapdrawabstract.h b/kstars/skymapdrawabstract.h index 60df7aa42..cfe412d8b 100644 --- a/kstars/skymapdrawabstract.h +++ b/kstars/skymapdrawabstract.h @@ -1,163 +1,163 @@ /*************************************************************************** skymapdrawabstract.h - K Desktop Planetarium ------------------- begin : Mon Dec 20 2010 05:04 AM UTC-6 copyright : (C) 2010 Akarsh Simha email : akarsh.simha@kdemail.net ***************************************************************************/ /*************************************************************************** * * * 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 SKYMAPDRAWABSTRACT_H_ #define SKYMAPDRAWABSTRACT_H_ #include "kstarsdata.h" #include #include #include class SkyMap; class SkyQPainter; /** *@short This class defines the methods that both rendering engines * (GLPainter and QPainter) must implement. This also allows us to add * more rendering engines if required. *@version 1.0 *@author Akarsh Simha */ // In summary, this is a class created by stealing all the // drawing-related methods from the old SkyMap class class SkyMapDrawAbstract { protected: /** *@short Virtual Destructor */ - virtual ~SkyMapDrawAbstract() {} + virtual ~SkyMapDrawAbstract() = default; public: /** *@short Constructor that sets data and m_SkyMap, and initializes * the FPS counters. */ explicit SkyMapDrawAbstract(SkyMap *sm); // *********************** "IMPURE" VIRTUAL METHODS ******************* // // NOTE: The following methods are implemented using native // QPainter in both cases. So it's virtual, but not pure virtual /**Draw the overlays on top of the sky map. These include the infoboxes, *field-of-view indicator, telescope symbols, zoom box and any other *user-interaction graphics. * *The overlays can be updated rapidly, without having to recompute the entire SkyMap. *The stored Sky image is simply blitted onto the SkyMap widget, and then we call *drawOverlays() to refresh the overlays. *@param pm pointer to the Sky pixmap */ void drawOverlays(QPainter &p, bool drawFov = true); /**Draw symbols at the position of each Telescope currently being controlled by KStars. *@note The shape of the Telescope symbol is currently a hard-coded bullseye. *@param psky reference to the QPainter on which to draw (this should be the Sky pixmap). */ void drawTelescopeSymbols(QPainter &psky); /**Draw FOV of solved image in Ekos Alignment Module *@param psky reference to the QPainter on which to draw (this should be the Sky pixmap). */ void drawSolverFOV(QPainter &psky); /** *@short Draw a dotted-line rectangle which traces the potential new field-of-view in ZoomBox mode. *@param psky reference to the QPainter on which to draw (this should be the Sky pixmap). */ void drawZoomBox(QPainter &psky); /**Draw a dashed line from the Angular-Ruler start point to the current mouse cursor, *when in Angular-Ruler mode. *@param psky reference to the QPainter on which to draw (this should be the Sky pixmap). */ void drawAngleRuler(QPainter &psky); /** @short Draw the current Sky map to a pixmap which is to be printed or exported to a file. * *@param pd pointer to the QPaintDevice on which to draw. *@see KStars::slotExportImage() *@see KStars::slotPrint() */ void exportSkyImage(QPaintDevice *pd, bool scale = false); /** @short Draw the current Sky map using passed SkyQPainter instance. Required when * used QPaintDevice doesn't support drawing using multiple painters (e.g. QSvgGenerator * which generates broken SVG output when more than one QPainter subclass is used). * Passed painter should already be initialized to draw on selected QPaintDevice subclass * using begin() and it won't be ended [end()] by this method. *@param painter pointer to the SkyQPainter already set up to paint on selected QPaintDevice subclass. *@param scale should sky image be scaled to fit used QPaintDevice? */ void exportSkyImage(SkyQPainter *painter, bool scale = false); /** @short Draw "user labels". User labels are name labels attached to objects manually with * the right-click popup menu. Also adds a label to the FocusObject if the Option UseAutoLabel * is true. * @param labelObjects QList of pointers to the objects which need labels (excluding the centered object) * @param psky painter for the sky * @note the labelObjects list is managed by the SkyMapComponents class */ void drawObjectLabels(QList &labelObjects); /** *@return true if a draw is in progress or is locked, false otherwise. This is just the value of m_DrawLock */ static inline bool drawLock() { return m_DrawLock; } /** *@short Acquire / release a draw lock. This prevents other drawing from happening */ static void setDrawLock(bool state); // *********************** PURE VIRTUAL METHODS ******************* // // NOTE: The following methods differ between GL and QPainter backends // Thus, they are pure virtual and must be implemented by the sublcass protected: /** *@short Overridden paintEvent method. Must be implemented by * subclasses to draw the SkyMap. (This method is pure * virtual) */ virtual void paintEvent(QPaintEvent *e) = 0; /* *NOTE: * Depending on whether the implementation of this class is a * GL-backend, it may need to implement these methods: * virtual void resizeGL(int width, int height); * virtual void initializeGL(); * So we will not even bother defining them here. * They must be taken care of in the subclasses */ KStarsData *m_KStarsData; SkyMap *m_SkyMap; static bool m_DrawLock; /** Calculate FPS and dump result to stderr using qDebug */ //void calculateFPS(); private: // int m_framecount; // To count FPS //QTime m_fpstime; }; #endif diff --git a/kstars/skyobjects/deepskyobject.h b/kstars/skyobjects/deepskyobject.h index c14cdbfea..2d817cf59 100644 --- a/kstars/skyobjects/deepskyobject.h +++ b/kstars/skyobjects/deepskyobject.h @@ -1,228 +1,228 @@ /*************************************************************************** deepskyobject.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "dms.h" #include "skyobject.h" #include #include class QImage; class QString; class KSPopupMenu; class CatalogComponent; class CatalogEntryData; /** *@class DeepSkyObject *Provides all necessary information about a deep-sky object: *data inherited from SkyObject (coordinates, type, magnitude, *2 names, and URLs) and data specific to DeepSkyObjects *(common name, angular size, position angle, Image, catalog) *@short Information about a "dep-sky" object; i.e., anything *that's not a solar system body or a star. *@author Jason Harris *@version 1.0 */ class DeepSkyObject : public SkyObject { public: /** *Constructor. Create DeepSkyObject with data according to arguments. *@param t Type of object *@param r catalog Right Ascension *@param d catalog Declination *@param m magnitude (brightness) *@param n Primary name *@param n2 Secondary name *@param lname Long name (common name) *@param cat catalog ID *@param a major axis (arcminutes) *@param b minor axis (arcminutes) *@param pa position angle (degrees) *@param pgc PGC catalog number *@param ugc UGC catalog number */ explicit DeepSkyObject(int t = SkyObject::STAR, dms r = dms(0.0), dms d = dms(0.0), float m = 0.0, const QString &n = "unnamed", const QString &n2 = QString(), const QString &lname = QString(), const QString &cat = QString(), float a = 0.0, float b = 0.0, double pa = 0.0, int pgc = 0, int ugc = 0); /** @short Copy constructor. * @param o SkyObject from which to copy data */ DeepSkyObject(const DeepSkyObject &o); explicit DeepSkyObject(const CatalogEntryData &data, CatalogComponent *cat = nullptr); QString labelString() const override; DeepSkyObject *clone() const override; SkyObject::UID getUID() const override; /** *Destructor */ - ~DeepSkyObject() override {} + ~DeepSkyObject() override = default; /** *@enum CATALOG *The catalog ID of the DeepSkyObject. */ enum CATALOG { CAT_MESSIER = 0, CAT_NGC = 1, CAT_IC = 2, CAT_UNKNOWN }; /** *@return a QString identifying the object's primary catalog. *Possible catalog values are: *- "M" for Messier catalog *- "NGC" for NGC catalog *- "IC" for IC catalog *- empty string is presumed to be an object in a custom catalog *@sa setCatalog() */ QString catalog(void) const; /** *Set the internal Catalog value according to the QString *argument: * "M" : CAT_MESSIER * "NGC" : CAT_NGC * "IC" : CAT_IC *@sa catalog() */ void setCatalog(const QString &s); /** *Set the reference to the custom catalog component, if any *@sa customCatalog() */ inline void setCustomCatalog(CatalogComponent *s) { customCat = s; } /** *@return a pointer to a custom catalog component */ inline CatalogComponent *customCatalog() { return customCat; } /** *Set the integrated flux value of the object */ inline void setFlux(const float &f) { Flux = f; } /** *@return the object's integrated flux, unit value is stored in the custom catalog component. */ inline float flux() const { return Flux; } /** *@return the object's major axis length, in arcminutes. */ inline float a() const { return MajorAxis; } /** *@return the object's minor axis length, in arcminutes. */ inline float b() const { return MinorAxis; } /** *@return the object's aspect ratio (MinorAxis/MajorAxis). Returns 1.0 *if the object's MinorAxis=0.0. */ float e() const; /** *@return the object's position angle in degrees, measured clockwise from North. */ inline double pa() const override { return PositionAngle; } /** *@return the object's UGC catalog number. Return 0 if the object is not in UGC. */ inline int ugc() const { return UGC; } /** *@return the object's PGC catalog number. Return 0 if the object is not in PGC. */ inline int pgc() const { return PGC; } /** @return an object's image */ const QImage &image() { if (imageLoaded) return m_image; else { loadImage(); return m_image; } } /** Try to load the object's image */ void loadImage(); /** *@return true if the object is in the Messier catalog */ inline bool isCatalogM() const { return (Catalog == CAT_MESSIER); } /** *@return true if the object is in the NGC catalog */ inline bool isCatalogNGC() const { return (Catalog == CAT_NGC); } /** *@return true if the object is in the IC catalog */ inline bool isCatalogIC() const { return (Catalog == CAT_IC); } /** *@return true if the object is not in a catalog */ inline bool isCatalogNone() const { return (Catalog == CAT_UNKNOWN); } /** *@return the pixel distance for offseting the object's name label */ double labelOffset() const override; void initPopupMenu(KSPopupMenu *pmenu) override; public: quint64 updateID { 0 }; quint64 updateNumID { 0 }; private: double PositionAngle { 0 }; QImage m_image; /// Dreyer or other description QString Description; CatalogComponent *customCat { nullptr }; int UGC { 0 }; int PGC { 0 }; float MajorAxis { 0 }; float MinorAxis { 0 }; float Flux { 0 }; unsigned char Catalog { 0 }; bool imageLoaded { false }; }; diff --git a/kstars/skyobjects/ksasteroid.h b/kstars/skyobjects/ksasteroid.h index e062d417d..60f287f6d 100644 --- a/kstars/skyobjects/ksasteroid.h +++ b/kstars/skyobjects/ksasteroid.h @@ -1,251 +1,251 @@ /*************************************************************************** ksasteroid.h - K Desktop Planetarium ------------------- begin : Wed 19 Feb 2003 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include #include "ksplanetbase.h" class dms; class KSNumbers; /** @class KSAsteroid *@short A subclass of KSPlanetBase that implements asteroids. * * All elements are in the heliocentric ecliptic J2000 reference frame. * * Check here for full description: https://ssd.jpl.nasa.gov/?sb_elem#legend * *The orbital elements are stored as private member variables, and it *provides methods to compute the ecliptic coordinates for any time *from the orbital elements. * *The orbital elements are: *@li JD Epoch of element values *@li a semi-major axis length (AU) *@li e eccentricity of orbit *@li i inclination angle (with respect to J2000.0 ecliptic plane) *@li w argument of perihelion (w.r.t. J2000.0 ecliptic plane) *@li N longitude of ascending node (J2000.0 ecliptic) *@li M mean anomaly at epoch JD *@li H absolute magnitude *@li G slope parameter * *@author Jason Harris *@version 1.0 */ class KSAsteroid : public KSPlanetBase { public: /** Constructor. *@p catN number of asteroid *@p s the name of the asteroid *@p image_file the filename for an image of the asteroid *@p JD the Julian Day for the orbital elements *@p a the semi-major axis of the asteroid's orbit (AU) *@p e the eccentricity of the asteroid's orbit *@p i the inclination angle of the asteroid's orbit *@p w the argument of the orbit's perihelion *@p N the longitude of the orbit's ascending node *@p M the mean anomaly for the Julian Day *@p H absolute magnitude *@p G slope parameter */ KSAsteroid(int catN, const QString &s, const QString &image_file, long double JD, double a, double e, dms i, dms w, dms N, dms M, double H, double G); KSAsteroid *clone() const override; SkyObject::UID getUID() const override; static const SkyObject::TYPE TYPE = SkyObject::ASTEROID; /** Destructor (empty)*/ - ~KSAsteroid() override {} + ~KSAsteroid() override = default; /** This is inherited from KSPlanetBase. We don't use it in this class, *so it is empty. */ bool loadData() override; /** This lets other classes like KSPlanetBase access H and G values *Used by KSPlanetBase::FindMagnitude */ double inline getAbsoluteMagnitude() const { return H; } double inline getSlopeParameter() const { return G; } /** *@short Sets the asteroid's perihelion distance */ void setPerihelion(double perihelion); /** *@return Perihelion distance */ inline double getPerihelion() const { return q; } /** *@short Sets the asteroid's earth minimum orbit intersection distance */ void setEarthMOID(double earth_moid); /** *@return the asteroid's earth minimum orbit intersection distance in AU */ inline double getEarthMOID() const { return EarthMOID; } /** *@short Sets the asteroid's orbit solution ID */ void setOrbitID(QString orbit_id); /** *@return the asteroid's orbit solution ID */ inline QString getOrbitID() const { return OrbitID; } /** *@short Sets the asteroid's orbit class */ void setOrbitClass(QString orbit_class); /** *@return the asteroid's orbit class */ inline QString getOrbitClass() const { return OrbitClass; } /** *@short Sets if the comet is a near earth object */ void setNEO(bool neo); /** *@return true if the asteroid is a near earth object */ inline bool isNEO() const { return NEO; } /** *@short Sets the asteroid's albedo */ void setAlbedo(float albedo); /** *@return the asteroid's albedo */ inline float getAlbedo() const { return Albedo; } /** *@short Sets the asteroid's diameter */ void setDiameter(float diam); /** *@return the asteroid's diameter */ inline float getDiameter() const { return Diameter; } /** *@short Sets the asteroid's dimensions */ void setDimensions(QString dim); /** *@return the asteroid's dimensions */ inline QString getDimensions() const { return Dimensions; } /** *@short Sets the asteroid's rotation period */ void setRotationPeriod(float rot_per); /** *@return the asteroid's rotation period */ inline float getRotationPeriod() const { return RotationPeriod; } /** *@short Sets the asteroid's period */ void setPeriod(float per); /** *@return the asteroid's period */ inline float getPeriod() const { return Period; } // TODO: Add top level implementation /** * @brief toDraw * @return whether to draw the asteroid * * Note that you'd check for other, older filtering methids * upn implementing this on other types! (a.k.a find nearest) */ inline bool toDraw() { return toCalculate(); } /** * @brief toCalculate * @return whether to calculate the position */ bool toCalculate(); protected: /** Calculate the geocentric RA, Dec coordinates of the Asteroid. *@note reimplemented from KSPlanetBase *@param num time-dependent values for the desired date *@param Earth planet Earth (needed to calculate geocentric coords) *@return true if position was successfully calculated. */ bool findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth = nullptr) override; //these set functions are needed for the new KSPluto subclass void set_a(double newa) { a = newa; } void set_e(double newe) { e = newe; } void set_P(double newP) { P = newP; } void set_i(double newi) { i.setD(newi); } void set_w(double neww) { w.setD(neww); } void set_M(double newM) { M.setD(newM); } void set_N(double newN) { N.setD(newN); } void setJD(long double jd) { JD = jd; } private: /** * Serializers */ friend QDataStream &operator<<(QDataStream &out, const KSAsteroid &asteroid); friend QDataStream &operator>>(QDataStream &in, KSAsteroid *&asteroid); void findMagnitude(const KSNumbers *) override; int catN { 0 }; long double JD { 0 }; double q { 0 }; double a { 0 }; double e { 0 }; double P { 0 }; double EarthMOID { 0 }; float Albedo { 0 }; float Diameter { 0 }; float RotationPeriod { 0 }; float Period { 0 }; dms i, w, M, N; double H { 0 }; double G { 0 }; QString OrbitID, OrbitClass, Dimensions; bool NEO { false }; }; diff --git a/kstars/skyobjects/kscomet.h b/kstars/skyobjects/kscomet.h index 65f715aeb..022cf941e 100644 --- a/kstars/skyobjects/kscomet.h +++ b/kstars/skyobjects/kscomet.h @@ -1,221 +1,221 @@ /*************************************************************************** kscomet.h - K Desktop Planetarium ------------------- begin : Wed 19 Feb 2003 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ksplanetbase.h" /** * @class KSComet * @short A subclass of KSPlanetBase that implements comets. * * The orbital elements are stored as private member variables, and * it provides methods to compute the ecliptic coordinates for any * time from the orbital elements. * * All elements are in the heliocentric ecliptic J2000 reference frame. * * Check here for full description: https://ssd.jpl.nasa.gov/?sb_elem#legend * * The orbital elements are: * @li JD Epoch of element values * @li q perihelion distance (AU) * @li e eccentricity of orbit * @li i inclination angle (with respect to J2000.0 ecliptic plane) * @li w argument of perihelion (w.r.t. J2000.0 ecliptic plane) * @li N longitude of ascending node (J2000.0 ecliptic) * @li Tp time of perihelion passage (YYYYMMDD.DDD) * @li M1 comet total magnitude parameter * @li M2 comet nuclear magnitude parameter * @li K1 total magnitude slope parameter * @li K2 nuclear magnitude slope parameter * * @author Jason Harris * @version 1.1 */ class KSNumbers; class dms; class KSComet : public KSPlanetBase { public: /** * Constructor. * @param s the name of the comet * @param image_file the filename for an image of the comet * @param q the perihelion distance of the comet's orbit (AU) * @param e the eccentricity of the comet's orbit * @param i the inclination angle of the comet's orbit * @param w the argument of the orbit's perihelion * @param N the longitude of the orbit's ascending node * @param Tp The date of the most proximate perihelion passage (YYYYMMDD.DDD) * @param M1 the comet total magnitude parameter * @param M2 the comet nuclear magnitude parameter * @param K1 the comet total magnitude slope parameter * @param K2 the comet nuclear magnitude slope parameter */ KSComet(const QString &s, const QString &image_file, double q, double e, dms i, dms w, dms N, double Tp, float M1, float M2, float K1, float K2); KSComet *clone() const override; SkyObject::UID getUID() const override; /** Destructor (empty)*/ - ~KSComet() override {} + ~KSComet() override = default; /** * Unused virtual function inherited from KSPlanetBase thus it's simply empty here. */ bool loadData() override; /** * @short Returns the Julian Day of Perihelion passage * @return Julian Day of Perihelion Passage */ inline long double getPerihelionJD() { return JDp; } /** * @short Returns Perihelion distance * @return Perihelion distance */ inline double getPerihelion() { return q; } /** @return the comet total magnitude parameter */ inline float getTotalMagnitudeParameter() { return M1; } /** @return the comet nuclear magnitude parameter */ inline float getNuclearMagnitudeParameter() { return M2; } /** @return the total magnitude slope parameter */ inline float getTotalSlopeParameter() { return K1; } /** @return the nuclear magnitude slope parameter */ inline float getNuclearSlopeParameter() { return K2; } /** @short Sets the comet's tail length in km */ void setTailSize(double tailsize) { TailSize = tailsize; } /** @return the estimated tail length in km */ inline float getTailSize() { return TailSize; } /** @short Sets the comet's apparent tail length in degrees */ void setComaAngSize(double comaAngSize) { ComaAngSize = comaAngSize; } /** @return the estimated angular size of the tail as a dms */ inline dms getComaAngSize() { return dms(ComaAngSize); } /** @return the estimated diameter of the nucleus in km */ inline float getNuclearSize() { return NuclearSize; } /** @short Sets the comet's earth minimum orbit intersection distance */ void setEarthMOID(double earth_moid); /** @return the comet's earth minimum orbit intersection distance in km */ inline double getEarthMOID() { return EarthMOID; } /** @short Sets the comet's orbit solution ID */ void setOrbitID(QString orbit_id); /** @return the comet's orbit solution ID */ inline QString getOrbitID() { return OrbitID; } /** @short Sets the comet's orbit class */ void setOrbitClass(QString orbit_class); /** @return the comet's orbit class */ inline QString getOrbitClass() { return OrbitClass; } /** @short Sets if the comet is a near earth object */ void setNEO(bool neo); /** @return true if the comet is a near earth object */ inline bool isNEO() { return NEO; } /** @short Sets the comet's albedo */ void setAlbedo(float albedo); /** @return the comet's albedo */ inline float getAlbedo() { return Albedo; } /** @short Sets the comet's diameter */ void setDiameter(float diam); /** @return the comet's diameter */ inline float getDiameter() { return Diameter; } /** @short Sets the comet's dimensions */ void setDimensions(QString dim); /** @return the comet's dimensions */ inline QString getDimensions() { return Dimensions; } /** @short Sets the comet's rotation period */ void setRotationPeriod(float rot_per); /** @return the comet's rotation period */ inline float getRotationPeriod() { return RotationPeriod; } /** @short Sets the comet's period */ void setPeriod(float per); /** @return the comet's period */ inline float getPeriod() { return Period; } protected: /** * Calculate the geocentric RA, Dec coordinates of the Comet. * @note reimplemented from KSPlanetBase * @param num time-dependent values for the desired date * @param Earth planet Earth (needed to calculate geocentric coords) * @return true if position was successfully calculated. */ bool findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth = nullptr) override; /** * @short Estimate physical parameters of the comet such as coma size, tail length and size of the nucleus * @note invoked from findGeocentricPosition in order */ void findPhysicalParameters(); private: void findMagnitude(const KSNumbers *) override; long double JDp { 0 }; double q { 0 }; double e { 0 }; double a { 0 }; double P { 0 }; double EarthMOID { 0 }; double TailSize { 0 }; double ComaAngSize { 0 }; double ComaSize { 0 }; double NuclearSize { 0 }; float M1 { 0 }; float M2 { 0 }; float K1 { 0 }; float K2 { 0 }; float Albedo { 0 }; float Diameter { 0 }; float RotationPeriod { 0 }; float Period { 0 }; dms i, w, N; QString OrbitID, OrbitClass, Dimensions; bool NEO { false }; /// Part of UID qint64 uidPart { 0 }; }; diff --git a/kstars/skyobjects/ksplanet.cpp b/kstars/skyobjects/ksplanet.cpp index e286117ea..2f5b3ee3a 100644 --- a/kstars/skyobjects/ksplanet.cpp +++ b/kstars/skyobjects/ksplanet.cpp @@ -1,444 +1,440 @@ /*************************************************************************** ksplanet.cpp - K Desktop Planetarium ------------------- begin : Sun Jul 22 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "ksplanet.h" #include "ksnumbers.h" #include "ksutils.h" #include "ksfilereader.h" #include #include KSPlanet::OrbitDataManager KSPlanet::odm; -KSPlanet::OrbitDataColl::OrbitDataColl() -{ -} - KSPlanet::OrbitDataManager::OrbitDataManager() { //EMPTY } bool KSPlanet::OrbitDataManager::readOrbitData(const QString &fname, QVector *vector) { QFile f; if (KSUtils::openDataFile(f, fname)) { KSFileReader fileReader(f); // close file is included QStringList fields; while (fileReader.hasMoreLines()) { fields = fileReader.readLine().split(' ', QString::SkipEmptyParts); if (fields.size() == 3) { double A = fields[0].toDouble(); double B = fields[1].toDouble(); double C = fields[2].toDouble(); vector->append(OrbitData(A, B, C)); } } } else { return false; } return true; } bool KSPlanet::OrbitDataManager::loadData(KSPlanet::OrbitDataColl &odc, const QString &n) { QString fname, snum; QFile f; int nCount = 0; QString nl = n.toLower(); if (hash.contains(nl)) { odc = hash[nl]; return true; //orbit data already loaded } //Create a new OrbitDataColl OrbitDataColl ret; //Ecliptic Longitude for (int i = 0; i < 6; ++i) { snum.setNum(i); fname = nl + ".L" + snum + ".vsop"; if (readOrbitData(fname, &ret.Lon[i])) nCount++; } if (nCount == 0) return false; //Ecliptic Latitude for (int i = 0; i < 6; ++i) { snum.setNum(i); fname = nl + ".B" + snum + ".vsop"; if (readOrbitData(fname, &ret.Lat[i])) nCount++; } if (nCount == 0) return false; //Heliocentric Distance for (int i = 0; i < 6; ++i) { snum.setNum(i); fname = nl + ".R" + snum + ".vsop"; if (readOrbitData(fname, &ret.Dst[i])) nCount++; } if (nCount == 0) return false; hash[nl] = ret; odc = hash[nl]; return true; } KSPlanet::KSPlanet(const QString &s, const QString &imfile, const QColor &c, double pSize) : KSPlanetBase(s, imfile, c, pSize) { } KSPlanet::KSPlanet(int n) : KSPlanetBase() { switch (n) { case MERCURY: KSPlanetBase::init(i18n("Mercury"), "mercury", KSPlanetBase::planetColor[KSPlanetBase::MERCURY], 4879.4); break; case VENUS: KSPlanetBase::init(i18n("Venus"), "venus", KSPlanetBase::planetColor[KSPlanetBase::VENUS], 12103.6); break; case MARS: KSPlanetBase::init(i18n("Mars"), "mars", KSPlanetBase::planetColor[KSPlanetBase::MARS], 6792.4); break; case JUPITER: KSPlanetBase::init(i18n("Jupiter"), "jupiter", KSPlanetBase::planetColor[KSPlanetBase::JUPITER], 142984.); break; case SATURN: KSPlanetBase::init(i18n("Saturn"), "saturn", KSPlanetBase::planetColor[KSPlanetBase::SATURN], 120536.); break; case URANUS: KSPlanetBase::init(i18n("Uranus"), "uranus", KSPlanetBase::planetColor[KSPlanetBase::URANUS], 51118.); break; case NEPTUNE: KSPlanetBase::init(i18n("Neptune"), "neptune", KSPlanetBase::planetColor[KSPlanetBase::NEPTUNE], 49572.); break; default: qDebug() << "Error: Illegal identifier in KSPlanet constructor: " << n; break; } } KSPlanet *KSPlanet::clone() const { Q_ASSERT(typeid(this) == typeid(static_cast(this))); // Ensure we are not slicing a derived class return new KSPlanet(*this); } // TODO: Get rid of this dirty hack post KDE 4.2 release QString KSPlanet::untranslatedName() const { if (name() == i18n("Mercury")) return "Mercury"; else if (name() == i18n("Venus")) return "Venus"; else if (name() == i18n("Mars")) return "Mars"; else if (name() == i18n("Jupiter")) return "Jupiter"; else if (name() == i18n("Saturn")) return "Saturn"; else if (name() == i18n("Uranus")) return "Uranus"; else if (name() == i18n("Neptune")) return "Neptune"; else return name(); } //we don't need the reference to the ODC, so just give it a junk variable bool KSPlanet::loadData() { OrbitDataColl odc; return odm.loadData(odc, untranslatedName()); } void KSPlanet::calcEcliptic(double Tau, EclipticPosition &epret) const { double sum[6]; OrbitDataColl odc; double Tpow[6]; Tpow[0] = 1.0; for (int i = 1; i < 6; ++i) { Tpow[i] = Tpow[i - 1] * Tau; } if (!odm.loadData(odc, untranslatedName())) { epret.longitude = dms(0.0); epret.latitude = dms(0.0); epret.radius = 0.0; qWarning() << "Could not get data for '" << name() << "'" << endl; return; } //Ecliptic Longitude for (int i = 0; i < 6; ++i) { sum[i] = 0.0; for (int j = 0; j < odc.Lon[i].size(); ++j) { sum[i] += odc.Lon[i][j].A * cos(odc.Lon[i][j].B + odc.Lon[i][j].C * Tau); /* qDebug() << "sum[" << i <<"] =" << sum[i] << " A = " << odc.Lon[i][j].A << " B = " << odc.Lon[i][j].B << " C = " << odc.Lon[i][j].C << endl; */ } sum[i] *= Tpow[i]; //qDebug() << name() << " : sum[" << i << "] = " << sum[i]; } epret.longitude.setRadians(sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]); epret.longitude.setD(epret.longitude.reduce().Degrees()); //Compute Ecliptic Latitude for (uint i = 0; i < 6; ++i) { sum[i] = 0.0; for (int j = 0; j < odc.Lat[i].size(); ++j) { sum[i] += odc.Lat[i][j].A * cos(odc.Lat[i][j].B + odc.Lat[i][j].C * Tau); } sum[i] *= Tpow[i]; } epret.latitude.setRadians(sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]); //Compute Heliocentric Distance for (uint i = 0; i < 6; ++i) { sum[i] = 0.0; for (int j = 0; j < odc.Dst[i].size(); ++j) { sum[i] += odc.Dst[i][j].A * cos(odc.Dst[i][j].B + odc.Dst[i][j].C * Tau); } sum[i] *= Tpow[i]; } epret.radius = sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]; /* qDebug() << name() << " pre: Lat = " << epret.latitude.toDMSString() << " Long = " << epret.longitude.toDMSString() << " Dist = " << epret.radius << endl; */ } bool KSPlanet::findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth) { if (Earth != nullptr) { double sinL, sinL0, sinB, sinB0; double cosL, cosL0, cosB, cosB0; double x = 0.0, y = 0.0, z = 0.0; double olddst = -1000; double dst = 0; EclipticPosition trialpos; double jm = num->julianMillenia(); Earth->ecLong().SinCos(sinL0, cosL0); Earth->ecLat().SinCos(sinB0, cosB0); double eX = Earth->rsun() * cosB0 * cosL0; double eY = Earth->rsun() * cosB0 * sinL0; double eZ = Earth->rsun() * sinB0; bool once = true; while (fabs(dst - olddst) > .001) { calcEcliptic(jm, trialpos); // We store the heliocentric ecliptic coordinates the first time they are computed. if (once) { helEcPos = trialpos; once = false; } olddst = dst; trialpos.longitude.SinCos(sinL, cosL); trialpos.latitude.SinCos(sinB, cosB); x = trialpos.radius * cosB * cosL - eX; y = trialpos.radius * cosB * sinL - eY; z = trialpos.radius * sinB - eZ; //distance from Earth dst = sqrt(x * x + y * y + z * z); //The light-travel time delay, in millenia //0.0057755183 is the inverse speed of light, //in days/AU double delay = (.0057755183 * dst) / 365250.0; jm = num->julianMillenia() - delay; } ep.longitude.setRadians(atan2(y, x)); ep.longitude.reduce(); ep.latitude.setRadians(atan2(z, sqrt(x * x + y * y))); setRsun(trialpos.radius); setRearth(dst); EclipticToEquatorial(num->obliquity()); setRA0(ra()); setDec0(dec()); apparentCoord(J2000, lastPrecessJD); //nutate(num); //aberrate(num); } else { calcEcliptic(num->julianMillenia(), ep); helEcPos = ep; } //determine the position angle findPA(num); return true; } void KSPlanet::findMagnitude(const KSNumbers *num) { double cosDec, sinDec; dec().SinCos(cosDec, sinDec); /* Computation of the visual magnitude (V band) of the planet. * Algorithm provided by Pere Planesas (Observatorio Astronomico Nacional) * It has some simmilarity to J. Meeus algorithm in Astronomical Algorithms, Chapter 40. * */ // Initialized to the faintest magnitude observable with the HST float magnitude = 30; double param = 5 * log10(rsun() * rearth()); double phase = this->phase().Degrees(); double f1 = phase / 100.; if (name() == i18n("Mercury")) { if (phase > 150.) f1 = 1.5; magnitude = -0.36 + param + 3.8 * f1 - 2.73 * f1 * f1 + 2 * f1 * f1 * f1; } else if (name() == i18n("Venus")) { magnitude = -4.29 + param + 0.09 * f1 + 2.39 * f1 * f1 - 0.65 * f1 * f1 * f1; } else if (name() == i18n("Mars")) { magnitude = -1.52 + param + 0.016 * phase; } else if (name() == i18n("Jupiter")) { magnitude = -9.25 + param + 0.005 * phase; } else if (name() == i18n("Saturn")) { double T = num->julianCenturies(); double a0 = (40.66 - 4.695 * T) * dms::PI / 180.; double d0 = (83.52 + 0.403 * T) * dms::PI / 180.; double sinx = -cos(d0) * cosDec * cos(a0 - ra().radians()); sinx = fabs(sinx - sin(d0) * sinDec); double rings = -2.6 * sinx + 1.25 * sinx * sinx; magnitude = -8.88 + param + 0.044 * phase + rings; } else if (name() == i18n("Uranus")) { magnitude = -7.19 + param + 0.0028 * phase; } else if (name() == i18n("Neptune")) { magnitude = -6.87 + param; } setMag(magnitude); } SkyObject::UID KSPlanet::getUID() const { SkyObject::UID n; if (name() == i18n("Mercury")) { n = 1; } else if (name() == i18n("Venus")) { n = 2; } else if (name() == i18n("Earth")) { n = 3; } else if (name() == i18n("Mars")) { n = 4; } else if (name() == i18n("Jupiter")) { n = 5; } else if (name() == i18n("Saturn")) { n = 6; } else if (name() == i18n("Uranus")) { n = 7; } else if (name() == i18n("Neptune")) { n = 8; } else { return SkyObject::invalidUID; } return solarsysUID(UID_SOL_BIGOBJ) | n; } diff --git a/kstars/skyobjects/ksplanet.h b/kstars/skyobjects/ksplanet.h index d6e26ce0f..4de3055c3 100644 --- a/kstars/skyobjects/ksplanet.h +++ b/kstars/skyobjects/ksplanet.h @@ -1,186 +1,186 @@ /*************************************************************************** ksplanet.h - K Desktop Planetarium ------------------- begin : Sun Jul 22 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ksplanetbase.h" #include #include #include class KSNumbers; /** * @class KSPlanet * A subclass of KSPlanetBase for seven of the major planets in the solar system * (Earth and Pluto have their own specialized classes derived from KSPlanetBase). * @note The Sun is subclassed from KSPlanet. * * KSPlanet contains internal classes to manage the computations of a planet's position. * The position is computed as a series of sinusoidal sums, similar to a Fourier * transform. See "Astronomical Algorithms" by Jean Meeus or the file README.planetmath * for details. * @short Provides necessary information about objects in the solar system. * * @author Jason Harris * @version 1.0 */ class KSPlanet : public KSPlanetBase { public: /** * Constructor. * @param s Name of planet * @param image_file filename of the planet's image * @param c the color for the planet * @param pSize physical diameter of the planet, in km */ explicit KSPlanet(const QString &s = "unnamed", const QString &image_file = QString(), const QColor &c = Qt::white, double pSize = 0); /** * Simplified constructor * @param n identifier of the planet to be created * @see PLANET enum */ explicit KSPlanet(int n); KSPlanet *clone() const override; SkyObject::UID getUID() const override; - ~KSPlanet() override {} + ~KSPlanet() override = default; /** * @short return the untranslated name * This is a dirty way to solve a lot of localization-related trouble for the KDE 4.2 release * TODO: Change the whole architecture for names later */ QString untranslatedName() const; /** @short Preload the data used by findPosition. */ bool loadData() override; /** * Calculate the ecliptic longitude and latitude of the planet for * the given date (expressed in Julian Millenia since J2000). A reference * to the ecliptic coordinates is returned as the second object. * @param jm Julian Millenia (=jd/1000) * @param ret The ecliptic coordinates are returned by reference through this argument. */ virtual void calcEcliptic(double jm, EclipticPosition &ret) const; protected: /** * Calculate the geocentric RA, Dec coordinates of the Planet. * @note reimplemented from KSPlanetBase * @param num pointer to object with time-dependent values for the desired date * @param Earth pointer to the planet Earth (needed to calculate geocentric coords) * @return true if position was successfully calculated. */ bool findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth = nullptr) override; /** * @class OrbitData * This class contains doubles A,B,C which represent a single term in a planet's * positional expansion sums (each sum-term is A*COS(B+C*T)). * * @author Mark Hollomon * @version 1.0 */ class OrbitData { public: /** Default constructor */ OrbitData() : A(0.), B(0.), C(0.) {} /** * Constructor * @param a the A value * @param b the B value * @param c the C value */ OrbitData(double a, double b, double c) : A(a), B(b), C(c) {} double A, B, C; }; typedef QVector OBArray[6]; /** * OrbitDataColl contains three groups of six QVectors. Each QVector is a * list of OrbitData objects, representing a single sum used in computing * the planet's position. A set of six of these vectors comprises the large * "meta-sum" which yields the planet's Longitude, Latitude, or Distance value. * * @author Mark Hollomon * @version 1.0 */ class OrbitDataColl { public: /** Constructor */ - OrbitDataColl(); + OrbitDataColl() = default; OBArray Lon; OBArray Lat; OBArray Dst; }; /** * OrbitDataManager places the OrbitDataColl objects for all planets in a QDict * indexed by the planets' names. It also loads the positional data of each planet from disk. * * @author Mark Hollomon * @version 1.0 */ class OrbitDataManager { public: /** Constructor */ OrbitDataManager(); /** * Load orbital data for a planet from disk. * The data is stored on disk in a series of files named * "name.[LBR][0...5].vsop", where "L"=Longitude data, "B"=Latitude data, * and R=Radius data. * @param n the name of the planet whose data is to be loaded from disk. * @param odc reference to the OrbitDataColl containing the planet's orbital data. * @return true if data successfully loaded */ bool loadData(OrbitDataColl &odc, const QString &n); private: /** * Read a single orbital data file from disk into an OrbitData vector. * The data files are named "name.[LBR][0...5].vsop", where * "L"=Longitude data, "B"=Latitude data, and R=Radius data. * @param fname the filename to be read. * @param vector pointer to the OrbitData vector to be filled with these data. */ bool readOrbitData(const QString &fname, QVector *vector); QHash hash; }; private: void findMagnitude(const KSNumbers *) override; protected: bool data_loaded { false }; static OrbitDataManager odm; }; diff --git a/kstars/skyobjects/ksplanetbase.h b/kstars/skyobjects/ksplanetbase.h index b54fbea91..d0d306460 100644 --- a/kstars/skyobjects/ksplanetbase.h +++ b/kstars/skyobjects/ksplanetbase.h @@ -1,284 +1,284 @@ /*************************************************************************** ksplanetbase.h - K Desktop Planetarium ------------------- begin : Sun Jan 29 2002 copyright : (C) 2002 by Mark Hollomon email : mhh@mindspring.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. * * * ***************************************************************************/ #pragma once #include "trailobject.h" #include #include #include #include class KSNumbers; /** * @class EclipticPosition * @short The ecliptic position of a planet (Longitude, Latitude, and distance from Sun). * @author Mark Hollomon * @version 1.0 */ class EclipticPosition { public: dms longitude; dms latitude; double radius; /**Constructor. */ explicit EclipticPosition(dms plong = dms(), dms plat = dms(), double prad = 0.0) : longitude(plong), latitude(plat), radius(prad) { } }; /** * @class KSPlanetBase * A subclass of TrailObject that provides additional information needed for most solar system * objects. This is a base class for KSSun, KSMoon, KSPlanet, KSAsteroid and KSComet. * Those classes cover all solar system objects except planetary moons, which are * derived directly from TrailObject * @short Provides necessary information about objects in the solar system. * @author Mark Hollomon * @version 1.0 */ class KSPlanetBase : public TrailObject { public: /** * Constructor. Calls SkyObject constructor with type=2 (planet), * coordinates=0.0, mag=0.0, primary name s, and all other QStrings empty. * @param s Name of planet * @param image_file filename of the planet's image * @param c color of the symbol to use for this planet * @param pSize the planet's physical size, in km */ explicit KSPlanetBase(const QString &s = i18n("unnamed"), const QString &image_file = QString(), const QColor &c = Qt::white, double pSize = 0); /** Destructor (empty) */ - ~KSPlanetBase() override {} + ~KSPlanetBase() override = default; void init(const QString &s, const QString &image_file, const QColor &c, double pSize); //enum Planets { MERCURY=0, VENUS=1, MARS=2, JUPITER=3, SATURN=4, URANUS=5, NEPTUNE=6, PLUTO=7, SUN=8, MOON=9, UNKNOWN_PLANET }; enum Planets { MERCURY = 0, VENUS = 1, MARS = 2, JUPITER = 3, SATURN = 4, URANUS = 5, NEPTUNE = 6, SUN = 7, MOON = 8, UNKNOWN_PLANET }; static KSPlanetBase *createPlanet(int n); static QVector planetColor; virtual bool loadData() = 0; /** @return pointer to Ecliptic Longitude coordinate */ const dms &ecLong() const { return ep.longitude; } /** @return pointer to Ecliptic Latitude coordinate */ const dms &ecLat() const { return ep.latitude; } /** * @short Set Ecliptic Geocentric Longitude according to argument. * @param elong Ecliptic Longitude */ void setEcLong(dms elong) { ep.longitude = elong; } /** * @short Set Ecliptic Geocentric Latitude according to argument. * @param elat Ecliptic Latitude */ void setEcLat(dms elat) { ep.latitude = elat; } /** @return pointer to Ecliptic Heliocentric Longitude coordinate */ const dms &helEcLong() const { return helEcPos.longitude; } /** @return pointer to Ecliptic Heliocentric Latitude coordinate */ const dms &helEcLat() const { return helEcPos.latitude; } /** * @short Convert Ecliptic logitude/latitude to Right Ascension/Declination. * @param Obliquity current Obliquity of the Ecliptic (angle from Equator) */ void EclipticToEquatorial(const CachingDms *Obliquity); /** * @short Convert Right Ascension/Declination to Ecliptic logitude/latitude. * @param Obliquity current Obliquity of the Ecliptic (angle from Equator) */ void EquatorialToEcliptic(const CachingDms *Obliquity); /** @return pointer to this planet's texture */ const QImage &image() const { return m_image; } /** @return distance from Sun, in Astronomical Units (1 AU is Earth-Sun distance) */ double rsun() const { return ep.radius; } /** * @short Set the solar distance in AU. * @param r the new solar distance in AU */ void setRsun(double r) { ep.radius = r; } /** @return distance from Earth, in Astronomical Units (1 AU is Earth-Sun distance) */ double rearth() const { return Rearth; } /** * @short Set the distance from Earth, in AU. * @param r the new earth-distance in AU */ void setRearth(double r) { Rearth = r; } /** * @short compute and set the distance from Earth, in AU. * @param Earth pointer to the Earth from which to calculate the distance. */ void setRearth(const KSPlanetBase *Earth); /** * Update position of the planet (reimplemented from SkyPoint) * @param num current KSNumbers object * @param includePlanets this function does nothing if includePlanets=false * @param lat pointer to the geographic latitude; if nullptr, we skip localizeCoords() * @param LST pointer to the local sidereal time; if nullptr, we skip localizeCoords() */ void updateCoords(const KSNumbers *num, bool includePlanets = true, const CachingDms *lat = nullptr, const CachingDms *LST = nullptr, bool forceRecompute = false) override; /** * @short Find position, including correction for Figure-of-the-Earth. * @param num KSNumbers pointer for the target date/time * @param lat pointer to the geographic latitude; if nullptr, we skip localizeCoords() * @param LST pointer to the local sidereal time; if nullptr, we skip localizeCoords() * @param Earth pointer to the Earth (not used for the Moon) */ void findPosition(const KSNumbers *num, const CachingDms *lat = nullptr, const CachingDms *LST = nullptr, const KSPlanetBase *Earth = nullptr); /** @return the Planet's position angle. */ double pa() const override { return PositionAngle; } /** * @short Set the Planet's position angle. * @param p the new position angle */ void setPA(double p) { PositionAngle = p; } /** @return the Planet's angular size, in arcminutes */ double angSize() const { return AngularSize; } /** @short set the planet's angular size, in km. * @param size the planet's size, in km */ void setAngularSize(double size) { AngularSize = size; } /** @return the Planet's physical size, in km */ double physicalSize() const { return PhysicalSize; } /** @short set the planet's physical size, in km. * @param size the planet's size, in km */ void setPhysicalSize(double size) { PhysicalSize = size; } /** @return the phase angle of this planet */ inline dms phase() { return dms(Phase); } /** @return the color for the planet symbol */ QColor &color() { return m_Color; } /** @short Set the color for the planet symbol */ void setColor(const QColor &c) { m_Color = c; } /** @return true if the KSPlanet is one of the eight major planets */ bool isMajorPlanet() const; /** @return the pixel distance for offseting the object's name label */ double labelOffset() const override; protected: /** Big object. Planet, Moon, Sun. */ static const UID UID_SOL_BIGOBJ; /** Asteroids */ static const UID UID_SOL_ASTEROID; /** Comets */ static const UID UID_SOL_COMET; /** Compute high 32-bits of UID. */ inline UID solarsysUID(UID type) const { return (SkyObject::UID_SOLARSYS << 60) | (type << 56); } /** * @short find the object's current geocentric equatorial coordinates (RA and Dec) * This function is pure virtual; it must be overloaded by subclasses. * This function is private; it is called by the public function findPosition() * which also includes the figure-of-the-earth correction, localizeCoords(). * @param num pointer to current KSNumbers object * @param Earth pointer to planet Earth (needed to calculate geocentric coords) * @return true if position was successfully calculated. */ virtual bool findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth = nullptr) = 0; /** * @short Computes the visual magnitude for the major planets. * @param num pointer to a ksnumbers object. Needed for the saturn rings contribution to * saturn's magnitude. */ virtual void findMagnitude(const KSNumbers *num) = 0; /** * Determine the position angle of the planet for a given date * (used internally by findPosition() ) */ void findPA(const KSNumbers *num); /** Determine the phase of the planet. */ virtual void findPhase(); // Geocentric ecliptic position, but distance to the Sun EclipticPosition ep; // Heliocentric ecliptic position referred to the equinox of the epoch // as obtained from VSOP. EclipticPosition helEcPos; double Rearth; double Phase; QImage m_image; private: /** * @short correct the position for the fact that the location is not at the center of the Earth, * but a position on its surface. This causes a small parallactic shift in a solar system * body's apparent position. The effect is most significant for the Moon. * This function is private, and should only be called from the public findPosition() function. * @param num pointer to a ksnumbers object for the target date/time * @param lat pointer to the geographic latitude of the location. * @param LST pointer to the local sidereal time. */ void localizeCoords(const KSNumbers *num, const CachingDms *lat, const CachingDms *LST); double PositionAngle, AngularSize, PhysicalSize; QColor m_Color; }; diff --git a/kstars/skyobjects/kssun.h b/kstars/skyobjects/kssun.h index 23f3c69ac..e6f766926 100644 --- a/kstars/skyobjects/kssun.h +++ b/kstars/skyobjects/kssun.h @@ -1,67 +1,74 @@ /*************************************************************************** kssun.h - K Desktop Planetarium ------------------- begin : Sun Jan 29 2002 copyright : (C) 2002 by Mark Hollomon email : mhh@mindspring.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 KSSUN_H_ -#define KSSUN_H_ +#pragma once -#include #include "ksplanet.h" -/** @class KSSun - *Child class of KSPlanetBase; encapsulates information about the Sun. - *@short Provides necessary information about the Sun. - *@author Mark Hollomon - *@version 1.0 - */ +#include +/** + * @class KSSun + * + * Child class of KSPlanetBase; encapsulates information about the Sun. + * + * @short Provides necessary information about the Sun. + * + * @author Mark Hollomon + * @version 1.0 + */ class KSSun : public KSPlanet { public: - /** Constructor. Defines constants needed by findPosition(). - *Sets Ecliptic coordinates appropriate for J2000. - *@param kd pointer to KStarsData object - */ + /** + * Constructor. + * + * Defines constants needed by findPosition(). Sets Ecliptic coordinates appropriate for J2000. + * + * @param kd pointer to KStarsData object + */ KSSun(); KSSun *clone() const override; SkyObject::UID getUID() const override; - /** Destructor (empty) - */ - ~KSSun() override {} + virtual ~KSSun() override = default; - /** Read orbital data from disk - *@note reimplemented from KSPlanet - *@note we actually read Earth's orbital data. The Sun's geocentric - *ecliptic coordinates are by definition exactly the opposite of the - *Earth's heliocentric ecliptic coordinates. - */ + /** + * Read orbital data from disk + * + * @note reimplemented from KSPlanet + * @note we actually read Earth's orbital data. The Sun's geocentric + * ecliptic coordinates are by definition exactly the opposite of the + * Earth's heliocentric ecliptic coordinates. + */ bool loadData() override; protected: - /** Determine geocentric RA, Dec coordinates for the Epoch given in the argument. - *@p Epoch current Julian Date - *@p Earth pointer to earth object - */ + /** + * Determine geocentric RA, Dec coordinates for the Epoch given in the argument. + * + * @p Epoch current Julian Date + * @p Earth pointer to earth object + */ bool findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth = nullptr) override; private: void findMagnitude(const KSNumbers *) override; }; -long double equinox(int year, int d, int m, int angle); -#endif +long double equinox(int year, int d, int m, int angle); diff --git a/kstars/skyobjects/planetmoons.cpp b/kstars/skyobjects/planetmoons.cpp index ae4b8cad9..6f356130a 100644 --- a/kstars/skyobjects/planetmoons.cpp +++ b/kstars/skyobjects/planetmoons.cpp @@ -1,46 +1,43 @@ /*************************************************************************** planetmoons.cpp - description ------------------- begin : Sat Mar 13 2009 : by Vipul Kumar Singh, Médéric Boquien email : vipulkrsingh@gmail.com, mboquien@free.fr ***************************************************************************/ /*************************************************************************** * * * 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 - #include "planetmoons.h" + #include "ksnumbers.h" #include "ksplanetbase.h" #include "kssun.h" #include "trailobject.h" -PlanetMoons::PlanetMoons() -{ -} +#include PlanetMoons::~PlanetMoons() { qDeleteAll(Moon); } QString PlanetMoons::name(int id) const { return Moon[id]->translatedName(); } void PlanetMoons::EquatorialToHorizontal(const dms *LST, const dms *lat) { int nmoons = nMoons(); for (int i = 0; i < nmoons; ++i) moon(i)->EquatorialToHorizontal(LST, lat); } diff --git a/kstars/skyobjects/planetmoons.h b/kstars/skyobjects/planetmoons.h index 73a8326db..2387bb094 100644 --- a/kstars/skyobjects/planetmoons.h +++ b/kstars/skyobjects/planetmoons.h @@ -1,131 +1,128 @@ /*************************************************************************** planetmoons.h - description ------------------- begin : Sat Mar 13 2009 : by Vipul Kumar Singh, Médéric Boquien email : vipulkrsingh@gmail.com, mboquien@free.fr ***************************************************************************/ /*************************************************************************** * * * 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 PLANETMOONS_H_ -#define PLANETMOONS_H_ +#pragma once #include #include class KSNumbers; class KSPlanetBase; class KSSun; class TrailObject; class dms; /** - *@class PlanetMoons - *Implements the moons of a planet. - * - *TODO: make the moons SkyObjects, rather than just points. - * - *@author Vipul Kumar Singh - *@version 1.0 - */ + * @class PlanetMoons + * + * Implements the moons of a planet. + * + * TODO: make the moons SkyObjects, rather than just points. + * + * @author Vipul Kumar Singh + * @version 1.0 + */ class PlanetMoons { public: /** - *Constructor. Assign the name of each moon, - *and initialize their XYZ positions to zero. - */ - PlanetMoons(); + * Constructor. Assign the name of each moon, + * and initialize their XYZ positions to zero. + */ + PlanetMoons() = default; - /** - *Destructor. Delete moon objects. - */ + /** Destructor. Delete moon objects */ virtual ~PlanetMoons(); /** - *@return pointer to a moon given the ID number. - *@param id which moon? - */ + * @return pointer to a moon given the ID number. + * @param id which moon? + */ inline TrailObject *moon(int id) { return Moon[id]; } /** - *@return the name of a moon. - *@param id which moon? - */ + * @return the name of a moon. + * @param id which moon? + */ QString name(int id) const; /** - *Convert the RA,Dec coordinates of each moon to Az,Alt - *@param LSTh pointer to the current local sidereal time - *@param lat pointer to the geographic latitude - */ + * Convert the RA,Dec coordinates of each moon to Az,Alt + * + * @param LSTh pointer to the current local sidereal time + * @param lat pointer to the geographic latitude + */ void EquatorialToHorizontal(const dms *LSTh, const dms *lat); /** - *@short Find the positions of each Moon, relative to the planet. - *We use an XYZ coordinate system, centered on the planet, - *where the X-axis corresponds to the planet's Equator, - *the Y-Axis is parallel to the planet's Poles, and the - *Z-axis points along the line joining the Earth and - *the planet. Once the XYZ positions are known, this - *function also computes the RA, Dec positions of each - *Moon, and sets the inFront bool variable to indicate - *whether the Moon is nearer to us than the planet or not - *(this information is used to determine whether the - *Moon should be drawn on top of the planet, or vice versa). - * - *@param num pointer to the KSNumbers object describing - *the date/time at which to find the positions. - *@param pla pointer to the planet object - *@param sunptr pointer to the Sun object - */ + * @short Find the positions of each Moon, relative to the planet. + * + * We use an XYZ coordinate system, centered on the planet, + * where the X-axis corresponds to the planet's Equator, + * the Y-Axis is parallel to the planet's Poles, and the + * Z-axis points along the line joining the Earth and + * the planet. Once the XYZ positions are known, this + * function also computes the RA, Dec positions of each + * Moon, and sets the inFront bool variable to indicate + * whether the Moon is nearer to us than the planet or not + * (this information is used to determine whether the + * Moon should be drawn on top of the planet, or vice versa). + * + * @param num pointer to the KSNumbers object describing + * the date/time at which to find the positions. + * @param pla pointer to the planet object + * @param sunptr pointer to the Sun object + */ virtual void findPosition(const KSNumbers *num, const KSPlanetBase *pla, const KSSun *sunptr) = 0; /** - *@return true if the Moon is nearer to Earth than Saturn. - *@param id which moon? 0=Mimas,1=Enceladus,2=Tethys,3=Dione,4=Rhea,5=Titan,6=Hyperion,7=Lapetus - */ + * @return true if the Moon is nearer to Earth than Saturn. + * @param id which moon? 0=Mimas,1=Enceladus,2=Tethys,3=Dione,4=Rhea,5=Titan,6=Hyperion,7=Lapetus + */ inline bool inFront(int id) const { return InFront[id]; } /** - *@return the X-coordinate in the planet-centered coord. system. - *@param i which moon? - */ + * @return the X-coordinate in the planet-centered coord. system. + * @param i which moon? + */ double x(int i) const { return XP[i]; } /** - *@return the Y-coordinate in the planet-centered coord. system. - *@param i which moon? - */ + * @return the Y-coordinate in the planet-centered coord. system. + * @param i which moon? + */ double y(int i) const { return YP[i]; } /** - *@return the Z-coordinate in the Planet-centered coord. system. - *@param i which moon? - */ + * @return the Z-coordinate in the Planet-centered coord. system. + * @param i which moon? + */ double z(int i) const { return ZP[i]; } - /** - *@return the number of moons around the planet - */ + /** @return the number of moons around the planet */ int nMoons() const { return Moon.size(); } protected: QVector Moon; QVector InFront; //the rectangular position, relative to the planet. X-axis is equator of the planet; units are planet Radius QVector XP, YP, ZP; private: PlanetMoons(const PlanetMoons &); PlanetMoons &operator=(const PlanetMoons &); }; -#endif diff --git a/kstars/skyobjects/satellite.cpp b/kstars/skyobjects/satellite.cpp index 197e73cd3..e4ee7489b 100644 --- a/kstars/skyobjects/satellite.cpp +++ b/kstars/skyobjects/satellite.cpp @@ -1,1350 +1,1346 @@ /*************************************************************************** satellite.cpp - K Desktop Planetarium ------------------- begin : Tue 02 Mar 2011 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 "satellite.h" #include "ksplanetbase.h" #ifndef KSTARS_LITE #include "kspopupmenu.h" #endif #include "kstarsdata.h" #include "kssun.h" #include "Options.h" #include "skymapcomposite.h" #include #include #include // Define some constants // WGS-72 constants #define RADIUSEARTHKM 6378.135 // Earth radius (km) #define XKE 0.07436691613317 // 60.0 / sqrt(RADIUSEARTHKM^3/MU) #define J2 0.001082616 // The second gravitational zonal harmonic of the Earth #define J4 -0.00000165597 // The fourth gravitational zonal harmonic of the Earth #define J3OJ2 -2.34506972e-3 // J3 / J2 // Mathematical constants #define TWOPI 6.2831853071795864769 // 2*PI #define PIO2 1.5707963267948966192 // PI/2 #define X2O3 .66666666666666666667 // 2/3 #define DEG2RAD 1.745329251994330e-2 // Deg -> Rad // Other constants #define MINPD 1440 // Minutes per day #define MEANALT 0.84 // Mean altitude (km) #define SR 6.96000e5 // Solar radius - km (IAU 76) #define AU 1.49597870691e8 // Astronomical unit - km (IAU 76) #define XPDOTP 229.1831180523293 // 1440.0 / (2.0 * pi) #define SS 1.0122292801892716288 // Parameter for the SGP4 density function #define QZMS2T 1.8802791590152706439e-9 // (( 120.0 - 78.0) / RADIUSEARTHKM )^4 #define F 3.35281066474748e-3 // Flattening factor #define MFACTOR 7.292115e-5 Satellite::Satellite(const QString &name, const QString &line1, const QString &line2) { //m_name = name; m_number = line1.midRef(2, 5).toInt(); m_class = line1.at(7); m_id = line1.mid(9, 8); m_epoch = line1.midRef(18, 14).toDouble(); m_first_deriv = line1.midRef(33, 10).toDouble() / (XPDOTP * MINPD); m_second_deriv = line1.midRef(44, 6).toDouble() * (1.0e-5 / pow(10.0, line1.midRef(51, 1).toDouble())) / (XPDOTP * MINPD * MINPD); m_bstar = line1.midRef(53, 6).toDouble() * 1.0e-5 / pow(10.0, line1.midRef(60, 1).toDouble()); m_ephem_type = line1.midRef(62, 1).toInt(); m_elem_number = line1.midRef(64, 4).toInt(); m_inclination = line2.midRef(8, 8).toDouble() * DEG2RAD; m_ra = line2.midRef(17, 8).toDouble() * DEG2RAD; m_eccentricity = line2.midRef(26, 7).toDouble() * 1.0e-7; m_arg_perigee = line2.midRef(34, 8).toDouble() * DEG2RAD; m_mean_anomaly = line2.midRef(43, 8).toDouble() * DEG2RAD; m_mean_motion = line2.midRef(52, 11).toDouble() * TWOPI / MINPD; m_nb_revolution = line2.midRef(63, 5).toInt(); setName(name); setName2(name); setLongName(name + " (" + m_id + ')'); setType(SkyObject::SATELLITE); setMag(0.0); m_is_selected = Options::selectedSatellites().contains(name); // Convert TLE epoch to Julian date double day = modf(m_epoch * 1.e-3, &m_epoch_year) * 1.e3; if (m_epoch_year < 57.) m_epoch_year += 2000.; else m_epoch_year += 1900.; double year = m_epoch_year - 1.; long i = year / 100; long A = i; i = A / 4; long B = 2 - A + i; i = 365.25 * year; i += 30.6001 * 14; m_tle_jd = i + 1720994.5 + B + day; init(); } Satellite *Satellite::clone() const { Q_ASSERT(typeid(this) == typeid(static_cast(this))); // Ensure we are not slicing a derived class return new Satellite(*this); } -Satellite::~Satellite() -{ -} - void Satellite::init() { double ao, cosio, sinio, cosio2, omeosq, posq, rp, rteosq, eccsq, con42, cnodm, snodm, cosim, sinim, cosomm, sinomm, cc1sq, cc2, cc3, coef, coef1, cosio4, day, em, emsq, eeta, etasq, gam, inclm, nm, perige, pinvsq, psisq, qzms24, rtemsq, s1, s2, s3, s4, s5, s6, s7, sfour, ss1(0), ss2(0), ss3(0), ss4(0), ss5(0), ss6(0), ss7(0), sz1(0), sz2(0), sz3(0), sz11(0), sz12(0), sz13(0), sz21(0), sz22(0), sz23(0), sz31(0), sz32(0), sz33(0), tc, temp, temp1, temp2, temp3, tsi, xpidot, xhdot1, z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33, ak, d1, del, adel, po, ds70, ts70, tfrac, c1, thgr70, fk5r, c1p2p; // double dndt; // Init near earth variables isimp = false; aycof = 0.; con41 = 0.; cc1 = 0.; cc4 = 0.; cc5 = 0.; d2 = 0.; d3 = 0.; d4 = 0.; delmo = 0.; eta = 0.; argpdot = 0.; omgcof = 0.; sinmao = 0.; t = 0.; t2cof = 0.; t3cof = 0.; t4cof = 0.; t5cof = 0.; x1mth2 = 0.; x7thm1 = 0.; mdot = 0.; nodedot = 0.; xlcof = 0.; xmcof = 0.; nodecf = 0.; // Init deep space variables irez = 0; d2201 = 0.; d2211 = 0.; d3210 = 0.; d3222 = 0.; d4410 = 0.; d4422 = 0.; d5220 = 0.; d5232 = 0.; d5421 = 0.; d5433 = 0.; dedt = 0.; del1 = 0.; del2 = 0.; del3 = 0.; didt = 0.; dmdt = 0.; dnodt = 0.; domdt = 0.; e3 = 0.; ee2 = 0.; peo = 0.; pgho = 0.; pho = 0.; pinco = 0.; plo = 0.; se2 = 0.; se3 = 0.; sgh2 = 0.; sgh3 = 0.; sgh4 = 0.; sh2 = 0.; sh3 = 0.; si2 = 0.; si3 = 0.; sl2 = 0.; sl3 = 0.; sl4 = 0.; gsto = 0.; xfact = 0.; xgh2 = 0.; xgh3 = 0.; xgh4 = 0.; xh2 = 0.; xh3 = 0.; xi2 = 0.; xi3 = 0.; xl2 = 0.; xl3 = 0.; xl4 = 0.; xlamo = 0.; zmol = 0.; zmos = 0.; atime = 0.; xli = 0.; xni = 0.; method = 'n'; m_is_visible = false; // Divisor for divide by zero check on inclination const double temp4 = 1.5e-12; /*----- Initializes variables for sgp4 -----*/ // Calculate auxillary epoch quantities eccsq = m_eccentricity * m_eccentricity; omeosq = 1.0 - eccsq; rteosq = sqrt(omeosq); cosio = cos(m_inclination); cosio2 = cosio * cosio; // Un-kozai the mean motion ak = pow(XKE / m_mean_motion, X2O3); d1 = 0.75 * J2 * (3.0 * cosio2 - 1.0) / (rteosq * omeosq); del = d1 / (ak * ak); adel = ak * (1.0 - del * del - del * (1.0 / 3.0 + 134.0 * del * del / 81.0)); del = d1 / (adel * adel); m_mean_motion = m_mean_motion / (1.0 + del); ao = pow(XKE / m_mean_motion, X2O3); sinio = sin(m_inclination); po = ao * omeosq; con42 = 1.0 - 5.0 * cosio2; con41 = -con42 - (2.0 * cosio2); posq = po * po; rp = ao * (1.0 - m_eccentricity); method = 'n'; // Find sidereal time ts70 = m_tle_jd - 2433281.5 - 7305.0; ds70 = floor(ts70 + 1.0e-8); tfrac = ts70 - ds70; // find greenwich location at epoch c1 = 1.72027916940703639e-2; thgr70 = 1.7321343856509374; fk5r = 5.07551419432269442e-15; c1p2p = c1 + TWOPI; gsto = fmod(thgr70 + c1 * ds70 + c1p2p * tfrac + ts70 * ts70 * fk5r, TWOPI); if (gsto < 0.0) gsto = gsto + TWOPI; if ((omeosq >= 0.0) || (m_mean_motion >= 0.0)) { if (rp < (220.0 / RADIUSEARTHKM + 1.0)) isimp = true; sfour = SS; qzms24 = QZMS2T; perige = (rp - 1.0) * RADIUSEARTHKM; // For perigees below 156 km, s and qoms2t are altered if (perige < 156.0) { sfour = perige - 78.0; if (perige < 98.0) sfour = 20.0; qzms24 = pow(((120.0 - sfour) / RADIUSEARTHKM), 4.0); sfour = sfour / RADIUSEARTHKM + 1.0; } pinvsq = 1.0 / posq; tsi = 1.0 / (ao - sfour); eta = ao * m_eccentricity * tsi; etasq = eta * eta; eeta = m_eccentricity * eta; psisq = fabs(1.0 - etasq); coef = qzms24 * pow(tsi, 4.0); coef1 = coef / pow(psisq, 3.5); cc2 = coef1 * m_mean_motion * (ao * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + 0.375 * J2 * tsi / psisq * con41 * (8.0 + 3.0 * etasq * (8.0 + etasq))); cc1 = m_bstar * cc2; cc3 = 0.0; if (m_eccentricity > 1.0e-4) cc3 = -2.0 * coef * tsi * J3OJ2 * m_mean_motion * sinio / m_eccentricity; x1mth2 = 1.0 - cosio2; cc4 = 2.0 * m_mean_motion * coef1 * ao * omeosq * (eta * (2.0 + 0.5 * etasq) + m_eccentricity * (0.5 + 2.0 * etasq) - J2 * tsi / (ao * psisq) * (-3.0 * con41 * (1.0 - 2.0 * eeta + etasq * (1.5 - 0.5 * eeta)) + 0.75 * x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * m_arg_perigee))); cc5 = 2.0 * coef1 * ao * omeosq * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq); cosio4 = cosio2 * cosio2; temp1 = 1.5 * J2 * pinvsq * m_mean_motion; temp2 = 0.5 * temp1 * J2 * pinvsq; temp3 = -0.46875 * J4 * pinvsq * pinvsq * m_mean_motion; mdot = m_mean_motion + 0.5 * temp1 * rteosq * con41 + 0.0625 * temp2 * rteosq * (13.0 - 78.0 * cosio2 + 137.0 * cosio4); argpdot = -0.5 * temp1 * con42 + 0.0625 * temp2 * (7.0 - 114.0 * cosio2 + 395.0 * cosio4) + temp3 * (3.0 - 36.0 * cosio2 + 49.0 * cosio4); xhdot1 = -temp1 * cosio; nodedot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * cosio2) + 2.0 * temp3 * (3.0 - 7.0 * cosio2)) * cosio; xpidot = argpdot + nodedot; omgcof = m_bstar * cc3 * cos(m_arg_perigee); xmcof = 0.0; if (m_eccentricity > 1.0e-4) xmcof = -X2O3 * coef * m_bstar / eeta; nodecf = 3.5 * omeosq * xhdot1 * cc1; t2cof = 1.5 * cc1; // Do not divide by zero if (fabs(1.0 + cosio) > 1.5e-12) xlcof = -0.25 * J3OJ2 * sinio * (3.0 + 5.0 * cosio) / (1.0 + cosio); else xlcof = -0.25 * J3OJ2 * sinio * (3.0 + 5.0 * cosio) / temp4; aycof = -0.5 * J3OJ2 * sinio; delmo = pow((1.0 + eta * cos(m_mean_anomaly)), 3); sinmao = sin(m_mean_anomaly); x7thm1 = 7.0 * cosio2 - 1.0; // Deep space initialization if ((TWOPI / m_mean_motion) >= 225.0) { method = 'd'; isimp = true; tc = 0.0; inclm = m_inclination; // Init deep space common variables // Define some constants const double zes = 0.01675; const double zel = 0.05490; const double c1ss = 2.9864797e-6; const double c1l = 4.7968065e-7; const double zsinis = 0.39785416; const double zcosis = 0.91744867; const double zcosgs = 0.1945905; const double zsings = -0.98088458; int lsflg; double a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, betasq, cc, ctem, stem, x1, x2, x3, x4, x5, x6, x7, x8, xnodce, xnoi, zcosg, zcosgl, zcosh, zcoshl, zcosi, zcosil, zsing, zsingl, zsinh, zsinhl, zsini, zsinil, zx, zy; nm = m_mean_motion; em = m_eccentricity; snodm = sin(m_ra); cnodm = cos(m_ra); sinomm = sin(m_arg_perigee); cosomm = cos(m_arg_perigee); sinim = sin(m_inclination); cosim = cos(m_inclination); emsq = em * em; betasq = 1.0 - emsq; rtemsq = sqrt(betasq); // Initialize lunar solar terms peo = 0.0; pinco = 0.0; plo = 0.0; pgho = 0.0; pho = 0.0; day = m_tle_jd - 2433281.5 + 18261.5 + tc / 1440.0; xnodce = fmod(4.5236020 - 9.2422029e-4 * day, TWOPI); stem = sin(xnodce); ctem = cos(xnodce); zcosil = 0.91375164 - 0.03568096 * ctem; zsinil = sqrt(1.0 - zcosil * zcosil); zsinhl = 0.089683511 * stem / zsinil; zcoshl = sqrt(1.0 - zsinhl * zsinhl); gam = 5.8351514 + 0.0019443680 * day; zx = 0.39785416 * stem / zsinil; zy = zcoshl * ctem + 0.91744867 * zsinhl * stem; zx = atan2(zx, zy); zx = gam + zx - xnodce; zcosgl = cos(zx); zsingl = sin(zx); // Solar terms zcosg = zcosgs; zsing = zsings; zcosi = zcosis; zsini = zsinis; zcosh = cnodm; zsinh = snodm; cc = c1ss; xnoi = 1.0 / nm; for (lsflg = 1; lsflg <= 2; ++lsflg) { a1 = zcosg * zcosh + zsing * zcosi * zsinh; a3 = -zsing * zcosh + zcosg * zcosi * zsinh; a7 = -zcosg * zsinh + zsing * zcosi * zcosh; a8 = zsing * zsini; a9 = zsing * zsinh + zcosg * zcosi * zcosh; a10 = zcosg * zsini; a2 = cosim * a7 + sinim * a8; a4 = cosim * a9 + sinim * a10; a5 = -sinim * a7 + cosim * a8; a6 = -sinim * a9 + cosim * a10; x1 = a1 * cosomm + a2 * sinomm; x2 = a3 * cosomm + a4 * sinomm; x3 = -a1 * sinomm + a2 * cosomm; x4 = -a3 * sinomm + a4 * cosomm; x5 = a5 * sinomm; x6 = a6 * sinomm; x7 = a5 * cosomm; x8 = a6 * cosomm; z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3; z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4; z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4; z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * emsq; z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * emsq; z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * emsq; z11 = -6.0 * a1 * a5 + emsq * (-24.0 * x1 * x7 - 6.0 * x3 * x5); z12 = -6.0 * (a1 * a6 + a3 * a5) + emsq * (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5)); z13 = -6.0 * a3 * a6 + emsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6); z21 = 6.0 * a2 * a5 + emsq * (24.0 * x1 * x5 - 6.0 * x3 * x7); z22 = 6.0 * (a4 * a5 + a2 * a6) + emsq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8)); z23 = 6.0 * a4 * a6 + emsq * (24.0 * x2 * x6 - 6.0 * x4 * x8); z1 = z1 + z1 + betasq * z31; z2 = z2 + z2 + betasq * z32; z3 = z3 + z3 + betasq * z33; s3 = cc * xnoi; s2 = -0.5 * s3 / rtemsq; s4 = s3 * rtemsq; s1 = -15.0 * em * s4; s5 = x1 * x3 + x2 * x4; s6 = x2 * x3 + x1 * x4; s7 = x2 * x4 - x1 * x3; // Lunar terms if (lsflg == 1) { ss1 = s1; ss2 = s2; ss3 = s3; ss4 = s4; ss5 = s5; ss6 = s6; ss7 = s7; sz1 = z1; sz2 = z2; sz3 = z3; sz11 = z11; sz12 = z12; sz13 = z13; sz21 = z21; sz22 = z22; sz23 = z23; sz31 = z31; sz32 = z32; sz33 = z33; zcosg = zcosgl; zsing = zsingl; zcosi = zcosil; zsini = zsinil; zcosh = zcoshl * cnodm + zsinhl * snodm; zsinh = snodm * zcoshl - cnodm * zsinhl; cc = c1l; } } zmol = fmod(4.7199672 + 0.22997150 * day - gam, TWOPI); zmos = fmod(6.2565837 + 0.017201977 * day, TWOPI); // Solar terms se2 = 2.0 * ss1 * ss6; se3 = 2.0 * ss1 * ss7; si2 = 2.0 * ss2 * sz12; si3 = 2.0 * ss2 * (sz13 - sz11); sl2 = -2.0 * ss3 * sz2; sl3 = -2.0 * ss3 * (sz3 - sz1); sl4 = -2.0 * ss3 * (-21.0 - 9.0 * emsq) * zes; sgh2 = 2.0 * ss4 * sz32; sgh3 = 2.0 * ss4 * (sz33 - sz31); sgh4 = -18.0 * ss4 * zes; sh2 = -2.0 * ss2 * sz22; sh3 = -2.0 * ss2 * (sz23 - sz21); // Lunar terms ee2 = 2.0 * s1 * s6; e3 = 2.0 * s1 * s7; xi2 = 2.0 * s2 * z12; xi3 = 2.0 * s2 * (z13 - z11); xl2 = -2.0 * s3 * z2; xl3 = -2.0 * s3 * (z3 - z1); xl4 = -2.0 * s3 * (-21.0 - 9.0 * emsq) * zel; xgh2 = 2.0 * s4 * z32; xgh3 = 2.0 * s4 * (z33 - z31); xgh4 = -18.0 * s4 * zel; xh2 = -2.0 * s2 * z22; xh3 = -2.0 * s2 * (z23 - z21); // Apply deep space long period periodic contributions to the mean elements // double f2, f3, sinzf, zf, zm; double ses, sghl, sghs, shll, shs, sis, sls; // Define some constants const double zns = 1.19459e-5; const double znl = 1.5835218e-4; // Calculate time varying periodics // These results are never used, should we remove them? // zm = zmos; // zf = zm + 2.0 * zes * sin(zm); // sinzf = sin(zf); // f2 = 0.5 * sinzf * sinzf - 0.25; // f3 = -0.5 * sinzf * cos(zf); // ses = se2 * f2 + se3 * f3; // sis = si2 * f2 + si3 * f3; // sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; // sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; // shs = sh2 * f2 + sh3 * f3; // zm = zmol; // zf = zm + 2.0 * zel * sin(zm); // sinzf = sin(zf); // f2 = 0.5 * sinzf * sinzf - 0.25; // f3 = -0.5 * sinzf * cos(zf); // sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; // shll = xh2 * f2 + xh3 * f3; // Deep space contributions to mean motion dot due to geopotential resonance with half day and one day orbits double ainv2, aonv = 0.0, cosisq, eoc, f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543, g200, g201, g211, g300, g310, g322, g410, g422, g520, g521, g532, g533, sgs, sini2, temp, temp1, theta, xno2, emsqo; // double emo; // Define some constant const double q22 = 1.7891679e-6; const double q31 = 2.1460748e-6; const double q33 = 2.2123015e-7; const double root22 = 1.7891679e-6; const double root44 = 7.3636953e-9; const double root54 = 2.1765803e-9; const double rptim = 4.37526908801129966e-3; // this equates to 7.29211514668855e-5 rad/sec const double root32 = 3.7393792e-7; const double root52 = 1.1428639e-7; // Deep space initialization irez = 0; if ((nm < 0.0052359877) && (nm > 0.0034906585)) irez = 1; if ((nm >= 8.26e-3) && (nm <= 9.24e-3) && (em >= 0.5)) irez = 2; // Solar terms ses = ss1 * zns * ss5; sis = ss2 * zns * (sz11 + sz13); sls = -zns * ss3 * (sz1 + sz3 - 14.0 - 6.0 * emsq); sghs = ss4 * zns * (sz31 + sz33 - 6.0); shs = -zns * ss2 * (sz21 + sz23); if ((inclm < 5.2359877e-2) || (inclm > M_PI - 5.2359877e-2)) shs = 0.0; if (sinim != 0.0) shs = shs / sinim; sgs = sghs - cosim * shs; // Lunar terms dedt = ses + s1 * znl * s5; didt = sis + s2 * znl * (z11 + z13); dmdt = sls - znl * s3 * (z1 + z3 - 14.0 - 6.0 * emsq); sghl = s4 * znl * (z31 + z33 - 6.0); shll = -znl * s2 * (z21 + z23); if ((inclm < 5.2359877e-2) || (inclm > M_PI - 5.2359877e-2)) shll = 0.0; domdt = sgs + sghl; dnodt = shs; if (sinim != 0.0) { domdt = domdt - cosim / sinim * shll; dnodt = dnodt + shll / sinim; } // Calculate deep space resonance effects // Value never used // dndt = 0.0; theta = fmod(gsto + tc * rptim, TWOPI); // Initialize the resonance terms if (irez != 0) { aonv = pow(nm / XKE, X2O3); // Geopotential resonance for 12 hour orbits if (irez == 2) { cosisq = cosim * cosim; // Value never used // emo = em; em = m_eccentricity; emsqo = emsq; emsq = eccsq; eoc = em * emsq; g201 = -0.306 - (em - 0.64) * 0.440; if (em <= 0.65) { g211 = 3.616 - 13.2470 * em + 16.2900 * emsq; g310 = -19.302 + 117.3900 * em - 228.4190 * emsq + 156.5910 * eoc; g322 = -18.9068 + 109.7927 * em - 214.6334 * emsq + 146.5816 * eoc; g410 = -41.122 + 242.6940 * em - 471.0940 * emsq + 313.9530 * eoc; g422 = -146.407 + 841.8800 * em - 1629.014 * emsq + 1083.4350 * eoc; g520 = -532.114 + 3017.977 * em - 5740.032 * emsq + 3708.2760 * eoc; } else { g211 = -72.099 + 331.819 * em - 508.738 * emsq + 266.724 * eoc; g310 = -346.844 + 1582.851 * em - 2415.925 * emsq + 1246.113 * eoc; g322 = -342.585 + 1554.908 * em - 2366.899 * emsq + 1215.972 * eoc; g410 = -1052.797 + 4758.686 * em - 7193.992 * emsq + 3651.957 * eoc; g422 = -3581.690 + 16178.110 * em - 24462.770 * emsq + 12422.520 * eoc; if (em > 0.715) g520 = -5149.66 + 29936.92 * em - 54087.36 * emsq + 31324.56 * eoc; else g520 = 1464.74 - 4664.75 * em + 3763.64 * emsq; } if (em < 0.7) { g533 = -919.22770 + 4988.6100 * em - 9064.7700 * emsq + 5542.21 * eoc; g521 = -822.71072 + 4568.6173 * em - 8491.4146 * emsq + 5337.524 * eoc; g532 = -853.66600 + 4690.2500 * em - 8624.7700 * emsq + 5341.4 * eoc; } else { g533 = -37995.780 + 161616.52 * em - 229838.20 * emsq + 109377.94 * eoc; g521 = -51752.104 + 218913.95 * em - 309468.16 * emsq + 146349.42 * eoc; g532 = -40023.880 + 170470.89 * em - 242699.48 * emsq + 115605.82 * eoc; } sini2 = sinim * sinim; f220 = 0.75 * (1.0 + 2.0 * cosim + cosisq); f221 = 1.5 * sini2; f321 = 1.875 * sinim * (1.0 - 2.0 * cosim - 3.0 * cosisq); f322 = -1.875 * sinim * (1.0 + 2.0 * cosim - 3.0 * cosisq); f441 = 35.0 * sini2 * f220; f442 = 39.3750 * sini2 * sini2; f522 = 9.84375 * sinim * (sini2 * (1.0 - 2.0 * cosim - 5.0 * cosisq) + 0.33333333 * (-2.0 + 4.0 * cosim + 6.0 * cosisq)); f523 = sinim * (4.92187512 * sini2 * (-2.0 - 4.0 * cosim + 10.0 * cosisq) + 6.56250012 * (1.0 + 2.0 * cosim - 3.0 * cosisq)); f542 = 29.53125 * sinim * (2.0 - 8.0 * cosim + cosisq * (-12.0 + 8.0 * cosim + 10.0 * cosisq)); f543 = 29.53125 * sinim * (-2.0 - 8.0 * cosim + cosisq * (12.0 + 8.0 * cosim - 10.0 * cosisq)); xno2 = nm * nm; ainv2 = aonv * aonv; temp1 = 3.0 * xno2 * ainv2; temp = temp1 * root22; d2201 = temp * f220 * g201; d2211 = temp * f221 * g211; temp1 = temp1 * aonv; temp = temp1 * root32; d3210 = temp * f321 * g310; d3222 = temp * f322 * g322; temp1 = temp1 * aonv; temp = 2.0 * temp1 * root44; d4410 = temp * f441 * g410; d4422 = temp * f442 * g422; temp1 = temp1 * aonv; temp = temp1 * root52; d5220 = temp * f522 * g520; d5232 = temp * f523 * g532; temp = 2.0 * temp1 * root54; d5421 = temp * f542 * g521; d5433 = temp * f543 * g533; xlamo = fmod(m_mean_anomaly + m_ra + m_ra - theta - theta, TWOPI); xfact = mdot + dmdt + 2.0 * (nodedot + dnodt - rptim) - m_mean_motion; // Value never used // em = emo; emsq = emsqo; } if (irez == 1) { g200 = 1.0 + emsq * (-2.5 + 0.8125 * emsq); g310 = 1.0 + 2.0 * emsq; g300 = 1.0 + emsq * (-6.0 + 6.60937 * emsq); f220 = 0.75 * (1.0 + cosim) * (1.0 + cosim); f311 = 0.9375 * sinim * sinim * (1.0 + 3.0 * cosim) - 0.75 * (1.0 + cosim); f330 = 1.0 + cosim; f330 = 1.875 * f330 * f330 * f330; del1 = 3.0 * nm * nm * aonv * aonv; del2 = 2.0 * del1 * f220 * g200 * q22; del3 = 3.0 * del1 * f330 * g300 * q33 * aonv; del1 = del1 * f311 * g310 * q31 * aonv; xlamo = fmod(m_mean_anomaly + m_ra + m_arg_perigee - theta, TWOPI); xfact = mdot + xpidot - rptim + dmdt + domdt + dnodt - m_mean_motion; } xli = xlamo; xni = m_mean_motion; atime = 0.0; // Value never used // nm = m_mean_motion + dndt; } } // Set variables if not deep space if (!isimp) { cc1sq = cc1 * cc1; d2 = 4.0 * ao * tsi * cc1sq; temp = d2 * tsi * cc1 / 3.0; d3 = (17.0 * ao + sfour) * temp; d4 = 0.5 * temp * ao * tsi * (221.0 * ao + 31.0 * sfour) * cc1; t3cof = d2 + 2.0 * cc1sq; t4cof = 0.25 * (3.0 * d3 + cc1 * (12.0 * d2 + 10.0 * cc1sq)); t5cof = 0.2 * (3.0 * d4 + 12.0 * cc1 * d3 + 6.0 * d2 * d2 + 15.0 * cc1sq * (2.0 * d2 + cc1sq)); } } } int Satellite::updatePos() { KStarsData *data = KStarsData::Instance(); return sgp4((data->clock()->utc().djd() - m_tle_jd) * MINPD); } int Satellite::sgp4(double tsince) { KStarsData *data = KStarsData::Instance(); int ktr; double am, axnl, aynl, betal, cosim, cnod, cos2u, coseo1 = 0, cosi, cosip, cosisq, cossu, cosu, delm, delomg, em, ecose, el2, eo1, ep, esine, argpm, argpp, argpdf, pl, mrt = 0.0, mvt, rdotl, rl, rvdot, rvdotl, sinim, dndt, sin2u, sineo1 = 0, sini, sinip, sinsu, sinu, snod, su, t2, t3, t4, tem5, temp, temp1, temp2, tempa, tempe, templ, u, ux, uy, uz, vx, vy, vz, inclm, mm, nm, nodem, xinc, xincp, xl, xlm, mp, xmdf, xmx, xmy, nodedf, xnode, nodep, tc, sat_posx, sat_posy, sat_posz, sat_posw, sat_velx, sat_vely, sat_velz, sinlat, obs_posx, obs_posy, obs_posz, obs_posw, /*obs_velx, obs_vely, obs_velz,*/ coslat, thetageo, sintheta, costheta, c, sq, achcp, vkmpersec; // double emsq; const double temp4 = 1.5e-12; double jul_utc = data->clock()->utc().djd(); vkmpersec = RADIUSEARTHKM * XKE / 60.0; // Update for secular gravity and atmospheric drag xmdf = m_mean_anomaly + mdot * tsince; argpdf = m_arg_perigee + argpdot * tsince; nodedf = m_ra + nodedot * tsince; argpm = argpdf; mm = xmdf; t2 = tsince * tsince; nodem = nodedf + nodecf * t2; tempa = 1.0 - cc1 * tsince; tempe = m_bstar * cc4 * tsince; templ = t2cof * t2; if (!isimp) { delomg = omgcof * tsince; delm = xmcof * (pow((1.0 + eta * cos(xmdf)), 3) - delmo); temp = delomg + delm; mm = xmdf + temp; argpm = argpdf - temp; t3 = t2 * tsince; t4 = t3 * tsince; tempa = tempa - d2 * t2 - d3 * t3 - d4 * t4; tempe = tempe + m_bstar * cc5 * (sin(mm) - sinmao); templ = templ + t3cof * t3 + t4 * (t4cof + tsince * t5cof); } nm = m_mean_motion; em = m_eccentricity; inclm = m_inclination; if (method == 'd') { tc = tsince; // Deep space contributions to mean elements for perturbing third body int iretn; double delt, ft, theta, x2li, x2omi, xl, xldot, xnddt, xndt, xomi; // Define some constants const double fasx2 = 0.13130908; const double fasx4 = 2.8843198; const double fasx6 = 0.37448087; const double g22 = 5.7686396; const double g32 = 0.95240898; const double g44 = 1.8014998; const double g52 = 1.0508330; const double g54 = 4.4108898; const double rptim = 4.37526908801129966e-3; // this equates to 7.29211514668855e-5 rad/sec const double step = 720.0; const double step2 = step * step / 2; // Calculate deep space resonance effects // Value never used // dndt = 0.0; theta = fmod(gsto + tc * rptim, TWOPI); em = em + dedt * tsince; inclm = inclm + didt * tsince; argpm = argpm + domdt * tsince; nodem = nodem + dnodt * tsince; mm = mm + dmdt * tsince; // Update resonances : numerical (euler-maclaurin) integration ft = 0.0; if (irez != 0) { if ((atime == 0.0) || (tsince * atime <= 0.0) || (fabs(tsince) < fabs(atime))) { atime = 0.0; xni = m_mean_motion; xli = xlamo; } if (tsince > 0.0) delt = step; else delt = -step; iretn = 381; // added for do loop while (iretn == 381) { // Near - synchronous resonance terms if (irez != 2) { xndt = del1 * sin(xli - fasx2) + del2 * sin(2.0 * (xli - fasx4)) + del3 * sin(3.0 * (xli - fasx6)); xldot = xni + xfact; xnddt = del1 * cos(xli - fasx2) + 2.0 * del2 * cos(2.0 * (xli - fasx4)) + 3.0 * del3 * cos(3.0 * (xli - fasx6)); xnddt = xnddt * xldot; } else { // Near - half-day resonance terms xomi = m_arg_perigee + argpdot * atime; x2omi = xomi + xomi; x2li = xli + xli; xndt = d2201 * sin(x2omi + xli - g22) + d2211 * sin(xli - g22) + d3210 * sin(xomi + xli - g32) + d3222 * sin(-xomi + xli - g32) + d4410 * sin(x2omi + x2li - g44) + d4422 * sin(x2li - g44) + d5220 * sin(xomi + xli - g52) + d5232 * sin(-xomi + xli - g52) + d5421 * sin(xomi + x2li - g54) + d5433 * sin(-xomi + x2li - g54); xldot = xni + xfact; xnddt = d2201 * cos(x2omi + xli - g22) + d2211 * cos(xli - g22) + d3210 * cos(xomi + xli - g32) + d3222 * cos(-xomi + xli - g32) + d5220 * cos(xomi + xli - g52) + d5232 * cos(-xomi + xli - g52) + 2.0 * (d4410 * cos(x2omi + x2li - g44) + d4422 * cos(x2li - g44) + d5421 * cos(xomi + x2li - g54) + d5433 * cos(-xomi + x2li - g54)); xnddt = xnddt * xldot; } if (fabs(tsince - atime) >= step) { iretn = 381; } else { ft = tsince - atime; iretn = 0; } if (iretn == 381) { xli = xli + xldot * delt + xndt * step2; xni = xni + xndt * delt + xnddt * step2; atime = atime + delt; } } nm = xni + xndt * ft + xnddt * ft * ft * 0.5; xl = xli + xldot * ft + xndt * ft * ft * 0.5; if (irez != 1) { mm = xl - 2.0 * nodem + 2.0 * theta; dndt = nm - m_mean_motion; } else { mm = xl - nodem - argpm + theta; dndt = nm - m_mean_motion; } nm = m_mean_motion + dndt; } } if (nm <= 0.0) { qDebug() << "Mean motion less than 0.0"; return (2); } am = pow((XKE / nm), X2O3) * tempa * tempa; nm = XKE / pow(am, 1.5); em = em - tempe; if ((em >= 1.0) || (em < -0.001)) { qDebug() << "Eccentricity >= 1.0 or < -0.001"; return (1); } if (em < 1.0e-6) em = 1.0e-6; mm = mm + m_mean_motion * templ; xlm = mm + argpm + nodem; // Value never used // emsq = em * em; // Value never used // temp = 1.0 - emsq; nodem = fmod(nodem, TWOPI); argpm = fmod(argpm, TWOPI); xlm = fmod(xlm, TWOPI); mm = fmod(xlm - argpm - nodem, TWOPI); // Compute extra mean quantities sinim = sin(inclm); cosim = cos(inclm); // Add lunar-solar periodics ep = em; xincp = inclm; argpp = argpm; nodep = nodem; mp = mm; sinip = sinim; cosip = cosim; if (method == 'd') { double alfdp, betdp, cosip, cosop, dalf, dbet, dls, f2, f3, pe, pgh, ph, pinc, pl, sel, ses, sghl, sghs, shll, shs, sil, sinip, sinop, sinzf, sis, sll, sls, xls, xnoh, zf, zm; // Define some constants const double zns = 1.19459e-5; const double zes = 0.01675; const double znl = 1.5835218e-4; const double zel = 0.05490; // Calculate time varying periodics zm = zmos + zns * tsince; zf = zm + 2.0 * zes * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); ses = se2 * f2 + se3 * f3; sis = si2 * f2 + si3 * f3; sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; shs = sh2 * f2 + sh3 * f3; zm = zmol + znl * tsince; zf = zm + 2.0 * zel * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); sel = ee2 * f2 + e3 * f3; sil = xi2 * f2 + xi3 * f3; sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf; sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; shll = xh2 * f2 + xh3 * f3; pe = ses + sel; pinc = sis + sil; pl = sls + sll; pgh = sghs + sghl; ph = shs + shll; pe = pe - peo; pinc = pinc - pinco; pl = pl - plo; pgh = pgh - pgho; ph = ph - pho; xincp = xincp + pinc; ep = ep + pe; sinip = sin(xincp); cosip = cos(xincp); // Apply periodics directly if (xincp >= 0.2) { ph = ph / sinip; pgh = pgh - cosip * ph; argpp = argpp + pgh; nodep = nodep + ph; mp = mp + pl; } else { // Apply periodics with lyddane modification sinop = sin(nodep); cosop = cos(nodep); alfdp = sinip * sinop; betdp = sinip * cosop; dalf = ph * cosop + pinc * cosip * sinop; dbet = -ph * sinop + pinc * cosip * cosop; alfdp = alfdp + dalf; betdp = betdp + dbet; nodep = fmod(nodep, TWOPI); if (nodep < 0.0) nodep += TWOPI; xls = mp + argpp + cosip * nodep; dls = pl + pgh - pinc * nodep * sinip; xls = xls + dls; xnoh = nodep; nodep = atan2(alfdp, betdp); if ((nodep < 0.0)) nodep += TWOPI; if (fabs(xnoh - nodep) > M_PI) { if (nodep < xnoh) nodep += TWOPI; else nodep -= TWOPI; } mp = mp + pl; argpp = xls - mp - cosip * nodep; } if (xincp < 0.0) { xincp = -xincp; nodep = nodep + M_PI; argpp = argpp - M_PI; } if ((ep < 0.0) || (ep > 1.0)) { qDebug() << "Eccentricity < 0.0 or > 1.0"; return (3); } } // Long period periodics if (method == 'd') { sinip = sin(xincp); cosip = cos(xincp); aycof = -0.5 * J3OJ2 * sinip; if (fabs(cosip + 1.0) > 1.5e-12) xlcof = -0.25 * J3OJ2 * sinip * (3.0 + 5.0 * cosip) / (1.0 + cosip); else xlcof = -0.25 * J3OJ2 * sinip * (3.0 + 5.0 * cosip) / temp4; } axnl = ep * cos(argpp); temp = 1.0 / (am * (1.0 - ep * ep)); aynl = ep * sin(argpp) + temp * aycof; xl = mp + argpp + nodep + temp * xlcof * axnl; // Solve kepler's equation u = fmod(xl - nodep, TWOPI); eo1 = u; tem5 = 9999.9; ktr = 1; while ((fabs(tem5) >= 1.0e-12) && (ktr <= 10)) { sineo1 = sin(eo1); coseo1 = cos(eo1); tem5 = 1.0 - coseo1 * axnl - sineo1 * aynl; tem5 = (u - aynl * coseo1 + axnl * sineo1 - eo1) / tem5; if (fabs(tem5) >= 0.95) tem5 = tem5 > 0.0 ? 0.95 : -0.95; eo1 = eo1 + tem5; ktr = ktr + 1; } // Short period preliminary quantities ecose = axnl * coseo1 + aynl * sineo1; esine = axnl * sineo1 - aynl * coseo1; el2 = axnl * axnl + aynl * aynl; pl = am * (1.0 - el2); if (pl < 0.0) { qDebug() << "Semi-latus rectum < 0.0"; return (4); } rl = am * (1.0 - ecose); rdotl = sqrt(am) * esine / rl; rvdotl = sqrt(pl) / rl; betal = sqrt(1.0 - el2); temp = esine / (1.0 + betal); sinu = am / rl * (sineo1 - aynl - axnl * temp); cosu = am / rl * (coseo1 - axnl + aynl * temp); su = atan2(sinu, cosu); sin2u = (cosu + cosu) * sinu; cos2u = 1.0 - 2.0 * sinu * sinu; temp = 1.0 / pl; temp1 = 0.5 * J2 * temp; temp2 = temp1 * temp; // Update for short period periodics if (method == 'd') { cosisq = cosip * cosip; con41 = 3.0 * cosisq - 1.0; x1mth2 = 1.0 - cosisq; x7thm1 = 7.0 * cosisq - 1.0; } mrt = rl * (1.0 - 1.5 * temp2 * betal * con41) + 0.5 * temp1 * x1mth2 * cos2u; su = su - 0.25 * temp2 * x7thm1 * sin2u; xnode = nodep + 1.5 * temp2 * cosip * sin2u; xinc = xincp + 1.5 * temp2 * cosip * sinip * cos2u; mvt = rdotl - nm * temp1 * x1mth2 * sin2u / XKE; rvdot = rvdotl + nm * temp1 * (x1mth2 * cos2u + 1.5 * con41) / XKE; // Orientation vectors sinsu = sin(su); cossu = cos(su); snod = sin(xnode); cnod = cos(xnode); sini = sin(xinc); cosi = cos(xinc); xmx = -snod * cosi; xmy = cnod * cosi; ux = xmx * sinsu + cnod * cossu; uy = xmy * sinsu + snod * cossu; uz = sini * sinsu; vx = xmx * cossu - cnod * sinsu; vy = xmy * cossu - snod * sinsu; vz = sini * cossu; // Position and velocity (in km and km/sec) sat_posx = (mrt * ux) * RADIUSEARTHKM; sat_posy = (mrt * uy) * RADIUSEARTHKM; sat_posz = (mrt * uz) * RADIUSEARTHKM; sat_posw = sqrt(sat_posx * sat_posx + sat_posy * sat_posy + sat_posz * sat_posz); sat_velx = (mvt * ux + rvdot * vx) * vkmpersec; sat_vely = (mvt * uy + rvdot * vy) * vkmpersec; sat_velz = (mvt * uz + rvdot * vz) * vkmpersec; m_velocity = sqrt(sat_velx * sat_velx + sat_vely * sat_vely + sat_velz * sat_velz); // printf("tsince=%.15f\n", tsince); // printf("sat_posx=%.15f\n", sat_posx); // printf("sat_posy=%.15f\n", sat_posy); // printf("sat_posz=%.15f\n", sat_posz); // printf("sat_velx=%.15f\n", sat_velx); // printf("sat_vely=%.15f\n", sat_vely); // printf("sat_velz=%.15f\n", sat_velz); if (mrt < 1.0) { qDebug() << "Satellite has decayed"; return (6); } // Observer ECI position and velocity sinlat = sin(data->geo()->lat()->radians()); coslat = cos(data->geo()->lat()->radians()); thetageo = data->geo()->LMST(jul_utc); sintheta = sin(thetageo); costheta = cos(thetageo); c = 1.0 / sqrt(1.0 + F * (F - 2.0) * sinlat * sinlat); sq = (1.0 - F) * (1.0 - F) * c; achcp = (RADIUSEARTHKM * c + MEANALT) * coslat; obs_posx = achcp * costheta; obs_posy = achcp * sintheta; obs_posz = (RADIUSEARTHKM * sq + MEANALT) * sinlat; obs_posw = sqrt(obs_posx * obs_posx + obs_posy * sat_posy + obs_posz * obs_posz); /*obs_velx = -MFACTOR * obs_posy; obs_vely = MFACTOR * obs_posx; obs_velz = 0.;*/ m_altitude = sat_posw - obs_posw + MEANALT; // Az and Dec double range_posx = sat_posx - obs_posx; double range_posy = sat_posy - obs_posy; double range_posz = sat_posz - obs_posz; m_range = sqrt(range_posx * range_posx + range_posy * range_posy + range_posz * range_posz); // double range_velx = sat_velx - obs_velx; // double range_vely = sat_velx - obs_vely; // double range_velz = sat_velx - obs_velz; double top_s = sinlat * costheta * range_posx + sinlat * sintheta * range_posy - coslat * range_posz; double top_e = -sintheta * range_posx + costheta * range_posy; double top_z = coslat * costheta * range_posx + coslat * sintheta * range_posy + sinlat * range_posz; double azimut = atan(-top_e / top_s); if (top_s > 0.) azimut += M_PI; if (azimut < 0.) azimut += TWOPI; double elevation = arcSin(top_z / m_range); // printf("azimut=%.15f\n\r", azimut / DEG2RAD); // printf("elevation=%.15f\n\r", elevation / DEG2RAD); setAz(azimut / DEG2RAD); setAlt(elevation / DEG2RAD); HorizontalToEquatorial(data->lst(), data->geo()->lat()); // is the satellite visible ? // Find ECI coordinates of the sun double mjd, year, T, M, L, e, C, O, Lsa, nu, R, eps; mjd = jul_utc - 2415020.0; year = 1900.0 + mjd / 365.25; T = (mjd + deltaET(year) / (MINPD * 60.0)) / 36525.0; M = DEG2RAD * (Modulus(358.47583 + Modulus(35999.04975 * T, 360.0) - (0.000150 + 0.0000033 * T) * T * T, 360.0)); L = DEG2RAD * (Modulus(279.69668 + Modulus(36000.76892 * T, 360.0) + 0.0003025 * T * T, 360.0)); e = 0.01675104 - (0.0000418 + 0.000000126 * T) * T; C = DEG2RAD * ((1.919460 - (0.004789 + 0.000014 * T) * T) * sin(M) + (0.020094 - 0.000100 * T) * sin(2 * M) + 0.000293 * sin(3 * M)); O = DEG2RAD * (Modulus(259.18 - 1934.142 * T, 360.0)); Lsa = Modulus(L + C - DEG2RAD * (0.00569 - 0.00479 * sin(O)), TWOPI); nu = Modulus(M + C, TWOPI); R = 1.0000002 * (1.0 - e * e) / (1.0 + e * cos(nu)); eps = DEG2RAD * (23.452294 - (0.0130125 + (0.00000164 - 0.000000503 * T) * T) * T + 0.00256 * cos(O)); R = AU * R; double sun_posx = R * cos(Lsa); double sun_posy = R * sin(Lsa) * cos(eps); double sun_posz = R * sin(Lsa) * sin(eps); double sun_posw = R; // Calculates satellite's eclipse status and depth double sd_sun, sd_earth, delta, depth; // Determine partial eclipse sd_earth = arcSin(RADIUSEARTHKM / sat_posw); double rho_x = sun_posx - sat_posx; double rho_y = sun_posy - sat_posy; double rho_z = sun_posz - sat_posz; double rho_w = sqrt(rho_x * rho_x + rho_y * rho_y + rho_z * rho_z); sd_sun = arcSin(SR / rho_w); double earth_x = -1.0 * sat_posx; double earth_y = -1.0 * sat_posy; double earth_z = -1.0 * sat_posz; double earth_w = sat_posw; delta = PIO2 - arcSin((sun_posx * earth_x + sun_posy * earth_y + sun_posz * earth_z) / (sun_posw * earth_w)); depth = sd_earth - sd_sun - delta; KSSun *sun = (KSSun *)data->skyComposite()->findByName("Sun"); m_is_eclipsed = sd_earth >= sd_sun && depth >= 0; m_is_visible = !m_is_eclipsed && sun->alt().Degrees() <= -12.0 && elevation >= 0.0; return (0); } QString Satellite::sgp4ErrorString(int code) { switch (code) { case 0: return i18n("Success"); case 1: case 3: return i18n("Eccentricity >= 1.0 or < -0.001"); case 2: return i18n("Mean motion less than 0.0"); case 4: return i18n("Semi-latus rectum < 0.0"); case 6: return i18n("Satellite has decayed"); default: return i18n("Unknown error"); } } double Satellite::arcSin(double arg) { if (fabs(arg) >= 1.) if (arg > 0.) return PIO2; else if (arg < 0.) return -PIO2; else return 0.; else return (atan(arg / sqrt(1. - arg * arg))); } double Satellite::deltaET(double year) { double delta_et; delta_et = 26.465 + 0.747622 * (year - 1950) + 1.886913 * sin(TWOPI * (year - 1975) / 33); return delta_et; } double Satellite::Modulus(double arg1, double arg2) { int i; double ret_val; ret_val = arg1; i = ret_val / arg2; ret_val -= i * arg2; if (ret_val < 0.0) ret_val += arg2; return ret_val; } bool Satellite::isVisible() { return m_is_visible; } bool Satellite::selected() { return m_is_selected; } void Satellite::setSelected(bool selected) { m_is_selected = selected; } void Satellite::initPopupMenu(KSPopupMenu *pmenu) { #ifndef KSTARS_LITE pmenu->createSatelliteMenu(this); #else Q_UNUSED(pmenu); #endif } double Satellite::velocity() { return m_velocity; } double Satellite::altitude() { return m_altitude; } double Satellite::range() { return m_range; } QString Satellite::id() { return m_id; } diff --git a/kstars/skyobjects/satellite.h b/kstars/skyobjects/satellite.h index 4a06d67c1..4e59977fb 100644 --- a/kstars/skyobjects/satellite.h +++ b/kstars/skyobjects/satellite.h @@ -1,175 +1,175 @@ /*************************************************************************** satellite.h - K Desktop Planetarium ------------------- begin : Tue 02 Mar 2011 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "skyobject.h" #include class KSPopupMenu; /** * @class Satellite * Represents an artificial satellites. * * @author Jérôme SONRIER * @version 0.1 */ class Satellite : public SkyObject { public: /** @short Constructor */ Satellite(const QString &name, const QString &line1, const QString &line2); /** * @return a clone of this object * @note See SkyObject::clone() */ Satellite *clone() const override; /** @short Destructor */ - ~Satellite() override; + virtual ~Satellite() override = default; /** @short Update satellite position */ int updatePos(); /** * @return True if the satellite is visible (above horizon, in the sunlight and sun at least 12° under horizon) */ bool isVisible(); /** @return True if the satellite is selected */ bool selected(); /** @short Select or not the satellite */ void setSelected(bool selected); /** @return Satellite velocity in km/s */ double velocity(); /** @return Satellite altitude in km */ double altitude(); /** @return Satellite range from observer in km */ double range(); /** @return Satellite international designator */ QString id(); /** * @brief sgp4ErrorString Get error string associated with sgp4 calculation failure * @param code error code as returned from sgp4() function * @return error string */ QString sgp4ErrorString(int code); void initPopupMenu(KSPopupMenu *pmenu) override; private: /** @short Compute non time dependant parameters */ void init(); /** @short Compute satellite position */ int sgp4(double tsince); /** @return Arcsine of the argument */ double arcSin(double arg); /** * Provides the difference between UT (approximately the same as UTC) * and ET (now referred to as TDT). * This function is based on a least squares fit of data from 1950 * to 1991 and will need to be updated periodically. */ double deltaET(double year); /** @return arg1 mod arg2 */ double Modulus(double arg1, double arg2); // TLE /// Satellite Number int m_number { 0 }; /// Security Classification QChar m_class; /// International Designator QString m_id; /// Epoch Year double m_epoch_year { 0 }; /// Epoch (Day of the year and fractional portion of the day) double m_epoch { 0 }; /// First Time Derivative of the Mean Motion double m_first_deriv { 0 }; /// Second Time Derivative of Mean Motion double m_second_deriv { 0 }; /// BSTAR drag term (decimal point assumed) double m_bstar { 0 }; /// Ephemeris type int m_ephem_type { 0 }; /// Element number int m_elem_number { 0 }; /// Inclination [Radians] double m_inclination { 0 }; /// Right Ascension of the Ascending Node [Radians] double m_ra { 0 }; /// Eccentricity double m_eccentricity { 0 }; /// Argument of Perigee [Radians] double m_arg_perigee { 0 }; /// Mean Anomaly [Radians] double m_mean_anomaly { 0 }; /// Mean Motion [Radians per minutes] double m_mean_motion { 0 }; /// Revolution number at epoch [Revs] int m_nb_revolution { 0 }; /// TLE epoch converted to julian date double m_tle_jd { 0 }; // Satellite /// True if the satellite is visible bool m_is_visible { false }; /// True if the satellite is in the shadow of the earth bool m_is_eclipsed { false }; /// True if the satellite is selected bool m_is_selected { false }; /// Satellite velocity in km/s double m_velocity { 0 }; /// Satellite altitude in km double m_altitude { 0 }; /// Satellite range from observer in km double m_range { 0 }; // Near Earth bool isimp { false }; double aycof { 0 }, con41 { 0 }, cc1 { 0 }, cc4 { 0 }, cc5 { 0 }, d2 { 0 }, d3 { 0 }, d4 { 0 }; double delmo { 0 }, eta { 0 }, argpdot { 0 }, omgcof { 0 }, sinmao { 0 }, t { 0 }, t2cof { 0 }; double t3cof { 0 }, t4cof { 0 }, t5cof { 0 }, x1mth2 { 0 }, x7thm1 { 0 }, mdot { 0 }; double nodedot { 0 }, xlcof { 0 }, xmcof { 0 }, nodecf { 0 }; // Deep Space int irez { 0 }; double d2201 { 0 }, d2211 { 0 }, d3210 { 0 }, d3222 { 0 }, d4410 { 0 }, d4422 { 0 }, d5220 { 0 }; double d5232 { 0 }, d5421 { 0 }, d5433 { 0 }, dedt { 0 }, del1 { 0 }, del2 { 0 }, del3 { 0 }; double didt { 0 }, dmdt { 0 }, dnodt { 0 }, domdt { 0 }, e3 { 0 }, ee2 { 0 }, peo { 0 }; double pgho { 0 }, pho { 0 }, pinco { 0 }, plo { 0 }, se2 { 0 }, se3 { 0 }, sgh2 { 0 }; double sgh3 { 0 }, sgh4 { 0 }, sh2 { 0 }, sh3 { 0 }, si2 { 0 }, si3 { 0 }, sl2 { 0 }, sl3 { 0 }; double sl4 { 0 }, gsto { 0 }, xfact { 0 }, xgh2 { 0 }, xgh3 { 0 }, xgh4 { 0 }, xh2 { 0 }; double xh3 { 0 }, xi2 { 0 }, xi3 { 0 }, xl2 { 0 }, xl3 { 0 }, xl4 { 0 }, xlamo { 0 }, zmol { 0 }; double zmos { 0 }, atime { 0 }, xli { 0 }, xni { 0 }; char method; }; diff --git a/kstars/skyobjects/satellitegroup.cpp b/kstars/skyobjects/satellitegroup.cpp index 8cf00cae4..6de6545b8 100644 --- a/kstars/skyobjects/satellitegroup.cpp +++ b/kstars/skyobjects/satellitegroup.cpp @@ -1,99 +1,95 @@ /*************************************************************************** satellitegroup.cpp - K Desktop Planetarium ------------------- begin : Tue 22 Mar 2011 copyright : (C) 2011 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 "satellitegroup.h" #include "ksutils.h" #include "kspaths.h" #include "skyobjects/satellite.h" #include SatelliteGroup::SatelliteGroup(const QString& name, const QString& tle_filename, const QUrl& update_url) { m_name = name; m_tle_file = tle_filename; m_tle_url = update_url; // Read TLE file and create satellites readTLE(); } -SatelliteGroup::~SatelliteGroup() -{ -} - void SatelliteGroup::readTLE() { QFile file; QString line1, line2; // Delete all satellites clear(); // Read TLE file if (KSUtils::openDataFile(file, m_tle_file)) { QTextStream stream(&file); while (!stream.atEnd()) { // Read satellite name QString sat_name = stream.readLine().trimmed(); line1 = stream.readLine(); line2 = stream.readLine(); // Create a new satellite and add it to the list of satellites if (line1.startsWith('1') && line2.startsWith('2')) append(new Satellite(sat_name, line1, line2)); } file.close(); } } void SatelliteGroup::updateSatellitesPos() { QMutableListIterator sats(*this); while (sats.hasNext()) { Satellite *sat = sats.next(); if (sat->selected()) { int rc = sat->updatePos(); // If position cannot be calculated, remove it from list if (rc != 0) sats.remove(); } } } QUrl SatelliteGroup::tleFilename() { // Return absolute path with "file:" before the path //return QUrl( "file:" + (KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "") + m_tle_file) ; return QUrl::fromLocalFile(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + m_tle_file); } QUrl SatelliteGroup::tleUrl() { return m_tle_url; } QString SatelliteGroup::name() { return m_name; } diff --git a/kstars/skyobjects/satellitegroup.h b/kstars/skyobjects/satellitegroup.h index 75580e459..7615d9402 100644 --- a/kstars/skyobjects/satellitegroup.h +++ b/kstars/skyobjects/satellitegroup.h @@ -1,76 +1,73 @@ /*************************************************************************** satellitegroup.h - K Desktop Planetarium ------------------- begin : Tue 22 Mar 2011 copyright : (C) 2011 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include #include class Satellite; /** * @class SatelliteGroup * Represents a group of artificial satellites. * @author Jérôme SONRIER * @version 1.0 */ class SatelliteGroup : public QList { public: /** * @short Constructor */ SatelliteGroup(const QString& name, const QString& tle_filename, const QUrl& update_url); - /** - * @short Destructor - */ - ~SatelliteGroup(); + virtual ~SatelliteGroup() = default; /** * Read TLE file of the group and create all satellites found in the file. */ void readTLE(); /** * Compute current position of the each satellites in the group. */ void updateSatellitesPos(); /** * @return TLE filename */ QUrl tleFilename(); /** * @return URL from which new TLE file must be download */ QUrl tleUrl(); /** * @return Name of the group */ QString name(); private: /// Group name QString m_name; /// TLE filename QString m_tle_file; /// URL used to update TLE file QUrl m_tle_url; }; diff --git a/kstars/skyobjects/skyline.cpp b/kstars/skyobjects/skyline.cpp index 583f7041f..244b8e9e2 100644 --- a/kstars/skyobjects/skyline.cpp +++ b/kstars/skyobjects/skyline.cpp @@ -1,90 +1,86 @@ /*************************************************************************** skyline.cpp - K Desktop Planetarium ------------------- begin : Mon June 26 2006 copyright : (C) 2006 by Jason Harris email : kstarss@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "skyline.h" #include "kstarsdata.h" #include "ksnumbers.h" #include -SkyLine::SkyLine() -{ -} - SkyLine::~SkyLine() { clear(); } void SkyLine::clear() { qDeleteAll(m_pList); m_pList.clear(); } void SkyLine::append(SkyPoint *p) { m_pList.append(new SkyPoint(*p)); } void SkyLine::setPoint(int i, SkyPoint *p) { if (i < 0 || i >= m_pList.size()) { qDebug() << "SkyLine index error: no such point: " << i; return; } *m_pList[i] = *p; } dms SkyLine::angularSize(int i) const { if (i < 0 || i + 1 >= m_pList.size()) { qDebug() << "SkyLine index error: no such segment: " << i; return dms(); } SkyPoint *p1 = m_pList[i]; SkyPoint *p2 = m_pList[i + 1]; double dalpha = p1->ra().radians() - p2->ra().radians(); double ddelta = p1->dec().radians() - p2->dec().radians(); double sa = sin(dalpha / 2.); double sd = sin(ddelta / 2.); double hava = sa * sa; double havd = sd * sd; double aux = havd + cos(p1->dec().radians()) * cos(p2->dec().radians()) * hava; dms angDist; angDist.setRadians(2 * fabs(asin(sqrt(aux)))); return angDist; } void SkyLine::update(KStarsData *d, KSNumbers *num) { foreach (SkyPoint *p, m_pList) { if (num) p->updateCoords(num); p->EquatorialToHorizontal(d->lst(), d->geo()->lat()); } } diff --git a/kstars/skyobjects/skyline.h b/kstars/skyobjects/skyline.h index 22bb98b65..9337f6be9 100644 --- a/kstars/skyobjects/skyline.h +++ b/kstars/skyobjects/skyline.h @@ -1,80 +1,81 @@ /*************************************************************************** skyline.h - K Desktop Planetarium ------------------- begin : Mon June 26 2006 copyright : (C) 2006 by Jason Harris email : kstarss@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 SKYLINE_H_ -#define SKYLINE_H_ +#pragma once #include "skypoint.h" class dms; class KStarsData; /** * @class SkyLine * * A series of connected line segments in the sky, composed of SkyPoints at * its vertices. SkyLines are used for constellation lines and boundaries, * the coordinate grid, and the equator, ecliptic and horizon. * * @note the SkyLine segments are always straight lines, they are not * Great Circle segments joining the two endpoints. Therefore, line segments * that need to follow great circles must be approximated with many short * SkyLine segments. */ class SkyLine { public: - /** Default Constructor (empty). */ - SkyLine(); + SkyLine() = default; - /** Destructor */ ~SkyLine(); - /**Append a segment to the list by adding a new endpoint. - * @param p the new endpoint to be added - */ + /** + * Append a segment to the list by adding a new endpoint. + * + * @param p the new endpoint to be added + */ void append(SkyPoint *p); - /** @return a const pointer to a point in the SkyLine - * param i the index position of the point - */ + /** + * @return a const pointer to a point in the SkyLine + * param i the index position of the point + */ inline SkyPoint *point(int i) const { return m_pList[i]; } inline QList &points() { return m_pList; } /** Remove all points from list */ void clear(); - /**Set point i in the SkyLine - * @param i the index position of the point to modify - * @param p1 the new SkyPoint - */ + /** + * Set point i in the SkyLine + * + * @param i the index position of the point to modify + * @param p1 the new SkyPoint + */ void setPoint(int i, SkyPoint *p); - /** @return the angle subtended by any line segment along the SkyLine. - * @param i the index of the line segment to be measured. - * If no argument is given, the first segment is assumed. - */ + /** + * @return the angle subtended by any line segment along the SkyLine. + * @param i the index of the line segment to be measured. + * If no argument is given, the first segment is assumed. + */ dms angularSize(int i = 0) const; void update(KStarsData *data, KSNumbers *num = nullptr); private: QList m_pList; }; - -#endif diff --git a/kstars/skyobjects/skyobject.cpp b/kstars/skyobjects/skyobject.cpp index df6ecb7e0..ac8c5220c 100644 --- a/kstars/skyobjects/skyobject.cpp +++ b/kstars/skyobjects/skyobject.cpp @@ -1,558 +1,554 @@ /*************************************************************************** skyobject.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "skyobject.h" #include "geolocation.h" #include "ksnumbers.h" #include "kspaths.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "kspopupmenu.h" #include "skymap.h" #endif #include "kstarsdata.h" #include "Options.h" #include "starobject.h" #include "skycomponents/skylabeler.h" QString SkyObject::emptyString; QString SkyObject::unnamedString = QString(I18N_NOOP("unnamed")); QString SkyObject::unnamedObjectString = QString(I18N_NOOP("unnamed object")); QString SkyObject::starString = QString("star"); const SkyObject::UID SkyObject::invalidUID = ~0; const SkyObject::UID SkyObject::UID_STAR = 0; const SkyObject::UID SkyObject::UID_GALAXY = 1; const SkyObject::UID SkyObject::UID_DEEPSKY = 2; const SkyObject::UID SkyObject::UID_SOLARSYS = 3; SkyObject::SkyObject(int t, dms r, dms d, float m, const QString &n, const QString &n2, const QString &lname) : SkyPoint(r, d), info() { setType(t); sortMagnitude = m; setName(n); setName2(n2); setLongName(lname); } SkyObject::SkyObject(int t, double r, double d, float m, const QString &n, const QString &n2, const QString &lname) : SkyPoint(r, d), info() { setType(t); sortMagnitude = m; setName(n); setName2(n2); setLongName(lname); } SkyObject *SkyObject::clone() const { Q_ASSERT(typeid(this) == typeid(static_cast(this))); // Ensure we are not slicing a derived class return new SkyObject(*this); } -SkyObject::~SkyObject() -{ -} - void SkyObject::showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos) { #if defined(KSTARS_LITE) Q_UNUSED(pos) Q_UNUSED(pmenu); #else initPopupMenu(pmenu); pmenu->popup(pos); #endif } void SkyObject::initPopupMenu(KSPopupMenu *pmenu) { #ifdef KSTARS_LITE Q_UNUSED(pmenu) #else pmenu->createEmptyMenu(this); #endif } void SkyObject::setLongName(const QString &longname) { if (longname.isEmpty()) { if (hasName()) LongName = name(); else if (hasName2()) LongName = name2(); else LongName.clear(); } else { LongName = longname; } } QTime SkyObject::riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact) const { // If this object does not rise or set, return an invalid time SkyPoint p = recomputeCoords(dt, geo); if (p.checkCircumpolar(geo->lat())) return QTime(); //First of all, if the object is below the horizon at date/time dt, adjust the time //to bring it above the horizon KStarsDateTime dt2 = dt; dms lst(geo->GSTtoLST(dt.gst())); p.EquatorialToHorizontal(&lst, geo->lat()); if (p.alt().Degrees() < 0.0) { if (p.az().Degrees() < 180.0) //object has not risen yet { dt2 = dt.addSecs(12. * 3600.); // Move forward 12 hours, to a time when it has already risen } else //object has already set { dt2 = dt.addSecs(-12. * 3600.); // Move backward 12 hours, to a time when it has not yet set } } // The addition / subtraction of 12 hours ensures that we always // compute the _closest_ rise time and the _closest_ set time to // the current time. QTime rstUt = riseSetTimeUT(dt2, geo, rst, exact); if (!rstUt.isValid()) return QTime(); return geo->UTtoLT(KStarsDateTime(dt2.date(), rstUt)).time(); } QTime SkyObject::riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT, bool exact) const { // First trial to calculate UT QTime UT = auxRiseSetTimeUT(dt, geo, &ra(), &dec(), riseT); // We iterate once more using the calculated UT to compute again // the ra and dec for that time and hence the rise/set time. // Also, adjust the date by +/- 1 day, if necessary // By adding this +/- 1 day, we are double-checking that the // reported rise-time is the _already_ (last) risen time, and that // the reported set-time is the _future_ (next) set time // // However, issues with this are taken care of in // SkyObject::riseSetTime() KStarsDateTime dt0 = dt; dt0.setTime(UT); if (riseT && dt0 > dt) { dt0 = dt0.addDays(-1); } else if (!riseT && dt0 < dt) { dt0 = dt0.addDays(1); } SkyPoint sp = recomputeCoords(dt0, geo); UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT); if (exact) { // We iterate a second time (For the Moon the second iteration changes // aprox. 1.5 arcmin the coordinates). dt0.setTime(UT); sp = recomputeCoords(dt0, geo); UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT); } return UT; } QTime SkyObject::auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl, bool riseT) const { dms LST = auxRiseSetTimeLST(geo->lat(), righta, decl, riseT); return dt.GSTtoUT(geo->LSTtoGST(LST)); } dms SkyObject::auxRiseSetTimeLST(const dms *gLat, const dms *righta, const dms *decl, bool riseT) const { dms h0 = elevationCorrection(); double H = approxHourAngle(&h0, gLat, decl); dms LST; if (riseT) LST.setH(24.0 + righta->Hours() - H / 15.0); else LST.setH(righta->Hours() + H / 15.0); return LST.reduce(); } dms SkyObject::riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT) const { dms Azimuth; double AltRad, AzRad; double sindec, cosdec, sinlat, coslat, sinHA, cosHA; double sinAlt, cosAlt; QTime UT = riseSetTimeUT(dt, geo, riseT); KStarsDateTime dt0 = dt; dt0.setTime(UT); SkyPoint sp = recomputeCoords(dt0, geo); dms LST = auxRiseSetTimeLST(geo->lat(), &sp.ra0(), &sp.dec0(), riseT); dms HourAngle = dms(LST.Degrees() - sp.ra0().Degrees()); geo->lat()->SinCos(sinlat, coslat); dec().SinCos(sindec, cosdec); HourAngle.SinCos(sinHA, cosHA); sinAlt = sindec * sinlat + cosdec * coslat * cosHA; AltRad = asin(sinAlt); cosAlt = cos(AltRad); AzRad = acos((sindec - sinlat * sinAlt) / (coslat * cosAlt)); if (sinHA > 0.0) AzRad = 2.0 * dms::PI - AzRad; // resolve acos() ambiguity Azimuth.setRadians(AzRad); return Azimuth; } QTime SkyObject::transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const { dms LST = geo->GSTtoLST(dt.gst()); //dSec is the number of seconds until the object transits. dms HourAngle = dms(LST.Degrees() - ra().Degrees()); int dSec = int(-3600. * HourAngle.Hours()); //dt0 is the first guess at the transit time. KStarsDateTime dt0 = dt.addSecs(dSec); //recompute object's position at UT0 and then find //transit time of this refined position SkyPoint sp = recomputeCoords(dt0, geo); HourAngle = dms(LST.Degrees() - sp.ra().Degrees()); dSec = int(-3600. * HourAngle.Hours()); return dt.addSecs(dSec).time(); } QTime SkyObject::transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const { return geo->UTtoLT(KStarsDateTime(dt.date(), transitTimeUT(dt, geo))).time(); } dms SkyObject::transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const { KStarsDateTime dt0 = dt; dt0.setTime(transitTimeUT(dt, geo)); SkyPoint sp = recomputeCoords(dt0, geo); double delta = 90 - geo->lat()->Degrees() + sp.dec().Degrees(); if (delta > 90) delta = 180 - delta; return dms(delta); } double SkyObject::approxHourAngle(const dms *h0, const dms *gLat, const dms *dec) const { double sh0 = sin(h0->radians()); double r = (sh0 - sin(gLat->radians()) * sin(dec->radians())) / (cos(gLat->radians()) * cos(dec->radians())); double H = acos(r) / dms::DegToRad; return H; } dms SkyObject::elevationCorrection(void) const { /* The atmospheric refraction at the horizon shifts altitude by * - 34 arcmin = 0.5667 degrees. This value changes if the observer * is above the horizon, or if the weather conditions change much. * * For the sun we have to add half the angular sie of the body, since * the sunset is the time the upper limb of the sun disappears below * the horizon, and dawn, when the upper part of the limb appears * over the horizon. The angular size of the sun = angular size of the * moon = 31' 59''. * * So for the sun the correction is = -34 - 16 = 50 arcmin = -0.8333 * * This same correction should be applied to the moon however parallax * is important here. Meeus states that the correction should be * 0.7275 P - 34 arcmin, where P is the moon's horizontal parallax. * He proposes a mean value of 0.125 degrees if no great accuracy * is needed. */ if (name() == "Sun" || name() == "Moon") return dms(-0.8333); // else if ( name() == "Moon" ) // return dms(0.125); else // All sources point-like. return dms(-0.5667); } SkyPoint SkyObject::recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo) const { // Create a clone SkyObject *c = this->clone(); // compute coords of the copy for new time jd KSNumbers num(dt.djd()); // Note: isSolarSystem() below should give the same result on this // and c. The only very minor reason to prefer this is so that we // have an additional layer of warnings about subclasses of // KSPlanetBase that do not implement SkyObject::clone() due to // the passing of lat and LST if (isSolarSystem() && geo) { CachingDms LST = geo->GSTtoLST(dt.gst()); c->updateCoords(&num, true, geo->lat(), &LST); } else { c->updateCoords(&num); } // Transfer the coordinates into a SkyPoint SkyPoint p = *c; // Delete the clone delete c; // Return the SkyPoint return p; } SkyPoint SkyObject::recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const { Q_ASSERT(geo); SkyPoint ret = recomputeCoords(dt, geo); CachingDms LST = geo->GSTtoLST(dt.gst()); ret.EquatorialToHorizontal(&LST, geo->lat()); return ret; } QString SkyObject::typeName(int t) { switch (t) { case STAR: return i18n("Star"); case CATALOG_STAR: return i18n("Catalog Star"); case PLANET: return i18n("Planet"); case OPEN_CLUSTER: return i18n("Open Cluster"); case GLOBULAR_CLUSTER: return i18n("Globular Cluster"); case GASEOUS_NEBULA: return i18n("Gaseous Nebula"); case PLANETARY_NEBULA: return i18n("Planetary Nebula"); case SUPERNOVA_REMNANT: return i18n("Supernova Remnant"); case GALAXY: return i18n("Galaxy"); case COMET: return i18n("Comet"); case ASTEROID: return i18n("Asteroid"); case CONSTELLATION: return i18n("Constellation"); case MOON: return i18n("Moon"); case GALAXY_CLUSTER: return i18n("Galaxy Cluster"); case SATELLITE: return i18n("Satellite"); case SUPERNOVA: return i18n("Supernova"); case RADIO_SOURCE: return i18n("Radio Source"); case ASTERISM: return i18n("Asterism"); case DARK_NEBULA: return i18n("Dark Nebula"); case QUASAR: return i18n("Quasar"); case MULT_STAR: return i18n("Multiple Star"); default: return i18n("Unknown Type"); } } QString SkyObject::typeName() const { return typeName(Type); } QString SkyObject::messageFromTitle(const QString &imageTitle) const { QString message = imageTitle; //HST Image if (imageTitle == i18n("Show HST Image") || imageTitle.contains("HST")) { message = i18n("%1: Hubble Space Telescope, operated by STScI for NASA [public domain]", longname()); //Spitzer Image } else if (imageTitle.contains(i18n("Show Spitzer Image"))) { message = i18n("%1: Spitzer Space Telescope, courtesy NASA/JPL-Caltech [public domain]", longname()); //SEDS Image } else if (imageTitle == i18n("Show SEDS Image")) { message = i18n("%1: SEDS, http://www.seds.org [free for non-commercial use]", longname()); //Kitt Peak AOP Image } else if (imageTitle == i18n("Show KPNO AOP Image")) { message = i18n("%1: Advanced Observing Program at Kitt Peak National Observatory [free for non-commercial use; " "no physical reproductions]", longname()); //NOAO Image } else if (imageTitle.contains(i18n("Show NOAO Image"))) { message = i18n("%1: National Optical Astronomy Observatories and AURA [free for non-commercial use]", longname()); //VLT Image } else if (imageTitle.contains("VLT")) { message = i18n("%1: Very Large Telescope, operated by the European Southern Observatory [free for " "non-commercial use; no reproductions]", longname()); //All others } else if (imageTitle.startsWith(i18n("Show"))) { message = imageTitle.mid(imageTitle.indexOf(" ") + 1); //eat first word, "Show" message = longname() + ": " + message; } return message; } //TODO: Should create a special UserLog widget that encapsulates the "default" //message in the widget when no log exists (much like we do with dmsBox now) void SkyObject::saveUserLog(const QString &newLog) { QFile file; QString logs; //existing logs //Do nothing if: //+ new log is the "default" message //+ new log is empty if (newLog == (i18n("Record here observation logs and/or data on %1.", name())) || newLog.isEmpty()) return; // header label QString KSLabel = "[KSLABEL:" + name() + ']'; //However, we can't accept a star name if it has a greek letter in it: if (type() == STAR) { StarObject *star = (StarObject *)this; if (name() == star->gname()) KSLabel = "[KSLABEL:" + star->gname(false) + ']'; //"false": spell out greek letter } file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "userlog.dat"); //determine filename in local user KDE directory tree. if (file.open(QIODevice::ReadOnly)) { QTextStream instream(&file); // read all data into memory logs = instream.readAll(); file.close(); } //Remove old log entry from the logs text if (!userLog().isEmpty()) { int startIndex, endIndex; QString sub; startIndex = logs.indexOf(KSLabel); sub = logs.mid(startIndex); endIndex = sub.indexOf("[KSLogEnd]"); logs.remove(startIndex, endIndex + 11); } //append the new log entry to the end of the logs text logs.append(KSLabel + '\n' + newLog + "\n[KSLogEnd]\n"); //Open file for writing if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Cannot write to user log file"; return; } //Write new logs text QTextStream outstream(&file); outstream << logs; //Set the log text in the object itself. userLog() = newLog; file.close(); } QString SkyObject::labelString() const { return translatedName(); } double SkyObject::labelOffset() const { return SkyLabeler::ZoomOffset(); } AuxInfo *SkyObject::getAuxInfo() { if (!info) info = new AuxInfo; return &(*info); } SkyObject::UID SkyObject::getUID() const { return invalidUID; } diff --git a/kstars/skyobjects/skyobject.h b/kstars/skyobjects/skyobject.h index 129325f4d..624b692ab 100644 --- a/kstars/skyobjects/skyobject.h +++ b/kstars/skyobjects/skyobject.h @@ -1,492 +1,492 @@ /*************************************************************************** skyobject.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "auxinfo.h" #include "dms.h" #include "skypoint.h" #include #include #include #include class QPoint; class GeoLocation; class KSPopupMenu; /** * @class SkyObject * Provides all necessary information about an object in the sky: * its coordinates, name(s), type, magnitude, and QStringLists of * URLs for images and webpages regarding the object. * @short Information about an object in the sky. * @author Jason Harris * @version 1.0 */ class SkyObject : public SkyPoint { public: /** * @short Type for Unique object IDenticator. * * Each object has unique ID (UID). For different objects UIDs must be different. */ typedef qint64 UID; /** @short Kind of UID */ static const UID UID_STAR; static const UID UID_GALAXY; static const UID UID_DEEPSKY; static const UID UID_SOLARSYS; /** Invalid UID. Real sky object could not have such UID */ static const UID invalidUID; /** * Constructor. Set SkyObject data according to arguments. * @param t Type of object * @param r catalog Right Ascension * @param d catalog Declination * @param m magnitude (brightness) * @param n Primary name * @param n2 Secondary name * @param lname Long name (common name) */ explicit SkyObject(int t = TYPE_UNKNOWN, dms r = dms(0.0), dms d = dms(0.0), float m = 0.0, const QString &n = QString(), const QString &n2 = QString(), const QString &lname = QString()); /** * Constructor. Set SkyObject data according to arguments. Differs from * above function only in data type of RA and Dec. * @param t Type of object * @param r catalog Right Ascension * @param d catalog Declination * @param m magnitude (brightness) * @param n Primary name * @param n2 Secondary name * @param lname Long name (common name) */ SkyObject(int t, double r, double d, float m = 0.0, const QString &n = QString(), const QString &n2 = QString(), const QString &lname = QString()); /** Destructor (empty) */ - ~SkyObject() override; + virtual ~SkyObject() override = default; /** * @short Create copy of object. * This method is virtual copy constructor. It allows for safe * copying of objects. In other words, KSPlanet object stored in * SkyObject pointer will be copied as KSPlanet. * * Each subclass of SkyObject MUST implement clone method. There * is no checking to ensure this, though. * * @return pointer to newly allocated object. Caller takes full responsibility * for deallocating it. */ virtual SkyObject *clone() const; /** * @enum TYPE * The type classification of the SkyObject. * @note Keep TYPE_UNKNOWN at 255. To find out how many known * types exist, keep the NUMBER_OF_KNOWN_TYPES at the highest * non-Unknown value. This is a fake type that can be used in * comparisons and for loops. */ enum TYPE { STAR = 0, CATALOG_STAR = 1, PLANET = 2, OPEN_CLUSTER = 3, GLOBULAR_CLUSTER = 4, GASEOUS_NEBULA = 5, PLANETARY_NEBULA = 6, SUPERNOVA_REMNANT = 7, GALAXY = 8, COMET = 9, ASTEROID = 10, CONSTELLATION = 11, MOON = 12, ASTERISM = 13, GALAXY_CLUSTER = 14, DARK_NEBULA = 15, QUASAR = 16, MULT_STAR = 17, RADIO_SOURCE = 18, SATELLITE = 19, SUPERNOVA = 20, NUMBER_OF_KNOWN_TYPES = 21, TYPE_UNKNOWN = 255 }; /** * @return A translated string indicating the type name for a given type number * @param t The type number * @note Note the existence of a SkyObject::typeName( void ) method that is not static and returns the type of this object. */ static QString typeName(const int t); /** @return object's primary name. */ inline virtual QString name(void) const { return hasName() ? Name : unnamedString; } /** @return object's primary name, translated to local language. */ inline QString translatedName() const { return i18n( name() .toUtf8()); // FIXME: Hmm... that's funny. How does the string extraction work, if we are UTF8-ing the name first? Does the string extraction change to UTF8? } /** @return object's secondary name */ inline QString name2(void) const { return (hasName2() ? Name2 : emptyString); } /** @return object's secondary name, translated to local language. */ inline QString translatedName2() const { return (hasName2() ? i18n(Name2.toUtf8()) : emptyString); } /** * @return object's common (long) name */ virtual QString longname(void) const { return hasLongName() ? LongName : unnamedObjectString; } /** * @return object's common (long) name, translated to local language. */ QString translatedLongName() const { return i18n(longname().toUtf8()); } /** * Set the object's long name. * @param longname the object's long name. */ void setLongName(const QString &longname = QString()); /** * @return the string used to label the object on the map * In the default implementation, this just returns translatedName() * Overridden by StarObject. */ virtual QString labelString() const; /** * @return object's type identifier (int) * @see enum TYPE */ inline int type(void) const { return (int)Type; } /** * Set the object's type identifier to the argument. * @param t the object's type identifier (e.g., "SkyObject::PLANETARY_NEBULA") * @see enum TYPE */ inline void setType(int t) { Type = (unsigned char)t; } /** * @return the type name for this object * @note This just calls the static method by the same name, with the appropriate type number. See SkyObject::typeName( const int ) */ QString typeName() const; /** * @return object's magnitude */ inline float mag() const { return sortMagnitude; } /** * @return the object's position angle. This is overridden in KSPlanetBase * and DeepSkyObject; for all other SkyObjects, this returns 0.0. */ inline virtual double pa() const { return 0.0; } /** * @return true if the object is a solar system body. */ inline bool isSolarSystem() const { return (type() == 2 || type() == 9 || type() == 10 || type() == 12); } /** * Initialize the popup menut. This function should call correct * initialization function in KSPopupMenu. By overloading the * function, we don't have to check the object type when we need * the menu. */ virtual void initPopupMenu(KSPopupMenu *pmenu); /** Show Type-specific popup menu. Oveloading is done in the function initPopupMenu */ void showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos); /** * Determine the time at which the point will rise or set. Because solar system * objects move across the sky, it is necessary to iterate on the solution. * We compute the rise/set time for the object's current position, then * compute the object's position at that time. Finally, we recompute then * rise/set time for the new coordinates. Further iteration is not necessary, * even for the most swiftly-moving object (the Moon). * @return the local time that the object will rise * @param dt current UT date/time * @param geo current geographic location * @param rst If true, compute rise time. If false, compute set time. * @param exact If true, use a second iteration for more accurate time */ QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact = true) const; /** * @return the UT time when the object will rise or set * @param dt target date/time * @param geo pointer to Geographic location * @param rst Boolean. If true will compute rise time. If false * will compute set time. * @param exact If true, use a second iteration for more accurate time */ QTime riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact = true) const; /** * @return the Azimuth time when the object will rise or set. This function * recomputes set or rise UT times. * @param dt target date/time * @param geo GeoLocation object * @param rst Boolen. If true will compute rise time. If false * will compute set time. */ dms riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool rst) const; /** * The same iteration technique described in riseSetTime() is used here. * @return the local time that the object will transit the meridian. * @param dt target date/time * @param geo pointer to the geographic location */ QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const; /** * @return the universal time that the object will transit the meridian. * @param dt target date/time * @param geo pointer to the geographic location */ QTime transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const; /** * @return the altitude of the object at the moment it transits the meridian. * @param dt target date/time * @param geo pointer to the geographic location */ dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const; /** * The equatorial coordinates for the object on date dt are computed and returned, * but the object's internal coordinates are not modified. * @return the coordinates of the selected object for the time given by jd * @param dt date/time for which the coords will be computed. * @param geo pointer to geographic location (used for solar system only) * @note Does not update the horizontal coordinates. Call EquatorialToHorizontal for that. */ SkyPoint recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo = nullptr) const; /** * @short Like recomputeCoords, but also calls EquatorialToHorizontal before returning */ SkyPoint recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const; inline bool hasName() const { return !Name.isEmpty(); } inline bool hasName2() const { return !Name2.isEmpty(); } inline bool hasLongName() const { return !LongName.isEmpty(); } /** * @short Given the Image title from a URL file, try to convert it to an image credit string. */ QString messageFromTitle(const QString &imageTitle) const; /** * @short Save new user log text */ void saveUserLog(const QString &newLog); /** * @return the pixel distance for offseting the object's name label * @note overridden in StarObject, DeepSkyObject, KSPlanetBase */ virtual double labelOffset() const; /** * @short Query whether this SkyObject has a valid AuxInfo structure associated with it. * @return true if this SkyObject has a valid AuxInfo structure associated with it, false if not. */ inline bool hasAuxInfo() const { return !(!info); } /** * @return A reference to a QStringList storing a list of Image URLs associated with this SkyObject. */ inline QStringList &ImageList() { return getAuxInfo()->ImageList; } /** * @return A reference to a QStringList storing a list of Image Titles associated with this SkyObject. */ inline QStringList &ImageTitle() { return getAuxInfo()->ImageTitle; } /** * @return A reference to a QStringList storing a list of Information Links associated with * this SkyObject. */ inline QStringList &InfoList() { return getAuxInfo()->InfoList; } /** * @return A reference to a QStringList storing a list of Information Link Titles associated with * this SkyObject. */ inline QStringList &InfoTitle() { return getAuxInfo()->InfoTitle; } /** * @return a reference to a QString storing the users' log for this SkyObject */ inline QString &userLog() { return getAuxInfo()->userLog; } inline QString ¬es() { return getAuxInfo()->notes; } void setNotes(QString _notes) { getAuxInfo()->notes = _notes; } /** * @short Return UID for object. * This method should be reimplemented in all concrete * subclasses. Implementation for SkyObject just returns * invalidUID. It's required SkyObject is not an abstract class. */ virtual UID getUID() const; // TODO: (Valentin) have another think about onFocus handlers :) /** * @brief hashBeenUpdated * @return wether the coordinates of the object have been updated * * This is used for faster filtering. */ bool hashBeenUpdated() { return has_been_updated; } private: /** * Compute the UT time when the object will rise or set. It is an auxiliary * procedure because it does not use the RA and DEC of the object but values * given as parameters. You may want to use riseSetTimeUT() which is * public. riseSetTimeUT() calls this function iteratively. * @param dt target date/time * @param geo pointer to Geographic location * @param righta pointer to Right ascention of the object * @param decl pointer to Declination of the object * @param rst Boolean. If true will compute rise time. If false * will compute set time. * @return the time at which the given position will rise or set. */ QTime auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl, bool riseT) const; /** * Compute the LST time when the object will rise or set. It is an auxiliary * procedure because it does not use the RA and DEC of the object but values * given as parameters. You may want to use riseSetTimeLST() which is * public. riseSetTimeLST() calls this function iteratively. * @param gLt Geographic latitude * @param rga Right ascention of the object * @param decl Declination of the object * @param rst Boolean. If true will compute rise time. If false * will compute set time. */ dms auxRiseSetTimeLST(const dms *gLt, const dms *rga, const dms *decl, bool rst) const; /** * Compute the approximate hour angle that an object with declination d will have * when its altitude is h (as seen from geographic latitude gLat). * This function is only used by auxRiseSetTimeLST(). * @param h pointer to the altitude of the object * @param gLat pointer to the geographic latitude * @param d pointer to the declination of the object. * @return the Hour Angle, in degrees. */ double approxHourAngle(const dms *h, const dms *gLat, const dms *d) const; /** * Correct for the geometric altitude of the center of the body at the * time of rising or setting. This is due to refraction at the horizon * and to the size of the body. The moon correction has also to take into * account parallax. The value we use here is a rough approximation * suggested by J. Meeus. * * Weather status (temperature and pressure basically) is not taken * into account although change of conditions between summer and * winter could shift the times of sunrise and sunset by 20 seconds. * * This function is only used by auxRiseSetTimeLST(). * @return dms object with the correction. */ dms elevationCorrection(void) const; /** * @short Return a pointer to the AuxInfo object associated with this SkyObject. * @note This method creates the AuxInfo object if it is non-existent * @return Pointer to an AuxInfo structure */ AuxInfo *getAuxInfo(); // FIXME: Can this be made conceptually const or the like? unsigned char Type; float sortMagnitude; // This magnitude is used for sorting / making decisions about the visibility of an object. Should not be NaN. protected: /** * Set the object's sorting magnitude. * @param m the object's magnitude. */ inline void setMag(float m) { sortMagnitude = m < 36.0 ? m : NaN:: f; // Updating faintest sane magnitude to 36.0 (faintest visual magnitude visible with E-ELT, acc. to Wikipedia on Apparent Magnitude.) } // FIXME: We claim sortMagnitude should not be NaN, but we are setting it to NaN above!! ^ /** * Set the object's primary name. * @param name the object's primary name */ inline void setName(const QString &name) { Name = name; } /** * Set the object's secondary name. * @param name2 the object's secondary name. */ inline void setName2(const QString &name2 = QString()) { Name2 = name2; } QString Name, Name2, LongName; // Pointer to an auxiliary info structure that stores Image URLs, Info URLs etc. QSharedDataPointer info; // store often used name strings in static variables static QString emptyString; static QString unnamedString; static QString unnamedObjectString; static QString starString; // Wether the coordinates of the object have been updated. // The default value is chose for compatibility reasons. // It primarily matters for objects which are filtered. // See `KSAsteroid` for an example. bool has_been_updated = true; }; diff --git a/kstars/skyobjects/skypoint.cpp b/kstars/skyobjects/skypoint.cpp index 6a619df0b..e96d5c006 100644 --- a/kstars/skyobjects/skypoint.cpp +++ b/kstars/skyobjects/skypoint.cpp @@ -1,942 +1,938 @@ /*************************************************************************** skypoint.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001-2005 by Jason Harris email : jharris@30doradus.org copyright : (C) 2004-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 "skypoint.h" #include "dms.h" #include "ksnumbers.h" #include "kstarsdatetime.h" #include "kssun.h" #include "kstarsdata.h" #include "Options.h" #include "skyobject.h" #include "skycomponents/skymapcomposite.h" #include #include #include #ifdef PROFILE_COORDINATE_CONVERSION #include // For profiling, remove if not profiling. long unsigned SkyPoint::eqToHzCalls = 0; double SkyPoint::cpuTime_EqToHz = 0.; #endif KSSun *SkyPoint::m_Sun = nullptr; const double SkyPoint::altCrit = -1.0; SkyPoint::SkyPoint() { // Default constructor. Set nonsense values RA0.setD(-1); // RA >= 0 always :-) Dec0.setD(180); // Dec is between -90 and 90 Degrees :-) RA = RA0; Dec = Dec0; lastPrecessJD = J2000; // By convention, we use J2000 coordinates } -SkyPoint::~SkyPoint() -{ -} - void SkyPoint::set(const dms &r, const dms &d) { RA0 = RA = r; Dec0 = Dec = d; lastPrecessJD = J2000; // By convention, we use J2000 coordinates } void SkyPoint::EquatorialToHorizontal(const dms *LST, const dms *lat) { // qDebug() << "NOTE: This EquatorialToHorizontal overload (using dms pointers instead of CachingDms pointers) is deprecated and should be replaced with CachingDms prototype wherever speed is desirable!"; CachingDms _LST(*LST), _lat(*lat); EquatorialToHorizontal(&_LST, &_lat); } void SkyPoint::EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat) { #ifdef PROFILE_COORDINATE_CONVERSION std::clock_t start = std::clock(); #endif //Uncomment for spherical trig version double AltRad, AzRad; double sindec, cosdec, sinlat, coslat, sinHA, cosHA; double sinAlt, cosAlt; CachingDms HourAngle = (*LST) - ra(); // Using CachingDms subtraction operator to find cos/sin of HourAngle without calling sincos() lat->SinCos(sinlat, coslat); dec().SinCos(sindec, cosdec); HourAngle.SinCos(sinHA, cosHA); sinAlt = sindec * sinlat + cosdec * coslat * cosHA; AltRad = asin(sinAlt); cosAlt = sqrt( 1 - sinAlt * sinAlt); // Avoid trigonometric function. Return value of asin is always in [-pi/2, pi/2] and in this domain cosine is always non-negative, so we can use this. if (cosAlt == 0) cosAlt = cos(AltRad); double arg = (sindec - sinlat * sinAlt) / (coslat * cosAlt); if (arg <= -1.0) AzRad = dms::PI; else if (arg >= 1.0) AzRad = 0.0; else AzRad = acos(arg); if (sinHA > 0.0) AzRad = 2.0 * dms::PI - AzRad; // resolve acos() ambiguity Alt.setRadians(AltRad); Az.setRadians(AzRad); #ifdef PROFILE_COORDINATE_CONVERSION std::clock_t stop = std::clock(); cpuTime_EqToHz += double(stop - start) / double(CLOCKS_PER_SEC); // Accumulate time in seconds ++eqToHzCalls; #endif // //Uncomment for XYZ version // double xr, yr, zr, xr1, zr1, sa, ca; // //Z-axis rotation by -LST // dms a = dms( -1.0*LST->Degrees() ); // a.SinCos( sa, ca ); // xr1 = m_X*ca + m_Y*sa; // yr = -1.0*m_X*sa + m_Y*ca; // zr1 = m_Z; // // //Y-axis rotation by lat - 90. // a = dms( lat->Degrees() - 90.0 ); // a.SinCos( sa, ca ); // xr = xr1*ca - zr1*sa; // zr = xr1*sa + zr1*ca; // // //FIXME: eventually, we will work with XYZ directly // Alt.setRadians( asin( zr ) ); // Az.setRadians( atan2( yr, xr ) ); } void SkyPoint::HorizontalToEquatorial(const dms *LST, const dms *lat) { double HARad, DecRad; double sinlat, coslat, sinAlt, cosAlt, sinAz, cosAz; double sinDec, cosDec; lat->SinCos(sinlat, coslat); alt().SinCos(sinAlt, cosAlt); Az.SinCos(sinAz, cosAz); sinDec = sinAlt * sinlat + cosAlt * coslat * cosAz; DecRad = asin(sinDec); cosDec = cos(DecRad); Dec.setRadians(DecRad); double x = (sinAlt - sinlat * sinDec) / (coslat * cosDec); //Under certain circumstances, x can be very slightly less than -1.0000, or slightly //greater than 1.0000, leading to a crash on acos(x). However, the value isn't //*really* out of range; it's a kind of roundoff error. if (x < -1.0 && x > -1.000001) HARad = dms::PI; else if (x > 1.0 && x < 1.000001) HARad = 0.0; else if (x < -1.0) { //qWarning() << "Coordinate out of range."; HARad = dms::PI; } else if (x > 1.0) { //qWarning() << "Coordinate out of range."; HARad = 0.0; } else HARad = acos(x); if (sinAz > 0.0) HARad = 2.0 * dms::PI - HARad; // resolve acos() ambiguity RA.setRadians(LST->radians() - HARad); RA.reduceToRange(dms::ZERO_TO_2PI); } void SkyPoint::findEcliptic(const CachingDms *Obliquity, dms &EcLong, dms &EcLat) { double sinRA, cosRA, sinOb, cosOb, sinDec, cosDec, tanDec; ra().SinCos(sinRA, cosRA); dec().SinCos(sinDec, cosDec); Obliquity->SinCos(sinOb, cosOb); tanDec = sinDec / cosDec; // FIXME: -jbb div by zero? double y = sinRA * cosOb + tanDec * sinOb; double ELongRad = atan2(y, cosRA); EcLong.setRadians(ELongRad); EcLong.reduceToRange(dms::ZERO_TO_2PI); EcLat.setRadians(asin(sinDec * cosOb - cosDec * sinOb * sinRA)); } void SkyPoint::setFromEcliptic(const CachingDms *Obliquity, const dms &EcLong, const dms &EcLat) { double sinLong, cosLong, sinLat, cosLat, sinObliq, cosObliq; EcLong.SinCos(sinLong, cosLong); EcLat.SinCos(sinLat, cosLat); Obliquity->SinCos(sinObliq, cosObliq); double sinDec = sinLat * cosObliq + cosLat * sinObliq * sinLong; double y = sinLong * cosObliq - (sinLat / cosLat) * sinObliq; // double RARad = atan2( y, cosLong ); RA.setUsing_atan2(y, cosLong); RA.reduceToRange(dms::ZERO_TO_2PI); Dec.setUsing_asin(sinDec); } void SkyPoint::precess(const KSNumbers *num) { double cosRA0, sinRA0, cosDec0, sinDec0; const Eigen::Matrix3d &precessionMatrix = num->p2(); Eigen::Vector3d v, s; RA0.SinCos(sinRA0, cosRA0); Dec0.SinCos(sinDec0, cosDec0); s[0] = cosRA0 * cosDec0; s[1] = sinRA0 * cosDec0; s[2] = sinDec0; // NOTE: Rotation matrices are the fastest way to do rotations on // a vector. Quaternions need more multiplications. The rotation // matrix compensates in some sense by having more 'precomputed' // multiplications. The matrix elements seem to cache nicely, so // there isn't much overhead in accessing them. //Multiply P2 and s to get v, the vector representing the new coords. // for ( unsigned int i=0; i<3; ++i ) { // v[i] = 0.0; // for (uint j=0; j< 3; ++j) { // v[i] += num->p2( j, i )*s[j]; // } // } v.noalias() = precessionMatrix * s; //Extract RA, Dec from the vector: RA.setUsing_atan2(v[1], v[0]); RA.reduceToRange(dms::ZERO_TO_2PI); Dec.setUsing_asin(v[2]); } SkyPoint SkyPoint::deprecess(const KSNumbers *num, long double epoch) { SkyPoint p1(RA, Dec); long double now = num->julianDay(); p1.precessFromAnyEpoch(now, epoch); if ((std::isnan(RA0.Degrees()) || std::isnan(Dec0.Degrees())) || (!std::isnan(Dec0.Degrees()) && fabs(Dec0.Degrees()) > 90.0)) { // We have invalid RA0 and Dec0, so set them if epoch = J2000. Otherwise, do not touch. if (epoch == J2000) { RA0 = p1.ra(); Dec0 = p1.dec(); } } return p1; } void SkyPoint::nutate(const KSNumbers *num) { double cosRA, sinRA, cosDec, sinDec, tanDec; double cosOb, sinOb; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); num->obliquity()->SinCos(sinOb, cosOb); //Step 2: Nutation if (fabs(Dec.Degrees()) < 80.0) //approximate method { tanDec = sinDec / cosDec; double dRA = num->dEcLong() * (cosOb + sinOb * sinRA * tanDec) - num->dObliq() * cosRA * tanDec; double dDec = num->dEcLong() * (sinOb * cosRA) + num->dObliq() * sinRA; RA.setD(RA.Degrees() + dRA); Dec.setD(Dec.Degrees() + dDec); } else //exact method { dms EcLong, EcLat; findEcliptic(num->obliquity(), EcLong, EcLat); //Add dEcLong to the Ecliptic Longitude dms newLong(EcLong.Degrees() + num->dEcLong()); setFromEcliptic(num->obliquity(), newLong, EcLat); } } SkyPoint SkyPoint::moveAway(const SkyPoint &from, double dist) const { CachingDms lat1, dtheta; if (dist == 0.0) { qDebug() << "moveAway called with zero distance!"; return *this; } double dst = fabs(dist * dms::DegToRad / 3600.0); // In radian // Compute the bearing angle w.r.t. the RA axis ("latitude") CachingDms dRA(ra() - from.ra()); CachingDms dDec(dec() - from.dec()); double bearing = atan2(dRA.sin() / dRA.cos(), dDec.sin()); // Do not use dRA = PI / 2!! //double bearing = atan2( dDec.radians() , dRA.radians() ); // double dir0 = (dist >= 0 ) ? bearing : bearing + dms::PI; // in radian double dir0 = bearing + std::signbit(dist) * dms::PI; // might be faster? double sinDst = sin(dst), cosDst = cos(dst); lat1.setUsing_asin(dec().sin() * cosDst + dec().cos() * sinDst * cos(dir0)); dtheta.setUsing_atan2(sin(dir0) * sinDst * dec().cos(), cosDst - dec().sin() * lat1.sin()); return SkyPoint(ra() + dtheta, lat1); } bool SkyPoint::checkBendLight() { // First see if we are close enough to the sun to bother about the // gravitational lensing effect. We correct for the effect at // least till b = 10 solar radii, where the effect is only about // 0.06". Assuming min. sun-earth distance is 200 solar radii. static const dms maxAngle(1.75 * (30.0 / 200.0) / dms::DegToRad); if (!m_Sun) { SkyComposite *skycomopsite = KStarsData::Instance()->skyComposite(); if (skycomopsite == nullptr) return false; m_Sun = (KSSun *)skycomopsite->findByName("Sun"); if (m_Sun == nullptr) return false; } // TODO: This can be optimized further. We only need a ballpark estimate of the distance to the sun to start with. return (fabs(angularDistanceTo(static_cast(m_Sun)).Degrees()) <= maxAngle.Degrees()); // NOTE: dynamic_cast is slow and not important here. } bool SkyPoint::bendlight() { // NOTE: This should be applied before aberration // NOTE: One must call checkBendLight() before unnecessarily calling this. // We correct for GR effects // NOTE: This code is buggy. The sun needs to be initialized to // the current epoch -- but we are not certain that this is the // case. We have, as of now, no way of telling if the sun is // initialized or not. If we initialize the sun here, we will be // slowing down the program rather substantially and potentially // introducing bugs. Therefore, we just ignore this problem, and // hope that whenever the user is interested in seeing the effects // of GR, we have the sun initialized correctly. This is usually // the case. When the sun is not correctly initialized, rearth() // is not computed, so we just assume it is nominally equal to 1 // AU to get a reasonable estimate. Q_ASSERT(m_Sun); double corr_sec = 1.75 * m_Sun->physicalSize() / ((std::isfinite(m_Sun->rearth()) ? m_Sun->rearth() : 1) * AU_KM * angularDistanceTo(static_cast(m_Sun)).sin()); Q_ASSERT(corr_sec > 0); SkyPoint sp = moveAway(*m_Sun, corr_sec); setRA(sp.ra()); setDec(sp.dec()); return true; } void SkyPoint::aberrate(const KSNumbers *num) { double cosRA, sinRA, cosDec, sinDec; double cosOb, sinOb, cosL, sinL, cosP, sinP; double K = num->constAberr().Degrees(); //constant of aberration double e = num->earthEccentricity(); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); num->obliquity()->SinCos(sinOb, cosOb); // double tanOb = sinOb/cosOb; num->sunTrueLongitude().SinCos(sinL, cosL); num->earthPerihelionLongitude().SinCos(sinP, cosP); //Step 3: Aberration // double dRA = -1.0 * K * ( cosRA * cosL * cosOb + sinRA * sinL )/cosDec // + e * K * ( cosRA * cosP * cosOb + sinRA * sinP )/cosDec; // double dDec = -1.0 * K * ( cosL * cosOb * ( tanOb * cosDec - sinRA * sinDec ) + cosRA * sinDec * sinL ) // + e * K * ( cosP * cosOb * ( tanOb * cosDec - sinRA * sinDec ) + cosRA * sinDec * sinP ); double dRA = K * (cosRA * cosOb / cosDec) * (e * cosP - cosL); double dDec = K * (sinRA * (sinOb * cosDec - cosOb * sinDec) * (e * cosP - cosL) + cosRA * sinDec * (e * sinP - sinL)); RA.setD(RA.Degrees() + dRA); Dec.setD(Dec.Degrees() + dDec); } // Note: This method is one of the major rate determining factors in how fast the map pans / zooms in or out void SkyPoint::updateCoords(const KSNumbers *num, bool /*includePlanets*/, const CachingDms *lat, const CachingDms *LST, bool forceRecompute) { //Correct the catalog coordinates for the time-dependent effects //of precession, nutation and aberration bool recompute, lens; // NOTE: The same short-circuiting checks are also implemented in // StarObject::JITUpdate(), even before calling // updateCoords(). While this is code-duplication, these bits of // code need to be really optimized, at least for stars. For // optimization purposes, the code is left duplicated in two // places. Please be wary of changing one without changing the // other. Q_ASSERT(std::isfinite(lastPrecessJD)); if (Options::useRelativistic() && checkBendLight()) { recompute = true; lens = true; } else { recompute = (Options::alwaysRecomputeCoordinates() || forceRecompute || std::abs(lastPrecessJD - num->getJD()) >= 0.00069444); // Update once per solar minute lens = false; } if (recompute) { precess(num); nutate(num); if (lens) bendlight(); // FIXME: Shouldn't we apply this on the horizontal coordinates? aberrate(num); lastPrecessJD = num->getJD(); Q_ASSERT(std::isfinite(RA.Degrees()) && std::isfinite(Dec.Degrees())); } if (lat || LST) qWarning() << i18n("lat and LST parameters should only be used in KSPlanetBase objects."); } void SkyPoint::precessFromAnyEpoch(long double jd0, long double jdf) { double cosRA, sinRA, cosDec, sinDec; double v[3], s[3]; RA = RA0; Dec = Dec0; // Is this necessary? if (jd0 == jdf) return; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); if (jd0 == B1950) { B1950ToJ2000(); jd0 = J2000; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); } if (jd0 != jdf) { // The original coordinate is referred to the FK5 system and // is NOT J2000. if (jd0 != J2000) { //v is a column vector representing input coordinates. v[0] = cosRA * cosDec; v[1] = sinRA * cosDec; v[2] = sinDec; //Need to first precess to J2000.0 coords //s is the product of P1 and v; s represents the //coordinates precessed to J2000 KSNumbers num(jd0); for (unsigned int i = 0; i < 3; ++i) { s[i] = num.p1(0, i) * v[0] + num.p1(1, i) * v[1] + num.p1(2, i) * v[2]; } //Input coords already in J2000, set s accordingly. } else { s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; } if (jdf == B1950) { RA.setRadians(atan2(s[1], s[0])); Dec.setRadians(asin(s[2])); J2000ToB1950(); return; } KSNumbers num(jdf); for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p2(0, i) * s[0] + num.p2(1, i) * s[1] + num.p2(2, i) * s[2]; } RA.setUsing_atan2(v[1], v[0]); Dec.setUsing_asin(v[2]); RA.reduceToRange(dms::ZERO_TO_2PI); return; } } void SkyPoint::apparentCoord(long double jd0, long double jdf) { precessFromAnyEpoch(jd0, jdf); KSNumbers num(jdf); nutate(&num); if (Options::useRelativistic() && checkBendLight()) bendlight(); aberrate(&num); } void SkyPoint::Equatorial1950ToGalactic(dms &galLong, dms &galLat) { double a = 192.25; double sinb, cosb, sina_RA, cosa_RA, sinDEC, cosDEC, tanDEC; dms c(303.0); dms b(27.4); tanDEC = tan(Dec.radians()); b.SinCos(sinb, cosb); dms(a - RA.Degrees()).SinCos(sina_RA, cosa_RA); Dec.SinCos(sinDEC, cosDEC); galLong.setRadians(c.radians() - atan2(sina_RA, cosa_RA * sinb - tanDEC * cosb)); galLong.reduceToRange(dms::ZERO_TO_2PI); galLat.setRadians(asin(sinDEC * sinb + cosDEC * cosb * cosa_RA)); } void SkyPoint::GalacticToEquatorial1950(const dms *galLong, const dms *galLat) { double a = 123.0; double sinb, cosb, singLat, cosgLat, tangLat, singLong_a, cosgLong_a; dms c(12.25); dms b(27.4); tangLat = tan(galLat->radians()); galLat->SinCos(singLat, cosgLat); dms(galLong->Degrees() - a).SinCos(singLong_a, cosgLong_a); b.SinCos(sinb, cosb); RA.setRadians(c.radians() + atan2(singLong_a, cosgLong_a * sinb - tangLat * cosb)); RA.reduceToRange(dms::ZERO_TO_2PI); Dec.setRadians(asin(singLat * sinb + cosgLat * cosb * cosgLong_a)); } void SkyPoint::B1950ToJ2000(void) { double cosRA, sinRA, cosDec, sinDec; // double cosRA0, sinRA0, cosDec0, sinDec0; double v[3], s[3]; // 1984 January 1 0h KSNumbers num(2445700.5); // Eterms due to aberration addEterms(); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); // Precession from B1950 to J1984 s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p2b(0, i) * s[0] + num.p2b(1, i) * s[1] + num.p2b(2, i) * s[2]; } // RA zero-point correction at 1984 day 1, 0h. RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); RA.setH(RA.Hours() + 0.06390 / 3600.); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; // Precession from 1984 to J2000. for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p1(0, i) * s[0] + num.p1(1, i) * s[1] + num.p1(2, i) * s[2]; } RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); } void SkyPoint::J2000ToB1950(void) { double cosRA, sinRA, cosDec, sinDec; // double cosRA0, sinRA0, cosDec0, sinDec0; double v[3], s[3]; // 1984 January 1 0h KSNumbers num(2445700.5); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; // Precession from J2000 to 1984 day, 0h. for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p2(0, i) * s[0] + num.p2(1, i) * s[1] + num.p2(2, i) * s[2]; } RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); // RA zero-point correction at 1984 day 1, 0h. RA.setH(RA.Hours() - 0.06390 / 3600.); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); // Precession from B1950 to J1984 s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p1b(0, i) * s[0] + num.p1b(1, i) * s[1] + num.p1b(2, i) * s[2]; } RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); // Eterms due to aberration subtractEterms(); } SkyPoint SkyPoint::Eterms(void) { double sd, cd, sinEterm, cosEterm; dms raTemp, raDelta, decDelta; Dec.SinCos(sd, cd); raTemp.setH(RA.Hours() + 11.25); raTemp.SinCos(sinEterm, cosEterm); raDelta.setH(0.0227 * sinEterm / (3600. * cd)); decDelta.setD(0.341 * cosEterm * sd / 3600. + 0.029 * cd / 3600.); return SkyPoint(raDelta, decDelta); } void SkyPoint::addEterms(void) { SkyPoint spd = Eterms(); RA = RA + spd.ra(); Dec = Dec + spd.dec(); } void SkyPoint::subtractEterms(void) { SkyPoint spd = Eterms(); RA = RA - spd.ra(); Dec = Dec - spd.dec(); } dms SkyPoint::angularDistanceTo(const SkyPoint *sp, double *const positionAngle) const { // double dalpha = sp->ra().radians() - ra().radians() ; // double ddelta = sp->dec().radians() - dec().radians(); CachingDms dalpha = sp->ra() - ra(); CachingDms ddelta = sp->dec() - dec(); // double sa = sin(dalpha/2.); // double sd = sin(ddelta/2.); // double hava = sa*sa; // double havd = sd*sd; // Compute the haversin directly: double hava = (1 - dalpha.cos()) / 2.; double havd = (1 - ddelta.cos()) / 2.; // Haversine law double aux = havd + (sp->dec().cos()) * dec().cos() * hava; dms angDist; angDist.setRadians(2. * fabs(asin(sqrt(aux)))); if (positionAngle) { // Also compute the position angle of the line from this SkyPoint to sp //*positionAngle = acos( tan(-ddelta)/tan( angDist.radians() ) ); // FIXME: Might fail for large ddelta / zero angDist //if( -dalpha < 0 ) // *positionAngle = 2*M_PI - *positionAngle; *positionAngle = atan2f(dalpha.sin(), (dec().cos()) * tan(sp->dec().radians()) - (dec().sin()) * dalpha.cos()) * 180 / M_PI; } return angDist; } double SkyPoint::vRSun(long double jd0) { double ca, sa, cd, sd, vsun; double cosRA, sinRA, cosDec, sinDec; /* Sun apex (where the sun goes) coordinates */ dms asun(270.9592); // Right ascention: 18h 3m 50.2s [J2000] dms dsun(30.00467); // Declination: 30^o 0' 16.8'' [J2000] vsun = 20.; // [km/s] asun.SinCos(sa, ca); dsun.SinCos(sd, cd); /* We need an auxiliary SkyPoint since we need the * source referred to the J2000 equinox and we do not want to ovewrite * the current values */ SkyPoint aux; aux.set(RA0, Dec0); aux.precessFromAnyEpoch(jd0, J2000); aux.ra().SinCos(sinRA, cosRA); aux.dec().SinCos(sinDec, cosDec); /* Computation is done performing the scalar product of a unitary vector in the direction of the source with the vector velocity of Sun, both being in the LSR reference system: Vlsr = Vhel + Vsun.u_radial => Vlsr = Vhel + vsun(cos D cos A,cos D sen A,sen D).(cos d cos a,cos d sen a,sen d) Vhel = Vlsr - Vsun.u_radial */ return vsun * (cd * cosDec * (cosRA * ca + sa * sinRA) + sd * sinDec); } double SkyPoint::vHeliocentric(double vlsr, long double jd0) { return vlsr - vRSun(jd0); } double SkyPoint::vHelioToVlsr(double vhelio, long double jd0) { return vhelio + vRSun(jd0); } double SkyPoint::vREarth(long double jd0) { double sinRA, sinDec, cosRA, cosDec; /* u_radial = unitary vector in the direction of the source Vlsr = Vhel + Vsun.u_radial = Vgeo + VEarth.u_radial + Vsun.u_radial => Vgeo = (Vlsr -Vsun.u_radial) - VEarth.u_radial = Vhel - VEarth.u_radial = Vhel - (vx, vy, vz).(cos d cos a,cos d sen a,sen d) */ /* We need an auxiliary SkyPoint since we need the * source referred to the J2000 equinox and we do not want to ovewrite * the current values */ SkyPoint aux(RA0, Dec0); aux.precessFromAnyEpoch(jd0, J2000); aux.ra().SinCos(sinRA, cosRA); aux.dec().SinCos(sinDec, cosDec); /* vEarth is referred to the J2000 equinox, hence we need that the source coordinates are also in the same reference system. */ KSNumbers num(jd0); return num.vEarth(0) * cosDec * cosRA + num.vEarth(1) * cosDec * sinRA + num.vEarth(2) * sinDec; } double SkyPoint::vGeocentric(double vhelio, long double jd0) { return vhelio - vREarth(jd0); } double SkyPoint::vGeoToVHelio(double vgeo, long double jd0) { return vgeo + vREarth(jd0); } double SkyPoint::vRSite(double vsite[3]) { double sinRA, sinDec, cosRA, cosDec; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); return vsite[0] * cosDec * cosRA + vsite[1] * cosDec * sinRA + vsite[2] * sinDec; } double SkyPoint::vTopoToVGeo(double vtopo, double vsite[3]) { return vtopo + vRSite(vsite); } double SkyPoint::vTopocentric(double vgeo, double vsite[3]) { return vgeo - vRSite(vsite); } bool SkyPoint::checkCircumpolar(const dms *gLat) const { return fabs(dec().Degrees()) > (90 - fabs(gLat->Degrees())); } dms SkyPoint::altRefracted() const { if (Options::useRefraction()) return refract(Alt); else return Alt; } double SkyPoint::refractionCorr(double alt) { return 1.02 / tan(dms::DegToRad * (alt + 10.3 / (alt + 5.11))) / 60; } double SkyPoint::refract(const double alt) { static double corrCrit = SkyPoint::refractionCorr(SkyPoint::altCrit); if (alt > SkyPoint::altCrit) return (alt + SkyPoint::refractionCorr(alt)); else return (alt + corrCrit * (alt + 90) / (SkyPoint::altCrit + 90)); // Linear extrapolation from corrCrit at altCrit to 0 at -90 degrees } // Found uncorrected value by solving equation. This is OK since // unrefract is never called in loops. // // Convergence is quite fast just a few iterations. double SkyPoint::unrefract(const double alt) { double h0 = alt; double h1 = alt - (refract(h0) - h0); // It's probably okay to add h0 in refract() and subtract it here, since refract() is called way more frequently. while (fabs(h1 - h0) > 1e-4) { h0 = h1; h1 = alt - (refract(h0) - h0); } return h1; } dms SkyPoint::findAltitude(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour) { Q_ASSERT(p); if (!p) return dms(NaN::d); // Jasem 2015-08-24 Using correct procedure to find altitude return SkyPoint::timeTransformed(p, dt, geo, hour).alt(); } SkyPoint SkyPoint::timeTransformed(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour) { Q_ASSERT(p); if (!p) return SkyPoint(NaN::d, NaN::d); // Jasem 2015-08-24 Using correct procedure to find altitude SkyPoint sp = *p; // make a copy KStarsDateTime targetDateTime = dt.addSecs(hour * 3600.0); dms LST = geo->GSTtoLST(targetDateTime.gst()); sp.EquatorialToHorizontal(&LST, geo->lat()); return sp; } double SkyPoint::maxAlt(const dms &lat) const { double retval = (lat.Degrees() + 90. - dec().Degrees()); if (retval > 90.) retval = 180. - retval; return retval; } double SkyPoint::minAlt(const dms &lat) const { double retval = (lat.Degrees() - 90. + dec().Degrees()); if (retval < -90.) retval = 180. + retval; return retval; } diff --git a/kstars/skyobjects/skypoint.h b/kstars/skyobjects/skypoint.h index 9d5c03a16..5f6f11220 100644 --- a/kstars/skyobjects/skypoint.h +++ b/kstars/skyobjects/skypoint.h @@ -1,580 +1,650 @@ /*************************************************************************** skypoint.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001-2005 by Jason Harris email : jharris@30doradus.org copyright : (C) 2004-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "cachingdms.h" #include "kstarsdatetime.h" #include //#define PROFILE_COORDINATE_CONVERSION class KSNumbers; class KSSun; class GeoLocation; -/** @class SkyPoint - * - *The sky coordinates of a point in the sky. The - *coordinates are stored in both Equatorial (Right Ascension, - *Declination) and Horizontal (Azimuth, Altitude) coordinate systems. - *Provides set/get functions for each coordinate angle, and functions - *to convert between the Equatorial and Horizon coordinate systems. - * - *Because the coordinate values change slowly over time (due to - *precession, nutation), the "catalog coordinates" are stored - *(RA0, Dec0), which were the true coordinates on Jan 1, 2000. - *The true coordinates (RA, Dec) at any other epoch can be found - *from the catalog coordinates using updateCoords(). - *@short Stores dms coordinates for a point in the sky. - *for converting between coordinate systems. - *@author Jason Harris - *@version 1.0 - */ +/** + * @class SkyPoint + * + * The sky coordinates of a point in the sky. The + * coordinates are stored in both Equatorial (Right Ascension, + * Declination) and Horizontal (Azimuth, Altitude) coordinate systems. + * Provides set/get functions for each coordinate angle, and functions + * to convert between the Equatorial and Horizon coordinate systems. + * + * Because the coordinate values change slowly over time (due to + * precession, nutation), the "catalog coordinates" are stored + * (RA0, Dec0), which were the true coordinates on Jan 1, 2000. + * The true coordinates (RA, Dec) at any other epoch can be found + * from the catalog coordinates using updateCoords(). + * @short Stores dms coordinates for a point in the sky. + * for converting between coordinate systems. + * + * @author Jason Harris + * @version 1.0 + */ class SkyPoint { public: - /** Default constructor: Sets RA, Dec and RA0, Dec0 according - *to arguments. Does not set Altitude or Azimuth. - *@param r Right Ascension - *@param d Declination - */ + /** + * Default constructor: Sets RA, Dec and RA0, Dec0 according + * to arguments. Does not set Altitude or Azimuth. + * + * @param r Right Ascension + * @param d Declination + */ SkyPoint(const dms &r, const dms &d) : RA0(r), Dec0(d), RA(r), Dec(d), lastPrecessJD(J2000) {} SkyPoint(const CachingDms &r, const CachingDms &d) : RA0(r), Dec0(d), RA(r), Dec(d), lastPrecessJD(J2000) {} - /** Alternate constructor using double arguments, for convenience. - *It behaves essentially like the default constructor. - *@param r Right Ascension, expressed as a double - *@param d Declination, expressed as a double - *@note This also sets RA0 and Dec0 - */ + /** + * Alternate constructor using double arguments, for convenience. + * It behaves essentially like the default constructor. + * + * @param r Right Ascension, expressed as a double + * @param d Declination, expressed as a double + * @note This also sets RA0 and Dec0 + */ //FIXME: this (*15.0) thing is somewhat hacky. explicit SkyPoint(double r, double d) : RA0(r * 15.0), Dec0(d), RA(r * 15.0), Dec(d), lastPrecessJD(J2000) {} - /** - *@short Default constructor. Sets nonsense values for RA, Dec etc - */ + /** @short Default constructor. Sets nonsense values for RA, Dec etc */ SkyPoint(); - virtual ~SkyPoint(); + virtual ~SkyPoint() = default; //// //// 1. Setting Coordinates //// ======================= /** - * @short Sets RA, Dec and RA0, Dec0 according to arguments. - * Does not set Altitude or Azimuth. - * @param r Right Ascension - * @param d Declination - * @note This function also sets RA0 and Dec0 to the same values, so call at your own peril! - * @note FIXME: This method must be removed, or an epoch argument must be added. - */ + * @short Sets RA, Dec and RA0, Dec0 according to arguments. + * Does not set Altitude or Azimuth. + * + * @param r Right Ascension + * @param d Declination + * @note This function also sets RA0 and Dec0 to the same values, so call at your own peril! + * @note FIXME: This method must be removed, or an epoch argument must be added. + */ void set(const dms &r, const dms &d); - /** Sets RA0, the catalog Right Ascension. - *@param r catalog Right Ascension. - */ + /** + * Sets RA0, the catalog Right Ascension. + * + * @param r catalog Right Ascension. + */ inline void setRA0(dms r) { RA0 = r; } inline void setRA0(CachingDms r) { RA0 = r; } - /** Overloaded member function, provided for convenience. - *It behaves essentially like the above function. - *@param r Right Ascension, expressed as a double. - */ + /** + * Overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * @param r Right Ascension, expressed as a double. + */ inline void setRA0(double r) { RA0.setH(r); } - /** Sets Dec0, the catalog Declination. - *@param d catalog Declination. - */ + /** + * Sets Dec0, the catalog Declination. + * + * @param d catalog Declination. + */ inline void setDec0(dms d) { Dec0 = d; } inline void setDec0(const CachingDms &d) { Dec0 = d; } - /** Overloaded member function, provided for convenience. - *It behaves essentially like the above function. - *@param d Declination, expressed as a double. - */ + /** + * Overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * @param d Declination, expressed as a double. + */ inline void setDec0(double d) { Dec0.setD(d); } - /** Sets RA, the current Right Ascension. - *@param r Right Ascension. - */ + /** + * Sets RA, the current Right Ascension. + * + * @param r Right Ascension. + */ inline void setRA(dms& r) { RA = r; } inline void setRA(const CachingDms &r) { RA = r; } - /** Overloaded member function, provided for convenience. - *It behaves essentially like the above function. - *@param r Right Ascension, expressed as a double. - */ + /** + * Overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * @param r Right Ascension, expressed as a double. + */ inline void setRA(double r) { RA.setH(r); } - /** Sets Dec, the current Declination - *@param d Declination. - */ + /** + * Sets Dec, the current Declination + * + * @param d Declination. + */ inline void setDec(dms d) { Dec = d; } inline void setDec(const CachingDms &d) { Dec = d; } - /** Overloaded member function, provided for convenience. - *It behaves essentially like the above function. - *@param d Declination, expressed as a double. - */ + /** + * Overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * @param d Declination, expressed as a double. + */ inline void setDec(double d) { Dec.setD(d); } - /** Sets Alt, the Altitude. - *@param alt Altitude. - */ + /** + * Sets Alt, the Altitude. + * + * @param alt Altitude. + */ inline void setAlt(dms alt) { Alt = alt; } - /** Overloaded member function, provided for convenience. - *It behaves essentially like the above function. - *@param alt Altitude, expressed as a double. - */ + /** + * Overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * @param alt Altitude, expressed as a double. + */ inline void setAlt(double alt) { Alt.setD(alt); } - /** Sets Az, the Azimuth. - *@param az Azimuth. - */ + /** + * Sets Az, the Azimuth. + * + * @param az Azimuth. + */ inline void setAz(dms az) { Az = az; } - /** Overloaded member function, provided for convenience. - *It behaves essentially like the above function. - *@param az Azimuth, expressed as a double. - */ + /** + * Overloaded member function, provided for convenience. + * It behaves essentially like the above function. + * + * @param az Azimuth, expressed as a double. + */ inline void setAz(double az) { Az.setD(az); } //// //// 2. Returning coordinates. //// ========================= /** @return a pointer to the catalog Right Ascension. */ inline const CachingDms &ra0() const { return RA0; } /** @return a pointer to the catalog Declination. */ inline const CachingDms &dec0() const { return Dec0; } /** @returns a pointer to the current Right Ascension. */ inline const CachingDms &ra() const { return RA; } /** @return a pointer to the current Declination. */ inline const CachingDms &dec() const { return Dec; } /** @return a pointer to the current Azimuth. */ inline const dms &az() const { return Az; } /** @return a pointer to the current Altitude. */ inline const dms &alt() const { return Alt; } - /** @return refracted altitude. This function uses - * Options::useRefraction to determine whether refraction - * correction should be applied */ + /** + * @return refracted altitude. This function uses + * Options::useRefraction to determine whether refraction + * correction should be applied + */ dms altRefracted() const; - /** - * @return the JD for the precessed coordinates - */ + /** @return the JD for the precessed coordinates */ inline double getLastPrecessJD() const { return lastPrecessJD; } /** - * @return the airmass of the point. Convenience method. - * @note Question: is it better to use alt or refracted alt? Minor difference, probably doesn't matter. - */ + * @return the airmass of the point. Convenience method. + * @note Question: is it better to use alt or refracted alt? Minor difference, probably doesn't matter. + */ inline double airmass() const { return 1. / sin(alt().radians()); } //// //// 3. Coordinate conversions. //// ========================== - /** Determine the (Altitude, Azimuth) coordinates of the - *SkyPoint from its (RA, Dec) coordinates, given the local - *sidereal time and the observer's latitude. - *@param LST pointer to the local sidereal time - *@param lat pointer to the geographic latitude - */ + /** + * Determine the (Altitude, Azimuth) coordinates of the + * SkyPoint from its (RA, Dec) coordinates, given the local + * sidereal time and the observer's latitude. + * @param LST pointer to the local sidereal time + * @param lat pointer to the geographic latitude + */ void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat); // Deprecated method provided for compatibility void EquatorialToHorizontal(const dms *LST, const dms *lat); - /** Determine the (RA, Dec) coordinates of the - *SkyPoint from its (Altitude, Azimuth) coordinates, given the local - *sidereal time and the observer's latitude. - *@param LST pointer to the local sidereal time - *@param lat pointer to the geographic latitude - */ + /** + * Determine the (RA, Dec) coordinates of the + * SkyPoint from its (Altitude, Azimuth) coordinates, given the local + * sidereal time and the observer's latitude. + * + * @param LST pointer to the local sidereal time + * @param lat pointer to the geographic latitude + */ void HorizontalToEquatorial(const dms *LST, const dms *lat); - /** Determine the Ecliptic coordinates of the SkyPoint, given the Julian Date. - *The ecliptic coordinates are returned as reference arguments (since - *they are not stored internally) - */ + /** + * Determine the Ecliptic coordinates of the SkyPoint, given the Julian Date. + * The ecliptic coordinates are returned as reference arguments (since + * they are not stored internally) + */ void findEcliptic(const CachingDms *Obliquity, dms &EcLong, dms &EcLat); - /** Set the current (RA, Dec) coordinates of the - *SkyPoint, given pointers to its Ecliptic (Long, Lat) coordinates, and - *to the current obliquity angle (the angle between the equator and ecliptic). - */ + /** + * Set the current (RA, Dec) coordinates of the + * SkyPoint, given pointers to its Ecliptic (Long, Lat) coordinates, and + * to the current obliquity angle (the angle between the equator and ecliptic). + */ void setFromEcliptic(const CachingDms *Obliquity, const dms &EcLong, const dms &EcLat); - /** Computes galactic coordinates from equatorial coordinates referred to - * epoch 1950. RA and Dec are, therefore assumed to be B1950 - * coordinates. - */ + /** + * Computes galactic coordinates from equatorial coordinates referred to + * epoch 1950. RA and Dec are, therefore assumed to be B1950 coordinates. + */ void Equatorial1950ToGalactic(dms &galLong, dms &galLat); - /** Computes equatorial coordinates referred to 1950 from galactic ones referred to - * epoch B1950. RA and Dec are, therefore assumed to be B1950 - * coordinates. - */ + /** + * Computes equatorial coordinates referred to 1950 from galactic ones referred to + * epoch B1950. RA and Dec are, therefore assumed to be B1950 coordinates. + */ void GalacticToEquatorial1950(const dms *galLong, const dms *galLat); //// //// 4. Coordinate update/corrections. //// ================================= - /** Determine the current coordinates (RA, Dec) from the catalog - *coordinates (RA0, Dec0), accounting for both precession and nutation. - *@param num pointer to KSNumbers object containing current values of time-dependent variables. - *@param includePlanets does nothing in this implementation (see KSPlanetBase::updateCoords()). - *@param lat does nothing in this implementation (see KSPlanetBase::updateCoords()). - *@param LST does nothing in this implementation (see KSPlanetBase::updateCoords()). - *@param forceRecompute reapplies precession, nutation and aberration even if the time passed since the last computation is not significant. - */ + /** + * Determine the current coordinates (RA, Dec) from the catalog + * coordinates (RA0, Dec0), accounting for both precession and nutation. + * @param num pointer to KSNumbers object containing current values of time-dependent variables. + * @param includePlanets does nothing in this implementation (see KSPlanetBase::updateCoords()). + * @param lat does nothing in this implementation (see KSPlanetBase::updateCoords()). + * @param LST does nothing in this implementation (see KSPlanetBase::updateCoords()). + * @param forceRecompute reapplies precession, nutation and aberration even if the time passed + * since the last computation is not significant. + */ virtual void updateCoords(const KSNumbers *num, bool includePlanets = true, const CachingDms *lat = nullptr, const CachingDms *LST = nullptr, bool forceRecompute = false); /** - * @brief updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, nullptr, nullptr, true) - * @param num pointer to KSNumbers object containing current values of time-dependent variables. - */ + * @brief updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, nullptr, nullptr, true) + * + * @param num pointer to KSNumbers object containing current values of time-dependent variables. + */ virtual void updateCoordsNow(const KSNumbers *num) { updateCoords(num, false, nullptr, nullptr, true); } - /** Computes the apparent coordinates for this SkyPoint for any epoch, - *accounting for the effects of precession, nutation, and aberration. - *Similar to updateCoords(), but the starting epoch need not be - *J2000, and the target epoch need not be the present time. - *@param jd0 Julian Day which identifies the original epoch - *@param jdf Julian Day which identifies the final epoch - */ + /** + * Computes the apparent coordinates for this SkyPoint for any epoch, + * accounting for the effects of precession, nutation, and aberration. + * Similar to updateCoords(), but the starting epoch need not be + * J2000, and the target epoch need not be the present time. + * + * @param jd0 Julian Day which identifies the original epoch + * @param jdf Julian Day which identifies the final epoch + */ void apparentCoord(long double jd0, long double jdf); - /** Determine the effects of nutation for this SkyPoint. - *@param num pointer to KSNumbers object containing current values of - *time-dependent variables. - */ + /** + * Determine the effects of nutation for this SkyPoint. + * + * @param num pointer to KSNumbers object containing current values of + * time-dependent variables. + */ void nutate(const KSNumbers *num); /** - *@short Check if this sky point is close enough to the sun for - * gravitational lensing to be significant - */ + * @short Check if this sky point is close enough to the sun for + * gravitational lensing to be significant + */ bool checkBendLight(); - /** Correct for the effect of "bending" of light around the sun for - * positions near the sun. - * - * General Relativity tells us that a photon with an impact - * parameter b is deflected through an angle 1.75" (Rs / b) where - * Rs is the solar radius. - * - * @return: true if the light was bent, false otherwise - */ + /** + * Correct for the effect of "bending" of light around the sun for + * positions near the sun. + * + * General Relativity tells us that a photon with an impact + * parameter b is deflected through an angle 1.75" (Rs / b) where + * Rs is the solar radius. + * + * @return: true if the light was bent, false otherwise + */ bool bendlight(); /** - *@short Obtain a Skypoint with RA0 and Dec0 set from the RA, Dec - * of this skypoint. Also set the RA0, Dec0 of this SkyPoint if not - * set already and the target epoch is J2000. - */ + * @short Obtain a Skypoint with RA0 and Dec0 set from the RA, Dec + * of this skypoint. Also set the RA0, Dec0 of this SkyPoint if not + * set already and the target epoch is J2000. + */ SkyPoint deprecess(const KSNumbers *num, long double epoch = J2000); - /** Determine the effects of aberration for this SkyPoint. - *@param num pointer to KSNumbers object containing current values of - *time-dependent variables. - */ + /** + * Determine the effects of aberration for this SkyPoint. + * + * @param num pointer to KSNumbers object containing current values of + * time-dependent variables. + */ void aberrate(const KSNumbers *num); - /** General case of precession. It precess from an original epoch to a - *final epoch. In this case RA0, and Dec0 from SkyPoint object represent - *the coordinates for the original epoch and not for J2000, as usual. - *@param jd0 Julian Day which identifies the original epoch - *@param jdf Julian Day which identifies the final epoch - */ + /** + * General case of precession. It precess from an original epoch to a + * final epoch. In this case RA0, and Dec0 from SkyPoint object represent + * the coordinates for the original epoch and not for J2000, as usual. + * + * @param jd0 Julian Day which identifies the original epoch + * @param jdf Julian Day which identifies the final epoch + */ void precessFromAnyEpoch(long double jd0, long double jdf); - /** Determine the E-terms of aberration - *In the past, the mean places of stars published in catalogs included - *the contribution to the aberration due to the ellipticity of the orbit - *of the Earth. These terms, known as E-terms were almost constant, and - *in the newer catalogs (FK5) are not included. Therefore to convert from - *FK4 to FK5 one has to compute these E-terms. - */ + /** + * Determine the E-terms of aberration + * In the past, the mean places of stars published in catalogs included + * the contribution to the aberration due to the ellipticity of the orbit + * of the Earth. These terms, known as E-terms were almost constant, and + * in the newer catalogs (FK5) are not included. Therefore to convert from + * FK4 to FK5 one has to compute these E-terms. + */ SkyPoint Eterms(void); - /** Exact precession from Besselian epoch 1950 to epoch J2000. The - *coordinates referred to the first epoch are in the - FK4 catalog, while the latter are in the Fk5 one. - *Reference: Smith, C. A.; Kaplan, G. H.; Hughes, J. A.; Seidelmann, - *P. K.; Yallop, B. D.; Hohenkerk, C. Y. - *Astronomical Journal, vol. 97, Jan. 1989, p. 265-279 - *This transformation requires 4 steps: - * - Correct E-terms - * - Precess from B1950 to 1984, January 1st, 0h, using Newcomb expressions - * - Add zero point correction in right ascension for 1984 - * - Precess from 1984, January 1st, 0h to J2000 - */ + /** + * Exact precession from Besselian epoch 1950 to epoch J2000. The + * coordinates referred to the first epoch are in the + * FK4 catalog, while the latter are in the Fk5 one. + * + * Reference: Smith, C. A.; Kaplan, G. H.; Hughes, J. A.; Seidelmann, + * P. K.; Yallop, B. D.; Hohenkerk, C. Y. + * Astronomical Journal, vol. 97, Jan. 1989, p. 265-279 + * + * This transformation requires 4 steps: + * - Correct E-terms + * - Precess from B1950 to 1984, January 1st, 0h, using Newcomb expressions + * - Add zero point correction in right ascension for 1984 + * - Precess from 1984, January 1st, 0h to J2000 + */ void B1950ToJ2000(void); - /** Exact precession from epoch J2000 Besselian epoch 1950. The coordinates - *referred to the first epoch are in the FK4 catalog, while the - *latter are in the Fk5 one. - *Reference: Smith, C. A.; Kaplan, G. H.; Hughes, J. A.; Seidelmann, - *P. K.; Yallop, B. D.; Hohenkerk, C. Y. - *Astronomical Journal, vol. 97, Jan. 1989, p. 265-279 - *This transformation requires 4 steps: - * - Precess from J2000 to 1984, January 1st, 0h - * - Add zero point correction in right ascension for 1984 - * - Precess from 1984, January 1st, 0h, to B1950 using Newcomb expressions - * - Correct E-terms - */ + /** + * Exact precession from epoch J2000 Besselian epoch 1950. The coordinates + * referred to the first epoch are in the FK4 catalog, while the + * latter are in the Fk5 one. + * + * Reference: Smith, C. A.; Kaplan, G. H.; Hughes, J. A.; Seidelmann, + * P. K.; Yallop, B. D.; Hohenkerk, C. Y. + * Astronomical Journal, vol. 97, Jan. 1989, p. 265-279 + * + * This transformation requires 4 steps: + * - Precess from J2000 to 1984, January 1st, 0h + * - Add zero point correction in right ascension for 1984 + * - Precess from 1984, January 1st, 0h, to B1950 using Newcomb expressions + * - Correct E-terms + */ void J2000ToB1950(void); - /** Coordinates in the FK4 catalog include the effect of aberration due - *to the ellipticity of the orbit of the Earth. Coordinates in the FK5 - *catalog do not include these terms. In order to convert from B1950 (FK4) - *to actual mean places one has to use this function. - */ + /** + * Coordinates in the FK4 catalog include the effect of aberration due + * to the ellipticity of the orbit of the Earth. Coordinates in the FK5 + * catalog do not include these terms. In order to convert from B1950 (FK4) + * to actual mean places one has to use this function. + */ void addEterms(void); - /** Coordinates in the FK4 catalog include the effect of aberration due - *to the ellipticity of the orbit of the Earth. Coordinates in the FK5 - *catalog do not include these terms. In order to convert from - * FK5 coordinates to B1950 (FK4) one has to use this function. - */ + /** + * Coordinates in the FK4 catalog include the effect of aberration due + * to the ellipticity of the orbit of the Earth. Coordinates in the FK5 + * catalog do not include these terms. In order to convert from + * FK5 coordinates to B1950 (FK4) one has to use this function. + */ void subtractEterms(void); - /** Computes the angular distance between two SkyObjects. The algorithm - * to compute this distance is: - * cos(distance) = sin(d1)*sin(d2) + cos(d1)*cos(d2)*cos(a1-a2) - * where a1,d1 are the coordinates of the first object and a2,d2 are - * the coordinates of the second object. - * However this algorithm is not accurate when the angular separation - * is small. - * Meeus provides a different algorithm in page 111 which we - * implement here. - * @param sp SkyPoint to which distance is to be calculated - * @param positionAngle if a non-null pointer is passed, the position angle [E of N] in degrees from this SkyPoint to sp is computed and stored in the passed variable. - * @return dms angle representing angular separation. - **/ + /** + * Computes the angular distance between two SkyObjects. The algorithm + * to compute this distance is: + * cos(distance) = sin(d1)*sin(d2) + cos(d1)*cos(d2)*cos(a1-a2) + * where a1,d1 are the coordinates of the first object and a2,d2 are + * the coordinates of the second object. + * However this algorithm is not accurate when the angular separation is small. + * Meeus provides a different algorithm in page 111 which we implement here. + * + * @param sp SkyPoint to which distance is to be calculated + * @param positionAngle if a non-null pointer is passed, the position angle [E of N] + * in degrees from this SkyPoint to sp is computed and stored in the passed variable. + * @return dms angle representing angular separation. + **/ dms angularDistanceTo(const SkyPoint *sp, double *const positionAngle = nullptr) const; - /** - * @return returns true if _current_ epoch RA / Dec match - */ + /** @return returns true if _current_ epoch RA / Dec match */ inline bool operator==(SkyPoint &p) const { return (ra() == p.ra() && dec() == p.dec()); } - /** Computes the velocity of the Sun projected on the direction of the source. - * - * @param jd Epoch expressed as julian day to which the source coordinates refer to. - * @return Radial velocity of the source referred to the barycenter of the solar system in km/s - **/ + /** + * Computes the velocity of the Sun projected on the direction of the source. + * + * @param jd Epoch expressed as julian day to which the source coordinates refer to. + * @return Radial velocity of the source referred to the barycenter of the solar system in km/s + **/ double vRSun(long double jd); - /** Computes the radial velocity of a source referred to the solar system barycenter - * from the radial velocity referred to the - * Local Standard of Rest, aka known as VLSR. To compute it we need the coordinates of the - * source the VLSR and the epoch for the source coordinates. - * - * @param vlsr radial velocity of the source referred to the LSR in km/s - * @param jd Epoch expressed as julian day to which the source coordinates refer to. - * @return Radial velocity of the source referred to the barycenter of the solar system in km/s - **/ + /** + * Computes the radial velocity of a source referred to the solar system barycenter + * from the radial velocity referred to the + * Local Standard of Rest, aka known as VLSR. To compute it we need the coordinates of the + * source the VLSR and the epoch for the source coordinates. + * + * @param vlsr radial velocity of the source referred to the LSR in km/s + * @param jd Epoch expressed as julian day to which the source coordinates refer to. + * @return Radial velocity of the source referred to the barycenter of the solar system in km/s + **/ double vHeliocentric(double vlsr, long double jd); - /** Computes the radial velocity of a source referred to the Local Standard of Rest, also known as VLSR - * from the radial velocity referred to the solar system barycenter - * - * @param vhelio radial velocity of the source referred to the LSR in km/s - * @param jd Epoch expressed as julian day to which the source coordinates refer to. - * @return Radial velocity of the source referred to the barycenter of the solar system in km/s - **/ + /** + * Computes the radial velocity of a source referred to the Local Standard of Rest, also known as VLSR + * from the radial velocity referred to the solar system barycenter + * + * @param vhelio radial velocity of the source referred to the LSR in km/s + * @param jd Epoch expressed as julian day to which the source coordinates refer to. + * @return Radial velocity of the source referred to the barycenter of the solar system in km/s + **/ double vHelioToVlsr(double vhelio, long double jd); - /** Computes the velocity of any object projected on the direction of the source. - * @param jd0 Julian day for which we compute the direction of the source - * @return velocity of the Earth projected on the direction of the source kms-1 - */ + /** + * Computes the velocity of any object projected on the direction of the source. + * + * @param jd0 Julian day for which we compute the direction of the source + * @return velocity of the Earth projected on the direction of the source kms-1 + */ double vREarth(long double jd0); - /** Computes the radial velocity of a source referred to the center of the earth - * from the radial velocity referred to the solar system barycenter - * - * @param vhelio radial velocity of the source referred to the barycenter of the - * solar system in km/s - * @param jd Epoch expressed as julian day to which the source coordinates refer to. - * @return Radial velocity of the source referred to the center of the Earth in km/s - **/ + /** + * Computes the radial velocity of a source referred to the center of the earth + * from the radial velocity referred to the solar system barycenter + * + * @param vhelio radial velocity of the source referred to the barycenter of the + * solar system in km/s + * @param jd Epoch expressed as julian day to which the source coordinates refer to. + * @return Radial velocity of the source referred to the center of the Earth in km/s + **/ double vGeocentric(double vhelio, long double jd); - /** Computes the radial velocity of a source referred to the solar system barycenter - * from the velocity referred to the center of the earth - * - * @param vgeo radial velocity of the source referred to the center of the Earth - * [km/s] - * @param jd Epoch expressed as julian day to which the source coordinates refer to. - * @return Radial velocity of the source referred to the solar system barycenter in km/s - **/ + /** + * Computes the radial velocity of a source referred to the solar system barycenter + * from the velocity referred to the center of the earth + * + * @param vgeo radial velocity of the source referred to the center of the Earth [km/s] + * @param jd Epoch expressed as julian day to which the source coordinates refer to. + * @return Radial velocity of the source referred to the solar system barycenter in km/s + **/ double vGeoToVHelio(double vgeo, long double jd); - /** Computes the velocity of any object (oberver's site) projected on the - * direction of the source. - * @param vsite velocity of that object in cartesian coordinates - * @return velocity of the object projected on the direction of the source kms-1 - */ + /** + * Computes the velocity of any object (oberver's site) projected on the + * direction of the source. + * + * @param vsite velocity of that object in cartesian coordinates + * @return velocity of the object projected on the direction of the source kms-1 + */ double vRSite(double vsite[3]); - /** Computes the radial velocity of a source referred to the observer site on the surface - * of the earth from the geocentric velovity and the velocity of the site referred to the center - * of the Earth. - * - * @param vgeo radial velocity of the source referred to the center of the earth in km/s - * @param vsite Velocity at which the observer moves referred to the center of the earth. - * @return Radial velocity of the source referred to the observer's site in km/s - **/ + /** + * Computes the radial velocity of a source referred to the observer site on the surface + * of the earth from the geocentric velovity and the velocity of the site referred to the center + * of the Earth. + * + * @param vgeo radial velocity of the source referred to the center of the earth in km/s + * @param vsite Velocity at which the observer moves referred to the center of the earth. + * @return Radial velocity of the source referred to the observer's site in km/s + **/ double vTopocentric(double vgeo, double vsite[3]); - /** Computes the radial velocity of a source referred to the center of the Earth from - * the radial velocity referred to an observer site on the surface of the earth - * - * @param vtopo radial velocity of the source referred to the observer's site in km/s - * @param vsite Velocity at which the observer moves referred to the center of the earth. - * @return Radial velocity of the source referred the center of the earth in km/s - **/ + /** + * Computes the radial velocity of a source referred to the center of the Earth from + * the radial velocity referred to an observer site on the surface of the earth + * + * @param vtopo radial velocity of the source referred to the observer's site in km/s + * @param vsite Velocity at which the observer moves referred to the center of the earth. + * @return Radial velocity of the source referred the center of the earth in km/s + **/ double vTopoToVGeo(double vtopo, double vsite[3]); - /** Find the SkyPoint obtained by moving distance dist - * (arcseconds) away from the givenSkyPoint - * - * @param dist Distance to move through in arcseconds - * @param from The SkyPoint to move away from - * @return a SkyPoint that is at the dist away from this SkyPoint in the direction away from - */ + /** + * Find the SkyPoint obtained by moving distance dist + * (arcseconds) away from the givenSkyPoint + * + * @param dist Distance to move through in arcseconds + * @param from The SkyPoint to move away from + * @return a SkyPoint that is at the dist away from this SkyPoint in the direction away from + */ SkyPoint moveAway(const SkyPoint &from, double dist) const; - /** - * @short Check if this point is circumpolar at the given geographic latitude - */ + /** @short Check if this point is circumpolar at the given geographic latitude */ bool checkCircumpolar(const dms *gLat) const; /** Calculate refraction correction. Parameter and return value are in degrees */ static double refractionCorr(double alt); /** - * @short Apply refraction correction to altitude. - * @param alt altitude to be corrected, in degrees - * @return altitude after refraction correction, in degrees - */ + * @short Apply refraction correction to altitude. + * + * @param alt altitude to be corrected, in degrees + * @return altitude after refraction correction, in degrees + */ static double refract(const double alt); /** - * @short Remove refraction correction. - * @param alt altitude from which refraction correction must be removed, in degrees - * @return altitude without refraction correction, in degrees - */ + * @short Remove refraction correction. + * + * @param alt altitude from which refraction correction must be removed, in degrees + * @return altitude without refraction correction, in degrees + */ static double unrefract(const double alt); /** - * @short Apply refraction correction to altitude. Overloaded method using - * dms provided for convenience - * @see SkyPoint::refract( const double alt ) - */ + * @short Apply refraction correction to altitude. Overloaded method using + * dms provided for convenience + * @see SkyPoint::refract( const double alt ) + */ static inline dms refract(const dms alt) { return dms(refract(alt.Degrees())); } /** - * @short Remove refraction correction. Overloaded method using - * dms provided for convenience - * @see SkyPoint::unrefract( const double alt ) - */ + * @short Remove refraction correction. Overloaded method using + * dms provided for convenience + * @see SkyPoint::unrefract( const double alt ) + */ static inline dms unrefract(const dms alt) { return dms(unrefract(alt.Degrees())); } /** - * @short Compute the altitude of a given skypoint hour hours from the given date/time - * @param p SkyPoint whose altitude is to be computed (const pointer, the method works on a clone) - * @param dt Date/time that corresponds to 0 hour - * @param geo GeoLocation object specifying the location - * @param hour double specifying offset in hours from dt for which altitude is to be found - * @return a dms containing (unrefracted?) altitude of the object at dt + hour hours at the given location - * @note This method is used in multiple places across KStars - * @todo Fix code duplication in AltVsTime and KSAlmanac by using this method instead! FIXME. - */ + * @short Compute the altitude of a given skypoint hour hours from the given date/time + * + * @param p SkyPoint whose altitude is to be computed (const pointer, the method works on a clone) + * @param dt Date/time that corresponds to 0 hour + * @param geo GeoLocation object specifying the location + * @param hour double specifying offset in hours from dt for which altitude is to be found + * @return a dms containing (unrefracted?) altitude of the object at dt + hour hours at the given location + * + * @note This method is used in multiple places across KStars + * @todo Fix code duplication in AltVsTime and KSAlmanac by using this method instead! FIXME. + */ static dms findAltitude(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour = 0); /** - * @short returns a time-transformed SkyPoint. See SkyPoint::findAltitude() for details - * @todo Fix this documentation. - */ + * @short returns a time-transformed SkyPoint. See SkyPoint::findAltitude() for details + * @todo Fix this documentation. + */ static SkyPoint timeTransformed(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour = 0); /** - *@short Critical height for atmospheric refraction - * corrections. Below this, the height formula produces meaningles - * results and the correction value is just interpolated. - */ + * @short Critical height for atmospheric refraction + * corrections. Below this, the height formula produces meaningless + * results and the correction value is just interpolated. + */ static const double altCrit; /** - * @short Return the object's altitude at the upper culmination for the given latitude - * @return the maximum altitude in degrees - */ + * @short Return the object's altitude at the upper culmination for the given latitude + * + * @return the maximum altitude in degrees + */ double maxAlt(const dms &lat) const; /** - * @short Return the object's altitude at the lower culmination for the given latitude - * @return the minimum altitude in degrees - */ + * @short Return the object's altitude at the lower culmination for the given latitude + * + * @return the minimum altitude in degrees + */ double minAlt(const dms &lat) const; #ifdef PROFILE_COORDINATE_CONVERSION static double cpuTime_EqToHz; static long unsigned eqToHzCalls; #endif protected: /** - * Precess this SkyPoint's catalog coordinates to the epoch described by the - * given KSNumbers object. - * @param num pointer to a KSNumbers object describing the target epoch. - */ + * Precess this SkyPoint's catalog coordinates to the epoch described by the + * given KSNumbers object. + * + * @param num pointer to a KSNumbers object describing the target epoch. + */ void precess(const KSNumbers *num); #ifdef UNIT_TEST friend class TestSkyPoint; // Test class #endif private: CachingDms RA0, Dec0; //catalog coordinates CachingDms RA, Dec; //current true sky coordinates dms Alt, Az; static KSSun *m_Sun; protected: - double lastPrecessJD; // JD at which the last coordinate update (see updateCoords) for this SkyPoint was done + double lastPrecessJD { 0 }; // JD at which the last coordinate update (see updateCoords) for this SkyPoint was done }; diff --git a/kstars/skyobjects/starobject.h b/kstars/skyobjects/starobject.h index 5e2da91a1..223702d8e 100644 --- a/kstars/skyobjects/starobject.h +++ b/kstars/skyobjects/starobject.h @@ -1,307 +1,307 @@ /*************************************************************************** starobject.h - K Desktop Planetarium ------------------- begin : Tue Sep 18 2001 copyright : (C) 2001 by Thomas Kabelmann email : tk78@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once //#define PROFILE_UPDATECOORDS #include "skyobject.h" #include struct DeepStarData; class KSNumbers; class KSPopupMenu; struct StarData; /** @class StarObject *This is a subclass of SkyObject. It adds the Spectral type, and flags *for variability and multiplicity. *For stars, the primary name (n) is the latin name (e.g., "Betelgeuse"). The *secondary name (n2) is the genetive name (e.g., "alpha Orionis"). *@short subclass of SkyObject specialized for stars. *@author Thomas Kabelmann *@version 1.0 */ class StarObject : public SkyObject { public: /** @short returns the reindex interval (in centuries!) for the given * magnitude of proper motion (in milliarcsec/year). ASSUMING a * 25 arc-minute margin for proper motion. */ static double reindexInterval(double pm); /** *Constructor. Sets sky coordinates, magnitude, latin name, genetive name, and *spectral type. *@param r Right Ascension *@param d Declination *@param m magnitude *@param n common name *@param n2 genetive name *@param sptype Spectral Type *@param pmra Proper motion in RA direction [mas/yr] *@param pmdec Proper motion in Dec direction [mas/yr] *@param par Parallax angle [mas] *@param mult Multiplicity flag (false=dingle star; true=multiple star) *@param var Variability flag (true if star is a known periodic variable) *@param hd Henry Draper Number */ explicit StarObject(dms r = dms(0.0), dms d = dms(0.0), float m = 0.0, const QString &n = QString(), const QString &n2 = QString(), const QString &sptype = "--", double pmra = 0.0, double pmdec = 0.0, double par = 0.0, bool mult = false, bool var = false, int hd = 0); /** *Constructor. Sets sky coordinates, magnitude, latin name, genetive name, and *spectral type. Differs from above function only in data type of RA and Dec. *@param r Right Ascension *@param d Declination *@param m magnitude *@param n common name *@param n2 genetive name *@param sptype Spectral Type *@param pmra Proper motion in RA direction [mas/yr] *@param pmdec Proper motion in Dec direction [mas/yr] *@param par Parallax angle [mas] *@param mult Multiplicity flag (false=dingle star; true=multiple star) *@param var Variability flag (true if star is a known periodic variable) *@param hd Henry Draper Number */ StarObject(double r, double d, float m = 0.0, const QString &n = QString(), const QString &n2 = QString(), const QString &sptype = "--", double pmra = 0.0, double pmdec = 0.0, double par = 0.0, bool mult = false, bool var = false, int hd = 0); StarObject *clone() const override; UID getUID() const override; /** Copy constructor */ StarObject(const StarObject &o); /** Destructor. (Empty) */ - ~StarObject() override {} + ~StarObject() override = default; /** *@short Initializes a StarObject to given data * * This is almost like the StarObject constructor itself, but it avoids * setting up name, gname etc for unnamed stars. If called instead of the * constructor, this method will be much faster for unnamed stars * *@param stardata Pointer to starData object containing required data (except name and gname) *@return Nothing */ void init(const StarData *stardata); /** *@short Initializes a StarObject to given data * *@param stardata Pointer to deepStarData object containing the available data *@return Nothing */ void init(const DeepStarData *stardata); /** *@short Sets the name, genetive name, and long name * *@param name Common name *@param name2 Genetive name */ void setNames(const QString &name, const QString &name2); /** @return true if the star has a name ("star" doesn't count) */ inline bool hasName() const { return (!Name.isEmpty() && Name != starString); } /** @return true if the star has a latin name ("star" or HD... doesn't count) */ inline bool hasLatinName() const { return (!Name.isEmpty() && Name != starString && Name != gname(false) && Name != gname(true) && !Name.startsWith("HD ")); } /** If star is unnamed return "star" otherwise return the name */ inline QString name(void) const override { return hasName() ? Name : starString; } /** If star is unnamed return "star" otherwise return the longname */ inline QString longname(void) const override { return hasLongName() ? LongName : starString; } /** Returns entire spectral type string * @return Spectral Type string */ QString sptype(void) const; /** Returns just the first character of the spectral type string. */ char spchar() const; /** Returns the genetive name of the star. * @return genetive name of the star */ QString gname(bool useGreekChars = true) const; /** Returns the greek letter portion of the star's genetive name. * Returns empty string if star has no genetive name defined. * @return greek letter portion of genetive name */ QString greekLetter(bool useGreekChars = true) const; /** @return the genitive form of the star's constellation. */ QString constell(void) const; /** Determine the current coordinates (RA, Dec) from the catalog * coordinates (RA0, Dec0), accounting for both precession and nutation. * @param num pointer to KSNumbers object containing current values of * time-dependent variables. * @param includePlanets does nothing in this implementation (see KSPlanetBase::updateCoords()). * @param lat does nothing in this implementation (see KSPlanetBase::updateCoords()). * @param LST does nothing in this implementation (see KSPlanetBase::updateCoords()). */ void updateCoords(const KSNumbers *num, bool includePlanets = true, const CachingDms *lat = nullptr, const CachingDms *LST = nullptr, bool forceRecompute = false) override; /** @short fills ra and dec with the coordinates of the star with the proper * motion correction but without precesion and its friends. It is used * in StarComponent to re-index all the stars. * * @return true if we changed the coordinates, false otherwise * NOTE: ra and dec both in degrees. */ bool getIndexCoords(const KSNumbers *num, CachingDms &ra, CachingDms &dec); bool getIndexCoords(const KSNumbers *num, double *ra, double *dec); /** @short added for JIT updates from both StarComponent and ConstellationLines */ void JITupdate(); /** @short returns the magnitude of the proper motion correction in milliarcsec/year */ inline double pmMagnitude() const { double cosDec = dec0().cos(); return sqrt(cosDec * cosDec * pmRA() * pmRA() + pmDec() * pmDec()); } /** *@short returns the square of the magnitude of the proper motion correction in (milliarcsec/year)^2 *@note This method is faster when the square root need not be taken */ inline double pmMagnitudeSquared() const { double metric_weighted_pmRA = dec0().cos() * pmRA(); return (metric_weighted_pmRA * metric_weighted_pmRA + pmDec() * pmDec()); } /** @short Set the Ra and Dec components of the star's proper motion, in milliarcsec/year. * Note that the RA component is multiplied by cos(dec). * @param pmra the new RA propoer motion * @param pmdec the new Dec proper motion */ inline void setProperMotion(double pmra, double pmdec) { PM_RA = pmra; PM_Dec = pmdec; } /** @return the RA component of the star's proper motion, in mas/yr (multiplied by cos(dec)) */ inline double pmRA() const { return PM_RA; } /** @return the Dec component of the star's proper motion, in mas/yr */ inline double pmDec() const { return PM_Dec; } /** @short set the star's parallax angle, in milliarcsec */ inline void setParallax(double plx) { Parallax = plx; } /** @return the star's parallax angle, in milliarcsec */ inline double parallax() const { return Parallax; } /** @return the star's distance from the Sun in parsecs, as computed from the parallax. */ inline double distance() const { return 1000. / parallax(); } /** @short set the star's multiplicity flag (i.e., is it a binary or multiple star?) * @param m true if binary/multiple star system */ inline void setMultiple(bool m) { Multiplicity = m; } /** @return whether the star is a binary or multiple starobject */ inline bool isMultiple() const { return Multiplicity; } /** @return the star's HD index */ inline int getHDIndex() const { return HD; } /** @short set the star's variability flag *@param v true if star is variable */ inline void setVariable(bool v) { Variability = v; } /** @return whether the star is a binary or multiple starobject */ inline bool isVariable() const { return Variability; } /** @short returns the name, the magnitude or both. */ QString nameLabel(bool drawName, bool drawMag) const; QString labelString() const override; /** *@return the pixel distance for offseting the star's name label *This takes the zoom level and the star's brightness into account. */ double labelOffset() const override; /** *@return the Visual magnitude of the star */ inline float getVMag() const { return V; } /** *@return the blue magnitude of the star */ inline float getBMag() const { return B; } /** *@return the B - V color index of the star, or a nonsense number *larger than 30 if it's not well defined */ inline float getBVIndex() const { return ((B < 30.0 && V < 30.0) ? B - V : 99.9); } void initPopupMenu(KSPopupMenu *pmenu) override; quint64 updateID; quint64 updateNumID; #ifdef PROFILE_UPDATECOORDS static double updateCoordsCpuTime; static unsigned int starsUpdated; #endif protected: // DEBUG EDIT. For testing proper motion, uncomment this, and related blocks // See starobject.cpp for further info. // static QVector Trail; // bool testStar; // END DEBUG private: double PM_RA { 0 }; double PM_Dec { 0 }; double Parallax { 0 }; bool Multiplicity { false }; bool Variability { false }; char SpType[2]; int HD { 0 }; // B and V magnitudes, separately. NOTE 1) This is kept separate from mag for a reason. // See init( const DeepStarData *); 2) This applies only to deep stars at the moment float B { 0 }; float V { 0 }; }; diff --git a/kstars/skyobjects/supernova.h b/kstars/skyobjects/supernova.h index e89ff21e8..7f2824a3a 100644 --- a/kstars/skyobjects/supernova.h +++ b/kstars/skyobjects/supernova.h @@ -1,79 +1,79 @@ /* Supernova Object Copyright (C) 2016 Jasem Mutlaq Based on Samikshan Bairagya GSoC work. This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #ifndef SUPERNOVA_H #define SUPERNOVA_H #include "skyobject.h" /** * @class Supernova * Represents the supernova object. It is a subclass of the SkyObject class. * This class has the information for different supernovae. * * N.B. This was modified to use the Open Supernova Project * @author Samikshan Bairagya * @author Jasem Mutlaq */ /** * @note The Data File Contains the following parameters * @li sName Designation * @li RA Right Ascension * @li Dec Declination * @li type Supernova Type * @li hostGalaxy Host Galaxy for the supernova * @li date Discovery date yyyy/mm/dd * @li sRedShift Redshift * @li sMag Maximum Apparent magnitude */ class Supernova : public SkyObject { public: explicit Supernova(const QString &sName, dms ra, dms dec, const QString &type = QString(), const QString &hostGalaxy = QString(), const QString &date = QString(), float sRedShift = 0.0, float sMag = 99.9); /** * @return a clone of this object * @note See SkyObject::clone() */ Supernova *clone() const override; /** Destructor(Empty) */ - ~Supernova() override {} + ~Supernova() override = default; /** * @return the type of the supernova */ inline QString getType() const { return type; } /** * @return the host galaxy of the supernova */ inline QString getHostGalaxy() const { return hostGalaxy; } /** * @return the date the supernova was observed */ inline QString getDate() const { return date; } /** * @return the date the supernova was observed */ inline float getRedShift() const { return redShift; } void initPopupMenu(KSPopupMenu *) override; private: QString type, hostGalaxy, date; float redShift; }; #endif diff --git a/kstars/skypainter.cpp b/kstars/skypainter.cpp index 79dab671f..893171ce8 100644 --- a/kstars/skypainter.cpp +++ b/kstars/skypainter.cpp @@ -1,67 +1,63 @@ /* SkyPainter: class for painting onto the sky for KStars Copyright (C) 2010 Henry de Valence 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "skypainter.h" #include "skymap.h" #include "Options.h" #include "kstarsdata.h" #include "skycomponents/skiphashlist.h" #include "skycomponents/linelistlabel.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/kscomet.h" #include "skyobjects/ksasteroid.h" #include "skyobjects/ksplanetbase.h" #include "skyobjects/trailobject.h" #include "skyobjects/constellationsart.h" -SkyPainter::SkyPainter() : m_sizeMagLim(10.) +SkyPainter::SkyPainter() { m_sm = SkyMap::Instance(); } -SkyPainter::~SkyPainter() -{ -} - void SkyPainter::setSizeMagLimit(float sizeMagLim) { m_sizeMagLim = sizeMagLim; } float SkyPainter::starWidth(float mag) const { //adjust maglimit for ZoomLevel const double maxSize = 10.0; double lgmin = log10(MINZOOM); // double lgmax = log10(MAXZOOM); double lgz = log10(Options::zoomFactor()); float sizeFactor = maxSize + (lgz - lgmin); float size = (sizeFactor * (m_sizeMagLim - mag) / m_sizeMagLim) + 1.; if (size <= 1.0) size = 1.0; if (size > maxSize) size = maxSize; return size; } diff --git a/kstars/skypainter.h b/kstars/skypainter.h index edaf8ae34..5e12c6f3d 100644 --- a/kstars/skypainter.h +++ b/kstars/skypainter.h @@ -1,185 +1,183 @@ /* SkyPainter: class for painting onto the sky for KStars Copyright (C) 2010 Henry de Valence 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #pragma once +#include "skycomponents/typedef.h" + #include #include -#include "skycomponents/typedef.h" - class ConstellationsArt; class DeepSkyObject; class KSComet; class KSPlanetBase; class LineList; class LineListLabel; class Satellite; class SkipHashList; class SkyMap; class SkyObject; class SkyPoint; class Supernova; /** * @short Draws things on the sky, without regard to backend. * This class serves as an interface to draw objects onto the sky without * worrying about whether we are using a QPainter or OpenGL. */ class SkyPainter { public: - /** @short Constructor. */ SkyPainter(); - /** @short Destructor */ - virtual ~SkyPainter(); + virtual ~SkyPainter() = default; /** @short Set the pen of the painter **/ virtual void setPen(const QPen &pen) = 0; /** @short Set the brush of the painter **/ virtual void setBrush(const QBrush &brush) = 0; //FIXME: find a better way to do this. void setSizeMagLimit(float sizeMagLim); /** * Begin painting. * @note this function must be called before painting anything. * @see end() */ virtual void begin() = 0; /** * End and finalize painting. * @note this function must be called after painting anything. * @note it is not guaranteed that anything will actually be drawn until end() is called. * @see begin(); */ virtual void end() = 0; //////////////////////////////////// // // // SKY DRAWING FUNCTIONS: // // // //////////////////////////////////// /** @short Draw the sky background */ virtual void drawSkyBackground() = 0; /** * @short Draw a line between points in the sky. * @param a the first point * @param b the second point * @note this function will skip lines not on screen and clip lines * that are only partially visible. */ virtual void drawSkyLine(SkyPoint *a, SkyPoint *b) = 0; /** * @short Draw a polyline in the sky. * @param list a list of points in the sky * @param skipList a SkipList object used to control skipping line segments * @param label a pointer to the label for this line * @note it's more efficient to use this than repeated calls to drawSkyLine(), * because it avoids an extra points->size() -2 projections. */ virtual void drawSkyPolyline(LineList *list, SkipHashList *skipList = nullptr, LineListLabel *label = nullptr) = 0; /** * @short Draw a polygon in the sky. * @param list a list of points in the sky * @param forceClip If true (default), it enforces clipping of the polygon, otherwise, it draws the * complete polygen without running any boundary checks. * @see drawSkyPolyline() */ virtual void drawSkyPolygon(LineList *list, bool forceClip = true) = 0; /** * @short Draw a comet in the sky. * @param com comet to draw * @return true if a comet was drawn */ virtual bool drawComet(KSComet *com) = 0; /** * @short Draw a point source (e.g., a star). * @param loc the location of the source in the sky * @param mag the magnitude of the source * @param sp the spectral class of the source * @return true if a source was drawn */ virtual bool drawPointSource(SkyPoint *loc, float mag, char sp = 'A') = 0; /** * @short Draw a deep sky object * @param obj the object to draw * @param drawImage if true, try to draw the image of the object * @return true if it was drawn */ virtual bool drawDeepSkyObject(DeepSkyObject *obj, bool drawImage = false) = 0; /** * @short Draw a planet * @param planet the planet to draw * @return true if it was drawn */ virtual bool drawPlanet(KSPlanetBase *planet) = 0; /** * @short Draw the symbols for the observing list * @param obs the oberving list */ virtual void drawObservingList(const QList &obs) = 0; /** @short Draw flags */ virtual void drawFlags() = 0; /** @short Draw a satellite */ virtual bool drawSatellite(Satellite *sat) = 0; /** @short Draw a Supernova */ virtual bool drawSupernova(Supernova *sup) = 0; virtual void drawHorizon(bool filled, SkyPoint *labelPoint = nullptr, bool *drawLabel = nullptr) = 0; /** @short Get the width of a star of magnitude mag */ float starWidth(float mag) const; /** * @short Draw a ConstellationsArt object * @param obj the object to draw * @return true if it was drawn */ virtual bool drawConstellationArtImage(ConstellationsArt *obj) = 0; /** * @brief drawHips Draw HIPS all sky catalog * @return true if it was drawn */ virtual bool drawHips() = 0; protected: SkyMap *m_sm { nullptr }; private: - float m_sizeMagLim; + float m_sizeMagLim { 10.0f }; }; diff --git a/kstars/tools/conjunctions.cpp b/kstars/tools/conjunctions.cpp index a2298751f..ed0985a36 100644 --- a/kstars/tools/conjunctions.cpp +++ b/kstars/tools/conjunctions.cpp @@ -1,414 +1,410 @@ /*************************************************************************** conjunctions.cpp - Conjunctions Tool ------------------- begin : Sun 20th Apr 2008 copyright : (C) 2008 Akarsh Simha email : akarshsimha@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. * * * ***************************************************************************/ /*************************************************************************** * * * Much of the code here is taken from Pablo de Vicente's * * modcalcplanets.cpp * * * ***************************************************************************/ #include "conjunctions.h" #include "geolocation.h" #include "ksconjunct.h" #include "kstars.h" #include "kstarsdata.h" #include "skymap.h" #include "dialogs/finddialog.h" #include "dialogs/locationdialog.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/kscomet.h" #include "skyobjects/kspluto.h" #include #include #include #include ConjunctionsTool::ConjunctionsTool(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); KStarsData *kd = KStarsData::Instance(); KStarsDateTime dtStart(KStarsDateTime::currentDateTime()); KStarsDateTime dtStop(dtStart.djd() + 365.24); // TODO: Refine //startDate -> setDateTime( dtStart.dateTime() ); //stopDate -> setDateTime( dtStop.dateTime() ); //TODO Check if this change works startDate->setDateTime(dtStart); stopDate->setDateTime(dtStop); geoPlace = kd->geo(); LocationButton->setText(geoPlace->fullName()); // Init filter type combobox FilterTypeComboBox->addItem(i18n("Single Object...")); FilterTypeComboBox->addItem(i18n("Any")); FilterTypeComboBox->addItem(i18n("Stars")); FilterTypeComboBox->addItem(i18n("Solar System")); FilterTypeComboBox->addItem(i18n("Planets")); FilterTypeComboBox->addItem(i18n("Comets")); FilterTypeComboBox->addItem(i18n("Asteroids")); FilterTypeComboBox->addItem(i18n("Open Clusters")); FilterTypeComboBox->addItem(i18n("Globular Clusters")); FilterTypeComboBox->addItem(i18n("Gaseous Nebulae")); FilterTypeComboBox->addItem(i18n("Planetary Nebulae")); FilterTypeComboBox->addItem(i18n("Galaxies")); pNames[KSPlanetBase::MERCURY] = i18n("Mercury"); pNames[KSPlanetBase::VENUS] = i18n("Venus"); pNames[KSPlanetBase::MARS] = i18n("Mars"); pNames[KSPlanetBase::JUPITER] = i18n("Jupiter"); pNames[KSPlanetBase::SATURN] = i18n("Saturn"); pNames[KSPlanetBase::URANUS] = i18n("Uranus"); pNames[KSPlanetBase::NEPTUNE] = i18n("Neptune"); //pNames[KSPlanetBase::PLUTO] = i18nc("Asteroid name (optional)", "Pluto"); pNames[KSPlanetBase::SUN] = i18n("Sun"); pNames[KSPlanetBase::MOON] = i18n("Moon"); for (int i = 0; i < KSPlanetBase::UNKNOWN_PLANET; ++i) { // Obj1ComboBox->insertItem( i, pNames[i] ); Obj2ComboBox->insertItem(i, pNames[i]); } // Initialize the Maximum Separation box to 1 degree maxSeparationBox->setDegType(true); maxSeparationBox->setDMS("01 00 00.0"); //Set up the Table Views m_Model = new QStandardItemModel(0, 5, this); m_Model->setHorizontalHeaderLabels(QStringList() << i18n("Conjunction/Opposition") << i18n("Date & Time (UT)") << i18n("Object 1") << i18n("Object 2") << i18n("Separation")); m_SortModel = new QSortFilterProxyModel(this); m_SortModel->setSourceModel(m_Model); OutputList->setModel(m_SortModel); OutputList->setSortingEnabled(true); OutputList->horizontalHeader()->setStretchLastSection(true); OutputList->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); OutputList->horizontalHeader()->resizeSection(2, 100); OutputList->horizontalHeader()->resizeSection(3, 100); OutputList->horizontalHeader()->resizeSection(4, 120); //is it bad way to fix default size of columns ? //FilterEdit->showClearButton = true; ClearFilterButton->setIcon(QIcon::fromTheme("edit-clear")); // signals and slots connections connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(Obj1FindButton, SIGNAL(clicked()), this, SLOT(slotFindObject())); //connect(ComputeButton, SIGNAL(clicked()), this, SLOT(slotCompute())); connect(ComputeButton, &QPushButton::clicked, [this]() { QtConcurrent::run(this, &ConjunctionsTool::slotCompute); }); connect(FilterTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(slotFilterType(int))); connect(ClearButton, SIGNAL(clicked()), this, SLOT(slotClear())); connect(ExportButton, SIGNAL(clicked()), this, SLOT(slotExport())); connect(OutputList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotGoto())); connect(ClearFilterButton, SIGNAL(clicked()), FilterEdit, SLOT(clear())); connect(FilterEdit, SIGNAL(textChanged(QString)), this, SLOT(slotFilterReg(QString))); show(); } -ConjunctionsTool::~ConjunctionsTool() -{ -} - void ConjunctionsTool::slotGoto() { int index = m_SortModel->mapToSource(OutputList->currentIndex()).row(); // Get the number of the line long double jd = outputJDList.value(index); KStarsDateTime dt; KStars *ks = KStars::Instance(); KStarsData *data = KStarsData::Instance(); SkyMap *map = ks->map(); // Show conjunction data->setLocation(*geoPlace); dt.setDJD(jd); data->changeDateTime(dt); map->setClickedObject(data->skyComposite()->findByName(m_Model->data(m_Model->index(index, 2)).toString())); map->setClickedPoint(map->clickedObject()); map->slotCenter(); } void ConjunctionsTool::slotFindObject() { QPointer fd = new FindDialog(KStars::Instance()); if (fd->exec() == QDialog::Accepted) { if (!fd->targetObject()) return; Object1 = fd->targetObject(); if (Object1 != nullptr) Obj1FindButton->setText(Object1->name()); } delete fd; } void ConjunctionsTool::slotLocation() { QPointer ld(new LocationDialog(this)); if (ld->exec() == QDialog::Accepted && ld) { geoPlace = ld->selectedCity(); LocationButton->setText(geoPlace->fullName()); } delete ld; } void ConjunctionsTool::slotFilterType(int) { // Disable find button if the user select an object type if (FilterTypeComboBox->currentIndex() == 0) Obj1FindButton->setEnabled(true); else Obj1FindButton->setEnabled(false); } void ConjunctionsTool::slotClear() { m_Model->setRowCount(0); outputJDList.clear(); m_index = 0; } void ConjunctionsTool::slotExport() { int i, j; QByteArray line; //QFile file( KFileDialog::getSaveFileName( QDir::homePath(), "*|All files", this, "Save Conjunctions" ) ); QFile file(QFileDialog::getSaveFileName(nullptr, i18n("Save Conjunctions"), QDir::homePath(), "*|All files")); file.open(QIODevice::WriteOnly | QIODevice::Text); for (i = 0; i < m_Model->rowCount(); ++i) { for (j = 0; j < m_Model->columnCount(); ++j) { line.append(m_Model->data(m_Model->index(i, j)).toByteArray()); if (j < m_Model->columnCount() - 1) line.append(";"); else line.append("\n"); } file.write(line); line.clear(); } file.close(); } void ConjunctionsTool::slotFilterReg(const QString &filter) { m_SortModel->setFilterRegExp(QRegExp(filter, Qt::CaseInsensitive, QRegExp::RegExp)); m_SortModel->setFilterKeyColumn(-1); } void ConjunctionsTool::slotCompute(void) { KStarsDateTime dtStart(startDate->dateTime()); // Start date KStarsDateTime dtStop(stopDate->dateTime()); // Stop date long double startJD = dtStart.djd(); // Start julian day long double stopJD = dtStop.djd(); // Stop julian day bool opposition = false; // true=opposition, false=conjunction if (Opposition->currentIndex()) opposition = true; QStringList objects; // List of sky object used as Object1 KStarsData *data = KStarsData::Instance(); int progress = 0; // Check if we have a valid angle in maxSeparationBox dms maxSeparation(0.0); bool ok; maxSeparation = maxSeparationBox->createDms(true, &ok); if (!ok) { KMessageBox::sorry(nullptr, i18n("Maximum separation entered is not a valid angle. Use the What's this help feature " "for information on how to enter a valid angle")); return; } // Check if Object1 and Object2 are set if (FilterTypeComboBox->currentIndex() == 0 && Object1 == nullptr) { KMessageBox::sorry( nullptr, i18n("Please select an object to check conjunctions with, by clicking on the \'Find Object\' button.")); return; } Object2.reset(KSPlanetBase::createPlanet(Obj2ComboBox->currentIndex())); if (FilterTypeComboBox->currentIndex() == 0 && Object1->name() == Object2->name()) { // FIXME: Must free the created Objects KMessageBox::sorry(nullptr, i18n("Please select two different objects to check conjunctions with.")); return; } // Init KSConjunct object KSConjunct ksc; connect(&ksc, SIGNAL(madeProgress(int)), this, SLOT(showProgress(int))); ksc.setGeoLocation(geoPlace); switch (FilterTypeComboBox->currentIndex()) { case 1: // All object types foreach (int type, data->skyComposite()->objectNames().keys()) objects += data->skyComposite()->objectNames(type); break; case 2: // Stars objects += data->skyComposite()->objectNames(SkyObject::STAR); objects += data->skyComposite()->objectNames(SkyObject::CATALOG_STAR); break; case 3: // Solar system objects += data->skyComposite()->objectNames(SkyObject::PLANET); objects += data->skyComposite()->objectNames(SkyObject::COMET); objects += data->skyComposite()->objectNames(SkyObject::ASTEROID); objects += data->skyComposite()->objectNames(SkyObject::MOON); objects += i18n("Sun"); // Remove Object2 planet objects.removeAll(Object2->name()); break; case 4: // Planet objects += data->skyComposite()->objectNames(SkyObject::PLANET); // Remove Object2 planet objects.removeAll(Object2->name()); break; case 5: // Comet objects += data->skyComposite()->objectNames(SkyObject::COMET); break; case 6: // Ateroid objects += data->skyComposite()->objectNames(SkyObject::ASTEROID); break; case 7: // Open Clusters objects = data->skyComposite()->objectNames(SkyObject::OPEN_CLUSTER); break; case 8: // Open Clusters objects = data->skyComposite()->objectNames(SkyObject::GLOBULAR_CLUSTER); break; case 9: // Gaseous nebulae objects = data->skyComposite()->objectNames(SkyObject::GASEOUS_NEBULA); break; case 10: // Planetary nebula objects = data->skyComposite()->objectNames(SkyObject::PLANETARY_NEBULA); break; case 11: // Galaxies objects = data->skyComposite()->objectNames(SkyObject::GALAXY); break; } // Remove all Jupiter and Saturn moons // KStars crash if we compute a conjunction between a planet and one of this moon if (FilterTypeComboBox->currentIndex() == 1 || FilterTypeComboBox->currentIndex() == 3 || FilterTypeComboBox->currentIndex() == 6) { objects.removeAll("Io"); objects.removeAll("Europa"); objects.removeAll("Ganymede"); objects.removeAll("Callisto"); objects.removeAll("Mimas"); objects.removeAll("Enceladus"); objects.removeAll("Tethys"); objects.removeAll("Dione"); objects.removeAll("Rhea"); objects.removeAll("Titan"); objects.removeAll("Hyperion"); objects.removeAll("Iapetus"); } if (FilterTypeComboBox->currentIndex() != 0) { // Show a progress dialog while processing QProgressDialog progressDlg(i18n("Compute conjunction..."), i18n("Abort"), 0, objects.count(), this); progressDlg.setWindowTitle(i18n("Conjunction")); progressDlg.setWindowModality(Qt::WindowModal); progressDlg.setValue(0); for (auto &object : objects) { // If the user click on the 'cancel' button if (progressDlg.wasCanceled()) break; // Update progress dialog ++progress; progressDlg.setValue(progress); progressDlg.setLabelText(i18n("Compute conjunction between %1 and %2", Object2->name(), object)); // Compute conjuction Object1 = data->skyComposite()->findByName(object); showConjunctions(ksc.findClosestApproach(*Object1, *Object2, startJD, stopJD, maxSeparation, opposition), object, Object2->name()); } progressDlg.setValue(objects.count()); } else { // Change cursor while we search for conjunction QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); ComputeStack->setCurrentIndex(1); showConjunctions(ksc.findClosestApproach(*Object1, *Object2, startJD, stopJD, maxSeparation, opposition), Object1->name(), Object2->name()); ComputeStack->setCurrentIndex(0); // Restore cursor QApplication::restoreOverrideCursor(); } Object2.reset(); } void ConjunctionsTool::showProgress(int n) { progress->setValue(n); } void ConjunctionsTool::showConjunctions(const QMap &conjunctionlist, const QString &object1, const QString &object2) { KStarsDateTime dt; QList itemList; for (auto it = conjunctionlist.constBegin(); it != conjunctionlist.constEnd(); ++it) { dt.setDJD(it.key()); QStandardItem *typeItem; if (!Opposition->currentIndex()) typeItem = new QStandardItem(i18n("Conjunction")); else typeItem = new QStandardItem(i18n("Opposition")); itemList << typeItem //FIXME TODO is this ISO date? is there a ready format to use? //<< new QStandardItem( QLocale().toString( dt.dateTime(), "YYYY-MM-DDTHH:mm:SS" ) ) //<< new QStandardItem( QLocale().toString( dt, Qt::ISODate) ) << new QStandardItem(dt.toString(Qt::ISODate)) << new QStandardItem(object1) << new QStandardItem(object2) << new QStandardItem(it.value().toDMSString()); m_Model->appendRow(itemList); itemList.clear(); outputJDList.insert(m_index, it.key()); ++m_index; } } diff --git a/kstars/tools/conjunctions.h b/kstars/tools/conjunctions.h index 1a3cdddc0..f9bed1328 100644 --- a/kstars/tools/conjunctions.h +++ b/kstars/tools/conjunctions.h @@ -1,80 +1,80 @@ /*************************************************************************** conjunctions.h - Conjunctions Tool ------------------- begin : Sun 20th Apr 2008 copyright : (C) 2008 Akarsh Simha email : akarshsimha@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. * * * ***************************************************************************/ /*************************************************************************** * * * Much of the code here is taken from Pablo de Vicente's * * modcalcplanets.h * * * ***************************************************************************/ #pragma once #include "dms.h" #include "ui_conjunctions.h" #include #include #include #include class QSortFilterProxyModel; class QStandardItemModel; class GeoLocation; class KSPlanetBase; class SkyObject; /** * @short Predicts conjunctions using KSConjunct in the background */ class ConjunctionsTool : public QFrame, public Ui::ConjunctionsDlg { Q_OBJECT public: explicit ConjunctionsTool(QWidget *p); - ~ConjunctionsTool() override; + virtual ~ConjunctionsTool() override = default; public slots: void slotLocation(); void slotCompute(); void showProgress(int); void slotFindObject(); void slotGoto(); void slotFilterType(int); void slotClear(); void slotExport(); void slotFilterReg(const QString &); private: void showConjunctions(const QMap &conjunctionlist, const QString &object1, const QString &object2); SkyObject* Object1 = nullptr; std::unique_ptr Object2; // Second object is always a planet. /// To store the names of Planets vs. values expected by KSPlanetBase::createPlanet() QHash pNames; /// To store Julian Days corresponding to the row index in the output list widget QMap outputJDList; GeoLocation *geoPlace { nullptr }; QStandardItemModel *m_Model { nullptr }; QSortFilterProxyModel *m_SortModel { nullptr }; int m_index { 0 }; }; diff --git a/kstars/tools/flagmanager.cpp b/kstars/tools/flagmanager.cpp index 01a1c8536..03dc322d0 100644 --- a/kstars/tools/flagmanager.cpp +++ b/kstars/tools/flagmanager.cpp @@ -1,378 +1,373 @@ /*************************************************************************** flagmanager.cpp - Flags manager ------------------- begin : Mon Feb 01 2009 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 "flagmanager.h" #include "config-kstars.h" #include "kspaths.h" #include "kstars.h" +#include "kstars_debug.h" #include "kstarsdata.h" #include "Options.h" #include "skymap.h" #include "skycomponents/flagcomponent.h" #include "skycomponents/skymapcomposite.h" #ifdef HAVE_INDI #include #include "indi/indilistener.h" #include "indi/indistd.h" #include "indi/driverinfo.h" #endif #include #include #include -#include "kstars_debug.h" - FlagManagerUI::FlagManagerUI(QWidget *p) : QFrame(p) { setupUi(this); } FlagManager::FlagManager(QWidget *ks) : QDialog(ks) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif QList itemList; QList imageList; QStringList flagNames; int i; ui = new FlagManagerUI(this); setWindowTitle(i18n("Flag manager")); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); m_Ks = KStars::Instance(); ui->hintLabel->setText(i18n("To add custom icons, just add images in %1. File names must begin with flag. " "For example, the file flagSmall_red_cross.png will be shown as Small red " "cross in the combo box.", KSPaths::writableLocation(QStandardPaths::GenericDataLocation))); //Set up the Table Views m_Model = new QStandardItemModel(0, 5, this); m_Model->setHorizontalHeaderLabels(QStringList() << i18nc("Right Ascension", "RA") << i18nc("Declination", "Dec") << i18n("Epoch") << i18n("Icon") << i18n("Label")); m_SortModel = new QSortFilterProxyModel(this); m_SortModel->setSourceModel(m_Model); m_SortModel->setDynamicSortFilter(true); ui->flagList->setModel(m_SortModel); ui->flagList->horizontalHeader()->setStretchLastSection(true); ui->flagList->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->saveButton->setEnabled(false); //Fill the list imageList = m_Ks->data()->skyComposite()->flags()->imageList(); flagNames = m_Ks->data()->skyComposite()->flags()->getNames(); FlagComponent *flags = m_Ks->data()->skyComposite()->flags(); QPixmap pixmap; for (i = 0; i < m_Ks->data()->skyComposite()->flags()->size(); ++i) { QStandardItem *labelItem = new QStandardItem(flags->label(i)); labelItem->setForeground(QBrush(flags->labelColor(i))); itemList << new QStandardItem(flags->pointList().at(i)->ra0().toHMSString()) << new QStandardItem(flags->pointList().at(i)->dec0().toDMSString()) << new QStandardItem(flags->epoch(i)) << new QStandardItem(QIcon(pixmap.fromImage(flags->image(i))), flags->imageName(i)) << labelItem; m_Model->appendRow(itemList); itemList.clear(); } //Fill the combobox for (i = 0; i < imageList.size(); ++i) { ui->flagCombobox->addItem(QIcon(pixmap.fromImage(flags->imageList(i))), flagNames.at(i), flagNames.at(i)); } //Connect buttons connect(ui->addButton, SIGNAL(clicked()), this, SLOT(slotAddFlag())); connect(ui->delButton, SIGNAL(clicked()), this, SLOT(slotDeleteFlag())); connect(ui->CenterButton, SIGNAL(clicked()), this, SLOT(slotCenterFlag())); connect(ui->ScopeButton, SIGNAL(clicked()), this, SLOT(slotCenterTelescope())); connect(ui->flagList, SIGNAL(clicked(QModelIndex)), this, SLOT(slotSetShownFlag(QModelIndex))); connect(ui->flagList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotCenterFlag())); connect(ui->saveButton, SIGNAL(clicked()), this, SLOT(slotSaveChanges())); } -FlagManager::~FlagManager() -{ -} - void FlagManager::setRaDec(const dms &ra, const dms &dec) { ui->raBox->show(ra, false); ui->decBox->show(dec, true); } void FlagManager::clearFields() { ui->raBox->clear(); ui->decBox->clear(); ui->epochBox->setText("2000.0"); ui->flagLabel->clear(); ui->flagLabel->setFocus(); //disable "Save changes" button ui->saveButton->setEnabled(false); //unselect item from flagList ui->flagList->clearSelection(); } void FlagManager::showFlag(int flagIdx) { if (flagIdx < 0 || flagIdx >= m_Model->rowCount()) { return; } else { ui->raBox->setText(m_Model->data(m_Model->index(flagIdx, 0)).toString()); ui->decBox->setText(m_Model->data(m_Model->index(flagIdx, 1)).toString()); ui->epochBox->setText(m_Model->data(m_Model->index(flagIdx, 2)).toString()); //ui->flagCombobox->setCurrentItem( m_Model->data( m_Model->index( flagIdx, 3) ).toString() ); ui->flagCombobox->setCurrentText(m_Model->data(m_Model->index(flagIdx, 3)).toString()); ui->flagLabel->setText(m_Model->data(m_Model->index(flagIdx, 4)).toString()); QColor labelColor = m_Model->item(flagIdx, 4)->foreground().color(); ui->labelColorcombo->setColor(labelColor); } ui->flagList->selectRow(flagIdx); ui->saveButton->setEnabled(true); } bool FlagManager::validatePoint() { bool raOk(false), decOk(false); dms ra(ui->raBox->createDms(false, &raOk)); //false means expressed in hours dms dec(ui->decBox->createDms(true, &decOk)); QString message; //check if ra & dec values were successfully converted if (!raOk || !decOk) { KMessageBox::error(KStars::Instance(), i18n("Invalid coordinates!")); return false; } //make sure values are in valid range if (ra.Hours() < 0.0 || ra.Degrees() > 360.0) message = i18n("The Right Ascension value must be between 0.0 and 24.0."); if (dec.Degrees() < -90.0 || dec.Degrees() > 90.0) message += '\n' + i18n("The Declination value must be between -90.0 and 90.0."); if (!message.isEmpty()) { KMessageBox::sorry(nullptr, message, i18n("Invalid Coordinate Data")); return false; } //all checks passed return true; } void FlagManager::deleteFlagItem(int flagIdx) { if (flagIdx < m_Model->rowCount()) { m_Model->removeRow(flagIdx); } } void FlagManager::slotAddFlag() { if (validatePoint() == false) return; dms ra(ui->raBox->createDms(false)); //false means expressed in hours dms dec(ui->decBox->createDms(true)); insertFlag(true); FlagComponent *flags = m_Ks->data()->skyComposite()->flags(); //Add flag in FlagComponent SkyPoint flagPoint(ra, dec); flags->add(flagPoint, ui->epochBox->text(), ui->flagCombobox->currentText(), ui->flagLabel->text(), ui->labelColorcombo->color()); ui->flagList->selectRow(m_Model->rowCount() - 1); ui->saveButton->setEnabled(true); flags->saveToFile(); //Redraw map m_Ks->map()->forceUpdate(false); } void FlagManager::slotDeleteFlag() { int flag = ui->flagList->currentIndex().row(); //Remove from FlagComponent m_Ks->data()->skyComposite()->flags()->remove(flag); //Remove from list m_Model->removeRow(flag); //Clear form fields clearFields(); //Remove from file m_Ks->data()->skyComposite()->flags()->saveToFile(); //Redraw map m_Ks->map()->forceUpdate(false); } void FlagManager::slotCenterFlag() { if (ui->flagList->currentIndex().isValid()) { m_Ks->map()->setClickedObject(nullptr); m_Ks->map()->setClickedPoint( m_Ks->data()->skyComposite()->flags()->pointList().at(ui->flagList->currentIndex().row()).get()); m_Ks->map()->slotCenter(); } } void FlagManager::slotCenterTelescope() { #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 == nullptr) 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); gd->setProperty(&SlewCMD); gd->runCommand(INDI_SEND_COORDS, m_Ks->data()->skyComposite()->flags()->pointList().at(ui->flagList->currentIndex().row()).get()); return; } KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); #endif } void FlagManager::slotSaveChanges() { int row = ui->flagList->currentIndex().row(); if (validatePoint() == false) return; insertFlag(false, row); m_Ks->map()->forceUpdate(); dms ra(ui->raBox->createDms(false)); //false means expressed in hours dms dec(ui->decBox->createDms(true)); SkyPoint flagPoint(ra, dec); //Update FlagComponent m_Ks->data()->skyComposite()->flags()->updateFlag(row, flagPoint, ui->epochBox->text(), ui->flagCombobox->currentText(), ui->flagLabel->text(), ui->labelColorcombo->color()); //Save changes to file m_Ks->data()->skyComposite()->flags()->saveToFile(); ui->flagList->selectRow(row); } void FlagManager::slotSetShownFlag(QModelIndex idx) { showFlag(idx.row()); } void FlagManager::insertFlag(bool isNew, int row) { dms ra(ui->raBox->createDms(false)); //false means expressed in hours dms dec(ui->decBox->createDms(true)); SkyPoint flagPoint(ra, dec); // Add flag in the list QList itemList; QStandardItem *labelItem = new QStandardItem(ui->flagLabel->text()); labelItem->setForeground(QBrush(ui->labelColorcombo->color())); FlagComponent *flags = m_Ks->data()->skyComposite()->flags(); QPixmap pixmap; itemList << new QStandardItem(flagPoint.ra0().toHMSString()) << new QStandardItem(flagPoint.dec0().toDMSString()) << new QStandardItem(ui->epochBox->text()) << new QStandardItem(QIcon(pixmap.fromImage(flags->imageList(ui->flagCombobox->currentIndex()))), ui->flagCombobox->currentText()) << labelItem; if (isNew) { m_Model->appendRow(itemList); } else { for (int i = 0; i < m_Model->columnCount(); i++) { m_Model->setItem(row, i, itemList.at(i)); } } } diff --git a/kstars/tools/flagmanager.h b/kstars/tools/flagmanager.h index c6f74ed6a..184e602c9 100644 --- a/kstars/tools/flagmanager.h +++ b/kstars/tools/flagmanager.h @@ -1,87 +1,85 @@ /*************************************************************************** flagmanager.h - Flag manager ------------------- begin : Mon Feb 01 2009 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_flagmanager.h" #include #include class QSortFilterProxyModel; class QStandardItemModel; class KStars; class FlagManagerUI : public QFrame, public Ui::FlagManager { Q_OBJECT public: /** @short Constructor */ explicit FlagManagerUI(QWidget *parent); }; /** * @class FlagManager * @short Flag manager * Dialog box to add and remove flags * * @version 1.1 * @author Jerome SONRIER */ class FlagManager : public QDialog { Q_OBJECT public: - /** @short Constructor */ explicit FlagManager(QWidget *ks); - /** @short Destructor */ - ~FlagManager() override; + virtual ~FlagManager() override = default; void setRaDec(const dms &ra, const dms &dec); void clearFields(); void showFlag(const int flagIdx); bool validatePoint(); void deleteFlagItem(int flagIdx); public slots: /** @short Verify coordinates and add a flag */ void slotAddFlag(); /** @short Delete a flag */ void slotDeleteFlag(); /** @short Center the selected flag in the display */ void slotCenterFlag(); /** @brief Center the selected flag in the telescope. */ void slotCenterTelescope(); private slots: void slotSaveChanges(); void slotSetShownFlag(QModelIndex idx); private: void insertFlag(bool isNew, int row = 0); KStars *m_Ks { nullptr }; FlagManagerUI *ui { nullptr }; QStandardItemModel *m_Model { nullptr }; QSortFilterProxyModel *m_SortModel { nullptr }; }; diff --git a/kstars/tools/horizonmanager.cpp b/kstars/tools/horizonmanager.cpp index eb88830cd..928f8de97 100644 --- a/kstars/tools/horizonmanager.cpp +++ b/kstars/tools/horizonmanager.cpp @@ -1,585 +1,581 @@ /* Artificial Horizon Manager Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "horizonmanager.h" #include "kstars.h" #include "kstarsdata.h" #include "linelist.h" #include "Options.h" #include "skymap.h" #include "projections/projector.h" #include "skycomponents/artificialhorizoncomponent.h" #include "skycomponents/skymapcomposite.h" #include #include HorizonManagerUI::HorizonManagerUI(QWidget *p) : QFrame(p) { setupUi(this); } HorizonManager::HorizonManager(QWidget *w) : QDialog(w) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ui = new HorizonManagerUI(this); ui->setStyleSheet("QPushButton:checked { background-color: red; }"); ui->addRegionB->setIcon(QIcon::fromTheme("list-add")); ui->addPointB->setIcon(QIcon::fromTheme("list-add")); ui->removeRegionB->setIcon(QIcon::fromTheme("list-remove")); ui->removePointB->setIcon(QIcon::fromTheme("list-remove")); ui->clearPointsB->setIcon(QIcon::fromTheme("edit-clear")); ui->saveB->setIcon(QIcon::fromTheme("document-save")); ui->selectPointsB->setIcon( QIcon::fromTheme("snap-orthogonal")); ui->tipLabel->setPixmap( (QIcon::fromTheme("help-hint").pixmap(64, 64))); ui->polygonValidatoin->setPixmap( QIcon::fromTheme("process-stop").pixmap(32, 32)); ui->polygonValidatoin->setToolTip(i18n("Region is invalid. The polygon must be closed and located at the horizon")); ui->polygonValidatoin->hide(); setWindowTitle(i18n("Artificial Horizon Manager")); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setLayout(mainLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(slotSaveChanges())); selectPoints = false; // Set up List view m_RegionsModel = new QStandardItemModel(0, 3, this); m_RegionsModel->setHorizontalHeaderLabels(QStringList() << i18n("Region") << i18nc("Azimuth", "Az") << i18nc("Altitude", "Alt")); connect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(checkRegionState(QStandardItem*))); ui->regionsList->setModel(m_RegionsModel); ui->pointsList->setModel(m_RegionsModel); ui->pointsList->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); ui->pointsList->verticalHeader()->hide(); ui->pointsList->setColumnHidden(0, true); horizonComponent = KStarsData::Instance()->skyComposite()->artificialHorizon(); // Get the list m_HorizonList = horizonComponent->horizonList(); for (auto &horizon : *m_HorizonList) { QStandardItem *regionItem = new QStandardItem(horizon->region()); regionItem->setCheckable(true); regionItem->setCheckState(horizon->enabled() ? Qt::Checked : Qt::Unchecked); m_RegionsModel->appendRow(regionItem); SkyList *points = horizon->list()->points(); for (auto& p : *points) { QList pointsList; pointsList << new QStandardItem("") << new QStandardItem(p->az().toDMSString()) << new QStandardItem(p->alt().toDMSString()); regionItem->appendRow(pointsList); } } ui->removeRegionB->setEnabled(true); connect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(verifyItemValue(QStandardItem*))); //Connect buttons connect(ui->addRegionB, SIGNAL(clicked()), this, SLOT(slotAddRegion())); connect(ui->removeRegionB, SIGNAL(clicked()), this, SLOT(slotRemoveRegion())); connect(ui->removeRegionB, SIGNAL(clicked()), this, SLOT(slotRemovePoint())); connect(ui->regionsList, SIGNAL(clicked(QModelIndex)), this, SLOT(slotSetShownRegion(QModelIndex))); connect(ui->addPointB, SIGNAL(clicked()), this, SLOT(slotAddPoint())); connect(ui->removePointB, SIGNAL(clicked()), this, SLOT(slotRemovePoint())); connect(ui->clearPointsB, SIGNAL(clicked()), this, SLOT(clearPoints())); connect(ui->selectPointsB, SIGNAL(clicked(bool)), this, SLOT(setSelectPoints(bool))); connect(ui->saveB, SIGNAL(clicked()), this, SLOT(slotSaveChanges())); if (m_HorizonList->count() > 0) { ui->regionsList->selectionModel()->setCurrentIndex(m_RegionsModel->index(0, 0), QItemSelectionModel::SelectCurrent); showRegion(0); } } -HorizonManager::~HorizonManager() -{ -} - void HorizonManager::showRegion(int regionID) { if (regionID < 0 || regionID >= m_RegionsModel->rowCount()) return; else { ui->pointsList->setRootIndex(m_RegionsModel->index(regionID, 0)); ui->pointsList->setColumnHidden(0, true); QStandardItem *regionItem = m_RegionsModel->item(regionID, 0); ui->polygonValidatoin->hide(); if (regionItem && regionItem->rowCount() > 4) { if (validatePolygon(regionID)) { ui->polygonValidatoin->setPixmap( QIcon::fromTheme("dialog-ok").pixmap(32, 32)); ui->polygonValidatoin->setEnabled(true); ui->polygonValidatoin->setToolTip(i18n("Region is valid")); } else { ui->polygonValidatoin->setPixmap( QIcon::fromTheme("process-stop").pixmap(32, 32)); ui->polygonValidatoin->setEnabled(false); ui->polygonValidatoin->setToolTip(i18n("Region is invalid. The polygon must be closed")); } ui->polygonValidatoin->show(); } ui->addPointB->setEnabled(true); ui->removePointB->setEnabled(true); ui->selectPointsB->setEnabled(true); ui->clearPointsB->setEnabled(true); } ui->saveB->setEnabled(true); } bool HorizonManager::validatePolygon(int regionID) { QStandardItem *regionItem = m_RegionsModel->item(regionID, 0); const Projector *proj = SkyMap::Instance()->projector(); if (regionItem == nullptr) return false; QPolygonF poly; dms az, alt; SkyPoint p; for (int i = 0; i < regionItem->rowCount(); i++) { az = dms::fromString(regionItem->child(i, 1)->data(Qt::DisplayRole).toString(), true); alt = dms::fromString(regionItem->child(i, 2)->data(Qt::DisplayRole).toString(), true); if (std::isnan(az.Degrees()) || std::isnan(alt.Degrees())) return false; p.setAz(az); p.setAlt(alt); p.HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF point = proj->toScreen(&p); poly << point; } return poly.isClosed(); } void HorizonManager::slotAddRegion() { terminateLivePreview(); setPointSelection(false); QStandardItem *regionItem = new QStandardItem(i18n("Region %1", m_RegionsModel->rowCount() + 1)); regionItem->setCheckable(true); regionItem->setCheckState(Qt::Checked); m_RegionsModel->appendRow(regionItem); QModelIndex index = regionItem->index(); ui->regionsList->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); showRegion(m_RegionsModel->rowCount() - 1); } void HorizonManager::slotRemoveRegion() { terminateLivePreview(); setPointSelection(false); int regionID = ui->regionsList->currentIndex().row(); deleteRegion(regionID); if (regionID > 0) showRegion(regionID - 1); else if (m_RegionsModel->rowCount() == 0) { ui->polygonValidatoin->hide(); m_RegionsModel->clear(); } } void HorizonManager::deleteRegion(int regionID) { if (regionID == -1) return; if (regionID < m_RegionsModel->rowCount()) { horizonComponent->removeRegion(m_RegionsModel->item(regionID, 0)->data(Qt::DisplayRole).toString()); m_RegionsModel->removeRow(regionID); //Redraw map SkyMap::Instance()->forceUpdate(); } } void HorizonManager::slotSaveChanges() { terminateLivePreview(); setPointSelection(false); for (int i = 0; i < m_RegionsModel->rowCount(); i++) { if (validatePolygon(i) == false) { KMessageBox::error(KStars::Instance(), i18n("%1 polygon is invalid.", m_RegionsModel->item(i, 0)->data(Qt::DisplayRole).toString())); return; } } for (int i = 0; i < m_RegionsModel->rowCount(); i++) { QStandardItem *regionItem = m_RegionsModel->item(i, 0); QString regionName = regionItem->data(Qt::DisplayRole).toString(); horizonComponent->removeRegion(regionName); std::shared_ptr list(new LineList()); dms az, alt; std::shared_ptr p; for (int j = 0; j < regionItem->rowCount(); j++) { az = dms::fromString(regionItem->child(j, 1)->data(Qt::DisplayRole).toString(), true); alt = dms::fromString(regionItem->child(j, 2)->data(Qt::DisplayRole).toString(), true); p.reset(new SkyPoint()); p->setAz(az); p->setAlt(alt); p->HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); list->append(p); } horizonComponent->addRegion(regionName, regionItem->checkState() == Qt::Checked ? true : false, list); } horizonComponent->save(); SkyMap::Instance()->forceUpdateNow(); } void HorizonManager::slotSetShownRegion(QModelIndex idx) { showRegion(idx.row()); } void HorizonManager::processSkyPoint(QStandardItem *item, int row) { QStandardItem *azItem = item->child(row, 1); QStandardItem *altItem = item->child(row, 2); dms az = dms::fromString(azItem->data(Qt::DisplayRole).toString(), true); dms alt = dms::fromString(altItem->data(Qt::DisplayRole).toString(), true); // First & Last points always have altitude of zero if ((row == 0 && alt.Degrees() != 0) || (alt.Degrees() < 0)) { disconnect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(verifyItemValue(QStandardItem*))); altItem->setData(QVariant(0), Qt::DisplayRole); alt.setD(0); connect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(verifyItemValue(QStandardItem*))); } std::shared_ptr point; if (livePreview.get() == nullptr) { livePreview.reset(new LineList()); if (row > 0) { for (int i = 0; i < row; i++) { QStandardItem *azItem = item->child(i, 1); QStandardItem *altItem = item->child(i, 2); dms az = dms::fromString(azItem->data(Qt::DisplayRole).toString(), true); dms alt = dms::fromString(altItem->data(Qt::DisplayRole).toString(), true); std::shared_ptr point(new SkyPoint()); point->setAz(az); point->setAlt(alt); point->HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); livePreview->append(point); } /* LineList *prevList = m_HorizonList->at(item->row())->list(); if (prevList) { for (int i=0; i < prevList->points()->count(); i++) livePreview->append(prevList->at(i)); }*/ } horizonComponent->setLivePreview(livePreview); } if (item->rowCount() >= livePreview->points()->count()) { point.reset(new SkyPoint()); livePreview->append(point); } else point = livePreview->at(item->rowCount()); point->setAz(az); point->setAlt(alt); point->HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); qCDebug(KSTARS) << "Received Az: " << point->az().toDMSString() << " Alt: " << point->alt().toDMSString(); SkyMap::Instance()->forceUpdateNow(); bool finalPoint = false; if (item->rowCount() >= 5) { dms firstAz, firstAlt; firstAz = dms::fromString(item->child(0, 1)->data(Qt::DisplayRole).toString(), true); firstAlt = dms::fromString(item->child(0, 2)->data(Qt::DisplayRole).toString(), true); if (fabs(firstAz.Degrees() - point->az().Degrees()) <= 2 && fabs(firstAlt.Degrees() - point->alt().Degrees()) <= 2) { point->setAz(firstAz.Degrees()); point->setAlt(0); disconnect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(verifyItemValue(QStandardItem*))); azItem->setData(firstAz.toDMSString(), Qt::DisplayRole); altItem->setData(QVariant(0), Qt::DisplayRole); connect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(verifyItemValue(QStandardItem*))); setPointSelection(false); finalPoint = true; } } if (finalPoint && item->rowCount() > 4) { if (validatePolygon(ui->regionsList->currentIndex().row())) { ui->polygonValidatoin->setPixmap( QIcon::fromTheme("dialog-ok").pixmap(32, 32)); ui->polygonValidatoin->setEnabled(true); ui->polygonValidatoin->setToolTip(i18n("Region is valid")); } else { ui->polygonValidatoin->setPixmap( QIcon::fromTheme("process-stop").pixmap(32, 32)); ui->polygonValidatoin->setEnabled(false); ui->polygonValidatoin->setToolTip( i18n("Region is invalid. The polygon must be closed and located at the horizon")); } ui->polygonValidatoin->show(); } } void HorizonManager::addSkyPoint(SkyPoint *skypoint) { if (selectPoints == false) return; slotAddPoint(); QStandardItem *regionItem = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0); if (regionItem) { QStandardItem *az = regionItem->child(regionItem->rowCount() - 1, 1); QStandardItem *alt = regionItem->child(regionItem->rowCount() - 1, 2); az->setData(skypoint->az().toDMSString(), Qt::DisplayRole); alt->setData(skypoint->alt().toDMSString(), Qt::DisplayRole); } } void HorizonManager::slotAddPoint() { QStandardItem *regionItem = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0); if (regionItem == nullptr) return; QList pointsList; pointsList << new QStandardItem("") << new QStandardItem("") << new QStandardItem(""); regionItem->appendRow(pointsList); m_RegionsModel->setHorizontalHeaderLabels(QStringList() << i18n("Region") << i18nc("Azimuth", "Az") << i18nc("Altitude", "Alt")); ui->pointsList->setColumnHidden(0, true); ui->pointsList->setRootIndex(regionItem->index()); } void HorizonManager::slotRemovePoint() { QStandardItem *regionItem = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0); if (regionItem) { int row = ui->pointsList->currentIndex().row(); if (row == -1) row = regionItem->rowCount() - 1; regionItem->removeRow(row); if (regionItem->rowCount() < 4) ui->polygonValidatoin->hide(); else { if (validatePolygon(ui->regionsList->currentIndex().row())) { ui->polygonValidatoin->setPixmap( QIcon::fromTheme("dialog-ok").pixmap(32, 32)); ui->polygonValidatoin->setEnabled(true); ui->polygonValidatoin->setToolTip(i18n("Region is valid")); } else { ui->polygonValidatoin->setPixmap( QIcon::fromTheme("process-stop").pixmap(32, 32)); ui->polygonValidatoin->setEnabled(false); ui->polygonValidatoin->setToolTip(i18n("Region is invalid. The polygon must be closed")); } } if (livePreview.get() && row < livePreview->points()->count()) { livePreview->points()->takeAt(row); if (livePreview->points()->count() == 0) terminateLivePreview(); else SkyMap::Instance()->forceUpdateNow(); } } } void HorizonManager::clearPoints() { QStandardItem *regionItem = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0); //qCDebug(KSTARS) << "Row " << ui->regionsList->currentIndex().row(); if (regionItem) { regionItem->removeRows(0, regionItem->rowCount()); horizonComponent->removeRegion(regionItem->data(Qt::DisplayRole).toString(), true); ui->polygonValidatoin->hide(); } terminateLivePreview(); } void HorizonManager::setSelectPoints(bool enable) { selectPoints = enable; ui->selectPointsB->clearFocus(); } void HorizonManager::verifyItemValue(QStandardItem *item) { bool azOK = true, altOK = true; if (item->column() >= 1) { QStandardItem *parent = item->parent(); dms azAngle = dms::fromString(parent->child(item->row(), 1)->data(Qt::DisplayRole).toString(), true); dms altAngle = dms::fromString(parent->child(item->row(), 2)->data(Qt::DisplayRole).toString(), true); if (std::isnan(azAngle.Degrees())) azOK = false; if (std::isnan(altAngle.Degrees())) altOK = false; if ((item->column() == 1 && azOK == false) || (item->column() == 2 && altOK == false)) { KMessageBox::error(KStars::Instance(), i18n("Invalid angle value: %1", item->data(Qt::DisplayRole).toString())); item->setData(QVariant(0), Qt::DisplayRole); return; } else if (azOK && altOK) processSkyPoint(item->parent(), item->row()); } } void HorizonManager::terminateLivePreview() { if (!livePreview.get()) return; livePreview.reset(); horizonComponent->setLivePreview(livePreview); } void HorizonManager::setPointSelection(bool enable) { selectPoints = enable; ui->selectPointsB->setChecked(enable); } void HorizonManager::checkRegionState(QStandardItem *item) { //foreach(ArtificialHorizonEntity *horizon, *m_HorizonList) if (item->row() >= m_HorizonList->count()) return; ArtificialHorizonEntity *horizon = m_HorizonList->at(item->row()); horizon->setRegion(item->data(Qt::DisplayRole).toString()); horizon->setEnabled(item->checkState() == Qt::Checked); SkyMap::Instance()->forceUpdateNow(); } diff --git a/kstars/tools/horizonmanager.h b/kstars/tools/horizonmanager.h index cd78265b8..b2dc7d7ee 100644 --- a/kstars/tools/horizonmanager.h +++ b/kstars/tools/horizonmanager.h @@ -1,94 +1,94 @@ /* Artificial Horizon Manager Copyright (C) 2015 Jasem Mutlaq This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #pragma once #include "ui_horizonmanager.h" #include #include class QStandardItem; class QStandardItemModel; class ArtificialHorizonComponent; class ArtificialHorizonEntity; class LineList; class SkyPoint; class HorizonManagerUI : public QFrame, public Ui::HorizonManager { Q_OBJECT public: /** @short Constructor */ explicit HorizonManagerUI(QWidget *parent); }; /** * @class HorizonManager * @short Manages adding/removing and editing regions and points associated with * user-customized artificial horizons. * * @version 1.0 * @author Jasem Mutlaq */ class HorizonManager : public QDialog { Q_OBJECT public: /** @short Constructor */ explicit HorizonManager(QWidget *ks); /** @short Destructor */ - ~HorizonManager() override; + virtual ~HorizonManager() override = default; void showRegion(const int regionID); bool validatePolygon(int regionID); void deleteRegion(int regionID); public slots: /** @short Add region */ void slotAddRegion(); /** @short Delete region */ void slotRemoveRegion(); void addSkyPoint(SkyPoint *skypoint); void slotAddPoint(); void slotRemovePoint(); void clearPoints(); void setSelectPoints(bool); private slots: void processSkyPoint(QStandardItem *item, int row); void verifyItemValue(QStandardItem *item); void checkRegionState(QStandardItem *item); void slotSaveChanges(); void slotSetShownRegion(QModelIndex idx); private: void terminateLivePreview(); void setPointSelection(bool enable); HorizonManagerUI *ui { nullptr }; QStandardItemModel *m_RegionsModel { nullptr }; ArtificialHorizonComponent *horizonComponent { nullptr }; QList *m_HorizonList { nullptr }; std::shared_ptr livePreview; bool selectPoints { false }; }; diff --git a/kstars/tools/modcalcaltaz.cpp b/kstars/tools/modcalcaltaz.cpp index 21c1e1a2f..45e0deea4 100644 --- a/kstars/tools/modcalcaltaz.cpp +++ b/kstars/tools/modcalcaltaz.cpp @@ -1,145 +1,141 @@ /*************************************************************************** modcalcaltaz.cpp - description ------------------- begin : s� oct 26 2002 copyright : (C) 2002 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcaltaz.h" -#include -#include - -#include -#include - -#include "skyobjects/skypoint.h" #include "geolocation.h" #include "kstars.h" #include "kstarsdata.h" #include "kstarsdatetime.h" -#include "widgets/dmsbox.h" #include "dialogs/finddialog.h" #include "dialogs/locationdialog.h" +#include "skyobjects/skypoint.h" +#include "widgets/dmsbox.h" + +#include +#include + +#include +#include modCalcAltAz::modCalcAltAz(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); KStarsData *data = KStarsData::Instance(); RA->setDegType(false); //Initialize Date/Time and Location data geoPlace = data->geo(); LocationButton->setText(geoPlace->fullName()); //Make sure slotDateTime() gets called, so that LST will be set connect(DateTime, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(slotDateTimeChanged(QDateTime))); DateTime->setDateTime(data->lt()); connect(NowButton, SIGNAL(clicked()), this, SLOT(slotNow())); connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(ObjectButton, SIGNAL(clicked()), this, SLOT(slotObject())); connect(RA, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(Dec, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(Az, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(Alt, SIGNAL(editingFinished()), this, SLOT(slotCompute())); show(); } -modCalcAltAz::~modCalcAltAz() -{ -} - void modCalcAltAz::slotNow() { DateTime->setDateTime(KStarsDateTime::currentDateTime()); slotCompute(); } void modCalcAltAz::slotLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { GeoLocation *newGeo = ld->selectedCity(); if (newGeo) { geoPlace = newGeo; LocationButton->setText(geoPlace->fullName()); slotCompute(); } } delete ld; } void modCalcAltAz::slotObject() { FindDialog fd(KStars::Instance()); if (fd.exec() == QDialog::Accepted) { SkyObject *o = fd.targetObject(); RA->showInHours(o->ra()); Dec->showInDegrees(o->dec()); slotCompute(); } } void modCalcAltAz::slotDateTimeChanged(const QDateTime &dt) { KStarsDateTime ut = geoPlace->LTtoUT(KStarsDateTime(dt)); LST = geoPlace->GSTtoLST(ut.gst()); } void modCalcAltAz::slotCompute() { //Determine whether we are calculating Alt/Az coordinates from RA/Dec, //or vice versa. We calculate Alt/Az by default, unless the signal //was sent by changing the Az or Alt value. if (sender()->objectName() == "Az" || sender()->objectName() == "Alt") { //Validate Az and Alt coordinates bool ok(false); dms alt; dms az = Az->createDms(true, &ok); if (ok) alt = Alt->createDms(true, &ok); if (ok) { SkyPoint sp; sp.setAz(az); sp.setAlt(alt); sp.HorizontalToEquatorial(&LST, geoPlace->lat()); RA->showInHours(sp.ra()); Dec->showInDegrees(sp.dec()); } } else { //Validate RA and Dec coordinates bool ok(false); dms ra; dms dec = Dec->createDms(true, &ok); if (ok) ra = RA->createDms(false, &ok); if (ok) { SkyPoint sp(ra, dec); sp.EquatorialToHorizontal(&LST, geoPlace->lat()); Az->showInDegrees(sp.az()); Alt->showInDegrees(sp.alt()); } } } diff --git a/kstars/tools/modcalcaltaz.h b/kstars/tools/modcalcaltaz.h index d9d48b3a4..20a8c7553 100644 --- a/kstars/tools/modcalcaltaz.h +++ b/kstars/tools/modcalcaltaz.h @@ -1,48 +1,48 @@ /*************************************************************************** modcalcaltaz.h - description ------------------- begin : sáb oct 26 2002 copyright : (C) 2002 by Pablo de Vicente email : pvicentea@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "dms.h" #include "ui_modcalcaltaz.h" class QDateTime; class GeoLocation; /** *@author Pablo de Vicente */ class modCalcAltAz : public QFrame, public Ui::modCalcAltAz { Q_OBJECT public: explicit modCalcAltAz(QWidget *p); - ~modCalcAltAz() override; + virtual ~modCalcAltAz() override = default; public slots: void slotCompute(); void slotNow(); void slotLocation(); void slotObject(); void slotDateTimeChanged(const QDateTime &); private: GeoLocation *geoPlace; dms LST; }; diff --git a/kstars/tools/modcalcangdist.cpp b/kstars/tools/modcalcangdist.cpp index ec806a2f4..d2498b828 100644 --- a/kstars/tools/modcalcangdist.cpp +++ b/kstars/tools/modcalcangdist.cpp @@ -1,268 +1,264 @@ /*************************************************************************** modcalcapcoord.cpp - description ------------------- begin : Sun May 30 2004 copyright : (C) 2004 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcangdist.h" -#include -#include +#include "dms.h" +#include "kstars.h" +#include "dialogs/finddialog.h" +#include "skyobjects/skyobject.h" +#include "skyobjects/skypoint.h" +#include "widgets/dmsbox.h" #include #include -#include "dms.h" -#include "widgets/dmsbox.h" -#include "skyobjects/skypoint.h" -#include "skyobjects/skyobject.h" -#include "dialogs/finddialog.h" -#include "kstars.h" +#include +#include modCalcAngDist::modCalcAngDist(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); FirstRA->setDegType(false); SecondRA->setDegType(false); connect(FirstRA, SIGNAL(editingFinished()), this, SLOT(slotValidatePositions())); connect(FirstDec, SIGNAL(editingFinished()), this, SLOT(slotValidatePositions())); connect(SecondRA, SIGNAL(editingFinished()), this, SLOT(slotValidatePositions())); connect(SecondDec, SIGNAL(editingFinished()), this, SLOT(slotValidatePositions())); connect(FirstRA, SIGNAL(textEdited(QString)), this, SLOT(slotResetTitle())); connect(FirstDec, SIGNAL(textEdited(QString)), this, SLOT(slotResetTitle())); connect(SecondRA, SIGNAL(textEdited(QString)), this, SLOT(slotResetTitle())); connect(SecondDec, SIGNAL(textEdited(QString)), this, SLOT(slotResetTitle())); connect(FirstObjectButton, SIGNAL(clicked()), this, SLOT(slotObjectButton())); connect(SecondObjectButton, SIGNAL(clicked()), this, SLOT(slotObjectButton())); connect(runButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); show(); slotValidatePositions(); } -modCalcAngDist::~modCalcAngDist() -{ -} - SkyPoint modCalcAngDist::getCoords(dmsBox *rBox, dmsBox *dBox, bool *ok) { dms raCoord, decCoord; bool ok2 = false; raCoord = rBox->createDms(false, &ok2); if (ok2) decCoord = dBox->createDms(true, &ok2); if (ok2) { if (ok) *ok = ok2; return SkyPoint(raCoord, decCoord); } else { if (ok) *ok = ok2; return SkyPoint(); } } void modCalcAngDist::slotValidatePositions() { SkyPoint sp0, sp1; bool ok; sp0 = getCoords(FirstRA, FirstDec, &ok); if (ok) sp1 = getCoords(SecondRA, SecondDec, &ok); if (ok) { double PA = 0; AngDist->setText(sp0.angularDistanceTo(&sp1, &PA).toDMSString()); PositionAngle->setText(QString::number(PA, 'f', 3)); } else { AngDist->setText(" .... "); PositionAngle->setText(" .... "); } } void modCalcAngDist::slotObjectButton() { QPointer fd = new FindDialog(this); if (fd->exec() == QDialog::Accepted) { SkyObject *o = fd->targetObject(); if (sender()->objectName() == QString("FirstObjectButton")) { FirstRA->showInHours(o->ra()); FirstDec->showInDegrees(o->dec()); FirstPositionBox->setTitle(i18n("First position: %1", o->name())); } else { SecondRA->showInHours(o->ra()); SecondDec->showInDegrees(o->dec()); SecondPositionBox->setTitle(i18n("Second position: %1", o->name())); } slotValidatePositions(); } delete fd; } void modCalcAngDist::slotResetTitle() { QString name = sender()->objectName(); if (name.contains("First")) FirstPositionBox->setTitle(i18n("First position")); else SecondPositionBox->setTitle(i18n("Second position")); } void modCalcAngDist::slotRunBatch() { QString inputFileName = InputLineEditBatch->url().toLocalFile(); // We open the input file and read its content if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); inputFileName.clear(); return; } // processLines(&f); QTextStream istream(&f); processLines(istream); // readFile( istream ); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); inputFileName.clear(); InputLineEditBatch->setText(inputFileName); return; } } //void modCalcAngDist::processLines( const QFile * fIn ) { void modCalcAngDist::processLines(QTextStream &istream) { // we open the output file // QTextStream istream(&fIn); QString outputFileName; outputFileName = OutputLineEditBatch->text(); QFile fOut(outputFileName); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); QString line; QChar space = ' '; int i = 0; SkyPoint sp0, sp1; double PA = 0; dms ra0B, dec0B, ra1B, dec1B, dist; while (!istream.atEnd()) { line = istream.readLine(); line = line.trimmed(); //Go through the line, looking for parameters QStringList fields = line.split(' '); i = 0; // Read RA and write in ostream if corresponds if (ra0CheckBatch->isChecked()) { ra0B = dms::fromString(fields[i], false); i++; } else ra0B = ra0BoxBatch->createDms(false); if (allRadioBatch->isChecked()) ostream << ra0B.toHMSString() << space; else if (ra0CheckBatch->isChecked()) ostream << ra0B.toHMSString() << space; // Read DEC and write in ostream if corresponds if (dec0CheckBatch->isChecked()) { dec0B = dms::fromString(fields[i], true); i++; } else dec0B = dec0BoxBatch->createDms(); if (allRadioBatch->isChecked()) ostream << dec0B.toDMSString() << space; else if (dec0CheckBatch->isChecked()) ostream << dec0B.toDMSString() << space; // Read RA and write in ostream if corresponds if (ra1CheckBatch->isChecked()) { ra1B = dms::fromString(fields[i], false); i++; } else ra1B = ra1BoxBatch->createDms(false); if (allRadioBatch->isChecked()) ostream << ra1B.toHMSString() << space; else if (ra1CheckBatch->isChecked()) ostream << ra1B.toHMSString() << space; // Read DEC and write in ostream if corresponds if (dec1CheckBatch->isChecked()) { dec1B = dms::fromString(fields[i], true); i++; } else dec1B = dec1BoxBatch->createDms(); if (allRadioBatch->isChecked()) ostream << dec1B.toDMSString() << space; else if (dec1CheckBatch->isChecked()) ostream << dec1B.toDMSString() << space; sp0 = SkyPoint(ra0B, dec0B); sp1 = SkyPoint(ra1B, dec1B); dist = sp0.angularDistanceTo(&sp1, &PA); ostream << dist.toDMSString() << QString::number(PA, 'f', 3) << endl; } fOut.close(); } diff --git a/kstars/tools/modcalcangdist.h b/kstars/tools/modcalcangdist.h index 8191dc29f..999d8933d 100644 --- a/kstars/tools/modcalcangdist.h +++ b/kstars/tools/modcalcangdist.h @@ -1,56 +1,55 @@ /*************************************************************************** modcalcapcoord.h - description ------------------- begin : Sun May 30 2004 copyright : (C) 2004 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 MODCALCANGDIST_H_ -#define MODCALCANGDIST_H_ +#pragma once #include "ui_modcalcangdist.h" class dms; class dmsBox; class SkyPoint; class QTextStream; -/** Module to compute the angular distance between two points in the sky and position angle. - *@author Pablo de Vicente - *@author Jasem Mutlaq - *@version 1.0 - */ +/** + * Module to compute the angular distance between two points in the sky and position angle. + * + * @author Pablo de Vicente + * @author Jasem Mutlaq + * @version 1.0 + */ class modCalcAngDist : public QFrame, public Ui::modCalcAngDistDlg { Q_OBJECT public: /**Constructor. */ explicit modCalcAngDist(QWidget *p); - /**Destructor. */ - ~modCalcAngDist() override; + + virtual ~modCalcAngDist() override = default; public slots: void slotValidatePositions(); void slotObjectButton(); void slotResetTitle(); void slotRunBatch(); private: /** Process Lines **/ void processLines(QTextStream &istream); /** @returns a SkyPoint constructed from the coordinates in the RA and Dec dmsBoxes. */ SkyPoint getCoords(dmsBox *rBox, dmsBox *dBox, bool *ok); }; - -#endif diff --git a/kstars/tools/modcalcdaylength.cpp b/kstars/tools/modcalcdaylength.cpp index 67a33842e..8e79ccf61 100644 --- a/kstars/tools/modcalcdaylength.cpp +++ b/kstars/tools/modcalcdaylength.cpp @@ -1,336 +1,332 @@ /*************************************************************************** modcalcdaylength.cpp - description ------------------- begin : wed jun 12 2002 copyright : (C) 2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcdaylength.h" #include "geolocation.h" #include "kstarsdata.h" #include "dialogs/locationdialog.h" #include "skyobjects/ksmoon.h" #include "skyobjects/kssun.h" #include "skyobjects/skyobject.h" #include modCalcDayLength::modCalcDayLength(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); showCurrentDate(); initGeo(); slotComputeAlmanac(); connect(Date, SIGNAL(dateChanged(QDate)), this, SLOT(slotComputeAlmanac())); connect(Location, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(LocationBatch, SIGNAL(clicked()), this, SLOT(slotLocationBatch())); connect(InputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); connect(OutputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); connect(RunButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); connect(ViewButtonBatch, SIGNAL(clicked()), this, SLOT(slotViewBatch())); RunButtonBatch->setEnabled(false); ViewButtonBatch->setEnabled(false); show(); } -modCalcDayLength::~modCalcDayLength() -{ -} - void modCalcDayLength::showCurrentDate(void) { KStarsDateTime dt(KStarsDateTime::currentDateTime()); Date->setDate(dt.date()); } void modCalcDayLength::initGeo(void) { KStarsData *data = KStarsData::Instance(); geoPlace = data->geo(); geoBatch = data->geo(); Location->setText(geoPlace->fullName()); LocationBatch->setText(geoBatch->fullName()); } QTime modCalcDayLength::lengthOfDay(const QTime &setQTime, const QTime &riseQTime) { QTime dL(0, 0, 0); int dds = riseQTime.secsTo(setQTime); QTime dLength = dL.addSecs(dds); return dLength; } void modCalcDayLength::slotLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { GeoLocation *newGeo = ld->selectedCity(); if (newGeo) { geoPlace = newGeo; Location->setText(geoPlace->fullName()); } } delete ld; slotComputeAlmanac(); } void modCalcDayLength::slotLocationBatch() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { GeoLocation *newGeo = ld->selectedCity(); if (newGeo) { geoBatch = newGeo; LocationBatch->setText(geoBatch->fullName()); } } delete ld; } void modCalcDayLength::updateAlmanac(const QDate &d, GeoLocation *geo) { //Determine values needed for the Almanac long double jd0 = KStarsDateTime(d, QTime(8, 0, 0)).djd(); KSNumbers num(jd0); //Sun KSSun Sun; Sun.findPosition(&num); QTime ssTime = Sun.riseSetTime(KStarsDateTime(jd0), geo, false); QTime srTime = Sun.riseSetTime(KStarsDateTime(jd0), geo, true); QTime stTime = Sun.transitTime(KStarsDateTime(jd0), geo); dms ssAz = Sun.riseSetTimeAz(KStarsDateTime(jd0), geo, false); dms srAz = Sun.riseSetTimeAz(KStarsDateTime(jd0), geo, true); dms stAlt = Sun.transitAltitude(KStarsDateTime(jd0), geo); //In most cases, the Sun will rise and set: if (ssTime.isValid()) { ssAzString = ssAz.toDMSString(); stAltString = stAlt.toDMSString(); srAzString = srAz.toDMSString(); ssTimeString = QLocale().toString(ssTime); srTimeString = QLocale().toString(srTime); stTimeString = QLocale().toString(stTime); QTime daylength = lengthOfDay(ssTime, srTime); //daylengthString = QLocale().toString(daylength); daylengthString = QLocale().toString(daylength, "hh:mm:ss"); //...but not always! } else if (stAlt.Degrees() > 0.) { ssAzString = i18n("Circumpolar"); stAltString = stAlt.toDMSString(); srAzString = i18n("Circumpolar"); ssTimeString = "--:--"; srTimeString = "--:--"; stTimeString = QLocale().toString(stTime); daylengthString = "24:00"; } else if (stAlt.Degrees() < 0.) { ssAzString = i18n("Does not rise"); stAltString = stAlt.toDMSString(); srAzString = i18n("Does not set"); ssTimeString = "--:--"; srTimeString = "--:--"; stTimeString = QLocale().toString(stTime); daylengthString = "00:00"; } //Moon KSMoon Moon; QTime msTime = Moon.riseSetTime(KStarsDateTime(jd0), geo, false); QTime mrTime = Moon.riseSetTime(KStarsDateTime(jd0), geo, true); QTime mtTime = Moon.transitTime(KStarsDateTime(jd0), geo); dms msAz = Moon.riseSetTimeAz(KStarsDateTime(jd0), geo, false); dms mrAz = Moon.riseSetTimeAz(KStarsDateTime(jd0), geo, true); dms mtAlt = Moon.transitAltitude(KStarsDateTime(jd0), geo); //In most cases, the Moon will rise and set: if (msTime.isValid()) { msAzString = msAz.toDMSString(); mtAltString = mtAlt.toDMSString(); mrAzString = mrAz.toDMSString(); msTimeString = QLocale().toString(msTime); mrTimeString = QLocale().toString(mrTime); mtTimeString = QLocale().toString(mtTime); //...but not always! } else if (mtAlt.Degrees() > 0.) { msAzString = i18n("Circumpolar"); mtAltString = mtAlt.toDMSString(); mrAzString = i18n("Circumpolar"); msTimeString = "--:--"; mrTimeString = "--:--"; mtTimeString = QLocale().toString(mtTime); } else if (mtAlt.Degrees() < 0.) { msAzString = i18n("Does not rise"); mtAltString = mtAlt.toDMSString(); mrAzString = i18n("Does not rise"); msTimeString = "--:--"; mrTimeString = "--:--"; mtTimeString = QLocale().toString(mtTime); } //after calling riseSetTime Phase needs to reset, setting it before causes Phase to set nan Moon.findPosition(&num); Moon.findPhase(nullptr); lunarphaseString = Moon.phaseName() + " (" + QString::number(int(100 * Moon.illum())) + "%)"; //Fix length of Az strings if (srAz.Degrees() < 100.0) srAzString = ' ' + srAzString; if (ssAz.Degrees() < 100.0) ssAzString = ' ' + ssAzString; if (mrAz.Degrees() < 100.0) mrAzString = ' ' + mrAzString; if (msAz.Degrees() < 100.0) msAzString = ' ' + msAzString; } void modCalcDayLength::slotComputeAlmanac() { updateAlmanac(Date->date(), geoPlace); SunSet->setText(ssTimeString); SunRise->setText(srTimeString); SunTransit->setText(stTimeString); SunSetAz->setText(ssAzString); SunRiseAz->setText(srAzString); SunTransitAlt->setText(stAltString); DayLength->setText(daylengthString); MoonSet->setText(msTimeString); MoonRise->setText(mrTimeString); MoonTransit->setText(mtTimeString); MoonSetAz->setText(msAzString); MoonRiseAz->setText(mrAzString); MoonTransitAlt->setText(mtAltString); LunarPhase->setText(lunarphaseString); } void modCalcDayLength::slotCheckFiles() { bool flag = !InputFileBatch->lineEdit()->text().isEmpty() && !OutputFileBatch->lineEdit()->text().isEmpty(); RunButtonBatch->setEnabled(flag); } void modCalcDayLength::slotRunBatch() { QString inputFileName = InputFileBatch->url().toLocalFile(); if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } QTextStream istream(&f); processLines(istream); ViewButtonBatch->setEnabled(true); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); return; } } void modCalcDayLength::processLines(QTextStream &istream) { QFile fOut(OutputFileBatch->url().toLocalFile()); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); //Write header ostream << "# " << i18nc("%1 is a location on earth", "Almanac for %1", geoBatch->fullName()) << QString(" [%1, %2]").arg(geoBatch->lng()->toDMSString(), geoBatch->lat()->toDMSString()) << endl << "# " << i18n("computed by KStars") << endl << "#" << endl << "# Date SRise STran SSet SRiseAz STranAlt SSetAz DayLen MRise MTran MSet " " MRiseAz MTranAlt MSetAz LunarPhase" << endl << "#" << endl; QString line; QDate d; while (!istream.atEnd()) { line = istream.readLine(); line = line.trimmed(); //Parse the line as a date, then compute Almanac values d = QDate::fromString(line); if (d.isValid()) { updateAlmanac(d, geoBatch); ostream << d.toString(Qt::ISODate) << " " << srTimeString << " " << stTimeString << " " << ssTimeString << " " << srAzString << " " << stAltString << " " << ssAzString << " " << daylengthString << " " << mrTimeString << " " << mtTimeString << " " << msTimeString << " " << mrAzString << " " << mtAltString << " " << msAzString << " " << lunarphaseString << endl; } } } void modCalcDayLength::slotViewBatch() { QFile fOut(OutputFileBatch->url().toLocalFile()); fOut.open(QIODevice::ReadOnly); QTextStream istream(&fOut); QStringList text; while (!istream.atEnd()) text.append(istream.readLine()); fOut.close(); KMessageBox::informationList(nullptr, i18n("Results of Almanac calculation"), text, OutputFileBatch->url().toLocalFile()); } diff --git a/kstars/tools/modcalcdaylength.h b/kstars/tools/modcalcdaylength.h index 60973695e..3087d7e68 100644 --- a/kstars/tools/modcalcdaylength.h +++ b/kstars/tools/modcalcdaylength.h @@ -1,69 +1,68 @@ /*************************************************************************** modcalcdaylength.h - description ------------------- begin : wed jun 12 2002 copyright : (C) 2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_modcalcdaylength.h" #include class QDate; class QTextStream; class QTime; class GeoLocation; /** * Module to compute the equatorial coordinates for a given date and time * from a given epoch or equinox * * @author Pablo de Vicente */ class modCalcDayLength : public QFrame, public Ui::modCalcDayLengthDlg { Q_OBJECT public: - /** Constructor */ explicit modCalcDayLength(QWidget *p); - /** Destructor */ - ~modCalcDayLength() override; + + virtual ~modCalcDayLength() override = default; public slots: void slotLocation(); void slotLocationBatch(); void slotComputeAlmanac(); void slotRunBatch(); void slotViewBatch(); void slotCheckFiles(); private: void updateAlmanac(const QDate &d, GeoLocation *geo); QTime lengthOfDay(const QTime &setQTime, const QTime &riseQTime); void showCurrentDate(); void initGeo(); void processLines(QTextStream &istream); GeoLocation *geoPlace { nullptr }; GeoLocation *geoBatch { nullptr }; QString srTimeString, stTimeString, ssTimeString; QString mrTimeString, mtTimeString, msTimeString; QString srAzString, stAltString, ssAzString; QString mrAzString, mtAltString, msAzString; QString daylengthString, lunarphaseString; }; diff --git a/kstars/tools/modcalceclipticcoords.cpp b/kstars/tools/modcalceclipticcoords.cpp index 3be9a1459..7db6f8994 100644 --- a/kstars/tools/modcalceclipticcoords.cpp +++ b/kstars/tools/modcalceclipticcoords.cpp @@ -1,123 +1,119 @@ /*************************************************************************** modcalceclipticcoords.cpp - description ------------------- begin : Fri May 14 2004 copyright : (C) 2004 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalceclipticcoords.h" -#include -#include - -#include -#include -#include - #include "dms.h" +#include "ksnumbers.h" #include "kstars.h" #include "kstarsdata.h" -#include "skyobjects/skypoint.h" -#include "ksnumbers.h" #include "dialogs/finddialog.h" +#include "skyobjects/skypoint.h" #include "widgets/dmsbox.h" +#include +#include +#include + +#include +#include + modCalcEclCoords::modCalcEclCoords(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); RA->setDegType(false); //Initialize Date/Time and Location data DateTime->setDateTime(KStarsData::Instance()->lt()); kdt = KStarsDateTime(DateTime->dateTime()); connect(NowButton, SIGNAL(clicked()), this, SLOT(slotNow())); connect(ObjectButton, SIGNAL(clicked()), this, SLOT(slotObject())); connect(DateTime, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(slotDateTimeChanged(QDateTime))); connect(RA, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(Dec, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(EcLong, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(EcLat, SIGNAL(editingFinished()), this, SLOT(slotCompute())); this->show(); } -modCalcEclCoords::~modCalcEclCoords() -{ -} - void modCalcEclCoords::slotNow() { DateTime->setDateTime(KStarsDateTime::currentDateTime()); slotCompute(); } void modCalcEclCoords::slotObject() { FindDialog fd(KStars::Instance()); if (fd.exec() == QDialog::Accepted) { SkyObject *o = fd.targetObject(); RA->showInHours(o->ra()); Dec->showInDegrees(o->dec()); slotCompute(); } } void modCalcEclCoords::slotDateTimeChanged(const QDateTime &edt) { kdt = ((KStarsDateTime)edt); } void modCalcEclCoords::slotCompute(void) { KSNumbers num(kdt.djd()); //Determine whether we are calculating ecliptic coordinates from equatorial, //or vice versa. We calculate ecliptic by default, unless the signal //was sent by changing the EcLong or EcLat value. if (sender()->objectName() == "EcLong" || sender()->objectName() == "EcLat") { //Validate ecliptic coordinates bool ok(false); dms elat; dms elong = EcLong->createDms(true, &ok); if (ok) elat = EcLat->createDms(true, &ok); if (ok) { SkyPoint sp; sp.setFromEcliptic(num.obliquity(), elong, elat); RA->showInHours(sp.ra()); Dec->showInDegrees(sp.dec()); } } else { //Validate RA and Dec coordinates bool ok(false); dms ra; dms dec = Dec->createDms(true, &ok); if (ok) ra = RA->createDms(false, &ok); if (ok) { SkyPoint sp(ra, dec); dms elong, elat; sp.findEcliptic(num.obliquity(), elong, elat); EcLong->showInDegrees(elong); EcLat->showInDegrees(elat); } } } diff --git a/kstars/tools/modcalceclipticcoords.h b/kstars/tools/modcalceclipticcoords.h index fed128d33..06befdd70 100644 --- a/kstars/tools/modcalceclipticcoords.h +++ b/kstars/tools/modcalceclipticcoords.h @@ -1,47 +1,47 @@ /*************************************************************************** modcalceclipticcoords.h - description ------------------- begin : Fri May 14 2004 copyright : (C) 2004 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "kstarsdatetime.h" #include "ui_modcalceclipticcoords.h" /** - * Class which implements the KStars calculator module to compute - * geocentric ecliptic coordinates to/from geocentric equatorial coordinates. - * - * Inherits QWidget - * - * @author Pablo de Vicente - */ + * Class which implements the KStars calculator module to compute + * geocentric ecliptic coordinates to/from geocentric equatorial coordinates. + * + * Inherits QWidget + * + * @author Pablo de Vicente + */ class modCalcEclCoords : public QFrame, public Ui::modCalcEclCoordsDlg { - Q_OBJECT + Q_OBJECT public: explicit modCalcEclCoords(QWidget *p); - ~modCalcEclCoords() override; + virtual ~modCalcEclCoords() override = default; public slots: void slotNow(void); void slotObject(void); void slotDateTimeChanged(const QDateTime &edt); void slotCompute(void); private: KStarsDateTime kdt; }; diff --git a/kstars/tools/modcalcgalcoord.cpp b/kstars/tools/modcalcgalcoord.cpp index 7fef760d2..19907ba95 100644 --- a/kstars/tools/modcalcgalcoord.cpp +++ b/kstars/tools/modcalcgalcoord.cpp @@ -1,350 +1,346 @@ /*************************************************************************** modcalcgal.cpp - description ------------------- begin : Thu Jan 17 2002 copyright : (C) 2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcgalcoord.h" #include "dialogs/finddialog.h" #include "skyobjects/skyobject.h" #include "skyobjects/skypoint.h" #include #include #include modCalcGalCoord::modCalcGalCoord(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); RA->setDegType(false); connect(RA, SIGNAL(editingFinished()), this, SLOT(slotComputeCoords())); connect(Dec, SIGNAL(editingFinished()), this, SLOT(slotComputeCoords())); connect(GalLongitude, SIGNAL(editingFinished()), this, SLOT(slotComputeCoords())); connect(GalLatitude, SIGNAL(editingFinished()), this, SLOT(slotComputeCoords())); connect(ObjectButton, SIGNAL(clicked()), this, SLOT(slotObject())); connect(decCheckBatch, SIGNAL(clicked()), this, SLOT(slotDecCheckedBatch())); connect(raCheckBatch, SIGNAL(clicked()), this, SLOT(slotRaCheckedBatch())); connect(epochCheckBatch, SIGNAL(clicked()), this, SLOT(slotEpochCheckedBatch())); connect(galLongCheckBatch, SIGNAL(clicked()), this, SLOT(slotGalLongCheckedBatch())); connect(galLatCheckBatch, SIGNAL(clicked()), this, SLOT(slotGalLatCheckedBatch())); connect(runButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); show(); } -modCalcGalCoord::~modCalcGalCoord() -{ -} - void modCalcGalCoord::slotObject() { QPointer fd = new FindDialog(this); if (fd->exec() == QDialog::Accepted) { SkyObject *o = fd->targetObject(); RA->showInHours(o->ra()); Dec->showInDegrees(o->dec()); slotComputeCoords(); } delete fd; } void modCalcGalCoord::slotComputeCoords() { if (GalLongitude->hasFocus()) GalLongitude->clearFocus(); //Determine whether we should compute galactic coords from //equatorial, or vice versa if (sender()->objectName() == "GalLongitude" || sender()->objectName() == "GalLatitude") { //Validate GLong and GLat bool ok(false); dms glat; dms glong = GalLongitude->createDms(true, &ok); if (ok) glat = GalLatitude->createDms(true, &ok); if (ok) { SkyPoint sp; sp.GalacticToEquatorial1950(&glong, &glat); sp.B1950ToJ2000(); RA->showInHours(sp.ra()); Dec->showInDegrees(sp.dec()); } } else { //Validate RA and Dec bool ok(false); dms dec; dms ra = RA->createDms(false, &ok); if (ok) dec = Dec->createDms(true, &ok); if (ok) { dms glong, glat; SkyPoint sp(ra, dec); sp.J2000ToB1950(); sp.Equatorial1950ToGalactic(glong, glat); GalLongitude->showInDegrees(glong); GalLatitude->showInDegrees(glat); } } } void modCalcGalCoord::galCheck() { galLatCheckBatch->setChecked(false); galLatBoxBatch->setEnabled(false); galLongCheckBatch->setChecked(false); galLongBoxBatch->setEnabled(false); galInputCoords = false; } void modCalcGalCoord::equCheck() { raCheckBatch->setChecked(false); raBoxBatch->setEnabled(false); decCheckBatch->setChecked(false); decBoxBatch->setEnabled(false); epochCheckBatch->setChecked(false); galInputCoords = true; } void modCalcGalCoord::slotRaCheckedBatch() { if (raCheckBatch->isChecked()) { raBoxBatch->setEnabled(false); galCheck(); } else { raBoxBatch->setEnabled(true); } } void modCalcGalCoord::slotDecCheckedBatch() { if (decCheckBatch->isChecked()) { decBoxBatch->setEnabled(false); galCheck(); } else { decBoxBatch->setEnabled(true); } } void modCalcGalCoord::slotEpochCheckedBatch() { epochCheckBatch->setChecked(false); if (epochCheckBatch->isChecked()) { epochBoxBatch->setEnabled(false); galCheck(); } else { epochBoxBatch->setEnabled(true); } } void modCalcGalCoord::slotGalLatCheckedBatch() { if (galLatCheckBatch->isChecked()) { galLatBoxBatch->setEnabled(false); equCheck(); } else { galLatBoxBatch->setEnabled(true); } } void modCalcGalCoord::slotGalLongCheckedBatch() { if (galLongCheckBatch->isChecked()) { galLongBoxBatch->setEnabled(false); equCheck(); } else { galLongBoxBatch->setEnabled(true); } } void modCalcGalCoord::slotRunBatch() { const QString inputFileName = InputFileBoxBatch->url().toLocalFile(); // We open the input file and read its content if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } // processLines(&f); QTextStream istream(&f); processLines(istream); // readFile( istream ); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); InputFileBoxBatch->setUrl(QUrl()); } } void modCalcGalCoord::processLines(QTextStream &istream) { // we open the output file // QTextStream istream(&fIn); const QString outputFileName = OutputFileBoxBatch->url().toLocalFile(); QFile fOut(outputFileName); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); QString line; QChar space = ' '; int i = 0; SkyPoint sp; dms raB, decB, galLatB, galLongB; QString epoch0B; while (!istream.atEnd()) { line = istream.readLine(); line = line.trimmed(); //Go through the line, looking for parameters QStringList fields = line.split(' '); i = 0; // Input coords are galactic coordinates: if (galInputCoords) { // Read Galactic Longitude and write in ostream if corresponds if (galLongCheckBatch->isChecked()) { galLongB = dms::fromString(fields[i], true); i++; } else galLongB = galLongBoxBatch->createDms(true); if (allRadioBatch->isChecked()) ostream << galLongB.toDMSString() << space; else if (galLongCheckBatch->isChecked()) ostream << galLongB.toDMSString() << space; // Read Galactic Latitude and write in ostream if corresponds if (galLatCheckBatch->isChecked()) { galLatB = dms::fromString(fields[i], true); i++; } else galLatB = galLatBoxBatch->createDms(true); if (allRadioBatch->isChecked()) ostream << galLatB.toDMSString() << space; else if (galLatCheckBatch->isChecked()) ostream << galLatB.toDMSString() << space; sp = SkyPoint(); sp.GalacticToEquatorial1950(&galLongB, &galLatB); ostream << sp.ra().toHMSString() << space << sp.dec().toDMSString() << epoch0B << endl; // Input coords. are equatorial coordinates: } else { // Read RA and write in ostream if corresponds if (raCheckBatch->isChecked()) { raB = dms::fromString(fields[i], false); i++; } else raB = raBoxBatch->createDms(false); if (allRadioBatch->isChecked()) ostream << raB.toHMSString() << space; else if (raCheckBatch->isChecked()) ostream << raB.toHMSString() << space; // Read DEC and write in ostream if corresponds if (decCheckBatch->isChecked()) { decB = dms::fromString(fields[i], true); i++; } else decB = decBoxBatch->createDms(); if (allRadioBatch->isChecked()) ostream << decB.toDMSString() << space; else if (decCheckBatch->isChecked()) ostream << decB.toDMSString() << space; // Read Epoch and write in ostream if corresponds if (epochCheckBatch->isChecked()) { epoch0B = fields[i]; i++; } else epoch0B = epochBoxBatch->text(); if (allRadioBatch->isChecked()) ostream << epoch0B << space; else if (epochCheckBatch->isChecked()) ostream << epoch0B << space; sp = SkyPoint(raB, decB); sp.J2000ToB1950(); sp.Equatorial1950ToGalactic(galLongB, galLatB); ostream << galLongB.toDMSString() << space << galLatB.toDMSString() << endl; } } fOut.close(); } diff --git a/kstars/tools/modcalcgalcoord.h b/kstars/tools/modcalcgalcoord.h index eb4f68d9d..5c37f4a66 100644 --- a/kstars/tools/modcalcgalcoord.h +++ b/kstars/tools/modcalcgalcoord.h @@ -1,58 +1,58 @@ /*************************************************************************** modcalcgal.h - description ------------------- begin : Thu Jan 17 2002 copyright : (C) 2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "dms.h" #include "ui_modcalcgalcoord.h" /** * Class which implements the KStars calculator module to compute * Galactic coordinates to/from Equatorial coordinates. * * @author Pablo de Vicente * @version 0.9 */ class modCalcGalCoord : public QFrame, public Ui::modCalcGalCoordDlg { Q_OBJECT public: explicit modCalcGalCoord(QWidget *p); - ~modCalcGalCoord() override; + virtual ~modCalcGalCoord() override = default; public slots: void slotComputeCoords(); void slotObject(); void slotGalLatCheckedBatch(); void slotGalLongCheckedBatch(); void slotRaCheckedBatch(); void slotDecCheckedBatch(); void slotEpochCheckedBatch(); void slotRunBatch(); private: void equCheck(); void galCheck(); void processLines(QTextStream &is); dms galLong, galLat, raCoord, decCoord; QString epoch; bool galInputCoords { false }; }; diff --git a/kstars/tools/modcalcjd.cpp b/kstars/tools/modcalcjd.cpp index 7e8f640e7..2f979e91d 100644 --- a/kstars/tools/modcalcjd.cpp +++ b/kstars/tools/modcalcjd.cpp @@ -1,257 +1,253 @@ /*************************************************************************** modcalcjd.cpp - description ------------------- begin : Tue Jan 15 2002 copyright : (C) 2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcjd.h" -#include -#include -#include +#include "kstarsdatetime.h" #include #include #include -#include "kstarsdatetime.h" +#include +#include +#include #define MJD0 2400000.5 modCalcJD::modCalcJD(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); // signals and slots connections connect(NowButton, SIGNAL(clicked()), this, SLOT(showCurrentTime())); connect(DateTimeBox, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(slotUpdateCalendar())); connect(JDBox, SIGNAL(editingFinished()), this, SLOT(slotUpdateJD())); connect(ModJDBox, SIGNAL(editingFinished()), this, SLOT(slotUpdateModJD())); connect(InputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); connect(OutputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); connect(RunButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); connect(ViewButtonBatch, SIGNAL(clicked()), this, SLOT(slotViewBatch())); RunButtonBatch->setEnabled(false); ViewButtonBatch->setEnabled(false); showCurrentTime(); slotUpdateCalendar(); show(); } -modCalcJD::~modCalcJD(void) -{ -} - void modCalcJD::slotUpdateCalendar() { long double julianDay, modjulianDay; julianDay = KStarsDateTime(DateTimeBox->dateTime()).djd(); showJd(julianDay); modjulianDay = julianDay - MJD0; showMjd(modjulianDay); } void modCalcJD::slotUpdateModJD() { long double julianDay, modjulianDay; modjulianDay = ModJDBox->text().toDouble(); julianDay = MJD0 + modjulianDay; showJd(julianDay); DateTimeBox->setDateTime(KStarsDateTime(julianDay)); } void modCalcJD::slotUpdateJD() { long double julianDay, modjulianDay; julianDay = JDBox->text().toDouble(); KStarsDateTime dt(julianDay); DateTimeBox->setDateTime(dt); modjulianDay = julianDay - MJD0; showMjd(modjulianDay); } void modCalcJD::showCurrentTime(void) { DateTimeBox->setDateTime(KStarsDateTime::currentDateTime()); } void modCalcJD::showJd(long double julianDay) { JDBox->setText(QLocale().toString((double)julianDay, 5)); } void modCalcJD::showMjd(long double modjulianDay) { ModJDBox->setText(QLocale().toString((double)modjulianDay, 5)); } void modCalcJD::slotCheckFiles() { if (!InputFileBatch->lineEdit()->text().isEmpty() && !OutputFileBatch->lineEdit()->text().isEmpty()) { RunButtonBatch->setEnabled(true); } else { RunButtonBatch->setEnabled(false); } } void modCalcJD::slotRunBatch() { QString inputFileName = InputFileBatch->url().toLocalFile(); if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } QTextStream istream(&f); processLines(istream, InputComboBatch->currentIndex()); ViewButtonBatch->setEnabled(true); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); return; } } void modCalcJD::processLines(QTextStream &istream, int inputData) { QFile fOut(OutputFileBatch->url().toLocalFile()); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); QString line; long double jd(0); double mjd(0); KStarsDateTime dt; while (!istream.atEnd()) { line = istream.readLine(); line = line.trimmed(); QStringList data = line.split(' ', QString::SkipEmptyParts); if (inputData == 0) //Parse date & time { //Is the first field parseable as a date or date&time? if (data[0].length() > 10) dt = KStarsDateTime::fromString(data[0]); else dt = KStarsDateTime(QDate::fromString(data[0]), QTime(0, 0, 0)); //DEBUG qDebug() << data[0]; if (dt.isValid()) qDebug() << dt.toString(); if (dt.isValid()) { //Try to parse the second field as a time if (data.size() > 1) { QString s = data[1]; if (s.length() == 4) s = '0' + s; QTime t = QTime::fromString(s); if (t.isValid()) dt.setTime(t); } } else //Did not parse the first field as a date; try it as a time { QTime t = QTime::fromString(data[0]); if (t.isValid()) { dt.setTime(t); //Now try the second field as a date if (data.size() > 1) { QDate d = QDate::fromString(data[1]); if (d.isValid()) dt.setDate(d); else dt.setDate(QDate::currentDate()); } } } if (dt.isValid()) { //Compute JD and MJD jd = dt.djd(); mjd = jd - MJD0; } } else if (inputData == 1) //Parse Julian day { bool ok(false); jd = data[0].toDouble(&ok); if (ok) { dt.setDJD(jd); mjd = jd - MJD0; } } else if (inputData == 2) //Parse Modified Julian day { bool ok(false); mjd = data[0].toDouble(&ok); if (ok) { jd = mjd + MJD0; dt.setDJD(jd); } } //Write to output file ostream << QLocale().toString(dt, QLocale::LongFormat) << " " << QString::number(jd, 'f', 2) << " " << QString::number(mjd, 'f', 2) << endl; } fOut.close(); } void modCalcJD::slotViewBatch() { QFile fOut(OutputFileBatch->url().toLocalFile()); fOut.open(QIODevice::ReadOnly); QTextStream istream(&fOut); QStringList text; while (!istream.atEnd()) text.append(istream.readLine()); fOut.close(); KMessageBox::informationList(nullptr, i18n("Results of Julian day calculation"), text, OutputFileBatch->url().toLocalFile()); } diff --git a/kstars/tools/modcalcjd.h b/kstars/tools/modcalcjd.h index c1932e31a..dade34781 100644 --- a/kstars/tools/modcalcjd.h +++ b/kstars/tools/modcalcjd.h @@ -1,59 +1,56 @@ /*************************************************************************** modcalcjd.h - description ------------------- begin : Tue Jan 15 2002 copyright : (C) 2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 MODCALCJD_H_ -#define MODCALCJD_H_ +#pragma once #include "ui_modcalcjd.h" -class QWidget; -class VBox; class QTextStream; +class QWidget; /** * Class for KStars module which computes JD, MJD and Date/Time from the * any of the other entries. * * Inherits QVBox - *@author Pablo de Vicente - *@version 0.9 + * + * @author Pablo de Vicente + * @version 0.9 */ class modCalcJD : public QFrame, public Ui::modCalcJdDlg { Q_OBJECT public: explicit modCalcJD(QWidget *p); - ~modCalcJD() override; + virtual ~modCalcJD() override = default; public slots: void slotUpdateCalendar(); void slotUpdateModJD(); void slotUpdateJD(); void showCurrentTime(void); void slotRunBatch(); void slotViewBatch(); void slotCheckFiles(); private: void processLines(QTextStream &istream, int inputData); /** Shows Julian Day in the Box */ void showJd(long double jd); /** Shows the modified Julian Day in the Box */ void showMjd(long double mjd); }; - -#endif diff --git a/kstars/tools/modcalcplanets.cpp b/kstars/tools/modcalcplanets.cpp index 1d0cf9883..23a48f238 100644 --- a/kstars/tools/modcalcplanets.cpp +++ b/kstars/tools/modcalcplanets.cpp @@ -1,457 +1,453 @@ /*************************************************************************** modcalcequinox.cpp - description ------------------- begin : dom may 2 2004 copyright : (C) 2004-2005 by Pablo de Vicente email : p.devicentea@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcplanets.h" #include "geolocation.h" #include "kstarsdata.h" #include "dialogs/locationdialog.h" #include "skyobjects/ksmoon.h" #include "skyobjects/kssun.h" modCalcPlanets::modCalcPlanets(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); KStarsDateTime dt(KStarsDateTime::currentDateTime()); DateTimeBox->setDateTime(dt); DateBoxBatch->setDate(dt.date()); UTBoxBatch->setTime(dt.time()); geoPlace = KStarsData::Instance()->geo(); LocationButton->setText(geoPlace->fullName()); RABox->setDegType(false); // signals and slots connections connect(PlanetComboBox, SIGNAL(activated(int)), this, SLOT(slotComputePosition())); connect(DateTimeBox, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(slotComputePosition())); connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(UTCheckBatch, SIGNAL(clicked()), this, SLOT(slotUtCheckedBatch())); connect(DateCheckBatch, SIGNAL(clicked()), this, SLOT(slotDateCheckedBatch())); connect(LatCheckBatch, SIGNAL(clicked()), this, SLOT(slotLatCheckedBatch())); connect(LongCheckBatch, SIGNAL(clicked()), this, SLOT(slotLongCheckedBatch())); connect(PlanetCheckBatch, SIGNAL(clicked()), this, SLOT(slotPlanetsCheckedBatch())); slotComputePosition(); show(); } -modCalcPlanets::~modCalcPlanets() -{ -} - void modCalcPlanets::slotLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { geoPlace = ld->selectedCity(); LocationButton->setText(geoPlace->fullName()); slotComputePosition(); } delete ld; } void modCalcPlanets::slotComputePosition() { KStarsDateTime dt(DateTimeBox->dateTime()); long double julianDay = dt.djd(); KSNumbers num(julianDay); CachingDms LST(geoPlace->GSTtoLST(dt.gst())); // Earth KSPlanet Earth(I18N_NOOP("Earth")); Earth.findPosition(&num); // Earth is special case! if (PlanetComboBox->currentIndex() == 2) { showCoordinates(Earth); return; } // Pointer to hold planet data. Pointer is used since it has to // hold objects of different type. It's safe to use new/delete // because exceptions are disallowed. std::unique_ptr p; switch (PlanetComboBox->currentIndex()) { case 0: p.reset(new KSPlanet(KSPlanetBase::MERCURY)); break; case 1: p.reset(new KSPlanet(KSPlanetBase::VENUS)); break; case 3: p.reset(new KSPlanet(KSPlanetBase::MARS)); break; case 4: p.reset(new KSPlanet(KSPlanetBase::JUPITER)); break; case 5: p.reset(new KSPlanet(KSPlanetBase::SATURN)); break; case 6: p.reset(new KSPlanet(KSPlanetBase::URANUS)); break; case 7: p.reset(new KSPlanet(KSPlanetBase::NEPTUNE)); break; /*case 8: p.reset(new KSPluto(); break;*/ case 8: p.reset(new KSMoon()); break; case 9: p.reset(new KSSun()); p->setRsun(0.0); break; } if (p.get() == nullptr) return; // Show data. p->findPosition(&num, geoPlace->lat(), &LST, &Earth); p->EquatorialToHorizontal(&LST, geoPlace->lat()); showCoordinates(*p); } void modCalcPlanets::showCoordinates(const KSPlanetBase &ksp) { showHeliocentricEclipticCoords(ksp.helEcLong(), ksp.helEcLat(), ksp.rsun()); showGeocentricEclipticCoords(ksp.ecLong(), ksp.ecLat(), ksp.rearth()); showEquatorialCoords(ksp.ra(), ksp.dec()); showTopocentricCoords(ksp.az(), ksp.alt()); } void modCalcPlanets::showHeliocentricEclipticCoords(const dms &hLong, const dms &hLat, double dist) { HelioLongBox->show(hLong); HelioLatBox->show(hLat); HelioDistBox->setText(QLocale().toString(dist, 6)); } void modCalcPlanets::showGeocentricEclipticCoords(const dms &eLong, const dms &eLat, double dist) { GeoLongBox->show(eLong); GeoLatBox->show(eLat); GeoDistBox->setText(QLocale().toString(dist, 6)); } void modCalcPlanets::showEquatorialCoords(const dms &ra, const dms &dec) { RABox->show(ra, false); DecBox->show(dec); } void modCalcPlanets::showTopocentricCoords(const dms &az, const dms &el) { AzBox->show(az); AltBox->show(el); } void modCalcPlanets::slotPlanetsCheckedBatch() { PlanetComboBoxBatch->setEnabled(!PlanetCheckBatch->isChecked()); } void modCalcPlanets::slotUtCheckedBatch() { UTBoxBatch->setEnabled(!UTCheckBatch->isChecked()); } void modCalcPlanets::slotDateCheckedBatch() { DateBoxBatch->setEnabled(!DateCheckBatch->isChecked()); } void modCalcPlanets::slotLongCheckedBatch() { LongBoxBatch->setEnabled(!LongCheckBatch->isChecked()); } void modCalcPlanets::slotLatCheckedBatch() { LatBoxBatch->setEnabled(!LatCheckBatch->isChecked()); } void modCalcPlanets::slotRunBatch() { const QString inputFileName = InputFileBoxBatch->url().toLocalFile(); // We open the input file and read its content if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } QTextStream istream(&f); processLines(istream); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); InputFileBoxBatch->setUrl(QUrl()); } } unsigned int modCalcPlanets::requiredBatchFields() { unsigned int i = 0; if (PlanetCheckBatch->isChecked()) i++; if (UTCheckBatch->isChecked()) i++; if (DateCheckBatch->isChecked()) i++; if (LongCheckBatch->isChecked()) i++; if (LatCheckBatch->isChecked()) i++; return i; } void modCalcPlanets::processLines(QTextStream &istream) { // we open the output file const QString outputFileName = OutputFileBoxBatch->url().toLocalFile(); QFile fOut(outputFileName); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); bool lineIsValid = true; QChar space = ' '; QString planetB; unsigned int i = 0, nline = 0; QTime utB; QDate dtB; CachingDms longB, latB, hlongB, hlatB, glongB, glatB, raB, decB, azmB, altB; double rSunB(0.0), rEarthB(0.0); //Initialize planet names QString pn; QStringList pNames, pNamesi18n; pNames << "Mercury" << "Venus" << "Earth" << "Mars" << "Jupiter" << "Saturn" << "Uranus" << "Neptune" /* << "Pluto" */ << "Sun" << "Moon"; pNamesi18n << i18n("Mercury") << i18n("Venus") << i18n("Earth") << i18n("Mars") << i18n("Jupiter") << i18n("Saturn") << i18n("Uranus") << i18n("Neptune") /* << i18nc("Asteroid name (optional)", "Pluto") */ << i18n("Sun") << i18n("Moon"); ///Parse the input file int numberOfRequiredFields = requiredBatchFields(); while (!istream.atEnd()) { QString lineToWrite; QString line = istream.readLine(); line = line.trimmed(); //Go through the line, looking for parameters QStringList fields = line.split(' '); if (fields.count() != numberOfRequiredFields) { lineIsValid = false; qWarning() << i18n("Incorrect number of fields in line %1: ", nline) << i18n("Present fields %1. ", fields.count()) << i18n("Required fields %1. ", numberOfRequiredFields) << endl; nline++; continue; } i = 0; if (PlanetCheckBatch->isChecked()) { planetB = fields[i]; int j = pNamesi18n.indexOf(planetB); if (j == -1) { qWarning() << i18n("Unknown planet ") << fields[i] << i18n(" in line %1: ", nline) << endl; continue; } pn = pNames.at(j); //untranslated planet name i++; } else { planetB = PlanetComboBoxBatch->currentText(); } if (AllRadioBatch->isChecked() || PlanetCheckBatch->isChecked()) { lineToWrite = planetB; lineToWrite += space; } // Read Ut and write in ostream if corresponds if (UTCheckBatch->isChecked()) { utB = QTime::fromString(fields[i]); if (!utB.isValid()) { qWarning() << i18n("Line %1 contains an invalid time", nline); lineIsValid = false; nline++; continue; } i++; } else { utB = UTBoxBatch->time(); } if (AllRadioBatch->isChecked() || UTCheckBatch->isChecked()) lineToWrite += QLocale().toString(utB).append(space); // Read date and write in ostream if corresponds if (DateCheckBatch->isChecked()) { dtB = QDate::fromString(fields[i], Qt::ISODate); if (!dtB.isValid()) { qWarning() << i18n("Line %1 contains an invalid date: ", nline) << fields[i] << endl; lineIsValid = false; nline++; continue; } i++; } else { dtB = DateBoxBatch->date(); } if (AllRadioBatch->isChecked() || DateCheckBatch->isChecked()) lineToWrite += QLocale().toString(dtB, QLocale::LongFormat).append(space); // Read Longitude and write in ostream if corresponds if (LongCheckBatch->isChecked()) { longB = CachingDms::fromString(fields[i], true); i++; } else { longB = LongBoxBatch->createDms(true); } if (AllRadioBatch->isChecked() || LongCheckBatch->isChecked()) lineToWrite += longB.toDMSString() + space; // Read Latitude if (LatCheckBatch->isChecked()) { latB = CachingDms::fromString(fields[i], true); i++; } else { latB = LatBoxBatch->createDms(true); } if (AllRadioBatch->isChecked() || LatCheckBatch->isChecked()) lineToWrite += latB.toDMSString() + space; KStarsDateTime edt(dtB, utB); CachingDms LST = edt.gst() + longB; KSNumbers num(edt.djd()); KSPlanet Earth(I18N_NOOP("Earth")); Earth.findPosition(&num); // FIXME: allocate new object for every iteration is probably not wisest idea. KSPlanetBase *kspb = nullptr; /*if ( pn == "Pluto" ) { kspb = new KSPluto(); } else*/ if (pn == "Sun") { kspb = new KSSun(); } else if (pn == "Moon") { kspb = new KSMoon(); } else { kspb = new KSPlanet(i18n(pn.toLocal8Bit()), QString(), Qt::white, 1.0); } kspb->findPosition(&num, &latB, &LST, &Earth); kspb->EquatorialToHorizontal(&LST, &latB); // Heliocentric Ecl. coords. hlongB = kspb->helEcLong(); hlatB = kspb->helEcLat(); rSunB = kspb->rsun(); // Geocentric Ecl. coords. glongB = kspb->ecLong(); glatB = kspb->ecLat(); rEarthB = kspb->rearth(); // Equatorial coords. decB = kspb->dec(); raB = kspb->ra(); // Topocentric Coords. azmB = kspb->az(); altB = kspb->alt(); ostream << lineToWrite; if (HelioEclCheckBatch->isChecked()) ostream << hlongB.toDMSString() << space << hlatB.toDMSString() << space << rSunB << space; if (GeoEclCheckBatch->isChecked()) ostream << glongB.toDMSString() << space << glatB.toDMSString() << space << rEarthB << space; if (EquatorialCheckBatch->isChecked()) ostream << raB.toHMSString() << space << decB.toDMSString() << space; if (HorizontalCheckBatch->isChecked()) ostream << azmB.toDMSString() << space << altB.toDMSString() << space; ostream << endl; // Delete object delete kspb; nline++; } if (!lineIsValid) { QString message = i18n("Errors found while parsing some lines in the input file"); KMessageBox::sorry(nullptr, message, i18n("Errors in lines")); } fOut.close(); } diff --git a/kstars/tools/modcalcplanets.h b/kstars/tools/modcalcplanets.h index 9cbc65217..226f1fb6e 100644 --- a/kstars/tools/modcalcplanets.h +++ b/kstars/tools/modcalcplanets.h @@ -1,64 +1,64 @@ /*************************************************************************** modcalcazel.h - description ------------------- begin : mier abr 20 2004 copyright : (C) 2004-2005 by Pablo de Vicente email : pvicentea@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_modcalcplanets.h" class QTextStream; class GeoLocation; class KSPlanetBase; /** *@author Pablo de Vicente */ class modCalcPlanets : public QFrame, public Ui::modCalcPlanetsDlg { Q_OBJECT public: explicit modCalcPlanets(QWidget *p); - ~modCalcPlanets() override; + virtual ~modCalcPlanets() override = default; public slots: void slotLocation(); void slotComputePosition(); void slotUtCheckedBatch(); void slotDateCheckedBatch(); void slotLongCheckedBatch(); void slotLatCheckedBatch(); void slotPlanetsCheckedBatch(); void slotRunBatch(); void processLines(QTextStream &istream); //void slotInputFile(); //void slotOutputFile(); //void slotRunBatch(); private: void showCoordinates(const KSPlanetBase &ksp); void showHeliocentricEclipticCoords(const dms &hLong, const dms &hLat, double dist); void showGeocentricEclipticCoords(const dms &eLong, const dms &eLat, double r); void showEquatorialCoords(const dms &ra, const dms &dec); void showTopocentricCoords(const dms &az, const dms &el); unsigned int requiredBatchFields(); // void processLines( QTextStream &istream ); GeoLocation *geoPlace; }; diff --git a/kstars/tools/modcalcvizequinox.cpp b/kstars/tools/modcalcvizequinox.cpp index ae61dd6f7..6cd7ca570 100644 --- a/kstars/tools/modcalcvizequinox.cpp +++ b/kstars/tools/modcalcvizequinox.cpp @@ -1,383 +1,379 @@ /*************************************************************************** modcalcvizequinox.cpp - description ------------------- begin : Thu 22 Feb 2007 copyright : (C) 2007 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcvizequinox.h" #include "dms.h" #include "kstarsdata.h" #include "skyobjects/kssun.h" #include "widgets/dmsbox.h" #include #include #include #include #include modCalcEquinox::modCalcEquinox(QWidget *parentSplit) : QFrame(parentSplit), dSpring(), dSummer(), dAutumn(), dWinter() { setupUi(this); connect(Year, SIGNAL(valueChanged(int)), this, SLOT(slotCompute())); connect(InputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); connect(OutputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles())); connect(RunButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); connect(ViewButtonBatch, SIGNAL(clicked()), this, SLOT(slotViewBatch())); Plot->axis(KPlotWidget::LeftAxis)->setLabel(i18n("Sun's Declination")); Plot->setTopPadding(40); //Don't draw Top & Bottom axes; custom axes drawn as plot objects Plot->axis(KPlotWidget::BottomAxis)->setVisible(false); Plot->axis(KPlotWidget::TopAxis)->setVisible(false); //This will call slotCompute(): Year->setValue(KStarsData::Instance()->lt().date().year()); RunButtonBatch->setEnabled(false); ViewButtonBatch->setEnabled(false); show(); } -modCalcEquinox::~modCalcEquinox() -{ -} - double modCalcEquinox::dmonth(int i) { Q_ASSERT(i >= 0 && i < 12 && "Month must be in 0 .. 11 range"); return DMonth[i]; } void modCalcEquinox::slotCheckFiles() { RunButtonBatch->setEnabled(!InputFileBatch->lineEdit()->text().isEmpty() && !OutputFileBatch->lineEdit()->text().isEmpty()); } void modCalcEquinox::slotRunBatch() { QString inputFileName = InputFileBatch->url().toLocalFile(); if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); inputFileName.clear(); return; } QTextStream istream(&f); processLines(istream); ViewButtonBatch->setEnabled(true); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); inputFileName.clear(); return; } } void modCalcEquinox::processLines(QTextStream &istream) { QFile fOut(OutputFileBatch->url().toLocalFile()); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); int originalYear = Year->value(); //Write header to output file ostream << i18n("# Timing of Equinoxes and Solstices\n") << i18n("# computed by KStars\n#\n") << i18n("# Vernal Equinox\t\tSummer Solstice\t\t\tAutumnal Equinox\t\tWinter Solstice\n#\n"); while (!istream.atEnd()) { QString line = istream.readLine(); bool ok = false; int year = line.toInt(&ok); //for now I will simply change the value of the Year widget to trigger //computation of the Equinoxes and Solstices. if (ok) { //triggers slotCompute(), which sets values of dSpring et al.: Year->setValue(year); //Write to output file ostream << QLocale().toString(dSpring.date(), QLocale::LongFormat) << "\t" << QLocale().toString(dSummer.date(), QLocale::LongFormat) << "\t" << QLocale().toString(dAutumn.date(), QLocale::LongFormat) << "\t" << QLocale().toString(dWinter.date(), QLocale::LongFormat) << endl; } } if (Year->value() != originalYear) Year->setValue(originalYear); } void modCalcEquinox::slotViewBatch() { QFile fOut(OutputFileBatch->url().toLocalFile()); fOut.open(QIODevice::ReadOnly); QTextStream istream(&fOut); QStringList text; while (!istream.atEnd()) text.append(istream.readLine()); fOut.close(); KMessageBox::informationList(nullptr, i18n("Results of Sidereal time calculation"), text, OutputFileBatch->url().toLocalFile()); } void modCalcEquinox::slotCompute() { KStarsData *data = KStarsData::Instance(); KSSun Sun; int year0 = Year->value(); KStarsDateTime dt(QDate(year0, 1, 1), QTime(0, 0, 0)); long double jd0 = dt.djd(); //save JD on Jan 1st for (int imonth = 0; imonth < 12; imonth++) { KStarsDateTime kdt(QDate(year0, imonth + 1, 1), QTime(0, 0, 0)); DMonth[imonth] = kdt.djd() - jd0; } Plot->removeAllPlotObjects(); //Add the celestial equator, just a single line bisecting the plot horizontally KPlotObject *ce = new KPlotObject(data->colorScheme()->colorNamed("EqColor"), KPlotObject::Lines, 2.0); ce->addPoint(0.0, 0.0); ce->addPoint(366.0, 0.0); Plot->addPlotObject(ce); // Tropic of cancer KPlotObject *tcan = new KPlotObject(data->colorScheme()->colorNamed("EqColor"), KPlotObject::Lines, 2.0); tcan->addPoint(0.0, 23.5); tcan->addPoint(366.0, 23.5); Plot->addPlotObject(tcan); // Tropic of capricorn KPlotObject *tcap = new KPlotObject(data->colorScheme()->colorNamed("EqColor"), KPlotObject::Lines, 2.0); tcap->addPoint(0.0, -23.5); tcap->addPoint(366.0, -23.5); Plot->addPlotObject(tcap); //Add Ecliptic. This is more complicated than simply incrementing the //ecliptic longitude, because we want the x-axis to be time, not RA. //For each day in the year, compute the Sun's position. KPlotObject *ecl = new KPlotObject(data->colorScheme()->colorNamed("EclColor"), KPlotObject::Lines, 2); ecl->setLinePen(QPen(ecl->pen().color(), 4)); Plot->setLimits(1.0, double(dt.date().daysInYear()), -30.0, 30.0); //Add top and bottom axis lines, and custom tickmarks at each month addDateAxes(); for (int i = 1; i <= dt.date().daysInYear(); i++) { KSNumbers num(dt.djd()); Sun.findPosition(&num); ecl->addPoint(double(i), Sun.dec().Degrees()); dt = dt.addDays(1); } Plot->addPlotObject(ecl); dSpring = findEquinox(Year->value(), true, ecl); dSummer = findSolstice(Year->value(), true); dAutumn = findEquinox(Year->value(), false, ecl); dWinter = findSolstice(Year->value(), false); //Display the Date/Time of each event in the text fields VEquinox->setText(QLocale().toString(dSpring, QLocale::LongFormat)); SSolstice->setText(QLocale().toString(dSummer, QLocale::LongFormat)); AEquinox->setText(QLocale().toString(dAutumn, QLocale::LongFormat)); WSolstice->setText(QLocale().toString(dWinter, QLocale::LongFormat)); //Add vertical dotted lines at times of the equinoxes and solstices KPlotObject *poSpring = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poSpring->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); poSpring->addPoint(dSpring.djd() - jd0, Plot->dataRect().top()); poSpring->addPoint(dSpring.djd() - jd0, Plot->dataRect().bottom()); Plot->addPlotObject(poSpring); KPlotObject *poSummer = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poSummer->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); poSummer->addPoint(dSummer.djd() - jd0, Plot->dataRect().top()); poSummer->addPoint(dSummer.djd() - jd0, Plot->dataRect().bottom()); Plot->addPlotObject(poSummer); KPlotObject *poAutumn = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poAutumn->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); poAutumn->addPoint(dAutumn.djd() - jd0, Plot->dataRect().top()); poAutumn->addPoint(dAutumn.djd() - jd0, Plot->dataRect().bottom()); Plot->addPlotObject(poAutumn); KPlotObject *poWinter = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poWinter->setLinePen(QPen(Qt::white, 1.0, Qt::DotLine)); poWinter->addPoint(dWinter.djd() - jd0, Plot->dataRect().top()); poWinter->addPoint(dWinter.djd() - jd0, Plot->dataRect().bottom()); Plot->addPlotObject(poWinter); } //Add custom top/bottom axes with tickmarks for each month void modCalcEquinox::addDateAxes() { KPlotObject *poTopAxis = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poTopAxis->addPoint(0.0, Plot->dataRect().bottom()); //y-axis is reversed! poTopAxis->addPoint(366.0, Plot->dataRect().bottom()); Plot->addPlotObject(poTopAxis); KPlotObject *poBottomAxis = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poBottomAxis->addPoint(0.0, Plot->dataRect().top() + 0.02); poBottomAxis->addPoint(366.0, Plot->dataRect().top() + 0.02); Plot->addPlotObject(poBottomAxis); //Tick mark for each month for (int imonth = 0; imonth < 12; imonth++) { KPlotObject *poMonth = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poMonth->addPoint(dmonth(imonth), Plot->dataRect().top()); poMonth->addPoint(dmonth(imonth), Plot->dataRect().top() + 1.4); Plot->addPlotObject(poMonth); poMonth = new KPlotObject(Qt::white, KPlotObject::Lines, 1); poMonth->addPoint(dmonth(imonth), Plot->dataRect().bottom()); poMonth->addPoint(dmonth(imonth), Plot->dataRect().bottom() - 1.4); Plot->addPlotObject(poMonth); } } KStarsDateTime modCalcEquinox::findEquinox(int year, bool Spring, KPlotObject *ecl) { // Interpolate to find the moment when the Sun crosses the equator // Set initial guess in February or August to be sure that this // point is before equinox. const int month = Spring ? 2 : 8; int i = QDate(year, month, 1).dayOfYear(); double dec1, dec2; dec2 = ecl->points().at(i)->y(); do { ++i; dec1 = dec2; dec2 = ecl->points().at(i)->y(); } while (dec1 * dec2 > 0.0); //when dec1*dec2<0.0, we bracket the zero double x1 = ecl->points().at(i-1)->x(); double x2 = ecl->points().at(i)->x(); double d = fabs(dec2 - dec1); double f = 1.0 - fabs(dec2) / d; //fractional distance of the zero, from point1 to point2 KStarsDateTime dt0(QDate(year, 1, 1), QTime(0, 0, 0)); KStarsDateTime dt = dt0.addSecs(86400.0 * (x1 - 1 + f * (x2 - x1))); return dt; } KStarsDateTime modCalcEquinox::findSolstice(int year, bool Summer) { //Find the moment when the Sun reaches maximum declination //First find three points which bracket the maximum (i.e., x2 > x1,x3) //Start at June 16th, which will always be approaching the solstice long double jd1, jd2, jd3, jd4; double y2(0.0), y3(0.0), y4(0.0); int month = 6; if (!Summer) month = 12; jd3 = KStarsDateTime(QDate(year, month, 16), QTime(0, 0, 0)).djd(); KSNumbers num(jd3); KSSun Sun; Sun.findPosition(&num); y3 = Sun.dec().Degrees(); int sgn = 1; if (!Summer) sgn = -1; //find minimum if the winter solstice is sought do { jd3 += 1.0; num.updateValues(jd3); Sun.findPosition(&num); y2 = y3; Sun.findPosition(&num); y3 = Sun.dec().Degrees(); } while (y3 * sgn > y2 * sgn); //Ok, now y2 is larger(smaller) than both y3 and y1. // Never read back // jd2 = jd3 - 1.0; jd1 = jd3 - 2.0; //Choose a new starting jd2 that follows the golden ratio: // a/b = 1.618; a+b = 2...a = 0.76394 jd2 = jd1 + 0.76394; num.updateValues(jd2); Sun.findPosition(&num); y2 = Sun.dec().Degrees(); while (jd3 - jd1 > 0.0005) //sub-minute pecision { jd4 = jd1 + jd3 - jd2; num.updateValues(jd4); Sun.findPosition(&num); y4 = Sun.dec().Degrees(); if (y4 * sgn > y2 * sgn) //make jd4 the new center { if (jd4 > jd2) { jd1 = jd2; jd2 = jd4; y2 = y4; } else { jd3 = jd2; // Never read back // y3 = y2; jd2 = jd4; y2 = y4; } } else //make jd4 a new endpoint { if (jd4 > jd2) { jd3 = jd4; // Never read back // y3 = y4; } else { jd1 = jd4; } } } return KStarsDateTime(jd2); } diff --git a/kstars/tools/modcalcvizequinox.h b/kstars/tools/modcalcvizequinox.h index ca598b4bb..3b8abc297 100644 --- a/kstars/tools/modcalcvizequinox.h +++ b/kstars/tools/modcalcvizequinox.h @@ -1,58 +1,58 @@ /*************************************************************************** modcalcvizequinox.h - description ------------------- begin : Thu 22 Feb 2007 copyright : (C) 2007 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "kstarsdatetime.h" #include "ui_modcalcvizequinox.h" class QTextStream; class KPlotObject; /** - * @class modCalcEquinox - * - * @author Jason Harris - */ + * @class modCalcEquinox + * + * @author Jason Harris + */ class modCalcEquinox : public QFrame, public Ui::modCalcEquinox { Q_OBJECT public: explicit modCalcEquinox(QWidget *p); - ~modCalcEquinox() override; + virtual ~modCalcEquinox() override = default; double dmonth(int imonth); private slots: void slotCompute(); void slotCheckFiles(); void slotRunBatch(); void slotViewBatch(); private: void processLines(QTextStream &istream); void addDateAxes(); KStarsDateTime findEquinox(int year, bool Spring, KPlotObject *po); KStarsDateTime findSolstice(int year, bool Summer); public: KStarsDateTime dSpring, dSummer, dAutumn, dWinter; private: double DMonth[12]; }; diff --git a/kstars/tools/modcalcvlsr.cpp b/kstars/tools/modcalcvlsr.cpp index 6c3666976..d4546ad4f 100644 --- a/kstars/tools/modcalcvlsr.cpp +++ b/kstars/tools/modcalcvlsr.cpp @@ -1,514 +1,510 @@ /*************************************************************************** modcalcvlsr.cpp - description ------------------- begin : sun mar 13 2005 copyright : (C) 2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 "modcalcvlsr.h" -#include -#include - -#include -#include - -#include "ksnumbers.h" #include "dms.h" -#include "skyobjects/skypoint.h" #include "geolocation.h" +#include "ksnumbers.h" #include "kstars.h" #include "kstarsdata.h" #include "kstarsdatetime.h" -#include "widgets/dmsbox.h" #include "dialogs/locationdialog.h" #include "dialogs/finddialog.h" +#include "skyobjects/skypoint.h" +#include "widgets/dmsbox.h" + +#include +#include + +#include +#include modCalcVlsr::modCalcVlsr(QWidget *parentSplit) : QFrame(parentSplit), velocityFlag(0) { setupUi(this); RA->setDegType(false); Date->setDateTime(KStarsDateTime::currentDateTime()); initGeo(); VLSR->setValidator(new QDoubleValidator(VLSR)); VHelio->setValidator(new QDoubleValidator(VHelio)); VGeo->setValidator(new QDoubleValidator(VGeo)); VTopo->setValidator(new QDoubleValidator(VTopo)); // signals and slots connections connect(Date, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(slotCompute())); connect(NowButton, SIGNAL(clicked()), this, SLOT(slotNow())); connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(ObjectButton, SIGNAL(clicked()), this, SLOT(slotFindObject())); connect(RA, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(Dec, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(VLSR, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(VHelio, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(VGeo, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(VTopo, SIGNAL(editingFinished()), this, SLOT(slotCompute())); connect(RunButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch())); show(); } -modCalcVlsr::~modCalcVlsr() -{ -} - void modCalcVlsr::initGeo(void) { geoPlace = KStarsData::Instance()->geo(); LocationButton->setText(geoPlace->fullName()); } void modCalcVlsr::slotNow() { Date->setDateTime(KStarsDateTime::currentDateTime()); slotCompute(); } void modCalcVlsr::slotFindObject() { QPointer fd = new FindDialog(KStars::Instance()); if (fd->exec() == QDialog::Accepted) { SkyObject *o = fd->targetObject(); RA->showInHours(o->ra0()); Dec->showInDegrees(o->dec0()); } delete fd; } void modCalcVlsr::slotLocation() { QPointer ld(new LocationDialog(this)); if (ld->exec() == QDialog::Accepted && ld) { GeoLocation *newGeo = ld->selectedCity(); if (newGeo) { geoPlace = newGeo; LocationButton->setText(geoPlace->fullName()); } } delete ld; slotCompute(); } void modCalcVlsr::slotCompute() { bool ok1(false), ok2(false); SkyPoint sp(RA->createDms(false, &ok1), Dec->createDms(true, &ok2)); if (!ok1 || !ok2) return; KStarsDateTime dt(Date->dateTime()); double vst[3]; geoPlace->TopocentricVelocity(vst, dt.gst()); if (sender()->objectName() == "VLSR") velocityFlag = 0; if (sender()->objectName() == "VHelio") velocityFlag = 1; if (sender()->objectName() == "VGeo") velocityFlag = 2; if (sender()->objectName() == "VTopo") velocityFlag = 3; switch (velocityFlag) { case 0: //Hold VLSR constant, compute the others { double vlsr = VLSR->text().toDouble(); double vhelio = sp.vHeliocentric(vlsr, dt.djd()); double vgeo = sp.vGeocentric(vhelio, dt.djd()); VHelio->setText(QString::number(vhelio)); VGeo->setText(QString::number(vgeo)); VTopo->setText(QString::number(sp.vTopocentric(vgeo, vst))); break; } case 1: //Hold VHelio constant, compute the others { double vhelio = VHelio->text().toDouble(); double vlsr = sp.vHelioToVlsr(vhelio, dt.djd()); double vgeo = sp.vGeocentric(vhelio, dt.djd()); VLSR->setText(QString::number(vlsr)); VGeo->setText(QString::number(vgeo)); VTopo->setText(QString::number(sp.vTopocentric(vgeo, vst))); break; } case 2: //Hold VGeo constant, compute the others { double vgeo = VGeo->text().toDouble(); double vhelio = sp.vGeoToVHelio(vgeo, dt.djd()); double vlsr = sp.vHelioToVlsr(vhelio, dt.djd()); VLSR->setText(QString::number(vlsr)); VHelio->setText(QString::number(vhelio)); VTopo->setText(QString::number(sp.vTopocentric(vgeo, vst))); break; } case 3: //Hold VTopo constant, compute the others { double vtopo = VTopo->text().toDouble(); double vgeo = sp.vTopoToVGeo(vtopo, vst); double vhelio = sp.vGeoToVHelio(vgeo, dt.djd()); double vlsr = sp.vHelioToVlsr(vhelio, dt.djd()); VLSR->setText(QString::number(vlsr)); VHelio->setText(QString::number(vhelio)); VGeo->setText(QString::number(vgeo)); break; } default: //oops qDebug() << "Error: do not know which velocity to use for input."; break; } } void modCalcVlsr::slotUtChecked() { if (UTCheckBatch->isChecked()) UTBoxBatch->setEnabled(false); else { UTBoxBatch->setEnabled(true); } } void modCalcVlsr::slotDateChecked() { if (DateCheckBatch->isChecked()) DateBoxBatch->setEnabled(false); else { DateBoxBatch->setEnabled(true); } } void modCalcVlsr::slotRaChecked() { if (RACheckBatch->isChecked()) { RABoxBatch->setEnabled(false); } else { RABoxBatch->setEnabled(true); } } void modCalcVlsr::slotDecChecked() { if (DecCheckBatch->isChecked()) { DecBoxBatch->setEnabled(false); } else { DecBoxBatch->setEnabled(true); } } void modCalcVlsr::slotEpochChecked() { if (EpochCheckBatch->isChecked()) EpochBoxBatch->setEnabled(false); else EpochBoxBatch->setEnabled(true); } void modCalcVlsr::slotLongChecked() { if (LongCheckBatch->isChecked()) LongitudeBoxBatch->setEnabled(false); else LongitudeBoxBatch->setEnabled(true); } void modCalcVlsr::slotLatChecked() { if (LatCheckBatch->isChecked()) LatitudeBoxBatch->setEnabled(false); else { LatitudeBoxBatch->setEnabled(true); } } void modCalcVlsr::slotHeightChecked() { if (ElevationCheckBatch->isChecked()) ElevationBoxBatch->setEnabled(false); else { ElevationBoxBatch->setEnabled(true); } } void modCalcVlsr::slotVlsrChecked() { if (InputVelocityCheckBatch->isChecked()) InputVelocityBoxBatch->setEnabled(false); else { InputVelocityBoxBatch->setEnabled(true); } } void modCalcVlsr::slotInputFile() { const QString inputFileName = QFileDialog::getOpenFileName(KStars::Instance(), QString(), QString()); if (!inputFileName.isEmpty()) InputFileBoxBatch->setUrl(QUrl::fromLocalFile(inputFileName)); } void modCalcVlsr::slotOutputFile() { const QString outputFileName = QFileDialog::getSaveFileName(); if (!outputFileName.isEmpty()) OutputFileBoxBatch->setUrl(QUrl::fromLocalFile(outputFileName)); } void modCalcVlsr::slotRunBatch() { const QString inputFileName = InputFileBoxBatch->url().toLocalFile(); // We open the input file and read its content if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } // processLines(&f); QTextStream istream(&f); processLines(istream); // readFile( istream ); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); InputFileBoxBatch->setUrl(QUrl()); } } void modCalcVlsr::processLines(QTextStream &istream) { // we open the output file // QTextStream istream(&fIn); QString outputFileName; outputFileName = OutputFileBoxBatch->url().toLocalFile(); QFile fOut(outputFileName); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); QString line; QChar space = ' '; int i = 0; long double jd0; SkyPoint spB; double sra, cra, sdc, cdc; dms raB, decB, latB, longB; QString epoch0B; double vhB, vgB, vtB, vlsrB, heightB; double vtopo[3]; QTime utB; QDate dtB; KStarsDateTime dt0B; while (!istream.atEnd()) { line = istream.readLine(); line = line.trimmed(); //Go through the line, looking for parameters QStringList fields = line.split(' '); i = 0; // Read Ut and write in ostream if corresponds if (UTCheckBatch->isChecked()) { utB = QTime::fromString(fields[i]); i++; } else utB = UTBoxBatch->time(); if (AllRadioBatch->isChecked()) ostream << QLocale().toString(utB) << space; else if (UTCheckBatch->isChecked()) ostream << QLocale().toString(utB) << space; // Read date and write in ostream if corresponds if (DateCheckBatch->isChecked()) { dtB = QDate::fromString(fields[i]); i++; } else dtB = DateBoxBatch->date(); if (AllRadioBatch->isChecked()) ostream << QLocale().toString(dtB, QLocale::LongFormat).append(space); else if (DateCheckBatch->isChecked()) ostream << QLocale().toString(dtB, QLocale::LongFormat).append(space); // Read RA and write in ostream if corresponds if (RACheckBatch->isChecked()) { raB = dms::fromString(fields[i], false); i++; } else raB = RABoxBatch->createDms(false); if (AllRadioBatch->isChecked()) ostream << raB.toHMSString() << space; else if (RACheckBatch->isChecked()) ostream << raB.toHMSString() << space; // Read DEC and write in ostream if corresponds if (DecCheckBatch->isChecked()) { decB = dms::fromString(fields[i], true); i++; } else decB = DecBoxBatch->createDms(); if (AllRadioBatch->isChecked()) ostream << decB.toDMSString() << space; else if (DecCheckBatch->isChecked()) ostream << decB.toDMSString() << space; // Read Epoch and write in ostream if corresponds if (EpochCheckBatch->isChecked()) { epoch0B = fields[i]; i++; } else epoch0B = EpochBoxBatch->text(); if (AllRadioBatch->isChecked()) ostream << epoch0B << space; else if (EpochCheckBatch->isChecked()) ostream << epoch0B << space; // Read vlsr and write in ostream if corresponds if (InputVelocityCheckBatch->isChecked()) { vlsrB = fields[i].toDouble(); i++; } else vlsrB = InputVelocityComboBatch->currentText().toDouble(); if (AllRadioBatch->isChecked()) ostream << vlsrB << space; else if (InputVelocityCheckBatch->isChecked()) ostream << vlsrB << space; // Read Longitude and write in ostream if corresponds if (LongCheckBatch->isChecked()) { longB = dms::fromString(fields[i], true); i++; } else longB = LongitudeBoxBatch->createDms(true); if (AllRadioBatch->isChecked()) ostream << longB.toDMSString() << space; else if (LongCheckBatch->isChecked()) ostream << longB.toDMSString() << space; // Read Latitude if (LatCheckBatch->isChecked()) { latB = dms::fromString(fields[i], true); i++; } else latB = LatitudeBoxBatch->createDms(true); if (AllRadioBatch->isChecked()) ostream << latB.toDMSString() << space; else if (LatCheckBatch->isChecked()) ostream << latB.toDMSString() << space; // Read height and write in ostream if corresponds if (ElevationCheckBatch->isChecked()) { heightB = fields[i].toDouble(); i++; } else heightB = ElevationBoxBatch->text().toDouble(); if (AllRadioBatch->isChecked()) ostream << heightB << space; else if (ElevationCheckBatch->isChecked()) ostream << heightB << space; // We make the first calculations spB = SkyPoint(raB, decB); dt0B.setFromEpoch(epoch0B); vhB = spB.vHeliocentric(vlsrB, dt0B.djd()); jd0 = KStarsDateTime(dtB, utB).djd(); vgB = spB.vGeocentric(vlsrB, jd0); geoPlace->setLong(longB); geoPlace->setLat(latB); geoPlace->setElevation(heightB); dms gsidt = KStarsDateTime(dtB, utB).gst(); geoPlace->TopocentricVelocity(vtopo, gsidt); spB.ra().SinCos(sra, cra); spB.dec().SinCos(sdc, cdc); vtB = vgB - (vtopo[0] * cdc * cra + vtopo[1] * cdc * sra + vtopo[2] * sdc); ostream << vhB << space << vgB << space << vtB << endl; } fOut.close(); } diff --git a/kstars/tools/modcalcvlsr.h b/kstars/tools/modcalcvlsr.h index 05a23a4f1..fb7596324 100644 --- a/kstars/tools/modcalcvlsr.h +++ b/kstars/tools/modcalcvlsr.h @@ -1,81 +1,80 @@ /*************************************************************************** modcalcLSRVel.h - description ------------------- begin : dom mar 13 2005 copyright : (C) 2005 by Pablo de Vicente email : pvicentea@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 MODCALCVLSR_H_ -#define MODCALCVLSR_H_ - -#include +#pragma once #include "ui_modcalcvlsr.h" +#include + class QWidget; class SkyPoint; class GeoLocation; class dms; /** - *@author Pablo de Vicente - *Module to compute the heliocentric radial velocity, geocentric radial velocity and - *topocentric radial velocity for a source, given its coordinates, its Vlsr and the date and - *location on the Earth. - */ + * Module to compute the heliocentric radial velocity, geocentric radial velocity and + * topocentric radial velocity for a source, given its coordinates, its Vlsr and the date and + * location on the Earth. + * + * @author Pablo de Vicente + */ class modCalcVlsr : public QFrame, public Ui::modCalcVlsrDlg { Q_OBJECT public: explicit modCalcVlsr(QWidget *p); - ~modCalcVlsr() override; + virtual ~modCalcVlsr() override = default; private slots: void slotNow(); void slotLocation(); void slotFindObject(); void slotCompute(); void slotUtChecked(); void slotDateChecked(); void slotRaChecked(); void slotDecChecked(); void slotEpochChecked(); void slotLongChecked(); void slotLatChecked(); void slotHeightChecked(); void slotVlsrChecked(); void slotInputFile(); void slotOutputFile(); void slotRunBatch(); private: - /** @returns a SkyPoint constructed from the coordinates in the - * RA and Dec dmsBoxes. */ + /** + * @returns a SkyPoint constructed from the coordinates in the + * RA and Dec dmsBoxes. */ SkyPoint skypoint(); - /* Constructs the a GeoLocation object (geoPlace) from the calling classes. - * This is for using as Geolocation the location setup in KStars - * */ + /** + * Constructs the a GeoLocation object (geoPlace) from the calling classes. + * This is for using as Geolocation the location setup in KStars + */ void initGeo(void); - /* Method to process the lines from a file - */ + /** Method to process the lines from a file */ void processLines(QTextStream &istream); - GeoLocation *geoPlace; - int velocityFlag; + GeoLocation *geoPlace { nullptr }; + int velocityFlag { 0 }; }; - -#endif diff --git a/kstars/tools/observinglist.cpp b/kstars/tools/observinglist.cpp index cac6d17a7..4050c12a2 100644 --- a/kstars/tools/observinglist.cpp +++ b/kstars/tools/observinglist.cpp @@ -1,1675 +1,1669 @@ /*************************************************************************** observinglist.cpp - K Desktop Planetarium ------------------- begin : 29 Nov 2004 copyright : (C) 2004-2014 by Jeff Woods, Jason Harris, Prakash Mohan, Akarsh Simha email : jcwoods@bellsouth.net, jharris@30doradus.org, prakash.mohan@kdemail.net, akarsh@kde.org ***************************************************************************/ /*************************************************************************** * * * 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 "observinglist.h" #include "config-kstars.h" #include "constellationboundarylines.h" #include "fov.h" #include "imageviewer.h" #include "ksalmanac.h" #include "ksdssdownloader.h" #include "kspaths.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include "obslistpopupmenu.h" #include "obslistwizard.h" #include "Options.h" #include "sessionsortfilterproxymodel.h" #include "skymap.h" #include "thumbnailpicker.h" #include "dialogs/detaildialog.h" #include "dialogs/finddialog.h" #include "dialogs/locationdialog.h" #include "oal/execute.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/starobject.h" #include "tools/altvstime.h" #include "tools/eyepiecefield.h" #include "tools/wutdialog.h" #ifdef HAVE_INDI #include #include "indi/indilistener.h" #include "indi/drivermanager.h" #include "indi/driverinfo.h" #include "ekos/ekosmanager.h" #endif #include #include #include // // ObservingListUI // --------------------------------- ObservingListUI::ObservingListUI(QWidget *p) : QFrame(p) { setupUi(this); } // // ObservingList // --------------------------------- ObservingList::ObservingList() : QDialog((QWidget *)KStars::Instance()), LogObject(nullptr), m_CurrentObject(nullptr), isModified(false), m_dl(nullptr) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ui = new ObservingListUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(ui); setWindowTitle(i18n("Observation Planner")); setLayout(mainLayout); dt = KStarsDateTime::currentDateTime(); setFocusPolicy(Qt::StrongFocus); geo = KStarsData::Instance()->geo(); sessionView = false; m_listFileName = QString(); pmenu.reset(new ObsListPopupMenu()); //Set up the Table Views m_WishListModel.reset(new QStandardItemModel(0, 5, this)); m_SessionModel.reset(new QStandardItemModel(0, 5)); m_WishListModel->setHorizontalHeaderLabels( QStringList() << i18n("Name") << i18n("Alternate Name") << i18nc("Right Ascension", "RA (J2000)") << i18nc("Declination", "Dec (J2000)") << i18nc("Magnitude", "Mag") << i18n("Type") << i18n("Current Altitude")); m_SessionModel->setHorizontalHeaderLabels( QStringList() << i18n("Name") << i18n("Alternate Name") << i18nc("Right Ascension", "RA (J2000)") << i18nc("Declination", "Dec (J2000)") << i18nc("Magnitude", "Mag") << i18n("Type") << i18nc("Constellation", "Constell.") << i18n("Time") << i18nc("Altitude", "Alt") << i18nc("Azimuth", "Az")); m_WishListSortModel.reset(new QSortFilterProxyModel(this)); m_WishListSortModel->setSourceModel(m_WishListModel.get()); m_WishListSortModel->setDynamicSortFilter(true); m_WishListSortModel->setSortRole(Qt::UserRole); ui->WishListView->setModel(m_WishListSortModel.get()); ui->WishListView->horizontalHeader()->setStretchLastSection(true); ui->WishListView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); m_SessionSortModel.reset(new SessionSortFilterProxyModel()); m_SessionSortModel->setSourceModel(m_SessionModel.get()); m_SessionSortModel->setDynamicSortFilter(true); ui->SessionView->setModel(m_SessionSortModel.get()); ui->SessionView->horizontalHeader()->setStretchLastSection(true); ui->SessionView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); ksal.reset(new KSAlmanac); ksal->setLocation(geo); ui->avt->setGeoLocation(geo); ui->avt->setSunRiseSetTimes(ksal->getSunRise(), ksal->getSunSet()); ui->avt->setLimits(-12.0, 12.0, -90.0, 90.0); ui->avt->axis(KPlotWidget::BottomAxis)->setTickLabelFormat('t'); ui->avt->axis(KPlotWidget::BottomAxis)->setLabel(i18n("Local Time")); ui->avt->axis(KPlotWidget::TopAxis)->setTickLabelFormat('t'); ui->avt->axis(KPlotWidget::TopAxis)->setTickLabelsShown(true); ui->DateEdit->setDate(dt.date()); ui->SetLocation->setText(geo->fullName()); ui->ImagePreview->installEventFilter(this); ui->WishListView->viewport()->installEventFilter(this); ui->WishListView->installEventFilter(this); ui->SessionView->viewport()->installEventFilter(this); ui->SessionView->installEventFilter(this); // setDefaultImage(); //Connections connect(ui->WishListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotCenterObject())); connect(ui->WishListView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(slotNewSelection())); connect(ui->SessionView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(slotNewSelection())); connect(ui->WUTButton, SIGNAL(clicked()), this, SLOT(slotWUT())); connect(ui->FindButton, SIGNAL(clicked()), this, SLOT(slotFind())); connect(ui->OpenButton, SIGNAL(clicked()), this, SLOT(slotOpenList())); connect(ui->SaveButton, SIGNAL(clicked()), this, SLOT(slotSaveSession())); connect(ui->SaveAsButton, SIGNAL(clicked()), this, SLOT(slotSaveSessionAs())); connect(ui->WizardButton, SIGNAL(clicked()), this, SLOT(slotWizard())); connect(ui->SetLocation, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(ui->Update, SIGNAL(clicked()), this, SLOT(slotUpdate())); connect(ui->DeleteImage, SIGNAL(clicked()), this, SLOT(slotDeleteCurrentImage())); connect(ui->SearchImage, SIGNAL(clicked()), this, SLOT(slotSearchImage())); connect(ui->SetTime, SIGNAL(clicked()), this, SLOT(slotSetTime())); connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotChangeTab(int))); connect(ui->saveImages, SIGNAL(clicked()), this, SLOT(slotSaveAllImages())); connect(ui->DeleteAllImages, SIGNAL(clicked()), this, SLOT(slotDeleteAllImages())); connect(ui->OALExport, SIGNAL(clicked()), this, SLOT(slotOALExport())); connect(ui->clearListB, SIGNAL(clicked()), this, SLOT(slotClearList())); //Add icons to Push Buttons ui->OpenButton->setIcon(QIcon::fromTheme("document-open")); ui->OpenButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); ui->SaveButton->setIcon(QIcon::fromTheme("document-save")); ui->SaveButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); ui->SaveAsButton->setIcon( QIcon::fromTheme("document-save-as")); ui->SaveAsButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); ui->WizardButton->setIcon(QIcon::fromTheme("tools-wizard")); ui->WizardButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); noSelection = true; showScope = false; ui->NotesEdit->setEnabled(false); ui->SetTime->setEnabled(false); ui->TimeEdit->setEnabled(false); ui->SearchImage->setEnabled(false); ui->saveImages->setEnabled(false); ui->DeleteImage->setEnabled(false); ui->OALExport->setEnabled(false); m_NoImagePixmap = QPixmap(":/images/noimage.png") .scaled(ui->ImagePreview->width(), ui->ImagePreview->height(), Qt::KeepAspectRatio, Qt::FastTransformation); m_altCostHelper = [this](const SkyPoint &p) -> QStandardItem * { const double inf = std::numeric_limits::infinity(); double altCost = 0.; QString itemText; double maxAlt = p.maxAlt(*(geo->lat())); if (Options::obsListDemoteHole() && maxAlt > 90. - Options::obsListHoleSize()) maxAlt = 90. - Options::obsListHoleSize(); if (maxAlt <= 0.) { altCost = -inf; itemText = i18n("Never rises"); } else { altCost = (p.alt().Degrees() / maxAlt) * 100.; if (altCost < 0) itemText = i18nc("Short text to describe that object has not risen yet", "Not risen"); else { if (altCost > 100.) { altCost = -inf; itemText = i18nc("Object is in the Dobsonian hole", "In hole"); } else itemText = QString::number(altCost, 'f', 0) + '%'; } } QStandardItem *altItem = new QStandardItem(itemText); altItem->setData(altCost, Qt::UserRole); // qCDebug(KSTARS) << "Updating altitude for " << p.ra().toHMSString() << " " << p.dec().toDMSString() << " alt = " << p.alt().toDMSString() << " info to " << itemText; return altItem; }; // Needed to fix weird bug on Windows that started with Qt 5.9 that makes the title bar // not visible and therefore dialog not movable. #ifdef Q_OS_WIN move(100,100); #endif } -ObservingList::~ObservingList() -{ -} - -// Show Event - void ObservingList::showEvent(QShowEvent *) { // ONLY run for first ever load if (m_initialWishlistLoad == false) { m_initialWishlistLoad = true; slotLoadWishList(); //Load the wishlist from disk if present m_CurrentObject = nullptr; setSaveImagesButton(); slotUpdateAltitudes(); m_altitudeUpdater = new QTimer(this); connect(m_altitudeUpdater, SIGNAL(timeout()), this, SLOT(slotUpdateAltitudes())); m_altitudeUpdater->start(120000); // update altitudes every 2 minutes } } //SLOTS void ObservingList::slotAddObject(const SkyObject *_obj, bool session, bool update) { bool addToWishList = true; if (!_obj) _obj = SkyMap::Instance()->clickedObject(); // Eh? Why? Weird default behavior. if (!_obj) { qCWarning(KSTARS) << "Trying to add null object to observing list! Ignoring."; return; } QString finalObjectName = getObjectName(_obj); if (finalObjectName.isEmpty()) { KMessageBox::sorry(nullptr, i18n("Unnamed stars are not supported in the observing lists")); return; } //First, make sure object is not already in the list QSharedPointer obj = findObject(_obj); if (obj) { addToWishList = false; if (!session) { KStars::Instance()->statusBar()->showMessage( i18n("%1 is already in your wishlist.", finalObjectName), 0); // FIXME: This message is too inconspicuous if using the Find dialog to add return; } } else { assert(!findObject(_obj, session)); qCDebug(KSTARS) << "Cloned object " << finalObjectName << " to add to observing list."; obj = QSharedPointer( _obj->clone()); // Use a clone in case the original SkyObject is deleted due to change in catalog configuration. } if (session && sessionList().contains(obj)) { KStars::Instance()->statusBar()->showMessage(i18n("%1 is already in the session plan.", finalObjectName), 0); return; } // JM: If we are loading observing list from disk, solar system objects magnitudes are not calculated until later // Therefore, we manual invoke updateCoords to force computation of magnitude. if ((obj->type() == SkyObject::COMET || obj->type() == SkyObject::ASTEROID || obj->type() == SkyObject::MOON || obj->type() == SkyObject::PLANET) && obj->mag() == 0) { KSNumbers num(dt.djd()); CachingDms LST = geo->GSTtoLST(dt.gst()); obj->updateCoords(&num, true, geo->lat(), &LST, true); } QString smag = "--"; if (-30.0 < obj->mag() && obj->mag() < 90.0) smag = QString::number(obj->mag(), 'f', 2); // The lower limit to avoid display of unrealistic comet magnitudes SkyPoint p = obj->recomputeHorizontalCoords(dt, geo); QList itemList; auto getItemWithUserRole = [](const QString &itemText) -> QStandardItem * { QStandardItem *ret = new QStandardItem(itemText); ret->setData(itemText, Qt::UserRole); return ret; }; // Fill itemlist with items that are common to both wishlist additions and session plan additions auto populateItemList = [&getItemWithUserRole, &itemList, &finalObjectName, obj, &p, &smag]() { itemList.clear(); QStandardItem *keyItem = getItemWithUserRole(finalObjectName); keyItem->setData(QVariant::fromValue(static_cast(obj.data())), Qt::UserRole + 1); itemList << keyItem // NOTE: The rest of the methods assume that the SkyObject pointer is available in the first column! << getItemWithUserRole(obj->translatedLongName()) << getItemWithUserRole(p.ra0().toHMSString()) << getItemWithUserRole(p.dec0().toDMSString()) << getItemWithUserRole(smag) << getItemWithUserRole(obj->typeName()); }; //Insert object in the Wish List if (addToWishList) { m_WishList.append(obj); m_CurrentObject = obj.data(); //QString ra, dec; //ra = "";//p.ra().toHMSString(); //dec = p.dec().toDMSString(); populateItemList(); // FIXME: Instead sort by a "clever" observability score, calculated as follows: // - First sort by (max altitude) - (current altitude) rounded off to the nearest // - Weight by declination - latitude (in the northern hemisphere, southern objects get higher precedence) // - Demote objects in the hole SkyPoint p = obj->recomputeHorizontalCoords(KStarsDateTime::currentDateTimeUtc(), geo); // Current => now itemList << m_altCostHelper(p); m_WishListModel->appendRow(itemList); //Note addition in statusbar KStars::Instance()->statusBar()->showMessage(i18n("Added %1 to observing list.", finalObjectName), 0); ui->WishListView->resizeColumnsToContents(); if (!update) slotSaveList(); } //Insert object in the Session List if (session) { m_SessionList.append(obj); dt.setTime(TimeHash.value(finalObjectName, obj->transitTime(dt, geo))); dms lst(geo->GSTtoLST(dt.gst())); p.EquatorialToHorizontal(&lst, geo->lat()); QString alt = "--", az = "--"; QStandardItem *BestTime = new QStandardItem(); /* QString ra, dec; if(obj->name() == "star" ) { ra = obj->ra0().toHMSString(); dec = obj->dec0().toDMSString(); BestTime->setData( QString( "--" ), Qt::DisplayRole ); } else {*/ BestTime->setData(TimeHash.value(finalObjectName, obj->transitTime(dt, geo)), Qt::DisplayRole); alt = p.alt().toDMSString(); az = p.az().toDMSString(); //} // TODO: Change the rest of the parameters to their appropriate datatypes. populateItemList(); itemList << getItemWithUserRole(KSUtils::constNameToAbbrev( KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(obj.data()))) << BestTime << getItemWithUserRole(alt) << getItemWithUserRole(az); m_SessionModel->appendRow(itemList); //Adding an object should trigger the modified flag isModified = true; ui->SessionView->resizeColumnsToContents(); //Note addition in statusbar KStars::Instance()->statusBar()->showMessage(i18n("Added %1 to session list.", finalObjectName), 0); } setSaveImagesButton(); } void ObservingList::slotRemoveObject(const SkyObject *_o, bool session, bool update) { if (!update) // EH?! { if (!_o) _o = SkyMap::Instance()->clickedObject(); else if (sessionView) //else if is needed as clickedObject should not be removed from the session list. session = true; } // Is the pointer supplied in our own lists? const QList> &list = (session ? sessionList() : obsList()); QStandardItemModel *currentModel = (session ? m_SessionModel.get() : m_WishListModel.get()); QSharedPointer o = findObject(_o, session); if (!o) { qWarning() << "Object (name: " << getObjectName(o.data()) << ") supplied to ObservingList::slotRemoveObject() was not found in the " << QString(session ? "session" : "observing") << " list!"; return; } int k = list.indexOf(o); assert(k >= 0); // Remove from hash ImagePreviewHash.remove(o.data()); if (o.data() == LogObject) saveCurrentUserLog(); //Remove row from the TableView model // FIXME: Is there no faster way? for (int irow = 0; irow < currentModel->rowCount(); ++irow) { QString name = currentModel->item(irow, 0)->text(); if (getObjectName(o.data()) == name) { currentModel->removeRow(irow); break; } } if (!session) { obsList().removeAt(k); ui->avt->removeAllPlotObjects(); ui->WishListView->resizeColumnsToContents(); if (!update) slotSaveList(); } else { if (!update) TimeHash.remove(o->name()); sessionList().removeAt(k); //Remove from the session list isModified = true; //Removing an object should trigger the modified flag ui->avt->removeAllPlotObjects(); ui->SessionView->resizeColumnsToContents(); } } void ObservingList::slotRemoveSelectedObjects() { //Find each object by name in the session list, and remove it //Go backwards so item alignment doesn't get screwed up as rows are removed. for (int irow = getActiveModel()->rowCount() - 1; irow >= 0; --irow) { bool rowSelected; if (sessionView) rowSelected = ui->SessionView->selectionModel()->isRowSelected(irow, QModelIndex()); else rowSelected = ui->WishListView->selectionModel()->isRowSelected(irow, QModelIndex()); if (rowSelected) { QModelIndex sortIndex, index; sortIndex = getActiveSortModel()->index(irow, 0); index = getActiveSortModel()->mapToSource(sortIndex); SkyObject *o = static_cast(index.data(Qt::UserRole + 1).value()); Q_ASSERT(o); slotRemoveObject(o, sessionView); } } if (sessionView) { //we've removed all selected objects, so clear the selection ui->SessionView->selectionModel()->clear(); //Update the lists in the Execute window as well KStarsData::Instance()->executeSession()->init(); } setSaveImagesButton(); ui->ImagePreview->setCursor(Qt::ArrowCursor); } void ObservingList::slotNewSelection() { bool found = false; singleSelection = false; noSelection = false; showScope = false; //ui->ImagePreview->clearPreview(); //ui->ImagePreview->setPixmap(QPixmap()); ui->ImagePreview->setCursor(Qt::ArrowCursor); QModelIndexList selectedItems; QString newName; QSharedPointer o; QString labelText; ui->DeleteImage->setEnabled(false); selectedItems = getActiveSortModel()->mapSelectionToSource(getActiveView()->selectionModel()->selection()).indexes(); if (selectedItems.size() == getActiveModel()->columnCount()) { newName = selectedItems[0].data().toString(); singleSelection = true; //Find the selected object in the SessionList, //then break the loop. Now SessionList.current() //points to the new selected object (until now it was the previous object) for (auto& o_temp : getActiveList()) { if (getObjectName(o_temp.data()) == newName) { o = o_temp; found = true; break; } } } if (singleSelection) { //Enable buttons ui->ImagePreview->setCursor(Qt::PointingHandCursor); #ifdef HAVE_INDI showScope = true; #endif if (found) { m_CurrentObject = o.data(); //QPoint pos(0,0); plot(o.data()); //Change the m_currentImageFileName, DSS/SDSS Url to correspond to the new object setCurrentImage(o.data()); ui->SearchImage->setEnabled(true); if (newName != i18n("star")) { //Display the current object's user notes in the NotesEdit //First, save the last object's user log to disk, if necessary saveCurrentUserLog(); //uses LogObject, which is still the previous obj. //set LogObject to the new selected object LogObject = currentObject(); ui->NotesEdit->setEnabled(true); if (LogObject->userLog().isEmpty()) { ui->NotesEdit->setPlainText( i18n("Record here observation logs and/or data on %1.", getObjectName(LogObject))); } else { ui->NotesEdit->setPlainText(LogObject->userLog()); } if (sessionView) { ui->TimeEdit->setEnabled(true); ui->SetTime->setEnabled(true); ui->TimeEdit->setTime(TimeHash.value(o->name(), o->transitTime(dt, geo))); } } else //selected object is named "star" { //clear the log text box saveCurrentUserLog(); ui->NotesEdit->clear(); ui->NotesEdit->setEnabled(false); ui->SearchImage->setEnabled(false); } QString ImagePath = KSPaths::locate(QStandardPaths::GenericDataLocation, m_currentImageFileName); if (!ImagePath.isEmpty()) { //If the image is present, show it! KSDssImage ksdi(ImagePath); KSDssImage::Metadata md = ksdi.getMetadata(); //ui->ImagePreview->showPreview( QUrl::fromLocalFile( ksdi.getFileName() ) ); if (ImagePreviewHash.contains(o.data()) == false) ImagePreviewHash[o.data()] = QPixmap(ksdi.getFileName()).scaledToHeight(ui->ImagePreview->width()); //ui->ImagePreview->setPixmap(QPixmap(ksdi.getFileName()).scaledToHeight(ui->ImagePreview->width())); ui->ImagePreview->setPixmap(ImagePreviewHash[o.data()]); if (md.isValid()) { ui->dssMetadataLabel->setText( i18n("DSS Image metadata: \n Size: %1\' x %2\' \n Photometric band: %3 \n Version: %4", QString::number(md.width), QString::number(md.height), QString() + md.band, md.version)); } else ui->dssMetadataLabel->setText(i18n("No image info available.")); ui->ImagePreview->show(); ui->DeleteImage->setEnabled(true); } else { setDefaultImage(); ui->dssMetadataLabel->setText( i18n("No image available. Click on the placeholder image to download one.")); } QString cname = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(o.data()); if (o->type() != SkyObject::CONSTELLATION) { labelText = ""; if (o->type() == SkyObject::PLANET) labelText += o->translatedName(); else labelText += o->name(); if (std::isfinite(o->mag()) && o->mag() <= 30.) labelText += ": " + i18nc("%1 magnitude of object, %2 type of sky object (planet, asteroid " "etc), %3 name of a constellation", "%1 mag %2 in %3", o->mag(), o->typeName().toLower(), cname); else labelText += ": " + i18nc("%1 type of sky object (planet, asteroid etc), %2 name of a constellation", "%1 in %2", o->typeName(), cname); } } else { setDefaultImage(); qCWarning(KSTARS) << "Object " << newName << " not found in list."; } ui->quickInfoLabel->setText(labelText); } else { if (selectedItems.size() == 0) //Nothing selected { //Disable buttons noSelection = true; ui->NotesEdit->setEnabled(false); m_CurrentObject = nullptr; ui->TimeEdit->setEnabled(false); ui->SetTime->setEnabled(false); ui->SearchImage->setEnabled(false); //Clear the user log text box. saveCurrentUserLog(); ui->NotesEdit->setPlainText(""); //Clear the plot in the AVTPlotwidget ui->avt->removeAllPlotObjects(); } else //more than one object selected. { ui->NotesEdit->setEnabled(false); ui->TimeEdit->setEnabled(false); ui->SetTime->setEnabled(false); ui->SearchImage->setEnabled(false); m_CurrentObject = nullptr; //Clear the plot in the AVTPlotwidget ui->avt->removeAllPlotObjects(); //Clear the user log text box. saveCurrentUserLog(); ui->NotesEdit->setPlainText(""); ui->quickInfoLabel->setText(QString()); } } } void ObservingList::slotCenterObject() { if (getSelectedItems().size() == 1) { SkyMap::Instance()->setClickedObject(currentObject()); SkyMap::Instance()->setClickedPoint(currentObject()); SkyMap::Instance()->slotCenter(); } } void ObservingList::slotSlewToObject() { #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 == nullptr) 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); gd->setProperty(&SlewCMD); gd->runCommand(INDI_SEND_COORDS, currentObject()); return; } KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); #endif } void ObservingList::slotAddToEkosScheduler() { #ifdef HAVE_INDI KStars::Instance()->ekosManager()->addObjectToScheduler(currentObject()); #endif } //FIXME: This will open multiple Detail windows for each object; //Should have one window whose target object changes with selection void ObservingList::slotDetails() { if (currentObject()) { QPointer dd = new DetailDialog(currentObject(), KStarsData::Instance()->ut(), geo, KStars::Instance()); dd->exec(); delete dd; } } void ObservingList::slotWUT() { KStarsDateTime lt = dt; lt.setTime(QTime(8, 0, 0)); QPointer w = new WUTDialog(KStars::Instance(), sessionView, geo, lt); w->exec(); delete w; } void ObservingList::slotAddToSession() { Q_ASSERT(!sessionView); if (getSelectedItems().size()) { foreach (const QModelIndex &i, getSelectedItems()) { foreach (QSharedPointer o, obsList()) if (getObjectName(o.data()) == i.data().toString()) slotAddObject( o.data(), true); // FIXME: Would be good to have a wrapper that accepts QSharedPointer } } } void ObservingList::slotFind() { QPointer fd = new FindDialog(KStars::Instance()); if (fd->exec() == QDialog::Accepted) { SkyObject *o = fd->targetObject(); if (o != nullptr) { slotAddObject(o, sessionView); } } delete fd; } void ObservingList::slotEyepieceView() { KStars::Instance()->slotEyepieceView(currentObject(), getCurrentImagePath()); } void ObservingList::slotAVT() { QModelIndexList selectedItems; // TODO: Think and see if there's a more effecient way to do this. I can't seem to think of any, but this code looks like it could be improved. - Akarsh selectedItems = (sessionView ? m_SessionSortModel->mapSelectionToSource(ui->SessionView->selectionModel()->selection()).indexes() : m_WishListSortModel->mapSelectionToSource(ui->WishListView->selectionModel()->selection()).indexes()); if (selectedItems.size()) { QPointer avt = new AltVsTime(KStars::Instance()); foreach (const QModelIndex &i, selectedItems) { if (i.column() == 0) { SkyObject *o = static_cast(i.data(Qt::UserRole + 1).value()); Q_ASSERT(o); avt->processObject(o); } } avt->exec(); delete avt; } } //FIXME: On close, we will need to close any open Details/AVT windows void ObservingList::slotClose() { //Save the current User log text saveCurrentUserLog(); ui->avt->removeAllPlotObjects(); slotNewSelection(); saveCurrentList(); hide(); } void ObservingList::saveCurrentUserLog() { if (LogObject && !ui->NotesEdit->toPlainText().isEmpty() && ui->NotesEdit->toPlainText() != i18n("Record here observation logs and/or data on %1.", getObjectName(LogObject))) { LogObject->saveUserLog(ui->NotesEdit->toPlainText()); ui->NotesEdit->clear(); LogObject = nullptr; } } void ObservingList::slotOpenList() { QUrl fileURL = QFileDialog::getOpenFileUrl(KStars::Instance(), i18n("Open Observing List"), QUrl(), "KStars Observing List (*.obslist)"); QFile f; if (fileURL.isValid()) { f.setFileName(fileURL.toLocalFile()); //FIXME do we still need to do this? /* if ( ! fileURL.isLocalFile() ) { //Save remote list to a temporary local file QTemporaryFile tmpfile; tmpfile.setAutoRemove(false); tmpfile.open(); m_listFileName = tmpfile.fileName(); if( KIO::NetAccess::download( fileURL, m_listFileName, this ) ) f.setFileName( m_listFileName ); } else { m_listFileName = fileURL.toLocalFile(); f.setFileName( m_listFileName ); } */ if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } saveCurrentList(); //See if the current list needs to be saved before opening the new one ui->tabWidget->setCurrentIndex(1); slotChangeTab(1); sessionList().clear(); TimeHash.clear(); m_CurrentObject = nullptr; m_SessionModel->removeRows(0, m_SessionModel->rowCount()); //First line is the name of the list. The rest of the file is //object names, one per line. With the TimeHash value if present QTextStream istream(&f); QString input; input = istream.readAll(); OAL::Log logObject; logObject.readBegin(input); //Set the New TimeHash TimeHash = logObject.timeHash(); geo = logObject.geoLocation(); dt = logObject.dateTime(); //foreach (SkyObject *o, *(logObject.targetList())) for (auto &o : logObject.targetList()) slotAddObject(o.data(), true); //Update the location and user set times from file slotUpdate(); //Newly-opened list should not trigger isModified flag isModified = false; f.close(); } else if (!fileURL.toLocalFile().isEmpty()) { KMessageBox::sorry(nullptr, i18n("The specified file is invalid")); } } void ObservingList::slotClearList() { if ((ui->tabWidget->currentIndex() == 0 && obsList().isEmpty()) || (ui->tabWidget->currentIndex() == 1 && sessionList().isEmpty())) return; QString message = i18n("Are you sure you want to clear all objects?"); if (KMessageBox::questionYesNo(this, message, i18n("Clear all?")) == KMessageBox::Yes) { // Did I forget anything else to remove? ui->avt->removeAllPlotObjects(); m_CurrentObject = LogObject = nullptr; if (ui->tabWidget->currentIndex() == 0) { // IMPORTANT: Is this enough or we will have dangling pointers in memory? ImagePreviewHash.clear(); obsList().clear(); m_WishListModel->setRowCount(0); } else { // IMPORTANT: Is this enough or we will have dangling pointers in memory? sessionList().clear(); TimeHash.clear(); isModified = true; //Removing an object should trigger the modified flag m_SessionModel->setRowCount(0); } } } void ObservingList::saveCurrentList() { //Before loading a new list, do we need to save the current one? //Assume that if the list is empty, then there's no need to save if (sessionList().size()) { if (isModified) { QString message = i18n("Do you want to save the current session?"); if (KMessageBox::questionYesNo(this, message, i18n("Save Current session?"), KStandardGuiItem::save(), KStandardGuiItem::discard()) == KMessageBox::Yes) slotSaveSession(); } } } void ObservingList::slotSaveSessionAs(bool nativeSave) { if (sessionList().isEmpty()) return; QUrl fileURL = QFileDialog::getSaveFileUrl(KStars::Instance(), i18n("Save Observing List"), QUrl(), "KStars Observing List (*.obslist)"); if (fileURL.isValid()) { m_listFileName = fileURL.toLocalFile(); slotSaveSession(nativeSave); } } void ObservingList::slotSaveList() { QFile f; // FIXME: Move wishlist into a database. // TODO: Support multiple wishlists. QString fileContents; QTextStream ostream( &fileContents); // We first write to a QString to prevent truncating the file in case there is a crash. foreach (const QSharedPointer o, obsList()) { if (!o) { qWarning() << "Null entry in observing wishlist! Skipping!"; continue; } if (o->name() == "star") { //ostream << o->name() << " " << o->ra0().Hours() << " " << o->dec0().Degrees() << endl; ostream << getObjectName(o.data(), false) << endl; } else if (o->type() == SkyObject::STAR) { Q_ASSERT(dynamic_cast(o.data())); const QSharedPointer s = qSharedPointerCast(o); if (s->name() == s->gname()) ostream << s->name2() << endl; else ostream << s->name() << endl; } else { ostream << o->name() << endl; } } f.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "wishlist.obslist"); if (!f.open(QIODevice::WriteOnly)) { qWarning() << "Cannot save wish list to file!"; // TODO: This should be presented as a message box to the user return; } QTextStream writeemall(&f); writeemall << fileContents; f.close(); } void ObservingList::slotLoadWishList() { QFile f; f.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "wishlist.obslist"); if (!f.open(QIODevice::ReadOnly)) { qWarning(KSTARS) << "No WishList Saved yet"; return; } QTextStream istream(&f); QString line; QPointer addingObjectsProgress = new QProgressDialog(); addingObjectsProgress->setWindowTitle(i18n("Observing List Wizard")); addingObjectsProgress->setLabelText(i18n("Please wait while loading objects...")); addingObjectsProgress->setMaximum(0); addingObjectsProgress->setMinimum(0); addingObjectsProgress->show(); while (!istream.atEnd()) { line = istream.readLine(); //If the object is named "star", add it by coordinates SkyObject *o; /*if ( line.startsWith( QLatin1String( "star" ) ) ) { QStringList fields = line.split( ' ', QString::SkipEmptyParts ); dms ra = dms::fromString( fields[1], false ); //false = hours dms dc = dms::fromString( fields[2], true ); //true = degrees SkyPoint p( ra, dc ); double maxrad = 1000.0/Options::zoomFactor(); o = ks->data()->skyComposite()->starNearest( &p, maxrad ); } else {*/ o = KStarsData::Instance()->objectNamed(line); //} //If we haven't identified the object, try interpreting the //name as a star's genetive name (with ascii letters) if (!o) o = KStarsData::Instance()->skyComposite()->findStarByGenetiveName(line); if (o) slotAddObject(o, false, true); if (addingObjectsProgress->wasCanceled()) break; qApp->processEvents(); } delete (addingObjectsProgress); f.close(); } void ObservingList::slotSaveSession(bool nativeSave) { if (sessionList().isEmpty()) { KMessageBox::error(nullptr, i18n("Cannot save an empty session list!")); return; } if (m_listFileName.isEmpty()) { slotSaveSessionAs(nativeSave); return; } QFile f(m_listFileName); if (!f.open(QIODevice::WriteOnly)) { QString message = i18n("Could not open file %1. Try a different filename?", f.fileName()); if (KMessageBox::warningYesNo(nullptr, message, i18n("Could Not Open File"), KGuiItem(i18n("Try Different")), KGuiItem(i18n("Do Not Try"))) == KMessageBox::Yes) { m_listFileName.clear(); slotSaveSessionAs(nativeSave); } return; } QTextStream ostream(&f); OAL::Log log; ostream << log.writeLog(nativeSave); f.close(); isModified = false; //We've saved the session, so reset the modified flag. } void ObservingList::slotWizard() { QPointer wizard = new ObsListWizard(KStars::Instance()); if (wizard->exec() == QDialog::Accepted) { QPointer addingObjectsProgress = new QProgressDialog(); addingObjectsProgress->setWindowTitle(i18n("Observing List Wizard")); addingObjectsProgress->setLabelText(i18n("Please wait while adding objects...")); addingObjectsProgress->setMaximum(wizard->obsList().size()); addingObjectsProgress->setMinimum(0); addingObjectsProgress->setValue(0); addingObjectsProgress->show(); int counter = 1; foreach (SkyObject *o, wizard->obsList()) { slotAddObject(o); addingObjectsProgress->setValue(counter++); if (addingObjectsProgress->wasCanceled()) break; qApp->processEvents(); } delete addingObjectsProgress; } delete wizard; } void ObservingList::plot(SkyObject *o) { if (!o) return; float DayOffset = 0; if (TimeHash.value(o->name(), o->transitTime(dt, geo)).hour() > 12) DayOffset = 1; QDateTime midnight = QDateTime(dt.date(), QTime()); KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight)); double h1 = geo->GSTtoLST(ut.gst()).Hours(); if (h1 > 12.0) h1 -= 24.0; ui->avt->setSecondaryLimits(h1, h1 + 24.0, -90.0, 90.0); ksal->setLocation(geo); ksal->setDate(&ut); ui->avt->setGeoLocation(geo); ui->avt->setSunRiseSetTimes(ksal->getSunRise(), ksal->getSunSet()); ui->avt->setDawnDuskTimes(ksal->getDawnAstronomicalTwilight(), ksal->getDuskAstronomicalTwilight()); ui->avt->setMinMaxSunAlt(ksal->getSunMinAlt(), ksal->getSunMaxAlt()); ui->avt->setMoonRiseSetTimes(ksal->getMoonRise(), ksal->getMoonSet()); ui->avt->setMoonIllum(ksal->getMoonIllum()); ui->avt->update(); KPlotObject *po = new KPlotObject(Qt::white, KPlotObject::Lines, 2.0); for (double h = -12.0; h <= 12.0; h += 0.5) { po->addPoint(h, findAltitude(o, (h + DayOffset * 24.0))); } ui->avt->removeAllPlotObjects(); ui->avt->addPlotObject(po); } double ObservingList::findAltitude(SkyPoint *p, double hour) { // Jasem 2015-09-05 Using correct procedure to find altitude SkyPoint sp = *p; // make a copy QDateTime midnight = QDateTime(dt.date(), QTime()); KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(midnight)); KStarsDateTime targetDateTime = ut.addSecs(hour * 3600.0); dms LST = geo->GSTtoLST(targetDateTime.gst()); sp.EquatorialToHorizontal(&LST, geo->lat()); return sp.alt().Degrees(); } void ObservingList::slotChangeTab(int index) { noSelection = true; saveCurrentUserLog(); ui->NotesEdit->setEnabled(false); ui->TimeEdit->setEnabled(false); ui->SetTime->setEnabled(false); ui->SearchImage->setEnabled(false); ui->DeleteImage->setEnabled(false); m_CurrentObject = nullptr; sessionView = index != 0; setSaveImagesButton(); ui->WizardButton->setEnabled(!sessionView); //wizard adds only to the Wish List ui->OALExport->setEnabled(sessionView); //Clear the selection in the Tables ui->WishListView->clearSelection(); ui->SessionView->clearSelection(); //Clear the user log text box. saveCurrentUserLog(); ui->NotesEdit->setPlainText(""); ui->avt->removeAllPlotObjects(); } void ObservingList::slotLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { geo = ld->selectedCity(); ui->SetLocation->setText(geo->fullName()); } delete ld; } void ObservingList::slotUpdate() { dt.setDate(ui->DateEdit->date()); ui->avt->removeAllPlotObjects(); //Creating a copy of the lists, we can't use the original lists as they'll keep getting modified as the loop iterates QList> _obsList = m_WishList, _SessionList = m_SessionList; for (QSharedPointer &o : _obsList) { if (o->name() != "star") { slotRemoveObject(o.data(), false, true); slotAddObject(o.data(), false, true); } } for (QSharedPointer &obj : _SessionList) { if (obj->name() != "star") { slotRemoveObject(obj.data(), true, true); slotAddObject(obj.data(), true, true); } } } void ObservingList::slotSetTime() { SkyObject *o = currentObject(); slotRemoveObject(o, true); TimeHash[o->name()] = ui->TimeEdit->time(); slotAddObject(o, true, true); } void ObservingList::slotCustomDSS() { ui->SearchImage->setEnabled(false); //ui->ImagePreview->clearPreview(); ui->ImagePreview->setPixmap(QPixmap()); KSDssImage::Metadata md; bool ok = true; int width = QInputDialog::getInt(this, i18n("Customized DSS Download"), i18n("Specify image width (arcminutes): "), 15, 15, 75, 1, &ok); int height = QInputDialog::getInt(this, i18n("Customized DSS Download"), i18n("Specify image height (arcminutes): "), 15, 15, 75, 1, &ok); QStringList strList = (QStringList() << "poss2ukstu_blue" << "poss2ukstu_red" << "poss2ukstu_ir" << "poss1_blue" << "poss1_red" << "quickv" << "all"); QString version = QInputDialog::getItem(this, i18n("Customized DSS Download"), i18n("Specify version: "), strList, 0, false, &ok); QUrl srcUrl(KSDssDownloader::getDSSURL(currentObject()->ra0(), currentObject()->dec0(), width, height, "gif", version, &md)); delete m_dl; m_dl = new KSDssDownloader(); connect(m_dl, SIGNAL(downloadComplete(bool)), SLOT(downloadReady(bool))); m_dl->startSingleDownload(srcUrl, getCurrentImagePath(), md); } void ObservingList::slotGetImage(bool _dss, const SkyObject *o) { dss = _dss; Q_ASSERT( !o || o == currentObject()); // FIXME: Meaningless to operate on m_currentImageFileName unless o == currentObject()! if (!o) o = currentObject(); ui->SearchImage->setEnabled(false); //ui->ImagePreview->clearPreview(); //ui->ImagePreview->setPixmap(QPixmap()); setCurrentImage(o); QString currentImagePath = getCurrentImagePath(); if (QFile::exists(currentImagePath)) QFile::remove(currentImagePath); //QUrl url; dss = true; qWarning() << "FIXME: Removed support for SDSS. Until reintroduction, we will supply a DSS image"; std::function slot = std::bind(&ObservingList::downloadReady, this, std::placeholders::_1); new KSDssDownloader(o, currentImagePath, slot, this); } void ObservingList::downloadReady(bool success) { // set downloadJob to 0, but don't delete it - the job will be deleted automatically // downloadJob = 0; delete m_dl; m_dl = nullptr; // required if we came from slotCustomDSS; does nothing otherwise if (!success) { KMessageBox::sorry(nullptr, i18n("Failed to download DSS/SDSS image!")); } else { /* if( QFile( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + m_currentImageFileName ).size() > 13000) //The default image is around 8689 bytes */ //ui->ImagePreview->showPreview( QUrl::fromLocalFile( getCurrentImagePath() ) ); ui->ImagePreview->setPixmap(QPixmap(getCurrentImagePath()).scaledToHeight(ui->ImagePreview->width())); saveThumbImage(); ui->ImagePreview->show(); ui->ImagePreview->setCursor(Qt::PointingHandCursor); ui->DeleteImage->setEnabled(true); } /* // FIXME: Implement a priority order SDSS > DSS in the DSS downloader else if( ! dss ) slotGetImage( true ); */ } void ObservingList::setCurrentImage(const SkyObject *o) { QString sanitizedName = o->name().remove(' ').remove('\'').remove('\"').toLower(); // JM: Always use .png across all platforms. No JPGs at all? m_currentImageFileName = "image-" + sanitizedName + ".png"; m_currentThumbImageFileName = "thumb-" + sanitizedName + ".png"; // Does full image exists in the path? QString currentImagePath = KSPaths::locate(QStandardPaths::GenericDataLocation, m_currentImageFileName); // Let's try to fallback to thumb-* images if they exist if (currentImagePath.isEmpty()) { currentImagePath = KSPaths::locate(QStandardPaths::GenericDataLocation, m_currentThumbImageFileName); // If thumb image exists, let's use it if (currentImagePath.isEmpty() == false) m_currentImageFileName = m_currentThumbImageFileName; } // 2017-04-14: Unnamed stars already unsupported in observing list /* if( o->name() == "star" ) { QString RAString( o->ra0().toHMSString() ); QString DecString( o->dec0().toDMSString() ); m_currentImageFileName = "Image_J" + RAString.remove(' ').remove( ':' ) + DecString.remove(' ').remove( ':' ); // Note: Changed naming convention to standard 2016-08-25 asimha; old images shall have to be re-downloaded. // Unnecessary complication below: // QChar decsgn = ( (o->dec0().Degrees() < 0.0 ) ? '-' : '+' ); // m_currentImageFileName = m_currentImageFileName.remove('+').remove('-') + decsgn; } */ // 2017-04-14 JM: If we use .png always, let us use it across all platforms. /* QString imagePath = getCurrentImagePath(); if ( QFile::exists( imagePath)) // New convention -- append filename extension so file is usable on Windows etc. { QFile::rename( imagePath, imagePath + ".png" ); } m_currentImageFileName += ".png"; */ } QString ObservingList::getCurrentImagePath() { QString currentImagePath = KSPaths::locate(QStandardPaths::GenericDataLocation, m_currentImageFileName); if (QFile::exists(currentImagePath)) { return currentImagePath; } else return (KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + m_currentImageFileName); } void ObservingList::slotSaveAllImages() { ui->SearchImage->setEnabled(false); ui->DeleteImage->setEnabled(false); m_CurrentObject = nullptr; //Clear the selection in the Tables ui->WishListView->clearSelection(); ui->SessionView->clearSelection(); foreach (QSharedPointer o, getActiveList()) { if (!o) continue; // FIXME: Why would we have null objects? But appears that we do. setCurrentImage(o.data()); QString img(getCurrentImagePath()); // QUrl url( ( Options::obsListPreferDSS() ) ? DSSUrl : SDSSUrl ); // FIXME: We have removed SDSS support! QUrl url(KSDssDownloader::getDSSURL(o.data())); if (!o->isSolarSystem()) //TODO find a way for adding support for solar system images saveImage(url, img, o.data()); } } void ObservingList::saveImage(QUrl /*url*/, QString /*filename*/, const SkyObject *o) { if (!o) o = currentObject(); Q_ASSERT(o); if (!QFile::exists(getCurrentImagePath())) { // Call the DSS downloader slotGetImage(true, o); } } void ObservingList::slotImageViewer() { QPointer iv; QString currentImagePath = getCurrentImagePath(); if (QFile::exists(currentImagePath)) { QUrl url = QUrl::fromLocalFile(currentImagePath); iv = new ImageViewer(url); } if (iv) iv->show(); } void ObservingList::slotDeleteAllImages() { if (KMessageBox::warningYesNo(nullptr, i18n("This will delete all saved images. Are you sure you want to do this?"), i18n("Delete All Images")) == KMessageBox::No) return; ui->ImagePreview->setCursor(Qt::ArrowCursor); ui->SearchImage->setEnabled(false); ui->DeleteImage->setEnabled(false); m_CurrentObject = nullptr; //Clear the selection in the Tables ui->WishListView->clearSelection(); ui->SessionView->clearSelection(); //ui->ImagePreview->clearPreview(); ui->ImagePreview->setPixmap(QPixmap()); QDirIterator iterator(KSPaths::writableLocation(QStandardPaths::GenericDataLocation)); while (iterator.hasNext()) { // TODO: Probably, there should be a different directory for cached images in the observing list. if (iterator.fileName().contains("Image") && (!iterator.fileName().contains("dat")) && (!iterator.fileName().contains("obslist"))) { QFile file(iterator.filePath()); file.remove(); } iterator.next(); } } void ObservingList::setSaveImagesButton() { ui->saveImages->setEnabled(!getActiveList().isEmpty()); } // FIXME: Is there a reason to implement these as an event filter, // instead of as a signal-slot connection? Shouldn't we just use slots // to subscribe to various events from the Table / Session view? // // NOTE: ui->ImagePreview is a QLabel, which has no clicked() event or // public mouseReleaseEvent(), so eventFilter makes sense. bool ObservingList::eventFilter(QObject *obj, QEvent *event) { if (obj == ui->ImagePreview) { if (event->type() == QEvent::MouseButtonRelease) { if (currentObject()) { if (!QFile::exists(getCurrentImagePath())) { if (!currentObject()->isSolarSystem()) slotGetImage(Options::obsListPreferDSS()); else slotSearchImage(); } else slotImageViewer(); } return true; } } if (obj == ui->WishListView->viewport() || obj == ui->SessionView->viewport()) { bool sessionViewEvent = (obj == ui->SessionView->viewport()); if (event->type() == QEvent::MouseButtonRelease) // Mouse button release event { QMouseEvent *mouseEvent = static_cast(event); QPoint pos(mouseEvent->globalX(), mouseEvent->globalY()); if (mouseEvent->button() == Qt::RightButton) { if (!noSelection) { pmenu->initPopupMenu(sessionViewEvent, !singleSelection, showScope); pmenu->popup(pos); } return true; } } } if (obj == ui->WishListView || obj == ui->SessionView) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Delete) { slotRemoveSelectedObjects(); return true; } } } return false; } void ObservingList::slotSearchImage() { QPixmap *pm = new QPixmap(":/images/noimage.png"); QPointer tp = new ThumbnailPicker(currentObject(), *pm, this, 200, 200, i18n("Image Chooser")); if (tp->exec() == QDialog::Accepted) { QString currentImagePath = getCurrentImagePath(); QFile f(currentImagePath); //If a real image was set, save it. if (tp->imageFound()) { tp->image()->save(f.fileName(), "PNG"); //ui->ImagePreview->showPreview( QUrl::fromLocalFile( f.fileName() ) ); ui->ImagePreview->setPixmap(QPixmap(f.fileName()).scaledToHeight(ui->ImagePreview->width())); saveThumbImage(); slotNewSelection(); } } delete pm; delete tp; } void ObservingList::slotDeleteCurrentImage() { QFile::remove(getCurrentImagePath()); ImagePreviewHash.remove(m_CurrentObject); slotNewSelection(); } void ObservingList::saveThumbImage() { if (!QFile::exists(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + m_currentThumbImageFileName)) { QImage img(getCurrentImagePath()); img = img.scaled(200, 200, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); img.save(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + m_currentThumbImageFileName); } } QString ObservingList::getTime(const SkyObject *o) const { return TimeHash.value(o->name(), QTime(30, 0, 0)).toString("h:mm:ss AP"); } QTime ObservingList::scheduledTime(SkyObject *o) const { return TimeHash.value(o->name(), o->transitTime(dt, geo)); } void ObservingList::setTime(const SkyObject *o, QTime t) { TimeHash.insert(o->name(), t); } void ObservingList::slotOALExport() { slotSaveSessionAs(false); } void ObservingList::slotAddVisibleObj() { KStarsDateTime lt = dt; lt.setTime(QTime(8, 0, 0)); QPointer w = new WUTDialog(KStars::Instance(), sessionView, geo, lt); w->init(); QModelIndexList selectedItems; selectedItems = m_WishListSortModel->mapSelectionToSource(ui->WishListView->selectionModel()->selection()).indexes(); if (selectedItems.size()) { foreach (const QModelIndex &i, selectedItems) { foreach (QSharedPointer o, obsList()) if (getObjectName(o.data()) == i.data().toString() && w->checkVisibility(o.data())) slotAddObject( o.data(), true); // FIXME: Better if there is a QSharedPointer override for this, although the check will ensure that we don't duplicate. } } delete w; } SkyObject *ObservingList::findObjectByName(QString name) { foreach (QSharedPointer o, sessionList()) { if (getObjectName(o.data(), false) == name) return o.data(); } return nullptr; } void ObservingList::selectObject(const SkyObject *o) { ui->tabWidget->setCurrentIndex(1); ui->SessionView->selectionModel()->clear(); for (int irow = m_SessionModel->rowCount() - 1; irow >= 0; --irow) { QModelIndex mSortIndex = m_SessionSortModel->index(irow, 0); QModelIndex mIndex = m_SessionSortModel->mapToSource(mSortIndex); int idxrow = mIndex.row(); if (m_SessionModel->item(idxrow, 0)->text() == getObjectName(o)) ui->SessionView->selectRow(idxrow); slotNewSelection(); } } void ObservingList::setDefaultImage() { ui->ImagePreview->setPixmap(m_NoImagePixmap); ui->ImagePreview->update(); } QString ObservingList::getObjectName(const SkyObject *o, bool translated) { QString finalObjectName; if (o->name() == "star") { StarObject *s = (StarObject *)o; // JM: Enable HD Index stars to be added to the observing list. if (s->getHDIndex() != 0) finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex())); } else finalObjectName = translated ? o->translatedName() : o->name(); return finalObjectName; } void ObservingList::slotUpdateAltitudes() { // FIXME: Update upon gaining visibility, do not update when not visible KStarsDateTime now = KStarsDateTime::currentDateTimeUtc(); // qCDebug(KSTARS) << "Updating altitudes in observation planner @ JD - J2000 = " << double( now.djd() - J2000 ); for (int irow = m_WishListModel->rowCount() - 1; irow >= 0; --irow) { QModelIndex idx = m_WishListSortModel->mapToSource(m_WishListSortModel->index(irow, 0)); SkyObject *o = static_cast(idx.data(Qt::UserRole + 1).value()); Q_ASSERT(o); SkyPoint p = o->recomputeHorizontalCoords(now, geo); idx = m_WishListSortModel->mapToSource(m_WishListSortModel->index(irow, m_WishListSortModel->columnCount() - 1)); QStandardItem *replacement = m_altCostHelper(p); m_WishListModel->setData(idx, replacement->data(Qt::DisplayRole), Qt::DisplayRole); m_WishListModel->setData(idx, replacement->data(Qt::UserRole), Qt::UserRole); delete replacement; } emit m_WishListModel->dataChanged( m_WishListModel->index(0, m_WishListModel->columnCount() - 1), m_WishListModel->index(m_WishListModel->rowCount() - 1, m_WishListModel->columnCount() - 1)); } QSharedPointer ObservingList::findObject(const SkyObject *o, bool session) { const QList> &list = (session ? sessionList() : obsList()); const QString &target = getObjectName(o); foreach (QSharedPointer obj, list) { if (getObjectName(obj.data()) == target) return obj; } return QSharedPointer(); // null pointer } diff --git a/kstars/tools/observinglist.h b/kstars/tools/observinglist.h index 87f30ef0d..c1ec52ad1 100644 --- a/kstars/tools/observinglist.h +++ b/kstars/tools/observinglist.h @@ -1,428 +1,422 @@ /*************************************************************************** observinglist.h - K Desktop Planetarium ------------------- begin : 29 Nov 2004 copyright : (C) 2004 by Jeff Woods, Jason Harris email : jcwoods@bellsouth.net, jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once +#include "ksalmanac.h" #include "kstarsdatetime.h" #include "ui_observinglist.h" #include #include #include #include #include #include #include #include class QSortFilterProxyModel; class QStandardItem; class QStandardItemModel; class QTimer; class GeoLocation; -class KSAlmanac; class KSDssDownloader; class KStars; class KStarsDateTime; class ObsListPopupMenu; class SkyObject; class SkyPoint; class ObservingListUI : public QFrame, public Ui::ObservingList { Q_OBJECT public: - /** @short Cunstructor - */ explicit ObservingListUI(QWidget *parent); }; /** @class ObservingList *Tool window for managing a custom list of objects. The window *displays the Name, RA, Dec, mag, and type of each object in the list. * *By selecting an object in the list, you can perform a number of functions *on that object: *+ Center it in the display *+ Examine its Details Window *+ Point the telescope at it *+ Attach a custom icon or name label (TBD) *+ Attach a trail (solar system only) (TBD) *+ Open the AltVsTime tool * *The user can also save/load their observing lists, and can export *list data (TBD: as HTML table? CSV format? plain text?) * *The observing notes associated with the selected object are displayed *below the list. * *TODO: *+ Implement a "shaded" state, in which the UI is compressed to * make it easier to float on the KStars window. Displays only * object names, and single-letter action buttons, and no user log. *+ Implement an InfoBox version (the ultimate shaded state) * *@short Tool for managing a custom list of objects *@author Jeff Woods, Jason Harris *@version 1.0 */ class ObservingList : public QDialog { Q_OBJECT public: - /** @short Constructor - */ ObservingList(); - /** @short Destuctor (empty) - */ - ~ObservingList() override; + virtual ~ObservingList() override = default; /** @return reference to the current observing list */ QList> &obsList() { return m_WishList; } /** @return reference to the current observing list */ QList> &sessionList() { return m_SessionList; } /** @return pointer to the currently-selected object in the observing list *@note if more than one object is selected, this function returns 0. */ inline SkyObject *currentObject() const { return m_CurrentObject; } /** @short If the current list has unsaved changes, ask the user about saving it. *@note also clears the list in preparation of opening a new one */ void saveCurrentList(); /** @short Plot the SkyObject's Altitude vs Time in the AVTPlotWidget. *@p o pointer to the object to be plotted */ void plot(SkyObject *o); /** @short Return the altitude of the given SkyObject for the given hour. *@p p pointer to the SkyObject *@p hour time at which altitude has to be found */ double findAltitude(SkyPoint *p, double hour = 0); /** @short Sets the image parameters for the current object *@p o The passed object for setting the parameters */ void setCurrentImage(const SkyObject *o); /** * @short Returns a path to the current image, or a writable image. */ QString getCurrentImagePath(); /** @short Save the user log text to a file. *@note the log is attached to the current object in obsList. */ void saveCurrentUserLog(); /** @short decides on whether to enable the SaveImages button or not */ void setSaveImagesButton(); /** @short This is the declaration of the event filter function * which is installed on the KImageFilePreview and the TabeView */ bool eventFilter(QObject *obj, QEvent *event) override; /** @short saves a thumbnail image for the details dialog * from the downloaded image */ void saveThumbImage(); QString getTime(const SkyObject *o) const; QTime scheduledTime(SkyObject *o) const; void setTime(const SkyObject *o, QTime t); inline GeoLocation *geoLocation() { return geo; } inline KStarsDateTime dateTime() const { return dt; } /** @short return the object with the name as the passed * QString from the Session List, return null otherwise */ SkyObject *findObjectByName(QString name); /** @short make a selection in the session view */ void selectObject(const SkyObject *o); /** @short set the default image in the image preview. */ void setDefaultImage(); /** @short get object name. If star has no name, generate a name based on catalog number. * @param translated set to true if the translated name is required. */ QString getObjectName(const SkyObject *o, bool translated = true); /** * @return true if the selected list contains the given object */ inline bool contains(const SkyObject *o, bool session = false) { return bool(findObject(o, session)); } QSharedPointer findObject(const SkyObject *o, bool session = false); public slots: /** @short add a new object to list *@p o pointer to the object to add to the list *@p session flag toggle adding the object to the session list *@p update flag to toggle the call of slotSaveList */ void slotAddObject(const SkyObject *o = nullptr, bool session = false, bool update = false); /** @short Remove skyobjects which are highlighted in the *observing list tool from the observing list. */ void slotRemoveSelectedObjects(); /** @short Remove skyobject from the observing list. *@p o pointer to the SkyObject to be removed. *@p session flag to tell it whether to remove the object *from the sessionlist or from the wishlist *@p update flag to toggle the call of slotSaveList *Use SkyMap::clickedObject() if o is nullptr (default) */ void slotRemoveObject(const SkyObject *o = nullptr, bool session = false, bool update = false); /** @short center the selected object in the display */ void slotCenterObject(); /** @short slew the telescope to the selected object */ void slotSlewToObject(); /** * @brief slotAddToEkosScheduler Add object to Ekos scheduler */ void slotAddToEkosScheduler(); /** @short Show the details window for the selected object */ void slotDetails(); /** @short Show the Altitude vs Time for selecteld objects */ void slotAVT(); /** @short Open the WUT dialog */ void slotWUT(); /** @short Add the object to the Session List */ void slotAddToSession(); /** @short Open the Find Dialog */ void slotFind(); /** @short Tasks needed when changing the selected object *Save the user log of the previous selected object, *find the new selected object in the obsList, and *show the notes associated with the new selected object */ void slotNewSelection(); /** @short load an observing list from disk. */ void slotOpenList(); /** @short save the current observing list to disk. */ void slotSaveList(); /** @short Load the Wish list from disk. */ void slotLoadWishList(); /** @short save the current observing session plan to disk, specify filename. */ void slotSaveSessionAs(bool nativeSave = true); /** @short save the current session */ void slotSaveSession(bool nativeSave = true); /** @short construct a new observing list using the wizard. */ void slotWizard(); /** @short toggle the setEnabled flags according to current view *set the m_currentItem to nullptr and clear selections *@p index captures the integer value sent by the signal *which is the currentIndex of the table */ void slotChangeTab(int index); /** @short Opens the Location dialog to set the GeoLocation *for the sessionlist. */ void slotLocation(); /** @short Updates the tableviews for the new geolocation and date */ void slotUpdate(); /** @short Takes the time from the QTimeEdit box and sets it as the *time parameter in the tableview of the SessionList. */ void slotSetTime(); /** @short Downloads the corresponding DSS or SDSS image from the web and *displays it */ void slotGetImage(bool _dss = false, const SkyObject *o = nullptr); void slotSearchImage(); /** @short Downloads the images of all the objects in the session list *Note: This downloads the SDSS image, checks if the size is > default image *and gets the DSS image if thats the case */ void slotSaveAllImages(); /** @short saves the image syncronously from a given URL into a given file *@p url the url from which the image has to be downloaded *@p filename the file onto which the url has to be copied to *NOTE: This is not a generic image saver, it is specific to the current object */ void saveImage(QUrl url, QString filename, const SkyObject *o = nullptr); /** @short Shows the image in a ImageViewer window. */ void slotImageViewer(); /** @short Remove the current image */ void slotDeleteCurrentImage(); /** @short Removes all the save DSS/SDSS images from the disk. */ void slotDeleteAllImages(); /** @short download the DSS image and show it */ void slotDSS() { slotGetImage(true); } /** *@short Present the user with options to get the right DSS image for the job */ void slotCustomDSS(); /** @short Export a target list to the oal compliant format */ void slotOALExport(); void slotAddVisibleObj(); /** * @short Show the eyepiece field view */ void slotEyepieceView(); /** * @short Recalculate and update the values of the altitude in the wishlist for the current time */ void slotUpdateAltitudes(); /** * @brief slotClearList Remove all objects from current list */ void slotClearList(); protected slots: void slotClose(); void downloadReady(bool success); protected: void showEvent(QShowEvent *) override; private: /** * @short Return the active list * @return The session list or the wish list depending on which tab is currently being viewed. */ inline QList> &getActiveList() { return ((sessionView) ? m_SessionList : m_WishList); } /** * @short Return the active itemmodel * @return the session model or the wishlist model depending on which tab is currently being viewed. */ inline QStandardItemModel *getActiveModel() const { return (sessionView ? m_SessionModel.get() : m_WishListModel.get()); } /** * @short Return the active sort model * @return the session sort model or the wishlist sort model depending on which tab is currently being viewed. */ inline QSortFilterProxyModel *getActiveSortModel() const { return (sessionView ? m_SessionSortModel.get() : m_WishListSortModel.get()); } /** * @short Return the active view * @return the active view in the UI -- session view or wishlist view depending on which one is active. */ inline QTableView *getActiveView() const { return ((sessionView) ? (ui->SessionView) : (ui->WishListView)); } /** * @short Get the currently selected item indexes * @return a QModelIndexList containing the selected rows in the active QTableView */ inline QModelIndexList getSelectedItems() const { return getActiveView()->selectionModel()->selectedRows(); } std::unique_ptr ksal; ObservingListUI *ui { nullptr }; QList> m_WishList, m_SessionList; SkyObject *LogObject { nullptr }; SkyObject *m_CurrentObject { nullptr }; bool isModified { false }; bool sessionView { false }; bool dss { false }; bool singleSelection { false }; bool showScope { false }; bool noSelection { false }; QString m_listFileName, m_currentImageFileName, m_currentThumbImageFileName; KStarsDateTime dt; GeoLocation *geo { nullptr }; std::unique_ptr m_WishListModel; std::unique_ptr m_SessionModel; std::unique_ptr m_WishListSortModel; std::unique_ptr m_SessionSortModel; QHash TimeHash; std::unique_ptr pmenu; KSDssDownloader *m_dl { nullptr }; QHash ImagePreviewHash; QPixmap m_NoImagePixmap; QTimer *m_altitudeUpdater { nullptr }; std::function m_altCostHelper; bool m_initialWishlistLoad { false }; }; diff --git a/kstars/tools/obslistpopupmenu.cpp b/kstars/tools/obslistpopupmenu.cpp index d199d61ca..87e624ae8 100644 --- a/kstars/tools/obslistpopupmenu.cpp +++ b/kstars/tools/obslistpopupmenu.cpp @@ -1,95 +1,90 @@ /*************************************************************************** obslistpopupmenu.h - K Desktop Planetarium ------------------- begin : Sun July 5 2009 copyright : (C) 2008 by Prakash Mohan email : prakash.mohan@kdemail.net ***************************************************************************/ /*************************************************************************** * * * 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 "obslistpopupmenu.h" #include "config-kstars.h" - #include "kstars.h" #include "kstarsdata.h" #include "observinglist.h" ObsListPopupMenu::ObsListPopupMenu() : QMenu(nullptr) { } -ObsListPopupMenu::~ObsListPopupMenu() -{ -} - void ObsListPopupMenu::initPopupMenu(bool sessionView, bool multiSelection, bool showScope) { KStarsData *ksdata = KStarsData::Instance(); clear(); //Insert item for adding the object to the session view if (!sessionView) { addAction(i18n("Add to session plan"), ksdata->observingList(), SLOT(slotAddToSession())); addAction(i18n("Add objects visible tonight to session plan"), ksdata->observingList(), SLOT(slotAddVisibleObj())); #ifdef HAVE_INDI addAction(i18n("Add to Ekos Scheduler"), ksdata->observingList(), SLOT(slotAddToEkosScheduler())); #endif } addSeparator(); if (!multiSelection) addAction(i18n("Center"), ksdata->observingList(), SLOT(slotCenterObject())); //Insert item for centering on object if (!multiSelection && showScope) // Insert item for slewing telescope addAction(i18nc("Show the selected object in the telescope", "Scope"), ksdata->observingList(), SLOT(slotSlewToObject())); addSeparator(); if (!multiSelection) { addAction(i18nc("Show Detailed Information Dialog", "Details"), ksdata->observingList(), SLOT(slotDetails())); // Insert item for showing details dialog addAction(i18n("Eyepiece view"), ksdata->observingList(), SLOT(slotEyepieceView())); // Insert item for showing eyepiece view } //Insert item for opening the Altitude vs time dialog addAction(i18n("Altitude vs. Time"), ksdata->observingList(), SLOT(slotAVT())); addSeparator(); //Insert item for dowloading different images if (!multiSelection) { if (ksdata->observingList()->currentObject() != nullptr && !ksdata->observingList()->currentObject()->isSolarSystem()) { addAction(i18n("Show SDSS image"), ksdata->observingList(), SLOT(slotGetImage())); addAction(i18n("Show DSS image"), ksdata->observingList(), SLOT(slotDSS())); addAction(i18n("Customized DSS download"), ksdata->observingList(), SLOT(slotCustomDSS())); } addAction(i18n("Show images from web "), ksdata->observingList(), SLOT(slotSearchImage())); addSeparator(); } //Insert item for Removing the object(s) if (!sessionView) addAction(i18n("Remove from WishList"), ksdata->observingList(), SLOT(slotRemoveSelectedObjects())); else addAction(i18n("Remove from Session Plan"), ksdata->observingList(), SLOT(slotRemoveSelectedObjects())); } diff --git a/kstars/tools/obslistpopupmenu.h b/kstars/tools/obslistpopupmenu.h index 5f506cf67..273ae0206 100644 --- a/kstars/tools/obslistpopupmenu.h +++ b/kstars/tools/obslistpopupmenu.h @@ -1,48 +1,46 @@ /*************************************************************************** obslistpopupmenu.h - K Desktop Planetarium ------------------- begin : Sun July 5 2009 copyright : (C) 2008 by Prakash Mohan email : prakash.mohan@kdemail.net ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include /** * @class ObsListPopupMenu * The Popup Menu for the observing list in KStars. The menu is sensitive to the * type of selection in the observing list. * @author Prakash Mohan * @version 1.0 */ class ObsListPopupMenu : public QMenu { Q_OBJECT public: - /** Default constructor*/ ObsListPopupMenu(); - /** Destructor (empty)*/ - ~ObsListPopupMenu() override; + virtual ~ObsListPopupMenu() override = default; /** * Initialize the popup menus. * @short initializes the popup menu based on the kind of selection in the observation planner * @param sessionView true if we are viewing the session, false if we are viewing the wish list * @param multiSelection true if multiple objects were selected, false if a single object was selected * @param showScope true if we should show INDI/telescope-related options, false otherwise. * @note Showing this popup-menu without a selection may lead to crashes. */ void initPopupMenu(bool sessionView, bool multiSelection, bool showScope); }; diff --git a/kstars/tools/obslistwizard.cpp b/kstars/tools/obslistwizard.cpp index 85ddb2f0b..c4c41bfc2 100644 --- a/kstars/tools/obslistwizard.cpp +++ b/kstars/tools/obslistwizard.cpp @@ -1,949 +1,945 @@ /*************************************************************************** obslistwizard.cpp - Display overhead view of the solar system ------------------- begin : Thu 23 Jun 2005 copyright : (C) 2005 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "obslistwizard.h" #include "geolocation.h" #include "kstarsdata.h" #include "dialogs/locationdialog.h" #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/deepskyobject.h" ObsListWizardUI::ObsListWizardUI(QWidget *p) : QFrame(p) { setupUi(this); } ObsListWizard::ObsListWizard(QWidget *ksparent) : QDialog(ksparent) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif olw = new ObsListWizardUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(olw); setLayout(mainLayout); setWindowTitle(i18n("Observing List Wizard")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); nextB = new QPushButton(i18n("&Next >")); nextB->setDefault(true); backB = new QPushButton(i18n("< &Back")); backB->setEnabled(false); buttonBox->addButton(backB, QDialogButtonBox::ActionRole); buttonBox->addButton(nextB, QDialogButtonBox::ActionRole); mainLayout->addWidget(buttonBox); connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage())); connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotApplyFilters())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(olw->AllButton, SIGNAL(clicked()), this, SLOT(slotAllButton())); connect(olw->NoneButton, SIGNAL(clicked()), this, SLOT(slotNoneButton())); connect(olw->DeepSkyButton, SIGNAL(clicked()), this, SLOT(slotDeepSkyButton())); connect(olw->SolarSystemButton, SIGNAL(clicked()), this, SLOT(slotSolarSystemButton())); connect(olw->LocationButton, SIGNAL(clicked()), this, SLOT(slotChangeLocation())); //Update the count of objects when the user asks for it connect(olw->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateObjectCount())); // Enable the update count button when certain elements are changed connect(olw->TypeList, SIGNAL(itemSelectionChanged()), this, SLOT(slotObjectCountDirty())); connect(olw->ConstellationList, SIGNAL(itemSelectionChanged()), this, SLOT(slotObjectCountDirty())); connect(olw->RAMin, SIGNAL(editingFinished()), this, SLOT(slotParseRegion())); connect(olw->RAMax, SIGNAL(editingFinished()), this, SLOT(slotParseRegion())); connect(olw->DecMin, SIGNAL(editingFinished()), this, SLOT(slotParseRegion())); connect(olw->DecMax, SIGNAL(editingFinished()), this, SLOT(slotParseRegion())); connect(olw->RA, SIGNAL(editingFinished()), this, SLOT(slotParseRegion())); connect(olw->Dec, SIGNAL(editingFinished()), this, SLOT(slotParseRegion())); connect(olw->Radius, SIGNAL(editingFinished()), this, SLOT(slotObjectCountDirty())); connect(olw->Date, SIGNAL(dateChanged(QDate)), this, SLOT(slotObjectCountDirty())); connect(olw->Mag, SIGNAL(valueChanged(double)), this, SLOT(slotObjectCountDirty())); connect(olw->IncludeNoMag, SIGNAL(clicked()), this, SLOT(slotObjectCountDirty())); connect(olw->timeTo, SIGNAL(timeChanged(QTime)), this, SLOT(slotObjectCountDirty())); connect(olw->timeFrom, SIGNAL(timeChanged(QTime)), this, SLOT(slotObjectCountDirty())); connect(olw->minAlt, SIGNAL(valueChanged(double)), this, SLOT(slotObjectCountDirty())); connect(olw->maxAlt, SIGNAL(valueChanged(double)), this, SLOT(slotObjectCountDirty())); connect(olw->SelectByDate, SIGNAL(clicked()), this, SLOT(slotToggleDateWidgets())); connect(olw->SelectByMagnitude, SIGNAL(clicked()), this, SLOT(slotToggleMagWidgets())); geo = KStarsData::Instance()->geo(); olw->LocationButton->setText(geo->fullName()); olw->Date->setDate(KStarsDateTime::currentDateTime().date()); olw->timeFrom->setTime(QTime(18, 0)); olw->timeTo->setTime(QTime(23, 59)); initialize(); } -ObsListWizard::~ObsListWizard() -{ -} - void ObsListWizard::initialize() { KStarsData *data = KStarsData::Instance(); olw->olwStack->setCurrentIndex(0); //Populate the list of constellations foreach (SkyObject *p, data->skyComposite()->constellationNames()) olw->ConstellationList->addItem(p->name()); //unSelect all object types olw->TypeList->clearSelection(); olw->Mag->setMinimum(-5.0); olw->Mag->setMaximum(20.0); olw->Mag->setValue(6.0); olw->RA->setDegType(false); olw->RAMin->setDegType(false); olw->RAMax->setDegType(false); //Initialize object counts ObjectCount = 0; //number of objects in observing list StarCount = data->skyComposite()->stars().size(); //total number of stars PlanetCount = 10; //Sun, Moon, 8 planets AsteroidCount = data->skyComposite()->asteroids().size(); //total number of asteroids CometCount = data->skyComposite()->comets().size(); //total number of comets //DeepSkyObjects OpenClusterCount = 0; GlobClusterCount = 0; GasNebCount = 0; PlanNebCount = 0; GalaxyCount = 0; foreach (DeepSkyObject *o, data->skyComposite()->deepSkyObjects()) { if (o->type() == SkyObject::GALAXY) ++GalaxyCount; //most deepsky obj are galaxies, so check them first else if (o->type() == SkyObject::STAR || o->type() == SkyObject::CATALOG_STAR) ++StarCount; else if (o->type() == SkyObject::OPEN_CLUSTER) ++OpenClusterCount; else if (o->type() == SkyObject::GLOBULAR_CLUSTER) ++GlobClusterCount; else if (o->type() == SkyObject::GASEOUS_NEBULA || o->type() == SkyObject::SUPERNOVA_REMNANT) ++GasNebCount; else if (o->type() == SkyObject::PLANETARY_NEBULA) ++PlanNebCount; } } bool ObsListWizard::isItemSelected(const QString &name, QListWidget *listWidget, bool *ok) { /*QList items = listWidget->findItems(name, Qt::MatchContains); if (ok) *ok = items.size(); return items.size() && listWidget->isItemSelected(items[0]); ;*/ foreach(QListWidgetItem *item, listWidget->selectedItems()) { if (item->text().compare(name, Qt::CaseInsensitive) == 0) { if (ok) *ok = true; return true; } } if (ok) *ok = false; return false; } void ObsListWizard::setItemSelected(const QString &name, QListWidget *listWidget, bool value, bool *ok) { QList items = listWidget->findItems(name, Qt::MatchContains); if (ok) *ok = items.size(); if (items.size()) listWidget->setItemSelected(items[0], value); } //Advance to the next page in the stack. However, on page 2 the user //selects what regional filter they want to use, and this determnes //what the page following page 2 should be: // + Constellation(s): the next page index is 3 // + Rectangular region: the next page index is 4 // + Circular region: the next page index is 5 // + No region selected (a.k.a. "all over the sky"): the next page index is 6 // //Also, if the current page index is 3 or 4, then the next page should be 6. // //NOTE: the page indexes are hard-coded here, which isn't ideal. However, //There's no easy way to access the pointers of widgets in the stack //if you didn't save them at the start. void ObsListWizard::slotNextPage() { int NextPage = olw->olwStack->currentIndex() + 1; if (olw->olwStack->currentIndex() == 2) { //On the Region select page. Determine what //the next page index should be. //No need to handle "by constellation, it's already currentIndex + 1. if (isItemSelected(i18n("in a rectangular region"), olw->RegionList)) NextPage = 4; if (isItemSelected(i18n("in a circular region"), olw->RegionList)) NextPage = 5; if (isItemSelected(i18n("all over the sky"), olw->RegionList)) NextPage = 6; } if (olw->olwStack->currentIndex() == 3 || olw->olwStack->currentIndex() == 4) NextPage = 6; olw->olwStack->setCurrentIndex(NextPage); if (olw->olwStack->currentIndex() == olw->olwStack->count() - 1) nextB->setEnabled(false); backB->setEnabled(true); } //Advance to the previous page in the stack. However, because the //path through the wizard branches depending on the user's choice of //Region filter, the previous page is not always currentPage-1. //Specifically, if the current page index is 4, 5, or 6, then the Previous //page index should be 2 rather than currentIndex-1. void ObsListWizard::slotPrevPage() { int PrevPage = olw->olwStack->currentIndex() - 1; if (olw->olwStack->currentIndex() == 4 || olw->olwStack->currentIndex() == 5 || olw->olwStack->currentIndex() == 6) PrevPage = 2; olw->olwStack->setCurrentIndex(PrevPage); if (olw->olwStack->currentIndex() == 0) backB->setEnabled(false); nextB->setEnabled(true); } void ObsListWizard::slotAllButton() { for (int i = 0; i < olw->TypeList->count(); ++i) olw->TypeList->setItemSelected(olw->TypeList->item(i), true); } void ObsListWizard::slotNoneButton() { olw->TypeList->clearSelection(); } void ObsListWizard::slotDeepSkyButton() { olw->TypeList->clearSelection(); setItemSelected(i18n("Open clusters"), olw->TypeList, true); setItemSelected(i18n("Globular clusters"), olw->TypeList, true); setItemSelected(i18n("Gaseous nebulae"), olw->TypeList, true); setItemSelected(i18n("Planetary nebulae"), olw->TypeList, true); setItemSelected(i18n("Galaxies"), olw->TypeList, true); } void ObsListWizard::slotSolarSystemButton() { olw->TypeList->clearSelection(); setItemSelected(i18n("Sun, moon, planets"), olw->TypeList, true); setItemSelected(i18n("Comets"), olw->TypeList, true); setItemSelected(i18n("Asteroids"), olw->TypeList, true); } void ObsListWizard::slotChangeLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { //set geographic location if (ld->selectedCity()) { geo = ld->selectedCity(); olw->LocationButton->setText(geo->fullName()); } } delete ld; } void ObsListWizard::slotToggleDateWidgets() { olw->Date->setEnabled(olw->SelectByDate->isChecked()); olw->LocationButton->setEnabled(olw->SelectByDate->isChecked()); olw->timeTo->setEnabled(olw->SelectByDate->isChecked()); olw->timeFrom->setEnabled(olw->SelectByDate->isChecked()); olw->minAlt->setEnabled(olw->SelectByDate->isChecked()); olw->maxAlt->setEnabled(olw->SelectByDate->isChecked()); // slotUpdateObjectCount(); slotObjectCountDirty(); } void ObsListWizard::slotToggleMagWidgets() { olw->Mag->setEnabled(olw->SelectByMagnitude->isChecked()); olw->IncludeNoMag->setEnabled(olw->SelectByMagnitude->isChecked()); slotObjectCountDirty(); // slotUpdateObjectCount(); } void ObsListWizard::slotParseRegion() { if (sender()->objectName() == "RAMin" || sender()->objectName() == "RAMax" || sender()->objectName() == "DecMin" || sender()->objectName() == "DecMax") { if (!olw->RAMin->isEmpty() && !olw->RAMax->isEmpty() && !olw->DecMin->isEmpty() && !olw->DecMax->isEmpty()) { bool rectOk = false; xRect1 = 0.0; xRect2 = 0.0; yRect1 = 0.0; yRect2 = 0.0; xRect1 = olw->RAMin->createDms(false, &rectOk).Hours(); if (rectOk) xRect2 = olw->RAMax->createDms(false, &rectOk).Hours(); if (rectOk) yRect1 = olw->DecMin->createDms(true, &rectOk).Degrees(); if (rectOk) yRect2 = olw->DecMax->createDms(true, &rectOk).Degrees(); if (xRect2 == 0.0) xRect2 = 24.0; if (!rectOk) { // qWarning() << i18n( "Illegal rectangle specified, no region selection possible." ) ; return; } //Make sure yRect1 < yRect2. if (yRect1 > yRect2) { double temp = yRect2; yRect2 = yRect1; yRect1 = temp; } //If xRect1 > xRect2, we may need to swap the two values, or subtract 24h from xRect1. if (xRect1 > xRect2) { if (xRect1 - xRect2 > 12.0) //the user probably wants a region that straddles 0h { xRect1 -= 24.0; } else //the user probably wants xRect2 to be the lower limit { double temp = xRect2; xRect2 = xRect1; xRect1 = temp; } } // slotUpdateObjectCount(); slotObjectCountDirty(); } } else if (!olw->RA->isEmpty() && !olw->Dec->isEmpty() && !olw->Radius->isEmpty()) { bool circOk; dms ra = olw->RA->createDms(false, &circOk); dms dc; if (circOk) dc = olw->Dec->createDms(true, &circOk); if (circOk) { pCirc.set(ra, dc); rCirc = olw->Radius->createDms(true, &circOk).Degrees(); } else { qWarning() << i18n("Illegal circle specified, no region selection possible."); return; } // slotUpdateObjectCount(); slotObjectCountDirty(); } } void ObsListWizard::slotObjectCountDirty() { olw->updateButton->setDisabled(false); } void ObsListWizard::slotUpdateObjectCount() { QApplication::setOverrideCursor(Qt::WaitCursor); ObjectCount = 0; if (isItemSelected(i18n("Stars"), olw->TypeList)) ObjectCount += StarCount; if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList)) ObjectCount += PlanetCount; if (isItemSelected(i18n("Comets"), olw->TypeList)) ObjectCount += CometCount; if (isItemSelected(i18n("Asteroids"), olw->TypeList)) ObjectCount += AsteroidCount; if (isItemSelected(i18n("Galaxies"), olw->TypeList)) ObjectCount += GalaxyCount; if (isItemSelected(i18n("Open clusters"), olw->TypeList)) ObjectCount += OpenClusterCount; if (isItemSelected(i18n("Globular clusters"), olw->TypeList)) ObjectCount += GlobClusterCount; if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList)) ObjectCount += GasNebCount; if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList)) ObjectCount += PlanNebCount; applyFilters(false); //false = only adjust counts, do not build list QApplication::restoreOverrideCursor(); olw->updateButton->setDisabled(true); } void ObsListWizard::applyFilters(bool doBuildList) { bool filterPass = true; KStarsData *data = KStarsData::Instance(); if (doBuildList) obsList().clear(); //We don't need to call applyRegionFilter() if no region filter is selected, *and* //we are just counting items (i.e., doBuildList is false) bool needRegion = true; if (!doBuildList && isItemSelected(i18n("all over the sky"), olw->RegionList)) needRegion = false; double maglimit = 100.; if (olw->SelectByMagnitude->isChecked()) maglimit = olw->Mag->value(); //Stars if (isItemSelected(i18n("Stars"), olw->TypeList)) { const QList &starList = data->skyComposite()->stars(); int starIndex(starList.size()); if (maglimit < 100.) { //Stars are sorted by mag, so use binary search algo to find index of faintest mag int low(0), high(starList.size() - 1), middle(high); while (low < high) { middle = (low + high) / 2; if (maglimit == starList.at(middle)->mag()) break; if (maglimit < starList.at(middle)->mag()) high = middle - 1; if (maglimit > starList.at(middle)->mag()) low = middle + 1; } //now, the star at "middle" has the right mag, but we want the *last* star that has this mag. for (starIndex = middle + 1; starIndex < starList.size(); ++starIndex) { if (starList.at(starIndex)->mag() > maglimit) break; } } //DEBUG qDebug() << QString("starIndex for mag %1: %2").arg(maglimit).arg(starIndex) << endl; if (!doBuildList) { //reduce StarCount by appropriate amount ObjectCount -= StarCount; ObjectCount += starIndex; } for (int i = 0; i < starIndex; ++i) { SkyObject *o = (SkyObject *)(starList[i]); // JM 2012-10-22: Skip unnamed stars if (o->name() == "star") { if (!doBuildList) --ObjectCount; continue; } if (needRegion) filterPass = applyRegionFilter(o, doBuildList, !doBuildList); //Filter objects visible from geo at Date if region filter passes if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList, !doBuildList); } } //Sun, Moon, Planets if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList)) { if (maglimit < data->skyComposite()->findByName("Sun")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName("Sun"), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName("Sun"), doBuildList); if (maglimit < data->skyComposite()->findByName("Moon")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName("Moon"), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName("Moon"), doBuildList); if (maglimit < data->skyComposite()->findByName("Mercury")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList); if (maglimit < data->skyComposite()->findByName("Venus")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList); if (maglimit < data->skyComposite()->findByName("Mars")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList); if (maglimit < data->skyComposite()->findByName("Jupiter")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList); if (maglimit < data->skyComposite()->findByName("Saturn")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList); if (maglimit < data->skyComposite()->findByName("Uranus")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList); if (maglimit < data->skyComposite()->findByName("Neptune")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList); if (maglimit < data->skyComposite()->findByName("Pluto")->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList); } //Deep sky objects bool dso = (isItemSelected(i18n("Open clusters"), olw->TypeList) || isItemSelected(i18n("Globular clusters"), olw->TypeList) || isItemSelected(i18n("Gaseous nebulae"), olw->TypeList) || isItemSelected(i18n("Planetary nebulae"), olw->TypeList) || isItemSelected(i18n("Galaxies"), olw->TypeList)); if (dso) { //Don't need to do anything if we are just counting objects and not //filtering by region or magnitude if (needRegion || olw->SelectByMagnitude->isChecked() || olw->SelectByDate->isChecked()) { foreach (DeepSkyObject *o, data->skyComposite()->deepSkyObjects()) { //Skip unselected object types bool typeSelected = false; // if ( (o->type() == SkyObject::STAR || o->type() == SkyObject::CATALOG_STAR) && // isItemSelected( i18n( "Stars" ), olw->TypeList ) ) // typeSelected = true; switch (o->type()) { case SkyObject::OPEN_CLUSTER: if (isItemSelected(i18n("Open clusters"), olw->TypeList)) typeSelected = true; break; case SkyObject::GLOBULAR_CLUSTER: if (isItemSelected(i18n("Globular clusters"), olw->TypeList)) typeSelected = true; break; case SkyObject::GASEOUS_NEBULA: case SkyObject::SUPERNOVA_REMNANT: if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList)) typeSelected = true; break; case SkyObject::PLANETARY_NEBULA: if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList)) typeSelected = true; break; case SkyObject::GALAXY: if (isItemSelected(i18n("Galaxies"), olw->TypeList)) typeSelected = true; break; } if (!typeSelected) continue; if (olw->SelectByMagnitude->isChecked()) { if (o->mag() > 90.) { if (olw->IncludeNoMag->isChecked()) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } else { if (o->mag() <= maglimit) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } } else { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } } } } //Comets if (isItemSelected(i18n("Comets"), olw->TypeList)) { foreach (SkyObject *o, data->skyComposite()->comets()) { if (olw->SelectByMagnitude->isChecked()) { if (o->mag() > 90.) { if (olw->IncludeNoMag->isChecked()) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } else { if (o->mag() <= maglimit) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } } else { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } } } //Asteroids if (isItemSelected(i18n("Asteroids"), olw->TypeList)) { foreach (SkyObject *o, data->skyComposite()->asteroids()) { if (olw->SelectByMagnitude->isChecked()) { if (o->mag() > 90.) { if (olw->IncludeNoMag->isChecked()) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } else { if (o->mag() <= maglimit) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } } else { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } } } //Update the object count label if (doBuildList) ObjectCount = obsList().size(); olw->CountLabel->setText(i18np("Your observing list currently has 1 object", "Your observing list currently has %1 objects", ObjectCount)); } bool ObsListWizard::applyRegionFilter(SkyObject *o, bool doBuildList, bool doAdjustCount) { //select by constellation if (isItemSelected(i18n("by constellation"), olw->RegionList)) { QString c = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(o); if (isItemSelected(c, olw->ConstellationList)) { if (doBuildList) obsList().append(o); return true; } else if (doAdjustCount) { --ObjectCount; return false; } else return false; } //select by rectangular region else if (isItemSelected(i18n("in a rectangular region"), olw->RegionList)) { double ra = o->ra().Hours(); double dec = o->dec().Degrees(); bool addObject = false; if (dec >= yRect1 && dec <= yRect2) { if (xRect1 < 0.0) { addObject = ra >= xRect1 + 24.0 || ra <= xRect2; } else { addObject = ra >= xRect1 && ra <= xRect2; } } if (addObject) { if (doBuildList) obsList().append(o); return true; } else { if (doAdjustCount) --ObjectCount; return false; } } //select by circular region //make sure circ region data are valid else if (isItemSelected(i18n("in a circular region"), olw->RegionList)) { if (o->angularDistanceTo(&pCirc).Degrees() < rCirc) { if (doBuildList) obsList().append(o); return true; } else if (doAdjustCount) { --ObjectCount; return false; } else return false; } //No region filter, just add the object else if (doBuildList) { obsList().append(o); } return true; } bool ObsListWizard::applyObservableFilter(SkyObject *o, bool doBuildList, bool doAdjustCount) { SkyPoint p = *o; //Check altitude of object every hour from 18:00 to midnight //If it's ever above 15 degrees, flag it as visible KStarsDateTime Evening(olw->Date->date(), QTime(18, 0, 0)); KStarsDateTime Midnight(olw->Date->date().addDays(1), QTime(0, 0, 0)); double minAlt = 15, maxAlt = 90; // Or use user-selected values, if they're valid if (olw->timeFrom->time() < olw->timeTo->time()) { Evening.setTime(olw->timeFrom->time()); Midnight.setTime(olw->timeTo->time()); Midnight.setDate(olw->Date->date()); } if (olw->minAlt->value() < olw->maxAlt->value()) { minAlt = olw->minAlt->value(); maxAlt = olw->maxAlt->value(); } bool visible = false; for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0)) { dms LST = geo->GSTtoLST(t.gst()); p.EquatorialToHorizontal(&LST, geo->lat()); if (p.alt().Degrees() >= minAlt && p.alt().Degrees() <= maxAlt) { visible = true; break; } } if (visible) return true; if (doAdjustCount) --ObjectCount; if (doBuildList) obsList().takeAt(obsList().indexOf(o)); return false; } diff --git a/kstars/tools/obslistwizard.h b/kstars/tools/obslistwizard.h index 2bf17fa93..c5ed4111d 100644 --- a/kstars/tools/obslistwizard.h +++ b/kstars/tools/obslistwizard.h @@ -1,122 +1,120 @@ /*************************************************************************** obslistwizard.h - Display overhead view of the solar system ------------------- begin : Thu 23 Jun 2005 copyright : (C) 2005 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "ui_obslistwizard.h" #include "skyobjects/skypoint.h" #include class QListWidget; class QPushButton; class SkyObject; class GeoLocation; class ObsListWizardUI : public QFrame, public Ui::ObsListWizard { Q_OBJECT public: explicit ObsListWizardUI(QWidget *p); }; /** * @class ObsListWizard * @short Wizard for constructing observing lists * * @author Jason Harris */ class ObsListWizard : public QDialog { Q_OBJECT public: - /** @short Constructor */ explicit ObsListWizard(QWidget *parent); - /** @short Destructor */ - ~ObsListWizard() override; + virtual ~ObsListWizard() override = default; /** @return reference to QPtrList of objects selected by the wizard */ QList &obsList() { return ObsList; } private slots: void slotNextPage(); void slotPrevPage(); void slotAllButton(); void slotNoneButton(); void slotDeepSkyButton(); void slotSolarSystemButton(); void slotChangeLocation(); void slotToggleDateWidgets(); void slotToggleMagWidgets(); void slotParseRegion(); /** @short Construct the observing list by applying the selected filters */ void slotObjectCountDirty(); void slotUpdateObjectCount(); void slotApplyFilters() { applyFilters(true); } private: void initialize(); void applyFilters(bool doBuildList); /** @return true if the object passes the filter region constraints, false otherwise.*/ bool applyRegionFilter(SkyObject *o, bool doBuildList, bool doAdjustCount = true); bool applyObservableFilter(SkyObject *o, bool doBuildList, bool doAdjustCount = true); /** * Convenience function for safely getting the selected state of a QListWidget item by name. * QListWidget has no method for easily selecting a single item based on its text. * @return true if the named QListWidget item is selected. * @param name the QListWidget item to be queried is the one whose text matches this string * @param listWidget pointer to the QListWidget whose item is to be queried * @param ok pointer to a bool, which if present will return true if a matching list item was found */ bool isItemSelected(const QString &name, QListWidget *listWidget, bool *ok = nullptr); /** * Convenience function for safely setting the selected state of a QListWidget item by name. * QListWidget has no method for easily selecting a single item based on its text. * @param name the QListWidget item to be (de)selected is the one whose text matches this string * @param listWidget pointer to the QListWidget whose item is to be (de)selected * @param value set the item's selected state to this bool value * @param ok pointer to a bool, which if present will return true if a matching list item was found */ void setItemSelected(const QString &name, QListWidget *listWidget, bool value, bool *ok = nullptr); QList ObsList; ObsListWizardUI *olw { nullptr }; uint ObjectCount { 0 }; uint StarCount { 0 }; uint PlanetCount { 0 }; uint CometCount { 0 }; uint AsteroidCount { 0 }; uint GalaxyCount { 0 }; uint OpenClusterCount { 0 }; uint GlobClusterCount { 0 }; uint GasNebCount { 0 }; uint PlanNebCount { 0 }; double xRect1 { 0 }; double xRect2 { 0 }; double yRect1 { 0 }; double yRect2 { 0 }; double rCirc { 0 }; SkyPoint pCirc; GeoLocation *geo { nullptr }; QPushButton *nextB { nullptr }; QPushButton *backB { nullptr }; }; diff --git a/kstars/tools/scriptbuilder.cpp b/kstars/tools/scriptbuilder.cpp index 92f4d3b14..04f82ce93 100644 --- a/kstars/tools/scriptbuilder.cpp +++ b/kstars/tools/scriptbuilder.cpp @@ -1,2874 +1,2870 @@ /*************************************************************************** scriptbuilder.cpp - description ------------------- begin : Thu Apr 17 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "scriptbuilder.h" #include "kspaths.h" #include "scriptfunction.h" #include "kstars.h" #include "kstarsdata.h" #include "skymap.h" #include "kstarsdatetime.h" #include "dialogs/finddialog.h" #include "dialogs/locationdialog.h" #include "widgets/dmsbox.h" #include "widgets/timespinbox.h" #include "widgets/timestepbox.h" #include #include #include #include #include #include #include #include #include #include #include #include #include OptionsTreeViewWidget::OptionsTreeViewWidget(QWidget *p) : QFrame(p) { setupUi(this); } OptionsTreeView::OptionsTreeView(QWidget *p) : QDialog(p) { otvw.reset(new OptionsTreeViewWidget(this)); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(otvw.get()); setLayout(mainLayout); setWindowTitle(i18n("Options")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); setModal(false); } -OptionsTreeView::~OptionsTreeView() -{ -} - void OptionsTreeView::resizeColumns() { //Size each column to the maximum width of items in that column int maxwidth[3] = { 0, 0, 0 }; QFontMetrics qfm = optionsList()->fontMetrics(); for (int i = 0; i < optionsList()->topLevelItemCount(); ++i) { QTreeWidgetItem *topitem = optionsList()->topLevelItem(i); topitem->setExpanded(true); for (int j = 0; j < topitem->childCount(); ++j) { QTreeWidgetItem *child = topitem->child(j); for (int icol = 0; icol < 3; ++icol) { child->setExpanded(true); int w = qfm.width(child->text(icol)) + 4; if (icol == 0) { w += 2 * optionsList()->indentation(); } if (w > maxwidth[icol]) { maxwidth[icol] = w; } } } } for (int icol = 0; icol < 3; ++icol) { //DEBUG qDebug() << QString("max width of column %1: %2").arg(icol).arg(maxwidth[icol]) << endl; optionsList()->setColumnWidth(icol, maxwidth[icol]); } } ScriptNameWidget::ScriptNameWidget(QWidget *p) : QFrame(p) { setupUi(this); } ScriptNameDialog::ScriptNameDialog(QWidget *p) : QDialog(p) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif snw = new ScriptNameWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(snw); setLayout(mainLayout); setWindowTitle(i18n("Script Data")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); okB = buttonBox->button(QDialogButtonBox::Ok); connect(snw->ScriptName, SIGNAL(textChanged(QString)), this, SLOT(slotEnableOkButton())); } ScriptNameDialog::~ScriptNameDialog() { delete snw; } void ScriptNameDialog::slotEnableOkButton() { okB->setEnabled(!snw->ScriptName->text().isEmpty()); } ScriptBuilderUI::ScriptBuilderUI(QWidget *p) : QFrame(p) { setupUi(this); } ScriptBuilder::ScriptBuilder(QWidget *parent) : QDialog(parent), UnsavedChanges(false), checkForChanges(true), currentFileURL(), currentDir(QDir::homePath()), currentScriptName(), currentAuthor() { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif ks = (KStars *)parent; sb = new ScriptBuilderUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(sb); setLayout(mainLayout); setWindowTitle(i18n("Script Builder")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(slotClose())); sb->FuncDoc->setTextInteractionFlags(Qt::NoTextInteraction); //Initialize function templates and descriptions KStarsFunctionList.append(new ScriptFunction("lookTowards", i18n("Point the display at the specified location. %1 can be the name " "of an object, a cardinal point on the compass, or 'zenith'.", QString("dir")), false, "QString", "dir")); KStarsFunctionList.append(new ScriptFunction( "addLabel", i18n("Add a name label to the object named %1.", QString("name")), false, "QString", "name")); KStarsFunctionList.append( new ScriptFunction("removeLabel", i18n("Remove the name label from the object named %1.", QString("name")), false, "QString", "name")); KStarsFunctionList.append(new ScriptFunction( "addTrail", i18n("Add a trail to the solar system body named %1.", QString("name")), false, "QString", "name")); KStarsFunctionList.append(new ScriptFunction( "removeTrail", i18n("Remove the trail from the solar system body named %1.", QString("name")), false, "QString", "name")); KStarsFunctionList.append(new ScriptFunction("setRaDec", i18n("Point the display at the specified RA/Dec coordinates. RA is " "expressed in Hours; Dec is expressed in Degrees."), false, "double", "ra", "double", "dec")); KStarsFunctionList.append(new ScriptFunction( "setAltAz", i18n("Point the display at the specified Alt/Az coordinates. Alt and Az are expressed in Degrees."), false, "double", "alt", "double", "az")); KStarsFunctionList.append(new ScriptFunction("zoomIn", i18n("Increase the display Zoom Level."), false)); KStarsFunctionList.append(new ScriptFunction("zoomOut", i18n("Decrease the display Zoom Level."), false)); KStarsFunctionList.append( new ScriptFunction("defaultZoom", i18n("Set the display Zoom Level to its default value."), false)); KStarsFunctionList.append( new ScriptFunction("zoom", i18n("Set the display Zoom Level manually."), false, "double", "z")); KStarsFunctionList.append( new ScriptFunction("setLocalTime", i18n("Set the system clock to the specified Local Time."), false, "int", "year", "int", "month", "int", "day", "int", "hour", "int", "minute", "int", "second")); KStarsFunctionList.append(new ScriptFunction( "waitFor", i18n("Pause script execution for specified number of seconds."), false, "double", "sec")); KStarsFunctionList.append(new ScriptFunction("waitForKey", i18n("Halt script execution until the specified key is pressed. Only " "single-key strokes are possible; use 'space' for the spacebar."), false, "QString", "key")); KStarsFunctionList.append(new ScriptFunction( "setTracking", i18n("Set whether the display is tracking the current location."), false, "bool", "track")); KStarsFunctionList.append(new ScriptFunction( "changeViewOption", i18n("Change view option named %1 to value %2.", QString("opName"), QString("opValue")), false, "QString", "opName", "QString", "opValue")); KStarsFunctionList.append(new ScriptFunction( "setGeoLocation", i18n("Set the geographic location to the city specified by city, province and country."), false, "QString", "cityName", "QString", "provinceName", "QString", "countryName")); KStarsFunctionList.append(new ScriptFunction( "setColor", i18n("Set the color named %1 to the value %2.", QString("colorName"), QString("value")), false, "QString", "colorName", "QString", "value")); KStarsFunctionList.append(new ScriptFunction("loadColorScheme", i18n("Load the color scheme specified by name."), false, "QString", "name")); KStarsFunctionList.append( new ScriptFunction("exportImage", i18n("Export the sky image to the file, with specified width and height."), false, "QString", "fileName", "int", "width", "int", "height")); KStarsFunctionList.append( new ScriptFunction("printImage", i18n("Print the sky image to a printer or file. If %1 is true, it will show the print " "dialog. If %2 is true, it will use the Star Chart color scheme for printing.", QString("usePrintDialog"), QString("useChartColors")), false, "bool", "usePrintDialog", "bool", "useChartColors")); SimClockFunctionList.append(new ScriptFunction("stop", i18n("Halt the simulation clock."), true)); SimClockFunctionList.append(new ScriptFunction("start", i18n("Start the simulation clock."), true)); SimClockFunctionList.append(new ScriptFunction("setClockScale", i18n("Set the timescale of the simulation clock to specified scale. " " 1.0 means real-time; 2.0 means twice real-time; etc."), true, "double", "scale")); // JM: We're using QTreeWdiget for Qt4 now QTreeWidgetItem *kstars_tree = new QTreeWidgetItem(sb->FunctionTree, QStringList("KStars")); QTreeWidgetItem *simclock_tree = new QTreeWidgetItem(sb->FunctionTree, QStringList("SimClock")); for (int i = 0; i < KStarsFunctionList.size(); ++i) new QTreeWidgetItem(kstars_tree, QStringList(KStarsFunctionList[i]->prototype())); for (int i = 0; i < SimClockFunctionList.size(); ++i) new QTreeWidgetItem(simclock_tree, QStringList(SimClockFunctionList[i]->prototype())); kstars_tree->sortChildren(0, Qt::AscendingOrder); simclock_tree->sortChildren(0, Qt::AscendingOrder); sb->FunctionTree->setColumnCount(1); sb->FunctionTree->setHeaderLabels(QStringList(i18n("Functions"))); sb->FunctionTree->setSortingEnabled(false); //Add icons to Push Buttons sb->NewButton->setIcon(QIcon::fromTheme("document-new")); sb->OpenButton->setIcon(QIcon::fromTheme("document-open")); sb->SaveButton->setIcon(QIcon::fromTheme("document-save")); sb->SaveAsButton->setIcon( QIcon::fromTheme("document-save-as")); sb->RunButton->setIcon(QIcon::fromTheme("system-run")); sb->CopyButton->setIcon(QIcon::fromTheme("view-refresh")); sb->AddButton->setIcon(QIcon::fromTheme("go-previous")); sb->RemoveButton->setIcon(QIcon::fromTheme("go-next")); sb->UpButton->setIcon(QIcon::fromTheme("go-up")); sb->DownButton->setIcon(QIcon::fromTheme("go-down")); sb->NewButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->OpenButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->SaveButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->SaveAsButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->RunButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->CopyButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->AddButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->RemoveButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->UpButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); sb->DownButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); //Prepare the widget stack argBlank = new QWidget(); argLookToward = new ArgLookToward(sb->ArgStack); argFindObject = new ArgFindObject(sb->ArgStack); //shared by Add/RemoveLabel and Add/RemoveTrail argSetRaDec = new ArgSetRaDec(sb->ArgStack); argSetAltAz = new ArgSetAltAz(sb->ArgStack); argSetLocalTime = new ArgSetLocalTime(sb->ArgStack); argWaitFor = new ArgWaitFor(sb->ArgStack); argWaitForKey = new ArgWaitForKey(sb->ArgStack); argSetTracking = new ArgSetTrack(sb->ArgStack); argChangeViewOption = new ArgChangeViewOption(sb->ArgStack); argSetGeoLocation = new ArgSetGeoLocation(sb->ArgStack); argTimeScale = new ArgTimeScale(sb->ArgStack); argZoom = new ArgZoom(sb->ArgStack); argExportImage = new ArgExportImage(sb->ArgStack); argPrintImage = new ArgPrintImage(sb->ArgStack); argSetColor = new ArgSetColor(sb->ArgStack); argLoadColorScheme = new ArgLoadColorScheme(sb->ArgStack); sb->ArgStack->addWidget(argBlank); sb->ArgStack->addWidget(argLookToward); sb->ArgStack->addWidget(argFindObject); sb->ArgStack->addWidget(argSetRaDec); sb->ArgStack->addWidget(argSetAltAz); sb->ArgStack->addWidget(argSetLocalTime); sb->ArgStack->addWidget(argWaitFor); sb->ArgStack->addWidget(argWaitForKey); sb->ArgStack->addWidget(argSetTracking); sb->ArgStack->addWidget(argChangeViewOption); sb->ArgStack->addWidget(argSetGeoLocation); sb->ArgStack->addWidget(argTimeScale); sb->ArgStack->addWidget(argZoom); sb->ArgStack->addWidget(argExportImage); sb->ArgStack->addWidget(argPrintImage); sb->ArgStack->addWidget(argSetColor); sb->ArgStack->addWidget(argLoadColorScheme); sb->ArgStack->setCurrentIndex(0); snd = new ScriptNameDialog(ks); otv = new OptionsTreeView(ks); otv->resize(sb->width(), 0.5 * sb->height()); initViewOptions(); otv->resizeColumns(); //connect widgets in ScriptBuilderUI connect(sb->FunctionTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotAddFunction())); connect(sb->FunctionTree, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotShowDoc())); connect(sb->UpButton, SIGNAL(clicked()), this, SLOT(slotMoveFunctionUp())); connect(sb->ScriptListBox, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotArgWidget())); connect(sb->DownButton, SIGNAL(clicked()), this, SLOT(slotMoveFunctionDown())); connect(sb->CopyButton, SIGNAL(clicked()), this, SLOT(slotCopyFunction())); connect(sb->RemoveButton, SIGNAL(clicked()), this, SLOT(slotRemoveFunction())); connect(sb->NewButton, SIGNAL(clicked()), this, SLOT(slotNew())); connect(sb->OpenButton, SIGNAL(clicked()), this, SLOT(slotOpen())); connect(sb->SaveButton, SIGNAL(clicked()), this, SLOT(slotSave())); connect(sb->SaveAsButton, SIGNAL(clicked()), this, SLOT(slotSaveAs())); connect(sb->AddButton, SIGNAL(clicked()), this, SLOT(slotAddFunction())); connect(sb->RunButton, SIGNAL(clicked()), this, SLOT(slotRunScript())); //Connections for Arg Widgets connect(argSetGeoLocation->FindCityButton, SIGNAL(clicked()), this, SLOT(slotFindCity())); connect(argLookToward->FindButton, SIGNAL(clicked()), this, SLOT(slotFindObject())); connect(argChangeViewOption->TreeButton, SIGNAL(clicked()), this, SLOT(slotShowOptions())); connect(argFindObject->FindButton, SIGNAL(clicked()), this, SLOT(slotFindObject())); connect(argLookToward->FocusEdit, SIGNAL(editTextChanged(QString)), this, SLOT(slotLookToward())); connect(argFindObject->NameEdit, SIGNAL(textChanged(QString)), this, SLOT(slotArgFindObject())); connect(argSetRaDec->RABox, SIGNAL(textChanged(QString)), this, SLOT(slotRa())); connect(argSetRaDec->DecBox, SIGNAL(textChanged(QString)), this, SLOT(slotDec())); connect(argSetAltAz->AltBox, SIGNAL(textChanged(QString)), this, SLOT(slotAlt())); connect(argSetAltAz->AzBox, SIGNAL(textChanged(QString)), this, SLOT(slotAz())); connect(argSetLocalTime->DateWidget, SIGNAL(dateChanged(QDate)), this, SLOT(slotChangeDate())); connect(argSetLocalTime->TimeBox, SIGNAL(timeChanged(QTime)), this, SLOT(slotChangeTime())); connect(argWaitFor->DelayBox, SIGNAL(valueChanged(int)), this, SLOT(slotWaitFor())); connect(argWaitForKey->WaitKeyEdit, SIGNAL(textChanged(QString)), this, SLOT(slotWaitForKey())); connect(argSetTracking->CheckTrack, SIGNAL(stateChanged(int)), this, SLOT(slotTracking())); connect(argChangeViewOption->OptionName, SIGNAL(activated(QString)), this, SLOT(slotViewOption())); connect(argChangeViewOption->OptionValue, SIGNAL(textChanged(QString)), this, SLOT(slotViewOption())); connect(argSetGeoLocation->CityName, SIGNAL(textChanged(QString)), this, SLOT(slotChangeCity())); connect(argSetGeoLocation->ProvinceName, SIGNAL(textChanged(QString)), this, SLOT(slotChangeProvince())); connect(argSetGeoLocation->CountryName, SIGNAL(textChanged(QString)), this, SLOT(slotChangeCountry())); connect(argTimeScale->TimeScale, SIGNAL(scaleChanged(float)), this, SLOT(slotTimeScale())); connect(argZoom->ZoomBox, SIGNAL(textChanged(QString)), this, SLOT(slotZoom())); connect(argExportImage->ExportFileName, SIGNAL(textChanged(QString)), this, SLOT(slotExportImage())); connect(argExportImage->ExportWidth, SIGNAL(valueChanged(int)), this, SLOT(slotExportImage())); connect(argExportImage->ExportHeight, SIGNAL(valueChanged(int)), this, SLOT(slotExportImage())); connect(argPrintImage->UsePrintDialog, SIGNAL(toggled(bool)), this, SLOT(slotPrintImage())); connect(argPrintImage->UseChartColors, SIGNAL(toggled(bool)), this, SLOT(slotPrintImage())); connect(argSetColor->ColorName, SIGNAL(activated(QString)), this, SLOT(slotChangeColorName())); connect(argSetColor->ColorValue, SIGNAL(changed(QColor)), this, SLOT(slotChangeColor())); connect(argLoadColorScheme->SchemeList, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(slotLoadColorScheme())); //disable some buttons sb->CopyButton->setEnabled(false); sb->AddButton->setEnabled(false); sb->RemoveButton->setEnabled(false); sb->UpButton->setEnabled(false); sb->DownButton->setEnabled(false); sb->SaveButton->setEnabled(false); sb->SaveAsButton->setEnabled(false); sb->RunButton->setEnabled(false); } ScriptBuilder::~ScriptBuilder() { while (!KStarsFunctionList.isEmpty()) delete KStarsFunctionList.takeFirst(); while (!SimClockFunctionList.isEmpty()) delete SimClockFunctionList.takeFirst(); while (!ScriptList.isEmpty()) delete ScriptList.takeFirst(); } void ScriptBuilder::initViewOptions() { otv->optionsList()->setRootIsDecorated(true); QStringList fields; //InfoBoxes opsGUI = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("InfoBoxes"))); fields << "ShowInfoBoxes" << i18n("Toggle display of all InfoBoxes") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); fields << "ShowTimeBox" << i18n("Toggle display of Time InfoBox") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); fields << "ShowGeoBox" << i18n("Toggle display of Geographic InfoBox") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); fields << "ShowFocusBox" << i18n("Toggle display of Focus InfoBox") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); fields << "ShadeTimeBox" << i18n("(un)Shade Time InfoBox") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); fields << "ShadeGeoBox" << i18n("(un)Shade Geographic InfoBox") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); fields << "ShadeFocusBox" << i18n("(un)Shade Focus InfoBox") << i18n("bool"); new QTreeWidgetItem(opsGUI, fields); fields.clear(); argChangeViewOption->OptionName->addItem("ShowInfoBoxes"); argChangeViewOption->OptionName->addItem("ShowTimeBox"); argChangeViewOption->OptionName->addItem("ShowGeoBox"); argChangeViewOption->OptionName->addItem("ShowFocusBox"); argChangeViewOption->OptionName->addItem("ShadeTimeBox"); argChangeViewOption->OptionName->addItem("ShadeGeoBox"); argChangeViewOption->OptionName->addItem("ShadeFocusBox"); //Toolbars opsToolbar = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Toolbars"))); fields << "ShowMainToolBar" << i18n("Toggle display of main toolbar") << i18n("bool"); new QTreeWidgetItem(opsToolbar, fields); fields.clear(); fields << "ShowViewToolBar" << i18n("Toggle display of view toolbar") << i18n("bool"); new QTreeWidgetItem(opsToolbar, fields); fields.clear(); argChangeViewOption->OptionName->addItem("ShowMainToolBar"); argChangeViewOption->OptionName->addItem("ShowViewToolBar"); //Show Objects opsShowObj = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Show Objects"))); fields << "ShowStars" << i18n("Toggle display of Stars") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowDeepSky" << i18n("Toggle display of all deep-sky objects") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowMessier" << i18n("Toggle display of Messier object symbols") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowMessierImages" << i18n("Toggle display of Messier object images") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowNGC" << i18n("Toggle display of NGC objects") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowIC" << i18n("Toggle display of IC objects") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowSolarSystem" << i18n("Toggle display of all solar system bodies") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowSun" << i18n("Toggle display of Sun") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowMoon" << i18n("Toggle display of Moon") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowMercury" << i18n("Toggle display of Mercury") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowVenus" << i18n("Toggle display of Venus") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowMars" << i18n("Toggle display of Mars") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowJupiter" << i18n("Toggle display of Jupiter") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowSaturn" << i18n("Toggle display of Saturn") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowUranus" << i18n("Toggle display of Uranus") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowNeptune" << i18n("Toggle display of Neptune") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); //fields.clear(); //fields << "ShowPluto" << i18n( "Toggle display of Pluto" ) << i18n( "bool" ); //new QTreeWidgetItem( opsShowObj, fields ); fields.clear(); fields << "ShowAsteroids" << i18n("Toggle display of Asteroids") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); fields << "ShowComets" << i18n("Toggle display of Comets") << i18n("bool"); new QTreeWidgetItem(opsShowObj, fields); fields.clear(); argChangeViewOption->OptionName->addItem("ShowStars"); argChangeViewOption->OptionName->addItem("ShowDeepSky"); argChangeViewOption->OptionName->addItem("ShowMessier"); argChangeViewOption->OptionName->addItem("ShowMessierImages"); argChangeViewOption->OptionName->addItem("ShowNGC"); argChangeViewOption->OptionName->addItem("ShowIC"); argChangeViewOption->OptionName->addItem("ShowSolarSystem"); argChangeViewOption->OptionName->addItem("ShowSun"); argChangeViewOption->OptionName->addItem("ShowMoon"); argChangeViewOption->OptionName->addItem("ShowMercury"); argChangeViewOption->OptionName->addItem("ShowVenus"); argChangeViewOption->OptionName->addItem("ShowMars"); argChangeViewOption->OptionName->addItem("ShowJupiter"); argChangeViewOption->OptionName->addItem("ShowSaturn"); argChangeViewOption->OptionName->addItem("ShowUranus"); argChangeViewOption->OptionName->addItem("ShowNeptune"); //argChangeViewOption->OptionName->addItem( "ShowPluto" ); argChangeViewOption->OptionName->addItem("ShowAsteroids"); argChangeViewOption->OptionName->addItem("ShowComets"); opsShowOther = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Show Other"))); fields << "ShowCLines" << i18n("Toggle display of constellation lines") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowCBounds" << i18n("Toggle display of constellation boundaries") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowCNames" << i18n("Toggle display of constellation names") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowMilkyWay" << i18n("Toggle display of Milky Way") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowGrid" << i18n("Toggle display of the coordinate grid") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowEquator" << i18n("Toggle display of the celestial equator") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowEcliptic" << i18n("Toggle display of the ecliptic") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowHorizon" << i18n("Toggle display of the horizon line") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowGround" << i18n("Toggle display of the opaque ground") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowStarNames" << i18n("Toggle display of star name labels") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowStarMagnitudes" << i18n("Toggle display of star magnitude labels") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowAsteroidNames" << i18n("Toggle display of asteroid name labels") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowCometNames" << i18n("Toggle display of comet name labels") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowPlanetNames" << i18n("Toggle display of planet name labels") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); fields << "ShowPlanetImages" << i18n("Toggle display of planet images") << i18n("bool"); new QTreeWidgetItem(opsShowOther, fields); fields.clear(); argChangeViewOption->OptionName->addItem("ShowCLines"); argChangeViewOption->OptionName->addItem("ShowCBounds"); argChangeViewOption->OptionName->addItem("ShowCNames"); argChangeViewOption->OptionName->addItem("ShowMilkyWay"); argChangeViewOption->OptionName->addItem("ShowGrid"); argChangeViewOption->OptionName->addItem("ShowEquator"); argChangeViewOption->OptionName->addItem("ShowEcliptic"); argChangeViewOption->OptionName->addItem("ShowHorizon"); argChangeViewOption->OptionName->addItem("ShowGround"); argChangeViewOption->OptionName->addItem("ShowStarNames"); argChangeViewOption->OptionName->addItem("ShowStarMagnitudes"); argChangeViewOption->OptionName->addItem("ShowAsteroidNames"); argChangeViewOption->OptionName->addItem("ShowCometNames"); argChangeViewOption->OptionName->addItem("ShowPlanetNames"); argChangeViewOption->OptionName->addItem("ShowPlanetImages"); opsCName = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Constellation Names"))); fields << "UseLatinConstellNames" << i18n("Show Latin constellation names") << i18n("bool"); new QTreeWidgetItem(opsCName, fields); fields.clear(); fields << "UseLocalConstellNames" << i18n("Show constellation names in local language") << i18n("bool"); new QTreeWidgetItem(opsCName, fields); fields.clear(); fields << "UseAbbrevConstellNames" << i18n("Show IAU-standard constellation abbreviations") << i18n("bool"); new QTreeWidgetItem(opsCName, fields); fields.clear(); argChangeViewOption->OptionName->addItem("UseLatinConstellNames"); argChangeViewOption->OptionName->addItem("UseLocalConstellNames"); argChangeViewOption->OptionName->addItem("UseAbbrevConstellNames"); opsHide = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Hide Items"))); fields << "HideOnSlew" << i18n("Toggle whether objects hidden while slewing display") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "SlewTimeScale" << i18n("Timestep threshold (in seconds) for hiding objects") << i18n("double"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideStars" << i18n("Hide faint stars while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HidePlanets" << i18n("Hide solar system bodies while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideMessier" << i18n("Hide Messier objects while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideNGC" << i18n("Hide NGC objects while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideIC" << i18n("Hide IC objects while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideMilkyWay" << i18n("Hide Milky Way while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideCNames" << i18n("Hide constellation names while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideCLines" << i18n("Hide constellation lines while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideCBounds" << i18n("Hide constellation boundaries while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); fields << "HideGrid" << i18n("Hide coordinate grid while slewing?") << i18n("bool"); new QTreeWidgetItem(opsHide, fields); fields.clear(); argChangeViewOption->OptionName->addItem("HideOnSlew"); argChangeViewOption->OptionName->addItem("SlewTimeScale"); argChangeViewOption->OptionName->addItem("HideStars"); argChangeViewOption->OptionName->addItem("HidePlanets"); argChangeViewOption->OptionName->addItem("HideMessier"); argChangeViewOption->OptionName->addItem("HideNGC"); argChangeViewOption->OptionName->addItem("HideIC"); argChangeViewOption->OptionName->addItem("HideMilkyWay"); argChangeViewOption->OptionName->addItem("HideCNames"); argChangeViewOption->OptionName->addItem("HideCLines"); argChangeViewOption->OptionName->addItem("HideCBounds"); argChangeViewOption->OptionName->addItem("HideGrid"); opsSkymap = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Skymap Options"))); fields << "UseAltAz" << i18n("Use Horizontal coordinates? (otherwise, use Equatorial)") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "ZoomFactor" << i18n("Set the Zoom Factor") << i18n("double"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "FOVName" << i18n("Select angular size for the FOV symbol (in arcmin)") << i18n("double"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "FOVShape" << i18n("Select shape for the FOV symbol (0=Square, 1=Circle, 2=Crosshairs, 4=Bullseye)") << i18n("int"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "FOVColor" << i18n("Select color for the FOV symbol") << i18n("string"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "UseAnimatedSlewing" << i18n("Use animated slewing? (otherwise, \"snap\" to new focus)") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "UseRefraction" << i18n("Correct for atmospheric refraction?") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "UseAutoLabel" << i18n("Automatically attach name label to centered object?") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "UseHoverLabel" << i18n("Attach temporary name label when hovering mouse over an object?") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "UseAutoTrail" << i18n("Automatically add trail to centered solar system body?") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); fields << "FadePlanetTrails" << i18n("Planet trails fade to sky color? (otherwise color is constant)") << i18n("bool"); new QTreeWidgetItem(opsSkymap, fields); fields.clear(); argChangeViewOption->OptionName->addItem("UseAltAz"); argChangeViewOption->OptionName->addItem("ZoomFactor"); argChangeViewOption->OptionName->addItem("FOVName"); argChangeViewOption->OptionName->addItem("FOVSize"); argChangeViewOption->OptionName->addItem("FOVShape"); argChangeViewOption->OptionName->addItem("FOVColor"); argChangeViewOption->OptionName->addItem("UseRefraction"); argChangeViewOption->OptionName->addItem("UseAutoLabel"); argChangeViewOption->OptionName->addItem("UseHoverLabel"); argChangeViewOption->OptionName->addItem("UseAutoTrail"); argChangeViewOption->OptionName->addItem("AnimateSlewing"); argChangeViewOption->OptionName->addItem("FadePlanetTrails"); opsLimit = new QTreeWidgetItem(otv->optionsList(), QStringList(i18n("Limits"))); /* fields << "magLimitDrawStar" << i18n( "magnitude of faintest star drawn on map when zoomed in" ) << i18n( "double" ); new QTreeWidgetItem( opsLimit, fields ); fields.clear(); fields << "magLimitDrawStarZoomOut" << i18n( "magnitude of faintest star drawn on map when zoomed out" ) << i18n( "double" ); new QTreeWidgetItem( opsLimit, fields ); fields.clear(); */ // TODO: We have disabled the following two features. Enable them when feasible... /* fields << "magLimitDrawDeepSky" << i18n( "magnitude of faintest nonstellar object drawn on map when zoomed in" ) << i18n( "double" ); new QTreeWidgetItem( opsLimit, fields ); fields.clear(); fields << "magLimitDrawDeepSkyZoomOut" << i18n( "magnitude of faintest nonstellar object drawn on map when zoomed out" ) << i18n( "double" ); new QTreeWidgetItem( opsLimit, fields ); fields.clear(); */ //FIXME: This description is incorrect! Fix after strings freeze fields << "starLabelDensity" << i18n("magnitude of faintest star labeled on map") << i18n("double"); new QTreeWidgetItem(opsLimit, fields); fields.clear(); fields << "magLimitHideStar" << i18n("magnitude of brightest star hidden while slewing") << i18n("double"); new QTreeWidgetItem(opsLimit, fields); fields.clear(); fields << "magLimitAsteroid" << i18n("magnitude of faintest asteroid drawn on map") << i18n("double"); new QTreeWidgetItem(opsLimit, fields); fields.clear(); //FIXME: This description is incorrect! Fix after strings freeze fields << "asteroidLabelDensity" << i18n("magnitude of faintest asteroid labeled on map") << i18n("double"); new QTreeWidgetItem(opsLimit, fields); fields.clear(); fields << "maxRadCometName" << i18n("comets nearer to the Sun than this (in AU) are labeled on map") << i18n("double"); new QTreeWidgetItem(opsLimit, fields); fields.clear(); // argChangeViewOption->OptionName->addItem( "magLimitDrawStar" ); // argChangeViewOption->OptionName->addItem( "magLimitDrawStarZoomOut" ); argChangeViewOption->OptionName->addItem("magLimitDrawDeepSky"); argChangeViewOption->OptionName->addItem("magLimitDrawDeepSkyZoomOut"); argChangeViewOption->OptionName->addItem("starLabelDensity"); argChangeViewOption->OptionName->addItem("magLimitHideStar"); argChangeViewOption->OptionName->addItem("magLimitAsteroid"); argChangeViewOption->OptionName->addItem("asteroidLabelDensity"); argChangeViewOption->OptionName->addItem("maxRadCometName"); //init the list of color names and values for (unsigned int i = 0; i < ks->data()->colorScheme()->numberOfColors(); ++i) { argSetColor->ColorName->addItem(ks->data()->colorScheme()->nameAt(i)); } //init list of color scheme names argLoadColorScheme->SchemeList->addItem(i18nc("use default color scheme", "Default Colors")); argLoadColorScheme->SchemeList->addItem(i18nc("use 'star chart' color scheme", "Star Chart")); argLoadColorScheme->SchemeList->addItem(i18nc("use 'night vision' color scheme", "Night Vision")); argLoadColorScheme->SchemeList->addItem(i18nc("use 'moonless night' color scheme", "Moonless Night")); QFile file; QString line; file.setFileName(KSPaths::locate(QStandardPaths::GenericDataLocation, "colors.dat")); //determine filename in local user KDE directory tree. if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); while (!stream.atEnd()) { line = stream.readLine(); argLoadColorScheme->SchemeList->addItem(line.left(line.indexOf(':'))); } file.close(); } } //Slots defined in ScriptBuilderUI void ScriptBuilder::slotNew() { saveWarning(); if (!UnsavedChanges) { ScriptList.clear(); sb->ScriptListBox->clear(); sb->ArgStack->setCurrentWidget(argBlank); sb->CopyButton->setEnabled(false); sb->RemoveButton->setEnabled(false); sb->RunButton->setEnabled(false); sb->SaveAsButton->setEnabled(false); currentFileURL.clear(); currentScriptName.clear(); } } void ScriptBuilder::slotOpen() { saveWarning(); QString fname; QTemporaryFile tmpfile; tmpfile.open(); if (!UnsavedChanges) { currentFileURL = QFileDialog::getOpenFileUrl( KStars::Instance(), QString(), QUrl(currentDir), "*.kstars|" + i18nc("Filter by file type: KStars Scripts.", "KStars Scripts (*.kstars)")); if (currentFileURL.isValid()) { currentDir = currentFileURL.toLocalFile(); ScriptList.clear(); sb->ScriptListBox->clear(); sb->ArgStack->setCurrentWidget(argBlank); if (currentFileURL.isLocalFile()) { fname = currentFileURL.toLocalFile(); } else { fname = tmpfile.fileName(); if (KIO::copy(currentFileURL, QUrl(fname))->exec() == false) //if ( ! KIO::NetAccess::download( currentFileURL, fname, (QWidget*) 0 ) ) KMessageBox::sorry(nullptr, i18n("Could not download remote file."), i18n("Download Error")); } QFile f(fname); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); currentFileURL.clear(); return; } QTextStream istream(&f); readScript(istream); f.close(); } else if (!currentFileURL.url().isEmpty()) { QString message = i18n("Invalid URL: %1", currentFileURL.url()); KMessageBox::sorry(nullptr, message, i18n("Invalid URL")); currentFileURL.clear(); } } } void ScriptBuilder::slotSave() { QString fname; QTemporaryFile tmpfile; tmpfile.open(); if (currentScriptName.isEmpty()) { //Get Script Name and Author info if (snd->exec() == QDialog::Accepted) { currentScriptName = snd->scriptName(); currentAuthor = snd->authorName(); } else { return; } } bool newFilename = false; if (currentFileURL.isEmpty()) { currentFileURL = QFileDialog::getSaveFileUrl( KStars::Instance(), QString(), QUrl(currentDir), "*.kstars|" + i18nc("Filter by file type: KStars Scripts.", "KStars Scripts (*.kstars)")); newFilename = true; } if (currentFileURL.isValid()) { currentDir = currentFileURL.toLocalFile(); if (currentFileURL.isLocalFile()) { fname = currentFileURL.toLocalFile(); //Warn user if file exists if (newFilename == true && QFile::exists(currentFileURL.toLocalFile())) { int r = KMessageBox::warningContinueCancel(static_cast(parent()), i18n("A file named \"%1\" already exists. " "Overwrite it?", currentFileURL.fileName()), i18n("Overwrite File?"), KStandardGuiItem::overwrite()); if (r == KMessageBox::Cancel) return; } } else { fname = tmpfile.fileName(); } if (fname.right(7).toLower() != ".kstars") fname += ".kstars"; QFile f(fname); if (!f.open(QIODevice::WriteOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); currentFileURL.clear(); return; } QTextStream ostream(&f); writeScript(ostream); f.close(); #ifndef _WIN32 //set rwx for owner, rx for group, rx for other chmod(fname.toLatin1(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); #endif if (tmpfile.fileName() == fname) { //need to upload to remote location //if ( ! KIO::NetAccess::upload( tmpfile.fileName(), currentFileURL, (QWidget*) 0 ) ) if (KIO::storedHttpPost(&tmpfile, currentFileURL)->exec() == false) { QString message = i18n("Could not upload image to remote location: %1", currentFileURL.url()); KMessageBox::sorry(nullptr, message, i18n("Could not upload file")); } } setUnsavedChanges(false); } else { QString message = i18n("Invalid URL: %1", currentFileURL.url()); KMessageBox::sorry(nullptr, message, i18n("Invalid URL")); currentFileURL.clear(); } } void ScriptBuilder::slotSaveAs() { currentFileURL.clear(); currentScriptName.clear(); slotSave(); } void ScriptBuilder::saveWarning() { if (UnsavedChanges) { QString caption = i18n("Save Changes to Script?"); QString message = i18n("The current script has unsaved changes. Would you like to save before closing it?"); int ans = KMessageBox::warningYesNoCancel(nullptr, message, caption, KStandardGuiItem::save(), KStandardGuiItem::discard()); if (ans == KMessageBox::Yes) { slotSave(); setUnsavedChanges(false); } else if (ans == KMessageBox::No) { setUnsavedChanges(false); } //Do nothing if 'cancel' selected } } void ScriptBuilder::slotRunScript() { //hide window while script runs // If this is uncommented, the program hangs before the script is executed. Why? // hide(); //Save current script to a temporary file, then execute that file. //For some reason, I can't use KTempFile here! If I do, then the temporary script //is not executable. Bizarre... //KTempFile tmpfile; //QString fname = tmpfile.name(); QString fname = QDir::tempPath() + QDir::separator() + "kstars-tempscript"; QFile f(fname); if (f.exists()) f.remove(); if (!f.open(QIODevice::WriteOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); currentFileURL.clear(); return; } QTextStream ostream(&f); writeScript(ostream); f.close(); #ifndef _WIN32 //set rwx for owner, rx for group, rx for other chmod(QFile::encodeName(f.fileName()), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); #endif QProcess p; #ifdef Q_OS_OSX QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH", ""); env.insert("PATH", "/usr/local/bin:" + QCoreApplication::applicationDirPath() + ":" + path); p.setProcessEnvironment(env); #endif p.start(f.fileName()); if (!p.waitForStarted()) qDebug() << "Process did not start."; while (!p.waitForFinished(10)) { qApp->processEvents(); //otherwise tempfile may get deleted before script completes. if (p.state() != QProcess::Running) break; } //delete temp file if (f.exists()) f.remove(); //uncomment if 'hide()' is uncommented... // show(); } /* This can't work anymore and is also not protable in any way :( */ void ScriptBuilder::writeScript(QTextStream &ostream) { // FIXME Without --print-reply, the dbus-send doesn't do anything, why?? QString dbus_call = "dbus-send --dest=org.kde.kstars --print-reply "; QString main_method = "/KStars org.kde.kstars."; QString clock_method = "/KStars/SimClock org.kde.kstars.SimClock."; //Write script header ostream << "#!/bin/bash" << endl; ostream << "#KStars DBus script: " << currentScriptName << endl; ostream << "#by " << currentAuthor << endl; ostream << "#last modified: " << KStarsDateTime::currentDateTime().toString(Qt::ISODate) << endl; ostream << "#" << endl; foreach (ScriptFunction *sf, ScriptList) { if (!sf->valid()) continue; if (sf->isClockFunction()) { ostream << dbus_call << clock_method << sf->scriptLine() << endl; } else { ostream << dbus_call << main_method << sf->scriptLine() << endl; } } //Write script footer ostream << "##" << endl; } void ScriptBuilder::readScript(QTextStream &istream) { QString line; QString service_name = "org.kde.kstars."; QString fn_name; while (!istream.atEnd()) { line = istream.readLine(); //look for name of script if (line.contains("#KStars DBus script: ")) currentScriptName = line.mid(21).trimmed(); //look for author of scriptbuilder if (line.contains("#by ")) currentAuthor = line.mid(4).trimmed(); //Actual script functions if (line.left(4) == "dbus") { //is ClockFunction? if (line.contains("SimClock")) { service_name += "SimClock."; } //remove leading dbus prefix line = line.mid(line.lastIndexOf(service_name) + service_name.count()); fn_name = line.left(line.indexOf(' ')); line = line.mid(line.indexOf(' ') + 1); //construct a stringlist that is fcn name and its arg name/value pairs QStringList fn; // If the function lacks any arguments, do not attempt to split if (fn_name != line) fn = line.split(' '); if (parseFunction(fn_name, fn)) { sb->ScriptListBox->addItem(ScriptList.last()->name()); // Initially, any read script is valid! ScriptList.last()->setValid(true); } else qWarning() << i18n("Could not parse script. Line was: %1", line); } // end if left(4) == "dcop" } // end while !atEnd() //Select first item in sb->ScriptListBox if (sb->ScriptListBox->count()) { sb->ScriptListBox->setCurrentItem(nullptr); slotArgWidget(); } } bool ScriptBuilder::parseFunction(QString fn_name, QStringList &fn) { // clean up the string list first if needed // We need to perform this in case we havea quoted string "NGC 3000" because this will counted // as two arguments, and it should be counted as one. bool foundQuote(false), quoteProcessed(false); QString cur, arg; QStringList::iterator it; for (it = fn.begin(); it != fn.end(); ++it) { cur = (*it); cur = cur.mid(cur.indexOf(":") + 1).remove('\''); (*it) = cur; if (cur.startsWith('\"')) { arg += cur.rightRef(cur.length() - 1); arg += ' '; foundQuote = true; quoteProcessed = true; } else if (cur.endsWith('\"')) { arg += cur.leftRef(cur.length() - 1); arg += '\''; foundQuote = false; } else if (foundQuote) { arg += cur; arg += ' '; } else { arg += cur; arg += '\''; } } if (quoteProcessed) fn = arg.split('\'', QString::SkipEmptyParts); //loop over known functions to find a name match foreach (ScriptFunction *sf, KStarsFunctionList) { if (fn_name == sf->name()) { if (fn_name == "setGeoLocation") { QString city(fn[0]), prov, cntry(fn[1]); prov.clear(); if (fn.count() == 4) { prov = fn[1]; cntry = fn[2]; } if (fn.count() == 3 || fn.count() == 4) { ScriptList.append(new ScriptFunction(sf)); ScriptList.last()->setArg(0, city); ScriptList.last()->setArg(1, prov); ScriptList.last()->setArg(2, cntry); } else return false; } else if (fn.count() != sf->numArgs()) return false; ScriptList.append(new ScriptFunction(sf)); for (int i = 0; i < sf->numArgs(); ++i) ScriptList.last()->setArg(i, fn[i]); return true; } foreach (ScriptFunction *sf, SimClockFunctionList) { if (fn_name == sf->name()) { if (fn.count() != sf->numArgs()) return false; ScriptList.append(new ScriptFunction(sf)); for (int i = 0; i < sf->numArgs(); ++i) ScriptList.last()->setArg(i, fn[i]); return true; } } } //if we get here, no function-name match was found return false; } void ScriptBuilder::setUnsavedChanges(bool b) { if (checkForChanges) { UnsavedChanges = b; sb->SaveButton->setEnabled(b); } } void ScriptBuilder::slotCopyFunction() { if (!UnsavedChanges) setUnsavedChanges(true); int Pos = sb->ScriptListBox->currentRow() + 1; ScriptList.insert(Pos, new ScriptFunction(ScriptList[Pos - 1])); //copy ArgVals for (int i = 0; i < ScriptList[Pos - 1]->numArgs(); ++i) ScriptList[Pos]->setArg(i, ScriptList[Pos - 1]->argVal(i)); sb->ScriptListBox->insertItem(Pos, ScriptList[Pos]->name()); //sb->ScriptListBox->setSelected( Pos, true ); sb->ScriptListBox->setCurrentRow(Pos); slotArgWidget(); } void ScriptBuilder::slotRemoveFunction() { setUnsavedChanges(true); int Pos = sb->ScriptListBox->currentRow(); ScriptList.removeAt(Pos); sb->ScriptListBox->takeItem(Pos); if (sb->ScriptListBox->count() == 0) { sb->ArgStack->setCurrentWidget(argBlank); sb->CopyButton->setEnabled(false); sb->RemoveButton->setEnabled(false); sb->RunButton->setEnabled(false); sb->SaveAsButton->setEnabled(false); } else { //sb->ScriptListBox->setSelected( Pos, true ); if (Pos == sb->ScriptListBox->count()) { Pos = Pos - 1; } sb->ScriptListBox->setCurrentRow(Pos); } slotArgWidget(); } void ScriptBuilder::slotAddFunction() { ScriptFunction *found = nullptr; QTreeWidgetItem *currentItem = sb->FunctionTree->currentItem(); if (currentItem == nullptr || currentItem->parent() == nullptr) return; for (auto &sc : KStarsFunctionList) { if (sc->prototype() == currentItem->text(0)) { found = sc; break; } } for (auto &sc : SimClockFunctionList) { if (sc->prototype() == currentItem->text(0)) { found = sc; break; } } if (found == nullptr) return; setUnsavedChanges(true); int Pos = sb->ScriptListBox->currentRow() + 1; ScriptList.insert(Pos, new ScriptFunction(found)); sb->ScriptListBox->insertItem(Pos, ScriptList[Pos]->name()); sb->ScriptListBox->setCurrentRow(Pos); slotArgWidget(); } void ScriptBuilder::slotMoveFunctionUp() { if (sb->ScriptListBox->currentRow() > 0) { setUnsavedChanges(true); //QString t = sb->ScriptListBox->currentItem()->text(); QString t = sb->ScriptListBox->currentItem()->text(); unsigned int n = sb->ScriptListBox->currentRow(); ScriptFunction *tmp = ScriptList.takeAt(n); ScriptList.insert(n - 1, tmp); sb->ScriptListBox->takeItem(n); sb->ScriptListBox->insertItem(n - 1, t); sb->ScriptListBox->setCurrentRow(n - 1); slotArgWidget(); } } void ScriptBuilder::slotMoveFunctionDown() { if (sb->ScriptListBox->currentRow() > -1 && sb->ScriptListBox->currentRow() < ((int)sb->ScriptListBox->count()) - 1) { setUnsavedChanges(true); QString t = sb->ScriptListBox->currentItem()->text(); unsigned int n = sb->ScriptListBox->currentRow(); ScriptFunction *tmp = ScriptList.takeAt(n); ScriptList.insert(n + 1, tmp); sb->ScriptListBox->takeItem(n); sb->ScriptListBox->insertItem(n + 1, t); sb->ScriptListBox->setCurrentRow(n + 1); slotArgWidget(); } } void ScriptBuilder::slotArgWidget() { //First, setEnabled on buttons that act on the selected script function if (sb->ScriptListBox->currentRow() == -1) //no selection { sb->CopyButton->setEnabled(false); sb->RemoveButton->setEnabled(false); sb->UpButton->setEnabled(false); sb->DownButton->setEnabled(false); } else if (sb->ScriptListBox->count() == 1) //only one item, so disable up/down buttons { sb->CopyButton->setEnabled(true); sb->RemoveButton->setEnabled(true); sb->UpButton->setEnabled(false); sb->DownButton->setEnabled(false); } else if (sb->ScriptListBox->currentRow() == 0) //first item selected { sb->CopyButton->setEnabled(true); sb->RemoveButton->setEnabled(true); sb->UpButton->setEnabled(false); sb->DownButton->setEnabled(true); } else if (sb->ScriptListBox->currentRow() == ((int)sb->ScriptListBox->count()) - 1) //last item selected { sb->CopyButton->setEnabled(true); sb->RemoveButton->setEnabled(true); sb->UpButton->setEnabled(true); sb->DownButton->setEnabled(false); } else //other item selected { sb->CopyButton->setEnabled(true); sb->RemoveButton->setEnabled(true); sb->UpButton->setEnabled(true); sb->DownButton->setEnabled(true); } //RunButton and SaveAs button enabled when script not empty. if (sb->ScriptListBox->count()) { sb->RunButton->setEnabled(true); sb->SaveAsButton->setEnabled(true); } else { sb->RunButton->setEnabled(false); sb->SaveAsButton->setEnabled(false); setUnsavedChanges(false); } //Display the function's arguments widget if (sb->ScriptListBox->currentRow() > -1 && sb->ScriptListBox->currentRow() < ((int)sb->ScriptListBox->count())) { unsigned int n = sb->ScriptListBox->currentRow(); ScriptFunction *sf = ScriptList.at(n); checkForChanges = false; //Don't signal unsaved changes if (sf->name() == "lookTowards") { sb->ArgStack->setCurrentWidget(argLookToward); QString s = sf->argVal(0); argLookToward->FocusEdit->setEditText(s); } else if (sf->name() == "addLabel" || sf->name() == "removeLabel" || sf->name() == "addTrail" || sf->name() == "removeTrail") { sb->ArgStack->setCurrentWidget(argFindObject); QString s = sf->argVal(0); argFindObject->NameEdit->setText(s); } else if (sf->name() == "setRaDec") { bool ok(false); double r(0.0), d(0.0); dms ra(0.0); sb->ArgStack->setCurrentWidget(argSetRaDec); ok = !sf->argVal(0).isEmpty(); if (ok) r = sf->argVal(0).toDouble(&ok); else argSetRaDec->RABox->clear(); if (ok) { ra.setH(r); argSetRaDec->RABox->showInHours(ra); } ok = !sf->argVal(1).isEmpty(); if (ok) d = sf->argVal(1).toDouble(&ok); else argSetRaDec->DecBox->clear(); if (ok) argSetRaDec->DecBox->showInDegrees(dms(d)); } else if (sf->name() == "setAltAz") { bool ok(false); double x(0.0), y(0.0); sb->ArgStack->setCurrentWidget(argSetAltAz); ok = !sf->argVal(0).isEmpty(); if (ok) y = sf->argVal(0).toDouble(&ok); else argSetAltAz->AzBox->clear(); if (ok) argSetAltAz->AltBox->showInDegrees(dms(y)); else argSetAltAz->AltBox->clear(); ok = !sf->argVal(1).isEmpty(); x = sf->argVal(1).toDouble(&ok); if (ok) argSetAltAz->AzBox->showInDegrees(dms(x)); } else if (sf->name() == "zoomIn") { sb->ArgStack->setCurrentWidget(argBlank); //no Args } else if (sf->name() == "zoomOut") { sb->ArgStack->setCurrentWidget(argBlank); //no Args } else if (sf->name() == "defaultZoom") { sb->ArgStack->setCurrentWidget(argBlank); //no Args } else if (sf->name() == "zoom") { sb->ArgStack->setCurrentWidget(argZoom); bool ok(false); /*double z = */ sf->argVal(0).toDouble(&ok); if (ok) argZoom->ZoomBox->setText(sf->argVal(0)); else argZoom->ZoomBox->setText("2000."); } else if (sf->name() == "exportImage") { sb->ArgStack->setCurrentWidget(argExportImage); argExportImage->ExportFileName->setUrl(QUrl::fromUserInput(sf->argVal(0))); bool ok(false); int w = 0, h = 0; w = sf->argVal(1).toInt(&ok); if (ok) h = sf->argVal(2).toInt(&ok); if (ok) { argExportImage->ExportWidth->setValue(w); argExportImage->ExportHeight->setValue(h); } else { argExportImage->ExportWidth->setValue(ks->map()->width()); argExportImage->ExportHeight->setValue(ks->map()->height()); } } else if (sf->name() == "printImage") { if (sf->argVal(0) == i18n("true")) argPrintImage->UsePrintDialog->setChecked(true); else argPrintImage->UsePrintDialog->setChecked(false); if (sf->argVal(1) == i18n("true")) argPrintImage->UseChartColors->setChecked(true); else argPrintImage->UseChartColors->setChecked(false); } else if (sf->name() == "setLocalTime") { sb->ArgStack->setCurrentWidget(argSetLocalTime); bool ok(false); int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0; year = sf->argVal(0).toInt(&ok); if (ok) month = sf->argVal(1).toInt(&ok); if (ok) day = sf->argVal(2).toInt(&ok); if (ok) argSetLocalTime->DateWidget->setDate(QDate(year, month, day)); else argSetLocalTime->DateWidget->setDate(QDate::currentDate()); hour = sf->argVal(3).toInt(&ok); if (sf->argVal(3).isEmpty()) ok = false; if (ok) min = sf->argVal(4).toInt(&ok); if (ok) sec = sf->argVal(5).toInt(&ok); if (ok) argSetLocalTime->TimeBox->setTime(QTime(hour, min, sec)); else argSetLocalTime->TimeBox->setTime(QTime(QTime::currentTime())); } else if (sf->name() == "waitFor") { sb->ArgStack->setCurrentWidget(argWaitFor); bool ok(false); int sec = sf->argVal(0).toInt(&ok); if (ok) argWaitFor->DelayBox->setValue(sec); else argWaitFor->DelayBox->setValue(0); } else if (sf->name() == "waitForKey") { sb->ArgStack->setCurrentWidget(argWaitForKey); if (sf->argVal(0).length() == 1 || sf->argVal(0).toLower() == "space") argWaitForKey->WaitKeyEdit->setText(sf->argVal(0)); else argWaitForKey->WaitKeyEdit->setText(QString()); } else if (sf->name() == "setTracking") { sb->ArgStack->setCurrentWidget(argSetTracking); if (sf->argVal(0) == i18n("true")) argSetTracking->CheckTrack->setChecked(true); else argSetTracking->CheckTrack->setChecked(false); } else if (sf->name() == "changeViewOption") { sb->ArgStack->setCurrentWidget(argChangeViewOption); argChangeViewOption->OptionName->setCurrentIndex(argChangeViewOption->OptionName->findText(sf->argVal(0))); argChangeViewOption->OptionValue->setText(sf->argVal(1)); } else if (sf->name() == "setGeoLocation") { sb->ArgStack->setCurrentWidget(argSetGeoLocation); argSetGeoLocation->CityName->setText(sf->argVal(0)); argSetGeoLocation->ProvinceName->setText(sf->argVal(1)); argSetGeoLocation->CountryName->setText(sf->argVal(2)); } else if (sf->name() == "setColor") { sb->ArgStack->setCurrentWidget(argSetColor); if (sf->argVal(0).isEmpty()) sf->setArg(0, "SkyColor"); //initialize default value argSetColor->ColorName->setCurrentIndex( argSetColor->ColorName->findText(ks->data()->colorScheme()->nameFromKey(sf->argVal(0)))); argSetColor->ColorValue->setColor(QColor(sf->argVal(1).remove('\\'))); } else if (sf->name() == "loadColorScheme") { sb->ArgStack->setCurrentWidget(argLoadColorScheme); argLoadColorScheme->SchemeList->setCurrentItem( argLoadColorScheme->SchemeList->findItems(sf->argVal(0).remove('\"'), Qt::MatchExactly).at(0)); } else if (sf->name() == "stop") { sb->ArgStack->setCurrentWidget(argBlank); //no Args } else if (sf->name() == "start") { sb->ArgStack->setCurrentWidget(argBlank); //no Args } else if (sf->name() == "setClockScale") { sb->ArgStack->setCurrentWidget(argTimeScale); bool ok(false); double ts = sf->argVal(0).toDouble(&ok); if (ok) argTimeScale->TimeScale->tsbox()->changeScale(float(ts)); else argTimeScale->TimeScale->tsbox()->changeScale(0.0); } checkForChanges = true; //signal unsaved changes if the argument widgets are changed } } void ScriptBuilder::slotShowDoc() { ScriptFunction *found = nullptr; QTreeWidgetItem *currentItem = sb->FunctionTree->currentItem(); if (currentItem == nullptr || currentItem->parent() == nullptr) return; for (auto &sc : KStarsFunctionList) { if (sc->prototype() == currentItem->text(0)) { found = sc; break; } } for (auto &sc : SimClockFunctionList) { if (sc->prototype() == currentItem->text(0)) { found = sc; break; } } if (found == nullptr) { sb->AddButton->setEnabled(false); qWarning() << i18n("Function index out of bounds."); return; } sb->AddButton->setEnabled(true); sb->FuncDoc->setHtml(found->description()); } //Slots for Arg Widgets void ScriptBuilder::slotFindCity() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { if (ld->selectedCity()) { // set new location names argSetGeoLocation->CityName->setText(ld->selectedCityName()); if (!ld->selectedProvinceName().isEmpty()) { argSetGeoLocation->ProvinceName->setText(ld->selectedProvinceName()); } else { argSetGeoLocation->ProvinceName->clear(); } argSetGeoLocation->CountryName->setText(ld->selectedCountryName()); ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setGeoLocation") { setUnsavedChanges(true); sf->setArg(0, ld->selectedCityName()); sf->setArg(1, ld->selectedProvinceName()); sf->setArg(2, ld->selectedCountryName()); } else { warningMismatch("setGeoLocation"); } } } delete ld; } void ScriptBuilder::slotFindObject() { QPointer fd = new FindDialog(ks); if (fd->exec() == QDialog::Accepted && fd->targetObject()) { setUnsavedChanges(true); if (sender() == argLookToward->FindButton) argLookToward->FocusEdit->setEditText(fd->targetObject()->name()); else argFindObject->NameEdit->setText(fd->targetObject()->name()); } delete fd; } void ScriptBuilder::slotShowOptions() { //Show tree-view of view options if (otv->exec() == QDialog::Accepted) { argChangeViewOption->OptionName->setCurrentIndex( argChangeViewOption->OptionName->findText(otv->optionsList()->currentItem()->text(0))); } } void ScriptBuilder::slotLookToward() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "lookTowards") { setUnsavedChanges(true); sf->setArg(0, argLookToward->FocusEdit->currentText()); sf->setValid(true); } else { warningMismatch("lookTowards"); } } void ScriptBuilder::slotArgFindObject() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "addLabel" || sf->name() == "removeLabel" || sf->name() == "addTrail" || sf->name() == "removeTrail") { setUnsavedChanges(true); sf->setArg(0, argFindObject->NameEdit->text()); sf->setValid(true); } else { warningMismatch(sf->name()); } } void ScriptBuilder::slotRa() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setRaDec") { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if (argSetRaDec->RABox->text().isEmpty()) return; bool ok(false); dms ra = argSetRaDec->RABox->createDms(false, &ok); if (ok) { setUnsavedChanges(true); sf->setArg(0, QString("%1").arg(ra.Hours())); if (!sf->argVal(1).isEmpty()) sf->setValid(true); } else { sf->setArg(0, QString()); sf->setValid(false); } } else { warningMismatch("setRaDec"); } } void ScriptBuilder::slotDec() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setRaDec") { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if (argSetRaDec->DecBox->text().isEmpty()) return; bool ok(false); dms dec = argSetRaDec->DecBox->createDms(true, &ok); if (ok) { setUnsavedChanges(true); sf->setArg(1, QString("%1").arg(dec.Degrees())); if (!sf->argVal(0).isEmpty()) sf->setValid(true); } else { sf->setArg(1, QString()); sf->setValid(false); } } else { warningMismatch("setRaDec"); } } void ScriptBuilder::slotAz() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setAltAz") { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if (argSetAltAz->AzBox->text().isEmpty()) return; bool ok(false); dms az = argSetAltAz->AzBox->createDms(true, &ok); if (ok) { setUnsavedChanges(true); sf->setArg(1, QString("%1").arg(az.Degrees())); if (!sf->argVal(0).isEmpty()) sf->setValid(true); } else { sf->setArg(1, QString()); sf->setValid(false); } } else { warningMismatch("setAltAz"); } } void ScriptBuilder::slotAlt() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setAltAz") { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if (argSetAltAz->AltBox->text().isEmpty()) return; bool ok(false); dms alt = argSetAltAz->AltBox->createDms(true, &ok); if (ok) { setUnsavedChanges(true); sf->setArg(0, QString("%1").arg(alt.Degrees())); if (!sf->argVal(1).isEmpty()) sf->setValid(true); } else { sf->setArg(0, QString()); sf->setValid(false); } } else { warningMismatch("setAltAz"); } } void ScriptBuilder::slotChangeDate() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setLocalTime") { setUnsavedChanges(true); QDate date = argSetLocalTime->DateWidget->date(); sf->setArg(0, QString("%1").arg(date.year())); sf->setArg(1, QString("%1").arg(date.month())); sf->setArg(2, QString("%1").arg(date.day())); if (!sf->argVal(3).isEmpty()) sf->setValid(true); } else { warningMismatch("setLocalTime"); } } void ScriptBuilder::slotChangeTime() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setLocalTime") { setUnsavedChanges(true); QTime time = argSetLocalTime->TimeBox->time(); sf->setArg(3, QString("%1").arg(time.hour())); sf->setArg(4, QString("%1").arg(time.minute())); sf->setArg(5, QString("%1").arg(time.second())); if (!sf->argVal(0).isEmpty()) sf->setValid(true); } else { warningMismatch("setLocalTime"); } } void ScriptBuilder::slotWaitFor() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "waitFor") { bool ok(false); int delay = argWaitFor->DelayBox->text().toInt(&ok); if (ok) { setUnsavedChanges(true); sf->setArg(0, QString("%1").arg(delay)); sf->setValid(true); } else { sf->setValid(false); } } else { warningMismatch("waitFor"); } } void ScriptBuilder::slotWaitForKey() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "waitForKey") { QString sKey = argWaitForKey->WaitKeyEdit->text().trimmed(); //DCOP script can only use single keystrokes; make sure entry is either one character, //or the word 'space' if (sKey.length() == 1 || sKey == "space") { setUnsavedChanges(true); sf->setArg(0, sKey); sf->setValid(true); } else { sf->setValid(false); } } else { warningMismatch("waitForKey"); } } void ScriptBuilder::slotTracking() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setTracking") { setUnsavedChanges(true); sf->setArg(0, (argSetTracking->CheckTrack->isChecked() ? i18n("true") : i18n("false"))); sf->setValid(true); } else { warningMismatch("setTracking"); } } void ScriptBuilder::slotViewOption() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "changeViewOption") { if (argChangeViewOption->OptionName->currentIndex() >= 0 && argChangeViewOption->OptionValue->text().length()) { setUnsavedChanges(true); sf->setArg(0, argChangeViewOption->OptionName->currentText()); sf->setArg(1, argChangeViewOption->OptionValue->text()); sf->setValid(true); } else { sf->setValid(false); } } else { warningMismatch("changeViewOption"); } } void ScriptBuilder::slotChangeCity() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setGeoLocation") { QString city = argSetGeoLocation->CityName->text(); if (city.length()) { setUnsavedChanges(true); sf->setArg(0, city); if (sf->argVal(2).length()) sf->setValid(true); } else { sf->setArg(0, QString()); sf->setValid(false); } } else { warningMismatch("setGeoLocation"); } } void ScriptBuilder::slotChangeProvince() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setGeoLocation") { QString province = argSetGeoLocation->ProvinceName->text(); if (province.length()) { setUnsavedChanges(true); sf->setArg(1, province); if (sf->argVal(0).length() && sf->argVal(2).length()) sf->setValid(true); } else { sf->setArg(1, QString()); //might not be invalid } } else { warningMismatch("setGeoLocation"); } } void ScriptBuilder::slotChangeCountry() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setGeoLocation") { QString country = argSetGeoLocation->CountryName->text(); if (country.length()) { setUnsavedChanges(true); sf->setArg(2, country); if (sf->argVal(0).length()) sf->setValid(true); } else { sf->setArg(2, QString()); sf->setValid(false); } } else { warningMismatch("setGeoLocation"); } } void ScriptBuilder::slotTimeScale() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setClockScale") { setUnsavedChanges(true); sf->setArg(0, QString("%1").arg(argTimeScale->TimeScale->tsbox()->timeScale())); sf->setValid(true); } else { warningMismatch("setClockScale"); } } void ScriptBuilder::slotZoom() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "zoom") { setUnsavedChanges(true); bool ok(false); argZoom->ZoomBox->text().toDouble(&ok); if (ok) { sf->setArg(0, argZoom->ZoomBox->text()); sf->setValid(true); } } else { warningMismatch("zoom"); } } void ScriptBuilder::slotExportImage() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "exportImage") { setUnsavedChanges(true); sf->setArg(0, argExportImage->ExportFileName->url().url()); sf->setArg(1, QString("%1").arg(argExportImage->ExportWidth->value())); sf->setArg(2, QString("%1").arg(argExportImage->ExportHeight->value())); sf->setValid(true); } else { warningMismatch("exportImage"); } } void ScriptBuilder::slotPrintImage() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "printImage") { setUnsavedChanges(true); sf->setArg(0, (argPrintImage->UsePrintDialog->isChecked() ? i18n("true") : i18n("false"))); sf->setArg(1, (argPrintImage->UseChartColors->isChecked() ? i18n("true") : i18n("false"))); sf->setValid(true); } else { warningMismatch("exportImage"); } } void ScriptBuilder::slotChangeColorName() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setColor") { setUnsavedChanges(true); argSetColor->ColorValue->setColor(ks->data()->colorScheme()->colorAt(argSetColor->ColorName->currentIndex())); sf->setArg(0, ks->data()->colorScheme()->keyAt(argSetColor->ColorName->currentIndex())); QString cname(argSetColor->ColorValue->color().name()); //if ( cname.at(0) == '#' ) cname = "\\" + cname; //prepend a "\" so bash doesn't think we have a comment sf->setArg(1, cname); sf->setValid(true); } else { warningMismatch("setColor"); } } void ScriptBuilder::slotChangeColor() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "setColor") { setUnsavedChanges(true); sf->setArg(0, ks->data()->colorScheme()->keyAt(argSetColor->ColorName->currentIndex())); QString cname(argSetColor->ColorValue->color().name()); //if ( cname.at(0) == '#' ) cname = "\\" + cname; //prepend a "\" so bash doesn't think we have a comment sf->setArg(1, cname); sf->setValid(true); } else { warningMismatch("setColor"); } } void ScriptBuilder::slotLoadColorScheme() { ScriptFunction *sf = ScriptList[sb->ScriptListBox->currentRow()]; if (sf->name() == "loadColorScheme") { setUnsavedChanges(true); sf->setArg(0, '\"' + argLoadColorScheme->SchemeList->currentItem()->text() + '\"'); sf->setValid(true); } else { warningMismatch("loadColorScheme"); } } void ScriptBuilder::slotClose() { saveWarning(); if (!UnsavedChanges) { ScriptList.clear(); sb->ScriptListBox->clear(); sb->ArgStack->setCurrentWidget(argBlank); close(); } } //TODO JM: INDI Scripting to be included in KDE 4.1 #if 0 void ScriptBuilder::slotINDIStartDeviceName() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "startINDI" ) { setUnsavedChanges( true ); sf->setArg(0, argStartINDI->deviceName->text()); sf->setArg(1, argStartINDI->LocalButton->isChecked() ? "true" : "false"); sf->setValid(true); } else { warningMismatch( "startINDI" ); } } void ScriptBuilder::slotINDIStartDeviceMode() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "startINDI" ) { setUnsavedChanges( true ); sf->setArg(1, argStartINDI->LocalButton->isChecked() ? "true" : "false"); sf->setValid(true); } else { warningMismatch( "startINDI" ); } } void ScriptBuilder::slotINDISetDevice() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIDevice" ) { setUnsavedChanges( true ); sf->setArg(0, argSetDeviceINDI->deviceName->text()); sf->setValid(true); } else { warningMismatch( "startINDI" ); } } void ScriptBuilder::slotINDIShutdown() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "shutdownINDI" ) { if (argShutdownINDI->deviceName->text().isEmpty()) { sf->setValid(false); return; } if (sf->argVal(0) != argShutdownINDI->deviceName->text()) setUnsavedChanges( true ); sf->setArg(0, argShutdownINDI->deviceName->text()); sf->setValid(true); } else { warningMismatch( "shutdownINDI" ); } } void ScriptBuilder::slotINDISwitchDeviceConnection() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "switchINDI" ) { if (sf->argVal(0) != (argSwitchINDI->OnButton->isChecked() ? "true" : "false")) setUnsavedChanges( true ); sf->setArg(0, argSwitchINDI->OnButton->isChecked() ? "true" : "false"); sf->setValid(true); } else { warningMismatch( "switchINDI" ); } } void ScriptBuilder::slotINDISetPortDevicePort() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIPort" ) { if (argSetPortINDI->devicePort->text().isEmpty()) { sf->setValid(false); return; } if (sf->argVal(0) != argSetPortINDI->devicePort->text()) setUnsavedChanges( true ); sf->setArg(0, argSetPortINDI->devicePort->text()); sf->setValid(true); } else { warningMismatch( "setINDIPort" ); } } void ScriptBuilder::slotINDISetTargetCoordDeviceRA() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDITargetCoord" ) { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if ( argSetTargetCoordINDI->RABox->text().isEmpty() ) { sf->setValid(false); return; } bool ok(false); dms ra = argSetTargetCoordINDI->RABox->createDms(false, &ok); if ( ok ) { if (sf->argVal(0) != QString( "%1" ).arg( ra.Hours() )) setUnsavedChanges( true ); sf->setArg( 0, QString( "%1" ).arg( ra.Hours() ) ); if ( ( ! sf->argVal(1).isEmpty() )) sf->setValid( true ); else sf->setValid(false); } else { sf->setArg( 0, QString() ); sf->setValid( false ); } } else { warningMismatch( "setINDITargetCoord" ); } } void ScriptBuilder::slotINDISetTargetCoordDeviceDEC() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDITargetCoord" ) { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if ( argSetTargetCoordINDI->DecBox->text().isEmpty() ) { sf->setValid(false); return; } bool ok(false); dms dec = argSetTargetCoordINDI->DecBox->createDms(true, &ok); if ( ok ) { if (sf->argVal(1) != QString( "%1" ).arg( dec.Degrees() )) setUnsavedChanges( true ); sf->setArg( 1, QString( "%1" ).arg( dec.Degrees() ) ); if ( ( ! sf->argVal(0).isEmpty() )) sf->setValid( true ); else sf->setValid(false); } else { sf->setArg( 1, QString() ); sf->setValid( false ); } } else { warningMismatch( "setINDITargetCoord" ); } } void ScriptBuilder::slotINDISetTargetNameTargetName() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDITargetName" ) { if (argSetTargetNameINDI->targetName->text().isEmpty()) { sf->setValid(false); return; } if (sf->argVal(0) != argSetTargetNameINDI->targetName->text()) setUnsavedChanges( true ); sf->setArg(0, argSetTargetNameINDI->targetName->text()); sf->setValid(true); } else { warningMismatch( "setINDITargetName" ); } } void ScriptBuilder::slotINDISetActionName() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIAction" ) { if (argSetActionINDI->actionName->text().isEmpty()) { sf->setValid(false); return; } if (sf->argVal(0) != argSetActionINDI->actionName->text()) setUnsavedChanges( true ); sf->setArg(0, argSetActionINDI->actionName->text()); sf->setValid(true); } else { warningMismatch( "setINDIAction" ); } } void ScriptBuilder::slotINDIWaitForActionName() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "waitForINDIAction" ) { if (argWaitForActionINDI->actionName->text().isEmpty()) { sf->setValid(false); return; } if (sf->argVal(0) != argWaitForActionINDI->actionName->text()) setUnsavedChanges( true ); sf->setArg(0, argWaitForActionINDI->actionName->text()); sf->setValid(true); } else { warningMismatch( "waitForINDIAction" ); } } void ScriptBuilder::slotINDISetFocusSpeed() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIFocusSpeed" ) { if (sf->argVal(0).toInt() != argSetFocusSpeedINDI->speedIN->value()) setUnsavedChanges( true ); sf->setArg(0, QString("%1").arg(argSetFocusSpeedINDI->speedIN->value())); sf->setValid(true); } else { warningMismatch( "setINDIFocusSpeed" ); } } void ScriptBuilder::slotINDIStartFocusDirection() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "startINDIFocus" ) { if (sf->argVal(0) != argStartFocusINDI->directionCombo->currentText()) setUnsavedChanges( true ); sf->setArg(0, argStartFocusINDI->directionCombo->currentText()); sf->setValid(true); } else { warningMismatch( "startINDIFocus" ); } } void ScriptBuilder::slotINDISetFocusTimeout() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIFocusTimeout" ) { if (sf->argVal(0).toInt() != argSetFocusTimeoutINDI->timeOut->value()) setUnsavedChanges( true ); sf->setArg(0, QString("%1").arg(argSetFocusTimeoutINDI->timeOut->value())); sf->setValid(true); } else { warningMismatch( "setINDIFocusTimeout" ); } } void ScriptBuilder::slotINDISetGeoLocationDeviceLong() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIGeoLocation" ) { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if ( argSetGeoLocationINDI->longBox->text().isEmpty()) { sf->setValid(false); return; } bool ok(false); dms longitude = argSetGeoLocationINDI->longBox->createDms(true, &ok); if ( ok ) { if (sf->argVal(0) != QString( "%1" ).arg( longitude.Degrees())) setUnsavedChanges( true ); sf->setArg( 0, QString( "%1" ).arg( longitude.Degrees() ) ); if ( ! sf->argVal(1).isEmpty() ) sf->setValid( true ); else sf->setValid(false); } else { sf->setArg( 0, QString() ); sf->setValid( false ); } } else { warningMismatch( "setINDIGeoLocation" ); } } void ScriptBuilder::slotINDISetGeoLocationDeviceLat() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIGeoLocation" ) { //do nothing if box is blank (because we could be clearing boxes while switcing argWidgets) if ( argSetGeoLocationINDI->latBox->text().isEmpty() ) { sf->setValid(false); return; } bool ok(false); dms latitude = argSetGeoLocationINDI->latBox->createDms(true, &ok); if ( ok ) { if (sf->argVal(1) != QString( "%1" ).arg( latitude.Degrees())) setUnsavedChanges( true ); sf->setArg( 1, QString( "%1" ).arg( latitude.Degrees() ) ); if ( ! sf->argVal(0).isEmpty() ) sf->setValid( true ); else sf->setValid(false); } else { sf->setArg( 1, QString() ); sf->setValid( false ); } } else { warningMismatch( "setINDIGeoLocation" ); } } void ScriptBuilder::slotINDIStartExposureTimeout() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "startINDIExposure" ) { if (sf->argVal(0).toInt() != argStartExposureINDI->timeOut->value()) setUnsavedChanges( true ); sf->setArg(0, QString("%1").arg(argStartExposureINDI->timeOut->value())); sf->setValid(true); } else { warningMismatch( "startINDIExposure" ); } } void ScriptBuilder::slotINDISetUTC() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIUTC" ) { if (argSetUTCINDI->UTC->text().isEmpty()) { sf->setValid(false); return; } if (sf->argVal(0) != argSetUTCINDI->UTC->text()) setUnsavedChanges( true ); sf->setArg(0, argSetUTCINDI->UTC->text()); sf->setValid(true); } else { warningMismatch( "setINDIUTC" ); } } void ScriptBuilder::slotINDISetScopeAction() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIScopeAction" ) { if (sf->argVal(0) != argSetScopeActionINDI->actionCombo->currentText()) setUnsavedChanges( true ); sf->setArg(0, argSetScopeActionINDI->actionCombo->currentText()); sf->setINDIProperty("CHECK"); sf->setValid(true); } else { warningMismatch( "setINDIScopeAction" ); } } void ScriptBuilder::slotINDISetFrameType() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIFrameType" ) { if (sf->argVal(0) != argSetFrameTypeINDI->typeCombo->currentText()) setUnsavedChanges( true ); sf->setArg(0, argSetFrameTypeINDI->typeCombo->currentText()); sf->setValid(true); } else { warningMismatch( "setINDIFrameType" ); } } void ScriptBuilder::slotINDISetCCDTemp() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDICCDTemp" ) { if (sf->argVal(0).toInt() != argSetCCDTempINDI->temp->value()) setUnsavedChanges( true ); sf->setArg(0, QString("%1").arg(argSetCCDTempINDI->temp->value())); sf->setValid(true); } else { warningMismatch( "setINDICCDTemp" ); } } void ScriptBuilder::slotINDISetFilterNum() { ScriptFunction * sf = ScriptList[ sb->ScriptListBox->currentRow() ]; if ( sf->name() == "setINDIFilterNum" ) { if (sf->argVal(0).toInt() != argSetFilterNumINDI->filter_num->value()) setUnsavedChanges( true ); sf->setArg(0, QString("%1").arg(argSetFilterNumINDI->filter_num->value())); sf->setValid(true); } else { warningMismatch( "setINDIFilterNum" ); } } #endif void ScriptBuilder::warningMismatch(const QString &expected) const { qWarning() << i18n("Mismatch between function and Arg widget (expected %1.)", QString(expected)); } diff --git a/kstars/tools/scriptbuilder.h b/kstars/tools/scriptbuilder.h index 11881fa37..fb05f256a 100644 --- a/kstars/tools/scriptbuilder.h +++ b/kstars/tools/scriptbuilder.h @@ -1,253 +1,253 @@ /*************************************************************************** scriptbuilder.h - description ------------------- begin : Thu Apr 17 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "scriptargwidgets.h" #include "ui_scriptbuilder.h" #include "ui_scriptnamedialog.h" #include "ui_optionstreeview.h" #include #include #include class QTreeWidget; class QTreeWidgetItem; class QTextStream; class KStars; class ScriptFunction; class OptionsTreeViewWidget : public QFrame, public Ui::OptionsTreeView { Q_OBJECT public: explicit OptionsTreeViewWidget(QWidget *p); }; class OptionsTreeView : public QDialog { Q_OBJECT public: explicit OptionsTreeView(QWidget *p); - ~OptionsTreeView() override; + virtual ~OptionsTreeView() override = default; QTreeWidget *optionsList() { return otvw->OptionsList; } void resizeColumns(); private: std::unique_ptr otvw; }; class ScriptNameWidget : public QFrame, public Ui::ScriptNameDialog { Q_OBJECT public: explicit ScriptNameWidget(QWidget *p); }; class ScriptNameDialog : public QDialog { Q_OBJECT public: explicit ScriptNameDialog(QWidget *p); ~ScriptNameDialog() override; QString scriptName() const { return snw->ScriptName->text(); } QString authorName() const { return snw->AuthorName->text(); } private slots: void slotEnableOkButton(); private: ScriptNameWidget *snw { nullptr }; QPushButton *okB { nullptr }; }; class ScriptBuilderUI : public QFrame, public Ui::ScriptBuilder { Q_OBJECT public: explicit ScriptBuilderUI(QWidget *p); }; /** * @class ScriptBuilder * A GUI tool for building behavioral DBus scripts for KStars. * * @author Jason Harris * @version 1.0 */ class ScriptBuilder : public QDialog { Q_OBJECT public: explicit ScriptBuilder(QWidget *parent); ~ScriptBuilder() override; bool unsavedChanges() const { return UnsavedChanges; } void setUnsavedChanges(bool b = true); void saveWarning(); void readScript(QTextStream &istream); void writeScript(QTextStream &ostream); bool parseFunction(QString fn_name, QStringList &fn); public slots: void slotAddFunction(); void slotMoveFunctionUp(); void slotMoveFunctionDown(); void slotArgWidget(); void slotShowDoc(); void slotNew(); void slotOpen(); void slotSave(); void slotSaveAs(); void slotRunScript(); void slotClose(); void slotCopyFunction(); void slotRemoveFunction(); void slotFindCity(); void slotFindObject(); void slotShowOptions(); void slotLookToward(); void slotArgFindObject(); void slotRa(); void slotDec(); void slotAz(); void slotAlt(); void slotChangeDate(); void slotChangeTime(); void slotWaitFor(); void slotWaitForKey(); void slotTracking(); void slotViewOption(); void slotChangeCity(); void slotChangeProvince(); void slotChangeCountry(); void slotTimeScale(); void slotZoom(); void slotExportImage(); void slotPrintImage(); void slotChangeColor(); void slotChangeColorName(); void slotLoadColorScheme(); #if 0 void slotINDIWaitCheck(bool toggleState); void slotINDIFindObject(); void slotINDIStartDeviceName(); void slotINDIStartDeviceMode(); void slotINDISetDevice(); void slotINDIShutdown(); void slotINDISwitchDeviceConnection(); void slotINDISetPortDevicePort(); void slotINDISetTargetCoordDeviceRA(); void slotINDISetTargetCoordDeviceDEC(); void slotINDISetTargetNameTargetName(); void slotINDISetActionName(); void slotINDIWaitForActionName(); void slotINDISetFocusSpeed(); void slotINDIStartFocusDirection(); void slotINDISetFocusTimeout(); void slotINDISetGeoLocationDeviceLong(); void slotINDISetGeoLocationDeviceLat(); void slotINDIStartExposureTimeout(); void slotINDISetUTC(); void slotINDISetScopeAction(); void slotINDISetFrameType(); void slotINDISetCCDTemp(); void slotINDISetFilterNum(); #endif private: void initViewOptions(); void warningMismatch(const QString &expected) const; ScriptBuilderUI *sb { nullptr }; KStars *ks { nullptr }; //parent needed for sub-dialogs QList KStarsFunctionList; QList SimClockFunctionList; #if 0 QList INDIFunctionList; #endif QList ScriptList; QWidget *argBlank { nullptr }; ArgLookToward *argLookToward { nullptr }; ArgFindObject *argFindObject { nullptr }; ArgSetRaDec *argSetRaDec { nullptr }; ArgSetAltAz *argSetAltAz { nullptr }; ArgSetLocalTime *argSetLocalTime { nullptr }; ArgWaitFor *argWaitFor { nullptr }; ArgWaitForKey *argWaitForKey { nullptr }; ArgSetTrack *argSetTracking { nullptr }; ArgChangeViewOption *argChangeViewOption { nullptr }; ArgSetGeoLocation *argSetGeoLocation { nullptr }; ArgTimeScale *argTimeScale { nullptr }; ArgZoom *argZoom { nullptr }; ArgExportImage *argExportImage { nullptr }; ArgPrintImage *argPrintImage { nullptr }; ArgSetColor *argSetColor { nullptr }; ArgLoadColorScheme *argLoadColorScheme { nullptr }; #if 0 ArgStartINDI * argStartINDI; ArgSetDeviceINDI * argSetDeviceINDI; ArgShutdownINDI * argShutdownINDI; ArgSwitchINDI * argSwitchINDI; ArgSetPortINDI * argSetPortINDI; ArgSetTargetCoordINDI * argSetTargetCoordINDI; ArgSetTargetNameINDI * argSetTargetNameINDI; ArgSetActionINDI * argSetActionINDI; ArgSetActionINDI * argWaitForActionINDI; ArgSetFocusSpeedINDI * argSetFocusSpeedINDI; ArgStartFocusINDI * argStartFocusINDI; ArgSetFocusTimeoutINDI * argSetFocusTimeoutINDI; ArgSetGeoLocationINDI * argSetGeoLocationINDI; ArgStartExposureINDI * argStartExposureINDI; ArgSetUTCINDI * argSetUTCINDI; ArgSetScopeActionINDI * argSetScopeActionINDI; ArgSetFrameTypeINDI * argSetFrameTypeINDI; ArgSetCCDTempINDI * argSetCCDTempINDI; ArgSetFilterNumINDI * argSetFilterNumINDI; #endif ScriptNameDialog *snd { nullptr }; OptionsTreeView *otv { nullptr }; QTreeWidgetItem *opsGUI { nullptr }; QTreeWidgetItem *opsToolbar { nullptr }; QTreeWidgetItem *opsShowObj { nullptr }; QTreeWidgetItem *opsShowOther { nullptr }; QTreeWidgetItem *opsCName { nullptr }; QTreeWidgetItem *opsHide { nullptr }; QTreeWidgetItem *opsSkymap { nullptr }; QTreeWidgetItem *opsLimit { nullptr }; bool UnsavedChanges { false }; bool checkForChanges { false }; QUrl currentFileURL; QString currentDir; QString currentScriptName, currentAuthor; }; diff --git a/kstars/tools/scriptfunction.cpp b/kstars/tools/scriptfunction.cpp index 43ade04ac..97f1d2e32 100644 --- a/kstars/tools/scriptfunction.cpp +++ b/kstars/tools/scriptfunction.cpp @@ -1,244 +1,240 @@ /*************************************************************************** scriptfunction.cpp - description ------------------- begin : Thu Apr 17 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "scriptfunction.h" -#include #include +#include ScriptFunction::ScriptFunction(const QString &name, const QString &desc, bool clockfcn, const QString &at1, const QString &an1, const QString &at2, const QString &an2, const QString &at3, const QString &an3, const QString &at4, const QString &an4, const QString &at5, const QString &an5, const QString &at6, const QString &an6) : INDIProp(QString()) { Name = name; ClockFunction = clockfcn; ArgType[0] = at1; ArgDBusType[0] = DBusType(at1); ArgName[0] = an1; ArgType[1] = at2; ArgDBusType[1] = DBusType(at2); ArgName[1] = an2; ArgType[2] = at3; ArgDBusType[2] = DBusType(at3); ArgName[2] = an3; ArgType[3] = at4; ArgDBusType[3] = DBusType(at4); ArgName[3] = an4; ArgType[4] = at5; ArgDBusType[4] = DBusType(at5); ArgName[4] = an5; ArgType[5] = at6; ArgDBusType[5] = DBusType(at6); ArgName[5] = an6; //Construct a richtext description of the function QString nameStyle = "%1"; //bold QString typeStyle = "%1"; //green QString paramStyle = "%1"; //blue Description = ""; Description += ""; Description += "

" + nameStyle.arg(Name + '('); NumArgs = 0; if (!at1.isEmpty() && !an1.isEmpty()) { Description += ' ' + typeStyle.arg(at1); Description += ' ' + paramStyle.arg(an1); NumArgs++; } if (!at2.isEmpty() && !an2.isEmpty()) { Description += ", " + typeStyle.arg(at2); Description += ' ' + paramStyle.arg(an2); NumArgs++; } if (!at3.isEmpty() && !an3.isEmpty()) { Description += ", " + typeStyle.arg(at3); Description += ' ' + paramStyle.arg(an3); NumArgs++; } if (!at4.isEmpty() && !an4.isEmpty()) { Description += ", " + typeStyle.arg(at4); Description += ' ' + paramStyle.arg(an4); NumArgs++; } if (!at5.isEmpty() && !an5.isEmpty()) { Description += ", " + typeStyle.arg(at5); Description += ' ' + paramStyle.arg(an5); NumArgs++; } if (!at6.isEmpty() && !an6.isEmpty()) { Description += ", " + typeStyle.arg(at6); Description += ' ' + paramStyle.arg(an6); NumArgs++; } //Set Valid=false if there are arguments (indicates that this fcn's args must be filled in) Valid = true; if (NumArgs) Valid = false; //Finish writing function prototype if (NumArgs) Description += ' '; Description += nameStyle.arg(")") + "

"; //Add description Description += desc; //Finish up Description += "

"; } //Copy constructor ScriptFunction::ScriptFunction(ScriptFunction *sf) { Name = sf->name(); Description = sf->description(); ClockFunction = sf->isClockFunction(); NumArgs = sf->numArgs(); INDIProp = sf->INDIProperty(); Valid = sf->valid(); for (unsigned int i = 0; i < 6; i++) { ArgType[i] = sf->argType(i); ArgName[i] = sf->argName(i); ArgDBusType[i] = sf->argDBusType(i); //ArgVal[i] .clear(); // JM: Some default argument values might be passed from another object as well ArgVal[i] = sf->argVal(i); } } -ScriptFunction::~ScriptFunction() -{ -} - QString ScriptFunction::DBusType(const QString &type) { if (type == QString("int")) return QString("int32"); else if (type == QString("uint")) return QString("uint32"); else if (type == QString("double")) return type; else if (type == QString("QString")) return QString("string"); else if (type == QString("bool")) return QString("boolean"); return nullptr; } QString ScriptFunction::prototype() const { QString p = Name + '('; bool args(false); if (!ArgType[0].isEmpty() && !ArgName[0].isEmpty()) { p += ' ' + ArgType[0]; p += ' ' + ArgName[0]; args = true; //assume that if any args are present, 1st arg is present } if (!ArgType[1].isEmpty() && !ArgName[1].isEmpty()) { p += ", " + ArgType[1]; p += ' ' + ArgName[1]; } if (!ArgType[2].isEmpty() && !ArgName[2].isEmpty()) { p += ", " + ArgType[2]; p += ' ' + ArgName[2]; } if (!ArgType[3].isEmpty() && !ArgName[3].isEmpty()) { p += ", " + ArgType[3]; p += ' ' + ArgName[3]; } if (!ArgType[4].isEmpty() && !ArgName[4].isEmpty()) { p += ", " + ArgType[4]; p += ' ' + ArgName[4]; } if (!ArgType[5].isEmpty() && !ArgName[5].isEmpty()) { p += ", " + ArgType[5]; p += ' ' + ArgName[5]; } if (args) p += ' '; p += ')'; return p; } QString ScriptFunction::scriptLine() const { QString out(Name); unsigned int i = 0; while (i < 6 && !ArgName[i].isEmpty()) { //Make sure strings are quoted QString value = ArgVal[i]; if (ArgDBusType[i] == "string") { if (value.isEmpty()) { value = "\"\""; } else { if (value.at(0) != '\"' && value.at(0) != '\'') { value = '\"' + value; } if (value.right(1) != "\"" && value.right(1) != "\'") { value = value + '\"'; } } } // Write DBus style prototype compatible with dbus-send format out += ' ' + ArgDBusType[i] + ':' + value; ++i; } return out; } diff --git a/kstars/tools/scriptfunction.h b/kstars/tools/scriptfunction.h index 1b61b0c60..a6fe7acb5 100644 --- a/kstars/tools/scriptfunction.h +++ b/kstars/tools/scriptfunction.h @@ -1,70 +1,70 @@ /*************************************************************************** scriptfunction.h - description ------------------- begin : Thu Apr 17 2003 copyright : (C) 2003 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include /** * * Jason Harris **/ class ScriptFunction { public: ScriptFunction(const QString &name, const QString &desc, bool clockfcn = false, const QString &at1 = QString(), const QString &an1 = QString(), const QString &at2 = QString(), const QString &an2 = QString(), const QString &at3 = QString(), const QString &an3 = QString(), const QString &at4 = QString(), const QString &an4 = QString(), const QString &at5 = QString(), const QString &an5 = QString(), const QString &at6 = QString(), const QString &an6 = QString()); explicit ScriptFunction(ScriptFunction *sf); - ~ScriptFunction(); + ~ScriptFunction() = default; QString name() const { return Name; } QString prototype() const; QString description() const { return Description; } QString argType(unsigned int n) const { return ArgType[n]; } QString argName(unsigned int n) const { return ArgName[n]; } QString argVal(unsigned int n) const { return ArgVal[n]; } QString argDBusType(unsigned int n) const { return ArgDBusType[n]; } void setValid(bool b) { Valid = b; } bool valid() const { return Valid; } void setClockFunction(bool b = true) { ClockFunction = b; } bool isClockFunction() const { return ClockFunction; } void setArg(unsigned int n, QString newVal) { ArgVal[n] = newVal; } bool checkArgs(); int numArgs() const { return NumArgs; } QString scriptLine() const; void setINDIProperty(QString prop) { INDIProp = prop; } QString INDIProperty() const { return INDIProp; } QString DBusType(const QString &type); private: QString Name, Description; QString ArgType[6]; QString ArgDBusType[6]; QString ArgName[6]; QString ArgVal[6]; QString INDIProp; bool Valid, ClockFunction; int NumArgs; }; diff --git a/kstars/tools/whatsinteresting/modelmanager.h b/kstars/tools/whatsinteresting/modelmanager.h index 7cd8f226f..52d2fc523 100644 --- a/kstars/tools/whatsinteresting/modelmanager.h +++ b/kstars/tools/whatsinteresting/modelmanager.h @@ -1,123 +1,124 @@ /*************************************************************************** modelmanager.h - K Desktop Planetarium ------------------- begin : 2012/26/05 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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. * * * ***************************************************************************/ #pragma once +#include "skyobjitem.h" + #include #include class ObsConditions; -class SkyObjItem; class SkyObjListModel; /** * @class ModelManager * @brief Manages models for QML listviews of different types of sky-objects. * * @author Samikshan Bairagya */ class ModelManager : public QObject { Q_OBJECT public: /** * @enum ModelType * @brief Model type for different types of sky-objects. */ enum ObjectList { Planets, Stars, Constellations, Galaxies, Clusters, Nebulas, Satellites, Asteroids, Comets, Supernovas, Messier, NGC, IC, Sharpless, NumberOfLists }; /** * @brief Constructor - Creates models for different sky-object types. * @param obs Pointer to an ObsConditions object. */ explicit ModelManager(ObsConditions *obs); ~ModelManager() override; /** Updates sky-object list models. */ void updateAllModels(ObsConditions *obs); void updateModel(ObsConditions *obs, QString modelName); /** Clears all sky-objects list models. */ void resetAllModels(); void setShowOnlyVisibleObjects(bool show) { showOnlyVisible = show; } bool showOnlyVisibleObjects() { return showOnlyVisible; } void setShowOnlyFavoriteObjects(bool show) { showOnlyFavorites = show; } bool showOnlyFavoriteObjects() { return showOnlyFavorites; } /** * @brief Returns model of given type. * @return Pointer to SkyObjListModel of given type. * @param type Type of sky-object model to be returned. */ SkyObjListModel *returnModel(QString modelName); int getModelNumber(QString modelName); SkyObjListModel *getTempModel() { return tempModel; } void loadNGCCatalog(); void loadICCatalog(); void loadSharplessCatalog(); bool isNGCLoaded() { return ngcLoaded; } bool isICLoaded() { return icLoaded; } bool isSharplessLoaded() { return sharplessLoaded; } signals: void loadProgressUpdated(double progress); void modelUpdated(); private: void loadLists(); void loadObjectList(QList &skyObjectList, int type); void loadNamedStarList(); void loadObjectsIntoModel(SkyObjListModel &model, QList &skyObjectList); ObsConditions *m_ObsConditions { nullptr }; QList> m_ObjectList; QList m_ModelList; bool showOnlyVisible { true }; bool showOnlyFavorites { true }; QList favoriteGalaxies; QList favoriteNebulas; QList favoriteClusters; SkyObjListModel *tempModel { nullptr }; bool ngcLoaded { false }; bool icLoaded { false }; bool sharplessLoaded { false }; }; diff --git a/kstars/tools/whatsinteresting/obsconditions.cpp b/kstars/tools/whatsinteresting/obsconditions.cpp index 09366e7af..5f11c2093 100644 --- a/kstars/tools/whatsinteresting/obsconditions.cpp +++ b/kstars/tools/whatsinteresting/obsconditions.cpp @@ -1,126 +1,122 @@ /*************************************************************************** obsconditions.cpp - K Desktop Planetarium ------------------- begin : 2012/10/07 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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 "obsconditions.h" #include #include ObsConditions::ObsConditions(int bortle, double aperture, Equipment equip, TelescopeType telType) : m_BortleClass(bortle), m_Equip(equip), m_TelType(telType), m_Aperture(aperture) { // 't' parameter switch (m_TelType) { case Reflector: m_tParam = 0.7; break; case Refractor: m_tParam = 0.9; break; case Invalid: m_tParam = -1; //invalid value break; } setLimMagnitude(); qDebug() << "Aperture value being used:" << m_Aperture; } -ObsConditions::~ObsConditions() -{ -} - QMap ObsConditions::setLMMap() { QMap LMMap; LMMap.insert(1, 7.8); LMMap.insert(2, 7.3); LMMap.insert(3, 6.8); LMMap.insert(4, 6.3); LMMap.insert(5, 5.8); LMMap.insert(6, 5.3); LMMap.insert(7, 4.8); LMMap.insert(8, 4.3); LMMap.insert(9, 3.8); return LMMap; } const QMap ObsConditions::m_LMMap = setLMMap(); void ObsConditions::setLimMagnitude() { m_LM = m_LMMap[m_BortleClass]; } double ObsConditions::getOptimumMAG() { double power = (2.81 + 2.814 * m_LM - 0.3694 * pow(m_LM, 2)) / 5; return 0.1333 * m_Aperture * sqrt(m_tParam) * pow(power, 10); } double ObsConditions::getTrueMagLim() { // qDebug()<< (4.12 + 2.5 * log10( pow(aperture,2)*t )); // return 4.12 + 2.5 * log10( pow(aperture,2)*t ); //Taking optimum magnification into consideration ///If there is no equipment available then return limiting magnitude for naked-eye if (m_Equip == None || m_Aperture == -1) return m_LM; /** * This is a more traditional formula which does not take the * 't' parameter into account. It also does not take into account * the magnification being used. The formula used is: * * TLM_trad = LM + 5*log10(aperture/7.5) * * The calculation is just based on the calculation of the * telescope's aperture to eye's pupil surface ratio. */ return m_LM + 5 * log10(m_Aperture / 7.5); } bool ObsConditions::isVisible(GeoLocation *geo, dms *lst, SkyObject *so) { if (so->type() == SkyObject::SATELLITE) { return so->alt().Degrees() > 6.0; } KStarsDateTime ut = geo->LTtoUT(KStarsDateTime(QDateTime::currentDateTime().toLocalTime())); SkyPoint sp = so->recomputeCoords(ut, geo); //check altitude of object at this time. sp.EquatorialToHorizontal(lst, geo->lat()); return (sp.alt().Degrees() > 6.0 && so->mag() < getTrueMagLim()); } void ObsConditions::setObsConditions(int bortle, double aperture, ObsConditions::Equipment equip, ObsConditions::TelescopeType telType) { m_BortleClass = bortle; setLimMagnitude(); m_Aperture = aperture; m_Equip = equip; m_TelType = telType; qDebug() << "Aperture value being used:" << m_Aperture; } diff --git a/kstars/tools/whatsinteresting/obsconditions.h b/kstars/tools/whatsinteresting/obsconditions.h index c31e285b7..6802dc1fa 100644 --- a/kstars/tools/whatsinteresting/obsconditions.h +++ b/kstars/tools/whatsinteresting/obsconditions.h @@ -1,129 +1,130 @@ /*************************************************************************** obsconditions.h - K Desktop Planetarium ------------------- begin : 2012/10/07 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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 OBS_CONDITIONS_H -#define OBS_CONDITIONS_H +#pragma once #include "kstarsdata.h" /** - * \class ObsConditions + * @class ObsConditions + * * This class deals with the observing conditions of the night sky. * The limiting magntude is calculated depending on the equipment * available to the user and the amount of light-pollution in the * user's current observing location. - * \author Samikshan Bairagya + * + * @author Samikshan Bairagya */ class ObsConditions { public: /** - * \enum Equipment - * Equipment available to the user. - */ + * @enum Equipment + * + * Equipment available to the user. + */ enum Equipment { Telescope, Binoculars, Both, None }; /** - * \enum TelescopeType - * Telescope Type (Reflector/Refractor) - */ + * @enum TelescopeType + * + * Telescope Type (Reflector/Refractor) + */ enum TelescopeType { Reflector = 0, Refractor, Invalid }; /** - * \brief Constructor - * \param bortle Rating of light pollution based on the bortle dark-sky scale. - * \param aperture Aperture of equipment. - * \param equip Equipment available to the user. - * \param telType Refelctor/Refractor type of telescope (if available) - */ + * @brief Constructor + * + * @param bortle Rating of light pollution based on the bortle dark-sky scale. + * @param aperture Aperture of equipment. + * @param equip Equipment available to the user. + * @param telType Reflector/Refractor type of telescope (if available) + */ ObsConditions(int bortle, double aperture, Equipment equip, TelescopeType telType); - /** - * \brief Destructor - */ - ~ObsConditions(); + ~ObsConditions() = default; - /** - * \brief Inline method to set available equipment - */ + /** Inline method to set available equipment */ inline void setEquipment(Equipment equip) { m_Equip = equip; } - /** - * \brief Inline method to set reflector/refractor type for telescope. - */ + /** Inline method to set reflector/refractor type for telescope. */ inline void setTelescopeType(TelescopeType telType) { m_TelType = telType; } - /** - * \brief Set limiting magnitude depending on Bortle dark-sky rating. - */ + /** Set limiting magnitude depending on Bortle dark-sky rating. */ void setLimMagnitude(); - /** - * \brief Set new observing conditions. - */ + /** Set new observing conditions. */ void setObsConditions(int bortle, double aperture, Equipment equip, TelescopeType telType); /** - * \brief Get optimum magnification under current observing conditions. - * \return Get optimum magnification under current observing conditions - */ + * @brief Get optimum magnification under current observing conditions. + * + * @return Get optimum magnification under current observing conditions + */ double getOptimumMAG(); /** - * \brief Get true limiting magnitude after taking equipment specifications into consideration. - * \return True limiting magnitude after taking equipment specifications into consideration. - */ + * @brief Get true limiting magnitude after taking equipment specifications into consideration. + * + * @return True limiting magnitude after taking equipment specifications into consideration. + */ double getTrueMagLim(); /** - * \brief Evaluate visibility of sky-object based on current observing conditions. - * \return Visibility of sky-object based on current observing conditions as a boolean. - * \param geo Geographic location of user. - * \param lst Local standard time expressed as a dms object. - * \param so SkyObject for which visibility is to be evaluated. - */ + * @brief Evaluate visibility of sky-object based on current observing conditions. + * + * @param geo Geographic location of user. + * @param lst Local standard time expressed as a dms object. + * @param so SkyObject for which visibility is to be evaluated. + * @return Visibility of sky-object based on current observing conditions as a boolean. + */ bool isVisible(GeoLocation *geo, dms *lst, SkyObject *so); /** - * \brief Create QMap to be initialised to static member variable m_LMMap - * \return QMap to be initialised to static member variable m_LMMap - */ + * @brief Create QMap to be initialised to static member variable m_LMMap + * + * @return QMap to be initialised to static member variable m_LMMap + */ static QMap setLMMap(); private: - int m_BortleClass; ///Bortle dark-sky rating (from 1-9) - Equipment m_Equip; ///Equipment type - TelescopeType m_TelType; ///Telescope type - double m_Aperture; ///Aperture of equipment - double m_tParam; ///t-parameter corresponding to telescope type - double m_LM; ///Naked-eye limiting magnitude depending on m_BortleClass - static const QMap m_LMMap; ///Lookup table mapping Bortle Scale values - ///to corresponding limiting magnitudes + /// Bortle dark-sky rating (from 1-9) + int m_BortleClass { 0 }; + /// Equipment type + Equipment m_Equip; + /// Telescope type + TelescopeType m_TelType; + /// Aperture of equipment + double m_Aperture { 0 }; + /// t-parameter corresponding to telescope type + double m_tParam { 0 }; + /// Naked-eye limiting magnitude depending on m_BortleClass + double m_LM { 0 }; + /// Lookup table mapping Bortle Scale values to corresponding limiting magnitudes + static const QMap m_LMMap; }; - -#endif diff --git a/kstars/tools/whatsinteresting/skyobjdescription.cpp b/kstars/tools/whatsinteresting/skyobjdescription.cpp index 31d571aad..3ce560d3e 100644 --- a/kstars/tools/whatsinteresting/skyobjdescription.cpp +++ b/kstars/tools/whatsinteresting/skyobjdescription.cpp @@ -1,55 +1,51 @@ +#include "skyobjdescription.h" + #include #include #include -#include "skyobjdescription.h" - SkyObjDescription::SkyObjDescription(const QString so_Name, const QString so_Type) : soName(so_Name), soType(so_Type), m_description(""), m_DownloadedData("") { QUrl wikiUrl("http://en.wikipedia.org/w/api.php?action=opensearch&search=" + soName.replace(' ', '_').toLower() + '_' + soType.toLower() + "&format=xml&limit=1.xml"); QNetworkRequest request(wikiUrl); manager = new QNetworkAccessManager(this); connect(manager, SIGNAL(finished(QNetworkReply*)), SLOT(fileDownloaded(QNetworkReply*))); manager->get(request); } -SkyObjDescription::~SkyObjDescription() -{ -} - void SkyObjDescription::fileDownloaded(QNetworkReply *reply) { m_DownloadedData = reply->readAll(); if (!m_DownloadedData.isEmpty()) { QString data(m_DownloadedData); const QString descOpeing(""); const QString descClosing(""); // retrieving description from received data if (data.contains("description", Qt::CaseInsensitive)) { int startIndex = data.lastIndexOf(descOpeing) + descOpeing.length(); int endIndex = data.lastIndexOf(descClosing); m_description = data.mid(startIndex, endIndex - startIndex); } const QString urlOpening(""); const QString urlClosing(""); // retrieving link of wikipedia page from received data if (data.contains(urlOpening, Qt::CaseInsensitive)) { int startIndex = data.lastIndexOf(urlOpening) + urlOpening.length(); int endIndex = data.lastIndexOf(urlClosing); m_url = data.mid(startIndex, endIndex - startIndex); } } reply->deleteLater(); emit downloaded(); } diff --git a/kstars/tools/whatsinteresting/skyobjdescription.h b/kstars/tools/whatsinteresting/skyobjdescription.h index f6b3047b7..92549b0f2 100644 --- a/kstars/tools/whatsinteresting/skyobjdescription.h +++ b/kstars/tools/whatsinteresting/skyobjdescription.h @@ -1,76 +1,70 @@ /*************************************************************************** skyobjdescription.h - K Desktop Planetarium ------------------- begin : 2013/10/13 copyright : (C) 2013 by Vijay Dhameliya email : vijay.atwork13@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 SKYOBJDESCRIPTION_H -#define SKYOBJDESCRIPTION_H +#pragma once #include #include #include #include #include #include /** - * \class SkyObjDescription - * Fatches short description for various sky object from wikipedia. - * \author Vijay Dhameliya + * @class SkyObjDescription + * + * Fetches short description for various sky object from wikipedia. + * + * @author Vijay Dhameliya */ class SkyObjDescription : public QObject { - Q_OBJECT + Q_OBJECT + public: /** - * @brief Constructor sends request to network for data from wikipedia API and starts - * downloading data from QUrl - * @param soName SkyObject name - * @param soType SkyObject type - */ + * @brief Constructor sends request to network for data from wikipedia API and starts + * downloading data from QUrl + * @param soName SkyObject name + * @param soType SkyObject type + */ explicit SkyObjDescription(const QString soName, const QString soType); - /** - * \brief Destructor - */ - ~SkyObjDescription() override; + virtual ~SkyObjDescription() override = default; - /** - * @return returns description if it was available on wikipedia else returns empty string - */ + /** @return returns description if it was available on wikipedia else returns empty string */ QString downloadedData() const { return m_description; } - /** - * @return returns wikipedia link for skyobject - */ + /** @return returns wikipedia link for skyobject */ QString url() const { return m_url; } signals: void downloaded(); private slots: /** - * @brief parse downloaded data to extract description of SkyObject when downloading is finished - * @param reply - */ + * @brief parse downloaded data to extract description of SkyObject when downloading is finished + * + * @param reply + */ void fileDownloaded(QNetworkReply *reply); private: QString soName, soType, m_description, m_url; - QNetworkAccessManager *manager; + QNetworkAccessManager *manager { nullptr }; QByteArray m_DownloadedData; }; - -#endif // SKYOBJDESCRIPTION_H diff --git a/kstars/tools/whatsinteresting/skyobjitem.cpp b/kstars/tools/whatsinteresting/skyobjitem.cpp index 089834d91..7b4e3af8f 100644 --- a/kstars/tools/whatsinteresting/skyobjitem.cpp +++ b/kstars/tools/whatsinteresting/skyobjitem.cpp @@ -1,259 +1,255 @@ /*************************************************************************** skyobjitem.cpp - K Desktop Planetarium ------------------- begin : 2012/21/06 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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 "skyobjitem.h" #include "deepskyobject.h" #include "ksfilereader.h" #include "kspaths.h" #include "ksplanetbase.h" #include "kstarsdata.h" #include "ksutils.h" SkyObjItem::SkyObjItem(SkyObject *so) : m_Name(so->name()), m_LongName(so->longname()), m_TypeName(so->typeName()), m_So(so) { switch (so->type()) { case SkyObject::PLANET: case SkyObject::MOON: m_Type = Planet; break; case SkyObject::STAR: case SkyObject::CATALOG_STAR: case SkyObject::MULT_STAR: m_Type = Star; break; case SkyObject::CONSTELLATION: case SkyObject::ASTERISM: m_Type = Constellation; break; case SkyObject::GALAXY: m_Type = Galaxy; break; case SkyObject::OPEN_CLUSTER: case SkyObject::GLOBULAR_CLUSTER: case SkyObject::GALAXY_CLUSTER: m_Type = Cluster; break; case SkyObject::PLANETARY_NEBULA: case SkyObject::SUPERNOVA_REMNANT: case SkyObject::GASEOUS_NEBULA: case SkyObject::DARK_NEBULA: m_Type = Nebula; break; case SkyObject::SUPERNOVA: m_Type = Supernova; } setPosition(m_So); } -SkyObjItem::~SkyObjItem() -{ -} - QVariant SkyObjItem::data(int role) { switch (role) { case DispNameRole: return getDescName(); case DispImageRole: return getImageURL(true); case DispSummaryRole: return getSummary(true); case CategoryRole: return getType(); case CategoryNameRole: return getTypeName(); default: return QVariant(); } } ///Moved to skyobjlistmodel.cpp /* QHash SkyObjItem::roleNames() const { QHash roles; roles[DispNameRole] = "dispName"; roles[CategoryRole] = "type"; roles[CategoryNameRole] = "typeName"; return roles; } */ void SkyObjItem::setPosition(SkyObject *so) { double altitude; dms azimuth; if (so->type() == SkyObject::SATELLITE) { altitude = so->alt().Degrees(); azimuth = so->az(); } else { KStarsData *data = KStarsData::Instance(); KStarsDateTime ut = data->geo()->LTtoUT(KStarsDateTime(QDateTime::currentDateTime().toLocalTime())); SkyPoint sp = so->recomputeCoords(ut, data->geo()); //check altitude of object at this time. sp.EquatorialToHorizontal(data->lst(), data->geo()->lat()); altitude = sp.alt().Degrees(); azimuth = sp.az(); } double rounded_altitude = (int)(altitude / 5.0) * 5.0; if (rounded_altitude <= 0) m_Position = "" + xi18n("NOT VISIBLE: About %1 degrees below the %2 horizon", -rounded_altitude, KSUtils::toDirectionString(azimuth)) + ""; else m_Position = "" + xi18n("Now visible: About %1 degrees above the %2 horizon", rounded_altitude, KSUtils::toDirectionString(azimuth)) + ""; } QString SkyObjItem::getImageURL(bool preferThumb) const { QString thumbURL = QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::GenericDataLocation, "thumb-" + m_So->name().toLower().remove(' ') + ".png")) .url(); QString fullSizeURL = QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::GenericDataLocation, "image-" + m_So->name().toLower().remove(' ') + ".png")) .url(); QString wikiImageURL = QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::GenericDataLocation, "descriptions/wikiImage-" + m_So->name().toLower().remove(' ') + ".png")) .url(); QString XPlanetURL = QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::GenericDataLocation, "xplanet/" + m_So->name() + ".png")) .url(); //First try to return the preferred file if (!thumbURL.isEmpty() && preferThumb) return thumbURL; if (!fullSizeURL.isEmpty() && (!preferThumb)) return fullSizeURL; //If that fails, try to return the large image first, then the thumb, and then if it is a planet, the xplanet image. Finally if all else fails, the wiki image. QString fname = fullSizeURL; if (fname.isEmpty()) { fname = thumbURL; } if (fname.isEmpty() && m_Type == Planet) { fname = XPlanetURL; } if (fname.isEmpty()) { fname = wikiImageURL; } return fname; } QString SkyObjItem::getSummary(bool includeDescription) const { if (includeDescription) return m_So->typeName() + "
" + getRADE() + "
" + getAltAz() + "

" + loadObjectDescription(); else return m_So->typeName() + "
" + getRADE() + "
" + getAltAz(); } QString SkyObjItem::getSurfaceBrightness() const { /** Surface Brightness is applicable only for extended light sources like * Deep-Sky Objects. Here we use the formula SB = m + log10(a*b/4) * where m is the magnitude of the sky-object. a and b are the major and minor * axis lengths of the objects respectively in arcminutes. SB is the surface * brightness obtained in mag * arcminutes^-2 */ DeepSkyObject *dso = dynamic_cast(m_So); float SB = m_So->mag(); if (dso != nullptr) SB += 2.5 * log10(dso->a() * dso->b() / 4); switch (getType()) { case Galaxy: case Nebula: return QLocale().toString(SB, 'f', 2) + "
(mag/arcmin^2)"; default: return QString(" --"); // Not applicable for other sky-objects } } QString SkyObjItem::getSize() const { switch (getType()) { case Galaxy: case Cluster: case Nebula: return QLocale().toString(((DeepSkyObject *)m_So)->a(), 'f', 2) + "\""; case Planet: return QLocale().toString(((KSPlanetBase *)m_So)->angSize(), 'f', 2) + "\""; default: return QString(" --"); } } inline QString SkyObjItem::loadObjectDescription() const { QFile file; QString fname = "description-" + getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists()) { if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString line; line = in.readLine(); //This should only read the description since the source is on the next line file.close(); return line; } } return getTypeName(); } QString SkyObjItem::getRADE() const { return "RA: " + m_So->ra().toHMSString() + "
DE: " + m_So->dec().toDMSString(); } QString SkyObjItem::getAltAz() const { return "Alt: " + QString::number(m_So->alt().Degrees(), 'f', 2) + ", Az: " + QString::number(m_So->az().Degrees(), 'f', 2); } float SkyObjItem::getMagnitude() const { return m_So->mag(); } diff --git a/kstars/tools/whatsinteresting/skyobjitem.h b/kstars/tools/whatsinteresting/skyobjitem.h index 0aed835a5..6c0a774bc 100644 --- a/kstars/tools/whatsinteresting/skyobjitem.h +++ b/kstars/tools/whatsinteresting/skyobjitem.h @@ -1,185 +1,185 @@ /*************************************************************************** skyobjitem.h - K Desktop Planetarium ------------------- begin : 2012/21/06 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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. * * * ***************************************************************************/ #pragma once #include #include class SkyObject; /** * @class SkyObjItem * Represents an item in the list of interesting sky-objects. * * @author Samikshan Bairagya */ class SkyObjItem { public: /** * @enum SkyObjectRoles * User-defined role for the SkyObjItem */ enum SkyObjectRoles { DispNameRole = Qt::UserRole + 1, DispImageRole, DispSummaryRole, CategoryRole, CategoryNameRole }; /** * @enum Type * The type classification for the SkyObjItem */ enum Type { Planet, Star, Constellation, Galaxy, Cluster, Nebula, Supernova }; /** * @brief Constructor * @param so Pointer to the SkyObject which the SkyObjItem represents. */ explicit SkyObjItem(SkyObject *so = nullptr); - ~SkyObjItem(); + ~SkyObjItem() = default; /** * @brief Get data associated with a particular role for the SkyObjItem * @param role User-defined role for which data is required * @return QVariant data associated with role */ QVariant data(int role); /** * @brief Get name of sky-object associated with the SkyObjItem. * @return Name of sky-object associated with the SkyObjItem as a QString */ inline QString getName() const { return m_Name; } /** * @brief Get longname of sky-object associated with the SkyObjItem. * @return Longname of sky-object associated with the SkyObjItem as a QString */ inline QString getDescName() const { if (m_LongName == m_Name) return m_LongName; else return m_LongName + "\n (" + m_Name + ")"; } /** * @brief Get longname of sky-object associated with the SkyObjItem. * @return Longname of sky-object associated with the SkyObjItem as a QString */ inline QString getLongName() const { return m_LongName; } /** * @brief Get category of sky-object associated with the SkyObjItem as a QString. * @return Category of sky-object associated with the SkyObjItem as a QString. */ inline QString getTypeName() const { return m_TypeName; } /** * @brief Get category of sky-object associated with the SkyObjItem as an integer. * @return Category of sky-object associated with the SkyObjItem as a QString as an integer. */ inline int getType() const { return m_Type; } /** * @brief Get current position of sky-object associated with the SkyObjItem. * @return Current position of sky-object associated with the SkyObjItem. */ inline QString getPosition() const { return m_Position; } /** * @brief Get current RA/DE of sky-object associated with the SkyObjItem. * @return Current RA/DE of sky-object associated with the SkyObjItem. */ QString getRADE() const; /** * @brief Get current Altitute and Azimuth of sky-object associated with the SkyObjItem. * @return Current Altitute and Azimuth of sky-object associated with the SkyObjItem. */ QString getAltAz() const; /** * @brief Get sky-object associated with the SkyObjItem. * @return Pointer to SkyObject associated with the SkyObjItem. */ inline SkyObject *getSkyObject() { return m_So; } QString getImageURL(bool preferThumb) const; inline QString loadObjectDescription() const; /** * @brief Get Summary Description for the SkyObjItem. * @return Summary Description for the SkyObjItem as a QString. */ QString getSummary(bool includeDescription) const; /** * @brief Get magnitude of sky-object associated with the SkyObjItem. * @return Magnitude of sky-object associated with the SkyObjItem. */ float getMagnitude() const; /** * @brief Get surface-brightness of sky-object associated with the SkyObjItem as a QString * to be displayed on the details-view. * @return Surface-brightness of sky-object associated with the SkyObjItem as a QString. */ QString getSurfaceBrightness() const; /** * @brief Get size of sky-object associated with the SkyObjItem as a QString * to be displayed on the details-view. * @return Size of sky-object associated with the SkyObjItem as a QString. */ QString getSize() const; /** * @brief Set current position of the sky-object in the sky. * @param so Pointer to SkyObject for which position information is required. */ void setPosition(SkyObject *so); private: /// Name of sky-object QString m_Name; /// Long name of sky-object(if available) QString m_LongName; /// Category of sky-object QString m_TypeName; /// Position of sky-object in the sky. QString m_Position; /// Category of sky-object of type SkyObjItem::Type Type m_Type { SkyObjItem::Planet }; /// Pointer to SkyObject represented by SkyObjItem SkyObject *m_So { nullptr }; }; diff --git a/kstars/tools/whatsinteresting/wiview.cpp b/kstars/tools/whatsinteresting/wiview.cpp index 95fb5e3c8..23f178a4a 100644 --- a/kstars/tools/whatsinteresting/wiview.cpp +++ b/kstars/tools/whatsinteresting/wiview.cpp @@ -1,1030 +1,1026 @@ /*************************************************************************** wiview.cpp - K Desktop Planetarium ------------------- begin : 2012/26/05 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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 "wiview.h" #include "kspaths.h" #include "kstars.h" #include "modelmanager.h" #include "obsconditions.h" #include "Options.h" #include "skymap.h" #include "skymapcomposite.h" #include "skyobjitem.h" #include "skyobjlistmodel.h" #include "starobject.h" #include "wiequipsettings.h" #include "dialogs/detaildialog.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_INDI #include #include "indi/indilistener.h" #endif WIView::WIView(QWidget *parent) : QWidget(parent) { //These settings are like this just to get it started. int bortle = Options::bortleClass(); int aperture = 100; ObsConditions::Equipment equip = ObsConditions::Telescope; ObsConditions::TelescopeType telType = ObsConditions::Reflector; m_Obs = new ObsConditions(bortle, aperture, equip, telType); m_ModManager.reset(new ModelManager(m_Obs)); m_BaseView = new QQuickView(); ///To use i18n() instead of qsTr() in qml/wiview.qml for translation //KDeclarative kd; // kd.setDeclarativeEngine(m_BaseView->engine()); //kd.initialize(); //kd.setupBindings(); m_Ctxt = m_BaseView->rootContext(); m_Ctxt->setContextProperty("soListModel", m_ModManager->getTempModel()); // This is to avoid an error saying it doesn't exist. ///Use instead of KDeclarative m_Ctxt->setContextObject(new KLocalizedContext(m_BaseView)); #if 0 QString WI_Location; #if defined(Q_OS_OSX) WI_Location = QCoreApplication::applicationDirPath() + "/../Resources/data/tools/whatsinteresting/qml/wiview.qml"; if (!QFileInfo(WI_Location).exists()) WI_Location = KSPaths::locate(QStandardPaths::AppDataLocation, "tools/whatsinteresting/qml/wiview.qml"); #elif defined(Q_OS_WIN) WI_Location = KSPaths::locate(QStandardPaths::GenericDataLocation, "tools/whatsinteresting/qml/wiview.qml"); #else WI_Location = KSPaths::locate(QStandardPaths::AppDataLocation, "tools/whatsinteresting/qml/wiview.qml"); #endif m_BaseView->setSource(QUrl::fromLocalFile(WI_Location)); #endif m_BaseView->setSource(QUrl("qrc:/qml/whatisinteresting/wiview.qml")); m_BaseObj = m_BaseView->rootObject(); m_ProgressBar = m_BaseObj->findChild("progressBar"); m_loadingMessage = m_BaseObj->findChild("loadingMessage"); m_CategoryTitle = m_BaseObj->findChild(QString("categoryTitle")); m_ViewsRowObj = m_BaseObj->findChild(QString("viewsRowObj")); connect(m_ViewsRowObj, SIGNAL(categorySelected(QString)), this, SLOT(onCategorySelected(QString))); connect(m_ViewsRowObj, SIGNAL(inspectSkyObject(QString)), this, SLOT(inspectSkyObject(QString))); m_SoListObj = m_BaseObj->findChild("soListObj"); connect(m_SoListObj, SIGNAL(soListItemClicked(int)), this, SLOT(onSoListItemClicked(int))); m_DetailsViewObj = m_BaseObj->findChild("detailsViewObj"); descTextObj = m_DetailsViewObj->findChild("descTextObj"); infoBoxText = m_DetailsViewObj->findChild("infoBoxText"); m_NextObj = m_BaseObj->findChild("nextObj"); connect(m_NextObj, SIGNAL(nextObjClicked()), this, SLOT(onNextObjClicked())); m_PrevObj = m_BaseObj->findChild("prevObj"); connect(m_PrevObj, SIGNAL(prevObjClicked()), this, SLOT(onPrevObjClicked())); m_CenterButtonObj = m_BaseObj->findChild("centerButtonObj"); connect(m_CenterButtonObj, SIGNAL(centerButtonClicked()), this, SLOT(onCenterButtonClicked())); autoCenterCheckbox = m_DetailsViewObj->findChild("autoCenterCheckbox"); autoTrackCheckbox = m_DetailsViewObj->findChild("autoTrackCheckbox"); m_SlewTelescopeButtonObj = m_BaseObj->findChild("slewTelescopeButtonObj"); connect(m_SlewTelescopeButtonObj, SIGNAL(slewTelescopeButtonClicked()), this, SLOT(onSlewTelescopeButtonClicked())); m_DetailsButtonObj = m_BaseObj->findChild("detailsButtonObj"); connect(m_DetailsButtonObj, SIGNAL(detailsButtonClicked()), this, SLOT(onDetailsButtonClicked())); QObject *settingsIconObj = m_BaseObj->findChild("settingsIconObj"); connect(settingsIconObj, SIGNAL(settingsIconClicked()), this, SLOT(onSettingsIconClicked())); inspectIconObj = m_BaseObj->findChild("inspectIconObj"); connect(inspectIconObj, SIGNAL(inspectIconClicked(bool)), this, SLOT(onInspectIconClicked(bool))); visibleIconObj = m_BaseObj->findChild("visibleIconObj"); connect(visibleIconObj, SIGNAL(visibleIconClicked(bool)), this, SLOT(onVisibleIconClicked(bool))); favoriteIconObj = m_BaseObj->findChild("favoriteIconObj"); connect(favoriteIconObj, SIGNAL(favoriteIconClicked(bool)), this, SLOT(onFavoriteIconClicked(bool))); QObject *reloadIconObj = m_BaseObj->findChild("reloadIconObj"); connect(reloadIconObj, SIGNAL(reloadIconClicked()), this, SLOT(onReloadIconClicked())); QObject *downloadIconObj = m_BaseObj->findChild("downloadIconObj"); connect(downloadIconObj, SIGNAL(downloadIconClicked()), this, SLOT(onUpdateIconClicked())); m_BaseView->setResizeMode(QQuickView::SizeRootObjectToView); m_BaseView->show(); // Fix some weird issue with what's interesting panel view under Windows // In Qt 5.9 it content is messed up and there is no way to close the panel #ifdef Q_OS_WIN m_BaseView->setFlags(Qt::WindowCloseButtonHint); #endif connect(KStars::Instance()->map(), SIGNAL(objectClicked(SkyObject*)), this, SLOT(inspectSkyObjectOnClick(SkyObject*))); manager.reset(new QNetworkAccessManager()); setProgressBarVisible(true); connect(m_ModManager.get(), SIGNAL(loadProgressUpdated(double)), this, SLOT(updateProgress(double))); connect(m_ModManager.get(), SIGNAL(modelUpdated()), this, SLOT(refreshListView())); m_ViewsRowObj->setProperty("enabled", false); inspectOnClick = false; nightVision = m_BaseObj->findChild("nightVision"); //if (Options::darkAppColors()) // nightVision->setProperty("state", "active"); } -WIView::~WIView() -{ -} - void WIView::setNightVisionOn(bool on) { if (on) nightVision->setProperty("state", "active"); else nightVision->setProperty("state", ""); if (m_CurSoItem != nullptr) loadDetailsView(m_CurSoItem, m_CurIndex); } void WIView::setProgressBarVisible(bool visible) { m_ProgressBar->setProperty("visible", visible); } void WIView::updateProgress(double value) { m_ProgressBar->setProperty("value", value); if (value == 1) { setProgressBarVisible(false); m_ViewsRowObj->setProperty("enabled", true); m_loadingMessage->setProperty("state", ""); } else { setProgressBarVisible(true); m_loadingMessage->setProperty("state", "loading"); } } void WIView::updateObservingConditions() { int bortle = Options::bortleClass(); /** NOTE This part of the code dealing with equipment type is presently not required as WI does not differentiate between Telescope and Binoculars. It only needs the aperture of the equipment whichever available. However this is kept as a part of the code as support to be utilised in the future. **/ ObsConditions::Equipment equip = ObsConditions::None; if (Options::telescopeCheck() && Options::binocularsCheck()) equip = ObsConditions::Both; else if (Options::telescopeCheck()) equip = ObsConditions::Telescope; else if (Options::binocularsCheck()) equip = ObsConditions::Binoculars; ObsConditions::TelescopeType telType; if (KStars::Instance()->getWIEquipSettings()) telType = (equip == ObsConditions::Telescope) ? KStars::Instance()->getWIEquipSettings()->getTelType() : ObsConditions::Invalid; else telType = ObsConditions::Invalid; int aperture = 100; //This doesn't work correctly, FIXME!! // if(KStars::Instance()->getWIEquipSettings()) // aperture = KStars::Instance()->getWIEquipSettings()->getAperture(); if (!m_Obs) m_Obs = new ObsConditions(bortle, aperture, equip, telType); else m_Obs->setObsConditions(bortle, aperture, equip, telType); } void WIView::onCategorySelected(QString model) { m_CurrentObjectListName = model; m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName)); m_CurIndex = -2; if (!m_ModManager->showOnlyVisibleObjects()) visibleIconObj->setProperty("state", "unchecked"); if (!m_ModManager->showOnlyFavoriteObjects()) favoriteIconObj->setProperty("state", "unchecked"); if (model == "ngc" && (!m_ModManager->isNGCLoaded())) { QtConcurrent::run(m_ModManager.get(), &ModelManager::loadNGCCatalog); return; } if (model == "ic" && (!m_ModManager->isICLoaded())) { QtConcurrent::run(m_ModManager.get(), &ModelManager::loadICCatalog); return; } if (model == "sharpless" && (!m_ModManager->isSharplessLoaded())) { QtConcurrent::run(m_ModManager.get(), &ModelManager::loadSharplessCatalog); return; } updateModel(*m_Obs); } void WIView::onSoListItemClicked(int index) { SkyObjItem *soitem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem(index); if (soitem) loadDetailsView(soitem, index); } void WIView::onNextObjClicked() { if (!m_CurrentObjectListName.isEmpty()) { int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); SkyObjItem *nextItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize); loadDetailsView(nextItem, (m_CurIndex + 1) % modelSize); } } void WIView::onPrevObjClicked() { if (!m_CurrentObjectListName.isEmpty()) { int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); SkyObjItem *prevItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize); loadDetailsView(prevItem, (m_CurIndex - 1 + modelSize) % modelSize); } } void WIView::onCenterButtonClicked() { ///Center map on selected sky-object SkyObject *so = m_CurSoItem->getSkyObject(); KStars *kstars = KStars::Instance(); if (so) { kstars->map()->setFocusPoint(so); kstars->map()->setFocusObject(so); kstars->map()->setDestination(*kstars->map()->focusPoint()); Options::setIsTracking(autoTrackCheckbox->property("checked") == true); } } void WIView::onSlewTelescopeButtonClicked() { if (KMessageBox::Continue == KMessageBox::warningContinueCancel(nullptr, "Are you sure you want your telescope to slew to this object?", i18n("Continue Slew"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "continue_wi_slew_warning")) { #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 == nullptr) 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); gd->setProperty(&SlewCMD); gd->runCommand(INDI_SEND_COORDS, m_CurSoItem->getSkyObject()); /// Slew map to selected sky-object onCenterButtonClicked(); return; } KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); #endif } } void WIView::onDetailsButtonClicked() { ///Code taken from WUTDialog::slotDetails() KStars *kstars = KStars::Instance(); SkyObject *so = m_CurSoItem->getSkyObject(); if (so) { DetailDialog *detail = new DetailDialog(so, kstars->data()->lt(), kstars->data()->geo(), kstars); detail->exec(); delete detail; } } void WIView::onSettingsIconClicked() { KStars *kstars = KStars::Instance(); kstars->showWISettingsUI(); } void WIView::onReloadIconClicked() { if (!m_CurrentObjectListName.isEmpty()) { updateModel(*m_Obs); m_CurIndex = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjIndex(m_CurSoItem); } loadDetailsView(m_CurSoItem, m_CurIndex); } void WIView::onVisibleIconClicked(bool visible) { m_ModManager->setShowOnlyVisibleObjects(visible); onReloadIconClicked(); } void WIView::onFavoriteIconClicked(bool favorites) { m_ModManager->setShowOnlyFavoriteObjects(favorites); onReloadIconClicked(); } void WIView::onUpdateIconClicked() { QMessageBox mbox; QPushButton *currentObject = mbox.addButton("Current Object", QMessageBox::AcceptRole); QPushButton *missingObjects = nullptr; QPushButton *allObjects = nullptr; mbox.setText("Please choose which object(s) to try to update with Wikipedia data."); if (!m_CurrentObjectListName.isEmpty()) { missingObjects = mbox.addButton("Objects with no data", QMessageBox::AcceptRole); allObjects = mbox.addButton("Entire List", QMessageBox::AcceptRole); } QPushButton *cancel = mbox.addButton("Cancel", QMessageBox::AcceptRole); mbox.setDefaultButton(cancel); mbox.exec(); if (mbox.clickedButton() == currentObject) { if (m_CurSoItem != nullptr) { tryToUpdateWikipediaInfo(m_CurSoItem, getWikipediaName(m_CurSoItem)); } } else if (mbox.clickedButton() == allObjects || mbox.clickedButton() == missingObjects) { SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName); if (model->rowCount() > 0) { tryToUpdateWikipediaInfoInModel(mbox.clickedButton() == missingObjects); } else { qDebug() << "No Objects in List!"; } } } void WIView::refreshListView() { m_Ctxt->setContextProperty("soListModel", nullptr); if (!m_CurrentObjectListName.isEmpty()) m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName)); if (m_CurIndex == -2) onSoListItemClicked(0); if (m_CurIndex != -1) m_SoListObj->setProperty("currentIndex", m_CurIndex); } void WIView::updateModel(ObsConditions& obs) { if (!m_CurrentObjectListName.isEmpty()) { m_Obs = &obs; m_ModManager->updateModel(m_Obs, m_CurrentObjectListName); } } void WIView::inspectSkyObject(const QString& name) { if (!name.isEmpty() && name != "star") { SkyObject *obj = KStarsData::Instance()->skyComposite()->findByName(name); if (obj) inspectSkyObject(obj); } } void WIView::inspectSkyObjectOnClick(SkyObject *obj) { if (inspectOnClick && KStars::Instance()->isWIVisible()) inspectSkyObject(obj); } void WIView::inspectSkyObject(SkyObject *obj) { if (!obj) return; if (obj->name() != "star") { m_CurrentObjectListName = ""; trackedItem.reset(new SkyObjItem(obj)); loadDetailsView(trackedItem.get(), -1); m_BaseObj->setProperty("state", "singleItemSelected"); m_CategoryTitle->setProperty("text", "Selected Object"); } } void WIView::loadDetailsView(SkyObjItem *soitem, int index) { if (soitem == nullptr) return; int modelSize = -1; if (index != -1) modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); if (soitem != m_CurSoItem) m_CurSoItem = soitem; m_CurIndex = index; if (modelSize <= 1) { m_NextObj->setProperty("visible", "false"); m_PrevObj->setProperty("visible", "false"); } else { SkyObjItem *nextItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize); SkyObjItem *prevItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize); m_NextObj->setProperty("visible", "true"); m_PrevObj->setProperty("visible", "true"); QObject *nextTextObj = m_NextObj->findChild("nextTextObj"); nextTextObj->setProperty("text", nextItem->getName()); QObject *prevTextObj = m_PrevObj->findChild("prevTextObj"); prevTextObj->setProperty("text", prevItem->getName()); } QObject *sonameObj = m_DetailsViewObj->findChild("sonameObj"); QObject *posTextObj = m_DetailsViewObj->findChild("posTextObj"); QObject *detailImage = m_DetailsViewObj->findChild("detailImage"); QObject *detailsTextObj = m_DetailsViewObj->findChild("detailsTextObj"); sonameObj->setProperty("text", soitem->getDescName()); posTextObj->setProperty("text", soitem->getPosition()); detailImage->setProperty("refreshableSource", soitem->getImageURL(false)); loadObjectDescription(soitem); infoBoxText->setProperty( "text", "

No Wikipedia information.
Please try to download it using the orange download button below."); loadObjectInfoBox(soitem); QString summary = soitem->getSummary(false); QString magText; if (soitem->getType() == SkyObjItem::Constellation) magText = xi18n("Magnitude: --"); else magText = xi18n("Magnitude: %1", QLocale().toString(soitem->getMagnitude(), 'f', 2)); QString sbText = xi18n("Surface Brightness: %1", soitem->getSurfaceBrightness()); QString sizeText = xi18n("Size: %1", soitem->getSize()); QString details = summary + "
" + sbText + "
" + magText + "
" + sizeText; detailsTextObj->setProperty("text", details); if (autoCenterCheckbox->property("checked") == true) { QTimer::singleShot(500, this, SLOT(onCenterButtonClicked())); } if (m_CurIndex != -1) m_SoListObj->setProperty("currentIndex", m_CurIndex); } QString WIView::getWikipediaName(SkyObjItem *soitem) { if (!soitem) return ""; QString name; if (soitem->getName().toLower().startsWith(QLatin1String("m "))) name = soitem->getName().replace("M ", "Messier_").remove(' '); else if (soitem->getName().toLower().startsWith(QLatin1String("ngc"))) name = soitem->getName().toLower().replace("ngc", "NGC_").remove(' '); else if (soitem->getName().toLower().startsWith(QLatin1String("ic"))) name = soitem->getName().toLower().replace("ic", "IC_").remove(' '); else if (soitem->getType() == SkyObjItem::Constellation) { QStringList words = soitem->getName().split(" "); for (int i = 0; i < words.size(); i++) { QString temp = words.at(i).toLower(); temp[0] = temp[0].toUpper(); words.replace(i, temp); } name = words.join("_") + "_(constellation)"; if (name.contains("Serpens")) name = "Serpens_(constellation)"; } else if (soitem->getTypeName() == "Asteroid") name = soitem->getName().remove(' ') + "_(asteroid)"; else if (soitem->getTypeName() == "Comet") name = soitem->getLongName(); else if (soitem->getType() == SkyObjItem::Planet && soitem->getName() != "Sun" && soitem->getName() != "Moon") name = soitem->getName().remove(' ') + "_(planet)"; else if (soitem->getType() == SkyObjItem::Star) { StarObject *star = dynamic_cast(soitem->getSkyObject()); // The greek name seems to give the most consistent search results for opensearch. name = star->gname(false).replace(' ', '_'); if (name.isEmpty()) name = soitem->getName().replace(' ', '_') + "_(star)"; name.remove('[').remove(']'); } else name = soitem->getName().remove(' '); return name; } void WIView::updateWikipediaDescription(SkyObjItem *soitem) { if (!soitem) return; QString name = getWikipediaName(soitem); QUrl url("https://en.wikipedia.org/w/api.php?action=opensearch&search=" + name + "&format=xml"); QNetworkReply *response = manager->get(QNetworkRequest(url)); QTimer::singleShot(30000, response, [response] { //Shut it down after 30 sec. response->abort(); response->deleteLater(); qDebug() << "Wikipedia Download Timed out."; }); connect(response, &QNetworkReply::finished, this, [soitem, this, response] { response->deleteLater(); if (response->error() != QNetworkReply::NoError) return; QString contentType = response->header(QNetworkRequest::ContentTypeHeader).toString(); if (!contentType.contains("charset=utf-8")) { qWarning() << "Content charsets other than utf-8 are not implemented yet."; return; } QString result = QString::fromUtf8(response->readAll()); int leftPos = result.indexOf("") - leftPos; int leftURL = result.indexOf("") + 26; int rightURL = result.indexOf("") - leftURL; QString srchtml = "\n

Source: (" + "Wikipedia)"; //Note the \n is so that the description is put on another line in the file. Doesn't affect the display but allows the source to be loaded in the details but not the list. QString html = "" + result.mid(leftPos, rightPos) + srchtml + ""; saveObjectInfoBoxText(soitem, "description", html); //TODO is this explicitly needed now with themes? #if 0 QString color = (Options::darkAppColors()) ? "red" : "white"; QString linkColor = (Options::darkAppColors()) ? "red" : "yellow"; html = "" + html + ""; #endif if (soitem == m_CurSoItem) descTextObj->setProperty("text", html); refreshListView(); }); } void WIView::loadObjectDescription(SkyObjItem *soitem) { QFile file; QString fname = "description-" + soitem->getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists()) { if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); bool isDarkTheme = (Options::currentTheme() == "Night Vision"); QString color = (isDarkTheme) ? "red" : "white"; QString linkColor = (isDarkTheme) ? "red" : "yellow"; QString line = "
" + in.readAll() + ""; descTextObj->setProperty("text", line); file.close(); } } else { descTextObj->setProperty("text", soitem->getTypeName()); } } void WIView::loadObjectInfoBox(SkyObjItem *soitem) { if (!soitem) return; QFile file; QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists()) { if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString infoBoxHTML; while (!in.atEnd()) { infoBoxHTML = in.readAll(); QString wikiImageName = QUrl::fromLocalFile( KSPaths::locate(QStandardPaths::GenericDataLocation, "descriptions/wikiImage-" + soitem->getName().toLower().remove(' ') + ".png")) .url(); if (!wikiImageName.isEmpty()) { int captionEnd = infoBoxHTML.indexOf( ""); //Start looking for the image AFTER the caption. Planets have images in their caption. if (captionEnd == -1) captionEnd = 0; int leftImg = infoBoxHTML.indexOf("src=\"", captionEnd) + 5; int rightImg = infoBoxHTML.indexOf("\"", leftImg) - leftImg; QString imgURL = infoBoxHTML.mid(leftImg, rightImg); infoBoxHTML.replace(imgURL, wikiImageName); } bool isDarkTheme = (Options::currentTheme() == "Night Vision"); QString color = (isDarkTheme) ? "red" : "white"; QString linkColor = (isDarkTheme) ? "red" : "yellow"; if (isDarkTheme) infoBoxHTML.replace("color: white", "color: " + color); infoBoxHTML = "" + infoBoxHTML + ""; infoBoxText->setProperty("text", infoBoxHTML); } file.close(); } } } void WIView::tryToUpdateWikipediaInfoInModel(bool onlyMissing) { SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName); int objectNum = model->rowCount(); for (int i = 0; i < objectNum; i++) { SkyObjItem *soitem = model->getSkyObjItem(i); QFile file; QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists() && onlyMissing) continue; tryToUpdateWikipediaInfo(soitem, getWikipediaName(soitem)); } } void WIView::tryToUpdateWikipediaInfo(SkyObjItem *soitem, QString name) { if (name.isEmpty() || !soitem) return; QUrl url("https://en.wikipedia.org/w/index.php?action=render&title=" + name + "&redirects"); QNetworkReply *response = manager->get(QNetworkRequest(url)); QTimer::singleShot(30000, response, [response] { //Shut it down after 30 sec. response->abort(); response->deleteLater(); qDebug() << "Wikipedia Download Timed out."; }); connect(response, &QNetworkReply::finished, this, [name, response, soitem, this] { response->deleteLater(); if (response->error() == QNetworkReply::ContentNotFoundError) { QString html = "
Sorry, No Wikipedia article with this object name seems to exist. It is possible that " "one does exist but does not match the namimg scheme."; saveObjectInfoBoxText(soitem, "infoText", html); infoBoxText->setProperty("text", html); return; } if (response->error() != QNetworkReply::NoError) return; QString result = QString::fromUtf8(response->readAll()); int leftPos = result.indexOf("", leftPos) - leftPos; if (leftPos == -1) { //No InfoBox is Found if (soitem->getType() == SkyObjItem::Star && name != soitem->getName().replace(' ', '_')) //For stars, the regular name rather than gname { tryToUpdateWikipediaInfo(soitem, soitem->getName().replace(' ', '_')); return; } QString html = "
Sorry, no Information Box in the object's Wikipedia article was found."; saveObjectInfoBoxText(soitem, "infoText", html); infoBoxText->setProperty("text", html); return; } updateWikipediaDescription(soitem); QString infoText = result.mid(leftPos, rightPos); //This if statement should correct for a situation like for the planets where there is a single internal table inside the infoText Box. if (infoText.indexOf("", leftPos + rightPos + 6) - leftPos; infoText = result.mid(leftPos, rightPos); } //This next section is for the headers in the colored boxes. It turns them black instead of white because they are more visible that way. infoText.replace("background: #", "color:black;background: #") .replace("background-color: #", "color:black;background: #") .replace("background:#", "color:black;background:#") .replace("background-color:#", "color:black;background:#") .replace("background: pink", "color:black;background: pink"); infoText.replace("//", "http://"); //This is to fix links on wikipedia which are missing http from the url infoText.replace("https:http:", "https:") .replace("http:http:", "http:"); //Just in case it was done to an actual complete url //This section is intended to remove links from the object name header at the top. The links break up the header. int thLeft = infoText.indexOf("
", thLeft); int firstA = infoText.indexOf("", firstA) - firstA + 1; infoText.remove(firstA, rightA); int endA = infoText.indexOf("", firstA); infoText.remove(endA, 4); } } int annotationLeft = infoText.indexOf("", annotationLeft) + 13 - annotationLeft; infoText.remove(annotationLeft, annotationRight); //This removes the annotation that does not render correctly for some DSOs. int mathLeft = infoText.indexOf("", mathLeft) + 1 - mathLeft; infoText.remove(mathLeft, mathRight); //This removes an image that doesn't render properly for some DSOs. infoText.replace("style=\"width:22em\"", "style=\"width:100%;background-color: black;color: white;\""); infoText = infoText + "
(Source: Wikipedia)"; saveInfoURL(soitem, "https://en.wikipedia.org/w/index.php?title=" + name + "&redirects"); int captionEnd = infoText.indexOf( ""); //Start looking for the image AFTER the caption. Planets have images in their caption. if (captionEnd == -1) captionEnd = 0; int leftImg = infoText.indexOf("src=\"", captionEnd) + 5; if (leftImg > captionEnd + 5) { int rightImg = infoText.indexOf("\"", leftImg) - leftImg; QString imgURL = infoText.mid(leftImg, rightImg); imgURL.replace( "http://upload.wikimedia.org", "https://upload.wikimedia.org"); //Although they will display, the images apparently don't download properly unless they are https. saveImageURL(soitem, imgURL); downloadWikipediaImage(soitem, imgURL); } QString html = "
" + infoText + "
"; saveObjectInfoBoxText(soitem, "infoText", html); bool isDarkTheme = (Options::currentTheme() == "Night Vision"); QString color = (isDarkTheme) ? "red" : "white"; QString linkColor = (isDarkTheme) ? "red" : "yellow"; if (isDarkTheme) html.replace("color: white", "color: " + color); html = "" + html + ""; if (soitem == m_CurSoItem) infoBoxText->setProperty("text", html); }); } void WIView::saveObjectInfoBoxText(SkyObjItem *soitem, QString type, QString text) { QFile file; QString fname = type + '-' + soitem->getName().toLower().remove(' ') + ".html"; QDir writableDir; QString filePath = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions"; writableDir.mkpath(filePath); file.setFileName(filePath + '/' + fname); //determine filename in local user KDE directory tree. if (file.open(QIODevice::WriteOnly) == false) { qDebug() << "Image text cannot be saved for later. file save error"; return; } else { QTextStream stream(&file); stream << text; file.close(); } } void WIView::saveImageURL(SkyObjItem *soitem, QString imageURL) { QFile file; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat"); //determine filename in local user KDE directory tree. QString entry = soitem->getName() + ':' + "Show Wikipedia Image" + ':' + imageURL; if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString line; while (!in.atEnd()) { line = in.readLine(); if (line == entry) { file.close(); return; } } file.close(); } if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { qDebug() << "Image URL cannot be saved for later. image_url.dat error"; return; } else { QTextStream stream(&file); stream << entry << endl; file.close(); } } void WIView::saveInfoURL(SkyObjItem *soitem, QString infoURL) { QFile file; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat"); //determine filename in local user KDE directory tree. QString entry = soitem->getName() + ':' + "Wikipedia Page" + ':' + infoURL; if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString line; while (!in.atEnd()) { line = in.readLine(); if (line == entry) { file.close(); return; } } file.close(); } if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { qDebug() << "Info URL cannot be saved for later. info_url.dat error"; return; } else { QTextStream stream(&file); stream << entry << endl; file.close(); } } void WIView::downloadWikipediaImage(SkyObjItem *soitem, QString imageURL) { QString fname = "wikiImage-" + soitem->getName().toLower().remove(' ') + ".png"; QDir writableDir; QString filePath = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions"; writableDir.mkpath(filePath); QString fileN = filePath + '/' + fname; QNetworkReply *response = manager->get(QNetworkRequest(QUrl(imageURL))); QTimer::singleShot(60000, response, [response] { //Shut it down after 60 sec. response->abort(); response->deleteLater(); qDebug() << "Image Download Timed out."; }); connect(response, &QNetworkReply::finished, this, [fileN, response, this] { response->deleteLater(); if (response->error() != QNetworkReply::NoError) return; QImage *image = new QImage(); QByteArray responseData = response->readAll(); if (image->loadFromData(responseData)) { image->save(fileN); refreshListView(); //This is to update the images displayed with the new image. } else qDebug() << "image not downloaded"; }); } diff --git a/kstars/tools/whatsinteresting/wiview.h b/kstars/tools/whatsinteresting/wiview.h index 7f724a3d7..35f8e572f 100644 --- a/kstars/tools/whatsinteresting/wiview.h +++ b/kstars/tools/whatsinteresting/wiview.h @@ -1,171 +1,171 @@ /*************************************************************************** wiview.h - K Desktop Planetarium ------------------- begin : 2012/26/05 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@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. * * * ***************************************************************************/ #pragma once #include #include #include class QNetworkAccessManager; class QQmlContext; class QQuickItem; class QQuickView; class ModelManager; class ObsConditions; class SkyObject; class SkyObjItem; /** * @class WIView * @brief Manages the QML user interface for What's Interesting. WIView is used to display the * QML UI using a QQuickView. It acts on all signals emitted by the UI and manages the data * sent to the UI for display. * * @author Samikshan Bairagya */ class WIView : public QWidget { Q_OBJECT public: /** * @brief Constructor - Store QML components as QObject pointers. * Connect signals from various QML components into public slots. * Displays the user interface for What's Interesting */ explicit WIView(QWidget *parent = nullptr); - ~WIView() override; + virtual ~WIView() override = default; /** Load details-view for selected sky-object */ void loadDetailsView(SkyObjItem *soitem, int index); /** Updates sky-object list models */ void updateModel(ObsConditions& obs); inline QQuickView *getWIBaseView() const { return m_BaseView; } public slots: /** * @brief public slot - Act upon signal emitted when category of sky-object is selected * from category selection view of the QML UI. * @param type Category selected */ void onCategorySelected(QString model); /** * @brief public slot - Act upon signal emitted when an item is selected from list of sky-objects. * Display details-view for the skyobject selected. * @param type Category selected. * @param typename Name of category selected. * @param index Index of item in the list of skyobjects. */ void onSoListItemClicked(int index); /** public slot - Show details-view for next sky-object from list of current sky-objects's category. */ void onNextObjClicked(); /** * @brief public slot - Show details-view for previous sky-object from list of current sky-objects's * category. */ void onPrevObjClicked(); /** public slot - Slew map to current sky-object in the details view. */ void onCenterButtonClicked(); /** public slot - Slew map to current sky-object in the details view. */ void onSlewTelescopeButtonClicked(); /** public slot - Open Details Dialog to show more details for current sky-object. */ void onDetailsButtonClicked(); /** public slot - Open WI settings dialog. */ void onSettingsIconClicked(); void onInspectIconClicked(bool checked) { inspectOnClick = checked; } /** public slot - Reload list of visible sky-objects. */ void onReloadIconClicked(); void onVisibleIconClicked(bool checked); void onFavoriteIconClicked(bool checked); void onUpdateIconClicked(); void updateWikipediaDescription(SkyObjItem *soitem); void loadObjectDescription(SkyObjItem *soitem); void tryToUpdateWikipediaInfo(SkyObjItem *soitem, QString name); void loadObjectInfoBox(SkyObjItem *soitem); void saveImageURL(SkyObjItem *soitem, QString imageURL); void saveInfoURL(SkyObjItem *soitem, QString infoURL); void saveObjectInfoBoxText(SkyObjItem *soitem, QString type, QString infoText); void downloadWikipediaImage(SkyObjItem *soitem, QString imageURL); void inspectSkyObject(const QString& name); void inspectSkyObjectOnClick(SkyObject *obj); void inspectSkyObject(SkyObject *obj); bool inspectOnClickIsActive() { return inspectOnClick; } void updateObservingConditions(); void tryToUpdateWikipediaInfoInModel(bool onlyMissing); void refreshListView(); void updateProgress(double value); void setProgressBarVisible(bool visible); void setNightVisionOn(bool on); private: QString getWikipediaName(SkyObjItem *soitem); QQuickItem *m_BaseObj { nullptr }; QQuickItem *m_ViewsRowObj { nullptr }; QQuickItem *m_CategoryTitle { nullptr }; QQuickItem *m_SoListObj { nullptr }; QQuickItem *m_DetailsViewObj { nullptr }; QQuickItem *m_ProgressBar { nullptr }; QQuickItem *m_loadingMessage { nullptr }; QQuickItem *m_NextObj { nullptr }; QQuickItem *m_PrevObj { nullptr }; QQuickItem *m_CenterButtonObj { nullptr }; QQuickItem *m_SlewTelescopeButtonObj { nullptr }; QQuickItem *m_DetailsButtonObj { nullptr }; QQuickItem *inspectIconObj { nullptr }; QQuickItem *visibleIconObj { nullptr }; QQuickItem *favoriteIconObj { nullptr }; QQmlContext *m_Ctxt { nullptr }; QObject *infoBoxText { nullptr }; QObject *descTextObj { nullptr }; QObject *nightVision { nullptr }; QObject *autoTrackCheckbox { nullptr }; QObject *autoCenterCheckbox { nullptr }; QQuickView *m_BaseView { nullptr }; ObsConditions *m_Obs { nullptr }; std::unique_ptr m_ModManager; /// Current sky object item. SkyObjItem *m_CurSoItem { nullptr }; /// Created sky object item on-demand std::unique_ptr trackedItem; /// Index of current sky-object item in Details view. int m_CurIndex { 0 }; /// Currently selected category from WI QML view QString m_CurrentObjectListName; std::unique_ptr manager; bool inspectOnClick { false }; }; diff --git a/kstars/tools/wutdialog.cpp b/kstars/tools/wutdialog.cpp index 4f4d0479e..7f17d62c7 100644 --- a/kstars/tools/wutdialog.cpp +++ b/kstars/tools/wutdialog.cpp @@ -1,631 +1,627 @@ /*************************************************************************** wutdialog.cpp - K Desktop Planetarium ------------------- begin : Die Feb 25 2003 copyright : (C) 2003 by Thomas Kabelmann email : tk78@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "wutdialog.h" #include "kstars.h" #include "skymap.h" #include "dialogs/detaildialog.h" #include "dialogs/locationdialog.h" #include "dialogs/timedialog.h" #include "skyobjects/kssun.h" #include "skyobjects/ksmoon.h" #include "skycomponents/skymapcomposite.h" #include "tools/observinglist.h" WUTDialogUI::WUTDialogUI(QWidget *p) : QFrame(p) { setupUi(this); } WUTDialog::WUTDialog(QWidget *parent, bool _session, GeoLocation *_geo, KStarsDateTime _lt) : QDialog(parent), session(_session), T0(_lt), geo(_geo) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif WUT = new WUTDialogUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(WUT); setLayout(mainLayout); setWindowTitle(i18n("What's up Tonight")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); setModal(false); //If the Time is earlier than 6:00 am, assume the user wants the night of the previous date if (T0.time().hour() < 6) T0 = T0.addDays(-1); //Now, set time T0 to midnight (of the following day) T0.setTime(QTime(0, 0, 0)); T0 = T0.addDays(1); UT0 = geo->LTtoUT(T0); //Set the Tomorrow date/time to Noon the following day Tomorrow = T0.addSecs(12 * 3600); TomorrowUT = geo->LTtoUT(Tomorrow); //Set the Evening date/time to 6:00 pm Evening = T0.addSecs(-6 * 3600); EveningUT = geo->LTtoUT(Evening); QString sGeo = geo->translatedName(); if (!geo->translatedProvince().isEmpty()) sGeo += ", " + geo->translatedProvince(); sGeo += ", " + geo->translatedCountry(); WUT->LocationLabel->setText(i18n("at %1", sGeo)); WUT->DateLabel->setText(i18n("The night of %1", QLocale().toString(Evening.date(), QLocale::LongFormat))); m_Mag = 10.0; WUT->MagnitudeEdit->setValue(m_Mag); //WUT->MagnitudeEdit->setSliderEnabled( true ); WUT->MagnitudeEdit->setSingleStep(0.100); initCategories(); makeConnections(); QTimer::singleShot(0, this, SLOT(init())); } -WUTDialog::~WUTDialog() -{ -} - void WUTDialog::makeConnections() { connect(WUT->DateButton, SIGNAL(clicked()), SLOT(slotChangeDate())); connect(WUT->LocationButton, SIGNAL(clicked()), SLOT(slotChangeLocation())); connect(WUT->CenterButton, SIGNAL(clicked()), SLOT(slotCenter())); connect(WUT->DetailButton, SIGNAL(clicked()), SLOT(slotDetails())); connect(WUT->ObslistButton, SIGNAL(clicked()), SLOT(slotObslist())); connect(WUT->CategoryListWidget, SIGNAL(currentTextChanged(QString)), SLOT(slotLoadList(QString))); connect(WUT->ObjectListWidget, SIGNAL(currentTextChanged(QString)), SLOT(slotDisplayObject(QString))); connect(WUT->EveningMorningBox, SIGNAL(activated(int)), SLOT(slotEveningMorning(int))); connect(WUT->MagnitudeEdit, SIGNAL(valueChanged(double)), SLOT(slotChangeMagnitude())); } void WUTDialog::initCategories() { m_Categories << i18n("Planets") << i18n("Stars") << i18n("Nebulae") << i18n("Galaxies") << i18n("Star Clusters") << i18n("Constellations") << i18n("Asteroids") << i18n("Comets"); foreach (const QString &c, m_Categories) WUT->CategoryListWidget->addItem(c); WUT->CategoryListWidget->setCurrentRow(0); } void WUTDialog::init() { QString sRise, sSet, sDuration; float Dur; int hDur, mDur; KStarsData *data = KStarsData::Instance(); // reset all lists foreach (const QString &c, m_Categories) { if (m_VisibleList.contains(c)) visibleObjects(c).clear(); else m_VisibleList[c] = QSet(); m_CategoryInitialized[c] = false; } // sun almanac information KSSun *oSun = dynamic_cast(data->objectNamed("Sun")); sunRiseTomorrow = oSun->riseSetTime(TomorrowUT, geo, true); sunSetToday = oSun->riseSetTime(EveningUT, geo, false); sunRiseToday = oSun->riseSetTime(EveningUT, geo, true); //check to see if Sun is circumpolar KSNumbers *num = new KSNumbers(UT0.djd()); KSNumbers *oldNum = new KSNumbers(data->ut().djd()); CachingDms LST = geo->GSTtoLST(T0.gst()); oSun->updateCoords(num, true, geo->lat(), &LST, true); if (oSun->checkCircumpolar(geo->lat())) { if (oSun->alt().Degrees() > 0.0) { sRise = i18n("circumpolar"); sSet = i18n("circumpolar"); sDuration = "00:00"; Dur = hDur = mDur = 0; } else { sRise = i18n("does not rise"); sSet = i18n("does not rise"); sDuration = "24:00"; Dur = hDur = 24; mDur = 0; } } else { //Round times to the nearest minute by adding 30 seconds to the time sRise = QLocale().toString(sunRiseTomorrow); sSet = QLocale().toString(sunSetToday); Dur = 24.0 + (float)sunRiseTomorrow.hour() + (float)sunRiseTomorrow.minute() / 60.0 + (float)sunRiseTomorrow.second() / 3600.0 - (float)sunSetToday.hour() - (float)sunSetToday.minute() / 60.0 - (float)sunSetToday.second() / 3600.0; hDur = int(Dur); mDur = int(60.0 * (Dur - (float)hDur)); QTime tDur(hDur, mDur); //sDuration = QLocale().toString(tDur); // Should always be in 24 hour format sDuration = tDur.toString("hh:mm"); } WUT->SunSetLabel->setText(i18nc("Sunset at time %1 on date %2", "Sunset: %1 on %2", sSet, QLocale().toString(Evening.date(), QLocale::LongFormat))); WUT->SunRiseLabel->setText(i18nc("Sunrise at time %1 on date %2", "Sunrise: %1 on %2", sRise, QLocale().toString(Tomorrow.date(), QLocale::LongFormat))); if (Dur == 0) WUT->NightDurationLabel->setText(i18n("Night duration: %1", sDuration)); else if (Dur > 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 hours", sDuration)); else if (Dur == 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 hour", sDuration)); else if (mDur > 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 minutes", sDuration)); else if (mDur == 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 minute", sDuration)); // moon almanac information KSMoon *oMoon = dynamic_cast(data->objectNamed("Moon")); moonRise = oMoon->riseSetTime(UT0, geo, true); moonSet = oMoon->riseSetTime(UT0, geo, false); //check to see if Moon is circumpolar oMoon->updateCoords(num, true, geo->lat(), &LST, true); if (oMoon->checkCircumpolar(geo->lat())) { if (oMoon->alt().Degrees() > 0.0) { sRise = i18n("circumpolar"); sSet = i18n("circumpolar"); } else { sRise = i18n("does not rise"); sSet = i18n("does not rise"); } } else { //Round times to the nearest minute by adding 30 seconds to the time sRise = QLocale().toString(moonRise.addSecs(30)); sSet = QLocale().toString(moonSet.addSecs(30)); } WUT->MoonRiseLabel->setText( i18n("Moon rises at: %1 on %2", sRise, QLocale().toString(Evening.date(), QLocale::LongFormat))); // If the moon rises and sets on the same day, this will be valid [ Unless // the moon sets on the next day after staying on for over 24 hours ] if (moonSet > moonRise) WUT->MoonSetLabel->setText( i18n("Moon sets at: %1 on %2", sSet, QLocale().toString(Evening.date(), QLocale::LongFormat))); else WUT->MoonSetLabel->setText( i18n("Moon sets at: %1 on %2", sSet, QLocale().toString(Tomorrow.date(), QLocale::LongFormat))); oMoon->findPhase(nullptr); WUT->MoonIllumLabel->setText(oMoon->phaseName() + QString(" (%1%)").arg(int(100.0 * oMoon->illum()))); //Restore Sun's and Moon's coordinates, and recompute Moon's original Phase oMoon->updateCoords(oldNum, true, geo->lat(), data->lst(), true); oSun->updateCoords(oldNum, true, geo->lat(), data->lst(), true); oMoon->findPhase(nullptr); if (WUT->CategoryListWidget->currentItem()) slotLoadList(WUT->CategoryListWidget->currentItem()->text()); delete num; delete oldNum; } QSet &WUTDialog::visibleObjects(const QString &category) { return m_VisibleList[category]; } bool WUTDialog::isCategoryInitialized(const QString &category) { return m_CategoryInitialized[category]; } void WUTDialog::slotLoadList(const QString &c) { KStarsData *data = KStarsData::Instance(); if (!m_VisibleList.contains(c)) return; WUT->ObjectListWidget->clear(); setCursor(QCursor(Qt::WaitCursor)); if (!isCategoryInitialized(c)) { if (c == m_Categories[0]) //Planets { foreach (const QString &name, data->skyComposite()->objectNames(SkyObject::PLANET)) { SkyObject *o = data->skyComposite()->findByName(name); if (checkVisibility(o) && o->mag() <= m_Mag) visibleObjects(c).insert(o); } m_CategoryInitialized[c] = true; } else if (c == m_Categories[1]) //Stars { QVector> starObjects; starObjects.append(data->skyComposite()->objectLists(SkyObject::STAR)); starObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR)); for (QVector>::ConstIterator it = starObjects.cbegin();it != starObjects.cend(); ++it) { const SkyObject *o = (*it).second; if (checkVisibility(o) && o->mag() <= m_Mag) { visibleObjects(c).insert(o); } } m_CategoryInitialized[c] = true; } else if (c == m_Categories[5]) //Constellations { foreach (SkyObject *o, data->skyComposite()->constellationNames()) if (checkVisibility(o)) visibleObjects(c).insert(o); m_CategoryInitialized[c] = true; } else if (c == m_Categories[6]) //Asteroids { foreach (SkyObject *o, data->skyComposite()->asteroids()) if (checkVisibility(o) && o->name() != i18nc("Asteroid name (optional)", "Pluto") && o->mag() <= m_Mag) visibleObjects(c).insert(o); m_CategoryInitialized[c] = true; } else if (c == m_Categories[7]) //Comets { foreach (SkyObject *o, data->skyComposite()->comets()) if (checkVisibility(o) && o->mag() <= m_Mag) visibleObjects(c).insert(o); m_CategoryInitialized[c] = true; } else //all deep-sky objects, need to split clusters, nebulae and galaxies { foreach (DeepSkyObject *dso, data->skyComposite()->deepSkyObjects()) { SkyObject *o = (SkyObject *)dso; if (checkVisibility(o) && o->mag() <= m_Mag) { switch (o->type()) { case SkyObject::OPEN_CLUSTER: //fall through case SkyObject::GLOBULAR_CLUSTER: visibleObjects(m_Categories[4]).insert(o); //star clusters break; case SkyObject::GASEOUS_NEBULA: //fall through case SkyObject::PLANETARY_NEBULA: //fall through case SkyObject::SUPERNOVA_REMNANT: visibleObjects(m_Categories[2]).insert(o); //nebulae break; case SkyObject::GALAXY: visibleObjects(m_Categories[3]).insert(o); //galaxies break; } } } m_CategoryInitialized[m_Categories[2]] = true; m_CategoryInitialized[m_Categories[3]] = true; m_CategoryInitialized[m_Categories[4]] = true; } } //Now the category has been initialized, we can populate the list widget foreach (const SkyObject *o, visibleObjects(c)) //WUT->ObjectListWidget->addItem(o->name()); WUT->ObjectListWidget->addItem(o->longname()); setCursor(QCursor(Qt::ArrowCursor)); // highlight first item if (WUT->ObjectListWidget->count()) { WUT->ObjectListWidget->setCurrentRow(0); WUT->ObjectListWidget->setFocus(); } } bool WUTDialog::checkVisibility(const SkyObject *o) { bool visible(false); double minAlt = 6.0; //An object is considered 'visible' if it is above horizon during civil twilight. // reject objects that never rise if (o->checkCircumpolar(geo->lat()) == true && o->alt().Degrees() <= 0) return false; //Initial values for T1, T2 assume all night option of EveningMorningBox KStarsDateTime T1 = Evening; T1.setTime(sunSetToday); KStarsDateTime T2 = Tomorrow; T2.setTime(sunRiseTomorrow); //Check Evening/Morning only state: if (EveningFlag == 0) //Evening only { T2 = T0; //midnight } else if (EveningFlag == 1) //Morning only { T1 = T0; //midnight } for (KStarsDateTime test = T1; test < T2; test = test.addSecs(3600)) { //Need LST of the test time, expressed as a dms object. KStarsDateTime ut = geo->LTtoUT(test); dms LST = geo->GSTtoLST(ut.gst()); SkyPoint sp = o->recomputeCoords(ut, geo); //check altitude of object at this time. sp.EquatorialToHorizontal(&LST, geo->lat()); if (sp.alt().Degrees() > minAlt) { visible = true; break; } } return visible; } void WUTDialog::slotDisplayObject(const QString &name) { QTime tRise, tSet, tTransit; QString sRise, sTransit, sSet; sRise = "--:--"; sTransit = "--:--"; sSet = "--:--"; WUT->DetailButton->setEnabled(false); SkyObject *o = nullptr; if (name.isEmpty()) { //no object selected WUT->ObjectBox->setTitle(i18n("No Object Selected")); o = nullptr; } else { o = KStarsData::Instance()->objectNamed(name); if (!o) //should never get here { WUT->ObjectBox->setTitle(i18n("Object Not Found")); } } if (o) { WUT->ObjectBox->setTitle(o->name()); if (o->checkCircumpolar(geo->lat())) { if (o->alt().Degrees() > 0.0) { sRise = i18n("circumpolar"); sSet = i18n("circumpolar"); } else { sRise = i18n("does not rise"); sSet = i18n("does not rise"); } } else { tRise = o->riseSetTime(T0, geo, true); tSet = o->riseSetTime(T0, geo, false); // if ( tSet < tRise ) // tSet = o->riseSetTime( JDTomorrow, geo, false ); sRise.clear(); sRise.sprintf("%02d:%02d", tRise.hour(), tRise.minute()); sSet.clear(); sSet.sprintf("%02d:%02d", tSet.hour(), tSet.minute()); } tTransit = o->transitTime(T0, geo); // if ( tTransit < tRise ) // tTransit = o->transitTime( JDTomorrow, geo ); sTransit.clear(); sTransit.sprintf("%02d:%02d", tTransit.hour(), tTransit.minute()); WUT->DetailButton->setEnabled(true); } WUT->ObjectRiseLabel->setText(i18n("Rises at: %1", sRise)); WUT->ObjectTransitLabel->setText(i18n("Transits at: %1", sTransit)); WUT->ObjectSetLabel->setText(i18n("Sets at: %1", sSet)); } void WUTDialog::slotCenter() { KStars *kstars = KStars::Instance(); SkyObject *o = nullptr; // get selected item if (WUT->ObjectListWidget->currentItem() != nullptr) { o = kstars->data()->objectNamed(WUT->ObjectListWidget->currentItem()->text()); } if (o != nullptr) { kstars->map()->setFocusPoint(o); kstars->map()->setFocusObject(o); kstars->map()->setDestination(*kstars->map()->focusPoint()); } } void WUTDialog::slotDetails() { KStars *kstars = KStars::Instance(); SkyObject *o = nullptr; // get selected item if (WUT->ObjectListWidget->currentItem() != nullptr) { o = kstars->data()->objectNamed(WUT->ObjectListWidget->currentItem()->text()); } if (o != nullptr) { QPointer detail = new DetailDialog(o, kstars->data()->ut(), geo, kstars); detail->exec(); delete detail; } } void WUTDialog::slotObslist() { SkyObject *o = nullptr; // get selected item if (WUT->ObjectListWidget->currentItem() != nullptr) { o = KStarsData::Instance()->objectNamed(WUT->ObjectListWidget->currentItem()->text()); } if (o != nullptr) { KStarsData::Instance()->observingList()->slotAddObject(o, session); } } void WUTDialog::slotChangeDate() { // Set the time T0 to the evening of today. This will make life easier for the user, who most probably // wants to see what's up on the night of some date, rather than the night of the previous day T0.setTime(QTime(18, 0, 0)); // 6 PM QPointer td = new TimeDialog(T0, KStarsData::Instance()->geo(), this); if (td->exec() == QDialog::Accepted) { T0 = td->selectedDateTime(); // If the time is set to 12 midnight, set it to 00:00:01, so that we don't have date interpretation problems if (T0.time() == QTime(0, 0, 0)) T0.setTime(QTime(0, 0, 1)); //If the Time is earlier than 6:00 am, assume the user wants the night of the previous date if (T0.time().hour() < 6) T0 = T0.addDays(-1); //Now, set time T0 to midnight (of the following day) T0.setTime(QTime(0, 0, 0)); T0 = T0.addDays(1); UT0 = geo->LTtoUT(T0); //Set the Tomorrow date/time to Noon the following day Tomorrow = T0.addSecs(12 * 3600); TomorrowUT = geo->LTtoUT(Tomorrow); //Set the Evening date/time to 6:00 pm Evening = T0.addSecs(-6 * 3600); EveningUT = geo->LTtoUT(Evening); WUT->DateLabel->setText(i18n("The night of %1", QLocale().toString(Evening.date(), QLocale::LongFormat))); init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } delete td; } void WUTDialog::slotChangeLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { GeoLocation *newGeo = ld->selectedCity(); if (newGeo) { geo = newGeo; UT0 = geo->LTtoUT(T0); TomorrowUT = geo->LTtoUT(Tomorrow); EveningUT = geo->LTtoUT(Evening); WUT->LocationLabel->setText(i18n("at %1", geo->fullName())); init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } } delete ld; } void WUTDialog::slotEveningMorning(int index) { if (EveningFlag != index) { EveningFlag = index; init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } } void WUTDialog::updateMag() { m_Mag = WUT->MagnitudeEdit->value(); init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } void WUTDialog::slotChangeMagnitude() { if (timer) { timer->stop(); } else { timer = new QTimer(this); timer->setSingleShot(true); connect(timer, SIGNAL(timeout()), this, SLOT(updateMag())); } timer->start(500); } diff --git a/kstars/tools/wutdialog.h b/kstars/tools/wutdialog.h index 79c27ecf1..9fc85edde 100644 --- a/kstars/tools/wutdialog.h +++ b/kstars/tools/wutdialog.h @@ -1,133 +1,134 @@ /*************************************************************************** wutdialog.h - K Desktop Planetarium ------------------- begin : Die Feb 25 2003 copyright : (C) 2003 by Thomas Kabelmann email : tk78@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include "kstarsdata.h" #include "kstarsdatetime.h" #include "ui_wutdialog.h" #include #include class GeoLocation; class SkyObject; class WUTDialogUI : public QFrame, public Ui::WUTDialog { Q_OBJECT + public: explicit WUTDialogUI(QWidget *p = nullptr); }; /** * @class WUTDialog * * What's up tonight dialog is a window which lists all sky objects * that will be visible during the next night. * * @author Thomas Kabelmann * @version 1.0 */ class WUTDialog : public QDialog { Q_OBJECT public: /** Constructor */ explicit WUTDialog(QWidget *ks, bool session = false, GeoLocation *geo = KStarsData::Instance()->geo(), KStarsDateTime lt = KStarsData::Instance()->lt()); - ~WUTDialog() override; + virtual ~WUTDialog() override = default; /** * @short Check visibility of object * @p o the object to check * @return true if visible */ bool checkVisibility(const SkyObject *o); public slots: /** * @short Determine which objects are visible, and store them in * an array of lists, classified by object type */ void init(); private slots: /** * @short Load the list of visible objects for selected object type. * @p category the string describing the type of object */ void slotLoadList(const QString &category); /** Display the rise/transit/set times for selected object */ void slotDisplayObject(const QString &name); /** * @short Apply user's choice of what part of the night should be examined: * @li 0: Evening only (sunset to midnight) * @li 1: Morning only (midnight to sunrise) * @li 2: All night (sunset to sunrise) */ void slotEveningMorning(int flag); /** * @short Adjust the date for the WUT tool * @note this does NOT affect the date of the sky map */ void slotChangeDate(); /** * @short Adjust the geographic location for the WUT tool * @note this does NOT affect the geographic location for the sky map */ void slotChangeLocation(); /** Open the detail dialog for the current object */ void slotDetails(); /** Center the display on the current object */ void slotCenter(); /** Add the object to the observing list */ void slotObslist(); /** Filters the objects displayed by Magnitude */ void slotChangeMagnitude(); void updateMag(); private: QSet &visibleObjects(const QString &category); bool isCategoryInitialized(const QString &category); /** @short Initialize all SIGNAL/SLOT connections, used in constructor */ void makeConnections(); /** @short Initialize catgory list, used in constructor */ void initCategories(); WUTDialogUI *WUT { nullptr }; bool session { false }; QTime sunRiseTomorrow, sunSetToday, sunRiseToday, moonRise, moonSet; KStarsDateTime T0, UT0, Tomorrow, TomorrowUT, Evening, EveningUT; GeoLocation *geo { nullptr }; int EveningFlag { 0 }; double m_Mag { 0 }; QTimer *timer { nullptr }; QStringList m_Categories; QHash> m_VisibleList; QHash m_CategoryInitialized; }; diff --git a/kstars/widgets/clicklabel.h b/kstars/widgets/clicklabel.h index 5ebf4a2d4..405bd3095 100644 --- a/kstars/widgets/clicklabel.h +++ b/kstars/widgets/clicklabel.h @@ -1,46 +1,46 @@ /*************************************************************************** clicklabel.h - description ------------------- begin : Sat 03 Dec 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 CLICKLABEL_H #define CLICKLABEL_H #include #include /** @class ClickLabel is a QLabel with a clicked() signal. *@author Jason Harris *@version 1.0 */ class ClickLabel : public QLabel { Q_OBJECT public: explicit ClickLabel(QWidget *parent = nullptr, const char *name = nullptr); - ~ClickLabel() override {} + ~ClickLabel() override = default; signals: void clicked(); protected: void mousePressEvent(QMouseEvent *e) override { if (e->button() == Qt::LeftButton) emit clicked(); } }; #endif diff --git a/kstars/widgets/dmsbox.cpp b/kstars/widgets/dmsbox.cpp index 0c2799654..c0f6b4383 100644 --- a/kstars/widgets/dmsbox.cpp +++ b/kstars/widgets/dmsbox.cpp @@ -1,202 +1,198 @@ /*************************************************************************** dmsbox.cpp - description ------------------- begin : wed Dec 19 2001 copyright : (C) 2001-2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 "dmsbox.h" #include #include #include #include #include #include dmsBox::dmsBox(QWidget *parent, bool dg) : QLineEdit(parent), EmptyFlag(true) { setMaxLength(14); setMaximumWidth(160); setDegType(dg); connect(this, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); } void dmsBox::setEmptyText() { //Set the text color to the average between //QColorGroup::Text and QColorGroup::Base QPalette p = QApplication::palette(); QColor txc = p.color(QPalette::Active, QPalette::Text); QColor bgc = p.color(QPalette::Active, QPalette::Base); int r((txc.red() + bgc.red()) / 2); int g((txc.green() + bgc.green()) / 2); int b((txc.blue() + bgc.blue()) / 2); p.setColor(QPalette::Active, QPalette::Text, QColor(r, g, b)); p.setColor(QPalette::Inactive, QPalette::Text, QColor(r, g, b)); setPalette(p); if (degType()) setText("dd mm ss.s"); else setText("hh mm ss.s"); EmptyFlag = true; } void dmsBox::focusInEvent(QFocusEvent *e) { QLineEdit::focusInEvent(e); if (EmptyFlag) { clear(); setPalette(QApplication::palette()); EmptyFlag = false; } } void dmsBox::focusOutEvent(QFocusEvent *e) { QLineEdit::focusOutEvent(e); if (text().isEmpty()) { setEmptyText(); } } void dmsBox::slotTextChanged(const QString &t) { if (!hasFocus()) { if (EmptyFlag && !t.isEmpty()) { EmptyFlag = false; } if (!EmptyFlag && t.isEmpty()) { setEmptyText(); } } } void dmsBox::setDegType(bool t) { deg = t; QString sTip = (t ? i18n("Angle value in degrees.") : i18n("Angle value in hours.")); QString sWhatsThis; if (isReadOnly()) { if (t) { sWhatsThis = i18n("This box displays an angle in degrees. " "The three numbers displayed are the angle's " "degrees, arcminutes, and arcseconds."); } else { sWhatsThis = i18n("This box displays an angle in hours. " "The three numbers displayed are the angle's " "hours, minutes, and seconds."); } } else { if (t) { sTip += i18n(" You may enter a simple integer, or a floating-point value, " "or space- or colon-delimited values specifying " "degrees, arcminutes and arcseconds"); sWhatsThis = i18n("Enter an angle value in degrees. The angle can be expressed " "as a simple integer (\"12\"), a floating-point value " "(\"12.33\"), or as space- or colon-delimited " "values specifying degrees, arcminutes and arcseconds (\"12:20\", \"12:20:00\", " "\"12 20\", \"12 20 00.0\", etc.)."); } else { sTip += i18n(" You may enter a simple integer, or a floating-point value, " "or space- or colon-delimited values specifying " "hours, minutes and seconds"); sWhatsThis = i18n("Enter an angle value in hours. The angle can be expressed " "as a simple integer (\"12\"), a floating-point value " "(\"12.33\"), or as space- or colon-delimited " "values specifying hours, minutes and seconds (\"12:20\", \"12:20:00\", " "\"12 20\", \"12 20 00.0\", etc.)."); } } setToolTip(sTip); setWhatsThis(sWhatsThis); clear(); EmptyFlag = false; setEmptyText(); } void dmsBox::showInDegrees(const dms *d) { showInDegrees(dms(*d)); } void dmsBox::showInDegrees(dms d) { double seconds = d.arcsec() + d.marcsec() / 1000.; setDMS(QString().sprintf("%02d %02d %05.2f", d.degree(), d.arcmin(), seconds)); } void dmsBox::showInHours(const dms *d) { showInHours(dms(*d)); } void dmsBox::showInHours(dms d) { double seconds = d.second() + d.msecond() / 1000.; setDMS(QString().sprintf("%02d %02d %05.2f", d.hour(), d.minute(), seconds)); } void dmsBox::show(const dms *d, bool deg) { show(dms(*d), deg); } void dmsBox::show(dms d, bool deg) { if (deg) showInDegrees(d); else showInHours(d); } dms dmsBox::createDms(bool deg, bool *ok) { dms dmsAngle(0.0); // FIXME: Should we change this to NaN? bool check; check = dmsAngle.setFromString(text(), deg); if (ok) *ok = check; //ok might be a null pointer! return dmsAngle; } - -dmsBox::~dmsBox() -{ -} diff --git a/kstars/widgets/dmsbox.h b/kstars/widgets/dmsbox.h index 2bb8ef6e9..e85e692f1 100644 --- a/kstars/widgets/dmsbox.h +++ b/kstars/widgets/dmsbox.h @@ -1,148 +1,165 @@ /*************************************************************************** dmsbox.h - description ------------------- begin : Wed Dec 19 2002 copyright : (C) 2001-2002 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * 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 DMSBOX_H -#define DMSBOX_H - -#include -#include -//#include +#pragma once #include "dms.h" -/** @class dmsBox - *A QLineEdit which is capable of displaying and parsing angle values - *flexibly and robustly. Angle values can be displayed and parsed as - *Degrees or Hours. When displaying a value, it uses a space-delimited - *triplet of integers representing the degrees, arcminutes, and arcseconds - *of the angle (or hours, minutes, seconds). For example, "-34 45 57". - *When parsing a value input by the user, it can also understand - *a number of other formats: - *@li colon-delimited fields ("-34:45:57") - *@li one or two fields ("-35"; "-34 46") - *@li fields with unit-labels ("-34d 45m 57s") - *@li floating-point numbers ("-34.76583") - * - *@note Inherits QLineEdit. - *@author Pablo de Vicente - *@version 1.0 - */ +#include +#include +/** + * @class dmsBox + * + * A QLineEdit which is capable of displaying and parsing angle values + * flexibly and robustly. Angle values can be displayed and parsed as + * Degrees or Hours. When displaying a value, it uses a space-delimited + * triplet of integers representing the degrees, arcminutes, and arcseconds + * of the angle (or hours, minutes, seconds). For example, "-34 45 57". + * When parsing a value input by the user, it can also understand + * a number of other formats: + * @li colon-delimited fields ("-34:45:57") + * @li one or two fields ("-35"; "-34 46") + * @li fields with unit-labels ("-34d 45m 57s") + * @li floating-point numbers ("-34.76583") + * + * @note Inherits QLineEdit. + * @author Pablo de Vicente + * @version 1.0 + */ class dmsBox : public QLineEdit { Q_OBJECT Q_PROPERTY(bool degType READ degType WRITE setDegType) public: - /**Constructor for the dmsBox object. - *@param parent pointer to the parent QWidget - *@param deg if true use deg/arcmin/arcsec; otherwise - * use hours/min/sec. - */ + /** + * Constructor for the dmsBox object. + * + * @param parent Pointer to the parent QWidget + * @param deg If true use deg/arcmin/arcsec otherwise use hours/min/sec. + */ explicit dmsBox(QWidget *parent, bool deg = true); - /**Destructor (empty)*/ - ~dmsBox() override; + virtual ~dmsBox() override = default; - /**Display an angle using Hours/Min/Sec. - *@p t the dms object which is to be displayed - */ + /** + * Display an angle using Hours/Min/Sec. + * + * @p t the dms object which is to be displayed + */ void showInHours(dms t); - /**Display an angle using Hours/Min/Sec. This behaves just - *like the above function. It differs only in the data type of - *the argument. - *@p t pointer to the dms object which is to be displayed - */ + + /** + * Display an angle using Hours/Min/Sec. This behaves just + * like the above function. It differs only in the data type of + * the argument. + * + * @p t pointer to the dms object which is to be displayed + */ void showInHours(const dms *t); - /**Display an angle using Deg/Arcmin/Arcsec. - *@p t the dms object which is to be displayed - */ + /** + * Display an angle using Deg/Arcmin/Arcsec. + * + * @p t the dms object which is to be displayed + */ void showInDegrees(dms t); - /**Display an angle using Deg/Arcmin/Arcsec. This behaves just - *like the above function. It differs only in the data type of - *the argument. - *@p t pointer to the dms object which is to be displayed - */ + + /** + * Display an angle using Deg/Arcmin/Arcsec. This behaves just + * like the above function. It differs only in the data type of + * the argument. + * + * @p t pointer to the dms object which is to be displayed + */ void showInDegrees(const dms *t); - /**Display an angle. Simply calls showInDegrees(t) or - *showInHours(t) depending on the value of deg. - *@param t the dms object which is to be displayed. - *@param deg if true, display Deg/Arcmin/Arcsec; otherwise - *display Hours/Min/Sec. - */ + /** + * Display an angle. Simply calls showInDegrees(t) or + * showInHours(t) depending on the value of deg. + * + * @param t the dms object which is to be displayed. + * @param deg if true, display Deg/Arcmin/Arcsec; otherwise + * display Hours/Min/Sec. + */ void show(dms t, bool deg = true); - /**Display an angle. Simply calls showInDegrees(t) or - *showInHours(t) depending on the value of deg. - *This behaves essentially like the above function. It - *differs only in the data type of its argument. - *@param t the dms object which is to be displayed. - *@param deg if true, display Deg/Arcmin/Arcsec; otherwise - *display Hours/Min/Sec. - */ + + /** + * Display an angle. Simply calls showInDegrees(t) or + * showInHours(t) depending on the value of deg. + * This behaves essentially like the above function. It + * differs only in the data type of its argument. + * + * @param t the dms object which is to be displayed. + * @param deg if true, display Deg/Arcmin/Arcsec; otherwise + * display Hours/Min/Sec. + */ void show(const dms *t, bool deg = true); - /**Simply display a string. - *@note JH: Why don't we just use QLineEdit::setText() instead? - *@param s the string to display (it need not be a valid angle value). - */ + /** + * Simply display a string. + * + * @note JH: Why don't we just use QLineEdit::setText() instead? + * @param s the string to display (it need not be a valid angle value). + */ void setDMS(const QString &s) { setText(s); } - /**Parse the text in the dmsBox as an angle. The text may be an integer - *or double value, or it may be a triplet of integer values (separated by spaces - *or colons) representing deg/hrs, min, sec. It is also possible to have two - *fields. In this case, if the second field is a double, it is converted - *to decimal min and double sec. - *@param deg if true use deg/arcmin/arcsec; otherwise - * use hours/min/sec. - *@param ok set to true if a dms object was succedssfully created. - *@return a dms object constructed from the fields of the dmsbox - */ + /** + * Parse the text in the dmsBox as an angle. The text may be an integer + * or double value, or it may be a triplet of integer values (separated by spaces + * or colons) representing deg/hrs, min, sec. It is also possible to have two + * fields. In this case, if the second field is a double, it is converted + * to decimal min and double sec. + * + * @param deg if true use deg/arcmin/arcsec; otherwise + * use hours/min/sec. + * @param ok set to true if a dms object was succedssfully created. + * @return a dms object constructed from the fields of the dmsbox + */ dms createDms(bool deg = true, bool *ok = nullptr); - /** @return a boolean indicating if object contains degrees or hours - */ + /** @return a boolean indicating if object contains degrees or hours */ bool degType(void) const { return deg; } - /** @short set the dmsBox to Degrees or Hours - *@param t if true, the box expects angle values in degrees; otherwise - *it expects values in hours - */ + /** + * @short set the dmsBox to Degrees or Hours + * + * @param t if true, the box expects angle values in degrees; otherwise + * it expects values in hours + */ void setDegType(bool t); - /**Clears the QLineEdit - */ + /** Clears the QLineEdit */ void clearFields(void) { setDMS(QString()); } inline bool isEmpty() { return EmptyFlag; } protected: void focusInEvent(QFocusEvent *e) override; void focusOutEvent(QFocusEvent *e) override; private slots: void slotTextChanged(const QString &t); private: void setEmptyText(); - bool deg, EmptyFlag; + bool deg { false }; + bool EmptyFlag { false }; dms degValue; }; - -#endif diff --git a/kstars/widgets/fovwidget.cpp b/kstars/widgets/fovwidget.cpp index 9bbce407c..738d1d421 100644 --- a/kstars/widgets/fovwidget.cpp +++ b/kstars/widgets/fovwidget.cpp @@ -1,58 +1,55 @@ /*************************************************************************** fovwidget.cpp - description ------------------- begin : Sat 22 Sept 2007 copyright : (C) 2007 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "fovwidget.h" -#include "dialogs/fovdialog.h" + #include "fov.h" +#include "dialogs/fovdialog.h" -#include #include +#include FOVWidget::FOVWidget(QWidget *parent) : QFrame(parent), m_FOV(nullptr) { } -FOVWidget::~FOVWidget() -{ -} - void FOVWidget::setFOV(FOV *f) { m_FOV = f; } void FOVWidget::paintEvent(QPaintEvent *) { QPainter p; p.begin(this); p.setRenderHint(QPainter::Antialiasing, true); p.fillRect(contentsRect(), QColor("black")); if (m_FOV && m_FOV->sizeX() > 0 && m_FOV->sizeY() > 0) { m_FOV->draw(p, 0.6 * contentsRect().width(), 0.6 * contentsRect().height()); QFont smallFont = p.font(); smallFont.setPointSize(p.font().pointSize() - 2); p.setFont(smallFont); // TODO: Check if decimal points in this are localized (eg: It should read 1,5 x 1,5 in German rather than 1.5 x 1.5) p.drawText(rect(), Qt::AlignHCenter | Qt::AlignBottom, i18nc("angular size in arcminutes", "%1 x %2 arcmin", QString::number(m_FOV->sizeX(), 'f', 1), QString::number(m_FOV->sizeY(), 'f', 1))); } p.end(); } diff --git a/kstars/widgets/fovwidget.h b/kstars/widgets/fovwidget.h index d895e794d..b755672ae 100644 --- a/kstars/widgets/fovwidget.h +++ b/kstars/widgets/fovwidget.h @@ -1,41 +1,38 @@ /*************************************************************************** fovwidget.h - description ------------------- begin : Sat 22 Sept 2007 copyright : (C) 2007 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 FOVWIDGET_H_ -#define FOVWIDGET_H_ +#pragma once #include class FOV; class FOVWidget : public QFrame { Q_OBJECT public: explicit FOVWidget(QWidget *parent = nullptr); - ~FOVWidget() override; + virtual ~FOVWidget() override = default; void setFOV(FOV *f); protected: void paintEvent(QPaintEvent *e) override; private: - FOV *m_FOV; + FOV *m_FOV { nullptr }; }; - -#endif diff --git a/kstars/widgets/infoboxwidget.cpp b/kstars/widgets/infoboxwidget.cpp index 628fe04bf..ee5a0dbee 100644 --- a/kstars/widgets/infoboxwidget.cpp +++ b/kstars/widgets/infoboxwidget.cpp @@ -1,267 +1,259 @@ /*************************************************************************** fovwidget.cpp - description ------------------- begin : 20 Aug 2009 copyright : (C) 2009 by Khudyakov Alexey email : alexey.skladnoy@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 -#include -#include -#include - -#include - #include "infoboxwidget.h" -#include "kstarsdata.h" #include "colorscheme.h" +#include "kstarsdata.h" #include "ksutils.h" +#include + +#include +#include +#include +#include + const int InfoBoxWidget::padX = 6; const int InfoBoxWidget::padY = 2; -InfoBoxes::~InfoBoxes() -{ -} - InfoBoxes::InfoBoxes(QWidget *parent) : QWidget(parent) { setMouseTracking(true); } void InfoBoxes::addInfoBox(InfoBoxWidget *ibox) { ibox->setParent(this); m_boxes.append(ibox); } void InfoBoxes::resizeEvent(QResizeEvent *) { foreach (InfoBoxWidget *w, m_boxes) w->adjust(); } /* ================================================================ */ InfoBoxWidget::InfoBoxWidget(bool shade, const QPoint &pos, int anchor, const QStringList &str, QWidget *parent) : QWidget(parent), m_strings(str), m_adjusted(false), m_grabbed(false), m_shaded(shade), m_anchor(anchor) { move(pos); updateSize(); } -InfoBoxWidget::~InfoBoxWidget() -{ -} - void InfoBoxWidget::updateSize() { QFontMetrics fm(font()); int w = 0; foreach (const QString &str, m_strings) w = qMax(w, fm.width(str)); int h = fm.height() * (m_shaded ? 1 : m_strings.size()); // Add padding resize(w + 2 * padX, h + 2 * padY + 2); adjust(); } void InfoBoxWidget::slotTimeChanged() { KStarsData *data = KStarsData::Instance(); m_strings.clear(); m_strings << i18nc("Local Time", "LT: ") + data->lt().time().toString(QLocale().timeFormat().remove('t')) + " " + // Remove timezone, as timezone of geolocation in KStars might not be same as system locale timezone QLocale().toString(data->lt().date()); m_strings << i18nc("Universal Time", "UT: ") + data->ut().time().toString("HH:mm:ss") + " " + QLocale().toString(data->ut().date()); // Do not format UTC according to locale QString STString; STString = STString.sprintf("%02d:%02d:%02d ", data->lst()->hour(), data->lst()->minute(), data->lst()->second()); //Don't use KLocale::formatNumber() for Julian Day because we don't want //thousands-place separators QString JDString = QString::number(data->ut().djd(), 'f', 2); JDString.replace('.', QLocale().decimalPoint()); m_strings << i18nc("Sidereal Time", "ST: ") + STString + i18nc("Julian Day", "JD: ") + JDString; updateSize(); update(); } void InfoBoxWidget::slotGeoChanged() { GeoLocation *geo = KStarsData::Instance()->geo(); m_strings.clear(); m_strings << geo->fullName(); //m_strings << i18nc("Longitude", "Long:") + ' ' + QLocale().toString(geo->lng()->Degrees(), 3) + " " + // i18nc("Latitude", "Lat:") + ' ' + QLocale().toString(geo->lat()->Degrees(), 3); m_strings << i18nc("Longitude", "Long:") + ' ' + geo->lng()->toDMSString(true) + ' ' + i18nc("Latitude", "Lat:") + ' ' + geo->lat()->toDMSString(true); updateSize(); update(); } void InfoBoxWidget::slotObjectChanged(SkyObject *obj) { setPoint(obj->translatedLongName(), obj); } void InfoBoxWidget::slotPointChanged(SkyPoint *p) { setPoint(i18n("nothing"), p); } void InfoBoxWidget::setPoint(QString name, SkyPoint *p) { m_strings.clear(); m_strings << name; m_strings << i18nc("Right Ascension", "RA") + ": " + p->ra().toHMSString() + " " + i18nc("Declination", "Dec") + ": " + p->dec().toDMSString(true); m_strings << i18nc("Azimuth", "Az") + ": " + p->az().toDMSString(true) + " " + i18nc("Altitude", "Alt") + ": " + p->alt().toDMSString(true); updateSize(); update(); } void InfoBoxWidget::adjust() { if (!isVisible()) return; // X axis int newX = x(); int maxX = parentWidget()->width() - width(); if (m_anchor & AnchorRight) { newX = maxX; } else { newX = KSUtils::clamp(newX, 0, maxX); if (newX == maxX) m_anchor |= AnchorRight; } // Y axis int newY = y(); int maxY = parentWidget()->height() - height(); if (m_anchor & AnchorBottom) { newY = maxY; } else { newY = KSUtils::clamp(newY, 0, maxY); if (newY == maxY) m_anchor |= AnchorBottom; } // Do move m_adjusted = true; move(newX, newY); } void InfoBoxWidget::paintEvent(QPaintEvent *) { // If widget contain no strings return if (m_strings.empty()) return; // Start with painting ColorScheme *cs = KStarsData::Instance()->colorScheme(); QPainter p; p.begin(this); // Draw background QColor colBG = cs->colorNamed("BoxBGColor"); colBG.setAlpha(127); p.fillRect(contentsRect(), colBG); // Draw border if (m_grabbed) { p.setPen(cs->colorNamed("BoxGrabColor")); p.drawRect(0, 0, width() - 1, height() - 1); } // Draw text int h = QFontMetrics(font()).height(); int y = 0; p.setPen(cs->colorNamed("BoxTextColor")); foreach (const QString &str, m_strings) { y += h; p.drawText(padX, padY + y, str); } // Done p.end(); } void InfoBoxWidget::mouseMoveEvent(QMouseEvent *event) { m_grabbed = true; // X axis int newX = x() + event->x(); int maxX = parentWidget()->width() - width(); if (newX > maxX) { newX = maxX; m_anchor |= AnchorRight; } else { if (newX < 0) newX = 0; m_anchor &= ~AnchorRight; } // Y axis int newY = y() + event->y(); int maxY = parentWidget()->height() - height(); if (newY > maxY) { newY = maxY; m_anchor |= AnchorBottom; } else { if (newY < 0) newY = 0; m_anchor &= ~AnchorBottom; } // Do move m_adjusted = true; move(newX, newY); } void InfoBoxWidget::mousePressEvent(QMouseEvent *) { emit clicked(); } void InfoBoxWidget::showEvent(QShowEvent *) { if (!m_adjusted) adjust(); } void InfoBoxWidget::mouseDoubleClickEvent(QMouseEvent *) { m_shaded = !m_shaded; updateSize(); update(); } void InfoBoxWidget::mouseReleaseEvent(QMouseEvent *) { m_grabbed = false; } diff --git a/kstars/widgets/infoboxwidget.h b/kstars/widgets/infoboxwidget.h index 3ad8c420c..4b41acc33 100644 --- a/kstars/widgets/infoboxwidget.h +++ b/kstars/widgets/infoboxwidget.h @@ -1,118 +1,122 @@ /*************************************************************************** infoboxwidet.h - description ------------------- begin : 20 Aug 2009 copyright : (C) 2009 by Khudyakov Alexey email : alexey.skladnoy@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 INFOBOXWIDGET_H_ -#define INFOBOXWIDGET_H_ +#pragma once #include #include #include #include #include class SkyPoint; class SkyObject; class InfoBoxWidget; /** * @brief The InfoBoxes class is a collection of InfoBoxWidget objects that display a transparent box for display of text messages */ class InfoBoxes : public QWidget { Q_OBJECT + public: explicit InfoBoxes(QWidget *parent = nullptr); - ~InfoBoxes() override; + + virtual ~InfoBoxes() override = default; void addInfoBox(InfoBoxWidget *ibox); QList getInfoBoxes() const { return m_boxes; } protected: void resizeEvent(QResizeEvent *event) override; private: QList m_boxes; }; /** * @brief The InfoBoxWidget class is a widget that displays a transparent box for display of text messages. */ class InfoBoxWidget : public QWidget { Q_OBJECT public: /** Alignment of widget. */ enum { NoAnchor = 0, AnchorRight = 1, AnchorBottom = 2, AnchorBoth = 3 }; /** Create one infobox. */ InfoBoxWidget(bool shade, const QPoint &pos, int anchor = 0, const QStringList &str = QStringList(), QWidget *parent = nullptr); /** Destructor */ - ~InfoBoxWidget() override; + virtual ~InfoBoxWidget() override = default; /** Check whether box is shaded. In this case only one line is shown. */ bool shaded() const { return m_shaded; } /** Get stickyness status of */ int sticky() const { return m_anchor; } /** Adjust widget's postion */ void adjust(); public slots: /** Set information about time. Data is taken from KStarsData. */ void slotTimeChanged(); /** Set information about location. Data is taken from KStarsData. */ void slotGeoChanged(); /** Set information about object. */ void slotObjectChanged(SkyObject *obj); /** Set information about pointing. */ void slotPointChanged(SkyPoint *p); signals: /** Emitted when widget is clicked */ void clicked(); protected: void paintEvent(QPaintEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; void showEvent(QShowEvent *event) override; private: /** Uset to set information about object. */ void setPoint(QString name, SkyPoint *p); /** Recalculate size of widet */ void updateSize(); - QStringList m_strings; // list of string to show - bool m_adjusted; // True if widget coordinates were adjusted - bool m_grabbed; // True if widget is dragged around - bool m_shaded; // True if widget if shaded - int m_anchor; // Vertical alignment of widget + /// List of string to show + QStringList m_strings; + /// True if widget coordinates were adjusted + bool m_adjusted { true }; + /// True if widget is dragged around + bool m_grabbed { true }; + /// True if widget if shaded + bool m_shaded { true }; + /// Vertical alignment of widget + int m_anchor { 0 }; static const int padX; static const int padY; }; - -#endif /* INFOBOXWIDGET_H_ */ diff --git a/kstars/widgets/logedit.h b/kstars/widgets/logedit.h index 972c610bd..f26efad91 100644 --- a/kstars/widgets/logedit.h +++ b/kstars/widgets/logedit.h @@ -1,43 +1,43 @@ /*************************************************************************** logedit.h - description ------------------- begin : Sat 03 Dec 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 LOGEDIT_H -#define LOGEDIT_H +#pragma once -#include #include +#include -/** @class LogEdit is a simple derivative of QTextEdit, that just adds a - *focusOut() signal, emitted when the edit loses focus. - *@author Jason Harris - *@version 1.0 - */ +/** + * @class LogEdit is a simple derivative of QTextEdit, that just adds a + * focusOut() signal, emitted when the edit loses focus. + * + * @author Jason Harris + * @version 1.0 + */ class LogEdit : public QTextEdit { Q_OBJECT + public: explicit LogEdit(QWidget *parent = nullptr); - ~LogEdit() override {} + virtual ~LogEdit() override = default; signals: void focusOut(); protected: void focusOutEvent(QFocusEvent *e) override; }; - -#endif diff --git a/kstars/widgets/thumbimage.cpp b/kstars/widgets/thumbimage.cpp index f10a2cb28..1282c0d72 100644 --- a/kstars/widgets/thumbimage.cpp +++ b/kstars/widgets/thumbimage.cpp @@ -1,209 +1,205 @@ /*************************************************************************** thumbimage.cpp - description ------------------- begin : Fri 09 Dec 2005 copyright : (C) 2005 by Jason Harris and Jasem Mutlaq email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 "thumbimage.h" #include #include #include QPixmap ThumbImage::croppedImage() { return Image->copy(*CropRect); } ThumbImage::ThumbImage(QWidget *parent, const char *name) : QLabel(parent) { //FIXME: Deprecated? Maybe we don't need this, since double-buffering is now built in // setBackgroundMode( Qt::WA_NoBackground ); setObjectName(name); bMouseButtonDown = false; bTopLeftGrab = false; bTopRightGrab = false; bBottomLeftGrab = false; bBottomRightGrab = false; HandleSize = 10; CropRect = new QRect(); Anchor = new QPoint(); Image = new QPixmap(); } -ThumbImage::~ThumbImage() -{ -} - void ThumbImage::paintEvent(QPaintEvent *) { QPainter p; p.begin(this); p.drawPixmap(0, 0, *Image); p.setPen(QPen(QColor("Grey"), 2)); p.drawRect(*CropRect); p.setPen(QPen(QColor("Grey"), 0)); p.drawRect(QRect(CropRect->left(), CropRect->top(), HandleSize, HandleSize)); p.drawRect(QRect(CropRect->right() - HandleSize, CropRect->top(), HandleSize, HandleSize)); p.drawRect(QRect(CropRect->left(), CropRect->bottom() - HandleSize, HandleSize, HandleSize)); p.drawRect(QRect(CropRect->right() - HandleSize, CropRect->bottom() - HandleSize, HandleSize, HandleSize)); if (CropRect->x() > 0) p.fillRect(0, 0, CropRect->x(), height(), QBrush(QColor("white"), Qt::Dense3Pattern)); if (CropRect->right() < width()) p.fillRect(CropRect->right(), 0, (width() - CropRect->right()), height(), QBrush(QColor("white"), Qt::Dense3Pattern)); if (CropRect->y() > 0) p.fillRect(0, 0, width(), CropRect->y(), QBrush(QColor("white"), Qt::Dense3Pattern)); if (CropRect->bottom() < height()) p.fillRect(0, CropRect->bottom(), width(), (height() - CropRect->bottom()), QBrush(QColor("white"), Qt::Dense3Pattern)); p.end(); } void ThumbImage::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton && CropRect->contains(e->pos())) { bMouseButtonDown = true; //The Anchor tells how far from the CropRect corner we clicked Anchor->setX(e->x() - CropRect->left()); Anchor->setY(e->y() - CropRect->top()); if (e->x() <= CropRect->left() + HandleSize && e->y() <= CropRect->top() + HandleSize) { bTopLeftGrab = true; } if (e->x() <= CropRect->left() + HandleSize && e->y() >= CropRect->bottom() - HandleSize) { bBottomLeftGrab = true; Anchor->setY(e->y() - CropRect->bottom()); } if (e->x() >= CropRect->right() - HandleSize && e->y() <= CropRect->top() + HandleSize) { bTopRightGrab = true; Anchor->setX(e->x() - CropRect->right()); } if (e->x() >= CropRect->right() - HandleSize && e->y() >= CropRect->bottom() - HandleSize) { bBottomRightGrab = true; Anchor->setX(e->x() - CropRect->right()); Anchor->setY(e->y() - CropRect->bottom()); } } } void ThumbImage::mouseReleaseEvent(QMouseEvent *) { if (bMouseButtonDown) bMouseButtonDown = false; if (bTopLeftGrab) bTopLeftGrab = false; if (bTopRightGrab) bTopRightGrab = false; if (bBottomLeftGrab) bBottomLeftGrab = false; if (bBottomRightGrab) bBottomRightGrab = false; } void ThumbImage::mouseMoveEvent(QMouseEvent *e) { if (bMouseButtonDown) { //If a corner was grabbed, we are resizing the box if (bTopLeftGrab) { if (e->x() >= 0 && e->x() <= width()) CropRect->setLeft(e->x() - Anchor->x()); if (e->y() >= 0 && e->y() <= height()) CropRect->setTop(e->y() - Anchor->y()); if (CropRect->left() < 0) CropRect->setLeft(0); if (CropRect->top() < 0) CropRect->setTop(0); if (CropRect->width() < 200) CropRect->setLeft(CropRect->left() - 200 + CropRect->width()); if (CropRect->height() < 200) CropRect->setTop(CropRect->top() - 200 + CropRect->height()); } else if (bTopRightGrab) { if (e->x() >= 0 && e->x() <= width()) CropRect->setRight(e->x() - Anchor->x()); if (e->y() >= 0 && e->y() <= height()) CropRect->setTop(e->y() - Anchor->y()); if (CropRect->right() > width()) CropRect->setRight(width()); if (CropRect->top() < 0) CropRect->setTop(0); if (CropRect->width() < 200) CropRect->setRight(CropRect->right() + 200 - CropRect->width()); if (CropRect->height() < 200) CropRect->setTop(CropRect->top() - 200 + CropRect->height()); } else if (bBottomLeftGrab) { if (e->x() >= 0 && e->x() <= width()) CropRect->setLeft(e->x() - Anchor->x()); if (e->y() >= 0 && e->y() <= height()) CropRect->setBottom(e->y() - Anchor->y()); if (CropRect->left() < 0) CropRect->setLeft(0); if (CropRect->bottom() > height()) CropRect->setBottom(height()); if (CropRect->width() < 200) CropRect->setLeft(CropRect->left() - 200 + CropRect->width()); if (CropRect->height() < 200) CropRect->setBottom(CropRect->bottom() + 200 - CropRect->height()); } else if (bBottomRightGrab) { if (e->x() >= 0 && e->x() <= width()) CropRect->setRight(e->x() - Anchor->x()); if (e->y() >= 0 && e->y() <= height()) CropRect->setBottom(e->y() - Anchor->y()); if (CropRect->right() > width()) CropRect->setRight(width()); if (CropRect->bottom() > height()) CropRect->setBottom(height()); if (CropRect->width() < 200) CropRect->setRight(CropRect->right() + 200 - CropRect->width()); if (CropRect->height() < 200) CropRect->setBottom(CropRect->bottom() + 200 - CropRect->height()); } else //no corner grabbed; move croprect { CropRect->moveTopLeft(QPoint(e->x() - Anchor->x(), e->y() - Anchor->y())); if (CropRect->left() < 0) CropRect->moveLeft(0); if (CropRect->right() > width()) CropRect->moveRight(width()); if (CropRect->top() < 0) CropRect->moveTop(0); if (CropRect->bottom() > height()) CropRect->moveBottom(height()); } emit cropRegionModified(); update(); } } diff --git a/kstars/widgets/thumbimage.h b/kstars/widgets/thumbimage.h index 70c4698c7..fcbe41c53 100644 --- a/kstars/widgets/thumbimage.h +++ b/kstars/widgets/thumbimage.h @@ -1,62 +1,62 @@ /*************************************************************************** thumbimage.h - description ------------------- begin : Fri 09 Dec 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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 THUMBIMAGE_H -#define THUMBIMAGE_H +#pragma once #include #include class ThumbImage : public QLabel { Q_OBJECT public: explicit ThumbImage(QWidget *parent, const char *name = nullptr); - ~ThumbImage() override; + virtual ~ThumbImage() override = default; void setImage(QPixmap *pm) { Image = pm; setFixedSize(Image->width(), Image->height()); } QPixmap *image() { return Image; } QPixmap croppedImage(); void setCropRect(int x, int y, int w, int h) { CropRect->setRect(x, y, w, h); } QRect *cropRect() const { return CropRect; } signals: void cropRegionModified(); protected: // void resizeEvent( QResizeEvent *e); void paintEvent(QPaintEvent *) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; private: - QRect *CropRect; - QPoint *Anchor; - QPixmap *Image; - - bool bMouseButtonDown; - bool bTopLeftGrab, bBottomLeftGrab, bTopRightGrab, bBottomRightGrab; - int HandleSize; + QRect *CropRect { nullptr }; + QPoint *Anchor { nullptr }; + QPixmap *Image { nullptr }; + + bool bMouseButtonDown { false }; + bool bTopLeftGrab { false }; + bool bBottomLeftGrab { false }; + bool bTopRightGrab { false }; + bool bBottomRightGrab { false }; + int HandleSize { 0 }; }; - -#endif diff --git a/kstars/widgets/timespinbox.h b/kstars/widgets/timespinbox.h index 8a2b9d092..638f6e322 100644 --- a/kstars/widgets/timespinbox.h +++ b/kstars/widgets/timespinbox.h @@ -1,76 +1,76 @@ /*************************************************************************** timespinbox.h - description ------------------- begin : Sun Mar 31 2002 copyright : (C) 2002 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #pragma once #include #include /** * @class TimeSpinBox * Custom spinbox to handle selection of timestep values with variable units. * @note this should only be used internally, embedded in a TimeStepBox widget. * * @author Jason Harris * @version 1.0 */ class TimeSpinBox : public QSpinBox { Q_OBJECT public: /** Constructor */ explicit TimeSpinBox(QWidget *parent, bool daysOnly = false); /** Destructor (empty) */ - ~TimeSpinBox() override {} + ~TimeSpinBox() override = default; /** * Convert the internal value to a display string. * @note reimplemented from QSpinBox * @p value the internal value to convert to a display string * @return the display string */ QString textFromValue(int value) const override; /** * Convert the displayed string to an internal value. * @note reimplemented from QSpinBox * @p ok bool pointer set to true if conversion was successful * @return internal value converted from displayed text */ int valueFromText(const QString &text) const override; /** @return the current TimeStep setting */ float timeScale() const; void setDaysOnly(bool daysonly); bool daysOnly() const { return DaysOnly; } signals: void scaleChanged(float s); public slots: void changeScale(float s); protected slots: void reportChange(); private: bool DaysOnly; float TimeScale[43]; QStringList TimeString; }; diff --git a/kstars/xplanet/opsxplanet.cpp b/kstars/xplanet/opsxplanet.cpp index 39b719c80..cec93bcf1 100644 --- a/kstars/xplanet/opsxplanet.cpp +++ b/kstars/xplanet/opsxplanet.cpp @@ -1,189 +1,185 @@ /*************************************************************************** opsxplanet.cpp - K Desktop Planetarium ------------------- begin : Wed 26 Nov 2008 copyright : (C) 2008 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 "opsxplanet.h" #include "kstars.h" #include "kstarsdata.h" #include "Options.h" OpsXplanet::OpsXplanet(KStars *_ks) : QFrame(_ks), ksw(_ks) { setupUi(this); #ifdef Q_OS_OSX connect(kcfg_xplanetIsInternal, SIGNAL(clicked()), this, SLOT(toggleXPlanetInternal())); kcfg_xplanetIsInternal->setToolTip(i18n("Internal or External XPlanet?")); if (Options::xplanetIsInternal()) kcfg_XplanetPath->setEnabled(false); #else kcfg_xplanetIsInternal->setVisible(false); #endif // Init projections combobox kcfg_XplanetProjection->addItem(i18nc("Map projection method", "No projection"), "no projection"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Ancient"), "ancient"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Azimuthal"), "azimuthal"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Bonne"), "bonne"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Gnomonic"), "gnomonic"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Hemisphere"), "hemisphere"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Lambert"), "lambert"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Mercator"), "mercator"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Mollweide"), "mollweide"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Orthographic"), "orthographic"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Peters"), "peters"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Polyconic"), "polyconic"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "Rectangular"), "rectangular"); kcfg_XplanetProjection->addItem(i18nc("Map projection method", "TSC"), "tsc"); // Enable/Disable somme widgets connect(kcfg_XplanetWait, SIGNAL(toggled(bool)), SLOT(slotUpdateWidgets(bool))); connect(kcfg_XplanetConfigFile, SIGNAL(toggled(bool)), SLOT(slotConfigFileWidgets(bool))); connect(kcfg_XplanetStarmap, SIGNAL(toggled(bool)), SLOT(slotStarmapFileWidgets(bool))); connect(kcfg_XplanetArcFile, SIGNAL(toggled(bool)), SLOT(slotArcFileWidgets(bool))); connect(kcfg_XplanetLabel, SIGNAL(toggled(bool)), SLOT(slotLabelWidgets(bool))); connect(kcfg_XplanetMarkerFile, SIGNAL(toggled(bool)), SLOT(slotMarkerFileWidgets(bool))); connect(kcfg_XplanetMarkerBounds, SIGNAL(toggled(bool)), SLOT(slotMarkerBoundsWidgets(bool))); connect(kcfg_XplanetProjection, SIGNAL(currentIndexChanged(int)), SLOT(slotProjectionWidgets(int))); connect(kcfg_XplanetBackground, SIGNAL(toggled(bool)), SLOT(slotBackgroundWidgets(bool))); kcfg_XplanetWaitValue->setEnabled(Options::xplanetWait()); textLabelXplanetSecondes->setEnabled(Options::xplanetWait()); kcfg_XplanetConfigFilePath->setEnabled(Options::xplanetConfigFile()); kcfg_XplanetStarmapPath->setEnabled(Options::xplanetStarmap()); kcfg_XplanetArcFilePath->setEnabled(Options::xplanetArcFile()); kcfg_XplanetLabelLocalTime->setEnabled(Options::xplanetLabel()); kcfg_XplanetLabelGMT->setEnabled(Options::xplanetLabel()); textLabelXplanetLabelString->setEnabled(Options::xplanetLabel()); kcfg_XplanetLabelString->setEnabled(Options::xplanetLabel()); textLabelXplanetDateFormat->setEnabled(Options::xplanetLabel()); kcfg_XplanetDateFormat->setEnabled(Options::xplanetLabel()); textLabelXplanetFontSize->setEnabled(Options::xplanetLabel()); kcfg_XplanetFontSize->setEnabled(Options::xplanetLabel()); textLabelXplanetColor->setEnabled(Options::xplanetLabel()); kcfg_XplanetColor->setEnabled(Options::xplanetLabel()); textLabelLabelPos->setEnabled(Options::xplanetLabel()); kcfg_XplanetLabelTL->setEnabled(Options::xplanetLabel()); kcfg_XplanetLabelTR->setEnabled(Options::xplanetLabel()); kcfg_XplanetLabelBR->setEnabled(Options::xplanetLabel()); kcfg_XplanetLabelBL->setEnabled(Options::xplanetLabel()); kcfg_XplanetMarkerFilePath->setEnabled(Options::xplanetMarkerFile()); kcfg_XplanetMarkerBounds->setEnabled(Options::xplanetMarkerFile()); if (Options::xplanetMarkerFile() && Options::xplanetMarkerBounds()) kcfg_XplanetMarkerBoundsPath->setEnabled(true); else kcfg_XplanetMarkerBoundsPath->setEnabled(false); if (Options::xplanetProjection() == 0) groupBoxBackground->setEnabled(false); kcfg_XplanetBackgroundImage->setEnabled(Options::xplanetBackgroundImage()); kcfg_XplanetBackgroundImagePath->setEnabled(Options::xplanetBackgroundImage()); kcfg_XplanetBackgroundColor->setEnabled(Options::xplanetBackgroundImage()); kcfg_XplanetBackgroundColorValue->setEnabled(Options::xplanetBackgroundImage()); if (Options::xplanetProjection() == 0) groupBoxBackground->setEnabled(false); } -OpsXplanet::~OpsXplanet() -{ -} - void OpsXplanet::toggleXPlanetInternal() { kcfg_XplanetPath->setEnabled(!kcfg_xplanetIsInternal->isChecked()); if (kcfg_xplanetIsInternal->isChecked()) kcfg_XplanetPath->setText("*Internal XPlanet*"); else kcfg_XplanetPath->setText(KSUtils::getDefaultPath("XplanetPath")); } void OpsXplanet::slotUpdateWidgets(bool on) { kcfg_XplanetWaitValue->setEnabled(on); textLabelXplanetSecondes->setEnabled(on); } void OpsXplanet::slotConfigFileWidgets(bool on) { kcfg_XplanetConfigFilePath->setEnabled(on); } void OpsXplanet::slotStarmapFileWidgets(bool on) { kcfg_XplanetStarmapPath->setEnabled(on); } void OpsXplanet::slotArcFileWidgets(bool on) { kcfg_XplanetArcFilePath->setEnabled(on); } void OpsXplanet::slotLabelWidgets(bool on) { kcfg_XplanetLabelLocalTime->setEnabled(on); kcfg_XplanetLabelGMT->setEnabled(on); textLabelXplanetLabelString->setEnabled(on); kcfg_XplanetLabelString->setEnabled(on); textLabelXplanetDateFormat->setEnabled(on); kcfg_XplanetDateFormat->setEnabled(on); textLabelXplanetFontSize->setEnabled(on); kcfg_XplanetFontSize->setEnabled(on); textLabelXplanetColor->setEnabled(on); kcfg_XplanetColor->setEnabled(on); textLabelLabelPos->setEnabled(on); kcfg_XplanetLabelTL->setEnabled(on); kcfg_XplanetLabelTR->setEnabled(on); kcfg_XplanetLabelBR->setEnabled(on); kcfg_XplanetLabelBL->setEnabled(on); } void OpsXplanet::slotMarkerFileWidgets(bool on) { kcfg_XplanetMarkerFilePath->setEnabled(on); kcfg_XplanetMarkerBounds->setEnabled(on); if (kcfg_XplanetMarkerBounds->isChecked()) kcfg_XplanetMarkerBoundsPath->setEnabled(on); } void OpsXplanet::slotMarkerBoundsWidgets(bool on) { kcfg_XplanetMarkerBoundsPath->setEnabled(on); } void OpsXplanet::slotProjectionWidgets(int index) { if (index == 0) groupBoxBackground->setEnabled(false); else groupBoxBackground->setEnabled(true); if (!kcfg_XplanetBackground->isChecked()) { kcfg_XplanetBackgroundImage->setEnabled(false); kcfg_XplanetBackgroundImagePath->setEnabled(false); kcfg_XplanetBackgroundColor->setEnabled(false); kcfg_XplanetBackgroundColorValue->setEnabled(false); } } void OpsXplanet::slotBackgroundWidgets(bool on) { kcfg_XplanetBackgroundImage->setEnabled(on); kcfg_XplanetBackgroundImagePath->setEnabled(on); kcfg_XplanetBackgroundColor->setEnabled(on); kcfg_XplanetBackgroundColorValue->setEnabled(on); } diff --git a/kstars/xplanet/opsxplanet.h b/kstars/xplanet/opsxplanet.h index 093c68b80..d6a6a2189 100644 --- a/kstars/xplanet/opsxplanet.h +++ b/kstars/xplanet/opsxplanet.h @@ -1,48 +1,45 @@ /*************************************************************************** opsxplanet.h - K Desktop Planetarium ------------------- begin : Wed 26 Nov 2008 copyright : (C) 2008 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * 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 OPSXPLANET_H_ -#define OPSXPLANET_H_ +#pragma once #include "ui_opsxplanet.h" class KStars; class OpsXplanet : public QFrame, public Ui::OpsXplanet { Q_OBJECT public: explicit OpsXplanet(KStars *_ks); - ~OpsXplanet() override; + virtual ~OpsXplanet() override = default; private: - KStars *ksw; + KStars *ksw { nullptr }; private slots: void slotUpdateWidgets(bool on); void slotConfigFileWidgets(bool on); void slotStarmapFileWidgets(bool on); void slotArcFileWidgets(bool on); void slotLabelWidgets(bool on); void slotMarkerFileWidgets(bool on); void slotMarkerBoundsWidgets(bool on); void slotProjectionWidgets(int index); void slotBackgroundWidgets(bool on); void toggleXPlanetInternal(); }; - -#endif // OPSGUIDES_H_