Instrument Neutral Distributed Interface INDI 2.2.5
BasicMathPlugin.cpp
Go to the documentation of this file.
1
4
5#include "BasicMathPlugin.h"
6
7#include "DriverCommon.h"
8#include "libastro.h"
9#include <libnova/julian_day.h>
10
11#include <gsl/gsl_blas.h>
12#include <gsl/gsl_permutation.h>
13#include <gsl/gsl_linalg.h>
14
15#include "indicom.h"
16
17#include <limits>
18#include <iostream>
19#include <map>
20
21namespace INDI
22{
23namespace AlignmentSubsystem
24{
26{
27 pActualToApparentTransform = gsl_matrix_alloc(3, 3);
28 pApparentToActualTransform = gsl_matrix_alloc(3, 3);
29}
30
31// Destructor
32
38
39// Public methods
40
42{
45
55 switch (SyncPoints.size())
56 {
57 // JM 2021-07-04: No Transformation required.
58 case 0:
59 return true;
60
61 // JM 2021-07-04: For 1 point, it should be direct reciporical transformation.
62 case 1:
63 {
64 AlignmentDatabaseEntry &Entry1 = SyncPoints[0];
66 INDI::IHorizontalCoordinates ActualSyncPoint1;
67 TelescopeDirectionVector ActualDirectionCosine1;
70 return false;
71 RaDec.declination = Entry1.Declination;
72 RaDec.rightascension = Entry1.RightAscension;
74 {
75 EquatorialToHorizontal(&RaDec, &Position, Entry1.ObservationJulianDate, &ActualSyncPoint1);
76 // Now express this coordinate as a normalised direction vector (a.k.a direction cosines)
77 ActualDirectionCosine1 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint1);
78 }
79 else
80 {
81 ActualDirectionCosine1 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec);
82 }
83 TelescopeDirectionVector DummyActualDirectionCosine2;
84 TelescopeDirectionVector DummyApparentDirectionCosine2;
85 TelescopeDirectionVector DummyActualDirectionCosine3;
86 TelescopeDirectionVector DummyApparentDirectionCosine3;
87
89 {
90 case ZENITH:
91 DummyActualDirectionCosine2.x = 0.0;
92 DummyActualDirectionCosine2.y = 0.0;
93 DummyActualDirectionCosine2.z = 1.0;
94 DummyApparentDirectionCosine2 = DummyActualDirectionCosine2;
95 break;
96
98 {
100 DummyRaDec.rightascension = 0.0;
101 DummyRaDec.declination = 90.0;
102 DummyActualDirectionCosine2 = TelescopeDirectionVectorFromEquatorialCoordinates(DummyRaDec);
103 DummyApparentDirectionCosine2 = DummyActualDirectionCosine2;
104 break;
105 }
107 {
109 DummyRaDec.rightascension = 0.0;
110 DummyRaDec.declination = -90.0;
111 DummyActualDirectionCosine2 = TelescopeDirectionVectorFromEquatorialCoordinates(DummyRaDec);
112 DummyApparentDirectionCosine2 = DummyActualDirectionCosine2;
113 break;
114 }
115 }
116 DummyActualDirectionCosine3 = ActualDirectionCosine1 * DummyActualDirectionCosine2;
117 DummyActualDirectionCosine3.Normalise();
118 DummyApparentDirectionCosine3 = Entry1.TelescopeDirection * DummyApparentDirectionCosine2;
119 DummyApparentDirectionCosine3.Normalise();
120 CalculateTransformMatrices(ActualDirectionCosine1, DummyActualDirectionCosine2, DummyActualDirectionCosine3,
121 Entry1.TelescopeDirection, DummyApparentDirectionCosine2,
122 DummyApparentDirectionCosine3, pActualToApparentTransform,
124 return true;
125 }
126 case 2:
127 {
128 // First compute local horizontal coordinates for the two sync points
129 AlignmentDatabaseEntry &Entry1 = SyncPoints[0];
130 AlignmentDatabaseEntry &Entry2 = SyncPoints[1];
133 TelescopeDirectionVector ActualDirectionCosine1;
134 TelescopeDirectionVector ActualDirectionCosine2;
135 RaDec1.declination = Entry1.Declination;
136 RaDec1.rightascension = Entry1.RightAscension;
137 RaDec2.declination = Entry2.Declination;
138 RaDec2.rightascension = Entry2.RightAscension;
139 IGeographicCoordinates Position { 0, 0, 0 };
141 return false;
143 {
144 INDI::IHorizontalCoordinates ActualSyncPoint1;
145 INDI::IHorizontalCoordinates ActualSyncPoint2;
146 EquatorialToHorizontal(&RaDec1, &Position, Entry1.ObservationJulianDate, &ActualSyncPoint1);
147 EquatorialToHorizontal(&RaDec2, &Position, Entry2.ObservationJulianDate, &ActualSyncPoint2);
148 ActualDirectionCosine1 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint1);
149 ActualDirectionCosine2 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint2);
150 }
151 else
152 {
153 ActualDirectionCosine1 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec1);
154 ActualDirectionCosine2 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec2);
155 }
156
157 // Now express these coordinates as normalised direction vectors (a.k.a direction cosines)
158 TelescopeDirectionVector DummyActualDirectionCosine3;
159 TelescopeDirectionVector DummyApparentDirectionCosine3;
160 DummyActualDirectionCosine3 = ActualDirectionCosine1 * ActualDirectionCosine2;
161 DummyActualDirectionCosine3.Normalise();
162 DummyApparentDirectionCosine3 = Entry1.TelescopeDirection * Entry2.TelescopeDirection;
163 DummyApparentDirectionCosine3.Normalise();
164
165 // The third direction vectors is generated by taking the cross product of the first two
166 CalculateTransformMatrices(ActualDirectionCosine1, ActualDirectionCosine2, DummyActualDirectionCosine3,
168 DummyApparentDirectionCosine3, pActualToApparentTransform,
170 return true;
171 }
172
173 case 3:
174 {
175 // First compute local horizontal coordinates for the three sync points
176 AlignmentDatabaseEntry &Entry1 = SyncPoints[0];
177 AlignmentDatabaseEntry &Entry2 = SyncPoints[1];
178 AlignmentDatabaseEntry &Entry3 = SyncPoints[2];
179 INDI::IEquatorialCoordinates RaDec1, RaDec2, RaDec3;
180 TelescopeDirectionVector ActualDirectionCosine1, ActualDirectionCosine2, ActualDirectionCosine3;
181 RaDec1.declination = Entry1.Declination;
182 RaDec1.rightascension = Entry1.RightAscension;
183 RaDec2.declination = Entry2.Declination;
184 RaDec2.rightascension = Entry2.RightAscension;
185 RaDec3.declination = Entry3.Declination;
186 RaDec3.rightascension = Entry3.RightAscension;
187 IGeographicCoordinates Position { 0, 0, 0 };
189 return false;
191 {
192 INDI::IHorizontalCoordinates ActualSyncPoint1;
193 INDI::IHorizontalCoordinates ActualSyncPoint2;
194 INDI::IHorizontalCoordinates ActualSyncPoint3;
195 EquatorialToHorizontal(&RaDec1, &Position, Entry1.ObservationJulianDate, &ActualSyncPoint1);
196 EquatorialToHorizontal(&RaDec2, &Position, Entry2.ObservationJulianDate, &ActualSyncPoint2);
197 EquatorialToHorizontal(&RaDec3, &Position, Entry3.ObservationJulianDate, &ActualSyncPoint3);
198
199 // Now express these coordinates as normalised direction vectors (a.k.a direction cosines)
200 ActualDirectionCosine1 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint1);
201 ActualDirectionCosine2 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint2);
202 ActualDirectionCosine3 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint3);
203 }
204 else
205 {
206 ActualDirectionCosine1 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec1);
207 ActualDirectionCosine2 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec2);
208 ActualDirectionCosine3 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec3);
209 }
210
211 CalculateTransformMatrices(ActualDirectionCosine1, ActualDirectionCosine2, ActualDirectionCosine3,
214 return true;
215 }
216
217 default:
218 {
219 IGeographicCoordinates Position { 0, 0, 0 };
221 return false;
222
223 // Compute Hulls etc.
227
228 // Add a dummy point at the nadir
229 ActualConvexHull.MakeNewVertex(0.0, 0.0, -1.0, 0);
230 ApparentConvexHull.MakeNewVertex(0.0, 0.0, -1.0, 0);
231
232 int VertexNumber = 1;
233 // Add the rest of the vertices
234 for (InMemoryDatabase::AlignmentDatabaseType::const_iterator Itr = SyncPoints.begin();
235 Itr != SyncPoints.end(); Itr++)
236 {
238 TelescopeDirectionVector ActualDirectionCosine;
239 RaDec.declination = (*Itr).Declination;
240 RaDec.rightascension = (*Itr).RightAscension;
242 {
243 INDI::IHorizontalCoordinates ActualSyncPoint;
244 EquatorialToHorizontal(&RaDec, &Position, (*Itr).ObservationJulianDate, &ActualSyncPoint);
245 // Now express this coordinate as normalised direction vectors (a.k.a direction cosines)
246 ActualDirectionCosine = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint);
247 }
248 else
249 {
250 ActualDirectionCosine = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec);
251 }
252 ActualDirectionCosines.push_back(ActualDirectionCosine);
253 ActualConvexHull.MakeNewVertex(ActualDirectionCosine.x, ActualDirectionCosine.y,
254 ActualDirectionCosine.z, VertexNumber);
255 ApparentConvexHull.MakeNewVertex((*Itr).TelescopeDirection.x, (*Itr).TelescopeDirection.y,
256 (*Itr).TelescopeDirection.z, VertexNumber);
257 VertexNumber++;
258 }
259 // I should only need to do this once but it is easier to do it twice
261 return false;
264
266 return false;
267
270
271 // Make the matrices
273#ifdef CONVEX_HULL_DEBUGGING
274 int ActualFaces = 0;
275#endif
276 if (nullptr != CurrentFace)
277 {
278 do
279 {
280#ifdef CONVEX_HULL_DEBUGGING
281 ActualFaces++;
282#endif
283 if ((0 == CurrentFace->vertex[0]->vnum) || (0 == CurrentFace->vertex[1]->vnum) ||
284 (0 == CurrentFace->vertex[2]->vnum))
285 {
286#ifdef CONVEX_HULL_DEBUGGING
287 ASSDEBUGF("Initialise - Ignoring actual face %d", ActualFaces);
288#endif
289 }
290 else
291 {
292#ifdef CONVEX_HULL_DEBUGGING
293 ASSDEBUGF("Initialise - Processing actual face %d v1 %d v2 %d v3 %d", ActualFaces,
294 CurrentFace->vertex[0]->vnum, CurrentFace->vertex[1]->vnum,
295 CurrentFace->vertex[2]->vnum);
296#endif
298 ActualDirectionCosines[CurrentFace->vertex[1]->vnum - 1],
299 ActualDirectionCosines[CurrentFace->vertex[2]->vnum - 1],
300 SyncPoints[CurrentFace->vertex[0]->vnum - 1].TelescopeDirection,
301 SyncPoints[CurrentFace->vertex[1]->vnum - 1].TelescopeDirection,
302 SyncPoints[CurrentFace->vertex[2]->vnum - 1].TelescopeDirection,
303 CurrentFace->pMatrix, nullptr);
304 }
305 CurrentFace = CurrentFace->next;
306 }
307 while (CurrentFace != ActualConvexHull.faces);
308 }
309
310 // One of these days I will optimise this
311 CurrentFace = ApparentConvexHull.faces;
312#ifdef CONVEX_HULL_DEBUGGING
313 int ApparentFaces = 0;
314#endif
315 if (nullptr != CurrentFace)
316 {
317 do
318 {
319#ifdef CONVEX_HULL_DEBUGGING
320 ApparentFaces++;
321#endif
322 if ((0 == CurrentFace->vertex[0]->vnum) || (0 == CurrentFace->vertex[1]->vnum) ||
323 (0 == CurrentFace->vertex[2]->vnum))
324 {
325#ifdef CONVEX_HULL_DEBUGGING
326 ASSDEBUGF("Initialise - Ignoring apparent face %d", ApparentFaces);
327#endif
328 }
329 else
330 {
331#ifdef CONVEX_HULL_DEBUGGING
332 ASSDEBUGF("Initialise - Processing apparent face %d v1 %d v2 %d v3 %d", ApparentFaces,
333 CurrentFace->vertex[0]->vnum, CurrentFace->vertex[1]->vnum,
334 CurrentFace->vertex[2]->vnum);
335#endif
336 CalculateTransformMatrices(SyncPoints[CurrentFace->vertex[0]->vnum - 1].TelescopeDirection,
337 SyncPoints[CurrentFace->vertex[1]->vnum - 1].TelescopeDirection,
338 SyncPoints[CurrentFace->vertex[2]->vnum - 1].TelescopeDirection,
339 ActualDirectionCosines[CurrentFace->vertex[0]->vnum - 1],
340 ActualDirectionCosines[CurrentFace->vertex[1]->vnum - 1],
341 ActualDirectionCosines[CurrentFace->vertex[2]->vnum - 1],
342 CurrentFace->pMatrix, nullptr);
343 }
344 CurrentFace = CurrentFace->next;
345 }
346 while (CurrentFace != ApparentConvexHull.faces);
347 }
348
349#ifdef CONVEX_HULL_DEBUGGING
350 ASSDEBUGF("Initialise - ActualFaces %d ApparentFaces %d", ActualFaces, ApparentFaces);
351 ActualConvexHull.PrintObj("ActualHull.obj");
353 ApparentConvexHull.PrintObj("ApparentHull.obj");
355#endif
356 return true;
357 }
358 }
359}
360
361bool BasicMathPlugin::TransformCelestialToTelescope(const double RightAscension, const double Declination,
362 double JulianOffset,
363 TelescopeDirectionVector &ApparentTelescopeDirectionVector)
364{
365 return TransformCelestialToTelescopeJD(RightAscension, Declination,
366 ln_get_julian_from_sys() + JulianOffset,
367 ApparentTelescopeDirectionVector);
368}
369
370bool BasicMathPlugin::TransformCelestialToTelescopeJD(double RightAscension, double Declination,
371 double JulianDate,
372 TelescopeDirectionVector &ApparentTelescopeDirectionVector)
373{
375 ActualRaDec.rightascension = RightAscension;
376 ActualRaDec.declination = Declination;
377 IGeographicCoordinates Position { 0, 0, 0 };
378
379 // Should check that this the same as the current observing position
381 return false;
382
384 switch (SyncPoints.size())
385 {
386 case 0:
387 {
388 // 0 sync points
390 {
391 case ZENITH:
393 EquatorialToHorizontal(&ActualRaDec, &Position, JulianDate, &ActualAltAz);
394 ApparentTelescopeDirectionVector = TelescopeDirectionVectorFromAltitudeAzimuth(ActualAltAz);
395 ASSDEBUGF("Celestial to telescope - Actual Az %lf Alt %lf", ActualAltAz.azimuth, ActualAltAz.altitude);
396 break;
397
399 // Rotate the TDV coordinate system clockwise (negative) around the y axis by 90 minus
400 // the (positive)observatory latitude. The vector itself is rotated anticlockwise
401 //ApparentTelescopeDirectionVector.RotateAroundY(Position.latitude - 90.0);
403 // Rotate the TDV coordinate system anticlockwise (positive) around the y axis by 90 plus
404 // the (negative)observatory latitude. The vector itself is rotated clockwise
405 //ApparentTelescopeDirectionVector.RotateAroundY(Position.latitude + 90.0);
406 ApparentTelescopeDirectionVector = TelescopeDirectionVectorFromEquatorialCoordinates(ActualRaDec);
407 break;
408 }
409 break;
410 }
411 case 1:
412 case 2:
413 case 3:
414 {
415 TelescopeDirectionVector ActualVector;
417 {
419 EquatorialToHorizontal(&ActualRaDec, &Position, JulianDate, &ActualAltAz);
420 ActualVector = TelescopeDirectionVectorFromAltitudeAzimuth(ActualAltAz);
421 }
422 else
423 {
424 ActualVector = TelescopeDirectionVectorFromEquatorialCoordinates(ActualRaDec);
425 }
426 gsl_vector *pGSLActualVector = gsl_vector_alloc(3);
427 gsl_vector_set(pGSLActualVector, 0, ActualVector.x);
428 gsl_vector_set(pGSLActualVector, 1, ActualVector.y);
429 gsl_vector_set(pGSLActualVector, 2, ActualVector.z);
430 gsl_vector *pGSLApparentVector = gsl_vector_alloc(3);
431 MatrixVectorMultiply(pActualToApparentTransform, pGSLActualVector, pGSLApparentVector);
432 ApparentTelescopeDirectionVector.x = gsl_vector_get(pGSLApparentVector, 0);
433 ApparentTelescopeDirectionVector.y = gsl_vector_get(pGSLApparentVector, 1);
434 ApparentTelescopeDirectionVector.z = gsl_vector_get(pGSLApparentVector, 2);
435 ApparentTelescopeDirectionVector.Normalise();
436 gsl_vector_free(pGSLActualVector);
437 gsl_vector_free(pGSLApparentVector);
438 break;
439 }
440
441 default:
442 {
443 TelescopeDirectionVector ActualVector;
445 {
447 EquatorialToHorizontal(&ActualRaDec, &Position, JulianDate, &ActualAltAz);
448 ActualVector = TelescopeDirectionVectorFromAltitudeAzimuth(ActualAltAz);
449 }
450 else
451 {
452 ActualVector = TelescopeDirectionVectorFromEquatorialCoordinates(ActualRaDec);
453 }
454
455 gsl_matrix *pTransform;
456 gsl_matrix *pComputedTransform = nullptr;
457 // Scale the actual telescope direction vector to make sure it traverses the unit sphere.
458 TelescopeDirectionVector ScaledActualVector = ActualVector * 2.0;
459 // Shoot the scaled vector in the into the list of actual facets
460 // and use the conversuion matrix from the one it intersects
462#ifdef CONVEX_HULL_DEBUGGING
463 int ActualFaces = 0;
464#endif
465 if (nullptr != CurrentFace)
466 {
467 do
468 {
469#ifdef CONVEX_HULL_DEBUGGING
470 ActualFaces++;
471#endif
472 // Ignore faces containing vertex 0 (nadir).
473 if ((0 == CurrentFace->vertex[0]->vnum) || (0 == CurrentFace->vertex[1]->vnum) ||
474 (0 == CurrentFace->vertex[2]->vnum))
475 {
476#ifdef CONVEX_HULL_DEBUGGING
477 ASSDEBUGF("Celestial to telescope - Ignoring actual face %d", ActualFaces);
478#endif
479 }
480 else
481 {
482#ifdef CONVEX_HULL_DEBUGGING
483 ASSDEBUGF("Celestial to telescope - Processing actual face %d v1 %d v2 %d v3 %d", ActualFaces,
484 CurrentFace->vertex[0]->vnum, CurrentFace->vertex[1]->vnum,
485 CurrentFace->vertex[2]->vnum);
486#endif
487 if (RayTriangleIntersection(ScaledActualVector,
488 ActualDirectionCosines[CurrentFace->vertex[0]->vnum - 1],
489 ActualDirectionCosines[CurrentFace->vertex[1]->vnum - 1],
490 ActualDirectionCosines[CurrentFace->vertex[2]->vnum - 1]))
491 break;
492 }
493 CurrentFace = CurrentFace->next;
494 }
495 while (CurrentFace != ActualConvexHull.faces);
496 if (CurrentFace == ActualConvexHull.faces)
497 {
498 // Find the three nearest points and build a transform
499 std::map<double, const AlignmentDatabaseEntry *> NearestMap;
500 for (InMemoryDatabase::AlignmentDatabaseType::const_iterator Itr = SyncPoints.begin();
501 Itr != SyncPoints.end(); Itr++)
502 {
504 TelescopeDirectionVector ActualDirectionCosine;
505 RaDec.rightascension = (*Itr).RightAscension;
506 RaDec.declination = (*Itr).Declination;
508 {
510 EquatorialToHorizontal(&RaDec, &Position, (*Itr).ObservationJulianDate, &ActualPoint);
511 ActualDirectionCosine = TelescopeDirectionVectorFromAltitudeAzimuth(ActualPoint);
512 }
513 else
514 {
515 ActualDirectionCosine = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec);
516 }
517 NearestMap[(ActualDirectionCosine - ActualVector).Length()] = &(*Itr);
518 }
519 // First compute local horizontal coordinates for the three sync points
520 std::map<double, const AlignmentDatabaseEntry *>::const_iterator Nearest = NearestMap.begin();
521 const AlignmentDatabaseEntry *pEntry1 = (*Nearest).second;
522 Nearest++;
523 const AlignmentDatabaseEntry *pEntry2 = (*Nearest).second;
524 Nearest++;
525 const AlignmentDatabaseEntry *pEntry3 = (*Nearest).second;
529 TelescopeDirectionVector ActualDirectionCosine1;
530 TelescopeDirectionVector ActualDirectionCosine2;
531 TelescopeDirectionVector ActualDirectionCosine3;
532 RaDec1.declination = pEntry1->Declination;
533 RaDec1.rightascension = pEntry1->RightAscension;
534 RaDec2.declination = pEntry2->Declination;
535 RaDec2.rightascension = pEntry2->RightAscension;
536 RaDec3.declination = pEntry3->Declination;
537 RaDec3.rightascension = pEntry3->RightAscension;
538
540 {
541 INDI::IHorizontalCoordinates ActualSyncPoint1;
542 INDI::IHorizontalCoordinates ActualSyncPoint2;
543 INDI::IHorizontalCoordinates ActualSyncPoint3;
544 EquatorialToHorizontal(&RaDec1, &Position, pEntry1->ObservationJulianDate, &ActualSyncPoint1);
545 EquatorialToHorizontal(&RaDec2, &Position, pEntry2->ObservationJulianDate, &ActualSyncPoint2);
546 EquatorialToHorizontal(&RaDec3, &Position, pEntry3->ObservationJulianDate, &ActualSyncPoint3);
547
548 // Now express these coordinates as normalised direction vectors (a.k.a direction cosines)
549 ActualDirectionCosine1 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint1);
550 ActualDirectionCosine2 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint2);
551 ActualDirectionCosine3 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint3);
552 }
553 else
554 {
555 ActualDirectionCosine1 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec1);
556 ActualDirectionCosine2 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec2);
557 ActualDirectionCosine3 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec3);
558 }
559
560 pComputedTransform = gsl_matrix_alloc(3, 3);
561 CalculateTransformMatrices(ActualDirectionCosine1, ActualDirectionCosine2, ActualDirectionCosine3,
562 pEntry1->TelescopeDirection, pEntry2->TelescopeDirection,
563 pEntry3->TelescopeDirection, pComputedTransform, nullptr);
564 pTransform = pComputedTransform;
565 }
566 else
567 pTransform = CurrentFace->pMatrix;
568 }
569 else
570 return false;
571
572 // OK - got an intersection - CurrentFace is pointing at the face
573 gsl_vector *pGSLActualVector = gsl_vector_alloc(3);
574 gsl_vector_set(pGSLActualVector, 0, ActualVector.x);
575 gsl_vector_set(pGSLActualVector, 1, ActualVector.y);
576 gsl_vector_set(pGSLActualVector, 2, ActualVector.z);
577 gsl_vector *pGSLApparentVector = gsl_vector_alloc(3);
578 MatrixVectorMultiply(pTransform, pGSLActualVector, pGSLApparentVector);
579 ApparentTelescopeDirectionVector.x = gsl_vector_get(pGSLApparentVector, 0);
580 ApparentTelescopeDirectionVector.y = gsl_vector_get(pGSLApparentVector, 1);
581 ApparentTelescopeDirectionVector.z = gsl_vector_get(pGSLApparentVector, 2);
582 ApparentTelescopeDirectionVector.Normalise();
583 gsl_vector_free(pGSLActualVector);
584 gsl_vector_free(pGSLApparentVector);
585 if (nullptr != pComputedTransform)
586 gsl_matrix_free(pComputedTransform);
587 break;
588 }
589 }
590
591 // INDI::IHorizontalCoordinates ApparentAltAz;
592 // AltitudeAzimuthFromTelescopeDirectionVector(ApparentTelescopeDirectionVector, ApparentAltAz);
593 // ASSDEBUGF("Celestial to telescope - Apparent Az %lf Alt %lf", ApparentAltAz.azimuth, ApparentAltAz.altitude);
594
595 return true;
596}
597
599 double &RightAscension, double &Declination, double JulianOffset)
600{
601 return TransformTelescopeToCelestialJD(ApparentTelescopeDirectionVector, RightAscension, Declination,
602 ln_get_julian_from_sys() + JulianOffset);
603}
604
606 double &RightAscension, double &Declination, double JulianDate)
607{
608 IGeographicCoordinates Position;
609
610 //INDI::IHorizontalCoordinates ApparentAltAz;
613
614 // AltitudeAzimuthFromTelescopeDirectionVector(ApparentTelescopeDirectionVector, ApparentAltAz);
615 // ASSDEBUGF("Telescope to celestial - Apparent Az %lf Alt %lf", ApparentAltAz.azimuth, ApparentAltAz.altitude);
616
618 {
619 // Should check that this the same as the current observing position
620 ASSDEBUG("No database or no position in database");
621 return false;
622 }
624 switch (SyncPoints.size())
625 {
626 case 0:
627 {
628 // 0 sync points
629
631 {
632 // For Alt-Az mounts, get Alt-Az from the telescope direction vector first
633 // Then transform to actual RA/DE
634 case ZENITH:
635 {
636 ASSDEBUGF("ApparentVector x %lf y %lf z %lf", ApparentTelescopeDirectionVector.x,
637 ApparentTelescopeDirectionVector.y, ApparentTelescopeDirectionVector.z);
638 //ASSDEBUGF("ActualVector x %lf y %lf z %lf", RotatedTDV.x, RotatedTDV.y, RotatedTDV.z);
639 AltitudeAzimuthFromTelescopeDirectionVector(ApparentTelescopeDirectionVector, ActualAltAz);
640 HorizontalToEquatorial(&ActualAltAz, &Position, JulianDate, &ActualRaDec);
641 }
642 break;
643
644 // For equatorial mount with zero sync points, just convert back from telescope
645 // direction vector to equatorial coordinates.
648 EquatorialCoordinatesFromTelescopeDirectionVector(ApparentTelescopeDirectionVector, ActualRaDec);
649 break;
650 }
651
652 RightAscension = ActualRaDec.rightascension;
653 Declination = ActualRaDec.declination;
654 break;
655 }
656 case 1:
657 case 2:
658 case 3:
659 {
660 gsl_vector *pGSLApparentVector = gsl_vector_alloc(3);
661 gsl_vector_set(pGSLApparentVector, 0, ApparentTelescopeDirectionVector.x);
662 gsl_vector_set(pGSLApparentVector, 1, ApparentTelescopeDirectionVector.y);
663 gsl_vector_set(pGSLApparentVector, 2, ApparentTelescopeDirectionVector.z);
664 gsl_vector *pGSLActualVector = gsl_vector_alloc(3);
665 MatrixVectorMultiply(pApparentToActualTransform, pGSLApparentVector, pGSLActualVector);
666
667 Dump3("ApparentVector", pGSLApparentVector);
668 Dump3("ActualVector", pGSLActualVector);
669
670 TelescopeDirectionVector ActualTelescopeDirectionVector;
671 ActualTelescopeDirectionVector.x = gsl_vector_get(pGSLActualVector, 0);
672 ActualTelescopeDirectionVector.y = gsl_vector_get(pGSLActualVector, 1);
673 ActualTelescopeDirectionVector.z = gsl_vector_get(pGSLActualVector, 2);
674 ActualTelescopeDirectionVector.Normalise();
676 {
677 AltitudeAzimuthFromTelescopeDirectionVector(ActualTelescopeDirectionVector, ActualAltAz);
678 HorizontalToEquatorial(&ActualAltAz, &Position, JulianDate, &ActualRaDec);
679 }
680 else
681 {
682 EquatorialCoordinatesFromTelescopeDirectionVector(ActualTelescopeDirectionVector, ActualRaDec);
683 }
684 RightAscension = ActualRaDec.rightascension;
685 Declination = ActualRaDec.declination;
686 gsl_vector_free(pGSLActualVector);
687 gsl_vector_free(pGSLApparentVector);
688 break;
689 }
690
691 default:
692 {
693 gsl_matrix *pTransform;
694 gsl_matrix *pComputedTransform = nullptr;
695 // Scale the apparent telescope direction vector to make sure it traverses the unit sphere.
696 TelescopeDirectionVector ScaledApparentVector = ApparentTelescopeDirectionVector * 2.0;
697 // Shoot the scaled vector in the into the list of apparent facets
698 // and use the conversuion matrix from the one it intersects
700#ifdef CONVEX_HULL_DEBUGGING
701 int ApparentFaces = 0;
702#endif
703 if (nullptr != CurrentFace)
704 {
705 do
706 {
707#ifdef CONVEX_HULL_DEBUGGING
708 ApparentFaces++;
709#endif
710 // Ignore faces containing vertex 0 (nadir).
711 if ((0 == CurrentFace->vertex[0]->vnum) || (0 == CurrentFace->vertex[1]->vnum) ||
712 (0 == CurrentFace->vertex[2]->vnum))
713 {
714#ifdef CONVEX_HULL_DEBUGGING
715 ASSDEBUGF("Celestial to telescope - Ignoring apparent face %d", ApparentFaces);
716#endif
717 }
718 else
719 {
720#ifdef CONVEX_HULL_DEBUGGING
721 ASSDEBUGF("TelescopeToCelestial - Processing apparent face %d v1 %d v2 %d v3 %d", ApparentFaces,
722 CurrentFace->vertex[0]->vnum, CurrentFace->vertex[1]->vnum,
723 CurrentFace->vertex[2]->vnum);
724#endif
725 if (RayTriangleIntersection(ScaledApparentVector,
726 SyncPoints[CurrentFace->vertex[0]->vnum - 1].TelescopeDirection,
727 SyncPoints[CurrentFace->vertex[1]->vnum - 1].TelescopeDirection,
728 SyncPoints[CurrentFace->vertex[2]->vnum - 1].TelescopeDirection))
729 break;
730 }
731 CurrentFace = CurrentFace->next;
732 }
733 while (CurrentFace != ApparentConvexHull.faces);
734 if (CurrentFace == ApparentConvexHull.faces)
735 {
736 // Find the three nearest points and build a transform
737 std::map<double, const AlignmentDatabaseEntry *> NearestMap;
738 for (InMemoryDatabase::AlignmentDatabaseType::const_iterator Itr = SyncPoints.begin();
739 Itr != SyncPoints.end(); Itr++)
740 {
741 NearestMap[((*Itr).TelescopeDirection - ApparentTelescopeDirectionVector).Length()] = &(*Itr);
742 }
743 // First compute local horizontal coordinates for the three sync points
744 std::map<double, const AlignmentDatabaseEntry *>::const_iterator Nearest = NearestMap.begin();
745 const AlignmentDatabaseEntry *pEntry1 = (*Nearest).second;
746 Nearest++;
747 const AlignmentDatabaseEntry *pEntry2 = (*Nearest).second;
748 Nearest++;
749 const AlignmentDatabaseEntry *pEntry3 = (*Nearest).second;
753 TelescopeDirectionVector ActualDirectionCosine1;
754 TelescopeDirectionVector ActualDirectionCosine2;
755 TelescopeDirectionVector ActualDirectionCosine3;
756 RaDec1.declination = pEntry1->Declination;
757 RaDec1.rightascension = pEntry1->RightAscension;
758 RaDec2.declination = pEntry2->Declination;
759 RaDec2.rightascension = pEntry2->RightAscension;
760 RaDec3.declination = pEntry3->Declination;
761 RaDec3.rightascension = pEntry3->RightAscension;
762
764 {
765 INDI::IHorizontalCoordinates ActualSyncPoint1;
766 INDI::IHorizontalCoordinates ActualSyncPoint2;
767 INDI::IHorizontalCoordinates ActualSyncPoint3;
768 EquatorialToHorizontal(&RaDec1, &Position, pEntry1->ObservationJulianDate, &ActualSyncPoint1);
769 EquatorialToHorizontal(&RaDec2, &Position, pEntry2->ObservationJulianDate, &ActualSyncPoint2);
770 EquatorialToHorizontal(&RaDec3, &Position, pEntry3->ObservationJulianDate, &ActualSyncPoint3);
771
772 // Now express these coordinates as normalised direction vectors (a.k.a direction cosines)
773 ActualDirectionCosine1 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint1);
774 ActualDirectionCosine2 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint2);
775 ActualDirectionCosine3 = TelescopeDirectionVectorFromAltitudeAzimuth(ActualSyncPoint3);
776 }
777 else
778 {
779 ActualDirectionCosine1 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec1);
780 ActualDirectionCosine2 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec2);
781 ActualDirectionCosine3 = TelescopeDirectionVectorFromEquatorialCoordinates(RaDec3);
782 }
783 pComputedTransform = gsl_matrix_alloc(3, 3);
785 pEntry3->TelescopeDirection, ActualDirectionCosine1,
786 ActualDirectionCosine2, ActualDirectionCosine3, pComputedTransform,
787 nullptr);
788 pTransform = pComputedTransform;
789 }
790 else
791 pTransform = CurrentFace->pMatrix;
792 }
793 else
794 return false;
795
796 // OK - got an intersection - CurrentFace is pointing at the face
797 gsl_vector *pGSLApparentVector = gsl_vector_alloc(3);
798 gsl_vector_set(pGSLApparentVector, 0, ApparentTelescopeDirectionVector.x);
799 gsl_vector_set(pGSLApparentVector, 1, ApparentTelescopeDirectionVector.y);
800 gsl_vector_set(pGSLApparentVector, 2, ApparentTelescopeDirectionVector.z);
801 gsl_vector *pGSLActualVector = gsl_vector_alloc(3);
802 MatrixVectorMultiply(pTransform, pGSLApparentVector, pGSLActualVector);
803 TelescopeDirectionVector ActualTelescopeDirectionVector;
804 ActualTelescopeDirectionVector.x = gsl_vector_get(pGSLActualVector, 0);
805 ActualTelescopeDirectionVector.y = gsl_vector_get(pGSLActualVector, 1);
806 ActualTelescopeDirectionVector.z = gsl_vector_get(pGSLActualVector, 2);
807 ActualTelescopeDirectionVector.Normalise();
809 {
810 AltitudeAzimuthFromTelescopeDirectionVector(ActualTelescopeDirectionVector, ActualAltAz);
811 HorizontalToEquatorial(&ActualAltAz, &Position, JulianDate, &ActualRaDec);
812 }
813 else
814 {
815 EquatorialCoordinatesFromTelescopeDirectionVector(ActualTelescopeDirectionVector, ActualRaDec);
816 }
817 // libnova works in decimal degrees so conversion is needed here
818 RightAscension = ActualRaDec.rightascension;
819 Declination = ActualRaDec.declination;
820 gsl_vector_free(pGSLActualVector);
821 gsl_vector_free(pGSLApparentVector);
822 if (nullptr != pComputedTransform)
823 gsl_matrix_free(pComputedTransform);
824 break;
825 }
826 }
827 //ASSDEBUGF("Telescope to Celestial - Actual Az %lf Alt %lf", ActualAltAz.azimuth, ActualAltAz.altitude);
828 return true;
829}
830
831// Private methods
832
833void BasicMathPlugin::Dump3(const char *Label, gsl_vector *pVector)
834{
835 ASSDEBUGF("Vector dump - %s", Label);
836 ASSDEBUGF("%lf %lf %lf", gsl_vector_get(pVector, 0), gsl_vector_get(pVector, 1), gsl_vector_get(pVector, 2));
837}
838
839void BasicMathPlugin::Dump3x3(const char *Label, gsl_matrix *pMatrix)
840{
841 ASSDEBUGF("Matrix dump - %s", Label);
842 ASSDEBUGF("Row 0 %lf %lf %lf", gsl_matrix_get(pMatrix, 0, 0), gsl_matrix_get(pMatrix, 0, 1),
843 gsl_matrix_get(pMatrix, 0, 2));
844 ASSDEBUGF("Row 1 %lf %lf %lf", gsl_matrix_get(pMatrix, 1, 0), gsl_matrix_get(pMatrix, 1, 1),
845 gsl_matrix_get(pMatrix, 1, 2));
846 ASSDEBUGF("Row 2 %lf %lf %lf", gsl_matrix_get(pMatrix, 2, 0), gsl_matrix_get(pMatrix, 2, 1),
847 gsl_matrix_get(pMatrix, 2, 2));
848}
849
851double BasicMathPlugin::Matrix3x3Determinant(gsl_matrix *pMatrix)
852{
853 gsl_permutation *pPermutation = gsl_permutation_alloc(3);
854 gsl_matrix *pDecomp = gsl_matrix_alloc(3, 3);
855 int Signum;
856 double Determinant;
857
858 gsl_matrix_memcpy(pDecomp, pMatrix);
859
860 gsl_linalg_LU_decomp(pDecomp, pPermutation, &Signum);
861
862 Determinant = gsl_linalg_LU_det(pDecomp, Signum);
863
864 gsl_matrix_free(pDecomp);
865 gsl_permutation_free(pPermutation);
866
867 return Determinant;
868}
869
871bool BasicMathPlugin::MatrixInvert3x3(gsl_matrix *pInput, gsl_matrix *pInversion)
872{
873 bool Retcode = true;
874 gsl_permutation *pPermutation = gsl_permutation_alloc(3);
875 gsl_matrix *pDecomp = gsl_matrix_alloc(3, 3);
876 int Signum;
877
878 gsl_matrix_memcpy(pDecomp, pInput);
879
880 gsl_linalg_LU_decomp(pDecomp, pPermutation, &Signum);
881
882 // Test for singularity
883 if (0 == gsl_linalg_LU_det(pDecomp, Signum))
884 {
885 Retcode = false;
886 }
887 else
888 gsl_linalg_LU_invert(pDecomp, pPermutation, pInversion);
889
890 gsl_matrix_free(pDecomp);
891 gsl_permutation_free(pPermutation);
892
893 return Retcode;
894}
895
898void BasicMathPlugin::MatrixMatrixMultiply(gsl_matrix *pA, gsl_matrix *pB, gsl_matrix *pC)
899{
900 // Zeroise the output matrix
901 gsl_matrix_set_zero(pC);
902
903 gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, pA, pB, 0.0, pC);
904}
905
908void BasicMathPlugin::MatrixVectorMultiply(gsl_matrix *pA, gsl_vector *pB, gsl_vector *pC)
909{
910 // Zeroise the output vector
911 gsl_vector_set_zero(pC);
912
913 gsl_blas_dgemv(CblasNoTrans, 1.0, pA, pB, 0.0, pC);
914}
915
917 TelescopeDirectionVector &TriangleVertex2,
918 TelescopeDirectionVector &TriangleVertex3)
919{
920 // Use Möller-Trumbore
921
922 //Find vectors for two edges sharing V1
923 TelescopeDirectionVector Edge1 = TriangleVertex2 - TriangleVertex1;
924 TelescopeDirectionVector Edge2 = TriangleVertex3 - TriangleVertex1;
925
926 TelescopeDirectionVector P = Ray * Edge2; // cross product
927 double Determinant = Edge1 ^ P; // dot product
928 double InverseDeterminant = 1.0 / Determinant;
929
930 // If the determinant is negative the triangle is backfacing
931 // If the determinant is close to 0, the ray misses the triangle
932 if ((Determinant > -std::numeric_limits<double>::epsilon()) &&
933 (Determinant < std::numeric_limits<double>::epsilon()))
934 return false;
935
936 // I use zero as ray origin so
937 TelescopeDirectionVector T(-TriangleVertex1.x, -TriangleVertex1.y, -TriangleVertex1.z);
938
939 // Calculate the u parameter
940 double u = (T ^ P) * InverseDeterminant;
941
942 if (u < 0.0 || u > 1.0)
943 //The intersection lies outside of the triangle
944 return false;
945
946 //Prepare to test v parameter
947 TelescopeDirectionVector Q = T * Edge1;
948
949 //Calculate v parameter and test bound
950 double v = (Ray ^ Q) * InverseDeterminant;
951
952 if (v < 0.0 || u + v > 1.0)
953 //The intersection lies outside of the triangle
954 return false;
955
956 double t = (Edge2 ^ Q) * InverseDeterminant;
957
958 if (t > std::numeric_limits<double>::epsilon())
959 {
960 //ray intersection
961 return true;
962 }
963
964 // No hit, no win
965 return false;
966}
967
968} // namespace AlignmentSubsystem
969} // namespace INDI
#define ASSDEBUG(msg)
#define ASSDEBUGF(msg,...)
bool MatrixInvert3x3(gsl_matrix *pInput, gsl_matrix *pInversion)
Calculate the inverse of the supplied matrix.
double Matrix3x3Determinant(gsl_matrix *pMatrix)
Calculate the determinant of the supplied matrix.
void MatrixMatrixMultiply(gsl_matrix *pA, gsl_matrix *pB, gsl_matrix *pC)
Multiply matrix A by matrix B and put the result in C.
virtual bool TransformTelescopeToCelestialJD(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianDate) override
Override for the base class virtual function.
void Dump3(const char *Label, gsl_vector *pVector)
Print out a 3 vector to debug.
virtual bool TransformTelescopeToCelestial(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianOffset=0) override
Compat shim - resolves JD and delegates to TransformTelescopeToCelestialJD.
virtual void CalculateTransformMatrices(const TelescopeDirectionVector &Alpha1, const TelescopeDirectionVector &Alpha2, const TelescopeDirectionVector &Alpha3, const TelescopeDirectionVector &Beta1, const TelescopeDirectionVector &Beta2, const TelescopeDirectionVector &Beta3, gsl_matrix *pAlphaToBeta, gsl_matrix *pBetaToAlpha)=0
Calculate transformation matrices from the supplied vectors.
std::vector< TelescopeDirectionVector > ActualDirectionCosines
virtual bool TransformCelestialToTelescope(const double RightAscension, const double Declination, double JulianOffset, TelescopeDirectionVector &ApparentTelescopeDirectionVector) override
Compat shim - resolves JD and delegates to TransformCelestialToTelescopeJD.
virtual bool TransformCelestialToTelescopeJD(double RightAscension, double Declination, double JulianDate, TelescopeDirectionVector &ApparentTelescopeDirectionVector) override
Override for the base class virtual function.
void MatrixVectorMultiply(gsl_matrix *pA, gsl_vector *pB, gsl_vector *pC)
Multiply matrix A by vector B and put the result in vector C.
virtual ~BasicMathPlugin()
Virtual destructor.
bool RayTriangleIntersection(TelescopeDirectionVector &Ray, TelescopeDirectionVector &TriangleVertex1, TelescopeDirectionVector &TriangleVertex2, TelescopeDirectionVector &TriangleVertex3)
Test if a ray intersects a triangle in 3d space.
virtual bool Initialise(InMemoryDatabase *pInMemoryDatabase) override
Override for the base class virtual function.
void Dump3x3(const char *Label, gsl_matrix *pMatrix)
Print out a 3x3 matrix to debug.
void EdgeOrderOnFaces()
EdgeOrderOnFaces: puts e0 between v0 and v1, e1 between v1 and v2, e2 between v2 and v0 on each face....
void Reset()
Frees the vertices edges and faces lists and resets the debug and check flags.
void MakeNewVertex(double x, double y, double z, int VertexId)
Makes a vertex from the supplied information and adds it to the vertices list.
void PrintOut(const char *FileName, tVertex v)
Prints vertices, edges and faces to the standard error output.
bool DoubleTriangle()
DoubleTriangle builds the initial double triangle. It first finds 3 noncollinear points and makes two...
void ConstructHull()
ConstructHull adds the vertices to the hull one at a time. The hull vertices are those in the list ma...
void PrintObj(const char *FileName="chull.obj")
Outputs the faces in Lightwave obj format for 3d viewing. The files chull.obj and chull....
This class provides the driver side API to the in memory alignment database.
std::vector< AlignmentDatabaseEntry > AlignmentDatabaseType
AlignmentDatabaseType & GetAlignmentDatabase()
Get a reference to the in memory database.
bool GetDatabaseReferencePosition(IGeographicCoordinates &Position)
Get the database reference position.
virtual bool Initialise(InMemoryDatabase *pInMemoryDatabase)
Initialise or re-initialise the math plugin. Re-reading the in memory database as necessary.
MountAlignment_t ApproximateMountAlignment
Describe the approximate alignment of the mount. This information is normally used in a one star alig...
Definition MathPlugin.h:128
const TelescopeDirectionVector TelescopeDirectionVectorFromAltitudeAzimuth(INDI::IHorizontalCoordinates HorizontalCoordinates)
Calculates a normalised direction vector from the supplied altitude and azimuth.
void EquatorialCoordinatesFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IEquatorialCoordinates &EquatorialCoordinates)
Calculates equatorial coordinates from the supplied telescope direction vector and declination.
const TelescopeDirectionVector TelescopeDirectionVectorFromEquatorialCoordinates(INDI::IEquatorialCoordinates EquatorialCoordinates)
Calculates a telescope direction vector from the supplied equatorial coordinates.
void AltitudeAzimuthFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IHorizontalCoordinates &HorizontalCoordinates)
Calculates an altitude and azimuth from the supplied normalised direction vector and declination.
Implementations for common driver routines.
Namespace to encapsulate INDI client, drivers, and mediator classes.
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition libastro.cpp:141
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition libastro.cpp:161
Entry in the in memory alignment database.
Definition Common.h:152
double RightAscension
Right ascension in decimal hours. N.B. libnova works in decimal degrees so conversion is always neede...
Definition Common.h:190
TelescopeDirectionVector TelescopeDirection
Normalised vector giving telescope pointing direction. This is referred to elsewhere as the "apparent...
Definition Common.h:197
double Declination
Declination in decimal degrees.
Definition Common.h:193
Holds a normalised direction vector (direction cosines)
Definition Common.h:69