File indexing completed on 2024-04-21 14:47:19
0001 /* 0002 SPDX-FileCopyrightText: 2020 Hy Murveit <hy@murveit.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "ekos/guide/internalguide/starcorrespondence.h" 0008 0009 #include <QTest> 0010 0011 #include <QObject> 0012 0013 0014 class TestStarCorrespondence : public QObject 0015 { 0016 Q_OBJECT 0017 0018 public: 0019 /** @short Constructor */ 0020 TestStarCorrespondence(); 0021 0022 /** @short Destructor */ 0023 ~TestStarCorrespondence() override = default; 0024 0025 private slots: 0026 void basicTest(); 0027 }; 0028 0029 #include "teststarcorrespondence.moc" 0030 0031 TestStarCorrespondence::TestStarCorrespondence() : QObject() 0032 { 0033 } 0034 0035 Edge makeEdge(float x, float y) 0036 { 0037 Edge e; 0038 e.x = x; 0039 e.y = y; 0040 return e; 0041 } 0042 0043 void runTest(int guideStar) 0044 { 0045 constexpr double maxDistanceToStar = 5.0; 0046 0047 // Setup a number of stars. 0048 QList<Edge> stars; 0049 stars.append(makeEdge(90, 70)); 0050 stars.append(makeEdge(110, 70)); 0051 stars.append(makeEdge(0, 0)); 0052 stars.append(makeEdge(10, 30)); 0053 stars.append(makeEdge(20, 70)); 0054 stars.append(makeEdge(70, 10)); 0055 stars.append(makeEdge(70, 80)); 0056 stars.append(makeEdge(80, 40)); 0057 stars.append(makeEdge(30, 20)); 0058 stars.append(makeEdge(30, 80)); 0059 stars.append(makeEdge(50, 60)); 0060 // Assign guideStar as the guide star. 0061 StarCorrespondence c(stars, guideStar); 0062 c.setImageSize(120, 100); // Not necessary, but speeds things up. 0063 QVector<int> output; 0064 0065 // Identity test. 0066 Edge gStar = c.find(stars, maxDistanceToStar, &output, false); 0067 QVERIFY2(gStar.x == stars[guideStar].x, "Identity"); 0068 QVERIFY2(gStar.y == stars[guideStar].y, "Identity"); 0069 for (int i = 0; i < stars.size(); ++i) 0070 { 0071 QVERIFY2(output[i] == i, "Identity"); 0072 } 0073 0074 srand(21); 0075 // Move the stars randomly upto 2 pixels away 0076 QList<Edge> stars2; 0077 for (int i = 0; i < stars.size(); ++i) 0078 { 0079 double rand_fraction = ((rand() % 200) - 100) / 50.0; 0080 double x = stars[i].x + rand_fraction; 0081 rand_fraction = ((rand() % 200) - 100) / 50.0; 0082 double y = stars[i].y + rand_fraction; 0083 stars2.append(makeEdge(x, y)); 0084 } 0085 gStar = c.find(stars2, maxDistanceToStar, &output, false); 0086 QVERIFY2(gStar.x == stars2[guideStar].x, "small move"); 0087 QVERIFY2(gStar.y == stars2[guideStar].y, "small move"); 0088 for (int i = 0; i < stars.size(); ++i) 0089 { 0090 QVERIFY2(output[i] == i, "small move"); 0091 } 0092 0093 // Remove one of the stars (not the guide star which is 0-5). 0094 stars2.removeAt(8); 0095 gStar = c.find(stars2, maxDistanceToStar, &output, false); 0096 QVERIFY2(gStar.x == stars2[guideStar].x, "one removed"); 0097 QVERIFY2(gStar.y == stars2[guideStar].y, "one removed"); 0098 for (int i = 0; i < stars2.size(); ++i) 0099 { 0100 if (i < 8) QVERIFY2(output[i] == i, "one removed"); 0101 else QVERIFY2(output[i] == (i + 1), "one removed"); 0102 } 0103 0104 // Remove the guide star. Nothing should be returned. 0105 double guideX = stars2[guideStar].x; 0106 double guideY = stars2[guideStar].y; 0107 stars2.removeAt(guideStar); 0108 c.setAllowMissingGuideStar(false); 0109 gStar = c.find(stars2, maxDistanceToStar, &output, false); 0110 QVERIFY2(gStar.x == -1, "guide removed"); 0111 QVERIFY2(gStar.y == -1, "guide removed"); 0112 for (int i = 0; i < stars2.size(); ++i) 0113 { 0114 QVERIFY2(output[i] == -1, "guide removed"); 0115 } 0116 0117 // Allow it to be resilient to missing guide stars. 0118 c.setAllowMissingGuideStar(true); 0119 gStar = c.find(stars2, maxDistanceToStar, &output, false); 0120 // There's been noise added so it won't recover the guide star exactly. 0121 QVERIFY2(fabs(gStar.x - guideX) < 2, "guide and 8 removed"); 0122 QVERIFY2(fabs(gStar.y - guideY) < 2, "guide and 8 removed"); 0123 for (int i = 0; i < stars2.size(); ++i) 0124 { 0125 // previously we removed 8, but also removing the guide star moves the index for star 8 to 7. 0126 const int missingStar = 7; 0127 if (i < missingStar && i < guideStar) 0128 QVERIFY2(output[i] == i, "guide and 8 removed"); 0129 else if (i < guideStar || i < missingStar) 0130 QVERIFY2(output[i] == (i + 1), "guide and 8 removed"); 0131 else 0132 QVERIFY2(output[i] == (i + 2), "guide and 8 removed"); 0133 } 0134 0135 // Use clean star positions to exactly recover the lost guide star. 0136 guideX = stars[guideStar].x; 0137 guideY = stars[guideStar].y; 0138 stars.removeAt(guideStar); 0139 c.setAllowMissingGuideStar(true); 0140 gStar = c.find(stars, maxDistanceToStar, &output, false); 0141 QVERIFY2(gStar.x == guideX, "guide removed and recovered"); 0142 QVERIFY2(gStar.y == guideY, "guide removed and recovered"); 0143 for (int i = 0; i < stars2.size(); ++i) 0144 { 0145 if (i < guideStar) 0146 QVERIFY2(output[i] == i, "guide removed & recovered"); 0147 else 0148 QVERIFY2(output[i] == (i + 1), "guide removed & recovered"); 0149 } 0150 } 0151 0152 void runAdaptationTest() 0153 { 0154 // Setup a number of stars. 0155 QList<Edge> stars; 0156 stars.append(makeEdge(100, 50)); 0157 stars.append(makeEdge(150, 50)); 0158 stars.append(makeEdge(100, 80)); 0159 StarCorrespondence c(stars, 0); 0160 QVERIFY(c.reference(0).x == 100); 0161 QVERIFY(c.reference(0).y == 50); 0162 QVERIFY(c.reference(1).x == 150); 0163 QVERIFY(c.reference(1).y == 50); 0164 QVERIFY(c.reference(2).x == 100); 0165 QVERIFY(c.reference(2).y == 80); 0166 0167 // Move star #1 from 150,50 to 151,148. Keep the other two in place. 0168 stars[1].x = 151; 0169 stars[1].y = 48; 0170 QVector<int> output; 0171 // Find the stars. All 3 should still correspond. 0172 // The position of star#1 should move a little towards its new position. 0173 c.find(stars, 5.0, &output, true); 0174 QVERIFY(output[0] == 0); 0175 QVERIFY(output[1] == 1); 0176 QVERIFY(output[2] == 2); 0177 QVERIFY(c.reference(0).x == 100); 0178 QVERIFY(c.reference(0).y == 50); 0179 QVERIFY(c.reference(1).x > 150); 0180 QVERIFY(c.reference(1).y < 50); 0181 QVERIFY(c.reference(2).x == 100); 0182 QVERIFY(c.reference(2).y == 80); 0183 0184 // Check the exact position it moved to. 0185 // Exact values for ref 1 using the adaptation equation should be: 0186 // alpha = 1 / 25**0.865 0187 // x(1) = alpha * 151 + (1-alpha) * 150 0188 // y(1) = alpha * 48 + (1-alpha) * 50 0189 // Of course, if the time constant or equation changes, this shuld be updated. 0190 const double alpha = 1 / pow(25.0, 0.865); 0191 const double x1 = alpha * 151 + (1 - alpha) * 150; 0192 const double y1 = alpha * 48 + (1 - alpha) * 50; 0193 QVERIFY(fabs(c.reference(1).x - x1) < .0001); 0194 QVERIFY(fabs(c.reference(1).y - y1) < .0001); 0195 } 0196 0197 // Checks a set of reference stars and detected stars which should fail star correspondence. 0198 void runNoCorrespondenceTest() 0199 { 0200 constexpr double maxDistanceToStar = 10.0; 0201 0202 // Setup a number of stars. 0203 QList<Edge> stars; 0204 stars.append(makeEdge(1186.73, 483.568)); 0205 stars.append(makeEdge(367.712, 293.589)); 0206 stars.append(makeEdge(190.09, 944.418)); 0207 stars.append(makeEdge(1067.85, 30.3361)); 0208 stars.append(makeEdge(1067.58, 313.792)); 0209 // Assign guideStar as the guide star. 0210 StarCorrespondence c(stars, 0); 0211 c.setImageSize(1280, 960); // Not necessary, but speeds things up. 0212 QVector<int> output; 0213 0214 // This detected set only has 2 of the ref stars, so multistar should fail. 0215 QList<Edge> stars2; 0216 stars2.append(makeEdge(241.8, 125.5)); 0217 stars2.append(makeEdge(233.5, 119.3)); 0218 stars2.append(makeEdge(1186.6, 483.5)); // ref 0 0219 stars2.append(makeEdge(368.1, 294.1)); // ref 1 0220 stars2.append(makeEdge(1006.5, 909.7)); 0221 stars2.append(makeEdge(903.3, 448.7)); 0222 stars2.append(makeEdge(940.5, 177.2)); 0223 stars2.append(makeEdge(695.4, 97.6)); 0224 0225 c.setAllowMissingGuideStar(true); 0226 Edge gStar = c.find(stars2, maxDistanceToStar, &output, false); 0227 QVERIFY(gStar.x == -1); 0228 QVERIFY(gStar.y == -1); 0229 for (int i = 0; i < output.size(); ++i) 0230 QVERIFY(output[i] == -1); 0231 } 0232 0233 void TestStarCorrespondence::basicTest() 0234 { 0235 for (int i = 0; i < 6; ++i) 0236 runTest(i); 0237 runAdaptationTest(); 0238 runNoCorrespondenceTest(); 0239 } 0240 0241 QTEST_GUILESS_MAIN(TestStarCorrespondence)