INDI Alignment Layer  0.0
AlignmentDatabase.cpp
00001 
00002 #include "indidevapi.h" // For IDLog
00003 #include "AlignmentDatabase.h"
00004 #include <memory>
00005 #include <cmath>
00006 #include <sys/time.h>
00007 #include <limits>
00008 #include "Wm5DistSegment3Segment3.h"
00009 #include "Wm5IntrSegment3Triangle3.h"
00010 #include <fstream>
00011 
00012 
00013 CAlignmentDatabase::CAlignmentDatabase()
00014 {
00015     Wm5::Memory::Initialize();
00016     LowerCoordinateSystemUnits = HOURS_DEGREES;
00017 
00018 #ifdef LOAD_TEST_DATA
00019     sleep(30); // Give time to attach debugger
00020     AddAlignmentPoint(5.619669, 0.03447, 0.506809, 1.732239, 1.463808);
00021     AddAlignmentPoint(5.659376, 0.618501, 1.557218, 5.427625, 0.611563);
00022     RecalculateDatabase();
00023 #endif
00024 }
00025 
00026 CAlignmentDatabase::~CAlignmentDatabase()
00027 {
00028     Wm5::Memory::Terminate("WM5MemoryDebug.txt");
00029 }
00030 
00031 bool CAlignmentDatabase::AddAlignmentPoint(const double ReferenceTime, const double RightAscension, const double Declination,
00032                                            const double Axis1, const double Axis2, bool Recalculate)
00033 {
00034     // Save the new alignment point
00035     AlignmentPoints.push_back(AlignmentPoint(ReferenceTime, RightAscension, Declination, Axis1, Axis2));
00036 
00037     if (Recalculate)
00038         return RecalculateDatabase();
00039     else
00040         return true;
00041 }
00042 
00043 bool CAlignmentDatabase::RecalculateDatabase()
00044 {
00045     Wm5::Vector3d* UpperDirectionCosines;
00046     Wm5::Vector3d* LowerDirectionCosines;
00047     std::auto_ptr<Wm5::ConvexHull3d> UpperLayerConvexHull;
00048 
00049     // TODO make this work with one and two alignment points
00050     if (AlignmentPoints.size() < 3)
00051     {
00052         IDLog("CAlignmentDatabase::RecalculateDatabase - Too few alignment points to generate matrices\n");
00053         return true;
00054     }
00055 
00056     // Allocate Vector3 arrays large enough to hold all the direction cosines
00057     UpperDirectionCosines = new Wm5::Vector3d[AlignmentPoints.size() + 1];
00058     LowerDirectionCosines = new Wm5::Vector3d[AlignmentPoints.size() + 1];
00059 
00060     // Set centre points to complete convex hulls
00061     UpperDirectionCosines[0] = Wm5::Vector3d(0.0, 0.0, 0.0);
00062     LowerDirectionCosines[0] = Wm5::Vector3d(0.0, 0.0, 0.0);
00063 
00064     // Iterate through all the alignment points generating new sets of rectangular coordinates (direction cosines) to give to the convex hull classes
00065     int iCount = 1;
00066     for (std::vector<AlignmentPoint>::iterator i = AlignmentPoints.begin() ; i != AlignmentPoints.end(); i++)
00067     {
00068         UpperDirectionCosines[iCount] = MakeUpperDirectionCosine((*i).RightAscension, (*i).Declination, (*i).ReferenceTime);
00069 
00070         LowerDirectionCosines[iCount] = MakeLowerDirectionCosine((*i).Axis1, (*i).Axis2, (*i).ReferenceTime);
00071 
00072         iCount++;
00073     }
00074 
00075 
00076     // Set up the hulls
00077     UpperLayerConvexHull.reset(new Wm5::ConvexHull3d(iCount, UpperDirectionCosines, Epsilon, false, Wm5::Query::QT_RATIONAL));
00078 
00079     // Sanity checks
00080     int UpperLayerDimension = UpperLayerConvexHull->GetDimension();
00081     int UpperLayerFacets = UpperLayerConvexHull->GetNumSimplices();
00082     const int *UpperIndices = UpperLayerConvexHull->GetIndices();
00083     if (UpperLayerDimension < 3)
00084     {
00085         IDLog("CAlignmentDatabase::AddAlignmentPoint - UpperLayerConvexHull not 3d (%d)\n", UpperLayerDimension);
00086         delete[] UpperDirectionCosines;
00087         delete[] LowerDirectionCosines;
00088         return false;
00089     }
00090 
00091     // Reset the database
00092     UpperLayerTinFacets.clear();
00093     LowerLayerTinFacets.clear();
00094 
00095     // Iterate through the facets of the upper layer convex hull;
00096     for (int i = 0; i < UpperLayerFacets; i++)
00097     {
00098         TinFacet UpperLayerFacet;
00099         TinFacet LowerLayerFacet;
00100 
00101         // Construct the triangular facets
00102         UpperLayerFacet.Facet = Wm5::Triangle3d(UpperDirectionCosines[UpperIndices[3 * i + 0]],
00103                                                 UpperDirectionCosines[UpperIndices[3 * i + 1]],
00104                                                 UpperDirectionCosines[UpperIndices[3 * i + 2]]);
00105         LowerLayerFacet.Facet = Wm5::Triangle3d(LowerDirectionCosines[UpperIndices[3 * i + 0]],
00106                                                 LowerDirectionCosines[UpperIndices[3 * i + 1]],
00107                                                 LowerDirectionCosines[UpperIndices[3 * i + 2]]);
00108 
00109         // Compute the matrices
00110 
00111         Wm5::Vector3d UpperVertex1 = UpperLayerFacet.Facet.V[0];
00112         Wm5::Vector3d UpperVertex2 = UpperLayerFacet.Facet.V[1];
00113         Wm5::Vector3d UpperVertex3 = UpperLayerFacet.Facet.V[2];
00114         Wm5::Vector3d LowerVertex1 = LowerLayerFacet.Facet.V[0];
00115         Wm5::Vector3d LowerVertex2 = LowerLayerFacet.Facet.V[1];
00116         Wm5::Vector3d LowerVertex3 = LowerLayerFacet.Facet.V[2];
00117 
00118         if ((UpperVertex1 == Wm5::Vector3d::ZERO) ||
00119             (UpperVertex2 == Wm5::Vector3d::ZERO) ||
00120             (UpperVertex3 == Wm5::Vector3d::ZERO))
00121         {
00122             // One of the vertices is the origin. So this facet does not lie on the
00123             // surface of the unit sphere. However the two vertices that are not the
00124             // origin will define a line segment of the border of the triangulated area (TIN)
00125             // Compute an artificially generated third point (vector cross product) to replace
00126             // the origin vertex
00127             Wm5::Segment3d BoundarySegment;
00128             if (UpperVertex1 == Wm5::Vector3d::ZERO)
00129             {
00130                 UpperVertex1 = UpperVertex2.UnitCross(UpperVertex3);
00131                 LowerVertex1 = LowerVertex2.UnitCross(LowerVertex3);
00132             }
00133             else if (UpperVertex2 == Wm5::Vector3d::ZERO)
00134             {
00135                 UpperVertex2 = UpperVertex1.UnitCross(UpperVertex3);
00136                 LowerVertex2 = LowerVertex1.UnitCross(LowerVertex3);
00137             }
00138             else
00139             {
00140                 UpperVertex3 = UpperVertex1.UnitCross(UpperVertex2);
00141                 LowerVertex3 = LowerVertex1.UnitCross(LowerVertex2);
00142             }
00143         }
00144 
00145         Wm5::Matrix3d UpperMatrix(UpperVertex1.X(), UpperVertex2.X(), UpperVertex3.X(),
00146                                   UpperVertex1.Y(), UpperVertex2.Y(), UpperVertex3.Y(),
00147                                   UpperVertex1.Z(), UpperVertex2.Z(), UpperVertex3.Z());
00148         Wm5::Matrix3d LowerMatrix(LowerVertex1.X(), LowerVertex2.X(), LowerVertex3.X(),
00149                                   LowerVertex1.Y(), LowerVertex2.Y(), LowerVertex3.Y(),
00150                                   LowerVertex1.Z(), LowerVertex2.Z(), LowerVertex3.Z());
00151         UpperLayerFacet.TransformationMatrix = LowerMatrix * UpperMatrix.Inverse();
00152         LowerLayerFacet.TransformationMatrix = UpperLayerFacet.TransformationMatrix.Inverse();
00153 
00154         // Update the database
00155         UpperLayerTinFacets.push_back(UpperLayerFacet);
00156         LowerLayerTinFacets.push_back(LowerLayerFacet);
00157     }
00158     delete[] UpperDirectionCosines;
00159     delete[] LowerDirectionCosines;
00160     return true;
00161 }
00162 
00163 bool CAlignmentDatabase::TransformUpperToLower(const double RightAscension, const double Declination, double& Axis1, double& Axis2)
00164 {
00165     const double ApproximateGMST = UTCNow();
00166 
00167     Wm5::Vector3d UpperDirectionCosine = MakeUpperDirectionCosine(RightAscension, Declination, ApproximateGMST);
00168 
00169     Wm5::Matrix3d TransformationMatrix = FindTransformationmatrix(UpperDirectionCosine, UPPER_TO_LOWER);
00170 
00171     Wm5::Vector3d LowerDirectionCosine = TransformationMatrix * UpperDirectionCosine;
00172 
00173     double Polar1 = atan2(LowerDirectionCosine[1], LowerDirectionCosine[0]); // Radians minus PI to plus PI
00174     double Polar2 = asin(LowerDirectionCosine[2]); // Radians minus PI/2 to plus PI/2
00175 
00176     switch (LowerCoordinateSystemUnits)
00177     {
00178         case HOURS_DEGREES:
00179         {
00180             Axis1 = DummyHourAngleToRightAscension(Polar1, ApproximateGMST);
00181             Axis2 = Polar2 * 180.0 / M_PI;
00182             break;
00183         }
00184         case DEGREES_DEGREES:
00185         {
00186             Axis1 = Polar1 * 180.0 / M_PI;
00187             Axis2 = Polar2 * 180.0 / M_PI;
00188             break;
00189         }
00190         default:
00191         case RADIANS_RADIANS:
00192         {
00193             Axis1 = Polar1;
00194             Axis2 = Polar2;
00195             break;
00196         }
00197     }
00198 
00199     IDLog("CAlignmentDatabase::TransformUpperToLower RightAscension %g Declination %g Axis1 %g Axis2 %g\n", RightAscension,
00200                                                                                 Declination,
00201                                                                                 Axis1,
00202                                                                                 Axis2);
00203 
00204     return true;
00205 }
00206 
00207 bool CAlignmentDatabase::TransformLowerToUpper(const double Axis1, const double Axis2, double& RightAscension, double& Declination)
00208 {
00209     const double ApproximateGMST = UTCNow();
00210     Wm5::Vector3d LowerDirectionCosine = MakeLowerDirectionCosine(Axis1, Axis2, ApproximateGMST);
00211 
00212     Wm5::Matrix3d TransformationMatrix = FindTransformationmatrix(LowerDirectionCosine, LOWER_TO_UPPER);
00213 
00214     Wm5::Vector3d UpperDirectionCosine = TransformationMatrix * LowerDirectionCosine;
00215     RightAscension = DummyHourAngleToRightAscension(atan2(UpperDirectionCosine[1], UpperDirectionCosine[0]), ApproximateGMST);
00216     Declination = asin(UpperDirectionCosine[2]) * 180.0 / M_PI; // minus PI/2 to plus PI/2
00217 
00218     IDLog("CAlignmentDatabase::TransformLowerToUpper Axis1 %g Axis2 %g RightAscension %g Declination %g\n", Axis1,
00219                                                                                 Axis2,
00220                                                                                 RightAscension,
00221                                                                                 Declination);
00222 
00223     return true;
00224 }
00225 
00226 double CAlignmentDatabase::RightAscensionToDummyHourAngle(const double RightAscension, const double ReferenceTime)
00227 {
00228     double DummyHourAngle =  (RightAscension * M_PI / 12.0) - 1.002737908 * (ReferenceTime * M_PI / 12.0);
00229     IDLog("CAlignmentDatabase::RightAscensionToDummyHourAngle - RightAscension %g ReferenceTime %g DummyHourAngle %g\n", RightAscension,
00230                                                                                                                         ReferenceTime,
00231                                                                                                                         DummyHourAngle);
00232     return DummyHourAngle;
00233 }
00234 
00235 double CAlignmentDatabase::DummyHourAngleToRightAscension(const double DummyHourAngle, const double ReferenceTime)
00236 {
00237     // Normalise the hour angle;
00238     double NormalisedHourAngle;
00239     if (DummyHourAngle < 0)
00240         NormalisedHourAngle = 2 * M_PI + DummyHourAngle;
00241     else
00242         NormalisedHourAngle = DummyHourAngle;
00243     double RightAscension = (NormalisedHourAngle * 12.0 / M_PI) + 1.002737908 * ReferenceTime;
00244     IDLog("CAlignmentDatabase::DummyHourAngleToRightAscension - DummyHourAngle %g ReferenceTime %g RightAscension %g\n", DummyHourAngle,
00245                                                                                                                         ReferenceTime,
00246                                                                                                                         RightAscension);
00247     return RightAscension;
00248 }
00249 
00250 Wm5::Vector3d CAlignmentDatabase::MakeUpperDirectionCosine(const double RightAscension, const double Declination, const double ReferenceTime)
00251 {
00252     double DeclinationInRadians = Declination * M_PI / 180.0;
00253     return Wm5::Vector3d(cos(DeclinationInRadians) * cos(RightAscensionToDummyHourAngle(RightAscension, ReferenceTime)),
00254                         cos(DeclinationInRadians) * sin(RightAscensionToDummyHourAngle(RightAscension, ReferenceTime)),
00255                         sin(DeclinationInRadians));
00256 }
00257 
00258 Wm5::Vector3d CAlignmentDatabase::MakeLowerDirectionCosine(const double Axis1, const double Axis2, const double ReferenceTime)
00259 {
00260     switch (LowerCoordinateSystemUnits)
00261     {
00262         case HOURS_DEGREES:
00263         {
00264             double RA = RightAscensionToDummyHourAngle(Axis1, ReferenceTime);
00265             double Dec = Axis2 * M_PI / 180.0;
00266             return Wm5::Vector3d(cos(Dec) * cos(RA), cos(Dec) * sin(RA), sin(Dec));
00267         }
00268         case DEGREES_DEGREES:
00269         {
00270 
00271             double Azimuth = -Axis1 * M_PI / 180.0;
00272             double Altitude = Axis2 * M_PI / 180.0;
00273             return Wm5::Vector3d(cos(Altitude) * cos(Azimuth), cos(Altitude) * sin(Azimuth), sin(Altitude));
00274         }
00275         default:
00276         case RADIANS_RADIANS:
00277         {
00278             double Azimuth = -Axis1;
00279             double Altitude = Axis2;
00280             return Wm5::Vector3d(cos(Altitude) * cos(Azimuth), cos(Altitude) * sin(Azimuth), sin(Altitude));
00281         }
00282     }
00283 }
00284 
00285 const double CAlignmentDatabase::UTCNow()
00286 {
00287     // I would really like this to be UT1Now but that would be harder
00288     // this will be within 0.9 seconds of UT1
00289     struct timeval now;
00290     gettimeofday(&now,NULL);
00291     struct tm breakdown = *gmtime(&now.tv_sec);
00292     return breakdown.tm_hour + breakdown.tm_min / 60.0 + breakdown.tm_sec / 3600.0 + now.tv_usec / 3600000000.0;
00293 }
00294 
00295 const Wm5::Matrix3d CAlignmentDatabase::FindTransformationmatrix(const Wm5::Vector3d DirectionCosine, Direction Direction)
00296 {
00297     Wm5::Matrix3d TransformationMatrix;
00298 
00299     TinFacets& Facets = Direction == UPPER_TO_LOWER ? UpperLayerTinFacets : LowerLayerTinFacets;
00300 
00301     // Make the direction cosine (should be a unit vector) a bit longer just in case.
00302     Wm5::Segment3d DirectionSegment(Wm5::Vector3d::ZERO, DirectionCosine * 2.0);
00303 
00304     // The facets should all approximate to a patch on the surface of a unit sphere centered on the origin.
00305     // The only exception being those facets with the origin as a vertex.
00306     // So iterate through the facets and for each facet that has the origin as vertex check the distance of closest approach
00307     // between my DirectionVector and the line segment formed by the other two facet vertices. If the facet does not have the
00308     // origin as vertex check whether the vector intersects the surface patch. If the vector intersects then return the
00309     // TrasnformationMatrix from that facet. If I reach the end without intersecting the return the TransformationMatrix from the
00310     // origin facet the vector passes nearest to (whew!).
00311 
00312     TinFacets::iterator NearestOriginFacet;
00313     double DistanceToNearestOriginSegment = std::numeric_limits<double>::max();
00314 
00315     for (TinFacets::iterator i = Facets.begin(); i != Facets.end(); i++)
00316     {
00317         Wm5::Vector3d Vertex1 = (*i).Facet.V[0];
00318         Wm5::Vector3d Vertex2 = (*i).Facet.V[1];
00319         Wm5::Vector3d Vertex3 = (*i).Facet.V[2];
00320 
00321         if ((Vertex1 == Wm5::Vector3d::ZERO) ||
00322             (Vertex2 == Wm5::Vector3d::ZERO) ||
00323             (Vertex3 == Wm5::Vector3d::ZERO))
00324         {
00325             Wm5::Segment3d BoundarySegment;
00326             if (Vertex1 == Wm5::Vector3d::ZERO)
00327                 BoundarySegment = Wm5::Segment3d(Vertex2, Vertex3);
00328             else if (Vertex2 == Wm5::Vector3d::ZERO)
00329                 BoundarySegment = Wm5::Segment3d(Vertex1, Vertex3);
00330             else
00331                 BoundarySegment = Wm5::Segment3d(Vertex1, Vertex2);
00332             Wm5::DistSegment3Segment3d Distance(DirectionSegment, BoundarySegment);
00333             double DistanceToSegment = Distance.Get();
00334             if (DistanceToSegment < DistanceToNearestOriginSegment)
00335             {
00336                 DistanceToNearestOriginSegment = DistanceToSegment;
00337                 NearestOriginFacet = i;
00338             }
00339         }
00340         else
00341         {
00342             Wm5::IntrSegment3Triangle3d Intersection(DirectionSegment, (*i).Facet);
00343             if (Intersection.Test())
00344                 return (*i).TransformationMatrix;
00345         }
00346     }
00347 
00348     if (std::numeric_limits<double>::max() == DistanceToNearestOriginSegment)
00349     {
00350         IDLog("CAlignmentDatabase::FindTransformationmatrix - FATAL cannot find transformation matrix\n");
00351         return Wm5::Matrix3d::IDENTITY;
00352     }
00353     return (*NearestOriginFacet).TransformationMatrix;
00354 }
00355 
00356 bool CAlignmentDatabase::Save(const std::string& Filename)
00357 {
00358     std::fstream SavedAlignmentPoints(Filename.c_str(), std::ios_base::out);
00359 
00360     for (std::vector<AlignmentPoint>::iterator i = AlignmentPoints.begin() ; i != AlignmentPoints.end() && !SavedAlignmentPoints.fail(); i++)
00361     {
00362         SavedAlignmentPoints << (*i).ReferenceTime << " "
00363                             << (*i).RightAscension << " "
00364                             << (*i).Declination << " "
00365                             << (*i).Axis1 << " "
00366                             << (*i).Axis2 << std::endl;
00367     }
00368     if (SavedAlignmentPoints.fail())
00369         return false;
00370     SavedAlignmentPoints.close();
00371     return true;
00372 }
00373 
00374 bool CAlignmentDatabase::Load(const std::string& Filename)
00375 {
00376     std::vector<AlignmentPoint> NewAlignmentPoints;
00377     std::fstream SavedAlignmentPoints(Filename.c_str(), std::ios_base::in);
00378     if (!SavedAlignmentPoints)
00379     {
00380         IDLog("CAlignmentDatabase::Load - Failed to open saved points file (%s)\n", Filename.c_str());
00381         return false;
00382     }
00383     double ReferenceTime;
00384     while (SavedAlignmentPoints >> ReferenceTime)
00385     {
00386         double RightAscension, Declination, Axis1, Axis2;
00387 
00388         // Not sure if I should test for eof here - I never really understood iostreams
00389 
00390         // Read rest of line
00391         SavedAlignmentPoints >> RightAscension >> Declination >> Axis1 >> Axis2;
00392 
00393         if (!SavedAlignmentPoints.eof() && !SavedAlignmentPoints.fail())
00394             NewAlignmentPoints.push_back(AlignmentPoint(ReferenceTime, RightAscension, Declination, Axis1, Axis2));
00395     }
00396     if (!SavedAlignmentPoints.eof())
00397     {
00398         IDLog("CAlignmentDatabase::Load - Failed to read saved points from file (%s)\n", Filename.c_str());
00399         return false;
00400     }
00401     SavedAlignmentPoints.close();
00402     AlignmentPoints = NewAlignmentPoints;
00403     RecalculateDatabase();
00404     return true;
00405 }
00406 
00407 bool CAlignmentDatabase::Append(const std::string& Filename)
00408 {
00409     std::vector<AlignmentPoint> NewAlignmentPoints;
00410     std::fstream SavedAlignmentPoints(Filename.c_str(), std::ios_base::in);
00411     if (!SavedAlignmentPoints)
00412     {
00413         IDLog("CAlignmentDatabase::Append - Failed to open saved points file (%s)\n", Filename.c_str());
00414         return false;
00415     }
00416     double ReferenceTime;
00417     while (SavedAlignmentPoints >> ReferenceTime)
00418     {
00419         double RightAscension, Declination, Axis1, Axis2;
00420 
00421         // Not sure if I should test for eof here - I never really understood iostreams
00422 
00423         // Read rest of line
00424         SavedAlignmentPoints >> RightAscension >> Declination >> Axis1 >> Axis2;
00425 
00426         if (!SavedAlignmentPoints.eof() && !SavedAlignmentPoints.fail())
00427             NewAlignmentPoints.push_back(AlignmentPoint(ReferenceTime, RightAscension, Declination, Axis1, Axis2));
00428     }
00429     if (!SavedAlignmentPoints.eof())
00430     {
00431         IDLog("CAlignmentDatabase::Append - Failed to read saved points from file (%s)\n", Filename.c_str());
00432         return false;
00433     }
00434     SavedAlignmentPoints.close();
00435     AlignmentPoints.insert(AlignmentPoints.end(), NewAlignmentPoints.begin(), NewAlignmentPoints.end());
00436     RecalculateDatabase();
00437     return true;
00438 }
00439 
00440 
00441