diff --git a/kstars/auxiliary/ksutils.cpp b/kstars/auxiliary/ksutils.cpp index 86cc4f40d..285fc8660 100644 --- a/kstars/auxiliary/ksutils.cpp +++ b/kstars/auxiliary/ksutils.cpp @@ -1,1453 +1,1455 @@ /*************************************************************************** ksutils.cpp - K Desktop Planetarium ------------------- begin : Mon Jan 7 10:48:09 EST 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. * * * ***************************************************************************/ #include "ksutils.h" #include "config-kstars.h" #include "ksnotification.h" #include "deepskyobject.h" #ifndef KSTARS_LITE #include "kswizard.h" #endif #include "Options.h" #include "starobject.h" #include "auxiliary/kspaths.h" #ifndef KSTARS_LITE #include #endif #ifdef HAVE_LIBRAW #include #endif #include #include #include namespace KSUtils { bool isHardwareLimited() { #ifdef __arm__ return true; #else return false; #endif } bool openDataFile(QFile &file, const QString &s) { QString FileName = KSPaths::locate(QStandardPaths::GenericDataLocation, s); if (!FileName.isNull()) { file.setFileName(FileName); return file.open(QIODevice::ReadOnly); } return false; } QString getDSSURL(const SkyPoint *const p) { const DeepSkyObject *dso = nullptr; double height, width; double dss_default_size = Options::defaultDSSImageSize(); double dss_padding = Options::dSSPadding(); Q_ASSERT(p); Q_ASSERT(dss_default_size > 0.0 && dss_padding >= 0.0); dso = dynamic_cast(p); // Decide what to do about the height and width if (dso) { // For deep-sky objects, use their height and width information double a, b, pa; a = dso->a(); b = dso->a() * dso->e(); // Use a * e instead of b, since e() returns 1 whenever one of the dimensions is zero. This is important for circular objects pa = dso->pa() * dms::DegToRad; // We now want to convert a, b, and pa into an image // height and width -- i.e. a dRA and dDec. // DSS uses dDec for height and dRA for width. (i.e. "top" is north in the DSS images, AFAICT) // From some trigonometry, assuming we have a rectangular object (worst case), we need: width = a * sin(pa) + b * cos(pa); height = a * cos(pa) + b * sin(pa); // 'a' and 'b' are in arcminutes, so height and width are in arcminutes // Pad the RA and Dec, so that we show more of the sky than just the object. height += dss_padding; width += dss_padding; } else { // For a generic sky object, we don't know what to do. So // we just assume the default size. height = width = dss_default_size; } // There's no point in tiny DSS images that are smaller than dss_default_size if (height < dss_default_size) height = dss_default_size; if (width < dss_default_size) width = dss_default_size; return getDSSURL(p->ra0(), p->dec0(), width, height); } QString getDSSURL(const dms &ra, const dms &dec, float width, float height, const QString &type) { const QString URLprefix("http://archive.stsci.edu/cgi-bin/dss_search?"); QString URLsuffix = QString("&e=J2000&f=%1&c=none&fov=NONE").arg(type); const double dss_default_size = Options::defaultDSSImageSize(); char decsgn = (dec.Degrees() < 0.0) ? '-' : '+'; int dd = abs(dec.degree()); int dm = abs(dec.arcmin()); int ds = abs(dec.arcsec()); // Infinite and NaN sizes are replaced by the default size and tiny DSS images are resized to default size if (!qIsFinite(height) || height <= 0.0) height = dss_default_size; if (!qIsFinite(width) || width <= 0.0) width = dss_default_size; // DSS accepts images that are no larger than 75 arcminutes if (height > 75.0) height = 75.0; if (width > 75.0) width = 75.0; QString RAString, DecString, SizeString; DecString = DecString.sprintf("&d=%c%02d+%02d+%02d", decsgn, dd, dm, ds); RAString = RAString.sprintf("r=%02d+%02d+%02d", ra.hour(), ra.minute(), ra.second()); SizeString = SizeString.sprintf("&h=%02.1f&w=%02.1f", height, width); return (URLprefix + RAString + DecString + SizeString + URLsuffix); } QString toDirectionString(dms angle) { // TODO: Instead of doing it this way, it would be nicer to // compute the string to arbitrary precision. Although that will // not be easy to localize. (Consider, for instance, Indian // languages that have special names for the intercardinal points) // -- asimha static const char *directions[] = { I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "N"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NNE"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NE"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "ENE"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "E"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "ESE"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SE"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SSE"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "S"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SSW"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SW"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "WSW"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "W"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "WNW"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NW"), I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NNW"), I18N_NOOP2("Unknown cardinal / intercardinal direction", "???") }; int index = (int)((angle.reduce().Degrees() + 11.25) / 22.5); // A number between 0 and 16 (inclusive) is expected if (index < 0 || index > 16) index = 16; // Something went wrong. else index = (index == 16 ? 0 : index); return i18nc("Abbreviated cardinal / intercardinal etc. direction", directions[index]); } QList *castStarObjListToSkyObjList(QList *starObjList) { QList *skyObjList = new QList(); foreach (StarObject *so, *starObjList) { skyObjList->append(so); } return skyObjList; } QString constGenetiveFromAbbrev(const QString &code) { if (code == "And") return QString("Andromedae"); if (code == "Ant") return QString("Antliae"); if (code == "Aps") return QString("Apodis"); if (code == "Aqr") return QString("Aquarii"); if (code == "Aql") return QString("Aquilae"); if (code == "Ara") return QString("Arae"); if (code == "Ari") return QString("Arietis"); if (code == "Aur") return QString("Aurigae"); if (code == "Boo") return QString("Bootis"); if (code == "Cae") return QString("Caeli"); if (code == "Cam") return QString("Camelopardalis"); if (code == "Cnc") return QString("Cancri"); if (code == "CVn") return QString("Canum Venaticorum"); if (code == "CMa") return QString("Canis Majoris"); if (code == "CMi") return QString("Canis Minoris"); if (code == "Cap") return QString("Capricorni"); if (code == "Car") return QString("Carinae"); if (code == "Cas") return QString("Cassiopeiae"); if (code == "Cen") return QString("Centauri"); if (code == "Cep") return QString("Cephei"); if (code == "Cet") return QString("Ceti"); if (code == "Cha") return QString("Chamaeleontis"); if (code == "Cir") return QString("Circini"); if (code == "Col") return QString("Columbae"); if (code == "Com") return QString("Comae Berenices"); if (code == "CrA") return QString("Coronae Austrinae"); if (code == "CrB") return QString("Coronae Borealis"); if (code == "Crv") return QString("Corvi"); if (code == "Crt") return QString("Crateris"); if (code == "Cru") return QString("Crucis"); if (code == "Cyg") return QString("Cygni"); if (code == "Del") return QString("Delphini"); if (code == "Dor") return QString("Doradus"); if (code == "Dra") return QString("Draconis"); if (code == "Equ") return QString("Equulei"); if (code == "Eri") return QString("Eridani"); if (code == "For") return QString("Fornacis"); if (code == "Gem") return QString("Geminorum"); if (code == "Gru") return QString("Gruis"); if (code == "Her") return QString("Herculis"); if (code == "Hor") return QString("Horologii"); if (code == "Hya") return QString("Hydrae"); if (code == "Hyi") return QString("Hydri"); if (code == "Ind") return QString("Indi"); if (code == "Lac") return QString("Lacertae"); if (code == "Leo") return QString("Leonis"); if (code == "LMi") return QString("Leonis Minoris"); if (code == "Lep") return QString("Leporis"); if (code == "Lib") return QString("Librae"); if (code == "Lup") return QString("Lupi"); if (code == "Lyn") return QString("Lyncis"); if (code == "Lyr") return QString("Lyrae"); if (code == "Men") return QString("Mensae"); if (code == "Mic") return QString("Microscopii"); if (code == "Mon") return QString("Monocerotis"); if (code == "Mus") return QString("Muscae"); if (code == "Nor") return QString("Normae"); if (code == "Oct") return QString("Octantis"); if (code == "Oph") return QString("Ophiuchi"); if (code == "Ori") return QString("Orionis"); if (code == "Pav") return QString("Pavonis"); if (code == "Peg") return QString("Pegasi"); if (code == "Per") return QString("Persei"); if (code == "Phe") return QString("Phoenicis"); if (code == "Pic") return QString("Pictoris"); if (code == "Psc") return QString("Piscium"); if (code == "PsA") return QString("Piscis Austrini"); if (code == "Pup") return QString("Puppis"); if (code == "Pyx") return QString("Pyxidis"); if (code == "Ret") return QString("Reticuli"); if (code == "Sge") return QString("Sagittae"); if (code == "Sgr") return QString("Sagittarii"); if (code == "Sco") return QString("Scorpii"); if (code == "Scl") return QString("Sculptoris"); if (code == "Sct") return QString("Scuti"); if (code == "Ser") return QString("Serpentis"); if (code == "Sex") return QString("Sextantis"); if (code == "Tau") return QString("Tauri"); if (code == "Tel") return QString("Telescopii"); if (code == "Tri") return QString("Trianguli"); if (code == "TrA") return QString("Trianguli Australis"); if (code == "Tuc") return QString("Tucanae"); if (code == "UMa") return QString("Ursae Majoris"); if (code == "UMi") return QString("Ursae Minoris"); if (code == "Vel") return QString("Velorum"); if (code == "Vir") return QString("Virginis"); if (code == "Vol") return QString("Volantis"); if (code == "Vul") return QString("Vulpeculae"); return code; } QString constNameFromAbbrev(const QString &code) { if (code == "And") return QString("Andromeda"); if (code == "Ant") return QString("Antlia"); if (code == "Aps") return QString("Apus"); if (code == "Aqr") return QString("Aquarius"); if (code == "Aql") return QString("Aquila"); if (code == "Ara") return QString("Ara"); if (code == "Ari") return QString("Aries"); if (code == "Aur") return QString("Auriga"); if (code == "Boo") return QString("Bootes"); if (code == "Cae") return QString("Caelum"); if (code == "Cam") return QString("Camelopardalis"); if (code == "Cnc") return QString("Cancer"); if (code == "CVn") return QString("Canes Venatici"); if (code == "CMa") return QString("Canis Major"); if (code == "CMi") return QString("Canis Minor"); if (code == "Cap") return QString("Capricornus"); if (code == "Car") return QString("Carina"); if (code == "Cas") return QString("Cassiopeia"); if (code == "Cen") return QString("Centaurus"); if (code == "Cep") return QString("Cepheus"); if (code == "Cet") return QString("Cetus"); if (code == "Cha") return QString("Chamaeleon"); if (code == "Cir") return QString("Circinus"); if (code == "Col") return QString("Columba"); if (code == "Com") return QString("Coma Berenices"); if (code == "CrA") return QString("Corona Australis"); if (code == "CrB") return QString("Corona Borealis"); if (code == "Crv") return QString("Corvus"); if (code == "Crt") return QString("Crater"); if (code == "Cru") return QString("Crux"); if (code == "Cyg") return QString("Cygnus"); if (code == "Del") return QString("Delphinus"); if (code == "Dor") return QString("Doradus"); if (code == "Dra") return QString("Draco"); if (code == "Equ") return QString("Equuleus"); if (code == "Eri") return QString("Eridanus"); if (code == "For") return QString("Fornax"); if (code == "Gem") return QString("Gemini"); if (code == "Gru") return QString("Grus"); if (code == "Her") return QString("Hercules"); if (code == "Hor") return QString("Horologium"); if (code == "Hya") return QString("Hydra"); if (code == "Hyi") return QString("Hydrus"); if (code == "Ind") return QString("Indus"); if (code == "Lac") return QString("Lacerta"); if (code == "Leo") return QString("Leo"); if (code == "LMi") return QString("Leo Minor"); if (code == "Lep") return QString("Lepus"); if (code == "Lib") return QString("Libra"); if (code == "Lup") return QString("Lupus"); if (code == "Lyn") return QString("Lynx"); if (code == "Lyr") return QString("Lyra"); if (code == "Men") return QString("Mensa"); if (code == "Mic") return QString("Microscopium"); if (code == "Mon") return QString("Monoceros"); if (code == "Mus") return QString("Musca"); if (code == "Nor") return QString("Norma"); if (code == "Oct") return QString("Octans"); if (code == "Oph") return QString("Ophiuchus"); if (code == "Ori") return QString("Orion"); if (code == "Pav") return QString("Pavo"); if (code == "Peg") return QString("Pegasus"); if (code == "Per") return QString("Perseus"); if (code == "Phe") return QString("Phoenix"); if (code == "Pic") return QString("Pictor"); if (code == "Psc") return QString("Pisces"); if (code == "PsA") return QString("Piscis Austrinus"); if (code == "Pup") return QString("Puppis"); if (code == "Pyx") return QString("Pyxis"); if (code == "Ret") return QString("Reticulum"); if (code == "Sge") return QString("Sagitta"); if (code == "Sgr") return QString("Sagittarius"); if (code == "Sco") return QString("Scorpius"); if (code == "Scl") return QString("Sculptor"); if (code == "Sct") return QString("Scutum"); if (code == "Ser") return QString("Serpens"); if (code == "Sex") return QString("Sextans"); if (code == "Tau") return QString("Taurus"); if (code == "Tel") return QString("Telescopium"); if (code == "Tri") return QString("Triangulum"); if (code == "TrA") return QString("Triangulum Australe"); if (code == "Tuc") return QString("Tucana"); if (code == "UMa") return QString("Ursa Major"); if (code == "UMi") return QString("Ursa Minor"); if (code == "Vel") return QString("Vela"); if (code == "Vir") return QString("Virgo"); if (code == "Vol") return QString("Volans"); if (code == "Vul") return QString("Vulpecula"); return code; } QString constNameToAbbrev(const QString &fullName_) { QString fullName = fullName_.toLower(); if (fullName == "andromeda") return QString("And"); if (fullName == "antlia") return QString("Ant"); if (fullName == "apus") return QString("Aps"); if (fullName == "aquarius") return QString("Aqr"); if (fullName == "aquila") return QString("Aql"); if (fullName == "ara") return QString("Ara"); if (fullName == "aries") return QString("Ari"); if (fullName == "auriga") return QString("Aur"); if (fullName == "bootes") return QString("Boo"); if (fullName == "caelum") return QString("Cae"); if (fullName == "camelopardalis") return QString("Cam"); if (fullName == "cancer") return QString("Cnc"); if (fullName == "canes venatici") return QString("CVn"); if (fullName == "canis major") return QString("CMa"); if (fullName == "canis minor") return QString("CMi"); if (fullName == "capricornus") return QString("Cap"); if (fullName == "carina") return QString("Car"); if (fullName == "cassiopeia") return QString("Cas"); if (fullName == "centaurus") return QString("Cen"); if (fullName == "cepheus") return QString("Cep"); if (fullName == "cetus") return QString("Cet"); if (fullName == "chamaeleon") return QString("Cha"); if (fullName == "circinus") return QString("Cir"); if (fullName == "columba") return QString("Col"); if (fullName == "coma berenices") return QString("Com"); if (fullName == "corona australis") return QString("CrA"); if (fullName == "corona borealis") return QString("CrB"); if (fullName == "corvus") return QString("Crv"); if (fullName == "crater") return QString("Crt"); if (fullName == "crux") return QString("Cru"); if (fullName == "cygnus") return QString("Cyg"); if (fullName == "delphinus") return QString("Del"); if (fullName == "doradus") return QString("Dor"); if (fullName == "draco") return QString("Dra"); if (fullName == "equuleus") return QString("Equ"); if (fullName == "eridanus") return QString("Eri"); if (fullName == "fornax") return QString("For"); if (fullName == "gemini") return QString("Gem"); if (fullName == "grus") return QString("Gru"); if (fullName == "hercules") return QString("Her"); if (fullName == "horologium") return QString("Hor"); if (fullName == "hydra") return QString("Hya"); if (fullName == "hydrus") return QString("Hyi"); if (fullName == "indus") return QString("Ind"); if (fullName == "lacerta") return QString("Lac"); if (fullName == "leo") return QString("Leo"); if (fullName == "leo minor") return QString("LMi"); if (fullName == "lepus") return QString("Lep"); if (fullName == "libra") return QString("Lib"); if (fullName == "lupus") return QString("Lup"); if (fullName == "lynx") return QString("Lyn"); if (fullName == "lyra") return QString("Lyr"); if (fullName == "mensa") return QString("Men"); if (fullName == "microscopium") return QString("Mic"); if (fullName == "monoceros") return QString("Mon"); if (fullName == "musca") return QString("Mus"); if (fullName == "norma") return QString("Nor"); if (fullName == "octans") return QString("Oct"); if (fullName == "ophiuchus") return QString("Oph"); if (fullName == "orion") return QString("Ori"); if (fullName == "pavo") return QString("Pav"); if (fullName == "pegasus") return QString("Peg"); if (fullName == "perseus") return QString("Per"); if (fullName == "phoenix") return QString("Phe"); if (fullName == "pictor") return QString("Pic"); if (fullName == "pisces") return QString("Psc"); if (fullName == "piscis austrinus") return QString("PsA"); if (fullName == "puppis") return QString("Pup"); if (fullName == "pyxis") return QString("Pyx"); if (fullName == "reticulum") return QString("Ret"); if (fullName == "sagitta") return QString("Sge"); if (fullName == "sagittarius") return QString("Sgr"); if (fullName == "scorpius") return QString("Sco"); if (fullName == "sculptor") return QString("Scl"); if (fullName == "scutum") return QString("Sct"); if (fullName == "serpens") return QString("Ser"); if (fullName == "sextans") return QString("Sex"); if (fullName == "taurus") return QString("Tau"); if (fullName == "telescopium") return QString("Tel"); if (fullName == "triangulum") return QString("Tri"); if (fullName == "triangulum australe") return QString("TrA"); if (fullName == "tucana") return QString("Tuc"); if (fullName == "ursa major") return QString("UMa"); if (fullName == "ursa minor") return QString("UMi"); if (fullName == "vela") return QString("Vel"); if (fullName == "virgo") return QString("Vir"); if (fullName == "volans") return QString("Vol"); if (fullName == "vulpecula") return QString("Vul"); return fullName_; } QString constGenetiveToAbbrev(const QString &genetive_) { QString genetive = genetive_.toLower(); if (genetive == "andromedae") return QString("And"); if (genetive == "antliae") return QString("Ant"); if (genetive == "apodis") return QString("Aps"); if (genetive == "aquarii") return QString("Aqr"); if (genetive == "aquilae") return QString("Aql"); if (genetive == "arae") return QString("Ara"); if (genetive == "arietis") return QString("Ari"); if (genetive == "aurigae") return QString("Aur"); if (genetive == "bootis") return QString("Boo"); if (genetive == "caeli") return QString("Cae"); if (genetive == "camelopardalis") return QString("Cam"); if (genetive == "cancri") return QString("Cnc"); if (genetive == "canum venaticorum") return QString("CVn"); if (genetive == "canis majoris") return QString("CMa"); if (genetive == "canis minoris") return QString("CMi"); if (genetive == "capricorni") return QString("Cap"); if (genetive == "carinae") return QString("Car"); if (genetive == "cassiopeiae") return QString("Cas"); if (genetive == "centauri") return QString("Cen"); if (genetive == "cephei") return QString("Cep"); if (genetive == "ceti") return QString("Cet"); if (genetive == "chamaeleontis") return QString("Cha"); if (genetive == "circini") return QString("Cir"); if (genetive == "columbae") return QString("Col"); if (genetive == "comae berenices") return QString("Com"); if (genetive == "coronae austrinae") return QString("CrA"); if (genetive == "coronae borealis") return QString("CrB"); if (genetive == "corvi") return QString("Crv"); if (genetive == "crateris") return QString("Crt"); if (genetive == "crucis") return QString("Cru"); if (genetive == "cygni") return QString("Cyg"); if (genetive == "delphini") return QString("Del"); if (genetive == "doradus") return QString("Dor"); if (genetive == "draconis") return QString("Dra"); if (genetive == "equulei") return QString("Equ"); if (genetive == "eridani") return QString("Eri"); if (genetive == "fornacis") return QString("For"); if (genetive == "geminorum") return QString("Gem"); if (genetive == "gruis") return QString("Gru"); if (genetive == "herculis") return QString("Her"); if (genetive == "horologii") return QString("Hor"); if (genetive == "hydrae") return QString("Hya"); if (genetive == "hydri") return QString("Hyi"); if (genetive == "indi") return QString("Ind"); if (genetive == "lacertae") return QString("Lac"); if (genetive == "leonis") return QString("Leo"); if (genetive == "leonis minoris") return QString("LMi"); if (genetive == "leporis") return QString("Lep"); if (genetive == "librae") return QString("Lib"); if (genetive == "lupi") return QString("Lup"); if (genetive == "lyncis") return QString("Lyn"); if (genetive == "lyrae") return QString("Lyr"); if (genetive == "mensae") return QString("Men"); if (genetive == "microscopii") return QString("Mic"); if (genetive == "monocerotis") return QString("Mon"); if (genetive == "muscae") return QString("Mus"); if (genetive == "normae") return QString("Nor"); if (genetive == "octantis") return QString("Oct"); if (genetive == "ophiuchi") return QString("Oph"); if (genetive == "orionis") return QString("Ori"); if (genetive == "pavonis") return QString("Pav"); if (genetive == "pegasi") return QString("Peg"); if (genetive == "persei") return QString("Per"); if (genetive == "phoenicis") return QString("Phe"); if (genetive == "pictoris") return QString("Pic"); if (genetive == "piscium") return QString("Psc"); if (genetive == "piscis austrini") return QString("PsA"); if (genetive == "puppis") return QString("Pup"); if (genetive == "pyxidis") return QString("Pyx"); if (genetive == "reticuli") return QString("Ret"); if (genetive == "sagittae") return QString("Sge"); if (genetive == "sagittarii") return QString("Sgr"); if (genetive == "scorpii") return QString("Sco"); if (genetive == "sculptoris") return QString("Scl"); if (genetive == "scuti") return QString("Sct"); if (genetive == "serpentis") return QString("Ser"); if (genetive == "sextantis") return QString("Sex"); if (genetive == "tauri") return QString("Tau"); if (genetive == "telescopii") return QString("Tel"); if (genetive == "trianguli") return QString("Tri"); if (genetive == "trianguli australis") return QString("TrA"); if (genetive == "tucanae") return QString("Tuc"); if (genetive == "ursae majoris") return QString("UMa"); if (genetive == "ursae minoris") return QString("UMi"); if (genetive == "velorum") return QString("Vel"); if (genetive == "virginis") return QString("Vir"); if (genetive == "volantis") return QString("Vol"); if (genetive == "vulpeculae") return QString("Vul"); return genetive_; } QString Logging::_filename; void Logging::UseFile() { if (_filename.isEmpty()) { QDir dir; QString path = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "logs/" + QDateTime::currentDateTime().toString("yyyy-MM-dd"); dir.mkpath(path); QString name = "log_" + QDateTime::currentDateTime().toString("HH-mm-ss") + ".txt"; _filename = path + QStringLiteral("/") + name; // Clear file contents QFile file(_filename); file.open(QFile::WriteOnly); file.close(); } qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} %{if-debug}DEBG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{endif}%{if-critical}CRIT%{endif}%{if-fatal}FATL%{endif}] %{if-category}[%{category}]%{endif} - %{message}"); qInstallMessageHandler(File); } void Logging::File(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QFile file(_filename); if (file.open(QFile::Append | QIODevice::Text)) { QTextStream stream(&file); Write(stream, type, context, msg); } } void Logging::UseStdout() { qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} %{if-debug}DEBG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{endif}%{if-critical}CRIT%{endif}%{if-fatal}FATL%{endif}] %{if-category}[%{category}]%{endif} - %{message}"); qInstallMessageHandler(Stdout); } void Logging::Stdout(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QTextStream stream(stdout, QIODevice::WriteOnly); Write(stream, type, context, msg); } void Logging::UseStderr() { qInstallMessageHandler(Stderr); } void Logging::Stderr(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QTextStream stream(stderr, QIODevice::WriteOnly); Write(stream, type, context, msg); } void Logging::Write(QTextStream &stream, QtMsgType type, const QMessageLogContext &context, const QString &msg) { stream << QDateTime::currentDateTime().toString("[yyyy-MM-ddThh:mm:ss.zzz t "); switch (type) { case QtInfoMsg: stream << "INFO ]"; break; case QtDebugMsg: stream << "DEBG ]"; break; case QtWarningMsg: stream << "WARN ]"; break; case QtCriticalMsg: stream << "CRIT ]"; break; case QtFatalMsg: stream << "FATL ]"; break; default: stream << "UNKN ]"; } stream << "[" << qSetFieldWidth(30) << context.category << qSetFieldWidth(0) << "] - "; stream << msg << endl; //stream << qFormatLogMessage(type, context, msg) << endl; } void Logging::UseDefault() { qInstallMessageHandler(nullptr); } void Logging::Disable() { qInstallMessageHandler(Disabled); } void Logging::Disabled(QtMsgType, const QMessageLogContext &, const QString &) { } void Logging::SyncFilterRules() { QString rules = QString("org.kde.kstars.ekos.debug=%1\n" "org.kde.kstars.indi.debug=%2\n" "org.kde.kstars.fits.debug=%3\n" "org.kde.kstars.ekos.capture.debug=%4\n" "org.kde.kstars.ekos.focus.debug=%5\n" "org.kde.kstars.ekos.guide.debug=%6\n" "org.kde.kstars.ekos.align.debug=%7\n" "org.kde.kstars.ekos.mount.debug=%8\n" - "org.kde.kstars.ekos.scheduler.debug=%9\n" - "org.kde.kstars.debug=%1").arg( + "org.kde.kstars.ekos.scheduler.debug=%9\n").arg( Options::verboseLogging() ? "true" : "false", Options::iNDILogging() ? "true" : "false", Options::fITSLogging() ? "true" : "false", Options::captureLogging() ? "true" : "false", Options::focusLogging() ? "true" : "false", Options::guideLogging() ? "true" : "false", Options::alignmentLogging() ? "true" : "false", Options::mountLogging() ? "true" : "false", - Options::schedulerLogging() ? "true" : "false" - ); + Options::schedulerLogging() ? "true" : "false") + .append(QString("org.kde.kstars.ekos.observatory.debug=%2\n" + "org.kde.kstars.debug=%1").arg( + Options::verboseLogging() ? "true" : "false", + Options::observatoryLogging() ? "true" : "false")); QLoggingCategory::setFilterRules(rules); } /** This method provides a centralized location for the default paths to important external files used in the Options on different operating systems. Note that on OS X, if the user builds the app without indi, astrometry, and xplanet internally then the options below will be used. If the user drags the app from a dmg and has to install the KStars data directory, then most of these paths will be overwritten since it is preferred to use the internal versions. **/ QString getDefaultPath(QString option) { QString snap = QProcessEnvironment::systemEnvironment().value("SNAP"); QString flat = QProcessEnvironment::systemEnvironment().value("FLATPAK_DEST"); if (option == "fitsDir") { return QDir::homePath(); } else if (option == "indiServer") { #ifdef Q_OS_OSX return "/usr/local/bin/indiserver"; #endif if (flat.isEmpty() == false) return flat + "/bin/indiserver"; else return snap + "/usr/bin/indiserver"; } else if (option == "indiDriversDir") { #ifdef Q_OS_OSX return "/usr/local/share/indi"; #elif defined(Q_OS_LINUX) if (flat.isEmpty() == false) return flat + "/share/indi"; else return snap + "/usr/share/indi"; #else return QStandardPaths::locate(QStandardPaths::GenericDataLocation, "indi", QStandardPaths::LocateDirectory); #endif } else if (option == "AstrometrySolverBinary") { #ifdef Q_OS_OSX return "/usr/local/bin/solve-field"; #endif if (flat.isEmpty() == false) return flat + "/bin/solve-field"; else return snap + "/usr/bin/solve-field"; } else if (option == "AstrometryWCSInfo") { #ifdef Q_OS_OSX return "/usr/local/bin/wcsinfo"; #endif if (flat.isEmpty() == false) return flat + "/bin/wcsinfo"; else return snap + "/usr/bin/wcsinfo"; } else if (option == "AstrometryConfFile") { #ifdef Q_OS_OSX return "/usr/local/etc/astrometry.cfg"; #endif if (flat.isEmpty() == false) return flat + "/etc/astrometry.cfg"; else return snap + "/etc/astrometry.cfg"; } else if (option == "AstrometryIndexFileLocation") { #ifdef Q_OS_OSX return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/Astrometry/"; #endif if (flat.isEmpty() == false) return flat + "/usr/share/astrometry/"; else return snap + "/usr/share/astrometry/"; } else if (option == "XplanetPath") { #ifdef Q_OS_OSX return "/usr/local/bin/xplanet"; #endif if (flat.isEmpty() == false) return flat + "/bin/xplanet"; else return snap + "/usr/bin/xplanet"; } return QString(); } #ifdef Q_OS_OSX //Note that this will copy and will not overwrite, so that the user's changes in the files are preserved. void copyResourcesFolderFromAppBundle(QString folder) { QString folderLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, folder, QStandardPaths::LocateDirectory); QDir folderSourceDir; if(folder == "kstars") folderSourceDir = QDir(QCoreApplication::applicationDirPath() + "/../Resources/data").absolutePath(); else folderSourceDir = QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + folder).absolutePath(); if (folderSourceDir.exists()) { folderLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + '/' + folder; QDir writableDir; writableDir.mkdir(folderLocation); copyRecursively(folderSourceDir.absolutePath(), folderLocation); } } bool copyDataFolderFromAppBundleIfNeeded() //The method returns true if the data directory is good to go. { //This will copy the locale folder, the notifications folder, and the sounds folder and any missing files in them to Application Support if needed. copyResourcesFolderFromAppBundle("locale"); copyResourcesFolderFromAppBundle("knotifications5"); copyResourcesFolderFromAppBundle("sounds"); //This will check for the data directory and if its not present, it will run the wizard. QString dataLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); if (dataLocation.isEmpty()) //If there is no kstars user data directory { QPointer wizard = new KSWizard(new QFrame()); wizard->exec(); //This will pause the startup until the user installs the data directory from the Wizard. dataLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); if (dataLocation.isEmpty()) return false; //This sets some important OS X options. Options::setIndiServerIsInternal(true); Options::setIndiServer("*Internal INDI Server*"); Options::setIndiDriversAreInternal(true); Options::setIndiDriversDir("*Internal INDI Drivers*"); Options::setAstrometrySolverIsInternal(true); Options::setAstrometrySolverBinary("*Internal Solver*"); Options::setAstrometryConfFileIsInternal(true); Options::setAstrometryConfFile("*Internal astrometry.cfg*"); Options::setAstrometryWCSIsInternal(true); Options::setAstrometryWCSInfo("*Internal wcsinfo*"); Options::setAstrometryUseNoFITS2FITS(false); Options::setXplanetIsInternal(true); Options::setXplanetPath("*Internal XPlanet*"); Options::setRunStartupWizard(false); //don't run on startup because we are doing it now. return true; //This means the data directory is good to go now that we created it from the default. } //This will copy any of the critical KStars files from the app bundle to application support if they are missing. copyResourcesFolderFromAppBundle("kstars"); return true; //This means the data directory was good to go from the start and the wizard did not run. } bool 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) { KSNotification::error(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.trimmed().mid(9).trimmed(); return true; } } KSNotification::error(i18n("Unable to find data dir in astrometry configuration file.")); return false; } bool setAstrometryDataDir(QString dataDir) { if(Options::astrometryIndexFileLocation() != dataDir) Options::setAstrometryIndexFileLocation(dataDir); QString confPath; if (Options::astrometryConfFileIsInternal()) confPath = QCoreApplication::applicationDirPath() + "/astrometry/bin/astrometry.cfg"; else confPath = Options::astrometryConfFile(); QFile confFile(confPath); QString contents; if (confFile.open(QIODevice::ReadOnly) == false) { KSNotification::error( i18n("Astrometry Configuration File Read Error.")); return false; } else { QTextStream in(&confFile); QString line; bool foundPathBefore = false; bool fileNeedsUpdating = false; while (!in.atEnd()) { line = in.readLine(); if (line.trimmed().startsWith(QLatin1String("add_path"))) { if (!foundPathBefore) //This will ensure there is not more than one add_path line in the file. { foundPathBefore = true; QString dataDirInFile = line.trimmed().mid(9).trimmed(); if (dataDirInFile != dataDir) //Update to the correct path. { contents += "add_path " + dataDir + '\n'; fileNeedsUpdating = true; } } } else { contents += line + '\n'; } } confFile.close(); if (fileNeedsUpdating) { if (confFile.open(QIODevice::WriteOnly) == false) { KSNotification::error( i18n("Internal Astrometry Configuration File Write Error.")); return false; } else { QTextStream out(&confFile); out << contents; confFile.close(); } } } return true; } bool configureAstrometry() { QString astrometryDataDir; if (KSUtils::getAstrometryDataDir(astrometryDataDir) == false) return false; if(astrometryDataDir == "IndexFileLocationNotYetSet") { astrometryDataDir = Options::astrometryIndexFileLocation(); setAstrometryDataDir(astrometryDataDir); } if(Options::astrometryIndexFileLocation() != astrometryDataDir) { if (KMessageBox::warningYesNo( nullptr, i18n("The Astrometry Index File Location Stored in KStars: \n %1 \n does not match the Index file location in the config file: \n %2 \n Do you want to update the config file?", Options::astrometryIndexFileLocation(), astrometryDataDir), i18n("Update Config File?")) == KMessageBox::Yes) { astrometryDataDir = Options::astrometryIndexFileLocation(); setAstrometryDataDir(astrometryDataDir); } else { return false; } } if (QDir(astrometryDataDir).exists() == false) { if (KMessageBox::warningYesNo( nullptr, i18n("The selected Astrometry Index File Location:\n %1 \n does not exist. Do you want to make the directory?", astrometryDataDir), i18n("Make Astrometry Index File Directory?")) == KMessageBox::Yes) { if(QDir(astrometryDataDir).mkdir(astrometryDataDir)) { KSNotification::info(i18n("The Astrometry Index File Location was created.")); } else { KSNotification::sorry(i18n("The Astrometry Index File Directory does not exist and was not able to be created.")); } } else { return false; } } //If the Index File Directories match and the directory exists, we are good to go. return true; } bool copyRecursively(QString sourceFolder, QString destFolder) { QDir sourceDir(sourceFolder); if (!sourceDir.exists()) return false; QDir destDir(destFolder); if (!destDir.exists()) destDir.mkdir(destFolder); QStringList files = sourceDir.entryList(QDir::Files); for (int i = 0; i < files.count(); i++) { QString srcName = sourceFolder + QDir::separator() + files[i]; QString destName = destFolder + QDir::separator() + files[i]; QFile::copy(srcName, destName); //Note this does not overwrite files } files.clear(); files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); for (int i = 0; i < files.count(); i++) { QString srcName = sourceFolder + QDir::separator() + files[i]; QString destName = destFolder + QDir::separator() + files[i]; copyRecursively(srcName, destName); } return true; } #endif QByteArray getJPLQueryString(const QByteArray &kind, const QByteArray &dataFields, const QVector &filters) { QByteArray query("obj_group=all&obj_kind=" + kind + "&obj_numbered=all&OBJ_field=0&ORB_field=0"); // Apply filters: for (int i = 0; i < filters.length(); i++) { QByteArray f = QByteArray::number(i + 1); query += "&c" + f + "_group=OBJ&c1_item=" + filters[i].item + "&c" + f + "_op=" + filters[i].op + "&c" + f + "_value=" + filters[i].value; } // Apply query data fields... query += "&c_fields=" + dataFields; query += "&table_format=CSV&max_rows=10&format_option=full&query=Generate%20Table&." "cgifields=format_option&.cgifields=field_list&.cgifields=obj_kind&.cgifie" "lds=obj_group&.cgifields=obj_numbered&.cgifields=combine_mode&.cgifields=" "ast_orbit_class&.cgifields=table_format&.cgifields=ORB_field_set&.cgifiel" "ds=OBJ_field_set&.cgifields=preset_field_set&.cgifields=com_orbit_class"; return query; } bool RAWToJPEG(const QString &rawImage, const QString &output, QString &errorMessage) { #ifndef HAVE_LIBRAW errorMessage = i18n("Unable to find dcraw and cjpeg. Please install the required tools to convert CR2/NEF to JPEG."); return false; #else int ret = 0; // Creation of image processing object LibRaw RawProcessor; // Let us open the file if ((ret = RawProcessor.open_file(rawImage.toLatin1().data())) != LIBRAW_SUCCESS) { errorMessage = i18n("Cannot open %1: %2", rawImage, libraw_strerror(ret)); RawProcessor.recycle(); return false; } // Let us unpack the thumbnail if ((ret = RawProcessor.unpack_thumb()) != LIBRAW_SUCCESS) { errorMessage = i18n("Cannot unpack_thumb %1: %2", rawImage, libraw_strerror(ret)); RawProcessor.recycle(); return false; } else // We have successfully unpacked the thumbnail, now let us write it to a file { //snprintf(thumbfn,sizeof(thumbfn),"%s.%s",av[i],T.tformat == LIBRAW_THUMBNAIL_JPEG ? "thumb.jpg" : "thumb.ppm"); if (LIBRAW_SUCCESS != (ret = RawProcessor.dcraw_thumb_writer(output.toLatin1().data()))) { errorMessage = i18n("Cannot write %s %1: %2", output, libraw_strerror(ret)); RawProcessor.recycle(); return false; } } return true; #endif } } diff --git a/kstars/ekos/auxiliary/dome.cpp b/kstars/ekos/auxiliary/dome.cpp index ef0f76cdd..63daa3213 100644 --- a/kstars/ekos/auxiliary/dome.cpp +++ b/kstars/ekos/auxiliary/dome.cpp @@ -1,226 +1,211 @@ /* 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 "domeadaptor.h" #include "ekos/manager.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; } void Dome::setDome(ISD::GDInterface *newDome) { if (newDome == currentDome) return; currentDome = static_cast(newDome); currentDome->disconnect(this); connect(currentDome, &ISD::Dome::newStatus, this, &Dome::newStatus); - connect(currentDome, &ISD::Dome::newParkStatus, this, &Dome::newParkStatus); + connect(currentDome, &ISD::Dome::newStatus, this, &Dome::setStatus); connect(currentDome, &ISD::Dome::newParkStatus, [&](ISD::ParkStatus status) { m_ParkStatus = status; + emit newParkStatus(status); }); connect(currentDome, &ISD::Dome::newShutterStatus, this, &Dome::newShutterStatus); connect(currentDome, &ISD::Dome::newShutterStatus, [&](ISD::Dome::ShutterStatus status) { m_ShutterStatus = status; }); connect(currentDome, &ISD::Dome::newAutoSyncStatus, this, &Dome::newAutoSyncStatus); connect(currentDome, &ISD::Dome::azimuthPositionChanged, this, &Dome::azimuthPositionChanged); connect(currentDome, &ISD::Dome::ready, this, &Dome::ready); connect(currentDome, &ISD::Dome::Disconnected, this, &Dome::disconnected); } -//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(); } +bool Dome::isRolloffRoof() +{ + // a rolloff roof is a dome that can move neither absolutely nor relatively + return (currentDome && !currentDome->canAbsMove() && !currentDome->canRelMove()); +} + bool Dome::canAbsoluteMove() { if (currentDome) return currentDome->canAbsMove(); return false; } bool Dome::canRelativeMove() { if (currentDome) return currentDome->canRelMove(); return false; } double Dome::azimuthPosition() { if (currentDome) return currentDome->azimuthPosition(); return -1; } void Dome::setAzimuthPosition(double position) { if (currentDome) currentDome->setAzimuthPosition(position); } void Dome::setRelativePosition(double position) { if (currentDome) currentDome->setRelativePosition(position); } +bool Dome::moveDome(bool moveCW, bool start) +{ + if (currentDome == nullptr) + return false; + + return currentDome->moveDome(moveCW ? ISD::Dome::DOME_CW : ISD::Dome::DOME_CCW, + start ? ISD::Dome::MOTION_START : ISD::Dome::MOTION_STOP); +} + bool Dome::isAutoSync() { if (currentDome) return currentDome->isAutoSync(); // value could not be determined return false; } bool Dome::setAutoSync(bool activate) { if (currentDome) return currentDome->setAutoSync(activate); // not succeeded return false; } bool Dome::hasShutter() { if (currentDome) return currentDome->hasShutter(); // no dome, no shutter return false; } bool Dome::controlShutter(bool open) { if (currentDome) return currentDome->ControlShutter(open); // no dome, no shutter control return false; } -#if 0 -Dome::ParkingStatus Dome::getParkingStatus() +void Dome::removeDevice(ISD::GDInterface *device) { - 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) + device->disconnect(this); + if (device == currentDome) { - case IPS_IDLE: - return PARKING_IDLE; - - case IPS_OK: - if (parkSP->sp[0].s == ISS_ON) - return PARKING_OK; - else - return UNPARKING_OK; - - case IPS_BUSY: - if (parkSP->sp[0].s == ISS_ON) - return PARKING_BUSY; - else - return UNPARKING_BUSY; - - case IPS_ALERT: - return PARKING_ERROR; + currentDome = nullptr; } - - return PARKING_ERROR; } -#endif -void Dome::removeDevice(ISD::GDInterface *device) +void Dome::setStatus(ISD::Dome::Status status) { - device->disconnect(this); - if (device == currentDome) + // special case for rolloff roofs. + if (isRolloffRoof()) { - currentDome = nullptr; + // if a parked rollof roof starts to move, its state changes to unparking + if (status == ISD::Dome::DOME_MOVING_CW && (m_ParkStatus == ISD::PARK_PARKED || m_ParkStatus == ISD::PARK_PARKING)) + { + m_ParkStatus = ISD::PARK_UNPARKING; + emit newParkStatus(m_ParkStatus); + } + // if a unparked rollof roof starts to move, its state changes to parking + else if (status == ISD::Dome::DOME_MOVING_CCW && (m_ParkStatus == ISD::PARK_UNPARKED || m_ParkStatus == ISD::PARK_UNPARKING)) + { + m_ParkStatus = ISD::PARK_PARKING; + emit newParkStatus(m_ParkStatus); + } } + // in all other cases, do nothing } } diff --git a/kstars/ekos/auxiliary/dome.h b/kstars/ekos/auxiliary/dome.h index bc746c920..85c43e7c6 100644 --- a/kstars/ekos/auxiliary/dome.h +++ b/kstars/ekos/auxiliary/dome.h @@ -1,153 +1,158 @@ /* 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. */ #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 : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Dome") Q_PROPERTY(ISD::Dome::Status status READ status NOTIFY newStatus) Q_PROPERTY(ISD::ParkStatus parkStatus READ parkStatus NOTIFY newParkStatus) Q_PROPERTY(bool canPark READ canPark) Q_PROPERTY(bool canAbsoluteMove READ canAbsoluteMove) Q_PROPERTY(bool canRelativeMove READ canRelativeMove) Q_PROPERTY(bool isMoving READ isMoving) Q_PROPERTY(double azimuthPosition READ azimuthPosition WRITE setAzimuthPosition NOTIFY azimuthPositionChanged) public: Dome(); virtual ~Dome() override = default; /** * @defgroup DomeDBusInterface Ekos DBus Interface - Dome Interface * Ekos::Dome interface provides advanced basic dome operations. */ /*@{*/ /** * DBUS interface function. * Abort dome */ Q_SCRIPTABLE bool abort(); /** * DBUS interface function. * Can dome park? */ Q_SCRIPTABLE bool canPark(); /** * DBUS interface function. * Can dome move to an absolute azimuth position? */ Q_SCRIPTABLE bool canAbsoluteMove(); /** * DBUS interface function. * Can dome move to an relative azimuth position? */ Q_SCRIPTABLE bool canRelativeMove(); /** * DBUS interface function. * Park dome */ Q_SCRIPTABLE bool park(); /** * DBUS interface function. * Park dome */ Q_SCRIPTABLE bool unpark(); /** * DBUS interface function. * Get the dome park status */ //Q_SCRIPTABLE ParkingStatus getParkingStatus(); /** * DBUS interface function. * Check if the dome is in motion */ Q_SCRIPTABLE bool isMoving(); + Q_SCRIPTABLE bool isRolloffRoof(); Q_SCRIPTABLE double azimuthPosition(); Q_SCRIPTABLE void setAzimuthPosition(double position); Q_SCRIPTABLE void setRelativePosition(double position); + Q_SCRIPTABLE bool moveDome(bool moveCW, bool start); + + Q_SCRIPTABLE bool isAutoSync(); Q_SCRIPTABLE bool setAutoSync(bool activate); Q_SCRIPTABLE bool hasShutter(); Q_SCRIPTABLE bool controlShutter(bool open); /** @}*/ /** * @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); void removeDevice(ISD::GDInterface *device); ISD::Dome::Status status() { return currentDome->status(); } ISD::Dome::ShutterStatus shutterStatus() { return currentDome->shutterStatus(); } ISD::ParkStatus parkStatus() { return m_ParkStatus; } signals: void newStatus(ISD::Dome::Status status); void newParkStatus(ISD::ParkStatus status); void newShutterStatus(ISD::Dome::ShutterStatus status); void newAutoSyncStatus(bool enabled); void azimuthPositionChanged(double position); void ready(); // Signal when the underlying ISD::Dome signals a Disconnected() void disconnected(); private: // Devices needed for Dome operation ISD::Dome *currentDome { nullptr }; ISD::ParkStatus m_ParkStatus { ISD::PARK_UNKNOWN }; ISD::Dome::ShutterStatus m_ShutterStatus { ISD::Dome::SHUTTER_UNKNOWN }; + void setStatus(ISD::Dome::Status status); }; } diff --git a/kstars/ekos/auxiliary/opslogs.ui b/kstars/ekos/auxiliary/opslogs.ui index d1ff68827..968b90b53 100644 --- a/kstars/ekos/auxiliary/opslogs.ui +++ b/kstars/ekos/auxiliary/opslogs.ui @@ -1,507 +1,520 @@ OpsLogs 0 0 - 454 - 309 + 501 + 400 3 3 3 3 3 48 48 48 48 :/icons/AlignWarning.svg true <html><head/><body><p>Enable Logs to diagnose issues with Ekos and INDI. Only enable the necessary logs to help in diagnosing specific problems. Logs consume resources and may slow the system down. Turn off logs when no longer used.</p></body></html> true Logs - - - - Output: - - - - - + + - Log Ekos Guide module activity + Disable all logging output - Guide + &Disable + + + false - modulesGroup + verbosityButtonGroup - - + + + + Qt::Horizontal + + + + + - Auxiliary + Weather driversGroup - - - - Log Ekos Alignment module activity + + + + Verbosity: + + + + - Alignment + Focuser - modulesGroup + driversGroup - - + + - Log output to log file + Log Ekos Scheduler module activity - File + Scheduler + + modulesGroup + - - + + - Log Ekos Mount module activity + Log INDI devices activity - Mount + INDI modulesGroup - - - - Have problem with Ekos? Turn on logging for Ekos modules that exhibit issues. - + + - Ekos + Mount + + driversGroup + - - + + - Log debug messages to default output device used by the platform (e.g. Standard Error) + Log Ekos Focus module activity - Defaul&t - - - false + Focus + + modulesGroup + - - + + - Filter Wheel + Rotator driversGroup - - - - Qt::Horizontal - - - - - + + - Disable all logging output + Enable verbose debug output - &Disable - - - false + &Verbose verbosityButtonGroup - - + + - Enable verbose debug output + Log Ekos Guide module activity - &Verbose + Guide - verbosityButtonGroup + modulesGroup - - - - Qt::Horizontal + + + + CCD + + driversGroup + - - + + - Mount + GPS driversGroup + + + + Output: + + + + + + + Have problems with INDI drivers? Turn on logging for drivers that exhibit issues. + + + Drivers + + + Enable regular debug output Reg&ular true verbosityButtonGroup - - - - Verbosity: + + + + Log FITS processing activity - - - - - Weather + FITS - driversGroup + modulesGroup - - + + - Have problems with INDI drivers? Turn on logging for drivers that exhibit issues. + Log Ekos Alignment module activity - Drivers + Alignment + + modulesGroup + - - + + - Focuser + Dome driversGroup - - + + - Log INDI devices activity - - - INDI + Adaptive Optics - - modulesGroup - - - - - - Dome + AO driversGroup - + Detector driversGroup Log Ekos Capture module activity Capture modulesGroup - - + + - Log Ekos Focus module activity + Have problem with Ekos? Turn on logging for Ekos modules that exhibit issues. - Focus + Ekos - - modulesGroup - - - + + - Log Ekos Scheduler module activity + Log debug messages to default output device used by the platform (e.g. Standard Error) - Scheduler + Defaul&t + + + false - - modulesGroup - - - + + - Log FITS processing activity + Log Ekos Mount module activity - FITS + Mount modulesGroup - - + + - CCD + Filter Wheel driversGroup - - + + + + Qt::Horizontal + + + + + - GPS + Auxiliary driversGroup - - + + + + Log output to log file + - Rotator + File - - driversGroup - - - + + - Adaptive Optics + <html><head/><body><p>Log Ekos Observatory module activity</p></body></html> - AO + Observatory - driversGroup + modulesGroup Display INDI status messages in the status bar Display incoming INDI status messages in KStars status bar Display incoming INDI status messages in KStars status bar INDI messages in status &bar true Qt::Horizontal 40 20 font-weight:bold; Open Logs Directory 22 22 22 22 Clear all logs .. 22 22 false - false + diff --git a/kstars/ekos/observatory/observatory.cpp b/kstars/ekos/observatory/observatory.cpp index e4338992d..3b44093de 100644 --- a/kstars/ekos/observatory/observatory.cpp +++ b/kstars/ekos/observatory/observatory.cpp @@ -1,470 +1,611 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger 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 "observatory.h" #include "ekos_observatory_debug.h" namespace Ekos { Observatory::Observatory() { setupUi(this); // status control mObservatoryModel = new ObservatoryModel(); setObseratoryStatusControl(mObservatoryModel->statusControl()); // update UI for status control connect(useDomeCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(useShutterCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(useWeatherCB, &QCheckBox::clicked, this, &Ekos::Observatory::statusControlSettingsChanged); connect(mObservatoryModel, &Ekos::ObservatoryModel::newStatus, this, &Ekos::Observatory::observatoryStatusChanged); // ready button deactivated // connect(statusReadyButton, &QPushButton::clicked, mObservatoryModel, &Ekos::ObservatoryModel::makeReady); statusReadyButton->setEnabled(false); setDomeModel(new ObservatoryDomeModel()); setWeatherModel(new ObservatoryWeatherModel()); statusDefinitionBox->setVisible(true); statusDefinitionBox->setEnabled(true); // make invisible, since not implemented yet weatherWarningSchedulerCB->setVisible(false); weatherAlertSchedulerCB->setVisible(false); - motionCWButton->setVisible(false); - motionCCWButton->setVisible(false); } void Observatory::setObseratoryStatusControl(ObservatoryStatusControl control) { if (mObservatoryModel != nullptr) { useDomeCB->setChecked(control.useDome); useShutterCB->setChecked(control.useShutter); useWeatherCB->setChecked(control.useWeather); } } void Observatory::setDomeModel(ObservatoryDomeModel *model) { mObservatoryModel->setDomeModel(model); if (model != nullptr) { connect(model, &Ekos::ObservatoryDomeModel::ready, this, &Ekos::Observatory::initDome); connect(model, &Ekos::ObservatoryDomeModel::disconnected, this, &Ekos::Observatory::shutdownDome); connect(model, &Ekos::ObservatoryDomeModel::newStatus, this, &Ekos::Observatory::setDomeStatus); + connect(model, &Ekos::ObservatoryDomeModel::newParkStatus, this, &Ekos::Observatory::setDomeParkStatus); connect(model, &Ekos::ObservatoryDomeModel::newShutterStatus, this, &Ekos::Observatory::setShutterStatus); connect(model, &Ekos::ObservatoryDomeModel::azimuthPositionChanged, this, &Ekos::Observatory::domeAzimuthChanged); connect(model, &Ekos::ObservatoryDomeModel::newAutoSyncStatus, this, &Ekos::Observatory::showAutoSync); // motion controls connect(motionMoveAbsButton, &QCheckBox::clicked, [this]() { mObservatoryModel->getDomeModel()->setAzimuthPosition(absoluteMotionSB->value()); }); connect(motionMoveRelButton, &QCheckBox::clicked, [this]() { mObservatoryModel->getDomeModel()->setRelativePosition(relativeMotionSB->value()); }); // abort button connect(motionAbortButton, &QPushButton::clicked, model, &ObservatoryDomeModel::abort); // weather controls connect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); connect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherAlertSettingsChanged(); }); } else { shutdownDome(); disconnect(weatherWarningShutterCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); disconnect(weatherWarningDomeCB, &QCheckBox::clicked, this, &Observatory::weatherWarningSettingsChanged); connect(weatherWarningDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); disconnect(weatherAlertShutterCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); disconnect(weatherAlertDomeCB, &QCheckBox::clicked, this, &Observatory::weatherAlertSettingsChanged); connect(weatherAlertDelaySB, static_cast(&QSpinBox::valueChanged), [this](int i) { Q_UNUSED(i); weatherWarningSettingsChanged(); }); } } void Observatory::initDome() { domeBox->setEnabled(true); if (getDomeModel() != nullptr) { connect(getDomeModel(), &Ekos::ObservatoryDomeModel::newLog, this, &Ekos::Observatory::appendLogText); + // dome motion buttons + connect(motionCWButton, &QPushButton::clicked, [=](bool checked) {getDomeModel()->moveDome(true, checked);}); + connect(motionCCWButton, &QPushButton::clicked, [=](bool checked) {getDomeModel()->moveDome(false, checked);}); + if (getDomeModel()->canPark()) { connect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); connect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); domePark->setEnabled(true); domeUnpark->setEnabled(true); } else { domePark->setEnabled(false); domeUnpark->setEnabled(false); } - // initialize the dome motion controls - domeAzimuthChanged(getDomeModel()->azimuthPosition()); + if (getDomeModel()->isRolloffRoof()) + { + SlavingBox->setVisible(false); + domeAzimuthPosition->setText("N/A"); + enableMotionControl(true); + } + else + { + // initialize the dome motion controls + domeAzimuthChanged(getDomeModel()->azimuthPosition()); + + // slaving + showAutoSync(getDomeModel()->isAutoSync()); + connect(slavingEnableButton, &QPushButton::clicked, this, [this]() + { + enableAutoSync(true); + }); + connect(slavingDisableButton, &QPushButton::clicked, this, [this]() + { + enableAutoSync(false); + }); + } + // shutter handling if (getDomeModel()->hasShutter()) { shutterBox->setVisible(true); shutterBox->setEnabled(true); connect(shutterOpen, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::openShutter); connect(shutterClosed, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::closeShutter); shutterClosed->setEnabled(true); shutterOpen->setEnabled(true); + setShutterStatus(getDomeModel()->shutterStatus()); + useShutterCB->setVisible(true); } else { shutterBox->setVisible(false); weatherWarningShutterCB->setVisible(false); weatherAlertShutterCB->setVisible(false); + useShutterCB->setVisible(false); } + // abort button should always be available motionAbortButton->setEnabled(true); - - // slaving - connect(slavingEnableButton, &QPushButton::clicked, this, [this]() - { - enableAutoSync(true); - }); - connect(slavingDisableButton, &QPushButton::clicked, this, [this]() - { - enableAutoSync(false); - }); - - + // update the dome status setDomeStatus(getDomeModel()->status()); - setShutterStatus(getDomeModel()->shutterStatus()); - - enableAutoSync(getDomeModel()->isAutoSync()); + setDomeParkStatus(getDomeModel()->parkStatus()); } } void Observatory::shutdownDome() { domeBox->setEnabled(false); shutterBox->setEnabled(false); shutterBox->setVisible(false); domePark->setEnabled(false); domeUnpark->setEnabled(false); shutterClosed->setEnabled(false); shutterOpen->setEnabled(false); disconnect(domePark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::park); disconnect(domeUnpark, &QPushButton::clicked, getDomeModel(), &Ekos::ObservatoryDomeModel::unpark); } void Observatory::setDomeStatus(ISD::Dome::Status status) { + qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting dome status to " << status; + switch (status) { case ISD::Dome::DOME_ERROR: break; case ISD::Dome::DOME_IDLE: - domePark->setChecked(false); - domePark->setText(i18n("Park")); - domeUnpark->setChecked(true); - domeUnpark->setText(i18n("UnParked")); - enableMotionControl(true); + motionCWButton->setChecked(false); + motionCWButton->setEnabled(true); + motionCCWButton->setChecked(false); + motionCCWButton->setEnabled(true); + appendLogText(i18n("Dome is idle.")); break; - case ISD::Dome::DOME_MOVING: - enableMotionControl(false); - appendLogText(i18n("Dome is moving...")); + + case ISD::Dome::DOME_MOVING_CW: + motionCWButton->setChecked(true); + motionCCWButton->setEnabled(true); + motionCCWButton->setChecked(false); + if (getDomeModel()->isRolloffRoof()) + { + domeAzimuthPosition->setText(i18n("Opening")); + toggleButtons(domeUnpark, i18n("Unparking"), domePark, i18n("Park")); + appendLogText(i18n("Dome is opening...")); + } + else + { + appendLogText(i18n("Dome is moving clockwise...")); + } break; + + case ISD::Dome::DOME_MOVING_CCW: + motionCWButton->setChecked(false); + motionCWButton->setEnabled(true); + motionCCWButton->setChecked(true); + if (getDomeModel()->isRolloffRoof()) + { + domeAzimuthPosition->setText(i18n("Closing")); + toggleButtons(domePark, i18n("Parking"), domeUnpark, i18n("Unpark")); + appendLogText(i18n("Dome is closing...")); + } + else + { + appendLogText(i18n("Dome is moving counter clockwise...")); + } + break; + case ISD::Dome::DOME_PARKED: - domePark->setChecked(true); - domePark->setText(i18n("Parked")); - domeUnpark->setChecked(false); - domeUnpark->setText(i18n("UnPark")); - enableMotionControl(false); + setDomeParkStatus(ISD::PARK_PARKED); + appendLogText(i18n("Dome is parked.")); break; + case ISD::Dome::DOME_PARKING: - domePark->setText(i18n("Parking")); - domeUnpark->setText(i18n("UnPark")); - enableMotionControl(false); + toggleButtons(domePark, i18n("Parking"), domeUnpark, i18n("Unpark")); + motionCWButton->setEnabled(true); + + if (getDomeModel()->isRolloffRoof()) + domeAzimuthPosition->setText(i18n("Closing")); + else + enableMotionControl(false); + + motionCWButton->setChecked(false); + motionCCWButton->setChecked(true); + appendLogText(i18n("Dome is parking...")); break; + case ISD::Dome::DOME_UNPARKING: - domePark->setText(i18n("Park")); - domeUnpark->setText(i18n("UnParking")); - enableMotionControl(false); + toggleButtons(domeUnpark, i18n("Unparking"), domePark, i18n("Park")); + motionCCWButton->setEnabled(true); + + if (getDomeModel()->isRolloffRoof()) + domeAzimuthPosition->setText(i18n("Opening")); + else + enableMotionControl(false); + + motionCWButton->setChecked(true); + motionCCWButton->setChecked(false); + appendLogText(i18n("Dome is unparking...")); break; + case ISD::Dome::DOME_TRACKING: enableMotionControl(true); + motionCWButton->setEnabled(true); + motionCCWButton->setChecked(true); appendLogText(i18n("Dome is tracking.")); break; } } +void Observatory::setDomeParkStatus(ISD::ParkStatus status) +{ + qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting dome park status to " << status; + switch (status) + { + case ISD::PARK_UNPARKED: + activateButton(domePark, i18n("Park")); + buttonPressed(domeUnpark, i18n("Unparked")); + motionCWButton->setChecked(false); + motionCWButton->setEnabled(false); + motionCCWButton->setChecked(false); + + if (getDomeModel()->isRolloffRoof()) + domeAzimuthPosition->setText(i18n("Open")); + else + enableMotionControl(true); + break; + + case ISD::PARK_PARKED: + buttonPressed(domePark, i18n("Parked")); + activateButton(domeUnpark, i18n("Unpark")); + motionCWButton->setChecked(false); + motionCCWButton->setChecked(false); + motionCCWButton->setEnabled(false); + + if (getDomeModel()->isRolloffRoof()) + domeAzimuthPosition->setText(i18n("Closed")); + else + enableMotionControl(false); + break; + + default: + break; + } +} + void Observatory::setShutterStatus(ISD::Dome::ShutterStatus status) { + qCDebug(KSTARS_EKOS_OBSERVATORY) << "Setting shutter status to " << status; + switch (status) { case ISD::Dome::SHUTTER_OPEN: - shutterOpen->setChecked(true); - shutterClosed->setChecked(false); - shutterOpen->setText(i18n("Opened")); - shutterClosed->setText(i18n("Close")); + buttonPressed(shutterOpen, i18n("Opened")); + activateButton(shutterClosed, i18n("Close")); appendLogText(i18n("Shutter is open.")); break; + case ISD::Dome::SHUTTER_OPENING: - shutterOpen->setText(i18n("Opening")); - shutterClosed->setText(i18n("Closed")); + toggleButtons(shutterOpen, i18n("Opening"), shutterClosed, i18n("Close")); appendLogText(i18n("Shutter is opening...")); break; + case ISD::Dome::SHUTTER_CLOSED: - shutterOpen->setChecked(false); - shutterClosed->setChecked(true); - shutterOpen->setText(i18n("Open")); - shutterClosed->setText(i18n("Closed")); + buttonPressed(shutterClosed, i18n("Closed")); + activateButton(shutterOpen, i18n("Open")); appendLogText(i18n("Shutter is closed.")); break; case ISD::Dome::SHUTTER_CLOSING: - shutterOpen->setText(i18n("Opened")); - shutterClosed->setText(i18n("Closing")); + toggleButtons(shutterClosed, i18n("Closing"), shutterOpen, i18n("Open")); appendLogText(i18n("Shutter is closing...")); break; default: break; } } void Observatory::setWeatherModel(ObservatoryWeatherModel *model) { mObservatoryModel->setWeatherModel(model); if (model != nullptr) { connect(weatherWarningBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setWarningActionsActive); connect(weatherAlertBox, &QGroupBox::clicked, model, &ObservatoryWeatherModel::setAlertActionsActive); connect(model, &Ekos::ObservatoryWeatherModel::ready, this, &Ekos::Observatory::initWeather); connect(model, &Ekos::ObservatoryWeatherModel::newStatus, this, &Ekos::Observatory::setWeatherStatus); connect(model, &Ekos::ObservatoryWeatherModel::disconnected, this, &Ekos::Observatory::shutdownWeather); connect(&weatherStatusTimer, &QTimer::timeout, [this]() { weatherWarningStatusLabel->setText(getWeatherModel()->getWarningActionsStatus()); weatherAlertStatusLabel->setText(getWeatherModel()->getAlertActionsStatus()); }); } else shutdownWeather(); } void Observatory::enableMotionControl(bool enabled) { MotionBox->setEnabled(enabled); // absolute motion controls if (getDomeModel()->canAbsoluteMove()) { motionMoveAbsButton->setEnabled(enabled); absoluteMotionSB->setEnabled(enabled); } else { motionMoveAbsButton->setEnabled(false); absoluteMotionSB->setEnabled(false); } // relative motion controls if (getDomeModel()->canRelativeMove()) { motionMoveRelButton->setEnabled(enabled); relativeMotionSB->setEnabled(enabled); + motionCWButton->setEnabled(enabled); + motionCCWButton->setEnabled(enabled); } else { motionMoveRelButton->setEnabled(false); relativeMotionSB->setEnabled(false); + motionCWButton->setEnabled(false); + motionCCWButton->setEnabled(false); } - + // special case for rolloff roofs + if (getDomeModel()->isRolloffRoof()) + { + motionCWButton->setText(i18n("Open")); + motionCCWButton->setText(i18n("Close")); + motionCWButton->setEnabled(enabled); + motionCCWButton->setEnabled(enabled); + motionMoveAbsButton->setVisible(false); + motionMoveRelButton->setVisible(false); + absoluteMotionSB->setVisible(false); + relativeMotionSB->setVisible(false); + } } void Observatory::enableAutoSync(bool enabled) { if (getDomeModel() == nullptr) showAutoSync(false); else { getDomeModel()->setAutoSync(enabled); showAutoSync(enabled); } } void Observatory::showAutoSync(bool enabled) { slavingEnableButton->setChecked(enabled); slavingDisableButton->setChecked(! enabled); } void Observatory::initWeather() { weatherBox->setEnabled(true); weatherLabel->setEnabled(true); weatherActionsBox->setVisible(true); weatherActionsBox->setEnabled(true); weatherWarningBox->setChecked(getWeatherModel()->getWarningActionsActive()); weatherAlertBox->setChecked(getWeatherModel()->getAlertActionsActive()); setWeatherStatus(getWeatherModel()->status()); setWarningActions(getWeatherModel()->getWarningActions()); setAlertActions(getWeatherModel()->getAlertActions()); weatherStatusTimer.start(1000); } void Observatory::shutdownWeather() { weatherBox->setEnabled(false); weatherLabel->setEnabled(false); setWeatherStatus(ISD::Weather::WEATHER_IDLE); weatherStatusTimer.stop(); } void Observatory::setWeatherStatus(ISD::Weather::Status status) { std::string label; switch (status) { case ISD::Weather::WEATHER_OK: label = "security-high"; appendLogText(i18n("Weather is OK")); break; case ISD::Weather::WEATHER_WARNING: label = "security-medium"; appendLogText(i18n("Weather Warning")); break; case ISD::Weather::WEATHER_ALERT: label = "security-low"; appendLogText(i18n("Weather Alert")); break; default: label = ""; break; } weatherStatusLabel->setPixmap(QIcon::fromTheme(label.c_str()).pixmap(QSize(48, 48))); } void Observatory::weatherWarningSettingsChanged() { struct WeatherActions actions; actions.parkDome = weatherWarningDomeCB->isChecked(); actions.closeShutter = weatherWarningShutterCB->isChecked(); actions.delay = weatherWarningDelaySB->value(); getWeatherModel()->setWarningActions(actions); } void Observatory::weatherAlertSettingsChanged() { struct WeatherActions actions; actions.parkDome = weatherAlertDomeCB->isChecked(); actions.closeShutter = weatherAlertShutterCB->isChecked(); actions.delay = weatherAlertDelaySB->value(); getWeatherModel()->setAlertActions(actions); } void Observatory::observatoryStatusChanged(bool ready) { // statusReadyButton->setEnabled(!ready); statusReadyButton->setChecked(ready); emit newStatus(ready); } void Observatory::domeAzimuthChanged(double position) { domeAzimuthPosition->setText(QString::number(position, 'f', 2)); } void Observatory::setWarningActions(WeatherActions actions) { weatherWarningDomeCB->setChecked(actions.parkDome); weatherWarningShutterCB->setChecked(actions.closeShutter); weatherWarningDelaySB->setValue(actions.delay); } void Observatory::setAlertActions(WeatherActions actions) { weatherAlertDomeCB->setChecked(actions.parkDome); weatherAlertShutterCB->setChecked(actions.closeShutter); weatherAlertDelaySB->setValue(actions.delay); } +void Observatory::toggleButtons(QPushButton *buttonPressed, QString titlePressed, QPushButton *buttonCounterpart, QString titleCounterpart) +{ + buttonPressed->setEnabled(false); + buttonPressed->setText(titlePressed); + + buttonCounterpart->setEnabled(true); + buttonCounterpart->setChecked(false); + buttonCounterpart->setCheckable(false); + buttonCounterpart->setText(titleCounterpart); +} + +void Observatory::activateButton(QPushButton *button, QString title) +{ + button->setEnabled(true); + button->setCheckable(false); + button->setText(title); +} + +void Observatory::buttonPressed(QPushButton *button, QString title) +{ + button->setEnabled(false); + button->setCheckable(true); + button->setChecked(true); + button->setText(title); + +} + + void Observatory::statusControlSettingsChanged() { ObservatoryStatusControl control; control.useDome = useDomeCB->isChecked(); control.useShutter = useShutterCB->isChecked(); control.useWeather = useWeatherCB->isChecked(); mObservatoryModel->setStatusControl(control); } void Observatory::appendLogText(const QString &text) { m_LogText.insert(0, 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_OBSERVATORY) << text; emit newLog(text); } void Observatory::clearLog() { m_LogText.clear(); emit newLog(QString()); } } diff --git a/kstars/ekos/observatory/observatory.h b/kstars/ekos/observatory/observatory.h index 9de475800..35e67d394 100644 --- a/kstars/ekos/observatory/observatory.h +++ b/kstars/ekos/observatory/observatory.h @@ -1,108 +1,114 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger 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_observatory.h" #include "observatorymodel.h" #include "observatorydomemodel.h" #include "observatoryweathermodel.h" #include #include namespace Ekos { class Observatory : public QWidget, public Ui::Observatory { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars.Ekos.Observatory") Q_PROPERTY(QStringList logText READ logText NOTIFY newLog) public: Observatory(); ObservatoryDomeModel *getDomeModel() { return mObservatoryModel->getDomeModel(); } ObservatoryWeatherModel *getWeatherModel() { return mObservatoryModel->getWeatherModel(); } // Logging QStringList logText() { return m_LogText; } QString getLogText() { return m_LogText.join("\n"); } void clearLog(); signals: Q_SCRIPTABLE void newLog(const QString &text); /** * @brief Signal a new observatory status */ Q_SCRIPTABLE void newStatus(bool isReady); private: ObservatoryModel *mObservatoryModel = nullptr; void setDomeModel(ObservatoryDomeModel *model); void setWeatherModel(ObservatoryWeatherModel *model); // motion control void enableMotionControl(bool enabled); // slaving control void enableAutoSync(bool enabled); void showAutoSync(bool enabled); // Logging QStringList m_LogText; void appendLogText(const QString &); // timer for refreshing the observatory status QTimer weatherStatusTimer; // reacting on weather changes void setWarningActions(WeatherActions actions); void setAlertActions(WeatherActions actions); + // button handling + void toggleButtons(QPushButton *buttonPressed, QString titlePressed, QPushButton *buttonCounterpart, QString titleCounterpart); + void activateButton(QPushButton *button, QString title); + void buttonPressed(QPushButton *button, QString title); + private slots: // observatory status handling void setObseratoryStatusControl(ObservatoryStatusControl control); void statusControlSettingsChanged(); void initWeather(); void shutdownWeather(); void setWeatherStatus(ISD::Weather::Status status); // reacting on weather changes void weatherWarningSettingsChanged(); void weatherAlertSettingsChanged(); // reacting on observatory status changes void observatoryStatusChanged(bool ready); void domeAzimuthChanged(double position); void initDome(); void shutdownDome(); void setDomeStatus(ISD::Dome::Status status); + void setDomeParkStatus(ISD::ParkStatus status); void setShutterStatus(ISD::Dome::ShutterStatus status); }; } diff --git a/kstars/ekos/observatory/observatory.ui b/kstars/ekos/observatory/observatory.ui index 11560d3d9..ae283f5c2 100644 --- a/kstars/ekos/observatory/observatory.ui +++ b/kstars/ekos/observatory/observatory.ui @@ -1,1043 +1,1089 @@ Observatory 0 0 873 494 Observatory Status of the Observatory 3 3 3 3 3 3 3 false Dome - + 3 3 3 3 - + - - - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - <html><head/><body><p>Park the dome. For advanced control of the dome please use the INDI tab.</p></body></html> - - - QPushButton:checked + + + + + 160 + 0 + + + + + 320 + 16777215 + + + + + 24 + 75 + true + + + + 0 + + + Qt::AlignCenter + + + + + + + Position + + + + + + + + + + + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + false + + + Motion + + + + + + + false + + + Absolute position the dome should move. + + + 999.990000000000009 + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Move the dome to the given absolute position. + + + Move (abs) + + + + + + + false + + + Relative position the dome should move. + + + -999.990000000000009 + + + 999.990000000000009 + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Move the dome for the given degrees and direction. + + + QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - - - Park - - - - 32 - 16 - - - - true - - - - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - <html><head/><body><p>Unpark the dome. For advanced control of the dome please use the INDI tab.</p></body></html> - - - QPushButton:checked + + + Move (rel) + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Rotate clockwise + + + QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - - - UnPark - - - true - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - Abort dome motion - - - QPushButton:checked + + + &CW + + + true + + + + + + + false + + + + 96 + 36 + + + + + 96 + 36 + + + + Rotate counter clockwise + + + QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - - - Abort - - - - + + + CCW + + + true + + + + + - - - - - - false - - - Relative position the dome should move. - - - -999.990000000000009 - - - 999.990000000000009 - - - - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - Move the dome for the given degrees and direction. - - - QPushButton:checked + + + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + Slaving + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Enable slaving, dome motion <span style=" font-weight:600;">follows telescope motion</span></p></body></html> + + + QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - - - Move (rel) - - - - - - - false - - - Absolute position the dome should move. - - - 999.990000000000009 - - - - - - - false - - - - 96 - 36 - - - - - 96 - 36 - - - - Move the dome to the given absolute position. - - - Move (abs) - - - - - - - + + + Enable + + + true + + + + + + + + 96 + 36 + + + + + 96 + 36 + + + + <html><head/><body><p>Disable slaving, dome <span style=" font-weight:600;">does not follow telescope motion</span>.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} + + + Disable + + + true + + + + + + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + false + - 160 - 0 + 96 + 36 - 320 - 16777215 + 96 + 36 - - - 24 - 75 - true - + + <html><head/><body><p>Park the dome. For advanced control of the dome please use the INDI tab.</p></body></html> + + + QPushButton:checked +{ +background-color: maroon; +border: 1px outset; +font-weight:bold; +} - 0 + Park - - Qt::AlignCenter + + + 32 + 16 + + + + false - - + + false 96 36 96 36 - Rotate counter clockwise + <html><head/><body><p>Unpark the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - CCW + UnPark - - - - - + false - - Motion: + + false - - - - + + + + Qt::Horizontal - - - - - - Position + + + 40 + 20 + - + - - + + false 96 36 96 36 - Rotate clockwise + Abort dome motion QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } - &CW + Abort - - - - - - Slaving - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 96 - 36 - - - - - 96 - 36 - - - - <html><head/><body><p>Enable slaving, dome motion <span style=" font-weight:600;">follows telescope motion</span></p></body></html> - - - QPushButton:checked -{ -background-color: maroon; -border: 1px outset; -font-weight:bold; -} - - - Enable - - - true - - - - - - - - 96 - 36 - - - - - 96 - 36 - - - - <html><head/><body><p>Disable slaving, dome <span style=" font-weight:600;">does not follow telescope motion</span>.</p></body></html> - - - QPushButton:checked -{ -background-color: maroon; -border: 1px outset; -font-weight:bold; -} - - - Disable - - - true - - - - - false Shutter 3 3 3 3 3 Qt::Horizontal 40 20 false 96 36 72 36 <html><head/><body><p>Close the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Close 32 16 - true + false false 96 36 72 36 <html><head/><body><p>Open the shutter of the dome. For advanced control of the dome please use the INDI tab.</p></body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Open - true + false false false Observatory Status 3 3 3 3 3 <html><head/><body><p>If selected, the dome needs to be unparked for the observatory status being &quot;READY&quot;.</p></body></html> Dome <html><head/><body><p>If selected, the shutter needs to be open for the observatory status being &quot;READY&quot;.</p></body></html> Shutter <html><head/><body><p>If selected, the weather needs to be OK for the observatory status being &quot;READY&quot;.</p></body></html> Weather false 96 36 72 36 <html><head/><body><p>Observatory status. Select the observatory elements that are relevant for the status:</p> <ul> <li><b>Dome</b>: unparked &rarr; ready</li> <li><b>Shutter</b>: open &rarr; ready</li> <li><b>Weather</b>: OK &rarr; ready</li> </ul> </body></html> QPushButton:checked { background-color: maroon; border: 1px outset; font-weight:bold; } Ready 32 16 true false false Weather 3 3 3 3 3 false Actions 3 3 3 3 3 War&ning true 3 0 3 0 3 Park Dome Close Shutter Stop Scheduler <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> Qt::AutoText Qt::Horizontal 40 20 - Delay (sec): + Delay (se&c): Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 9999 Ale&rt true 3 3 3 0 3 Close Shutter Park Dome Stop Scheduler <html><head/><body><p><span style=" font-style:italic;">Status: inactive</span></p></body></html> Qt::AutoText Qt::Horizontal 40 20 - Delay (sec): + De&lay (sec): Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 9999 0 0 48 48 48 48 Qt::Horizontal 40 20 false Display the weather status. The warning and alert limits are set in the INDI tab. Weather Status: Qt::Vertical 20 3 diff --git a/kstars/ekos/observatory/observatorydomemodel.cpp b/kstars/ekos/observatory/observatorydomemodel.cpp index 2ffa932a9..7fea5920d 100644 --- a/kstars/ekos/observatory/observatorydomemodel.cpp +++ b/kstars/ekos/observatory/observatorydomemodel.cpp @@ -1,109 +1,136 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger 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 "observatorydomemodel.h" #include namespace Ekos { void ObservatoryDomeModel::initModel(Dome *dome) { domeInterface = dome; connect(domeInterface, &Dome::ready, this, &ObservatoryDomeModel::ready); connect(domeInterface, &Dome::disconnected, this, &ObservatoryDomeModel::disconnected); connect(domeInterface, &Dome::newStatus, this, &ObservatoryDomeModel::newStatus); + connect(domeInterface, &Dome::newParkStatus, this, &ObservatoryDomeModel::newParkStatus); connect(domeInterface, &Dome::newShutterStatus, this, &ObservatoryDomeModel::newShutterStatus); connect(domeInterface, &Dome::azimuthPositionChanged, this, &ObservatoryDomeModel::azimuthPositionChanged); connect(domeInterface, &Dome::newAutoSyncStatus, this, &ObservatoryDomeModel::newAutoSyncStatus); } ISD::Dome::Status ObservatoryDomeModel::status() { if (domeInterface == nullptr) return ISD::Dome::DOME_IDLE; return domeInterface->status(); } ISD::Dome::ShutterStatus ObservatoryDomeModel::shutterStatus() { if (domeInterface == nullptr) return ISD::Dome::SHUTTER_UNKNOWN; return domeInterface->shutterStatus(); } void ObservatoryDomeModel::park() { if (domeInterface == nullptr) return; emit newLog(i18n("Parking dome...")); domeInterface->park(); } void ObservatoryDomeModel::unpark() { if (domeInterface == nullptr) return; emit newLog(i18n("Unparking dome...")); domeInterface->unpark(); } +ISD::ParkStatus ObservatoryDomeModel::parkStatus() +{ + if (domeInterface == nullptr) + return ISD::PARK_UNKNOWN; + else if (isRolloffRoof()) + { + // we need to override the parking status of the dome interface for opening and closing rolloff roofs + if (domeInterface->status() == ISD::Dome::DOME_MOVING_CW) + return ISD::PARK_UNPARKING; + else if (domeInterface->status() == ISD::Dome::DOME_MOVING_CCW) + return ISD::PARK_PARKING; + } + + // in all other cases use the underlying park status + return domeInterface->parkStatus(); +} + void ObservatoryDomeModel::setAutoSync(bool activate) { if (domeInterface == nullptr) return; if (domeInterface->setAutoSync(activate)) emit newLog(i18n(activate ? "Slaving activated." : "Slaving deactivated.")); } void ObservatoryDomeModel::abort() { if (domeInterface == nullptr) return; emit newLog(i18n("Aborting...")); domeInterface->abort(); } void ObservatoryDomeModel::openShutter() { if (domeInterface == nullptr) return; emit newLog(i18n("Opening shutter...")); domeInterface->controlShutter(true); } void ObservatoryDomeModel::closeShutter() { if (domeInterface == nullptr) return; emit newLog(i18n("Closing shutter...")); domeInterface->controlShutter(false); } +bool ObservatoryDomeModel::moveDome(bool moveCW, bool start) +{ + if (domeInterface == nullptr) + return false; + + emit newLog(i18n("%2 dome motion %1...", moveCW ? "clockwise" : "counter clockwise", start ? "Starting" : "Stopping")); + return domeInterface->moveDome(moveCW, start); +} + void ObservatoryDomeModel::execute(WeatherActions actions) { if (hasShutter() && actions.closeShutter) closeShutter(); if (actions.parkDome) park(); } } diff --git a/kstars/ekos/observatory/observatorydomemodel.h b/kstars/ekos/observatory/observatorydomemodel.h index 77c7e7604..a23f4a384 100644 --- a/kstars/ekos/observatory/observatorydomemodel.h +++ b/kstars/ekos/observatory/observatorydomemodel.h @@ -1,98 +1,107 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger 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 "../auxiliary/dome.h" #include "observatoryweathermodel.h" #include namespace Ekos { class ObservatoryDomeModel: public QObject { Q_OBJECT public: ObservatoryDomeModel() = default; void initModel(Dome *dome); ISD::Dome::Status status(); ISD::Dome::ShutterStatus shutterStatus(); // proxies to the underlying dome object bool canPark() { return (domeInterface != nullptr && domeInterface->canPark()); } void park(); void unpark(); + ISD::ParkStatus parkStatus(); double azimuthPosition() { return domeInterface->azimuthPosition(); } void setAzimuthPosition(double position) { domeInterface->setAzimuthPosition(position); } bool canAbsoluteMove() { return (domeInterface != nullptr && domeInterface->canAbsoluteMove()); } void setRelativePosition(double position) { domeInterface->setRelativePosition(position); } bool canRelativeMove() { return (domeInterface != nullptr && domeInterface->canRelativeMove()); } + bool isRolloffRoof() + { + return (domeInterface != nullptr && domeInterface->isRolloffRoof()); + } + bool isAutoSync() { return (domeInterface != nullptr && domeInterface->isAutoSync()); } void setAutoSync(bool activate); void abort(); bool hasShutter() { return (domeInterface != nullptr && domeInterface->hasShutter()); } void openShutter(); void closeShutter(); + bool moveDome(bool moveCW, bool start); + public slots: void execute(WeatherActions actions); private: Dome *domeInterface; signals: void newStatus(ISD::Dome::Status state); + void newParkStatus(ISD::ParkStatus status); void newShutterStatus(ISD::Dome::ShutterStatus status); void newAutoSyncStatus(bool enabled); void azimuthPositionChanged(double position); void ready(); void disconnected(); void newLog(const QString &text); }; } diff --git a/kstars/ekos/observatory/observatorymodel.cpp b/kstars/ekos/observatory/observatorymodel.cpp index 424b3a2eb..eb022e463 100644 --- a/kstars/ekos/observatory/observatorymodel.cpp +++ b/kstars/ekos/observatory/observatorymodel.cpp @@ -1,113 +1,112 @@ /* Ekos Observatory Module Copyright (C) Wolfgang Reissenberger 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 "observatorymodel.h" #include "Options.h" namespace Ekos { ObservatoryModel::ObservatoryModel() { mStatusControl.useDome = Options::observatoryStatusUseDome(); mStatusControl.useShutter = Options::observatoryStatusUseShutter(); mStatusControl.useWeather = Options::observatoryStatusUseWeather(); setDomeModel(new ObservatoryDomeModel()); setWeatherModel(new ObservatoryWeatherModel()); } void ObservatoryModel::setDomeModel(ObservatoryDomeModel *model) { mDomeModel = model; if (model != nullptr) { connect(mDomeModel, &ObservatoryDomeModel::newStatus, [this](ISD::Dome::Status s) { Q_UNUSED(s); updateStatus(); }); + connect(mDomeModel, &ObservatoryDomeModel::newParkStatus, [this](ISD::ParkStatus s) { Q_UNUSED(s); updateStatus(); }); connect(mDomeModel, &ObservatoryDomeModel::newShutterStatus, [this](ISD::Dome::ShutterStatus s) { Q_UNUSED(s); updateStatus(); }); if (mWeatherModel != nullptr) connect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } else { if (mWeatherModel != nullptr) disconnect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } updateStatus(); } void ObservatoryModel::setWeatherModel(ObservatoryWeatherModel *model) { mWeatherModel = model; if (model != nullptr) { connect(mWeatherModel, &ObservatoryWeatherModel::newStatus, [this](ISD::Weather::Status s) { Q_UNUSED(s); updateStatus(); }); if (mDomeModel != nullptr) { connect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } } else { if (mDomeModel != nullptr) disconnect(mWeatherModel, &ObservatoryWeatherModel::execute, mDomeModel, &ObservatoryDomeModel::execute); } updateStatus(); } void ObservatoryModel::setStatusControl(ObservatoryStatusControl control) { mStatusControl = control; Options::setObservatoryStatusUseDome(control.useDome); Options::setObservatoryStatusUseShutter(control.useShutter); Options::setObservatoryStatusUseWeather(control.useWeather); updateStatus(); } bool ObservatoryModel::isReady() { // dome relevant for the status and dome is ready - if (mStatusControl.useDome && (getDomeModel() == nullptr || - (getDomeModel()->status() != ISD::Dome::DOME_IDLE && - getDomeModel()->status() != ISD::Dome::DOME_TRACKING))) + if (mStatusControl.useDome && (getDomeModel() == nullptr || getDomeModel()->parkStatus() != ISD::PARK_UNPARKED)) return false; // shutter relevant for the status and shutter open if (mStatusControl.useShutter && (getDomeModel() == nullptr || (getDomeModel()->hasShutter() && getDomeModel()->shutterStatus() != ISD::Dome::SHUTTER_OPEN))) return false; // weather relevant for the status and weather is OK if (mStatusControl.useWeather && (getWeatherModel() == nullptr || getWeatherModel()->status() != ISD::Weather::WEATHER_OK)) return false; return true; } void ObservatoryModel::updateStatus() { emit newStatus(isReady()); } void ObservatoryModel::makeReady() { // dome relevant for the status and dome is ready if (mStatusControl.useDome && (getDomeModel() == nullptr || getDomeModel()->status() != ISD::Dome::DOME_IDLE)) getDomeModel()->unpark(); // shutter relevant for the status and shutter open if (mStatusControl.useShutter && (getDomeModel() == nullptr || (getDomeModel()->hasShutter() && getDomeModel()->shutterStatus() != ISD::Dome::SHUTTER_OPEN))) getDomeModel()->openShutter(); // weather relevant for the status and weather is OK // Haha, weather we can't change } } diff --git a/kstars/indi/indidome.cpp b/kstars/indi/indidome.cpp index 57ca869b6..fc4d3d3a9 100644 --- a/kstars/indi/indidome.cpp +++ b/kstars/indi/indidome.cpp @@ -1,541 +1,566 @@ /* INDI Dome 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 #include #include #include #include #include "indidome.h" #include "kstars.h" #include "clientmanager.h" namespace ISD { Dome::Dome(GDInterface *iPtr) : DeviceDecorator(iPtr) { dType = KSTARS_DOME; qRegisterMetaType("ISD::Dome::Status"); qDBusRegisterMetaType(); readyTimer.reset(new QTimer()); readyTimer.get()->setInterval(250); readyTimer.get()->setSingleShot(true); connect(readyTimer.get(), &QTimer::timeout, this, &Dome::ready); } void Dome::registerProperty(INDI::Property *prop) { if (isConnected()) readyTimer.get()->start(); if (!strcmp(prop->getName(), "DOME_PARK")) { ISwitchVectorProperty *svp = prop->getSwitch(); m_CanPark = true; if (svp) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if ((sp->s == ISS_ON) && svp->s == IPS_OK) { m_ParkStatus = PARK_PARKED; m_Status = DOME_PARKED; emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(true); } else if ((sp->s == ISS_OFF) && svp->s == IPS_OK) { m_ParkStatus = PARK_UNPARKED; m_Status = DOME_IDLE; emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(false); } } } } else if (!strcmp(prop->getName(), "ABS_DOME_POSITION")) { m_CanAbsMove = true; } else if (!strcmp(prop->getName(), "REL_DOME_POSITION")) { m_CanRelMove = true; } else if (!strcmp(prop->getName(), "DOME_ABORT_MOTION")) { m_CanAbort = true; } else if (!strcmp(prop->getName(), "DOME_SHUTTER")) { m_HasShutter = true; } DeviceDecorator::registerProperty(prop); } void Dome::processLight(ILightVectorProperty *lvp) { DeviceDecorator::processLight(lvp); } void Dome::processNumber(INumberVectorProperty *nvp) { if (!strcmp(nvp->name, "ABS_DOME_POSITION")) { emit azimuthPositionChanged(nvp->np[0].value); } DeviceDecorator::processNumber(nvp); } void Dome::processSwitch(ISwitchVectorProperty *svp) { if (!strcmp(svp->name, "CONNECTION")) { ISwitch *conSP = IUFindSwitch(svp, "CONNECT"); if (conSP) { if (isConnected() == false && conSP->s == ISS_ON) KStars::Instance()->slotSetDomeEnabled(true); else if (isConnected() && conSP->s == ISS_OFF) { KStars::Instance()->slotSetDomeEnabled(false); m_CanAbsMove = false; m_CanPark = false; } } } else if (!strcmp(svp->name, "DOME_PARK")) { ISwitch *sp = IUFindSwitch(svp, "PARK"); if (sp) { if (svp->s == IPS_ALERT) { emit newParkStatus(PARK_ERROR); // 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 (m_ParkStatus == PARK_PARKING) m_ParkStatus = PARK_UNPARKED; else if (m_ParkStatus == PARK_UNPARKING) m_ParkStatus = PARK_PARKED; emit newParkStatus(m_ParkStatus); } else if (svp->s == IPS_BUSY && sp->s == ISS_ON && m_ParkStatus != PARK_PARKING) { m_ParkStatus = PARK_PARKING; KNotification::event(QLatin1String("DomeParking"), i18n("Dome parking is in progress")); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_PARKING) { m_Status = DOME_PARKING; emit newStatus(m_Status); } } else if (svp->s == IPS_BUSY && sp->s == ISS_OFF && m_ParkStatus != PARK_UNPARKING) { m_ParkStatus = PARK_UNPARKING; KNotification::event(QLatin1String("DomeUnparking"), i18n("Dome unparking is in progress")); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_UNPARKING) { m_Status = DOME_UNPARKING; emit newStatus(m_Status); } } else if (svp->s == IPS_OK && sp->s == ISS_ON && m_ParkStatus != PARK_PARKED) { m_ParkStatus = PARK_PARKED; KNotification::event(QLatin1String("DomeParked"), i18n("Dome parked")); emit newParkStatus(m_ParkStatus); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(false); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(true); if (m_Status != DOME_PARKED) { m_Status = DOME_PARKED; emit newStatus(m_Status); } } else if ( (svp->s == IPS_OK || svp->s == IPS_IDLE) && sp->s == ISS_OFF && m_ParkStatus != PARK_UNPARKED) { m_ParkStatus = PARK_UNPARKED; KNotification::event(QLatin1String("DomeUnparked"), i18n("Dome unparked")); QAction *parkAction = KStars::Instance()->actionCollection()->action("dome_park"); if (parkAction) parkAction->setEnabled(true); QAction *unParkAction = KStars::Instance()->actionCollection()->action("dome_unpark"); if (unParkAction) unParkAction->setEnabled(false); emit newParkStatus(m_ParkStatus); if (m_Status != DOME_IDLE) { m_Status = DOME_IDLE; emit newStatus(m_Status); } } } } else if (!strcmp(svp->name, "DOME_MOTION")) { Status lastStatus = m_Status; - if (svp->s == IPS_BUSY && lastStatus != DOME_MOVING && lastStatus != DOME_PARKING && lastStatus != DOME_UNPARKING) + if (svp->s == IPS_BUSY && lastStatus != DOME_MOVING_CW && lastStatus != DOME_MOVING_CCW && lastStatus != DOME_PARKING && lastStatus != DOME_UNPARKING) { - m_Status = DOME_MOVING; + m_Status = svp->sp->s == ISS_ON ? DOME_MOVING_CW : DOME_MOVING_CCW; emit newStatus(m_Status); + + // rolloff roofs: cw = opening = unparking, ccw = closing = parking + if (!canAbsMove() && !canRelMove()) + { + m_ParkStatus = m_Status == DOME_MOVING_CW ? PARK_UNPARKING : PARK_PARKING; + emit newParkStatus(m_ParkStatus); + } } - else if (svp->s == IPS_OK && lastStatus == DOME_MOVING) + else if (svp->s == IPS_OK && (lastStatus == DOME_MOVING_CW || lastStatus == DOME_MOVING_CCW)) { m_Status = DOME_TRACKING; emit newStatus(m_Status); } else if (svp->s == IPS_IDLE && lastStatus != DOME_IDLE) { m_Status = DOME_IDLE; emit newStatus(m_Status); } } else if (!strcmp(svp->name, "DOME_SHUTTER")) { if (svp->s == IPS_ALERT) { emit newShutterStatus(SHUTTER_ERROR); // If alert, set shutter status to whatever it was opposite to. That is, if it was opening and failed // then we set status to closed since it did not successfully complete opening. if (m_ShutterStatus == SHUTTER_CLOSING) m_ShutterStatus = SHUTTER_OPEN; else if (m_ShutterStatus == SHUTTER_CLOSING) m_ShutterStatus = SHUTTER_CLOSED; emit newShutterStatus(m_ShutterStatus); } ShutterStatus status = shutterStatus(svp); switch (status) { case SHUTTER_CLOSING: if (m_ShutterStatus != SHUTTER_CLOSING) { m_ShutterStatus = SHUTTER_CLOSING; KNotification::event(QLatin1String("ShutterClosing"), i18n("Shutter closing is in progress")); emit newShutterStatus(m_ShutterStatus); } break; case SHUTTER_OPENING: if (m_ShutterStatus != SHUTTER_OPENING) { m_ShutterStatus = SHUTTER_OPENING; KNotification::event(QLatin1String("ShutterOpening"), i18n("Shutter opening is in progress")); emit newShutterStatus(m_ShutterStatus); } break; case SHUTTER_CLOSED: if (m_ShutterStatus != SHUTTER_CLOSED) { m_ShutterStatus = SHUTTER_CLOSED; KNotification::event(QLatin1String("ShutterClosed"), i18n("Shutter closed")); emit newShutterStatus(m_ShutterStatus); } break; case SHUTTER_OPEN: if (m_ShutterStatus != SHUTTER_OPEN) { m_ShutterStatus = SHUTTER_OPEN; KNotification::event(QLatin1String("ShutterOpened"), i18n("Shutter opened")); emit newShutterStatus(m_ShutterStatus); } break; default: break; } return; } else if (!strcmp(svp->name, "DOME_AUTOSYNC")) { ISwitch *sp = IUFindSwitch(svp, "DOME_AUTOSYNC_ENABLE"); if (sp != nullptr) emit newAutoSyncStatus(sp->s == ISS_ON); } DeviceDecorator::processSwitch(svp); } void Dome::processText(ITextVectorProperty *tvp) { DeviceDecorator::processText(tvp); } bool Dome::Abort() { if (m_CanAbort == false) return false; ISwitchVectorProperty *motionSP = baseDevice->getSwitch("DOME_ABORT_MOTION"); if (motionSP == nullptr) return false; ISwitch *abortSW = IUFindSwitch(motionSP, "ABORT"); if (abortSW == nullptr) return false; abortSW->s = ISS_ON; clientManager->sendNewSwitch(motionSP); return true; } bool Dome::Park() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("DOME_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "PARK"); if (parkSW == nullptr) return false; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Dome::UnPark() { ISwitchVectorProperty *parkSP = baseDevice->getSwitch("DOME_PARK"); if (parkSP == nullptr) return false; ISwitch *parkSW = IUFindSwitch(parkSP, "UNPARK"); if (parkSW == nullptr) return false; IUResetSwitch(parkSP); parkSW->s = ISS_ON; clientManager->sendNewSwitch(parkSP); return true; } bool Dome::isMoving() const { ISwitchVectorProperty *motionSP = baseDevice->getSwitch("DOME_MOTION"); if (motionSP && motionSP->s == IPS_BUSY) return true; return false; } double Dome::azimuthPosition() const { INumberVectorProperty *az = baseDevice->getNumber("ABS_DOME_POSITION"); if (az == nullptr) return -1; else return az->np[0].value; } bool Dome::setAzimuthPosition(double position) { INumberVectorProperty *az = baseDevice->getNumber("ABS_DOME_POSITION"); if (az == nullptr) return false; az->np[0].value = position; clientManager->sendNewNumber(az); return true; } bool Dome::setRelativePosition(double position) { INumberVectorProperty *azDiff = baseDevice->getNumber("REL_DOME_POSITION"); if (azDiff == nullptr) return false; azDiff->np[0].value = position; clientManager->sendNewNumber(azDiff); return true; } bool Dome::isAutoSync() { ISwitchVectorProperty *autosync = baseDevice->getSwitch("DOME_AUTOSYNC"); if (autosync == nullptr) return false; ISwitch *autosyncSW = IUFindSwitch(autosync, "DOME_AUTOSYNC_ENABLE"); if (autosync == nullptr) return false; else return (autosyncSW->s == ISS_ON); } bool Dome::setAutoSync(bool activate) { ISwitchVectorProperty *autosync = baseDevice->getSwitch("DOME_AUTOSYNC"); if (autosync == nullptr) return false; ISwitch *autosyncSW = IUFindSwitch(autosync, activate ? "DOME_AUTOSYNC_ENABLE" : "DOME_AUTOSYNC_DISABLE"); if (autosyncSW == nullptr) return false; IUResetSwitch(autosync); autosyncSW->s = ISS_ON; clientManager->sendNewSwitch(autosync); return true; } +bool Dome::moveDome(DomeDirection dir, DomeMotionCommand operation) +{ + ISwitchVectorProperty *domeMotion = baseDevice->getSwitch("DOME_MOTION"); + if (domeMotion == nullptr) + return false; + + ISwitch *opSwitch = IUFindSwitch(domeMotion, dir == DomeDirection::DOME_CW ? "DOME_CW": "DOME_CCW"); + IUResetSwitch(domeMotion); + opSwitch->s = (operation == DomeMotionCommand::MOTION_START ? ISS_ON : ISS_OFF); + + clientManager->sendNewSwitch(domeMotion); + + return true; +} + bool Dome::ControlShutter(bool open) { ISwitchVectorProperty *shutterSP = baseDevice->getSwitch("DOME_SHUTTER"); if (shutterSP == nullptr) return false; ISwitch *shutterSW = IUFindSwitch(shutterSP, open ? "SHUTTER_OPEN" : "SHUTTER_CLOSE"); if (shutterSW == nullptr) return false; IUResetSwitch(shutterSP); shutterSW->s = ISS_ON; clientManager->sendNewSwitch(shutterSP); return true; } Dome::ShutterStatus Dome::shutterStatus() { ISwitchVectorProperty *shutterSP = baseDevice->getSwitch("DOME_SHUTTER"); return shutterStatus(shutterSP); } Dome::ShutterStatus Dome::shutterStatus(ISwitchVectorProperty *svp) { if (svp == nullptr) return SHUTTER_UNKNOWN; ISwitch *sp = IUFindSwitch(svp, "SHUTTER_OPEN"); if (sp == nullptr) return SHUTTER_UNKNOWN; if (svp->s == IPS_ALERT) return SHUTTER_ERROR; else if (svp->s == IPS_BUSY) return (sp->s == ISS_ON) ? SHUTTER_OPENING : SHUTTER_CLOSING; else if (svp->s == IPS_OK) return (sp->s == ISS_ON) ? SHUTTER_OPEN : SHUTTER_CLOSED; // this should not happen return SHUTTER_UNKNOWN; } const QString Dome::getStatusString(Dome::Status status) { switch (status) { case ISD::Dome::DOME_IDLE: return i18n("Idle"); case ISD::Dome::DOME_PARKED: return i18n("Parked"); case ISD::Dome::DOME_PARKING: return i18n("Parking"); case ISD::Dome::DOME_UNPARKING: return i18n("UnParking"); - case ISD::Dome::DOME_MOVING: - return i18n("Moving"); + case ISD::Dome::DOME_MOVING_CW: + return i18n("Moving clockwise"); + + case ISD::Dome::DOME_MOVING_CCW: + return i18n("Moving counter clockwise"); case ISD::Dome::DOME_TRACKING: return i18n("Tracking"); case ISD::Dome::DOME_ERROR: return i18n("Error"); } return i18n("Error"); } } QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Dome::Status& source) { argument.beginStructure(); argument << static_cast(source); argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Dome::Status &dest) { int a; argument.beginStructure(); argument >> a; argument.endStructure(); dest = static_cast(a); return argument; } diff --git a/kstars/indi/indidome.h b/kstars/indi/indidome.h index 42de8f8b6..6bbb1759c 100644 --- a/kstars/indi/indidome.h +++ b/kstars/indi/indidome.h @@ -1,138 +1,153 @@ /* INDI Dome 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 #include #include "indistd.h" namespace ISD { /** * @class Dome - * Focuser class handles control of INDI dome devices. Both open and closed loop (senor feedback) domes are supported. + * Class handles control of INDI dome devices. Both open and closed loop (sensor feedback) domes are supported. * * @author Jasem Mutlaq */ class Dome : public DeviceDecorator { Q_OBJECT public: explicit Dome(GDInterface *iPtr); typedef enum { DOME_IDLE, - DOME_MOVING, + DOME_MOVING_CW, + DOME_MOVING_CCW, DOME_TRACKING, DOME_PARKING, DOME_UNPARKING, DOME_PARKED, DOME_ERROR } Status; typedef enum { SHUTTER_UNKNOWN, SHUTTER_OPEN, SHUTTER_CLOSED, SHUTTER_OPENING, SHUTTER_CLOSING, SHUTTER_ERROR } ShutterStatus; + typedef enum + { + DOME_CW, + DOME_CCW + } DomeDirection; + + typedef enum + { + MOTION_START, + MOTION_STOP + } DomeMotionCommand; + + void processSwitch(ISwitchVectorProperty *svp) override; void processText(ITextVectorProperty *tvp) override; void processNumber(INumberVectorProperty *nvp) override; void processLight(ILightVectorProperty *lvp) override; void registerProperty(INDI::Property *prop) override; DeviceFamily getType() override { return dType; } bool canPark() const { return m_CanPark; } bool canAbsMove() const { return m_CanAbsMove; } bool canRelMove() const { return m_CanRelMove; } bool canAbort() const { return m_CanAbort; } bool isParked() const { return m_ParkStatus == PARK_PARKED; } bool isMoving() const; double azimuthPosition() const; bool setAzimuthPosition(double position); bool setRelativePosition(double position); + bool moveDome(DomeDirection dir, DomeMotionCommand operation); bool hasShutter() const { return m_HasShutter; } // slaving bool isAutoSync(); bool setAutoSync(bool activate); Status status() const { return m_Status; } static const QString getStatusString (Status status); ShutterStatus shutterStatus(); ShutterStatus shutterStatus(ISwitchVectorProperty *svp); public slots: bool Abort(); bool Park(); bool UnPark(); bool ControlShutter(bool open); signals: void newStatus(Status status); void newParkStatus(ParkStatus status); void newShutterStatus(ShutterStatus status); void newAutoSyncStatus(bool enabled); void azimuthPositionChanged(double Az); void ready(); private: ParkStatus m_ParkStatus { PARK_UNKNOWN }; ShutterStatus m_ShutterStatus { SHUTTER_UNKNOWN }; Status m_Status { DOME_IDLE }; bool m_CanAbsMove { false }; bool m_CanRelMove { false }; bool m_CanPark { false }; bool m_CanAbort { false }; bool m_HasShutter { false }; std::unique_ptr readyTimer; }; } Q_DECLARE_METATYPE(ISD::Dome::Status) QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Dome::Status &source); const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Dome::Status &dest); diff --git a/kstars/kstars.kcfg b/kstars/kstars.kcfg index 65154a812..da93a995b 100644 --- a/kstars/kstars.kcfg +++ b/kstars/kstars.kcfg @@ -1,2388 +1,2392 @@ ksutils.h The screen coordinates of the Time InfoBox. QPoint(0,0) The screen coordinates of the Focus InfoBox. QPoint(600,0) The screen coordinates of the Geographic Location InfoBox. QPoint(0,600) If true, the Time InfoBox will show only its top line of data. true If true, the Focus InfoBox will show only its top line of data. true If true, the Geographic Location InfoBox will show only its top line of data. true Toggles display of all three InfoBoxes. true Toggles display of the Time InfoBox. true Toggles display of the Focus InfoBox. true Toggles display of the Geographic Location InfoBox. true Is the Time InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 0 0 3 Is the Focus InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 1 0 3 Is the Geographic Location InfoBox anchored to a window edge? 0 = not anchored; 1 = anchored to right edge; 2 = anchored to bottom edge; 3 = anchored to bottom and right edges. 2 0 3 Toggle display of the status bar. true Toggle display of the Horizontal coordinates of the mouse cursor in the status bar. true Toggle display of the Equatorial coordinates of the mouse cursor at the current epoch in the status bar. true Toggle display of the Equatorial coordinates of the mouse cursor at the standard epoch in the status bar. false true 1024 768 true Black Body List of the filenames of custom object catalogs. List of integers toggling display of each custom object catalog (any nonzero value indicates the objects in that catalog will be displayed). List of names for which custom catalogs are to be displayed. Names of objects entered into the find dialog are resolved using online services and stored in the database. This option also toggles the display of such resolved objects on the sky map. true 800 600 true true false Toggle display of crosshairs centered at telescope's pointed position in the KStars sky map. true Toggle display of INDI messages in the KStars statusbar. true Show INDI messages as desktop notifications instead of dialogs. false true false false The default location of saved FITS files KSUtils::getDefaultPath("fitsDir") INDI server will attempt to bind with ports starting from this port 7624 INDI server will attempt to bind with ports ending with this port 9000 List of the aliases for filter wheel slots. PATH to indiserver binary KSUtils::getDefaultPath("indiServer") false PATH to indi drivers directory KSUtils::getDefaultPath("indiDriversDir") false 320 240 false false false false false false false false false false false The City name of the current geographic location. Greenwich The Province name of the current geographic location. This is the name of the state for locations in the U. S. The Country name of the current geographic location. United Kingdom The longitude of the current geographic location, in decimal degrees. 0.0 The latitude of the current geographic location, in decimal degrees. 51.468 -10.0 0.0 Two-letter code that determines the dates on which daylight savings time begins and ends (you can view the rules by pressing the "Explain DST Rules" button in the Geographic Location window). -- If true, focus changes will cause the sky to visibly spin to the new position. Otherwise, the display will "snap" instantly to the new position. true If true, clicking on the skymap will select the closest object and highlights it. false Type of cursor when exploring the sky map. 1 The names of the currently selected field-of-view indicators. The list of defined FOV indicator names is listed in the "Settings|FOV Symbols" menu. Telrad If true, trails attached to solar system bodies will fade into the background sky color. true The right ascension of the initial focus position of the sky map, in decimal hours. This value is volatile; it is reset whenever the program shuts down. 180.0 The declination of the initial focus position of the sky map, in decimal degrees. This value is volatile; it is reset whenever the program shuts down. 45.0 The name of the object that should be centered and tracked on startup. If no object should be centered, set to "nothing". This value is volatile; it is reset whenever the program shuts down. nothing True if the skymap should track on its initial position on startup. This value is volatile; it is reset whenever the program shuts down. false Toggle whether KStars should hide some objects while the display is moving, for smoother motion. true Toggle whether constellation boundaries are hidden while the display is in motion. true Toggle whether constellation lines are hidden while the display is in motion. false Choose sky culture. 11 Toggle whether constellation names are hidden while the display is in motion. false Toggle whether the coordinate grids are hidden while the display is in motion. true Toggle whether the Milky Way contour is hidden while the display is in motion. true Toggle whether IC objects are hidden while the display is in motion. true Toggle whether Messier objects are hidden while the display is in motion. true Toggle whether NGC objects are hidden while the display is in motion. true Toggle whether extra objects are hidden while the display is in motion. true Toggle whether solar system objects are hidden while the display is in motion. false Toggle whether faint stars are hidden while the display is in motion. true Toggle whether name labels are hidden while the display is in motion. true Toggle whether asteroids are drawn in the sky map. true Toggle whether asteroid name labels are drawn in the sky map. false true Toggle whether comets are drawn in the sky map. true Toggle whether comet comas are drawn in the sky map. true Toggle whether comet name labels are drawn in the sky map. false Toggle whether supernovae are drawn in the sky map. false Toggle whether supernova name labels are drawn in the sky map. false Set magnitude limit for supernovae to be shown on the skymap. 16 Toggle supernova alerts. true Set magnitude limit for supernovae to be alerted. 13 Toggle whether constellation boundaries are drawn in the sky map. false Toggle whether constellation boundary containing the central focus point is highlighted in the sky map. false Toggle whether constellation lines are drawn in the sky map. false Toggle whether constellation art drawn in the sky map. false Toggle whether constellation name labels are drawn in the sky map. false Toggle whether deep-sky objects are drawn in the sky map. true Toggle whether the ecliptic line is drawn in the sky map. false Toggle whether the equator line is drawn in the sky map. false Coordinate grids will automatically change according to active coordinate system. true Toggle whether the equatorial coordinate grid is drawn in the sky map. false Toggle whether the horizontal coordinate grid is drawn in the sky map. false Toggle whether the local meridian line is drawn in the sky map. false Toggle whether the region below the horizon is opaque. true Toggle whether the horizon line is drawn in the sky map. true Toggle whether flags are drawn in the sky map. true Toggle whether IC objects are drawn in the sky map. false Toggle whether NGC objects are drawn in the sky map. true Toggle whether Messier objects are drawn in the sky map. true Toggle whether Messier objects are rendered as images in the sky map. true Toggle whether extra objects are drawn in the sky map. true Toggle whether the Milky Way contour is drawn in the sky map. true Toggle whether the Milky Way contour is filled. When this option is false, the Milky Way is shown as an outline. true Meta-option to control whether all major planets (and the Sun and Moon) are drawn in the sky map. true Toggle whether major planets (and the Sun and Moon) are rendered as images in the sky map. true Toggle whether major planets (and the Sun and Moon) are labeled in the sky map. true Toggle whether the Sun is drawn in the sky map. true Toggle whether the Moon is drawn in the sky map. true Toggle whether Mercury is drawn in the sky map. true Toggle whether Venus is drawn in the sky map. true Toggle whether Mars is drawn in the sky map. true Toggle whether Jupiter is drawn in the sky map. true Toggle whether Saturn is drawn in the sky map. true Toggle whether Uranus is drawn in the sky map. true Toggle whether Neptune is drawn in the sky map. true Toggle whether Pluto is drawn in the sky map. true Toggle whether stars are drawn in the sky map. true Toggle whether star magnitude (brightness) labels are shown in the sky map. false Toggle whether star name labels are shown in the sky map. true Toggle whether deep-sky object magnitude (brightness) labels are shown in the sky map. false Toggle whether deep-sky object name labels are shown in the sky map. false The timescale above which slewing mode is forced on at all times. 60 The background fill mode for the on-screen information boxes: 0="no BG"; 1="semi-transparent BG"; 2="opaque BG" 1 Algorithm for the mapping projection. 0 Use official IAU abbreviations for constellation names. false Use Latin constellation names. false Use localized constellation names (if localized names are not available, default to Latin names). true Display the sky with horizontal coordinates (when false, equatorial coordinates will be used). true Toggle whether a centered object automatically gets a name label attached. true Toggle whether a centered solar system object automatically gets a trail attached, as long as it remains centered. true Toggle whether the object under the mouse cursor gets a transient name label. true Toggle whether object positions are corrected for the effects of atmospheric refraction (only applies when horizontal coordinates are used). true Toggle whether corrections due to bending of light around the sun are taken into account false Toggle whether the sky is rendered using antialiasing. Lines and shapes are smoother with antialiasing, but rendering the screen will take more time. true The zoom level, measured in pixels per radian. 250. 250. 5000000. When zooming in or out, change zoom speed factor by this multiplier. 0.2 0.01 1.0 The faint magnitude limit for drawing asteroids. 15.0 The maximum magnitude (visibility) to filter the asteroid data download from JPL. 12.000 Controls the relative number of asteroid name labels drawn in the map. 4.0 The faint magnitude limit for drawing deep-sky objects, when fully zoomed in. 16.0 The faint magnitude limit for drawing deep-sky objects, when fully zoomed out. 5.0 When enabled, objects whose magnitudes are unknown, or not available to KStars, are drawn irrespective of the faint limits set. true Sets the density of stars in the field of view 5 The faint magnitude limit for drawing stars, when the map is in motion (only applicable if faint stars are set to be hidden while the map is in motion). 5.0 The relative density for drawing star name and magnitude labels. 2.0 The relative density for drawing deep-sky object name and magnitude labels. 5.0 If true, long names (common names) for deep-sky objects are shown in the labels. false The maximum solar distance for drawing comets. 3.0 Use experimental OpenGL backend (deprecated). false The state of the clock (running or not) true Objects in the observing list will be highlighted with a symbol in the map. true Objects in the observing list will be highlighted with a colored name label in the map. false The observing list will prefer DSS imagery while downloading imagery. true The observing list will prefer SDSS imagery while downloading imagery. false Check this if you use a large Dobsonian telescope. Sorting by percentage current altitude is an easy way of determining what objects are well-placed for observation. However, when using a large Dobsonian telescope, objects close to the zenith are hard to observe. Since tracking there corresponds to a rotation in azimuth, it is both counterintuitive and requires the observer to frequently move the ladder. The region around the zenith where this is particularly frustrating is called the Dobsonian hole. This checkbox makes the observing list consider objects present in the hole as unfit for observation. false This specifies the angular radius of the Dobsonian hole, i.e. the region where a large Dobsonian telescope cannot be pointed easily. 15.00 40.00 The name of the color scheme moonless-night.colors The method for rendering stars: 0="realistic colors"; 1="solid red"; 2="solid black"; 3="solid white"; 4="solid real colors" 0 4 The color saturation level of stars (only applicable when using "realistic colors" mode). 6 10 The color for the angular-distance measurement ruler. #FFF The background color of the on-screen information boxes. #000 The text color for the on-screen information boxes, when activated by a mouse click. #F00 The normal text color of the on-screen information boxes. #FFF The color for the constellation boundary lines. #222 The color for the constellation boundary lines. #222 The color for the constellation figure lines. #555 The color for the constellation names. #AA7 The color for the cardinal compass point labels. #002 The color for the ecliptic line. #663 The color for the equator line. #FFF The color for the equatorial coordinate grid lines. #456 The color for the horizontal coordinate grid lines. #5A3 The color for objects which have extra URL links available. #A00 The color for the horizon line and opaque ground. #5A3 The color for the local meridian line. #0059b3 The color for Messier object symbols. #0F0 The color for NGC object symbols. #066 The color for IC object symbols. #439 The color for the Milky Way contour. #123 The color for star name labels. #7AA The color for deep-sky object name labels. #7AA The color for solar system object labels. #439 The color for solar system object trails. #963 The color for the sky background. #002 The color for the artificial horizon region. #C82828 The color for telescope target symbols. #8B8 Color of visible satellites. #00FF00 Color of invisible satellites. #FF0000 Color of satellites labels. #640000 Color of supernova #FFA500 The color for user-added object labels. #439 The color for RA Guide Error bar in Ekos guide module. #00FF00 The color for DEC Guide Error bar in Ekos guide module. #00A5FF The color for solver FOV box in Ekos alignment module. #FFFF00 false Xplanet binary path KSUtils::getDefaultPath("XplanetPath") Option to use a FIFO file instead of saving to the hard disk true How long to wait for XPlanet before giving up in milliseconds 1000 How long to pause between frames in the XPlanet Animation 100 Width of xplanet window 640 Height of xplanet window 480 If true, display a label in the upper right corner. false Show local time. true Show GMT instead of local time. false Specify the text of the first line of the label. By default, it says something like "Looking at Earth". Any instances of %t will be replaced by the target name, and any instances of %o will be replaced by the origin name. Specify the point size. 12 Set the color for the label. #F00 Specify the format for the date/time label. This format string is passed to strftime(3). The default is "%c %Z", which shows the date, time, and time zone in the locale’s appropriate date and time representation. %c %Z false true false false Draw a glare around the sun with a radius of the specified value larger than the Sun. The default value is 28. 28 Place the observer above a random latitude and longitude false Place the observer above the specified longitude and latitude true Render the target body as seen from above the specified latitude (in degrees). The default value is 0. 0 Place the observer above the specified longitude (in degrees). Longitude is positive going east, negative going west (for the earth and moon), so for example Los Angeles is at -118 or 242. The default value is 0. 0 The default is no projection. Multiple bodies will not be shown if this option is specified, although shadows will still be drawn. 0 Use a file as the background image, with the planet to be superimposed upon it. This option is only meaningful with the -projection option. A color may also be supplied. false Use a file as the background image. false The path of the background image. Use a color as the background. true The color of the background. #000 A star of the specified magnitude will have a pixel brightness of 1. The default value is 10. Stars will be drawn more brightly if this number is larger. 10 If checked, use an arc file to be plotted against the background stars. false Specify an arc file to be plotted against the background stars. If checked, use a config file. false Use the specified configuration file. If checked, use kstars's FOV. false If checked, use the specified marker file. false Specify a file containing user-defined marker data to display against the background stars. If checked, write coordinates of the bounding box for each marker in a file. false Write coordinates of the bounding box for each marker to this file. If checked, use star map file to draw the background stars. false Star map file path This option is only used when creating JPEG images. The quality can range from 0 to 100. The default value is 80. 80 Toggle whether satellite tracks are drawn in the sky map. false Toggle whether satellite tracks are drawn in the sky map. false If selected, satellites will be draw like stars, otherwise, draw satellites as small colored square. false Toggle whether satellite labels are drawn in the sky map. false List of selected satellites. Checking this option causes recomputation of current equatorial coordinates from catalog coordinates (i.e. application of precession, nutation and aberration corrections) for every redraw of the map. This makes processing slower when there are many stars to handle, but is more likely to be bug free. There are known bugs in the rendering of stars when this recomputation is avoided. false The default size for DSS images downloaded from the Internet. 15.0 To include parts of the star field, we add some extra padding around DSS images of deep-sky objects. This option configures the total (both sides) padding added to either dimension of the field. 10.0 Checking this option causes KStars to generate verbose debug information for diagnostic purposes. This may cause slowdown of KStars. false Checking this option causes KStars to generate regular debug information. true Checking this option causes KStars to stop generating ANY debug information. false Checking this option causes KStars log debug messages to the default output used by the platform (e.g. Standard Error). true Checking this option causes KStars log debug messages to a log file as specified. false Log FITS Data activity. false Log INDI devices activity. false Log Ekos Capture Module activity. false Log Ekos Focus Module activity. false Log Ekos Guide Module activity. false Log Ekos Alignment Module activity. false Log Ekos Mount Module activity. false + + Log Ekos Observatory Module activity. + false + true Display all captured FITS images in a single tab instead of multiple tabs per image. true Display all captured FITS images in a single FITS Viewer window. By default each camera create its own FITS Viewer instance false Display all opened FITS images in a single FITS Viewer window. true Bring the FITSViewer window to the foreground when receiving a new image. true false !KSUtils::isHardwareLimited() false !KSUtils::isHardwareLimited() !KSUtils::isHardwareLimited() KSUtils::isHardwareLimited() 4 false false 40.0 0 600 600 true false true true true false Simulators false true false true 1 Minimum telescope altitude limit. If the telescope is below this limit, it will be commanded to stop. 0 Maximum telescope altitude limit. If the telescope is above this limit, it will be commanded to stop. 90.0 false If the target hour angle exceeds this value, Ekos will command a meridian flip and if successful it will resume guiding and capture operations. false false 1 0 If guide deviation exceeds this limit, the exposure will be automatically aborted and only resumed when the deviation is within this limit. 2 If HFR deviation exceeds this limit, the autofocus routine will be automatically started. 0.5 false false false Sets the time interval before forced autofocus attempts during a capture sequence. 60 false If set, Ekos will capture a few flat images to determine the optimal exposure time to achieve the desired ADU value. 0 Maximum difference between measured and target ADU values to deem the value as acceptable. 1000 0 0 0 0 0.1 0 false false 2.5 true false 1 30 true !KSUtils::isHardwareLimited() !KSUtils::isHardwareLimited() 0 KSUtils::getDefaultPath("fitsDir") 60 Step size of the absolute focuser. The step size TICKS should be adjusted so that when the focuser moves TICKS steps, the difference in HFR is more than 0.1 pixels. Lower the value when you are close to optimal focus. 100 Wait for this many seconds after moving the focuser before capturing the next image during AutoFocus. 0 Wait for this many seconds after resuming guide. 0 The tolerance specifies the percentage difference between the current focusing position and the minimum obtained during the focusing run. Adjustment of this value is necessary to prevent the focusing algorithm from oscillating back and forth. 1 Set the maximum travel distance of an absolute focuser. 10000 Specifies gain value of CCD when performing focusing if supported by camera. 0 Set box size to select a focus star. 64 Set horizontal binning of CCD camera while in focus mode. 1 Set vertical binning of CCD camera while in focus mode. 1 true false During full field focusing, stars which are inside this percentage of the frame are filtered out of HFR calculation (default 0%). Detection algorithms may also have an inherent filter. 0.0 During full field focusing, stars which are outside this percentage of the frame are filtered out of HFR calculation (default 100%). Detection algorithms may also have an inherent filter. 100.0 false true false 0 150 0 0 1 Specifies exposure value of CCD in seconds when performing plate solving. 1 Set binning index of CCD camera while in alignment mode. Default values 0-3 corresponding to 1x1 to 4x4 binning. 4 is max binning. 4 Use rotator when performing load and slew. false Threshold between measured and FITS position angles in arcminutes to consider the load and slew operation successful. 30 0 0 1 true false false 30 0 false 1500 false true true true true 1 true 2 true true true 30 false Path to astrometry.net solver location. KSUtils::getDefaultPath("AstrometrySolverBinary") false Path to astrometry.net wcsinfo location. KSUtils::getDefaultPath("AstrometryWCSInfo") false Path to astrometry.net file location. KSUtils::getDefaultPath("AstrometryConfFile") Astrometry.net Index files Location. KSUtils::getDefaultPath("AstrometryIndexFileLocation") false true Folder in which the desired python executable or link to be used for astrometry.net resides. /usr/local/opt/python/libexec/bin Key to access astrometry.net online web services. You must register with astrometry.net to obtain a key. iczikaqstszeptgs http://nova.astrometry.net true 180 -1 true 1.0 0 0 localhost 4400 localhost 5656 0 1000 2 false 1 false false false 3 60 10 true false false false 2 1 0 1 45 10 500 false false false 2 true true true true true true 133.33 133.33 0 0 0 0 5000 5000 100 100 0.5 2 true true false false Log Ekos Scheduler Module activity. false Sort scheduler jobs by priority and altitude. true true false false false false true false 2 true 5 30 3 0 0 0 0 0 0 7624 8624 300 1000 None false false Toggle whether the HIPS sources are drawn in the sky map. false true true false false true 600 true true true 30 true true true