File indexing completed on 2024-04-14 14:12:12

0001 /*
0002     SPDX-FileCopyrightText: 2016 Akarsh Simha <akarsh.simha@kdemail.net>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 /* Project Includes */
0008 #include "test_skypoint.h"
0009 #include "ksnumbers.h"
0010 #include "time/kstarsdatetime.h"
0011 #include "auxiliary/dms.h"
0012 #include "Options.h"
0013 #include <libnova/libnova.h>
0014 TestSkyPoint::TestSkyPoint() : QObject()
0015 {
0016     useRelativistic = Options::useRelativistic();
0017 }
0018 
0019 TestSkyPoint::~TestSkyPoint()
0020 {
0021     Options::setUseRelativistic(useRelativistic);
0022 }
0023 
0024 void TestSkyPoint::testPrecession()
0025 {
0026     /*
0027      * NOTE: These test cases were picked randomly and generated using
0028      * https://ned.ipac.caltech.edu/forms/calculator.html
0029      */
0030 
0031     auto verify = [](const SkyPoint & p, const double targetRA, const double targetDec, const double tolerance)
0032     {
0033         qDebug() << p.ra().toHMSString() << " " << p.dec().toDMSString();
0034         QVERIFY(fabs(p.ra().Degrees() - targetRA) < tolerance * 15.);
0035         QVERIFY(fabs(p.dec().Degrees() - targetDec) < tolerance);
0036     };
0037 
0038     /* Precession of equinoxes within FK5 system */
0039 
0040     // Check precession from J2000.0 to J1992.5
0041 
0042     // NOTE: (FIXME?) I set 1950.0 as the observation epoch in the
0043     // calculator. Changing it to 1992.5 made no difference; probably
0044     // required only for FK5 to FK4 conversion. The help is not
0045     // extremely clear on this. -- asimha
0046 
0047     constexpr double arcsecPrecision = 3.e-4;
0048 
0049     long double jd = KStarsDateTime::epochToJd(1992.5, KStarsDateTime::JULIAN);
0050     KSNumbers num(jd);
0051     SkyPoint p(dms("18:22:54", false), dms("34:23:19", true)); // J2000.0 coordinates
0052     p.precess(&num);
0053     verify(p, 275.65734620, 34.38447020, arcsecPrecision);
0054 
0055     // Check precession from J2000.0 to J2016.8
0056     jd = KStarsDateTime::epochToJd(2016.8);
0057     num.updateValues(jd);
0058     p.set(dms("23:24:25", false), dms("-08:07:06", true));
0059     p.precess(&num);
0060     verify(p, 351.32145112, (-8.02590004), arcsecPrecision);
0061 
0062     // Check precession from J2000.0 to J2109.8
0063     jd = KStarsDateTime::epochToJd(2109.8);
0064     num.updateValues(jd);
0065     p.precess(&num);
0066     verify(p, 352.52338626, (-7.51340424), arcsecPrecision);
0067 
0068     /* Precession of equinoxes + conversion from FK4 to FK5 */
0069 
0070     // Conversion from B1950.0 to J2000.0
0071     p.set(dms("11:22:33", false), dms("44:55:66", true)); // Sets all of RA0,Dec0,RA,Dec
0072     p.B1950ToJ2000(); // Assumes (RA, Dec) are referenced to FK4 @ B1950 and stores the FK5 @ J2000 result in (RA, Dec)
0073     verify(p, 171.32145647, 44.66010982, arcsecPrecision);
0074 
0075     // Conversion from B1950.0 to J2016.8
0076     // Current of B1950ToJ2000 is to NOT alter (RA0,Dec0), so we shouldn't have to reset it
0077     jd = KStarsDateTime::epochToJd(2016.8);
0078     p.precessFromAnyEpoch(
0079         B1950, jd); // Takes (RA0, Dec0) and precesses to (RA, Dec) applying FK4 <--> FK5 conversions as necessary
0080     verify(p, 171.55045618, 44.56762158, arcsecPrecision);
0081 
0082     /* Precession of equinoxes + conversion from FK5 to FK4 */
0083     p.set(dms("11:22:33", false), dms("44:55:66", true)); // Sets all of RA0,Dec0,RA,Dec
0084     p.J2000ToB1950(); // Assumes (RA, Dec) are referenced to FK5 @ J2000 and stores the FK4 @ B1950 result in (RA, Dec)
0085     // NOTE: I set observation epoch = 2000.0 in NED's calculator. This time, it made a difference whether I set 2000.0 or 1950.0, but the difference was very small.
0086     verify(p, 169.94978888, 45.20928652, arcsecPrecision);
0087 
0088     // Conversion from J2016.8 to B1950.0
0089     // Current behavior of J2000ToB1950 is to NOT alter (RA0,Dec0), so we shouldn't have to reset it
0090     jd = KStarsDateTime::epochToJd(2016.8);
0091     p.precessFromAnyEpoch(
0092         jd, B1950); // Takes (RA0, Dec0) and precesses to (RA, Dec) applying FK4 <--> FK5 conversions as necessary
0093     // NOTE: I set observation epoch = 2016.8 in NED's calculator. It made a difference whether I set 2000.0 or 2016.8, but the difference was very small.
0094     verify(p, 169.71785991, 45.30132855, arcsecPrecision);
0095 }
0096 
0097 void TestSkyPoint::compareNovas()
0098 {
0099     Options::setUseRelativistic(false);
0100 
0101     KStarsDateTime kdt = KStarsDateTime::currentDateTimeUtc();
0102     long double jd = kdt.djd();
0103 
0104     double lnJd = ln_get_julian_from_sys();
0105     // compare JD provided by KStarsDateTime and libnova
0106     qDebug() << "KDT JD " << static_cast<double>(jd) << "  LN JD " << lnJd << " Diff " << (static_cast<double>
0107              (jd) - lnJd) * 86400 << "secs";
0108     QVERIFY(fabs(static_cast<double>(jd) - lnJd) * 86400 < 1);
0109 
0110     qDebug() << "Check conversion in Align";
0111     // the align process is a bit like this:
0112     // Catalogue position
0113     SkyPoint target(4, 20); // ra in hours, dec in degrees
0114 
0115     // transform to JNow in EKOS
0116     // this is wrong because aberration and nutation are handled incorrectly
0117     target.apparentCoord(static_cast<long double>(J2000), jd);
0118     double raN = target.ra().Degrees();
0119     double decN = target.dec().Degrees();
0120     qDebug() << "SkyPoint J2000 to JNow " << raN << ", " << decN;
0121 
0122     // should be:
0123     // remove aberration
0124 
0125     // remove nutation
0126 
0127     // this is passed to the CCD simulator where it is transformed to J2K using libnova
0128     ln_equ_posn JnowPos { 0, 0 }, J2kPos { 0, 0 };
0129     JnowPos.ra = raN;
0130     JnowPos.dec = decN;
0131     ln_get_equ_prec2(&JnowPos, lnJd, JD2000, &J2kPos);
0132     double ra0 = J2kPos.ra;
0133     double dec0 = J2kPos.dec;
0134     qDebug() << "Libnova JNow to J2000" << ra0 << ", " << dec0;
0135 
0136     // this is used to generate a star image which is solved
0137     // assume this solve is perfect
0138 
0139     // Align converts this J2000 position back to JNow:
0140     SkyPoint ac(ra0 / 15., dec0);
0141     ac.apparentCoord(static_cast<long double>(J2000), jd);
0142     double ran2 = ac.ra().Degrees();
0143     double decn2 = ac.dec().Degrees();
0144     qDebug() << "SkyPoint J2000 to JNow" << ran2 << ", " << decn2;
0145     // get the error, this appears as the align error
0146     qDebug() << "Error (arcsec)" << (raN - ran2) * 3600 << ", " << (decN - decn2) * 3600;
0147 
0148     // check the difference
0149     //QVERIFY(fabs(decN - decn2) * 3600. < 1);
0150     //QVERIFY(fabs(raN - ran2) * 3600. < 1);
0151 
0152     qDebug();
0153     qDebug() << "Using libnova throughout";
0154     J2kPos.ra = 60;
0155     J2kPos.dec = 20;
0156     // convert to JNow
0157     ln_get_equ_prec2(&J2kPos, JD2000, lnJd, &JnowPos);
0158     qDebug() << "libnova J2000 to JNow " << JnowPos.ra << ", " << JnowPos.dec;
0159     // convert to J2000
0160     ln_get_equ_prec2(&JnowPos, lnJd, JD2000, &J2kPos);
0161     qDebug() << " libnova JNow to J2000" << J2kPos.ra << ", " << J2kPos.dec;
0162 
0163     // 'solve'
0164 
0165     // convert back to Jnow
0166     ln_equ_posn Jnow2Pos { 0, 0 };
0167     ln_get_equ_prec2(&J2kPos, JD2000, lnJd, &Jnow2Pos);
0168     qDebug() << "libnova J2000 to JNow " << Jnow2Pos.ra << ", " << Jnow2Pos.dec;
0169 
0170     qDebug() << "Error (arcsec)" << (JnowPos.ra - Jnow2Pos.ra) * 3600 << ", " << (JnowPos.dec - Jnow2Pos.dec) * 3600;
0171 
0172     // Using SkyPoint won't help:
0173     qDebug();
0174     qDebug() << "using SkyPoint both ways";
0175     // do the conversion from the previously computed JNow position
0176     SkyPoint ccd;
0177     ccd.setRA(raN / 15.);
0178     ccd.setDec(decN);
0179     KSNumbers num(jd);
0180     ccd.deprecess(&num);
0181     qDebug() << "SkyPoint Jnow to J2000 " << ccd.ra0().Degrees() << ", " << ccd.dec0().Degrees();
0182     // and to Jnow
0183     ccd.apparentCoord(static_cast<long double>(J2000), jd);
0184     qDebug() << "SkyPoint J2000 to JNow " << ccd.ra().Degrees() << ", " << ccd.dec().Degrees();
0185 
0186     qDebug() << "Error " << (raN - ccd.ra().Degrees()) * 3600 << ", " << (decN - ccd.dec().Degrees()) * 3600;
0187 
0188     //QVERIFY(fabs(p.ra().Degrees() - targetRA) < tolerance * 15.);
0189     //QVERIFY(fabs(p.dec().Degrees() - targetDec) < tolerance);
0190 
0191     //qCDebug(KSTARS_EKOS_ALIGN) << "libnova errors " <<
0192     //        (epochPos.ra - targetCoord.ra().Degrees()) * 3600. << "as, " << (epochPos.dec - targetCoord.dec().Degrees()) * 3600. << "as";
0193 
0194 
0195     Options::setUseRelativistic(useRelativistic);
0196 }
0197 
0198 void TestSkyPoint::testPrecess_data()
0199 {
0200     QTest::addColumn<double>("Ra");
0201     QTest::addColumn<double>("Dec");
0202 
0203     QTest::newRow("normal") << 4.0 << 20.0;
0204     QTest::newRow("south") << 15.0 << -35.0;
0205     QTest::newRow("near Pole") << 22.0 << 85.0;
0206     QTest::newRow("near S Pole") << 22.0 << -85.0;
0207 }
0208 
0209 void TestSkyPoint::testPrecess()
0210 {
0211     auto jd = KStarsDateTime::currentDateTimeUtc().djd();
0212     KSNumbers now(jd);
0213     SkyPoint sp;
0214     QFETCH(double, Ra);
0215     QFETCH(double, Dec);
0216 
0217     sp = SkyPoint(Ra, Dec);
0218 
0219     sp.precess(&now);
0220 
0221     qDebug() << "precess dRa " << sp.ra0().Degrees() - sp.ra().Degrees() << ", dDec " << sp.dec0().Degrees() -
0222              sp.dec().Degrees();
0223 
0224     SkyPoint spn = sp.deprecess(&now);
0225 
0226     compare("precess-deprecess ", sp.ra0().Degrees(), sp.dec0().Degrees(), spn.ra().Degrees(), spn.dec().Degrees());
0227 }
0228 
0229 void TestSkyPoint::testPrecessFromAnyEpoch_data()
0230 {
0231     QTest::addColumn<double>("Ra");
0232     QTest::addColumn<double>("Dec");
0233 
0234     QTest::newRow("normal") << 4.0 << 20.0;
0235     QTest::newRow("south") << 15.0 << -35.0;
0236     QTest::newRow("near Pole") << 22.0 << 85.0;
0237     QTest::newRow("near S Pole") << 22.0 << -85.0;
0238 }
0239 
0240 void TestSkyPoint::testPrecessFromAnyEpoch()
0241 {
0242     auto jd = KStarsDateTime::currentDateTimeUtc().djd();
0243     KSNumbers now(jd);
0244 
0245     SkyPoint sp;
0246     QFETCH(double, Ra);
0247     QFETCH(double, Dec);
0248     sp = SkyPoint(Ra, Dec);
0249 
0250     sp.precessFromAnyEpoch(J2000L, jd);
0251 
0252     qDebug() << "PFAE dRa " << sp.ra0().Degrees() - sp.ra().Degrees() << ", dDec " << sp.dec0().Degrees() - sp.dec().Degrees();
0253 
0254     SkyPoint spn = SkyPoint(sp.ra(), sp.dec());
0255     spn.precessFromAnyEpoch(jd, J2000L);
0256 
0257     compare("dePFAE ", sp.ra0().Degrees(), sp.dec0().Degrees(), spn.ra().Degrees(), spn.dec().Degrees());
0258 }
0259 
0260 void TestSkyPoint::testNutate_data()
0261 {
0262     QTest::addColumn<double>("Ra");
0263     QTest::addColumn<double>("Dec");
0264 
0265     QTest::newRow("normal") << 4.0 << 20.0;
0266     QTest::newRow("south") << 15.0 << -35.0;
0267     QTest::newRow("near Pole") << 22.0 << 85.0;
0268     QTest::newRow("near S Pole") << 22.0 << -85.0;
0269 }
0270 
0271 void TestSkyPoint::testNutate()
0272 {
0273     long double jd = KStarsDateTime::currentDateTimeUtc().djd();
0274     KSNumbers now(jd);
0275 
0276     SkyPoint sp;
0277     QFETCH(double, Ra);
0278     QFETCH(double, Dec);
0279     sp = SkyPoint(Ra, Dec);
0280 
0281     sp.nutate(&now);
0282 
0283     qDebug() << "nutate dRa " << sp.ra0().Degrees() - sp.ra().Degrees() << ", dDec " << sp.dec0().Degrees() -
0284              sp.dec().Degrees();
0285 
0286     sp.nutate(&now, true);
0287 
0288     compare("nutate-denutate", &sp);
0289 }
0290 
0291 void TestSkyPoint::testAberrate_data()
0292 {
0293     QTest::addColumn<double>("Ra");
0294     QTest::addColumn<double>("Dec");
0295 
0296     QTest::newRow("normal") << 4.0 << 20.0;
0297     QTest::newRow("south") << 15.0 << -35.0;
0298     QTest::newRow("near Pole") << 22.0 << 85.0;
0299     QTest::newRow("near S Pole") << 22.0 << -85.0;
0300 }
0301 
0302 void TestSkyPoint::testAberrate()
0303 {
0304     long double jd = KStarsDateTime::epochToJd(2028.3);
0305     KSNumbers now(jd);
0306 
0307     SkyPoint sp;
0308     QFETCH(double, Ra);
0309     QFETCH(double, Dec);
0310     sp = SkyPoint(Ra, Dec);
0311 
0312     sp.aberrate(&now);
0313 
0314     // deaberrate
0315     sp.aberrate(&now, true);
0316 
0317     compare("aberrate-deaberrate", &sp);
0318 }
0319 
0320 // test results obtained using the ASCOM Transform component
0321 // to convert from J2000 to Apparent
0322 // this uses SOFA so should be fairly good
0323 void TestSkyPoint::testApparentCatalogue_data()
0324 {
0325     QTest::addColumn<double>("Ra");
0326     QTest::addColumn<double>("Dec");
0327     QTest::addColumn<double>("RaOut");
0328     QTest::addColumn<double>("DecOut");
0329 
0330     QTest::newRow("Apparent") << 4.0 << 20.0 << 4.0274 << 20.0791;
0331     QTest::newRow("south") << 10.0 << -35.0 << 10.0208 << -35.1418;
0332     QTest::newRow("south") << 10.0 << -55.0 << 10.01698 << -55.14256;
0333     QTest::newRow("near Pole") << 22.0 << 85.0 << 21.9601 << 85.1317;
0334     QTest::newRow("near S Pole") << 15.0 << -85.0 << 15.1159 << -85.1112;
0335 }
0336 
0337 void TestSkyPoint::testApparentCatalogue()
0338 {
0339     Options::setUseRelativistic(false);
0340 
0341     //long double jd = KStarsDateTime::currentDateTimeUtc().djd();
0342     auto dt = KStarsDateTime::fromString("2028-04-27T06:30");
0343     long double jd = dt.djd();
0344 
0345     double rjd = static_cast<double>(jd - J2000L);
0346     //qDebug() << "DT " << dt.toString() << " JD " << static_cast<double>(jd) << " RJD " << rjd;
0347 
0348     QVERIFY(fabs(rjd - 10343.771) < 0.1);
0349 
0350     SkyPoint sp;
0351     QFETCH(double, Ra);
0352     QFETCH(double, Dec);
0353     sp = SkyPoint(Ra, Dec);
0354 
0355     // J2000 catalogue to Apparent
0356     sp.apparentCoord(J2000L, jd);
0357     //    qDebug() << "apparent ra0 " << sp.ra0().Hours() << ", dec0 " << sp.dec0().Degrees() <<
0358     //                " ra " << sp.ra().Hours() << ", dec " << sp.dec().Degrees();
0359     QFETCH(double, RaOut);
0360     QFETCH(double, DecOut);
0361 
0362     compare("J2000 to aparrent", RaOut, DecOut, sp.ra().Hours(), sp.dec().Degrees());
0363 
0364     SkyPoint spn = SkyPoint(sp.ra(), sp.dec());
0365     qDebug() << "spn ra0 " << spn.ra0().Degrees() << ", dec0 " << spn.dec0().Degrees() <<
0366              " ra " << spn.ra().Degrees() << ", dec " << spn.dec().Degrees();
0367 
0368     // apparent on jd to J2000Catalogue
0369     spn.catalogueCoord(jd);
0370 
0371     compare("J2K to app to catalogue", sp.ra0().Degrees(), sp.dec0().Degrees(), spn.ra0().Degrees(), spn.dec0().Degrees());
0372 }
0373 
0374 void TestSkyPoint::testApparentCatalogueInversion_data()
0375 {
0376     QTest::addColumn<double>("Ra");
0377     QTest::addColumn<double>("Dec");
0378     QTest::addColumn<double>("Epoch");
0379 
0380     QTest::newRow("Random1") << 15.32 << 34.50 << 2012.34;
0381     QTest::newRow("Random2") << 72.96 << 24.10 << 2022.39;
0382     QTest::newRow("Random3") << 188.52 << -36.58 << 2042.86;
0383     QTest::newRow("Random4") << 239.22 << -47.32 << 2139.23;
0384     QTest::newRow("Random5") << 278.78 << 01.68 << 2058.62;
0385     QTest::newRow("Near J2000 NCP") << 172.59 << 88.95 << 2029.88;
0386     QTest::newRow("Near J2000 SCP") << 145.22 << -88.86 << 2034.72;
0387     QTest::newRow("Near Current NEP") << 280.74 << 70.81 << 2021.23;
0388 }
0389 
0390 void TestSkyPoint::testApparentCatalogueInversion()
0391 {
0392     Options::setUseRelativistic(false);
0393 
0394     // This test tests the conversion of J2000 -> arbitrary ICRS epoch -> back to J2000 by inversion
0395     constexpr double sub_arcsecond_tolerance = 0.1 / 3600.0;
0396     SkyPoint sp_forward;
0397     QFETCH(double, Ra);
0398     QFETCH(double, Dec);
0399     QFETCH(double, Epoch);
0400     long double targetJd = KStarsDateTime::epochToJd(Epoch);
0401     dms dms_ra {Ra};
0402     KSNumbers num(targetJd);
0403     sp_forward.setRA0(dms_ra);
0404     sp_forward.setDec0(Dec);
0405     sp_forward.updateCoordsNow(&num);
0406     SkyPoint sp_backward {sp_forward.catalogueCoord(targetJd)};
0407     compare(
0408         QString("Apparent to Catalogue Inversion Check for epoch %1").arg(Epoch, 7, 'f', 2),
0409         sp_backward.ra0().reduce().Degrees(), sp_backward.dec0().Degrees(),
0410         dms_ra.reduce().Degrees(), Dec,
0411         sub_arcsecond_tolerance
0412     );
0413 }
0414 
0415 void TestSkyPoint::compareSkyPointLibNova_data()
0416 {
0417     QTest::addColumn<double>("Ra");
0418     QTest::addColumn<double>("Dec");
0419     QTest::addColumn<double>("RaOut");
0420     QTest::addColumn<double>("DecOut");
0421 
0422     QTest::newRow("Apparent") << 4.0 << 20.0 << 4.0274 << 20.0791;
0423     QTest::newRow("south") << 10.0 << -35.0 << 10.0208 << -35.1418;
0424     QTest::newRow("south") << 10.0 << -55.0 << 10.01698 << -55.14256;
0425     QTest::newRow("near Pole") << 22.0 << 85.0 << 21.9601 << 85.1317;
0426     QTest::newRow("near S Pole") << 15.0 << -85.0 << 15.1159 << -85.1112;
0427 }
0428 void TestSkyPoint::compareSkyPointLibNova()
0429 {
0430     // skypoint JD
0431     auto dt = KStarsDateTime::fromString("2028-04-27T06:30");
0432     long double ljd = dt.djd();
0433     double jd = static_cast<double>(ljd);
0434 
0435     SkyPoint sp;
0436     QFETCH(double, Ra);
0437     QFETCH(double, Dec);
0438     sp = SkyPoint(Ra, Dec);
0439     ln_equ_posn LnPos { Ra * 15.0, Dec }, observed { 0, 0 }, J2000Pos { 0, 0 };
0440 
0441     ln_equ_posn tempPosn;
0442 
0443     KSNumbers num(ljd);
0444 
0445     // apply precession from J2000 to jd
0446     ln_get_equ_prec2(&LnPos, JD2000, jd, &tempPosn);
0447     SkyPoint spp = sp;
0448     spp.precessFromAnyEpoch(J2000L, ljd);
0449     compare("precess", spp.ra().Degrees(), spp.dec().Degrees(), tempPosn.ra, tempPosn.dec);
0450 
0451     // apply nutation (needs new function)
0452     ln_get_equ_nut(&tempPosn, jd);
0453     SkyPoint spn = spp;
0454     spn.nutate(&num);
0455     compare("nutate", spn.ra().Degrees(), spn.dec().Degrees(), tempPosn.ra, tempPosn.dec);
0456 
0457     // apply aberration
0458     ln_get_equ_aber(&tempPosn, jd, &observed);
0459     SkyPoint spa = spn;
0460     spa.aberrate(&num);
0461     compare("aberrate", spa.ra().Degrees(), spa.dec().Degrees(), observed.ra, observed.dec);
0462 
0463     sp.apparentCoord(J2000L, ljd);
0464 
0465     // compare sp and observed with each other
0466     compare("sp and LN ", sp.ra().Degrees(), sp.dec().Degrees(), observed.ra, observed.dec);
0467     QFETCH(double, RaOut);
0468     QFETCH(double, DecOut);
0469     // compare sp with expected, use degrees
0470     compare("sp expected ", RaOut * 15., DecOut, sp.ra().Degrees(), sp.Dec.Degrees(), 0.0005);
0471     // compare ln with expected
0472     compare("ln expected ", RaOut * 15, DecOut, observed.ra, observed.dec, 0.0005);
0473 
0474     // now to J2000
0475 
0476     // remove the aberration
0477     ln_get_equ_aber(&observed, jd, &tempPosn);
0478     // this conversion has added the aberration, we want to subtract it
0479     tempPosn.ra = observed.ra - (tempPosn.ra - observed.ra);
0480     tempPosn.dec = observed.dec * 2 - tempPosn.dec;
0481     //sp.aberrate(&num, true);
0482 
0483 
0484     // remove the nutation
0485     ln_get_equ_nut(&tempPosn, jd, true);
0486 
0487     //sp.nutate(&num, true);
0488 
0489     // precess from now to J2000
0490     ln_get_equ_prec2(&tempPosn, jd, JD2000, &J2000Pos);
0491 
0492     // the start position needs to be in RA0,Dec0
0493     //sp.RA0 = sp.RA;
0494     //sp.Dec0 = sp.Dec;
0495     // from now to J2000
0496     //sp.precessFromAnyEpoch(jd, J2000L);
0497     // the J2000 position is in RA,Dec, move to RA0, Dec0
0498     //sp.RA0 = sp.RA;
0499     //sp.Dec0 = sp.Dec;
0500     //sp.lastPrecessJD = J2000;
0501 
0502     sp.catalogueCoord(ljd);
0503 
0504     //compare sp and the j2000Pos with each other and the original
0505     compare("Round trip ", &sp, &J2000Pos);
0506     compare("original", Ra, Dec, sp.RA0.Hours(), sp.Dec0.Degrees());
0507 }
0508 
0509 void TestSkyPoint::compare(QString msg, SkyPoint *sp, SkyPoint *sp1)
0510 {
0511     compare(msg, sp->ra0().Degrees(), sp->dec0().Degrees(), sp1->ra().Degrees(), sp1->dec().Degrees());
0512 }
0513 
0514 void TestSkyPoint::compare(QString msg, SkyPoint *sp, ln_equ_posn *lnp)
0515 {
0516     compare(msg, sp->ra0().Degrees(), sp->dec0().Degrees(), lnp->ra, lnp->dec);
0517 }
0518 
0519 void TestSkyPoint::compare(QString msg, SkyPoint *sp)
0520 {
0521     compare(msg, sp->ra0().Degrees(), sp->dec0().Degrees(), sp->ra().Degrees(), sp->dec().Degrees());
0522 }
0523 
0524 void TestSkyPoint::compare(QString msg, double ra1, double dec1, double ra2, double dec2, double err)
0525 {
0526     qDebug() << qPrintable(QString("%1 pos1 %2, %3 pos2 %4, %5 errors %6, %7 secs").arg(msg)
0527                            .arg(ra1).arg(dec1).arg(ra2).arg(dec2)
0528                            .arg((ra1 - ra2) * 3600.0, 6, 'f', 1).arg((dec1 - dec2) * 3600., 6, 'f', 1));
0529     //QString str;
0530     //str.sprintf("%s pos1 %f, %f pos2 %f, %f errors %.1f, %.1f sec", msg.data(), ra1, dec1, ra2, dec2, (ra1 - ra2) *3600, (dec1 - dec2) * 3600 );
0531     //qDebug() << str;// << msg << "SP " << ra1 << ", " << dec1 << " Sp0 " << ra2 << ", " << dec2
0532     //<< " errors " << ((ra1 - ra2) * 3600) << ", " << ((dec1 - dec2) * 3600) << " arcsec";
0533 
0534     double errRa = err / cos(dec1 * M_PI / 180.0);
0535 
0536     QVERIFY2(fabs(ra1 - ra2) < errRa, qPrintable(QString("Ra %1, %2 error %3").arg(ra1).arg(ra2).arg(((ra1 - ra2) * 3600.0), 6,
0537              'f', 1)));
0538     QVERIFY2(fabs(dec1 - dec2) < err, qPrintable(QString("Dec %1, %2 error %3").arg(dec1).arg(dec2).arg((dec1 - dec2) * 3600.,
0539              6, 'f', 1)));
0540 }
0541 
0542 // apply or remove nutation
0543 void TestSkyPoint::ln_get_equ_nut(ln_equ_posn *posn, double jd, bool reverse)
0544 {
0545     // code lifted from libnova ln_get_equ_nut
0546     // with the option to add or remove nutation
0547     struct ln_nutation nut;
0548     ln_get_nutation (jd, &nut);
0549 
0550     double mean_ra, mean_dec, delta_ra, delta_dec;
0551 
0552     mean_ra = ln_deg_to_rad(posn->ra);
0553     mean_dec = ln_deg_to_rad(posn->dec);
0554 
0555     // Equ 22.1
0556 
0557     double nut_ecliptic = ln_deg_to_rad(nut.ecliptic + nut.obliquity);
0558     double sin_ecliptic = sin(nut_ecliptic);
0559 
0560     double sin_ra = sin(mean_ra);
0561     double cos_ra = cos(mean_ra);
0562 
0563     double tan_dec = tan(mean_dec);
0564 
0565     delta_ra = (cos (nut_ecliptic) + sin_ecliptic * sin_ra * tan_dec) * nut.longitude - cos_ra * tan_dec * nut.obliquity;
0566     delta_dec = (sin_ecliptic * cos_ra) * nut.longitude + sin_ra * nut.obliquity;
0567 
0568     // the sign changed to remove nutation
0569     if (reverse)
0570     {
0571         delta_ra = -delta_ra;
0572         delta_dec = -delta_dec;
0573     }
0574     posn->ra += delta_ra;
0575     posn->dec += delta_dec;
0576 }
0577 
0578 void TestSkyPoint::testUpdateCoords()
0579 {
0580     Options::setUseRelativistic(false);
0581 
0582     dms ra, dec;
0583     //    ra.setH(11, 50, 3, 0); Min RA
0584     ra.setH(11, 55, 28, 0);
0585     //    ra.setH(12, 00, 53, 0); Max RA
0586 
0587     //    dec.setD(-89,52,41,0); Min abs(dec)
0588     dec.setD(-89, 53, 02, 0);
0589     //    dec.setD(-89,53,23,0); Max abs(Dec)
0590 
0591     auto dt = KStarsDateTime::fromString("2021-01-24T00:00");
0592     int numtest = 100;
0593     int numdays = 100;
0594 
0595     long double jd = dt.djd();
0596     double jdfrac, jdint;
0597     KSNumbers num(jd);
0598     SkyPoint sp;
0599     sp.setRA0(ra);
0600     sp.setDec0(dec);
0601     for(int i = 0; i < numtest; i++)
0602     {
0603         sp.updateCoordsNow(&num);
0604         jdfrac = modf(static_cast<double>(dt.djd()), &jdint);
0605         if (fabs(sp.dec().Degrees()) > 90.0)
0606             qDebug() << "i" << i << " jdfrac" << jdfrac << ": sp ra0 " << sp.ra0().Degrees() << ", dec " << sp.dec0().Degrees() <<
0607                      " ra " << sp.ra().Degrees() << ", dec " << sp.dec().Degrees();
0608         QVERIFY(fabs(sp.dec().Degrees()) <= 90.0);
0609 
0610         dt = dt.addSecs(numdays * 86400 / numtest);
0611         jd = dt.djd();
0612         num.updateValues(jd);
0613     }
0614 
0615 }
0616 
0617 void TestSkyPoint::testDeltaAngle()
0618 {
0619     SkyPoint p1(dms("00:43:13", false), dms("40:45:42"));
0620     SkyPoint p2(dms("00:43:44", false), dms("41:23:07"));
0621 
0622     auto diffRA = p1.ra().deltaAngle(p2.ra());
0623     auto diffDE = p1.dec().deltaAngle(p2.dec());
0624 
0625     QVERIFY(diffRA.Degrees() < 1);
0626     QVERIFY(diffDE.Degrees() < 1);
0627 }
0628 
0629 QTEST_GUILESS_MAIN(TestSkyPoint)