Kstars

polaralign.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Hy Murveit <hy@murveit.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "polaralign.h"
8#include "poleaxis.h"
9#include "rotations.h"
10
11#include <cmath>
12
13#include "fitsviewer/fitsdata.h"
14#include "kstarsdata.h"
15#include "skypoint.h"
16#include <ekos_align_debug.h>
17
18/******************************************************************
19PolarAlign is a class that supports polar alignment by determining the
20mount's axis of rotation when given 3 solved images taken with RA mount
21rotations between the images.
22
23addPoint(image) is called by the polar alignment UI after it takes and
24solves each of its three images. The solutions are store in SkyPoints (see below)
25and are processed so that the sky positions correspond to "what's in the sky
26now" and "at this geographic localtion".
27
28Addpoint() samples the location of a particular pixel in its image.
29When the 3 points are sampled, they should not be taken
30from the center of the image, as HA rotations may not move that point
31if the telescope and mount are well aligned. Thus, the points are sampled
32from the edge of the image.
33
34After all 3 images are sampled, findAxis() is called, which solves for the mount's
35axis of rotation. It then transforms poleAxis' result into azimuth and altitude
36offsets from the pole.
37
38After the mount's current RA axis is determined, the user then attempts to correct/improve
39it to match the Earth's real polar axes. Ekos has two techniques to do that. In both
40Ekos takes a series of "refresh images". The user looks at the images and their
41associated analyses and adjusts the mount's altitude and azimuth knobs.
42
43In the first scheme, the user identifies a refrence star on the image. Ekos draws a triangle
44over the image, and user attempts to "move the star" along two sides of that triangle.
45
46In the 2nd scheme, the system plate-solves the refresh images, telling the user which direction
47and how much to adjust the knobs.
48
49findCorrectedPixel() supports the "move the star" refresh scheme.
50It is given an x,y position on an image and the offsets
51generated by findAxis(). It computes a "corrected position" for that input
52x,y point such that if a user adjusted the GEM mount's altitude and azimuth
53knobs to move a star centered in the image's original x,y position to the corrected
54position in the image, the mount's axis of rotation should then coincide with the pole.
55
56processRefreshCoords() supports the plate-solving refresh scheme.
57It is given the center coordinates of a refresh image. It remembers the originally
58calculated mount axis, and the position of the 3rd measurement image. It computes how
59much the user has already adjusted the azimuth and altitude knobs from the difference
60in pointing between the new refresh image's center coordinates and that of the 3rd measurement
61image. It infers what the mounts new RA axis must be (based on that adjustment) and returns
62the new polar alignment error.
63******************************************************************/
64
65using Rotations::V3;
66
67PolarAlign::PolarAlign(const GeoLocation *geo)
68{
69 if (geo == nullptr && KStarsData::Instance() != nullptr)
70 geoLocation = KStarsData::Instance()->geo();
71 else
72 geoLocation = geo;
73}
74
75bool PolarAlign::northernHemisphere() const
76{
77 if ((geoLocation == nullptr) || (geoLocation->lat() == nullptr))
78 return true;
79 return geoLocation->lat()->Degrees() > 0;
80}
81
82void PolarAlign::reset()
83{
84 points.clear();
85 times.clear();
86}
87
88// Gets the pixel's j2000 RA&DEC coordinates, converts to JNow, adjust to
89// the local time, and sets up the azimuth and altitude coordinates.
90bool PolarAlign::prepareAzAlt(const QSharedPointer<FITSData> &image, const QPointF &pixel, SkyPoint *point) const
91{
92 // WCS must be set up for this image.
93 SkyPoint coords;
94 if (image && image->pixelToWCS(pixel, coords))
95 {
96 coords.apparentCoord(static_cast<long double>(J2000), image->getDateTime().djd());
97 *point = SkyPoint::timeTransformed(&coords, image->getDateTime(), geoLocation, 0);
98 return true;
99 }
100 return false;
101}
102
103bool PolarAlign::addPoint(const QSharedPointer<FITSData> &image)
104{
105 SkyPoint coords;
106 auto time = image->getDateTime();
107 // Use the HA and DEC from the center of the image.
108 if (!prepareAzAlt(image, QPointF(image->width() / 2, image->height() / 2), &coords))
109 return false;
110
111 QString debugString = QString("PAA: addPoint ra0 %1 dec0 %2 ra %3 dec %4 az %5 alt %6")
112 .arg(coords.ra0().Degrees()).arg(coords.dec0().Degrees())
113 .arg(coords.ra().Degrees()).arg(coords.dec().Degrees())
114 .arg(coords.az().Degrees()).arg(coords.alt().Degrees());
115 qCInfo(KSTARS_EKOS_ALIGN) << debugString;
116 if (points.size() > 2)
117 return false;
118 points.push_back(coords);
119 times.push_back(time);
120
121 return true;
122}
123
124namespace
125{
126
127// Returns the distance, in absolute-value degrees, of taking point "from",
128// rotating it around the Y axis by yAngle, then rotating around the Z axis
129// by zAngle and comparing that with "goal".
130double getResidual(const V3 &from, double yAngle, double zAngle, const V3 &goal)
131{
132 V3 point1 = Rotations::rotateAroundY(from, yAngle);
133 V3 point2 = Rotations::rotateAroundZ(point1, zAngle);
134 return fabs(getAngle(point2, goal));
135}
136
137// Finds the best rotations to change from pointing to 'from' to pointing to 'goal'.
138// It tries 'all' possible pairs of x and y rotations (sampled by increment).
139// Note that you can't simply find the best Z rotation, and the go from there to find the best Y.
140// The space is non-linear, and that would often lead to poor solutions.
141double getBestRotation(const V3 &from, const V3 &goal,
142 double zStart, double yStart,
143 double *bestAngleZ, double *bestAngleY,
144 double range, double increment)
145{
146
147 *bestAngleZ = 0;
148 *bestAngleY = 0;
149 double minDist = 1e8;
150 range = fabs(range);
151 for (double thetaY = yStart - range; thetaY <= yStart + range; thetaY += increment)
152 {
153 for (double thetaZ = zStart - range; thetaZ <= zStart + range; thetaZ += increment)
154 {
155 double dist = getResidual(from, thetaY, thetaZ, goal);
156 if (dist < minDist)
157 {
158 minDist = dist;
161 }
162 }
163 }
164 return minDist;
165}
166
167// Computes the rotations in Y (altitude) and Z (azimuth) that brings 'from' closest to 'goal'.
168// Returns the residual (error angle between where these rotations lead and "goal".
169double getRotationAngles(const V3 &from, const V3 &goal, double *zAngle, double *yAngle)
170{
171 // All in degrees.
172 constexpr double pass1Resolution = 1.0 / 60.0;
173 constexpr double pass2Resolution = 5 / 3600.0;
174 constexpr double pass2Range = 4.0 / 60.0;
175
176 // Compute the rotation using a great circle. This somewhat constrains our search below.
177 const double rotationAngle = getAngle(from, goal); // degrees
178 const double pass1Range = std::max(1.0, std::min(5.0, fabs(rotationAngle)));
179
180 // Grid search across all y,z angle possibilities, sampling by 2 arc-minutes.
183
184 // Refine the search around the best solution so far
186}
187
188} // namespace
189
190// Compute the polar-alignment azimuth and altitude error by comparing the new image's coordinates
191// with the coordinates from the 3rd measurement image. Use the difference to infer a rotation angle,
192// and rotate the originally computed polar-alignment axis by that angle to find the new axis
193// around which RA now rotates.
194bool PolarAlign::processRefreshCoords(const SkyPoint &coords, const KStarsDateTime &time,
195 double *azError, double *altError,
196 double *azAdjustment, double *altAdjustment) const
197{
198 // Get the az and alt from this new measurement (coords), and from that derive its x,y,z coordinates.
199 auto c = coords; // apparentCoord modifies its input. Use the temp variable c to keep coords const.
200 c.apparentCoord(static_cast<long double>(J2000), time.djd());
201 SkyPoint point = SkyPoint::timeTransformed(&c, time, geoLocation, 0);
202 const double az = point.az().Degrees(), alt = point.alt().Degrees();
203 const V3 newPoint = Rotations::azAlt2xyz(QPointF(az, alt));
204
205 // Get the x,y,z coordinates of the original position (from the 3rd polar-align image).
206 // We can't simply use the az/alt already computed for point3 because the mount is tracking,
207 // and thus, even if the user made no adjustments, but simply took an image a little while
208 // later at the same RA/DEC coordinates, the Az/Alt would have changed and we'd believe there
209 // was a user rotation due to changes in the Az/Alt knobs. Instead we must convert point3's az/alt
210 // values to what they would be if that image had been taken now, still pointing at point3's ra/dec.
211 // The key is to rotate the original point around the original RA axis by the rotation given by the time
212 // difference multiplied by the sidereal rate.
213
214 // Figure out what the az/alt would be if the user hadn't modified the knobs.
215 // That is, just rotate the 3rd measurement point around the mount's original RA axis.
216 // Time since third point in seconds
217 const double p3secs = times[2].secsTo(time);
218 // Angle corresponding to that interval assuming the sidereal rate.
219 const double p3Angle = (-15.041067 * p3secs) / 3600.0; // degrees
220
221 // Get the xyz coordinates of the original 3rd point.
222 const V3 p3OrigPoint = Rotations::azAlt2xyz(QPointF(points[2].az().Degrees(), points[2].alt().Degrees()));
223 // Get the unit vector corresponding the original RA axis
224 const V3 origAxisPt = Rotations::azAlt2xyz(QPointF(azimuthCenter, altitudeCenter));
225 // Rotate the original 3rd point around that axis, simulating the mount's tracking movements.
226 const V3 point3 = Rotations::rotateAroundAxis(p3OrigPoint, origAxisPt, p3Angle);
227
228 // Find the adjustment the user must have made by examining the change from point3 to newPoint
229 // (i.e. the rotation caused by the user adjusting the azimuth and altitude knobs).
230 // We assume that this was a rotation around a level mount's y axis and z axis.
231 double zAdjustment, yAdjustment;
233 if (residual > 0.5)
234 {
235 qCInfo(KSTARS_EKOS_ALIGN) << QString("PAA refresh: failed to estimate rotation angle (residual %1'").arg(residual * 60);
236 return false;
237 }
238 qCInfo(KSTARS_EKOS_ALIGN) << QString("PAA refresh: Estimated current adjustment: Az %1' Alt %2' residual %3a-s")
239 .arg(zAdjustment * 60, 0, 'f', 1).arg(yAdjustment * 60, 0, 'f', 1).arg(residual * 3600, 0, 'f', 0);
240
241 // Return the estimated adjustments (used by testing).
242 if (altAdjustment != nullptr) *altAdjustment = yAdjustment;
243 if (azAdjustment != nullptr) *azAdjustment = zAdjustment;
244
245 // Rotate the original RA axis position by the above adjustments.
246 const V3 origAxisPoint = Rotations::azAlt2xyz(QPointF(azimuthCenter, altitudeCenter));
247 const V3 tempPoint = Rotations::rotateAroundY(origAxisPoint, yAdjustment);
248 const V3 newAxisPoint = Rotations::rotateAroundZ(tempPoint, zAdjustment);
249
250 // Convert the rotated axis point back to an az/alt coordinate, representing the new RA axis.
251 const QPointF newAxisAzAlt = Rotations::xyz2azAlt(newAxisPoint);
252 const double newAxisAz = newAxisAzAlt.x();
253 const double newAxisAlt = newAxisAzAlt.y();
254
255 // Compute the polar alignment error for the new RA axis.
256 const double latitudeDegrees = geoLocation->lat()->Degrees();
257 *altError = northernHemisphere() ? newAxisAlt - latitudeDegrees : newAxisAlt + latitudeDegrees;
258 *azError = northernHemisphere() ? newAxisAz : newAxisAz + 180.0;
259 while (*azError > 180.0)
260 *azError -= 360;
261
263 QString("PAA refresh: ra0 %1 dec0 %2 Az/Alt: %3 %4 AXIS: %5 %6 --> %7 %8 ERR: %9' alt %10'")
264 .arg(coords.ra0().Degrees(), 0, 'f', 3).arg(coords.dec0().Degrees(), 0, 'f', 3)
265 .arg(az, 0, 'f', 3).arg(alt, 0, 'f', 3)
266 .arg(azimuthCenter, 0, 'f', 3).arg(altitudeCenter, 0, 'f', 3)
267 .arg(newAxisAz, 0, 'f', 3).arg(newAxisAlt, 0, 'f', 3)
268 .arg(*azError * 60, 0, 'f', 1).arg(*altError * 60, 0, 'f', 1);
269 qCInfo(KSTARS_EKOS_ALIGN) << infoString;
270
271 return true;
272}
273
274// Given the telescope's current RA axis, and the its current pointing position,
275// compute the coordinates where it should point such that its RA axis will be at the pole.
276bool PolarAlign::refreshSolution(SkyPoint *solution, SkyPoint *altOnlySolution) const
277{
278 if (points.size() != 3)
279 return false;
280
281 double azError, altError;
282 calculateAzAltError(&azError, &altError);
283
284 // The Y rotation to correct polar alignment is -altitude error, and the Z correction is -azimuth error.
285 // Rotate the 3rd-image center coordinate by the above angles.
286 // This is the position the telescope needs to point to (if it is taken there
287 // by adjusting alt and az knobs) such that the new RA rotation axis is aligned with the pole.
288 const V3 point3 = Rotations::azAlt2xyz(QPointF(points[2].az().Degrees(), points[2].alt().Degrees()));
289 const V3 altSolutionPoint = Rotations::rotateAroundY(point3, altError);
290 const V3 solutionPoint = Rotations::rotateAroundZ(altSolutionPoint, azError);
291
292 // Convert the solution xyz points back to az/alt and ra/dec.
293 const QPointF solutionAzAlt = Rotations::xyz2azAlt(solutionPoint);
294 solution->setAz(solutionAzAlt.x());
295 solution->setAlt(solutionAzAlt.y());
296 auto lst = geoLocation->GSTtoLST(times[2].gst());
297 solution->HorizontalToEquatorial(&lst, geoLocation->lat());
298
299 // Not sure if this is needed
300 solution->setRA0(solution->ra());
301 solution->setDec0(solution->dec());
302
303 // Move the solution back to J2000
304 KSNumbers num(times[2].djd());
305 *solution = solution->deprecess(&num);
306 solution->setRA0(solution->ra());
307 solution->setDec0(solution->dec());
308
309 // Ditto for alt-only solution
310 const QPointF altOnlySolutionAzAlt = Rotations::xyz2azAlt(altSolutionPoint);
313 auto altOnlyLst = geoLocation->GSTtoLST(times[2].gst());
314 altOnlySolution->HorizontalToEquatorial(&altOnlyLst, geoLocation->lat());
315 altOnlySolution->setRA0(altOnlySolution->ra());
316 altOnlySolution->setDec0(altOnlySolution->dec());
317 KSNumbers altOnlyNum(times[2].djd());
319 altOnlySolution->setRA0(altOnlySolution->ra());
320 altOnlySolution->setDec0(altOnlySolution->dec());
321
322 return true;
323}
324
325bool PolarAlign::findAxis()
326{
327 if (points.size() != 3)
328 return false;
329
330 // We have 3 points, get their xyz positions.
331 V3 p1(Rotations::azAlt2xyz(QPointF(points[0].az().Degrees(), points[0].alt().Degrees())));
332 V3 p2(Rotations::azAlt2xyz(QPointF(points[1].az().Degrees(), points[1].alt().Degrees())));
333 V3 p3(Rotations::azAlt2xyz(QPointF(points[2].az().Degrees(), points[2].alt().Degrees())));
334 V3 axis = Rotations::getAxis(p1, p2, p3);
335
336 if (axis.length() < 0.9)
337 {
338 // It failed to normalize the vector, something's wrong.
339 qCInfo(KSTARS_EKOS_ALIGN) << "Normal vector too short. findAxis failed.";
340 return false;
341 }
342
343 // Need to make sure we're pointing to the right pole.
344 if ((northernHemisphere() && (axis.x() < 0)) || (!northernHemisphere() && axis.x() > 0))
345 {
346 axis = V3(-axis.x(), -axis.y(), -axis.z());
347 }
348
349 QPointF azAlt = Rotations::xyz2azAlt(axis);
350 azimuthCenter = azAlt.x();
351 altitudeCenter = azAlt.y();
352
353 return true;
354}
355
356void PolarAlign::getAxis(double *azAxis, double *altAxis) const
357{
358 *azAxis = azimuthCenter;
359 *altAxis = altitudeCenter;
360}
361
362// Find the pixel in image corresponding to the desired azimuth & altitude.
363bool PolarAlign::findAzAlt(const QSharedPointer<FITSData> &image, double azimuth, double altitude, QPointF *pixel) const
364{
366 spt.setAz(azimuth);
367 spt.setAlt(altitude);
368 dms LST = geoLocation->GSTtoLST(image->getDateTime().gst());
369 spt.HorizontalToEquatorial(&LST, geoLocation->lat());
370 SkyPoint j2000Coord = spt.catalogueCoord(image->getDateTime().djd());
372 if (!image->wcsToPixel(j2000Coord, *pixel, imagePoint))
373 {
374 QString debugString =
375 QString("PolarAlign: Couldn't get pixel from WCS for az %1 alt %2 with j2000 RA %3 DEC %4")
376 .arg(QString::number(azimuth), QString::number(altitude), j2000Coord.ra0().toHMSString(), j2000Coord.dec0().toDMSString());
377 qCInfo(KSTARS_EKOS_ALIGN) << debugString;
378 return false;
379 }
380 return true;
381}
382
383// Calculate the mount's azimuth and altitude error given the known geographic location
384// and the azimuth center and altitude center computed in findAxis().
385void PolarAlign::calculateAzAltError(double *azError, double *altError) const
386{
387 const double latitudeDegrees = geoLocation->lat()->Degrees();
388 *altError = northernHemisphere() ?
389 altitudeCenter - latitudeDegrees : altitudeCenter + latitudeDegrees;
390 *azError = northernHemisphere() ? azimuthCenter : azimuthCenter + 180.0;
391 while (*azError > 180.0)
392 *azError -= 360;
393}
394
395void PolarAlign::setMaxPixelSearchRange(double degrees)
396{
397 // Suggestion for how far pixelError() below searches.
398 // Don't allow the search to be modified too much.
399 const double d = fabs(degrees);
400 if (d < 2)
401 maxPixelSearchRange = 2.0;
402 else if (d > 10)
403 maxPixelSearchRange = 10.0;
404 else
405 maxPixelSearchRange = d;
406}
407
408// Given the currently estimated RA axis polar alignment error, and given a start pixel,
409// find the polar-alignment error if the user moves a star (from his point of view)
410// from that pixel to pixel2.
411//
412// FindCorrectedPixel() determines where the user should move the star to fully correct
413// the alignment error. However, while the user is doing that, he/she may be at an intermediate
414// point (pixel2) and we want to feed back to the user what the "current" polar-alignment error is.
415// This searches using findCorrectedPixel() to
416// find the RA axis error which would be fixed by the user moving pixel to pixel2. The input
417// thus should be pixel = "current star position", and pixel2 = "solution star position"
418// from the original call to findCorrectedPixel. This calls findCorrectedPixel several hundred times
419// but is not too costly (about .1s on a RPi4). One could write a method that more directly estimates
420// the error given the current position, but it might not be applicable to our use-case as
421// we are constrained to move along paths detemined by a user adjusting an altitude knob and then
422// an azimuth adjustment. These corrections are likely not the most direct path to solve the axis error.
423bool PolarAlign::pixelError(const QSharedPointer<FITSData> &image, const QPointF &pixel, const QPointF &pixel2,
424 double *azError, double *altError)
425{
426 double azOffset, altOffset;
427 calculateAzAltError(&azOffset, &altOffset);
428
429 QPointF pix;
430 double azE = 0, altE = 0;
431
432 pixelError(image, pixel, pixel2,
433 -maxPixelSearchRange, maxPixelSearchRange, 0.2,
434 -maxPixelSearchRange, maxPixelSearchRange, 0.2, &azE, &altE, &pix);
435 pixelError(image, pixel, pixel2, azE - .2, azE + .2, 0.02,
436 altE - .2, altE + .2, 0.02, &azE, &altE, &pix);
437 pixelError(image, pixel, pixel2, azE - .02, azE + .02, 0.002,
438 altE - .02, altE + .02, 0.002, &azE, &altE, &pix);
439
440 const double pixDist = hypot(pix.x() - pixel2.x(), pix.y() - pixel2.y());
441 if (pixDist > 10)
442 return false;
443
444 *azError = azE;
445 *altError = altE;
446 return true;
447}
448
449void PolarAlign::pixelError(const QSharedPointer<FITSData> &image, const QPointF &pixel, const QPointF &pixel2,
450 double minAz, double maxAz, double azInc,
451 double minAlt, double maxAlt, double altInc,
452 double *azError, double *altError, QPointF *actualPixel)
453{
454 double minDistSq = 1e9;
455 for (double eAz = minAz; eAz < maxAz; eAz += azInc)
456 {
457 for (double eAlt = minAlt; eAlt < maxAlt; eAlt += altInc)
458 {
459 QPointF pix;
460 if (findCorrectedPixel(image, pixel, &pix, eAz, eAlt))
461 {
462 // compare the distance to the pixel
463 double distSq = ((pix.x() - pixel2.x()) * (pix.x() - pixel2.x()) +
464 (pix.y() - pixel2.y()) * (pix.y() - pixel2.y()));
465 if (distSq < minDistSq)
466 {
468 *actualPixel = pix;
469 *azError = eAz;
470 *altError = eAlt;
471 }
472 }
473 }
474 }
475}
476
477// Given a pixel, find its RA/DEC, then its alt/az, and then solve for another pixel
478// where, if the star in pixel is moved to that star in the user's image (by adjusting alt and az controls)
479// the polar alignment error would be 0.
480bool PolarAlign::findCorrectedPixel(const QSharedPointer<FITSData> &image, const QPointF &pixel, QPointF *corrected,
481 bool altOnly)
482{
483 double azOffset, altOffset;
484 calculateAzAltError(&azOffset, &altOffset);
485 if (altOnly)
486 azOffset = 0.0;
487 return findCorrectedPixel(image, pixel, corrected, azOffset, altOffset);
488}
489
490// Given a pixel, find its RA/DEC, then its alt/az, and then solve for another pixel
491// where, if the star in pixel is moved to that star in the user's image (by adjusting alt and az controls)
492// the polar alignment error would be 0. We use the fact that we can only move by adjusting and altitude
493// knob, then an azimuth knob--i.e. we likely don't traverse a great circle.
494bool PolarAlign::findCorrectedPixel(const QSharedPointer<FITSData> &image, const QPointF &pixel, QPointF *corrected,
495 double azOffset,
496 double altOffset)
497{
498 // 1. Find the az/alt for the x,y point on the image.
499 SkyPoint p;
500 if (!prepareAzAlt(image, pixel, &p))
501 return false;
502 double pixelAz = p.az().Degrees(), pixelAlt = p.alt().Degrees();
503
504 // 2. Apply the az/alt offsets.
505 // We know that the pole's az and alt offsets are effectively rotations
506 // of a sphere. The offsets that apply to correct different points depend
507 // on where (in the sphere) those points are. Points close to the pole can probably
508 // just add the pole's offsets. This calculation is a bit more precise, and is
509 // necessary if the points are not near the pole.
510 double altRotation = northernHemisphere() ? altOffset : -altOffset;
511 QPointF rotated = Rotations::rotateRaAxis(QPointF(pixelAz, pixelAlt), QPointF(azOffset, altRotation));
512
513 // 3. Find a pixel with those alt/az values.
514 if (!findAzAlt(image, rotated.x(), rotated.y(), corrected))
515 return false;
516
517 return true;
518}
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
There are several time-dependent values used in position calculations, that are not specific to an ob...
Definition ksnumbers.h:43
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
long double djd() const
The sky coordinates of a point in the sky.
Definition skypoint.h:45
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
Definition skypoint.cpp:700
const CachingDms & dec() const
Definition skypoint.h:269
const CachingDms & ra0() const
Definition skypoint.h:251
const CachingDms & ra() const
Definition skypoint.h:263
static SkyPoint timeTransformed(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour=0)
returns a time-transformed SkyPoint.
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
Definition skypoint.h:94
const dms & az() const
Definition skypoint.h:275
void setAlt(dms alt)
Sets Alt, the Altitude.
Definition skypoint.h:194
const dms & alt() const
Definition skypoint.h:281
void HorizontalToEquatorial(const dms *LST, const dms *lat)
Determine the (RA, Dec) coordinates of the SkyPoint from its (Altitude, Azimuth) coordinates,...
Definition skypoint.cpp:143
void setAz(dms az)
Sets Az, the Azimuth.
Definition skypoint.h:230
const CachingDms & dec0() const
Definition skypoint.h:257
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition skypoint.h:119
SkyPoint deprecess(const KSNumbers *num, long double epoch=J2000)
Obtain a Skypoint with RA0 and Dec0 set from the RA, Dec of this skypoint.
Definition skypoint.cpp:257
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
GeoCoordinates geo(const QVariant &location)
qreal x() const const
qreal y() const const
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.