siderust-cpp 0.8.0
Header-only C++ wrapper for siderust
Loading...
Searching...
No Matches
target.hpp
Go to the documentation of this file.
1#pragma once
2
35#include "altitude.hpp"
36#include "azimuth.hpp"
37#include "coordinates.hpp"
38#include "ffi_core.hpp"
39#include "time.hpp"
40#include "trackable.hpp"
41#include <sstream>
42#include <string>
43#include <type_traits>
44#include <utility>
45#include <vector>
46
47namespace siderust {
48
49// ============================================================================
50// Internal type traits
51// ============================================================================
52
53namespace detail {
54
56
58template <typename T> struct is_spherical_direction : std::false_type {};
59
60template <typename F> struct is_spherical_direction<spherical::Direction<F>> : std::true_type {};
61
62template <typename T>
63inline constexpr bool is_spherical_direction_v = is_spherical_direction<T>::value;
64
66template <typename T> struct spherical_direction_frame; // undefined primary
67
68template <typename F> struct spherical_direction_frame<spherical::Direction<F>> {
69 using type = F;
70};
71
72template <typename T>
73using spherical_direction_frame_t = typename spherical_direction_frame<T>::type;
74
76
77} // namespace detail
78
79// ============================================================================
80// Target<C>
81// ============================================================================
82
112template <typename C> class DirectionTarget : public Target {
113
114 static_assert(detail::is_spherical_direction_v<C>, "Target<C>: C must be a specialisation of "
115 "siderust::spherical::Direction<F>");
116
117 using Frame = detail::spherical_direction_frame_t<C>;
118
119 static_assert(frames::has_frame_transform_v<Frame, frames::ICRS>,
120 "Target<C>: frame F must support a transform to ICRS "
121 "(frames::has_frame_transform_v<F, frames::ICRS> must be true). "
122 "Supported frames: ICRS, ICRF, EquatorialMeanJ2000, "
123 "EquatorialMeanOfDate, EquatorialTrueOfDate, EclipticMeanJ2000.");
124
125public:
126 // ------------------------------------------------------------------
127 // Construction / destruction
128 // ------------------------------------------------------------------
129
143 std::string label = "")
144 : m_dir_(dir), m_epoch_(epoch), label_(std::move(label)) {
145 // Convert to ICRS for the FFI; identity transform when already ICRS.
146 if constexpr (std::is_same_v<Frame, frames::ICRS>) {
147 m_icrs_ = dir;
148 } else {
149 m_icrs_ = dir.template to_frame<frames::ICRS>(epoch);
150 }
151 SiderustGenericTarget *h = nullptr;
152 check_status(siderust_generic_target_create_icrs(m_icrs_.ra().value(), m_icrs_.dec().value(),
153 epoch.value(), &h),
154 "Target::Target");
155 handle_ = h;
156 }
157
159 if (handle_) {
160 siderust_generic_target_free(handle_);
161 handle_ = nullptr;
162 }
163 }
164
167 : m_dir_(std::move(other.m_dir_)), m_epoch_(other.m_epoch_), m_icrs_(other.m_icrs_),
168 label_(std::move(other.label_)), handle_(other.handle_) {
169 other.handle_ = nullptr;
170 }
171
174 if (this != &other) {
175 if (handle_) {
176 siderust_generic_target_free(handle_);
177 }
178 m_dir_ = std::move(other.m_dir_);
179 m_epoch_ = other.m_epoch_;
180 m_icrs_ = other.m_icrs_;
181 label_ = std::move(other.label_);
182 handle_ = other.handle_;
183 other.handle_ = nullptr;
184 }
185 return *this;
186 }
187
188 // Prevent copying (the handle has unique ownership).
191
192 // ------------------------------------------------------------------
193 // Identity (implements Target)
194 // ------------------------------------------------------------------
195
202 std::string name() const override {
203 if (!label_.empty())
204 return label_;
205 std::ostringstream ss;
206 ss << "Direction(" << m_icrs_.ra().value() << "\xc2\xb0, " << m_icrs_.dec().value()
207 << "\xc2\xb0)";
208 return ss.str();
209 }
210
211 // ------------------------------------------------------------------
212 // Coordinate accessors
213 // ------------------------------------------------------------------
214
216 const C &direction() const { return m_dir_; }
217
219 Time<TT, JD> epoch() const { return m_epoch_; }
220
223 const spherical::direction::ICRS &icrs_direction() const { return m_icrs_; }
224
226 template <typename F_ = Frame, std::enable_if_t<frames::has_ra_dec_v<F_>, int> = 0>
227 qtty::Degree ra() const {
228 return m_dir_.ra();
229 }
230
232 template <typename F_ = Frame, std::enable_if_t<frames::has_ra_dec_v<F_>, int> = 0>
233 qtty::Degree dec() const {
234 return m_dir_.dec();
235 }
236
237 // ------------------------------------------------------------------
238 // Altitude queries (implements Trackable)
239 // ------------------------------------------------------------------
240
246 qtty::Degree altitude_at(const Geodetic &obs, const Time<TT, MJD> &mjd) const override {
247 double out{};
248 check_status(siderust_altitude_at(detail::make_generic_target_subject(handle_), obs.to_c(),
249 mjd.value(), &out),
250 "Target::altitude_at");
251 return qtty::Radian(out).to<qtty::Degree>();
252 }
253
257 std::vector<Period<TT, MJD>> above_threshold(const Geodetic &obs, const Period<TT, MJD> &window,
258 qtty::Degree threshold,
259 const SearchOptions &opts = {}) const override {
260 tempoch_period_mjd_t *ptr = nullptr;
261 uintptr_t count = 0;
262 check_status(siderust_above_threshold(detail::make_generic_target_subject(handle_), obs.to_c(),
263 window.c_inner(), threshold.value(), opts.to_c(), &ptr,
264 &count),
265 "Target::above_threshold");
266 return detail_periods_from_c(ptr, count);
267 }
268
272 std::vector<Period<TT, MJD>> below_threshold(const Geodetic &obs, const Period<TT, MJD> &window,
273 qtty::Degree threshold,
274 const SearchOptions &opts = {}) const override {
275 tempoch_period_mjd_t *ptr = nullptr;
276 uintptr_t count = 0;
277 check_status(siderust_below_threshold(detail::make_generic_target_subject(handle_), obs.to_c(),
278 window.c_inner(), threshold.value(), opts.to_c(), &ptr,
279 &count),
280 "Target::below_threshold");
281 return detail_periods_from_c(ptr, count);
282 }
283
287 std::vector<CrossingEvent> crossings(const Geodetic &obs, const Period<TT, MJD> &window,
288 qtty::Degree threshold,
289 const SearchOptions &opts = {}) const override {
290 siderust_crossing_event_t *ptr = nullptr;
291 uintptr_t count = 0;
292 check_status(siderust_crossings(detail::make_generic_target_subject(handle_), obs.to_c(),
293 window.c_inner(), threshold.value(), opts.to_c(), &ptr, &count),
294 "Target::crossings");
295 return detail::crossings_from_c(ptr, count);
296 }
297
301 std::vector<CulminationEvent> culminations(const Geodetic &obs, const Period<TT, MJD> &window,
302 const SearchOptions &opts = {}) const override {
303 siderust_culmination_event_t *ptr = nullptr;
304 uintptr_t count = 0;
305 check_status(siderust_culminations(detail::make_generic_target_subject(handle_), obs.to_c(),
306 window.c_inner(), opts.to_c(), &ptr, &count),
307 "Target::culminations");
308 return detail::culminations_from_c(ptr, count);
309 }
310
311 // ------------------------------------------------------------------
312 // Azimuth queries (implements Trackable)
313 // ------------------------------------------------------------------
314
318 qtty::Degree azimuth_at(const Geodetic &obs, const Time<TT, MJD> &mjd) const override {
319 double out{};
320 check_status(siderust_azimuth_at(detail::make_generic_target_subject(handle_), obs.to_c(),
321 mjd.value(), &out),
322 "Target::azimuth_at");
323 return qtty::Degree(out);
324 }
325
329 std::vector<AzimuthCrossingEvent>
330 azimuth_crossings(const Geodetic &obs, const Period<TT, MJD> &window, qtty::Degree bearing,
331 const SearchOptions &opts = {}) const override {
332 siderust_azimuth_crossing_event_t *ptr = nullptr;
333 uintptr_t count = 0;
334 check_status(siderust_azimuth_crossings(detail::make_generic_target_subject(handle_),
335 obs.to_c(), window.c_inner(), bearing.value(),
336 opts.to_c(), &ptr, &count),
337 "Target::azimuth_crossings");
338 return detail::az_crossings_from_c(ptr, count);
339 }
340
342 const SiderustGenericTarget *c_handle() const { return handle_; }
343
345 SiderustGenericTargetData data() const {
346 SiderustGenericTargetData out{};
347 check_status(siderust_generic_target_get_data(handle_, &out), "DirectionTarget::data");
348 return out;
349 }
350
351private:
352 C m_dir_;
353 Time<TT, JD> m_epoch_;
355 std::string label_;
356 SiderustGenericTarget *handle_ = nullptr;
357
359 static std::vector<Period<TT, MJD>> detail_periods_from_c(tempoch_period_mjd_t *ptr,
360 uintptr_t count) {
361 std::vector<Period<TT, MJD>> result;
362 result.reserve(count);
363 for (uintptr_t i = 0; i < count; ++i) {
364 result.push_back(
365 Period<TT, MJD>(Time<TT, MJD>(ptr[i].start_mjd), Time<TT, MJD>(ptr[i].end_mjd)));
366 }
367 siderust_periods_free(ptr, count);
368 return result;
369 }
370};
371
372// ============================================================================
373// Convenience type aliases
374// ============================================================================
375
378
381
384
387
391
394
395// ============================================================================
396// ProperMotionTarget
397// ============================================================================
398
423public:
428 ProperMotion proper_motion, std::string label = "")
429 : position_(position), epoch_(epoch), proper_motion_(proper_motion),
430 label_(std::move(label)) {
431 SiderustGenericTarget *h = nullptr;
432 const auto pm = proper_motion_.to_c();
433 check_status(siderust_generic_target_create_icrs_with_pm(
434 position_.ra().value(), position_.dec().value(), epoch.value(),
435 pm.pm_ra_deg_yr, pm.pm_dec_deg_yr, pm.ra_convention, &h),
436 "ProperMotionTarget::ProperMotionTarget");
437 handle_ = h;
438 }
439
441 if (handle_) {
442 siderust_generic_target_free(handle_);
443 handle_ = nullptr;
444 }
445 }
446
448 : position_(other.position_), epoch_(other.epoch_), proper_motion_(other.proper_motion_),
449 label_(std::move(other.label_)), handle_(other.handle_) {
450 other.handle_ = nullptr;
451 }
452
454 if (this != &other) {
455 if (handle_)
456 siderust_generic_target_free(handle_);
457 position_ = other.position_;
458 epoch_ = other.epoch_;
459 proper_motion_ = other.proper_motion_;
460 label_ = std::move(other.label_);
461 handle_ = other.handle_;
462 other.handle_ = nullptr;
463 }
464 return *this;
465 }
466
469
470 // -- Target identity -------------------------------------------------------
471
472 std::string name() const override {
473 if (!label_.empty())
474 return label_;
475 SiderustGenericTargetData d{};
476 siderust_generic_target_get_data(handle_, &d);
477 std::ostringstream ss;
478 ss << "ProperMotion(" << d.coord.spherical_dir.azimuth_deg << "\xc2\xb0, "
479 << d.coord.spherical_dir.polar_deg << "\xc2\xb0)";
480 return ss.str();
481 }
482
483 // -- Accessors -------------------------------------------------------------
484
486 Time<TT, JD> epoch() const { return epoch_; }
487
489 const spherical::direction::ICRS &position() const { return position_; }
490
492 const ProperMotion &proper_motion() const { return proper_motion_; }
493
495 SiderustGenericTargetData data() const {
496 SiderustGenericTargetData out{};
497 check_status(siderust_generic_target_get_data(handle_, &out), "ProperMotionTarget::data");
498 return out;
499 }
500
501 // -- Altitude tracking (implements Trackable) ------------------------------
502
503 qtty::Degree altitude_at(const Geodetic &obs, const Time<TT, MJD> &mjd) const override {
504 double out{};
505 check_status(siderust_altitude_at(detail::make_generic_target_subject(handle_), obs.to_c(),
506 mjd.value(), &out),
507 "ProperMotionTarget::altitude_at");
508 return qtty::Radian(out).to<qtty::Degree>();
509 }
510
511 std::vector<Period<TT, MJD>> above_threshold(const Geodetic &obs, const Period<TT, MJD> &window,
512 qtty::Degree threshold,
513 const SearchOptions &opts = {}) const override {
514 tempoch_period_mjd_t *ptr = nullptr;
515 uintptr_t count = 0;
516 check_status(siderust_above_threshold(detail::make_generic_target_subject(handle_), obs.to_c(),
517 window.c_inner(), threshold.value(), opts.to_c(), &ptr,
518 &count),
519 "ProperMotionTarget::above_threshold");
520 return detail_periods_from_c(ptr, count);
521 }
522
523 std::vector<Period<TT, MJD>> below_threshold(const Geodetic &obs, const Period<TT, MJD> &window,
524 qtty::Degree threshold,
525 const SearchOptions &opts = {}) const override {
526 tempoch_period_mjd_t *ptr = nullptr;
527 uintptr_t count = 0;
528 check_status(siderust_below_threshold(detail::make_generic_target_subject(handle_), obs.to_c(),
529 window.c_inner(), threshold.value(), opts.to_c(), &ptr,
530 &count),
531 "ProperMotionTarget::below_threshold");
532 return detail_periods_from_c(ptr, count);
533 }
534
535 std::vector<CrossingEvent> crossings(const Geodetic &obs, const Period<TT, MJD> &window,
536 qtty::Degree threshold,
537 const SearchOptions &opts = {}) const override {
538 siderust_crossing_event_t *ptr = nullptr;
539 uintptr_t count = 0;
540 check_status(siderust_crossings(detail::make_generic_target_subject(handle_), obs.to_c(),
541 window.c_inner(), threshold.value(), opts.to_c(), &ptr, &count),
542 "ProperMotionTarget::crossings");
543 return detail::crossings_from_c(ptr, count);
544 }
545
546 std::vector<CulminationEvent> culminations(const Geodetic &obs, const Period<TT, MJD> &window,
547 const SearchOptions &opts = {}) const override {
548 siderust_culmination_event_t *ptr = nullptr;
549 uintptr_t count = 0;
550 check_status(siderust_culminations(detail::make_generic_target_subject(handle_), obs.to_c(),
551 window.c_inner(), opts.to_c(), &ptr, &count),
552 "ProperMotionTarget::culminations");
553 return detail::culminations_from_c(ptr, count);
554 }
555
556 // -- Azimuth tracking (implements Trackable) -------------------------------
557
558 qtty::Degree azimuth_at(const Geodetic &obs, const Time<TT, MJD> &mjd) const override {
559 double out{};
560 check_status(siderust_azimuth_at(detail::make_generic_target_subject(handle_), obs.to_c(),
561 mjd.value(), &out),
562 "ProperMotionTarget::azimuth_at");
563 return qtty::Degree(out);
564 }
565
566 std::vector<AzimuthCrossingEvent>
567 azimuth_crossings(const Geodetic &obs, const Period<TT, MJD> &window, qtty::Degree bearing,
568 const SearchOptions &opts = {}) const override {
569 siderust_azimuth_crossing_event_t *ptr = nullptr;
570 uintptr_t count = 0;
571 check_status(siderust_azimuth_crossings(detail::make_generic_target_subject(handle_),
572 obs.to_c(), window.c_inner(), bearing.value(),
573 opts.to_c(), &ptr, &count),
574 "ProperMotionTarget::azimuth_crossings");
575 return detail::az_crossings_from_c(ptr, count);
576 }
577
578private:
580 Time<TT, JD> epoch_;
581 ProperMotion proper_motion_;
582 std::string label_;
583 SiderustGenericTarget *handle_ = nullptr;
584
585 static std::vector<Period<TT, MJD>> detail_periods_from_c(tempoch_period_mjd_t *ptr,
586 uintptr_t count) {
587 std::vector<Period<TT, MJD>> result;
588 result.reserve(count);
589 for (uintptr_t i = 0; i < count; ++i) {
590 result.push_back(
591 Period<TT, MJD>(Time<TT, MJD>(ptr[i].start_mjd), Time<TT, MJD>(ptr[i].end_mjd)));
592 }
593 siderust_periods_free(ptr, count);
594 return result;
595 }
596};
597
598} // namespace siderust
Altitude computations for Sun, Moon, stars, and arbitrary ICRS directions.
Azimuth computations for Sun, Moon, stars, and arbitrary ICRS directions.
Fixed celestial direction target — a Target for a specific sky position.
Definition target.hpp:112
SiderustGenericTargetData data() const
Raw coordinate payload stored in the FFI handle.
Definition target.hpp:345
DirectionTarget(DirectionTarget &&other) noexcept
Move constructor.
Definition target.hpp:166
qtty::Degree azimuth_at(const Geodetic &obs, const Time< TT, MJD > &mjd) const override
Compute azimuth (degrees, N-clockwise) at a given Time<TT, MJD> instant.
Definition target.hpp:318
DirectionTarget & operator=(DirectionTarget &&other) noexcept
Move assignment.
Definition target.hpp:173
std::vector< Period< TT, MJD > > above_threshold(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree threshold, const SearchOptions &opts={}) const override
Find periods when the target is above a threshold altitude.
Definition target.hpp:257
std::vector< CulminationEvent > culminations(const Geodetic &obs, const Period< TT, MJD > &window, const SearchOptions &opts={}) const override
Find culmination (local altitude extremum) events.
Definition target.hpp:301
std::vector< AzimuthCrossingEvent > azimuth_crossings(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree bearing, const SearchOptions &opts={}) const override
Find epochs when the target crosses a given azimuth bearing.
Definition target.hpp:330
DirectionTarget(const DirectionTarget &)=delete
std::string name() const override
Human-readable name for this direction target.
Definition target.hpp:202
DirectionTarget & operator=(const DirectionTarget &)=delete
std::vector< CrossingEvent > crossings(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree threshold, const SearchOptions &opts={}) const override
Find threshold-crossing events (rising / setting).
Definition target.hpp:287
const SiderustGenericTarget * c_handle() const
Access the underlying C handle (advanced use).
Definition target.hpp:342
std::vector< Period< TT, MJD > > below_threshold(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree threshold, const SearchOptions &opts={}) const override
Find periods when the target is below a threshold altitude.
Definition target.hpp:272
const spherical::direction::ICRS & icrs_direction() const
Definition target.hpp:223
qtty::Degree dec() const
Declination — only available for equatorial frames (RA/Dec).
Definition target.hpp:233
Time< TT, JD > epoch() const
Epoch of the coordinate.
Definition target.hpp:219
qtty::Degree ra() const
Right ascension — only available for equatorial frames (RA/Dec).
Definition target.hpp:227
DirectionTarget(C dir, Time< TT, JD > epoch=Time< TT, JD >::J2000(), std::string label="")
Construct from a strongly-typed spherical direction.
Definition target.hpp:142
qtty::Degree altitude_at(const Geodetic &obs, const Time< TT, MJD > &mjd) const override
Compute altitude (degrees) at a given Time<TT, MJD> instant.
Definition target.hpp:246
const C & direction() const
The original typed direction as supplied at construction.
Definition target.hpp:216
ICRS target with proper motion (RAII wrapper over the FFI handle).
Definition target.hpp:422
std::vector< Period< TT, MJD > > above_threshold(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree threshold, const SearchOptions &opts={}) const override
Find periods when the object is above a threshold altitude.
Definition target.hpp:511
ProperMotionTarget(spherical::direction::ICRS position, Time< TT, JD > epoch, ProperMotion proper_motion, std::string label="")
Construct a proper-motion target from an ICRS direction and typed rates.
Definition target.hpp:427
SiderustGenericTargetData data() const
Raw coordinate payload stored in the FFI handle.
Definition target.hpp:495
ProperMotionTarget(ProperMotionTarget &&other) noexcept
Definition target.hpp:447
Time< TT, JD > epoch() const
Coordinate epoch.
Definition target.hpp:486
ProperMotionTarget & operator=(ProperMotionTarget &&other) noexcept
Definition target.hpp:453
std::string name() const override
Human-readable name for this target (e.g. "Sun", "Vega", "ICRS(279.2°, 38.8°)").
Definition target.hpp:472
std::vector< AzimuthCrossingEvent > azimuth_crossings(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree bearing, const SearchOptions &opts={}) const override
Find epochs when the object crosses a given azimuth bearing.
Definition target.hpp:567
std::vector< CrossingEvent > crossings(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree threshold, const SearchOptions &opts={}) const override
Find threshold-crossing events (rising / setting).
Definition target.hpp:535
qtty::Degree azimuth_at(const Geodetic &obs, const Time< TT, MJD > &mjd) const override
Compute azimuth (degrees, N-clockwise) at a given Time<TT, MJD> instant.
Definition target.hpp:558
const spherical::direction::ICRS & position() const
ICRS position at the coordinate epoch.
Definition target.hpp:489
ProperMotionTarget(const ProperMotionTarget &)=delete
std::vector< CulminationEvent > culminations(const Geodetic &obs, const Period< TT, MJD > &window, const SearchOptions &opts={}) const override
Find culmination (local altitude extremum) events.
Definition target.hpp:546
std::vector< Period< TT, MJD > > below_threshold(const Geodetic &obs, const Period< TT, MJD > &window, qtty::Degree threshold, const SearchOptions &opts={}) const override
Find periods when the object is below a threshold altitude.
Definition target.hpp:523
ProperMotionTarget & operator=(const ProperMotionTarget &)=delete
qtty::Degree altitude_at(const Geodetic &obs, const Time< TT, MJD > &mjd) const override
Compute altitude (degrees) at a given Time<TT, MJD> instant.
Definition target.hpp:503
const ProperMotion & proper_motion() const
Proper motion rates.
Definition target.hpp:492
Abstract base for any celestial object that can be tracked from an observer location.
Definition trackable.hpp:54
Coordinate module umbrella.
Error handling and utility base for the siderust C++ wrapper.
std::vector< CrossingEvent > crossings_from_c(siderust_crossing_event_t *ptr, uintptr_t count)
Definition altitude.hpp:87
siderust_subject_t make_generic_target_subject(const SiderustGenericTarget *h)
Build a siderust_subject_t for a generic target opaque handle.
Definition ffi_core.hpp:282
std::vector< CulminationEvent > culminations_from_c(siderust_culmination_event_t *ptr, uintptr_t count)
Definition altitude.hpp:98
std::vector< AzimuthCrossingEvent > az_crossings_from_c(siderust_azimuth_crossing_event_t *ptr, uintptr_t count)
Definition azimuth.hpp:74
tempoch::EncodedTime< Scale, Format > Time
Definition time.hpp:36
void check_status(siderust_status_t status, const char *operation)
Definition ffi_core.hpp:111
tempoch::Period< Time< Scale, Format > > Period
Definition time.hpp:37
Geodetic position (WGS84 ellipsoid).
Definition geodetic.hpp:29
siderust_geodetic_t to_c() const
Convert to C FFI struct.
Definition geodetic.hpp:44
Proper motion for a star (equatorial).
Definition bodies.hpp:24
siderust_proper_motion_t to_c() const
Definition bodies.hpp:29
Options for altitude search algorithms.
Definition altitude.hpp:58
A direction on the celestial sphere, compile-time tagged by frame.
Definition spherical.hpp:50
qtty::Degree dec() const
Definition spherical.hpp:76
qtty::Degree ra() const
Definition spherical.hpp:71
Public siderust time tags and typed time/period aliases.
Abstract base class for all celestial targets.