File indexing completed on 2024-04-28 15:09:04

0001 /*
0002     SPDX-FileCopyrightText: 2021 Hy Murveit <hy@murveit.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <dms.h>
0010 #include <skypoint.h>
0011 
0012 class FITSData;
0013 class TestPolarAlign;
0014 
0015 /*********************************************************************
0016  Polar alignment support class.  Note, the telescope can be pointing anywhere.
0017  It doesn't need to point at the pole.
0018 
0019  Use this class as follows:
0020  1) Construct with the geo information.
0021         PolarAlign polarAlign(geoLocation);
0022  2) Start the polar align procedure. Capture, solve and add wcs from the
0023     solve to the FITSData image. Then:
0024         polarAlign.addPoint(image);
0025  3) Rotate the mount in RA ~30 degrees (could be less or more) east (or west)
0026     and capture, solve and add wcs from the solve to the new image. Then:
0027         polarAlign.addPoint(image);
0028  4) Rotate the mount in RA another ~30 degrees east (or west)
0029     and capture, solve and add wcs from the solve to the new image. Then:
0030         polarAlign.addPoint(image);
0031  5) Find the mount's axis of rotation as follows:
0032         if (!polarAlign.findAxis())
0033           error();
0034  6) Compute the azimuth and altitude offset for the mount.
0035         double altitudeError = axisAlt - latitudeDegrees;
0036         double azimuthError = axisAz - 0;
0037  7) Compute the overall error
0038         dms polarError(hypot(azimuthError, altitudeError));
0039 
0040 Using the "move star" correction scheme:
0041 
0042  8a) Compute a target correction
0043         int correctedX, correctedY;
0044         if (!polarAlign.findCorrectedPixel(
0045                imageData, x, y, altitudeError, azimuthError, &correctedX, &correctedY))
0046           error();
0047  9a) The user uses the GEM azimuth and altitude adjustments to move the
0048     object at position x,y in the image to position correctedX, correctedY.
0049 
0050 Using the plate solve correction scheme:
0051 
0052  8b) Compute the remaining axis error (after partial correction)
0053          SkyPoint coords, solution;
0054          coords = [coordinates from plate-solving a refresh image]
0055          // The signs of the error indicate the correction direction
0056          processRefreshCoords(coords, currentTime, &azError, &altError);
0057  9b) The user uses the GEM azimuth and altitude adjustments to move the telescope.
0058       positive altitude error: reduce altitide.
0059       positive azimuth error: point telescope more to the left.
0060  *********************************************************************/
0061 
0062 class PolarAlign
0063 {
0064     public:
0065 
0066         // The polealignment scheme requires the GeoLocation to operate properly.
0067         // Certain aspects can be tested without it.
0068         PolarAlign(const GeoLocation *geo = nullptr);
0069 
0070         // Add a sample point.
0071         bool addPoint(const QSharedPointer<FITSData> &image);
0072 
0073         // Returns the i-th point (zero based).
0074         const SkyPoint getPoint(int index)
0075         {
0076             if (index >= 0 && index < points.size())
0077                 return points[index];
0078             return SkyPoint();
0079         }
0080 
0081         // Finds the mount's axis of rotation. Three points must have been added.
0082         // Returns false if the axis can't be found.
0083         bool findAxis();
0084 
0085         // Returns the image coordinate that pixel x,y should be moved to to correct
0086         // the mount's axis. Image is usually the 3rd PAA image. x,y are image coordinates.
0087         // 3 Points must have been added and findAxis() must have been called.
0088         // Uses the axis determined by findAxis().  Returns correctedX and correctedY,
0089         // the target position that the x,y pixel should move to.
0090         bool findCorrectedPixel(const QSharedPointer<FITSData> &image, const QPointF &pixel,
0091                                 QPointF *corrected, bool altOnly = false);
0092 
0093         // Returns the mount's azimuth and altitude error given the known geographic location
0094         // and the azimuth center and altitude center computed in findAxis().
0095         void calculateAzAltError(double *azError, double *altError) const;
0096 
0097         // Given the current axis, fill in azError and altError with the polar alignment
0098         // error, if a star at location pixel were move in the camera view to pixel2.
0099         // image would be the 3rd PAA image.
0100         // Returns false if the paa error couldn't be computer.
0101         bool pixelError(const QSharedPointer<FITSData> &image, const QPointF &pixel, const QPointF &pixel2,
0102                         double *azError, double *altError);
0103 
0104         /// reset starts the process over, removing the points.
0105         void reset();
0106 
0107         // Returns the mount's axis--for debugging.
0108         void getAxis(double *azAxis, double *altAxis) const;
0109 
0110         // This is not required, and is a hint as to how far the pixelError method should search
0111         // for a solution. The suggestion may be ignored if it is too large or small, but in
0112         // general this should be a degree or so larger than the polar alignment error in degrees.
0113         void setMaxPixelSearchRange(double degrees);
0114 
0115         // Compute the remaining polar-alignment azimuth and altitude error from a new image's coordinates
0116         // as compared with the coordinates from the last polar-align measurement image.
0117         // The differences between the two coordinates is a measure of the rotation that has been
0118         // applied during the polar-align correction phase.
0119         bool processRefreshCoords(const SkyPoint &coords, const KStarsDateTime &time,
0120                                   double *azError, double *altError,
0121                                   double *azAdjustment = nullptr, double *altAdjustment = nullptr) const;
0122 
0123         // Find the skypoints where the telescope needs to point (rotated there by adjusting az and alt)
0124         // in order for the mount to be properly polar aligned.
0125         bool refreshSolution(SkyPoint *solution, SkyPoint *altOnlySolution) const;
0126 
0127     private:
0128         // returns true in the northern hemisphere.
0129         // if no geo location available, defaults to northern.
0130         bool northernHemisphere() const;
0131 
0132         // These internal methods find the pixel with the desired azimuth and altitude.
0133         bool findAzAlt(const QSharedPointer<FITSData> &image, double azimuth, double altitude, QPointF *pixel) const;
0134 
0135         // Does the necessary processing so that azimuth and altitude values
0136         // can be retrieved for the x,y pixel in image.
0137         bool prepareAzAlt(const QSharedPointer<FITSData> &image, const QPointF &pixel, SkyPoint *point) const;
0138 
0139 
0140         // Internal utility used by the external findCorrectedPixel and by pixelError().
0141         // Similar args as the public findCorrectedPixel().
0142         bool findCorrectedPixel(const QSharedPointer<FITSData> &image, const QPointF &pixel,
0143                                 QPointF *corrected, double azError, double altError);
0144 
0145         // Internal utility used by the public pixelError, which iterates at different
0146         // resolutions passed in to this method. As the resoltion can be coarse, actualPixel
0147         // is the one used (as opposed to pixel2) for the error returned.
0148         void pixelError(const QSharedPointer<FITSData> &image, const QPointF &pixel, const QPointF &pixel2,
0149                         double minAz, double maxAz, double azInc,
0150                         double minAlt, double maxAlt, double altInc,
0151                         double *azError, double *altError, QPointF *actualPixel);
0152 
0153         // These three positions are used to estimate the polar alignment error.
0154         QVector<SkyPoint> points;
0155         QVector<KStarsDateTime> times;
0156 
0157         // The geographic location used to compute azimuth and altitude.
0158         const GeoLocation *geoLocation;
0159 
0160         // Values set by the last call to findAxis() that correspond to the mount's axis.
0161         double azimuthCenter { 0 };
0162         double altitudeCenter { 0 };
0163 
0164         // Constrains the search for the correction pixel. Units are degrees.
0165         double maxPixelSearchRange { 2 };
0166 
0167         friend TestPolarAlign;
0168 };