Kstars

dms.h
1 /*
2  SPDX-FileCopyrightText: 2001 Jason Harris <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "../nan.h"
10 
11 #include <QString>
12 #include <QDataStream>
13 
14 #include <cmath>
15 
16 //#define COUNT_DMS_SINCOS_CALLS true
17 //#define PROFILE_SINCOS true
18 
19 #ifdef PROFILE_SINCOS
20 #include <ctime>
21 #endif
22 
23 /** @class dms
24  * @short An angle, stored as degrees, but expressible in many ways.
25  * @author Jason Harris
26  * @version 1.0
27  *
28  * dms encapsulates an angle. The angle is stored as a double,
29  * equal to the value of the angle in degrees. Methods are available
30  * for setting/getting the angle as a floating-point measured in
31  * Degrees or Hours, or as integer triplets (degrees, arcminutes,
32  * arcseconds or hours, minutes, seconds). There is also a method
33  * to set the angle according to a radian value, and to return the
34  * angle expressed in radians. Finally, a SinCos() method computes
35  * the sin and cosine of the angle.
36  */
37 class dms
38 {
39  public:
40  /** Default constructor. */
41  dms()
42  : D(NaN::d)
43 #ifdef COUNT_DMS_SINCOS_CALLS
44  ,
45  m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
46 #endif
47  {
48 #ifdef COUNT_DMS_SINCOS_CALLS
49  ++dms_constructor_calls;
50 #endif
51  }
52 
53  /** Empty virtual destructor */
54  virtual ~dms() = default;
55 
56  /** @short Set the floating-point value of the angle according to the four integer arguments.
57  * @param d degree portion of angle (int). Defaults to zero.
58  * @param m arcminute portion of angle (int). Defaults to zero.
59  * @param s arcsecond portion of angle (int). Defaults to zero.
60  * @param ms arcsecond portion of angle (int). Defaults to zero.
61  */
62  explicit dms(const int &d, const int &m = 0, const int &s = 0, const int &ms = 0)
63 #ifdef COUNT_DMS_SINCOS_CALLS
64  : m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
65 #endif
66  {
67  dms::setD(d, m, s, ms);
68 #ifdef COUNT_DMS_SINCOS_CALLS
69  ++dms_constructor_calls;
70 #endif
71  }
72 
73  /** @short Construct an angle from a double value.
74  *
75  * Creates an angle whose value in Degrees is equal to the argument.
76  * @param x angle expressed as a floating-point number (in degrees)
77  */
78  explicit dms(const double &x)
79  : D(x)
80 #ifdef COUNT_DMS_SINCOS_CALLS
81  ,
82  m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
83 #endif
84  {
85 #ifdef COUNT_DMS_SINCOS_CALLS
86  ++dms_constructor_calls;
87 #endif
88  }
89 
90  /** @short Construct an angle from a string representation.
91  *
92  * Attempt to create the angle according to the string argument. If the string
93  * cannot be parsed as an angle value, the angle is set to zero.
94  *
95  * @warning There is not an unambiguous notification that it failed to parse the string,
96  * since the string could have been a valid representation of zero degrees.
97  * If this is a concern, use the setFromString() function directly instead.
98  *
99  * @param s the string to parse as a dms value.
100  * @param isDeg if true, value is in degrees; if false, value is in hours.
101  * @sa setFromString()
102  */
103  explicit dms(const QString &s, bool isDeg = true)
104 #ifdef COUNT_DMS_SINCOS_CALLS
105  : m_sinCosCalled(false), m_sinDirty(true), m_cosDirty(true)
106 #endif
107  {
108  setFromString(s, isDeg);
109 #ifdef COUNT_DMS_SINCOS_CALLS
110  ++dms_constructor_calls;
111 #endif
112  }
113 
114  /** @return integer degrees portion of the angle
115  */
116  inline int degree() const
117  {
118  if (std::isnan(D))
119  return 0;
120 
121  return int(D);
122  }
123 
124  /** @return integer arcminutes portion of the angle.
125  * @note an arcminute is 1/60 degree.
126  */
127  int arcmin() const;
128 
129  /** @return integer arcseconds portion of the angle
130  * @note an arcsecond is 1/60 arcmin, or 1/3600 degree.
131  */
132  int arcsec() const;
133 
134  /** @return integer milliarcseconds portion of the angle
135  * @note a milliarcsecond is 1/1000 arcsecond.
136  */
137  int marcsec() const;
138 
139  /** @return angle in degrees expressed as a double.
140  */
141  inline const double &Degrees() const { return D; }
142 
143  /** @return integer hours portion of the angle
144  * @note an angle can be measured in degrees/arcminutes/arcseconds
145  * or hours/minutes/seconds. An hour is equal to 15 degrees.
146  */
147  inline int hour() const { return int(reduce().Degrees() / 15.0); }
148 
149  /** @return integer minutes portion of the angle
150  * @note a minute is 1/60 hour (not the same as an arcminute)
151  */
152  int minute() const;
153 
154  /** @return integer seconds portion of the angle
155  * @note a second is 1/3600 hour (not the same as an arcsecond)
156  */
157  int second() const;
158 
159  /** @return integer milliseconds portion of the angle
160  * @note a millisecond is 1/1000 second (not the same as a milliarcsecond)
161  */
162  int msecond() const;
163 
164  /** @return angle in hours expressed as a double in the range 0 to 23.999...
165  * @note an angle can be measured in degrees/arcminutes/arcseconds
166  * or hours/minutes/seconds. An hour is equal to 15 degrees.
167  */
168  inline double Hours() const { return reduce().Degrees() / 15.0; }
169 
170  /** @return angle in hours expressed as a double in the range -11.999 to 0 to 12.0
171  * @note an angle can be measured in degrees/arcminutes/arcseconds
172  * or hours/minutes/seconds. An hour is equal to 15 degrees.
173  */
174  inline double HoursHa() const { return Hours() <= 12.0 ? Hours() : Hours() - 24.0; }
175 
176  /** Sets floating-point value of angle, in degrees.
177  * @param x new angle (double)
178  */
179  inline virtual void setD(const double &x)
180  {
181 #ifdef COUNT_DMS_SINCOS_CALLS
182  m_sinDirty = m_cosDirty = true;
183 #endif
184  D = x;
185  }
186 
187  /** @short Sets floating-point value of angle, in degrees.
188  *
189  * This is an overloaded member function; it behaves essentially
190  * like the above function. The floating-point value of the angle
191  * (D) is determined from the following formulae:
192  *
193  * \f$ fabs(D) = fabs(d) + \frac{(m + (s/60))}{60} \f$
194  * \f$ sgn(D) = sgn(d) \f$
195  *
196  * @param d integer degrees portion of angle
197  * @param m integer arcminutes portion of angle
198  * @param s integer arcseconds portion of angle
199  * @param ms integer arcseconds portion of angle
200  */
201  virtual void setD(const int &d, const int &m, const int &s, const int &ms = 0);
202 
203  /** @short Sets floating-point value of angle, in hours.
204  *
205  * Converts argument from hours to degrees, then
206  * sets floating-point value of angle, in degrees.
207  * @param x new angle, in hours (double)
208  * @sa setD()
209  */
210  inline virtual void setH(const double &x)
211  {
212  dms::setD(x * 15.0);
213 #ifdef COUNT_DMS_SINCOS_CALLS
214  m_cosDirty = m_sinDirty = true;
215 #endif
216  }
217 
218  /** @short Sets floating-point value of angle, in hours.
219  *
220  * Converts argument values from hours to degrees, then
221  * sets floating-point value of angle, in degrees.
222  * This is an overloaded member function, provided for convenience. It
223  * behaves essentially like the above function.
224  * @param h integer hours portion of angle
225  * @param m integer minutes portion of angle
226  * @param s integer seconds portion of angle
227  * @param ms integer milliseconds portion of angle
228  * @sa setD()
229  */
230  virtual void setH(const int &h, const int &m, const int &s, const int &ms = 0);
231 
232  /** @short Attempt to parse the string argument as a dms value, and set the dms object
233  * accordingly.
234  * @param s the string to be parsed as a dms value. The string can be an int or
235  * floating-point value, or a triplet of values (d/h, m, s) separated by spaces or colons.
236  * @param isDeg if true, the value is in degrees. Otherwise, it is in hours.
237  * @return true if sting was parsed successfully. Otherwise, set the dms value
238  * to 0.0 and return false.
239  */
240  virtual bool setFromString(const QString &s, bool isDeg = true);
241 
242  /** @short Compute Sine and Cosine of the angle simultaneously.
243  * On machines using glibc >= 2.1, calling SinCos() is somewhat faster
244  * than calling sin() and cos() separately.
245  * The values are returned through the arguments (passed by reference).
246  *
247  * @param s Sine of the angle
248  * @param c Cosine of the angle
249  * @sa sin() cos()
250  */
251  inline void SinCos(double &s, double &c) const;
252 
253  /** @short Compute the Angle's Sine.
254  *
255  * @return the Sine of the angle.
256  * @sa cos()
257  */
258  double sin() const
259  {
260 #ifdef COUNT_DMS_SINCOS_CALLS
261  if (!m_sinCosCalled)
262  {
263  m_sinCosCalled = true;
264  ++dms_with_sincos_called;
265  }
266  if (m_sinDirty)
267  m_sinDirty = false;
268  else
269  ++redundant_trig_function_calls;
270  ++trig_function_calls;
271 #endif
272 #ifdef PROFILE_SINCOS
273  std::clock_t start, stop;
274  double s;
275  start = std::clock();
276  s = ::sin(D * DegToRad);
277  stop = std::clock();
278  seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC);
279  return s;
280 #else
281  return ::sin(D * DegToRad);
282 #endif
283  }
284 
285  /** @short Compute the Angle's Cosine.
286  *
287  * @return the Cosine of the angle.
288  * @sa sin()
289  */
290  double cos() const
291  {
292 #ifdef COUNT_DMS_SINCOS_CALLS
293  if (!m_sinCosCalled)
294  {
295  m_sinCosCalled = true;
296  ++dms_with_sincos_called;
297  }
298  if (m_cosDirty)
299  m_cosDirty = false;
300  else
301  ++redundant_trig_function_calls;
302  ++trig_function_calls;
303 #endif
304 #ifdef PROFILE_SINCOS
305  std::clock_t start, stop;
306  double c;
307  start = std::clock();
308  c = ::cos(D * DegToRad);
309  stop = std::clock();
310  seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC);
311  return c;
312 #else
313  return ::cos(D * DegToRad);
314 #endif
315  }
316 
317  /**
318  * @short Convenience method to return tangent of the angle
319  */
320  inline double tan() const { return sin()/cos(); }
321 
322  /** @short Express the angle in radians.
323  * @return the angle in radians (double)
324  */
325  inline double radians() const { return D * DegToRad; }
326 
327  /** @short Set angle according to the argument, in radians.
328  *
329  * This function converts the argument to degrees, then sets the angle
330  * with setD().
331  * @param Rad an angle in radians
332  */
333  inline virtual void setRadians(const double &Rad)
334  {
335  dms::setD(Rad / DegToRad);
336 #ifdef COUNT_DMS_SINCOS_CALLS
337  m_cosDirty = m_sinDirty = true;
338 #endif
339  }
340 
341  /** return the equivalent angle between 0 and 360 degrees.
342  * @warning does not change the value of the parent angle itself.
343  */
344  const dms reduce() const;
345 
346  /**
347  * @brief deltaAngle Return the shortest difference (path) between this angle and the supplied angle. The range is normalized to [-180,+180]
348  * @param angle Angle to subtract from current angle.
349  * @return Normalized angle in the range [-180,+180]
350  */
351  const dms deltaAngle(dms angle) const;
352 
353  /**
354  * @short an enum defining standard angle ranges
355  */
357  {
358  ZERO_TO_2PI,
359  MINUSPI_TO_PI
360  };
361 
362  /**
363  * @short Reduce _this_ angle to the given range
364  */
365  void reduceToRange(enum dms::AngleRanges range);
366 
367  /** @return a nicely-formatted string representation of the angle
368  * in degrees, arcminutes, and arcseconds.
369  * @param forceSign if @c true then adds '+' or '-' to the string
370  * @param machineReadable uses a colon separator and produces +/-dd:mm:ss format instead
371  * @param highPrecision adds milliseconds, if @c false the seconds will be shown as an integer
372  */
373  const QString toDMSString(const bool forceSign = false, const bool machineReadable = false, const bool highPrecision=false) const;
374 
375  /** @return a nicely-formatted string representation of the angle
376  * in hours, minutes, and seconds.
377  * @param machineReadable uses a colon separator and produces hh:mm:ss format instead
378  * @param highPrecision adds milliseconds, if @c false the seconds will be shown as an integer
379  */
380  const QString toHMSString(const bool machineReadable = false, const bool highPrecision=false) const;
381 
382  /** PI is a const static member; it's public so that it can be used anywhere,
383  * as long as dms.h is included.
384  */
385  static constexpr double PI = { M_PI };
386 
387  /** DegToRad is a const static member equal to the number of radians in
388  * one degree (dms::PI/180.0).
389  */
390  static constexpr double DegToRad = { M_PI / 180.0 };
391 
392  /** @short Static function to create a DMS object from a QString.
393  *
394  * There are several ways to specify the angle:
395  * @li Integer numbers ( 5 or -33 )
396  * @li Floating-point numbers ( 5.0 or -33.0 )
397  * @li colon-delimited integers ( 5:0:0 or -33:0:0 )
398  * @li colon-delimited with float seconds ( 5:0:0.0 or -33:0:0.0 )
399  * @li colon-delimited with float minutes ( 5:0.0 or -33:0.0 )
400  * @li space-delimited ( 5 0 0; -33 0 0 ) or ( 5 0.0 or -33 0.0 )
401  * @li space-delimited, with unit labels ( 5h 0m 0s or -33d 0m 0s )
402  * @param s the string to be parsed as an angle value
403  * @param deg if true, s is expressed in degrees; if false, s is expressed in hours
404  * @return a dms object whose value is parsed from the string argument
405  */
406  static dms fromString(const QString &s, bool deg);
407 
408  inline dms operator-() { return dms(-D); }
409 #ifdef COUNT_DMS_SINCOS_CALLS
410  static long unsigned dms_constructor_calls; // counts number of DMS constructor calls
411  static long unsigned dms_with_sincos_called;
412  static long unsigned trig_function_calls; // total number of trig function calls
413  static long unsigned redundant_trig_function_calls; // counts number of redundant trig function calls
414  static double seconds_in_trig; // accumulates number of seconds spent in trig function calls
415 #endif
416 
417  protected:
418  double D;
419 
420  private:
421 #ifdef COUNT_DMS_SINCOS_CALLS
422  mutable bool m_sinDirty, m_cosDirty, m_sinCosCalled;
423 #endif
424 
425  friend dms operator+(dms, dms);
426  friend dms operator-(dms, dms);
427  friend QDataStream &operator<<(QDataStream &out, const dms &d);
428  friend QDataStream &operator>>(QDataStream &in, dms &d);
429 };
430 
431 /// Add two angles
432 inline dms operator+(dms a, dms b)
433 {
434  return dms(a.D + b.D);
435 }
436 
437 /// Subtract angles
438 inline dms operator-(dms a, dms b)
439 {
440  return dms(a.D - b.D);
441 }
442 
443 // Inline sincos
444 inline void dms::SinCos(double &s, double &c) const
445 {
446 #ifdef PROFILE_SINCOS
447  std::clock_t start, stop;
448  start = std::clock();
449 #endif
450 
451 #ifdef HAVE_SINCOS
452  sincos(radians(), &s, &c);
453 #else
454  s = ::sin(radians());
455  c = ::cos(radians());
456 #endif
457 
458 #ifdef PROFILE_SINCOS
459  stop = std::clock();
460  seconds_in_trig += double(stop - start) / double(CLOCKS_PER_SEC);
461 #endif
462 
463 #ifdef COUNT_DMS_SINCOS_CALLS
464  if (!m_sinCosCalled)
465  {
466  m_sinCosCalled = true;
467  ++dms_with_sincos_called;
468  }
469  if (m_sinDirty)
470  m_sinDirty = false;
471  else
472  ++redundant_trig_function_calls;
473 
474  if (m_cosDirty)
475  m_cosDirty = false;
476  else
477  ++redundant_trig_function_calls;
478 
479  trig_function_calls += 2;
480 #endif
481 }
482 
483 /** Overloaded equality operator */
484 inline bool operator==(const dms &a1, const dms &a2)
485 {
486  return a1.Degrees() == a2.Degrees();
487 }
488 
489 /**
490  * User-defined dms literals for convenience
491  */
492 
493 /**
494  * Create a constant angle in degrees
495  */
496 inline dms operator "" _deg(long double x) { return dms(double(x)); }
497 
498 /**
499  * Create a constant angle in hours
500  */
501 inline dms operator "" _h(long double x) { return dms(double(x * 15.0)); }
502 
503 /**
504  * Create a constant angle in radians
505  */
506 inline dms operator "" _rad(long double x) { return dms(double(x / dms::DegToRad)); }
507 
508 /**
509  * Create a constant angle from a DMS string
510  */
511 inline dms operator "" _dms(const char *dmsString) { return dms::fromString(QString(dmsString), true); }
512 
513 /**
514  * Create a constant angle from a HMS string
515  */
516 inline dms operator "" _hms(const char *hmsString) { return dms::fromString(QString(hmsString), false); }
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
Definition: dms.h:385
int marcsec() const
Definition: dms.cpp:207
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition: dms.h:179
int degree() const
Definition: dms.h:116
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
Definition: dms.h:390
void SinCos(double &s, double &c) const
Compute Sine and Cosine of the angle simultaneously.
Definition: dms.h:444
int msecond() const
Definition: dms.cpp:241
dms()
Default constructor.
Definition: dms.h:41
int arcmin() const
Definition: dms.cpp:180
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:370
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
Definition: dms.h:210
dms(const int &d, const int &m=0, const int &s=0, const int &ms=0)
Set the floating-point value of the angle according to the four integer arguments.
Definition: dms.h:62
dms(const double &x)
Construct an angle from a double value.
Definition: dms.h:78
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:279
const QCA_EXPORT SecureArray operator+(const SecureArray &a, const SecureArray &b)
double tan() const
Convenience method to return tangent of the angle.
Definition: dms.h:320
virtual ~dms()=default
Empty virtual destructor.
virtual bool setFromString(const QString &s, bool isDeg=true)
Attempt to parse the string argument as a dms value, and set the dms object accordingly.
Definition: dms.cpp:48
AngleRanges
an enum defining standard angle ranges
Definition: dms.h:356
double cos() const
Compute the Angle's Cosine.
Definition: dms.h:290
int second() const
Definition: dms.cpp:231
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
virtual void setRadians(const double &Rad)
Set angle according to the argument, in radians.
Definition: dms.h:333
double radians() const
Express the angle in radians.
Definition: dms.h:325
const double & Degrees() const
Definition: dms.h:141
int hour() const
Definition: dms.h:147
int arcsec() const
Definition: dms.cpp:193
const dms deltaAngle(dms angle) const
deltaAngle Return the shortest difference (path) between this angle and the supplied angle.
Definition: dms.cpp:259
void reduceToRange(enum dms::AngleRanges range)
Reduce this angle to the given range.
Definition: dms.cpp:438
const dms reduce() const
return the equivalent angle between 0 and 360 degrees.
Definition: dms.cpp:251
double sin() const
Compute the Angle's Sine.
Definition: dms.h:258
int minute() const
Definition: dms.cpp:221
dms(const QString &s, bool isDeg=true)
Construct an angle from a string representation.
Definition: dms.h:103
double Hours() const
Definition: dms.h:168
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition: dms.cpp:421
double HoursHa() const
Definition: dms.h:174
friend dms operator+(dms, dms)
Add two angles.
Definition: dms.h:432
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Jun 6 2023 03:56:46 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.