File indexing completed on 2024-03-24 03:45:04
0001 /* 0002 SPDX-FileCopyrightText: 2002 Mark Hollomon <mhh@mindspring.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "ksutils.h" 0008 #include "config-kstars.h" 0009 #include "ksnotification.h" 0010 #include "kstars_debug.h" 0011 0012 #include "catalogobject.h" 0013 #ifndef KSTARS_LITE 0014 #include "kswizard.h" 0015 #endif 0016 #include "Options.h" 0017 #include "starobject.h" 0018 #include "auxiliary/kspaths.h" 0019 0020 #ifndef KSTARS_LITE 0021 #include <KMessageBox> 0022 #include <zlib.h> 0023 #endif 0024 0025 #ifdef HAVE_LIBRAW 0026 #include <libraw/libraw.h> 0027 #endif 0028 0029 #if defined(__APPLE__) 0030 #include <sys/sysctl.h> 0031 #elif defined(_WIN32) 0032 #include "windows.h" 0033 #else //Linux 0034 #include <QProcess> 0035 #endif 0036 0037 #include <QPointer> 0038 #include <QProcessEnvironment> 0039 #include <QLoggingCategory> 0040 0041 #ifdef HAVE_STELLARSOLVER 0042 #include <stellarsolver.h> 0043 #endif 0044 0045 namespace KSUtils 0046 { 0047 bool isHardwareLimited() 0048 { 0049 #ifdef __arm__ 0050 return true; 0051 #else 0052 return false; 0053 #endif 0054 } 0055 0056 bool openDataFile(QFile &file, const QString &s) 0057 { 0058 QString FileName = KSPaths::locate(QStandardPaths::AppLocalDataLocation, s); 0059 if (!FileName.isNull()) 0060 { 0061 file.setFileName(FileName); 0062 return file.open(QIODevice::ReadOnly); 0063 } 0064 return false; 0065 } 0066 0067 QString getDSSURL(const SkyPoint *const p) 0068 { 0069 double height, width; 0070 double dss_default_size = Options::defaultDSSImageSize(); 0071 double dss_padding = Options::dSSPadding(); 0072 0073 Q_ASSERT(p); 0074 Q_ASSERT(dss_default_size > 0.0 && dss_padding >= 0.0); 0075 0076 const auto *dso = dynamic_cast<const CatalogObject *>(p); 0077 0078 // Decide what to do about the height and width 0079 if (dso) 0080 { 0081 // For deep-sky objects, use their height and width information 0082 double a, b, pa; 0083 a = dso->a(); 0084 b = dso->a() * 0085 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 0086 pa = dso->pa() * dms::DegToRad; 0087 0088 // We now want to convert a, b, and pa into an image 0089 // height and width -- i.e. a dRA and dDec. 0090 // DSS uses dDec for height and dRA for width. (i.e. "top" is north in the DSS images, AFAICT) 0091 // From some trigonometry, assuming we have a rectangular object (worst case), we need: 0092 width = a * sin(pa) + b * cos(pa); 0093 height = a * cos(pa) + b * sin(pa); 0094 // 'a' and 'b' are in arcminutes, so height and width are in arcminutes 0095 0096 // Pad the RA and Dec, so that we show more of the sky than just the object. 0097 height += dss_padding; 0098 width += dss_padding; 0099 } 0100 else 0101 { 0102 // For a generic sky object, we don't know what to do. So 0103 // we just assume the default size. 0104 height = width = dss_default_size; 0105 } 0106 // There's no point in tiny DSS images that are smaller than dss_default_size 0107 if (height < dss_default_size) 0108 height = dss_default_size; 0109 if (width < dss_default_size) 0110 width = dss_default_size; 0111 0112 return getDSSURL(p->ra0(), p->dec0(), width, height); 0113 } 0114 0115 QString getDSSURL(const dms &ra, const dms &dec, float width, float height, 0116 const QString &type) 0117 { 0118 const QString URLprefix("https://archive.stsci.edu/cgi-bin/dss_search?"); 0119 QString URLsuffix = QString("&e=J2000&f=%1&c=none&fov=NONE").arg(type); 0120 const double dss_default_size = Options::defaultDSSImageSize(); 0121 0122 char decsgn = (dec.Degrees() < 0.0) ? '-' : '+'; 0123 int dd = abs(dec.degree()); 0124 int dm = abs(dec.arcmin()); 0125 int ds = abs(dec.arcsec()); 0126 0127 // Infinite and NaN sizes are replaced by the default size and tiny DSS images are resized to default size 0128 if (!qIsFinite(height) || height <= 0.0) 0129 height = dss_default_size; 0130 if (!qIsFinite(width) || width <= 0.0) 0131 width = dss_default_size; 0132 0133 // DSS accepts images that are no larger than 75 arcminutes 0134 if (height > 75.0) 0135 height = 75.0; 0136 if (width > 75.0) 0137 width = 75.0; 0138 0139 QString RAString, DecString, SizeString; 0140 DecString = QString::asprintf("&d=%c%02d+%02d+%02d", decsgn, dd, dm, ds); 0141 RAString = QString::asprintf("r=%02d+%02d+%02d", ra.hour(), ra.minute(), ra.second()); 0142 SizeString = QString::asprintf("&h=%02.1f&w=%02.1f", height, width); 0143 0144 return (URLprefix + RAString + DecString + SizeString + URLsuffix); 0145 } 0146 0147 QString toDirectionString(dms angle) 0148 { 0149 // TODO: Instead of doing it this way, it would be nicer to 0150 // compute the string to arbitrary precision. Although that will 0151 // not be easy to localize. (Consider, for instance, Indian 0152 // languages that have special names for the intercardinal points) 0153 // -- asimha 0154 0155 static const char *directions[] = 0156 { 0157 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "N"), 0158 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NNE"), 0159 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NE"), 0160 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "ENE"), 0161 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "E"), 0162 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "ESE"), 0163 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SE"), 0164 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SSE"), 0165 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "S"), 0166 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SSW"), 0167 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SW"), 0168 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "WSW"), 0169 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "W"), 0170 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "WNW"), 0171 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NW"), 0172 I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NNW"), 0173 I18N_NOOP2("Unknown cardinal / intercardinal direction", "???") 0174 }; 0175 0176 int index = (int)((angle.reduce().Degrees() + 11.25) / 0177 22.5); // A number between 0 and 16 (inclusive) is expected 0178 0179 if (index < 0 || index > 16) 0180 index = 16; // Something went wrong. 0181 else 0182 index = (index == 16 ? 0 : index); 0183 0184 return i18nc("Abbreviated cardinal / intercardinal etc. direction", 0185 directions[index]); 0186 } 0187 0188 QList<SkyObject *> *castStarObjListToSkyObjList(QList<StarObject *> *starObjList) 0189 { 0190 QList<SkyObject *> *skyObjList = new QList<SkyObject *>(); 0191 0192 foreach (StarObject *so, *starObjList) 0193 { 0194 skyObjList->append(so); 0195 } 0196 return skyObjList; 0197 } 0198 0199 QString constGenetiveFromAbbrev(const QString &code) 0200 { 0201 if (code == "And") 0202 return QString("Andromedae"); 0203 if (code == "Ant") 0204 return QString("Antliae"); 0205 if (code == "Aps") 0206 return QString("Apodis"); 0207 if (code == "Aqr") 0208 return QString("Aquarii"); 0209 if (code == "Aql") 0210 return QString("Aquilae"); 0211 if (code == "Ara") 0212 return QString("Arae"); 0213 if (code == "Ari") 0214 return QString("Arietis"); 0215 if (code == "Aur") 0216 return QString("Aurigae"); 0217 if (code == "Boo") 0218 return QString("Bootis"); 0219 if (code == "Cae") 0220 return QString("Caeli"); 0221 if (code == "Cam") 0222 return QString("Camelopardalis"); 0223 if (code == "Cnc") 0224 return QString("Cancri"); 0225 if (code == "CVn") 0226 return QString("Canum Venaticorum"); 0227 if (code == "CMa") 0228 return QString("Canis Majoris"); 0229 if (code == "CMi") 0230 return QString("Canis Minoris"); 0231 if (code == "Cap") 0232 return QString("Capricorni"); 0233 if (code == "Car") 0234 return QString("Carinae"); 0235 if (code == "Cas") 0236 return QString("Cassiopeiae"); 0237 if (code == "Cen") 0238 return QString("Centauri"); 0239 if (code == "Cep") 0240 return QString("Cephei"); 0241 if (code == "Cet") 0242 return QString("Ceti"); 0243 if (code == "Cha") 0244 return QString("Chamaeleontis"); 0245 if (code == "Cir") 0246 return QString("Circini"); 0247 if (code == "Col") 0248 return QString("Columbae"); 0249 if (code == "Com") 0250 return QString("Comae Berenices"); 0251 if (code == "CrA") 0252 return QString("Coronae Austrinae"); 0253 if (code == "CrB") 0254 return QString("Coronae Borealis"); 0255 if (code == "Crv") 0256 return QString("Corvi"); 0257 if (code == "Crt") 0258 return QString("Crateris"); 0259 if (code == "Cru") 0260 return QString("Crucis"); 0261 if (code == "Cyg") 0262 return QString("Cygni"); 0263 if (code == "Del") 0264 return QString("Delphini"); 0265 if (code == "Dor") 0266 return QString("Doradus"); 0267 if (code == "Dra") 0268 return QString("Draconis"); 0269 if (code == "Equ") 0270 return QString("Equulei"); 0271 if (code == "Eri") 0272 return QString("Eridani"); 0273 if (code == "For") 0274 return QString("Fornacis"); 0275 if (code == "Gem") 0276 return QString("Geminorum"); 0277 if (code == "Gru") 0278 return QString("Gruis"); 0279 if (code == "Her") 0280 return QString("Herculis"); 0281 if (code == "Hor") 0282 return QString("Horologii"); 0283 if (code == "Hya") 0284 return QString("Hydrae"); 0285 if (code == "Hyi") 0286 return QString("Hydri"); 0287 if (code == "Ind") 0288 return QString("Indi"); 0289 if (code == "Lac") 0290 return QString("Lacertae"); 0291 if (code == "Leo") 0292 return QString("Leonis"); 0293 if (code == "LMi") 0294 return QString("Leonis Minoris"); 0295 if (code == "Lep") 0296 return QString("Leporis"); 0297 if (code == "Lib") 0298 return QString("Librae"); 0299 if (code == "Lup") 0300 return QString("Lupi"); 0301 if (code == "Lyn") 0302 return QString("Lyncis"); 0303 if (code == "Lyr") 0304 return QString("Lyrae"); 0305 if (code == "Men") 0306 return QString("Mensae"); 0307 if (code == "Mic") 0308 return QString("Microscopii"); 0309 if (code == "Mon") 0310 return QString("Monocerotis"); 0311 if (code == "Mus") 0312 return QString("Muscae"); 0313 if (code == "Nor") 0314 return QString("Normae"); 0315 if (code == "Oct") 0316 return QString("Octantis"); 0317 if (code == "Oph") 0318 return QString("Ophiuchi"); 0319 if (code == "Ori") 0320 return QString("Orionis"); 0321 if (code == "Pav") 0322 return QString("Pavonis"); 0323 if (code == "Peg") 0324 return QString("Pegasi"); 0325 if (code == "Per") 0326 return QString("Persei"); 0327 if (code == "Phe") 0328 return QString("Phoenicis"); 0329 if (code == "Pic") 0330 return QString("Pictoris"); 0331 if (code == "Psc") 0332 return QString("Piscium"); 0333 if (code == "PsA") 0334 return QString("Piscis Austrini"); 0335 if (code == "Pup") 0336 return QString("Puppis"); 0337 if (code == "Pyx") 0338 return QString("Pyxidis"); 0339 if (code == "Ret") 0340 return QString("Reticuli"); 0341 if (code == "Sge") 0342 return QString("Sagittae"); 0343 if (code == "Sgr") 0344 return QString("Sagittarii"); 0345 if (code == "Sco") 0346 return QString("Scorpii"); 0347 if (code == "Scl") 0348 return QString("Sculptoris"); 0349 if (code == "Sct") 0350 return QString("Scuti"); 0351 if (code == "Ser") 0352 return QString("Serpentis"); 0353 if (code == "Sex") 0354 return QString("Sextantis"); 0355 if (code == "Tau") 0356 return QString("Tauri"); 0357 if (code == "Tel") 0358 return QString("Telescopii"); 0359 if (code == "Tri") 0360 return QString("Trianguli"); 0361 if (code == "TrA") 0362 return QString("Trianguli Australis"); 0363 if (code == "Tuc") 0364 return QString("Tucanae"); 0365 if (code == "UMa") 0366 return QString("Ursae Majoris"); 0367 if (code == "UMi") 0368 return QString("Ursae Minoris"); 0369 if (code == "Vel") 0370 return QString("Velorum"); 0371 if (code == "Vir") 0372 return QString("Virginis"); 0373 if (code == "Vol") 0374 return QString("Volantis"); 0375 if (code == "Vul") 0376 return QString("Vulpeculae"); 0377 return code; 0378 } 0379 0380 QString constNameFromAbbrev(const QString &code) 0381 { 0382 if (code == "And") 0383 return QString("Andromeda"); 0384 if (code == "Ant") 0385 return QString("Antlia"); 0386 if (code == "Aps") 0387 return QString("Apus"); 0388 if (code == "Aqr") 0389 return QString("Aquarius"); 0390 if (code == "Aql") 0391 return QString("Aquila"); 0392 if (code == "Ara") 0393 return QString("Ara"); 0394 if (code == "Ari") 0395 return QString("Aries"); 0396 if (code == "Aur") 0397 return QString("Auriga"); 0398 if (code == "Boo") 0399 return QString("Bootes"); 0400 if (code == "Cae") 0401 return QString("Caelum"); 0402 if (code == "Cam") 0403 return QString("Camelopardalis"); 0404 if (code == "Cnc") 0405 return QString("Cancer"); 0406 if (code == "CVn") 0407 return QString("Canes Venatici"); 0408 if (code == "CMa") 0409 return QString("Canis Major"); 0410 if (code == "CMi") 0411 return QString("Canis Minor"); 0412 if (code == "Cap") 0413 return QString("Capricornus"); 0414 if (code == "Car") 0415 return QString("Carina"); 0416 if (code == "Cas") 0417 return QString("Cassiopeia"); 0418 if (code == "Cen") 0419 return QString("Centaurus"); 0420 if (code == "Cep") 0421 return QString("Cepheus"); 0422 if (code == "Cet") 0423 return QString("Cetus"); 0424 if (code == "Cha") 0425 return QString("Chamaeleon"); 0426 if (code == "Cir") 0427 return QString("Circinus"); 0428 if (code == "Col") 0429 return QString("Columba"); 0430 if (code == "Com") 0431 return QString("Coma Berenices"); 0432 if (code == "CrA") 0433 return QString("Corona Australis"); 0434 if (code == "CrB") 0435 return QString("Corona Borealis"); 0436 if (code == "Crv") 0437 return QString("Corvus"); 0438 if (code == "Crt") 0439 return QString("Crater"); 0440 if (code == "Cru") 0441 return QString("Crux"); 0442 if (code == "Cyg") 0443 return QString("Cygnus"); 0444 if (code == "Del") 0445 return QString("Delphinus"); 0446 if (code == "Dor") 0447 return QString("Doradus"); 0448 if (code == "Dra") 0449 return QString("Draco"); 0450 if (code == "Equ") 0451 return QString("Equuleus"); 0452 if (code == "Eri") 0453 return QString("Eridanus"); 0454 if (code == "For") 0455 return QString("Fornax"); 0456 if (code == "Gem") 0457 return QString("Gemini"); 0458 if (code == "Gru") 0459 return QString("Grus"); 0460 if (code == "Her") 0461 return QString("Hercules"); 0462 if (code == "Hor") 0463 return QString("Horologium"); 0464 if (code == "Hya") 0465 return QString("Hydra"); 0466 if (code == "Hyi") 0467 return QString("Hydrus"); 0468 if (code == "Ind") 0469 return QString("Indus"); 0470 if (code == "Lac") 0471 return QString("Lacerta"); 0472 if (code == "Leo") 0473 return QString("Leo"); 0474 if (code == "LMi") 0475 return QString("Leo Minor"); 0476 if (code == "Lep") 0477 return QString("Lepus"); 0478 if (code == "Lib") 0479 return QString("Libra"); 0480 if (code == "Lup") 0481 return QString("Lupus"); 0482 if (code == "Lyn") 0483 return QString("Lynx"); 0484 if (code == "Lyr") 0485 return QString("Lyra"); 0486 if (code == "Men") 0487 return QString("Mensa"); 0488 if (code == "Mic") 0489 return QString("Microscopium"); 0490 if (code == "Mon") 0491 return QString("Monoceros"); 0492 if (code == "Mus") 0493 return QString("Musca"); 0494 if (code == "Nor") 0495 return QString("Norma"); 0496 if (code == "Oct") 0497 return QString("Octans"); 0498 if (code == "Oph") 0499 return QString("Ophiuchus"); 0500 if (code == "Ori") 0501 return QString("Orion"); 0502 if (code == "Pav") 0503 return QString("Pavo"); 0504 if (code == "Peg") 0505 return QString("Pegasus"); 0506 if (code == "Per") 0507 return QString("Perseus"); 0508 if (code == "Phe") 0509 return QString("Phoenix"); 0510 if (code == "Pic") 0511 return QString("Pictor"); 0512 if (code == "Psc") 0513 return QString("Pisces"); 0514 if (code == "PsA") 0515 return QString("Piscis Austrinus"); 0516 if (code == "Pup") 0517 return QString("Puppis"); 0518 if (code == "Pyx") 0519 return QString("Pyxis"); 0520 if (code == "Ret") 0521 return QString("Reticulum"); 0522 if (code == "Sge") 0523 return QString("Sagitta"); 0524 if (code == "Sgr") 0525 return QString("Sagittarius"); 0526 if (code == "Sco") 0527 return QString("Scorpius"); 0528 if (code == "Scl") 0529 return QString("Sculptor"); 0530 if (code == "Sct") 0531 return QString("Scutum"); 0532 if (code == "Ser") 0533 return QString("Serpens"); 0534 if (code == "Sex") 0535 return QString("Sextans"); 0536 if (code == "Tau") 0537 return QString("Taurus"); 0538 if (code == "Tel") 0539 return QString("Telescopium"); 0540 if (code == "Tri") 0541 return QString("Triangulum"); 0542 if (code == "TrA") 0543 return QString("Triangulum Australe"); 0544 if (code == "Tuc") 0545 return QString("Tucana"); 0546 if (code == "UMa") 0547 return QString("Ursa Major"); 0548 if (code == "UMi") 0549 return QString("Ursa Minor"); 0550 if (code == "Vel") 0551 return QString("Vela"); 0552 if (code == "Vir") 0553 return QString("Virgo"); 0554 if (code == "Vol") 0555 return QString("Volans"); 0556 if (code == "Vul") 0557 return QString("Vulpecula"); 0558 return code; 0559 } 0560 0561 QString constNameToAbbrev(const QString &fullName_) 0562 { 0563 QString fullName = fullName_.toLower(); 0564 if (fullName == "andromeda") 0565 return QString("And"); 0566 if (fullName == "antlia") 0567 return QString("Ant"); 0568 if (fullName == "apus") 0569 return QString("Aps"); 0570 if (fullName == "aquarius") 0571 return QString("Aqr"); 0572 if (fullName == "aquila") 0573 return QString("Aql"); 0574 if (fullName == "ara") 0575 return QString("Ara"); 0576 if (fullName == "aries") 0577 return QString("Ari"); 0578 if (fullName == "auriga") 0579 return QString("Aur"); 0580 if (fullName == "bootes") 0581 return QString("Boo"); 0582 if (fullName == "caelum") 0583 return QString("Cae"); 0584 if (fullName == "camelopardalis") 0585 return QString("Cam"); 0586 if (fullName == "cancer") 0587 return QString("Cnc"); 0588 if (fullName == "canes venatici") 0589 return QString("CVn"); 0590 if (fullName == "canis major") 0591 return QString("CMa"); 0592 if (fullName == "canis minor") 0593 return QString("CMi"); 0594 if (fullName == "capricornus") 0595 return QString("Cap"); 0596 if (fullName == "carina") 0597 return QString("Car"); 0598 if (fullName == "cassiopeia") 0599 return QString("Cas"); 0600 if (fullName == "centaurus") 0601 return QString("Cen"); 0602 if (fullName == "cepheus") 0603 return QString("Cep"); 0604 if (fullName == "cetus") 0605 return QString("Cet"); 0606 if (fullName == "chamaeleon") 0607 return QString("Cha"); 0608 if (fullName == "circinus") 0609 return QString("Cir"); 0610 if (fullName == "columba") 0611 return QString("Col"); 0612 if (fullName == "coma berenices") 0613 return QString("Com"); 0614 if (fullName == "corona australis") 0615 return QString("CrA"); 0616 if (fullName == "corona borealis") 0617 return QString("CrB"); 0618 if (fullName == "corvus") 0619 return QString("Crv"); 0620 if (fullName == "crater") 0621 return QString("Crt"); 0622 if (fullName == "crux") 0623 return QString("Cru"); 0624 if (fullName == "cygnus") 0625 return QString("Cyg"); 0626 if (fullName == "delphinus") 0627 return QString("Del"); 0628 if (fullName == "doradus") 0629 return QString("Dor"); 0630 if (fullName == "draco") 0631 return QString("Dra"); 0632 if (fullName == "equuleus") 0633 return QString("Equ"); 0634 if (fullName == "eridanus") 0635 return QString("Eri"); 0636 if (fullName == "fornax") 0637 return QString("For"); 0638 if (fullName == "gemini") 0639 return QString("Gem"); 0640 if (fullName == "grus") 0641 return QString("Gru"); 0642 if (fullName == "hercules") 0643 return QString("Her"); 0644 if (fullName == "horologium") 0645 return QString("Hor"); 0646 if (fullName == "hydra") 0647 return QString("Hya"); 0648 if (fullName == "hydrus") 0649 return QString("Hyi"); 0650 if (fullName == "indus") 0651 return QString("Ind"); 0652 if (fullName == "lacerta") 0653 return QString("Lac"); 0654 if (fullName == "leo") 0655 return QString("Leo"); 0656 if (fullName == "leo minor") 0657 return QString("LMi"); 0658 if (fullName == "lepus") 0659 return QString("Lep"); 0660 if (fullName == "libra") 0661 return QString("Lib"); 0662 if (fullName == "lupus") 0663 return QString("Lup"); 0664 if (fullName == "lynx") 0665 return QString("Lyn"); 0666 if (fullName == "lyra") 0667 return QString("Lyr"); 0668 if (fullName == "mensa") 0669 return QString("Men"); 0670 if (fullName == "microscopium") 0671 return QString("Mic"); 0672 if (fullName == "monoceros") 0673 return QString("Mon"); 0674 if (fullName == "musca") 0675 return QString("Mus"); 0676 if (fullName == "norma") 0677 return QString("Nor"); 0678 if (fullName == "octans") 0679 return QString("Oct"); 0680 if (fullName == "ophiuchus") 0681 return QString("Oph"); 0682 if (fullName == "orion") 0683 return QString("Ori"); 0684 if (fullName == "pavo") 0685 return QString("Pav"); 0686 if (fullName == "pegasus") 0687 return QString("Peg"); 0688 if (fullName == "perseus") 0689 return QString("Per"); 0690 if (fullName == "phoenix") 0691 return QString("Phe"); 0692 if (fullName == "pictor") 0693 return QString("Pic"); 0694 if (fullName == "pisces") 0695 return QString("Psc"); 0696 if (fullName == "piscis austrinus") 0697 return QString("PsA"); 0698 if (fullName == "puppis") 0699 return QString("Pup"); 0700 if (fullName == "pyxis") 0701 return QString("Pyx"); 0702 if (fullName == "reticulum") 0703 return QString("Ret"); 0704 if (fullName == "sagitta") 0705 return QString("Sge"); 0706 if (fullName == "sagittarius") 0707 return QString("Sgr"); 0708 if (fullName == "scorpius") 0709 return QString("Sco"); 0710 if (fullName == "sculptor") 0711 return QString("Scl"); 0712 if (fullName == "scutum") 0713 return QString("Sct"); 0714 if (fullName == "serpens") 0715 return QString("Ser"); 0716 if (fullName == "sextans") 0717 return QString("Sex"); 0718 if (fullName == "taurus") 0719 return QString("Tau"); 0720 if (fullName == "telescopium") 0721 return QString("Tel"); 0722 if (fullName == "triangulum") 0723 return QString("Tri"); 0724 if (fullName == "triangulum australe") 0725 return QString("TrA"); 0726 if (fullName == "tucana") 0727 return QString("Tuc"); 0728 if (fullName == "ursa major") 0729 return QString("UMa"); 0730 if (fullName == "ursa minor") 0731 return QString("UMi"); 0732 if (fullName == "vela") 0733 return QString("Vel"); 0734 if (fullName == "virgo") 0735 return QString("Vir"); 0736 if (fullName == "volans") 0737 return QString("Vol"); 0738 if (fullName == "vulpecula") 0739 return QString("Vul"); 0740 return fullName_; 0741 } 0742 0743 QString constGenetiveToAbbrev(const QString &genetive_) 0744 { 0745 QString genetive = genetive_.toLower(); 0746 if (genetive == "andromedae") 0747 return QString("And"); 0748 if (genetive == "antliae") 0749 return QString("Ant"); 0750 if (genetive == "apodis") 0751 return QString("Aps"); 0752 if (genetive == "aquarii") 0753 return QString("Aqr"); 0754 if (genetive == "aquilae") 0755 return QString("Aql"); 0756 if (genetive == "arae") 0757 return QString("Ara"); 0758 if (genetive == "arietis") 0759 return QString("Ari"); 0760 if (genetive == "aurigae") 0761 return QString("Aur"); 0762 if (genetive == "bootis") 0763 return QString("Boo"); 0764 if (genetive == "caeli") 0765 return QString("Cae"); 0766 if (genetive == "camelopardalis") 0767 return QString("Cam"); 0768 if (genetive == "cancri") 0769 return QString("Cnc"); 0770 if (genetive == "canum venaticorum") 0771 return QString("CVn"); 0772 if (genetive == "canis majoris") 0773 return QString("CMa"); 0774 if (genetive == "canis minoris") 0775 return QString("CMi"); 0776 if (genetive == "capricorni") 0777 return QString("Cap"); 0778 if (genetive == "carinae") 0779 return QString("Car"); 0780 if (genetive == "cassiopeiae") 0781 return QString("Cas"); 0782 if (genetive == "centauri") 0783 return QString("Cen"); 0784 if (genetive == "cephei") 0785 return QString("Cep"); 0786 if (genetive == "ceti") 0787 return QString("Cet"); 0788 if (genetive == "chamaeleontis") 0789 return QString("Cha"); 0790 if (genetive == "circini") 0791 return QString("Cir"); 0792 if (genetive == "columbae") 0793 return QString("Col"); 0794 if (genetive == "comae berenices") 0795 return QString("Com"); 0796 if (genetive == "coronae austrinae") 0797 return QString("CrA"); 0798 if (genetive == "coronae borealis") 0799 return QString("CrB"); 0800 if (genetive == "corvi") 0801 return QString("Crv"); 0802 if (genetive == "crateris") 0803 return QString("Crt"); 0804 if (genetive == "crucis") 0805 return QString("Cru"); 0806 if (genetive == "cygni") 0807 return QString("Cyg"); 0808 if (genetive == "delphini") 0809 return QString("Del"); 0810 if (genetive == "doradus") 0811 return QString("Dor"); 0812 if (genetive == "draconis") 0813 return QString("Dra"); 0814 if (genetive == "equulei") 0815 return QString("Equ"); 0816 if (genetive == "eridani") 0817 return QString("Eri"); 0818 if (genetive == "fornacis") 0819 return QString("For"); 0820 if (genetive == "geminorum") 0821 return QString("Gem"); 0822 if (genetive == "gruis") 0823 return QString("Gru"); 0824 if (genetive == "herculis") 0825 return QString("Her"); 0826 if (genetive == "horologii") 0827 return QString("Hor"); 0828 if (genetive == "hydrae") 0829 return QString("Hya"); 0830 if (genetive == "hydri") 0831 return QString("Hyi"); 0832 if (genetive == "indi") 0833 return QString("Ind"); 0834 if (genetive == "lacertae") 0835 return QString("Lac"); 0836 if (genetive == "leonis") 0837 return QString("Leo"); 0838 if (genetive == "leonis minoris") 0839 return QString("LMi"); 0840 if (genetive == "leporis") 0841 return QString("Lep"); 0842 if (genetive == "librae") 0843 return QString("Lib"); 0844 if (genetive == "lupi") 0845 return QString("Lup"); 0846 if (genetive == "lyncis") 0847 return QString("Lyn"); 0848 if (genetive == "lyrae") 0849 return QString("Lyr"); 0850 if (genetive == "mensae") 0851 return QString("Men"); 0852 if (genetive == "microscopii") 0853 return QString("Mic"); 0854 if (genetive == "monocerotis") 0855 return QString("Mon"); 0856 if (genetive == "muscae") 0857 return QString("Mus"); 0858 if (genetive == "normae") 0859 return QString("Nor"); 0860 if (genetive == "octantis") 0861 return QString("Oct"); 0862 if (genetive == "ophiuchi") 0863 return QString("Oph"); 0864 if (genetive == "orionis") 0865 return QString("Ori"); 0866 if (genetive == "pavonis") 0867 return QString("Pav"); 0868 if (genetive == "pegasi") 0869 return QString("Peg"); 0870 if (genetive == "persei") 0871 return QString("Per"); 0872 if (genetive == "phoenicis") 0873 return QString("Phe"); 0874 if (genetive == "pictoris") 0875 return QString("Pic"); 0876 if (genetive == "piscium") 0877 return QString("Psc"); 0878 if (genetive == "piscis austrini") 0879 return QString("PsA"); 0880 if (genetive == "puppis") 0881 return QString("Pup"); 0882 if (genetive == "pyxidis") 0883 return QString("Pyx"); 0884 if (genetive == "reticuli") 0885 return QString("Ret"); 0886 if (genetive == "sagittae") 0887 return QString("Sge"); 0888 if (genetive == "sagittarii") 0889 return QString("Sgr"); 0890 if (genetive == "scorpii") 0891 return QString("Sco"); 0892 if (genetive == "sculptoris") 0893 return QString("Scl"); 0894 if (genetive == "scuti") 0895 return QString("Sct"); 0896 if (genetive == "serpentis") 0897 return QString("Ser"); 0898 if (genetive == "sextantis") 0899 return QString("Sex"); 0900 if (genetive == "tauri") 0901 return QString("Tau"); 0902 if (genetive == "telescopii") 0903 return QString("Tel"); 0904 if (genetive == "trianguli") 0905 return QString("Tri"); 0906 if (genetive == "trianguli australis") 0907 return QString("TrA"); 0908 if (genetive == "tucanae") 0909 return QString("Tuc"); 0910 if (genetive == "ursae majoris") 0911 return QString("UMa"); 0912 if (genetive == "ursae minoris") 0913 return QString("UMi"); 0914 if (genetive == "velorum") 0915 return QString("Vel"); 0916 if (genetive == "virginis") 0917 return QString("Vir"); 0918 if (genetive == "volantis") 0919 return QString("Vol"); 0920 if (genetive == "vulpeculae") 0921 return QString("Vul"); 0922 return genetive_; 0923 } 0924 0925 QString Logging::_filename; 0926 0927 void Logging::UseFile() 0928 { 0929 if (_filename.isEmpty()) 0930 { 0931 QDir dir; 0932 QString path = 0933 QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 0934 .filePath("logs" + QDir::separator() + QDateTime::currentDateTime().toString("yyyy-MM-dd")); 0935 dir.mkpath(path); 0936 QString name = 0937 "log_" + QDateTime::currentDateTime().toString("HH-mm-ss") + ".txt"; 0938 _filename = path + QDir::separator() + name; 0939 0940 // Clear file contents 0941 QFile file(_filename); 0942 file.open(QFile::WriteOnly); 0943 file.close(); 0944 } 0945 0946 qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} " 0947 "%{if-debug}DEBG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{" 0948 "endif}%{if-critical}CRIT%{endif}%{if-fatal}FATL%{endif}] " 0949 "%{if-category}[%{category}]%{endif} - %{message}"); 0950 qInstallMessageHandler(File); 0951 } 0952 0953 void Logging::File(QtMsgType type, const QMessageLogContext &context, const QString &msg) 0954 { 0955 QFile file(_filename); 0956 if (file.open(QFile::Append | QIODevice::Text)) 0957 { 0958 QTextStream stream(&file); 0959 Write(stream, type, context, msg); 0960 } 0961 } 0962 0963 void Logging::UseStdout() 0964 { 0965 qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} " 0966 "%{if-debug}DEBG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{" 0967 "endif}%{if-critical}CRIT%{endif}%{if-fatal}FATL%{endif}] " 0968 "%{if-category}[%{category}]%{endif} - %{message}"); 0969 qInstallMessageHandler(Stdout); 0970 } 0971 0972 void Logging::Stdout(QtMsgType type, const QMessageLogContext &context, 0973 const QString &msg) 0974 { 0975 QTextStream stream(stdout, QIODevice::WriteOnly); 0976 Write(stream, type, context, msg); 0977 } 0978 0979 void Logging::UseStderr() 0980 { 0981 qInstallMessageHandler(Stderr); 0982 } 0983 0984 void Logging::Stderr(QtMsgType type, const QMessageLogContext &context, 0985 const QString &msg) 0986 { 0987 QTextStream stream(stderr, QIODevice::WriteOnly); 0988 Write(stream, type, context, msg); 0989 } 0990 0991 void Logging::Write(QTextStream &stream, QtMsgType type, 0992 const QMessageLogContext &context, const QString &msg) 0993 { 0994 stream << QDateTime::currentDateTime().toString("[yyyy-MM-ddThh:mm:ss.zzz t "); 0995 0996 switch (type) 0997 { 0998 case QtInfoMsg: 0999 stream << "INFO ]"; 1000 break; 1001 case QtDebugMsg: 1002 stream << "DEBG ]"; 1003 break; 1004 case QtWarningMsg: 1005 stream << "WARN ]"; 1006 break; 1007 case QtCriticalMsg: 1008 stream << "CRIT ]"; 1009 break; 1010 case QtFatalMsg: 1011 stream << "FATL ]"; 1012 break; 1013 default: 1014 stream << "UNKN ]"; 1015 } 1016 1017 stream << "[" << qSetFieldWidth(30) << context.category << qSetFieldWidth(0) 1018 << "] - "; 1019 stream << msg << '\n'; 1020 stream.flush(); 1021 //stream << qFormatLogMessage(type, context, msg) << Qt::endl; 1022 } 1023 1024 void Logging::UseDefault() 1025 { 1026 qInstallMessageHandler(nullptr); 1027 } 1028 1029 void Logging::Disable() 1030 { 1031 qInstallMessageHandler(Disabled); 1032 } 1033 1034 void Logging::Disabled(QtMsgType, const QMessageLogContext &, const QString &) {} 1035 1036 void Logging::SyncFilterRules() 1037 { 1038 // QString rules = QString("org.kde.kstars.ekos.debug=%1\n" 1039 // "org.kde.kstars.indi.debug=%2\n" 1040 // "org.kde.kstars.fits.debug=%3\n" 1041 // "org.kde.kstars.ekos.capture.debug=%4\n" 1042 // "org.kde.kstars.ekos.focus.debug=%5\n" 1043 // "org.kde.kstars.ekos.guide.debug=%6\n" 1044 // "org.kde.kstars.ekos.align.debug=%7\n" 1045 // "org.kde.kstars.ekos.mount.debug=%8\n" 1046 // "org.kde.kstars.ekos.scheduler.debug=%9\n").arg( 1047 // Options::verboseLogging() ? "true" : "false", 1048 // Options::iNDILogging() ? "true" : "false", 1049 // Options::fITSLogging() ? "true" : "false", 1050 // Options::captureLogging() ? "true" : "false", 1051 // Options::focusLogging() ? "true" : "false", 1052 // Options::guideLogging() ? "true" : "false", 1053 // Options::alignmentLogging() ? "true" : "false", 1054 // Options::mountLogging() ? "true" : "false", 1055 // Options::schedulerLogging() ? "true" : "false") 1056 // .append(QString("org.kde.kstars.ekos.observatory.debug=%2\n" 1057 // "org.kde.kstars.debug=%1").arg( 1058 // Options::verboseLogging() ? "true" : "false", 1059 // Options::observatoryLogging() ? "true" : "false")); 1060 1061 QStringList rules; 1062 1063 rules << "org.kde.kstars.ekos.debug" 1064 << (Options::verboseLogging() ? "true" : "false"); 1065 rules << "org.kde.kstars.indi.debug" << (Options::iNDILogging() ? "true" : "false"); 1066 rules << "org.kde.kstars.fits.debug" << (Options::fITSLogging() ? "true" : "false"); 1067 rules << "org.kde.kstars.ekos.capture.debug" 1068 << (Options::captureLogging() ? "true" : "false"); 1069 rules << "org.kde.kstars.ekos.focus.debug" 1070 << (Options::focusLogging() ? "true" : "false"); 1071 rules << "org.kde.kstars.ekos.guide.debug" 1072 << (Options::guideLogging() ? "true" : "false"); 1073 rules << "org.kde.kstars.ekos.align.debug" 1074 << (Options::alignmentLogging() ? "true" : "false"); 1075 rules << "org.kde.kstars.ekos.mount.debug" 1076 << (Options::mountLogging() ? "true" : "false"); 1077 rules << "org.kde.kstars.ekos.scheduler.debug" 1078 << (Options::schedulerLogging() ? "true" : "false"); 1079 rules << "org.kde.kstars.ekos.observatory.debug" 1080 << (Options::observatoryLogging() ? "true" : "false"); 1081 rules << "org.kde.kstars.debug" << (Options::verboseLogging() ? "true" : "false"); 1082 1083 QString formattedRules; 1084 for (int i = 0; i < rules.size(); i += 2) 1085 formattedRules.append(QString("%1=%2\n").arg(rules[i], rules[i + 1])); 1086 1087 QLoggingCategory::setFilterRules(formattedRules); 1088 } 1089 1090 /** 1091 This method provides a centralized location for the default paths to important external files used in the Options 1092 on different operating systems. Note that on OS X, if the user builds the app without indi, astrometry, and xplanet internally 1093 then the options below will be used. If the user drags the app from a dmg and has to install the KStars data directory, 1094 then most of these paths will be overwritten since it is preferred to use the internal versions. 1095 **/ 1096 1097 QString getDefaultPath(const QString &option) 1098 { 1099 // We support running within Snaps, Flatpaks, and AppImage 1100 // The path should accomodate the differences between the different 1101 // packaging solutions 1102 QString snap = QProcessEnvironment::systemEnvironment().value("SNAP"); 1103 QString flat = QProcessEnvironment::systemEnvironment().value("FLATPAK_DEST"); 1104 QString appimg = QProcessEnvironment::systemEnvironment().value("APPDIR"); 1105 1106 // User prefix is the primary mounting point 1107 QString userPrefix = "/usr"; 1108 // By default /usr is the prefix 1109 QString prefix = userPrefix; 1110 // Detect if we are within an App Image 1111 if (QProcessEnvironment::systemEnvironment().value("APPIMAGE").isEmpty() == false && 1112 appimg.isEmpty() == false) 1113 prefix = appimg + userPrefix; 1114 else if (flat.isEmpty() == false) 1115 // Detect if we are within a Flatpak 1116 prefix = flat; 1117 // Detect if we are within a snap 1118 else if (snap.isEmpty() == false) 1119 prefix = snap + userPrefix; 1120 1121 if (option == "fitsDir") 1122 { 1123 return QDir::toNativeSeparators(QDir::homePath()); 1124 } 1125 else if (option == "indiServer") 1126 { 1127 #if defined(INDI_PREFIX) 1128 return QString(INDI_PREFIX "/bin/indiserver"); 1129 #elif defined(Q_OS_OSX) 1130 return "/usr/local/bin/indiserver"; 1131 #endif 1132 return prefix + "/bin/indiserver"; 1133 } 1134 else if (option == "PlaceholderFormat") 1135 { 1136 #if defined(Q_OS_WIN) 1137 return "\\%t\\%T\\%F\\%t_%T_%F"; 1138 #else 1139 return "/%t/%T/%F/%t_%T_%F"; 1140 #endif 1141 } 1142 else if (option == "INDIHubAgent") 1143 { 1144 #if defined(INDI_PREFIX) 1145 return QString(INDI_PREFIX "/bin/indihub-agent"); 1146 #elif defined(Q_OS_OSX) 1147 return "/usr/local/bin/indihub-agent"; 1148 #endif 1149 return prefix + "/bin/indihub-agent"; 1150 } 1151 else if (option == "indiDriversDir") 1152 { 1153 #if defined(INDI_PREFIX) 1154 return QString(INDI_PREFIX "/share/indi"); 1155 #elif defined(Q_OS_OSX) 1156 return "/usr/local/share/indi"; 1157 #elif defined(Q_OS_LINUX) 1158 return prefix + "/share/indi"; 1159 #else 1160 return QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, "indi", 1161 QStandardPaths::LocateDirectory); 1162 #endif 1163 } 1164 else if (option == "AstrometrySolverBinary") 1165 { 1166 #if defined(ASTROMETRY_PREFIX) 1167 return QString(ASTROMETRY_PREFIX "/bin/solve-field"); 1168 #elif defined(Q_OS_OSX) 1169 return "/usr/local/bin/solve-field"; 1170 #elif defined(Q_OS_WIN) 1171 return QDir::homePath() + 1172 "/AppData/Local/cygwin_ansvr/lib/astrometry/bin/solve-field.exe"; 1173 #endif 1174 return prefix + "/bin/solve-field"; 1175 } 1176 else if (option == "WatneyBinary") 1177 { 1178 #if defined(ASTROMETRY_PREFIX) 1179 return QString(ASTROMETRY_PREFIX "/opt/watney/watney-solve"); 1180 #elif defined(Q_OS_OSX) 1181 return "/usr/local/bin/watney-solve"; 1182 #elif defined(Q_OS_WIN) 1183 return "C:/watney/watney-solve.exe"; 1184 #endif 1185 return prefix + "/opt/watney/watney-solve"; 1186 } 1187 else if (option == "SextractorBinary") 1188 { 1189 #if defined(SEXTRACTOR_PREFIX) 1190 return QString(SEXTRACTOR_PREFIX "/bin/sextractor"); 1191 #elif defined(Q_OS_OSX) 1192 return "/usr/local/bin/sex"; 1193 #endif 1194 return prefix + "/bin/sextractor"; 1195 } 1196 else if (option == "AstrometryWCSInfo") 1197 { 1198 #if defined(ASTROMETRY_PREFIX) 1199 return QString(ASTROMETRY_PREFIX "/bin/wcsinfo"); 1200 #elif defined(Q_OS_OSX) 1201 return "/usr/local/bin/wcsinfo"; 1202 #elif defined(Q_OS_WIN) 1203 return QDir::homePath() + 1204 "/AppData/Local/cygwin_ansvr/lib/astrometry/bin/wcsinfo.exe"; 1205 #endif 1206 return prefix + "/bin/wcsinfo"; 1207 } 1208 else if (option == "AstrometryConfFile") 1209 { 1210 #if defined(ASTROMETRY_CONF_IN_PREFIX) && defined(ASTROMETRY_PREFIX) 1211 return QString(ASTROMETRY_PREFIX "/etc/astrometry.cfg"); 1212 #elif defined(Q_OS_OSX) 1213 return "/usr/local/etc/astrometry.cfg"; 1214 #elif defined(Q_OS_WIN) 1215 return QDir::homePath() + 1216 "/AppData/Local/cygwin_ansvr/etc/astrometry/backend.cfg"; 1217 #endif 1218 // We move /usr 1219 prefix.remove(userPrefix); 1220 return prefix + "/etc/astrometry.cfg"; 1221 } 1222 else if (option == "AstrometryIndexFileLocation") 1223 { 1224 #if defined(ASTROMETRY_PREFIX) 1225 return QString(ASTROMETRY_PREFIX "/share/astrometry"); 1226 #elif defined(Q_OS_OSX) 1227 return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 1228 .filePath("Astrometry/"); 1229 #endif 1230 return prefix + "/share/astrometry/"; 1231 } 1232 else if (option == "AstrometryLogFilepath") 1233 { 1234 return QDir::tempPath() + "/astrometryLog.txt"; 1235 } 1236 else if (option == "XplanetPath") 1237 { 1238 #if defined(XPLANET_PREFIX) 1239 return QString(XPLANET_PREFIX "/bin/xplanet"); 1240 #elif defined(Q_OS_OSX) 1241 return "/usr/local/bin/xplanet"; 1242 #endif 1243 return prefix + "/bin/xplanet"; 1244 } 1245 else if (option == "ASTAP") 1246 { 1247 #if defined(Q_OS_OSX) 1248 return "/Applications/ASTAP.app/Contents/MacOS/astap"; 1249 #elif defined(Q_OS_WIN) 1250 return "C:/Program Files/astap/astap.exe"; 1251 #endif 1252 return "/opt/astap/astap"; 1253 } 1254 1255 return QString(); 1256 } 1257 1258 QStringList getAstrometryDefaultIndexFolderPaths() 1259 { 1260 QStringList folderPaths; 1261 const QString confDir = 1262 QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 1263 .filePath(QLatin1String("astrometry")); 1264 folderPaths << confDir; 1265 // Check if directory already exists, if it doesn't create one 1266 QDir writableDir(confDir); 1267 if (writableDir.exists() == false) 1268 { 1269 if (writableDir.mkdir(confDir) == false) 1270 { 1271 qCCritical(KSTARS) << "Failed to create local astrometry directory"; 1272 folderPaths.clear(); 1273 } 1274 } 1275 1276 #ifdef HAVE_STELLARSOLVER 1277 folderPaths.append(StellarSolver::getDefaultIndexFolderPaths()); 1278 #endif 1279 return folderPaths; 1280 } 1281 1282 #if defined(Q_OS_OSX) 1283 //Note that this will copy and will not overwrite, so that the user's changes in the files are preserved. 1284 void copyResourcesFolderFromAppBundle(QString folder) 1285 { 1286 QString folderLocation = QStandardPaths::locate( 1287 QStandardPaths::GenericDataLocation, folder, QStandardPaths::LocateDirectory); 1288 QDir folderSourceDir; 1289 if (folder == "kstars") 1290 folderSourceDir = 1291 QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars") 1292 .absolutePath(); 1293 else 1294 folderSourceDir = 1295 QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + folder) 1296 .absolutePath(); 1297 if (folderSourceDir.exists()) 1298 { 1299 folderLocation = 1300 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + '/' + 1301 folder; 1302 QDir writableDir; 1303 writableDir.mkdir(folderLocation); 1304 copyRecursively(folderSourceDir.absolutePath(), folderLocation); 1305 } 1306 } 1307 1308 bool setupMacKStarsIfNeeded() // This method will return false if the KStars data directory doesn't exist when it's done 1309 { 1310 //This will copy the locale folder, the notifications folder, and the sounds folder and any missing files in them to Application Support if needed. 1311 copyResourcesFolderFromAppBundle("locale"); 1312 copyResourcesFolderFromAppBundle("knotifications5"); 1313 copyResourcesFolderFromAppBundle("sounds"); 1314 1315 //This will copy the KStars data directory 1316 copyResourcesFolderFromAppBundle("kstars"); 1317 copyResourcesFolderFromAppBundle("kstars/xplanet"); 1318 1319 if (Options::kStarsFirstRun()) 1320 { 1321 //This sets some important OS X options. 1322 Options::setIndiServerIsInternal(true); 1323 Options::setIndiServer("*Internal INDI Server*"); 1324 Options::setIndiDriversAreInternal(true); 1325 Options::setIndiDriversDir("*Internal INDI Drivers*"); 1326 Options::setXplanetIsInternal(true); 1327 Options::setXplanetPath("*Internal XPlanet*"); 1328 } 1329 1330 QString dataLocation = QStandardPaths::locate( 1331 QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory); 1332 if (dataLocation.isEmpty()) //If there is no kstars user data directory 1333 return false; 1334 1335 return true; 1336 } 1337 1338 bool configureAstrometry() 1339 { 1340 QStringList astrometryDataDirs = getAstrometryDataDirs(); 1341 if (astrometryDataDirs.count() == 0) 1342 return false; 1343 QString defaultAstrometryDataDir = getDefaultPath("AstrometryIndexFileLocation"); 1344 if (astrometryDataDirs.contains("IndexFileLocationNotYetSet")) 1345 replaceIndexFileNotYetSet(); 1346 if (QDir(defaultAstrometryDataDir).exists() == false) 1347 { 1348 if (KMessageBox::warningYesNo( 1349 nullptr, 1350 i18n("The selected Astrometry Index File Location:\n %1 \n does not " 1351 "exist. Do you want to make the directory?", 1352 defaultAstrometryDataDir), 1353 i18n("Make Astrometry Index File Directory?")) == KMessageBox::Yes) 1354 { 1355 if (QDir(defaultAstrometryDataDir).mkdir(defaultAstrometryDataDir)) 1356 { 1357 KSNotification::info( 1358 i18n("The Default Astrometry Index File Location was created.")); 1359 } 1360 else 1361 { 1362 KSNotification::sorry( 1363 i18n("The Default Astrometry Index File Directory does not exist and " 1364 "was not able to be created.")); 1365 } 1366 } 1367 else 1368 { 1369 return false; 1370 } 1371 } 1372 1373 return true; 1374 } 1375 1376 bool replaceIndexFileNotYetSet() 1377 { 1378 QString confPath = KSUtils::getAstrometryConfFilePath(); 1379 1380 QFile confFile(confPath); 1381 QString contents; 1382 if (confFile.open(QIODevice::ReadOnly) == false) 1383 { 1384 KSNotification::error(i18n("Astrometry Configuration File Read Error.")); 1385 return false; 1386 } 1387 else 1388 { 1389 QByteArray fileContent = confFile.readAll(); 1390 confFile.close(); 1391 QString contents = QString::fromLatin1(fileContent); 1392 contents.replace("IndexFileLocationNotYetSet", 1393 getDefaultPath("AstrometryIndexFileLocation")); 1394 1395 if (confFile.open(QIODevice::WriteOnly) == false) 1396 { 1397 KSNotification::error( 1398 i18n("Internal Astrometry Configuration File Write Error.")); 1399 return false; 1400 } 1401 else 1402 { 1403 QTextStream out(&confFile); 1404 out << contents; 1405 confFile.close(); 1406 } 1407 } 1408 return true; 1409 } 1410 1411 bool copyRecursively(QString sourceFolder, QString destFolder) 1412 { 1413 QDir sourceDir(sourceFolder); 1414 1415 if (!sourceDir.exists()) 1416 return false; 1417 1418 QDir destDir(destFolder); 1419 if (!destDir.exists()) 1420 destDir.mkdir(destFolder); 1421 1422 QStringList files = sourceDir.entryList(QDir::Files); 1423 for (int i = 0; i < files.count(); i++) 1424 { 1425 QString srcName = sourceFolder + QDir::separator() + files[i]; 1426 QString destName = destFolder + QDir::separator() + files[i]; 1427 QFile::copy(srcName, destName); //Note this does not overwrite files 1428 } 1429 1430 files.clear(); 1431 files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); 1432 for (int i = 0; i < files.count(); i++) 1433 { 1434 QString srcName = sourceFolder + QDir::separator() + files[i]; 1435 QString destName = destFolder + QDir::separator() + files[i]; 1436 copyRecursively(srcName, destName); 1437 } 1438 1439 return true; 1440 } 1441 #endif 1442 1443 QString getAstrometryConfFilePath() 1444 { 1445 return Options::astrometryConfFile(); 1446 } 1447 1448 QStringList getAstrometryDataDirs() 1449 { 1450 QStringList optionsDataDirs = Options::astrometryIndexFolderList(); 1451 1452 bool updated = false; 1453 1454 // Cleaning up the list of directories in options. 1455 for (int dir = 0; dir < optionsDataDirs.count(); dir++) 1456 { 1457 QString optionsDataDirName = optionsDataDirs.at(dir); 1458 QDir optionsDataDir(optionsDataDirName); 1459 if (optionsDataDir.exists()) 1460 { 1461 //This will replace directory names that aren't the absolute path 1462 if (optionsDataDir.absolutePath() != optionsDataDirName) 1463 { 1464 optionsDataDirs.replace(dir, optionsDataDir.absolutePath()); 1465 updated = true; 1466 } 1467 } 1468 else 1469 { 1470 //This removes directories that do not exist from the list. 1471 optionsDataDirs.removeAt(dir); 1472 dir--; 1473 updated = true; 1474 } 1475 } 1476 1477 //This will load the conf file if it exists 1478 QFile confFile(KSUtils::getAstrometryConfFilePath()); 1479 if (confFile.open(QIODevice::ReadOnly)) 1480 { 1481 QStringList confDataDirs; 1482 QTextStream in(&confFile); 1483 QString line; 1484 1485 //This will find the index file paths in the conf file 1486 while (!in.atEnd()) 1487 { 1488 line = in.readLine(); 1489 if (line.isEmpty() || line.startsWith('#')) 1490 continue; 1491 1492 line = line.trimmed(); 1493 if (line.startsWith(QLatin1String("add_path"))) 1494 { 1495 confDataDirs << line.mid(9).trimmed(); 1496 } 1497 } 1498 1499 //This will search through the paths and compare them to the index folder list 1500 //It will add them if they aren't in there. 1501 for (QString astrometryDataDirName : confDataDirs) 1502 { 1503 QDir astrometryDataDir(astrometryDataDirName); 1504 //This rejects any that do not exist 1505 if (!astrometryDataDir.exists()) 1506 continue; 1507 QString astrometryDataDirPath = astrometryDataDir.absolutePath(); 1508 if (!optionsDataDirs.contains(astrometryDataDirPath)) 1509 { 1510 optionsDataDirs.append(astrometryDataDirPath); 1511 updated = true; 1512 } 1513 } 1514 } 1515 1516 //This will remove any duplicate entries. 1517 if (optionsDataDirs.removeDuplicates() != 0) 1518 updated = true; 1519 1520 //This updates the list in Options if it changed. 1521 if (updated) 1522 Options::setAstrometryIndexFolderList(optionsDataDirs); 1523 1524 return optionsDataDirs; 1525 } 1526 1527 bool addAstrometryDataDir(const QString &dataDir) 1528 { 1529 //This will need to be fixed! 1530 //if(Options::astrometryIndexFileLocation() != dataDir) 1531 // Options::setAstrometryIndexFileLocation(dataDir); 1532 1533 QString confPath = KSUtils::getAstrometryConfFilePath(); 1534 QStringList astrometryDataDirs = getAstrometryDataDirs(); 1535 1536 QFile confFile(confPath); 1537 QString contents; 1538 if (confFile.open(QIODevice::ReadOnly) == false) 1539 { 1540 KSNotification::error(i18n("Astrometry Configuration File Read Error.")); 1541 return false; 1542 } 1543 else 1544 { 1545 QTextStream in(&confFile); 1546 QString line; 1547 bool foundSpot = false; 1548 while (!in.atEnd()) 1549 { 1550 line = in.readLine(); 1551 1552 if (line.trimmed().startsWith(QLatin1String("add_path"))) 1553 { 1554 if (!foundSpot) 1555 { 1556 foundSpot = true; 1557 for (QString astrometryDataDir : astrometryDataDirs) 1558 contents += "add_path " + astrometryDataDir + '\n'; 1559 contents += "add_path " + dataDir + '\n'; 1560 } 1561 else 1562 { 1563 //Do not keep adding the other add_paths because they just got added in the seciton above. 1564 } 1565 } 1566 else 1567 { 1568 contents += line + '\n'; 1569 } 1570 } 1571 if (!foundSpot) 1572 { 1573 for (QString astrometryDataDir : astrometryDataDirs) 1574 contents += "add_path " + astrometryDataDir + '\n'; 1575 contents += "add_path " + dataDir + '\n'; 1576 } 1577 1578 confFile.close(); 1579 1580 if (confFile.open(QIODevice::WriteOnly) == false) 1581 { 1582 KSNotification::error( 1583 i18n("Internal Astrometry Configuration File Write Error.")); 1584 return false; 1585 } 1586 else 1587 { 1588 QTextStream out(&confFile); 1589 out << contents; 1590 confFile.close(); 1591 } 1592 } 1593 return true; 1594 } 1595 1596 bool removeAstrometryDataDir(const QString &dataDir) 1597 { 1598 QString confPath = KSUtils::getAstrometryConfFilePath(); 1599 QStringList astrometryDataDirs = getAstrometryDataDirs(); 1600 1601 QFile confFile(confPath); 1602 QString contents; 1603 if (confFile.open(QIODevice::ReadOnly) == false) 1604 { 1605 KSNotification::error(i18n("Astrometry Configuration File Read Error.")); 1606 return false; 1607 } 1608 else 1609 { 1610 QTextStream in(&confFile); 1611 QString line; 1612 while (!in.atEnd()) 1613 { 1614 line = in.readLine(); 1615 if (line.mid(9).trimmed() != dataDir) 1616 { 1617 contents += line + '\n'; 1618 } 1619 } 1620 confFile.close(); 1621 1622 if (confFile.open(QIODevice::WriteOnly) == false) 1623 { 1624 KSNotification::error( 1625 i18n("Internal Astrometry Configuration File Write Error.")); 1626 return false; 1627 } 1628 else 1629 { 1630 QTextStream out(&confFile); 1631 out << contents; 1632 confFile.close(); 1633 } 1634 } 1635 return true; 1636 } 1637 1638 QByteArray getJPLQueryString(const QByteArray &kind, const QByteArray &dataFields, 1639 const QVector<JPLFilter> &filters) 1640 { 1641 /* For example: 1642 https://ssd-api.jpl.nasa.gov/sbdb_query.api?fields=full_name,neo,H,G,diameter,extent,albedo,rot_per,orbit_id,epoch.mjd,e,a,q,i,om,w,ma,per.y,moid,class&full-prec=false&sb-cdata=%7B%22AND%22:%5B%22H%7CLT%7C10%22%5D%7D&sb-kind=a&sb-ns=n&www=1 1643 */ 1644 1645 QByteArray query("sb-kind=" + kind + "&full-prec=true"); 1646 1647 // Apply filters: 1648 if (filters.size() > 0) 1649 { 1650 QByteArray filter_string("{\"AND\":["); 1651 for (const auto &item : filters) 1652 { 1653 filter_string += "\"" + item.item + "|" + item.op + "|" + item.value + "\","; 1654 } 1655 1656 filter_string.chop(1); 1657 filter_string += "]}"; 1658 1659 query += "&sb-cdata=" + filter_string; 1660 } 1661 1662 // Apply query data fields... 1663 query += "&fields=" + dataFields; 1664 1665 return query; 1666 } 1667 1668 bool RAWToJPEG(const QString &rawImage, const QString &output, QString &errorMessage) 1669 { 1670 #ifndef HAVE_LIBRAW 1671 errorMessage = i18n("Unable to find dcraw and cjpeg. Please install the required " 1672 "tools to convert CR2/NEF to JPEG."); 1673 return false; 1674 #else 1675 int ret = 0; 1676 // Creation of image processing object 1677 LibRaw RawProcessor; 1678 1679 // Let us open the file 1680 if ((ret = RawProcessor.open_file(rawImage.toLatin1().data())) != LIBRAW_SUCCESS) 1681 { 1682 errorMessage = i18n("Cannot open %1: %2", rawImage, libraw_strerror(ret)); 1683 RawProcessor.recycle(); 1684 return false; 1685 } 1686 1687 // Let us unpack the thumbnail 1688 if ((ret = RawProcessor.unpack_thumb()) != LIBRAW_SUCCESS) 1689 { 1690 errorMessage = i18n("Cannot unpack_thumb %1: %2", rawImage, libraw_strerror(ret)); 1691 RawProcessor.recycle(); 1692 return false; 1693 } 1694 else 1695 // We have successfully unpacked the thumbnail, now let us write it to a file 1696 { 1697 //snprintf(thumbfn,sizeof(thumbfn),"%s.%s",av[i],T.tformat == LIBRAW_THUMBNAIL_JPEG ? "thumb.jpg" : "thumb.ppm"); 1698 if (LIBRAW_SUCCESS != 1699 (ret = RawProcessor.dcraw_thumb_writer(output.toLatin1().data()))) 1700 { 1701 errorMessage = i18n("Cannot write %s %1: %2", output, libraw_strerror(ret)); 1702 RawProcessor.recycle(); 1703 return false; 1704 } 1705 } 1706 return true; 1707 #endif 1708 } 1709 1710 double getAvailableRAM() 1711 { 1712 #if defined(Q_OS_OSX) 1713 int mib[] = { CTL_HW, HW_MEMSIZE }; 1714 size_t length; 1715 length = sizeof(int64_t); 1716 int64_t RAMcheck; 1717 if (sysctl(mib, 2, &RAMcheck, &length, NULL, 0)) 1718 return false; // On Error 1719 //Until I can figure out how to get free RAM on Mac 1720 return RAMcheck; 1721 #elif defined(Q_OS_LINUX) 1722 QProcess p; 1723 p.start("awk", QStringList() << "/MemAvailable/ { print $2 }" 1724 << "/proc/meminfo"); 1725 p.waitForFinished(); 1726 QString memory = p.readAllStandardOutput(); 1727 p.close(); 1728 //kB to bytes 1729 return (memory.toLong() * 1024.0); 1730 #elif defined(Q_OS_WIN32) 1731 MEMORYSTATUSEX memory_status; 1732 ZeroMemory(&memory_status, sizeof(MEMORYSTATUSEX)); 1733 memory_status.dwLength = sizeof(MEMORYSTATUSEX); 1734 if (GlobalMemoryStatusEx(&memory_status)) 1735 { 1736 return memory_status.ullAvailPhys; 1737 } 1738 else 1739 { 1740 return 0; 1741 } 1742 #endif 1743 return 0; 1744 } 1745 1746 JPLParser::JPLParser(const QString &path) 1747 { 1748 QFile jpl_file(path); 1749 if (!jpl_file.open(QIODevice::ReadOnly)) 1750 { 1751 throw std::runtime_error("Could not open file."); 1752 } 1753 1754 const auto &ast_json = QJsonDocument::fromJson(jpl_file.readAll()); 1755 const auto &fields = ast_json["fields"].toArray(); 1756 m_data = ast_json["data"].toArray(); 1757 1758 { 1759 int i = 0; 1760 for (const auto &field : fields) 1761 { 1762 m_field_map[field.toString()] = i++; 1763 } 1764 } 1765 } 1766 1767 MPCParser::MPCParser(const QString &path) 1768 { 1769 QFile mpc_file(path); 1770 if (!mpc_file.open(QIODevice::ReadOnly)) 1771 { 1772 throw std::runtime_error("Could not open file."); 1773 } 1774 1775 gzFile file = gzopen(path.toLatin1().constData(), "r"); 1776 if (file) 1777 { 1778 1779 QByteArray data; 1780 const uint32_t len = 32768; 1781 1782 while (!gzeof(file)) 1783 { 1784 char buffer[len] = {0}; 1785 int bytes_read = gzread(file, buffer, len - 1); 1786 if (bytes_read < 0) 1787 break; 1788 buffer[bytes_read] = 0; 1789 data.append(buffer); 1790 } 1791 1792 gzclose(file); 1793 const auto &ast_json = QJsonDocument::fromJson(data); 1794 m_data = ast_json.array(); 1795 } 1796 else 1797 qCritical(KSTARS) << "Failed to read MPC comets data file" << path; 1798 } 1799 1800 void setGlobalSettings(const QVariantMap &settings) 1801 { 1802 for (auto &key : settings.keys()) 1803 { 1804 auto property = key; 1805 // Anything starting with kcfg_ must be processed to remove the 1806 // prefix and ensure first letter is lower case. 1807 if (property.startsWith("kcfg_")) 1808 { 1809 property.remove("kcfg_"); 1810 property.replace(0, 1, property.at(0).toLower()); 1811 } 1812 Options::self()->setProperty(property.toLatin1(), settings[key]); 1813 } 1814 1815 Options::self()->save(); 1816 } 1817 1818 QString sanitize(const QString &text) 1819 { 1820 static const QRegularExpression re1("\\s|/|\\(|\\)|:|\\*|\\+|~|\"" ); 1821 static const QRegularExpression re2("_{2,}"); 1822 static const QRegularExpression re3("_$"); 1823 1824 QString sanitized = text; 1825 if (sanitized != i18n("unnamed")) 1826 { 1827 // Remove illegal characters that can be problematic 1828 sanitized = sanitized.replace(re1, "_" ) 1829 // Remove any two or more __ 1830 .replace( re2, "_") 1831 // Remove any _ at the end 1832 .replace( re3, ""); 1833 } 1834 return sanitized; 1835 } 1836 1837 double rangePA(double pa) 1838 { 1839 while (pa > 180) 1840 pa -= 360; 1841 while (pa < -180) 1842 pa += 360; 1843 return pa; 1844 } 1845 1846 double range360(double r) 1847 { 1848 double res = r; 1849 while (res < 0.00) 1850 res += 360.00; 1851 while (res > 359.99) // uniqueness of angle (360 = 0) 1852 res -= 360.00; 1853 return res; 1854 } 1855 1856 double rotationToPositionAngle(double value) 1857 { 1858 double pa = value + 180; 1859 while (pa > 180) 1860 pa -= 360; 1861 while (pa < -180) 1862 pa += 360; 1863 return pa; 1864 } 1865 1866 double positionAngleToRotation(double value) 1867 { 1868 double rotation = value - 180; 1869 while (rotation > 180) 1870 rotation -= 360; 1871 while (rotation < -180) 1872 rotation += 360; 1873 return rotation; 1874 } 1875 1876 1877 } // namespace KSUtils