← Astronomy 8 / 18

Fixed stars

A "fixed" star is not fixed. What moves it, above everything else, is the reference frame: the Aries point goes back fifty arcseconds a year, so Regulus, which sat at 29 degrees of Leo in 2000, has been in Virgo since the end of 2011. On top of that comes the star's own proper motion, which in Arcturus or Sirius is a couple of arcseconds a year and in almost everything else is nothing at all, and on top of that the same corrections a planet gets: annual aberration, up to twenty arcseconds, and nutation, up to seventeen.

The catalogue is 1,099 objects from Hipparcos-2 and SIMBAD, and a position is built in five steps: proper motion from J2000 to the date, a rotation from ICRS equatorial into the J2000 ecliptic, precession to the ecliptic and equinox of the date with the same rotation a planet gets, annual aberration, and nutation in longitude.

use Astronomy\Stars;

echo count(Stars::catalog()), " objects\n";
1099 objects

Finding a star: Stars::find()

use Astronomy\Stars;

$regulus = Stars::find('Regulus');
echo $regulus->key, ' ', $regulus->designation, "\n";

echo Stars::find('regulus') === $regulus ? "case-insensitive: yes\n" : "no\n";
echo Stars::find('  Régulus ') ? "accent-insensitive: yes\n" : "no\n";
echo Stars::find($regulus->key) === $regulus ? "by its own key: yes\n" : "no\n";
regulus α Leo
case-insensitive: yes
accent-insensitive: yes
by its own key: yes

A star's name is not unique the way its designation is. Ten names in the catalogue belong to two different stars each, and π³ Ori and π⁴ Ori, both called Tabit, are one pair of them:

$tabit = Stars::find('Tabit');
echo $tabit->key, ' ', $tabit->designation, ' magnitude ', $tabit->magnitude, "\n";

$other = Stars::find('pi-4ori');
echo $other->key, ' ', $other->designation, ' magnitude ', $other->magnitude, "\n";
pi-3ori π³ Ori magnitude 3.19
pi-4ori π⁴ Ori magnitude 3.68

Asking for the shared name resolves to the brighter of the two, which is measured rather than chosen: it is what anyone writing "Tabit" with no further qualifier means. Reaching the fainter one takes its own designation reduced to a key, which is also how find() answers to whatever the published list calls a star that has no name of its own (594 stars in the catalogue have none). A star with no published magnitude never wins a shared name over one that has: unknown is not bright.

Where a star falls: Stars::position()

use Astronomy\Stars;
use Astronomy\Time;

[$jdTT] = Time::fromClock(new DateTimeImmutable('1981-05-11 07:15:00', new DateTimeZone('UTC')));

$position = Stars::position(Stars::find('Regulus'), $jdTT);

echo $position->formatted(), "\n";             // longitude on the ecliptic of the date
echo $position->sign()->name(), "\n";
echo $position->formattedDeclination(), "\n";  // declination, for the parallel
printf("%.4f %.4f\n", $position->rightAscension, $position->latitude);
29° 34' Leo
Leo
+12° 04'
151.8417 0.4643

Stars::position() takes Terrestrial Time, like a planet, and returns a StarPosition carrying both coordinate systems at once rather than just one: ecliptic longitude, for the conjunction, which is how a star has been read since Ptolemy, and declination, for the parallel, which is the modern reading. A Position will not do here, because it is built around a Body, and a star has no distance that matters and no speed worth reading.

formatted() rounds the whole longitude to minutes before splitting it into sign and degrees, and not the other way round: rounding the minutes first and splitting after lets 29° 59.6' Leo carry to 30° 00' Leo, a degree that does not exist, instead of 0° 00' Virgo. It happened to Regulus in the very month of its own ingress into Virgo, which is exactly when someone is looking.

Mean position against apparent, and why an ingress needs the mean one

use Astronomy\Stars;
use Astronomy\Time;

$regulus = Stars::find('Regulus');

foreach (['2011-03-01', '2011-08-01', '2012-01-01'] as $day) {
    [$jdTT] = Time::fromClock(new DateTimeImmutable($day, new DateTimeZone('UTC')));

    $apparent = Stars::position($regulus, $jdTT);
    $mean = Stars::position($regulus, $jdTT, apparent: false);

    printf("%s  apparent %.5f %-5s  mean %.5f %s\n", $day,
        $apparent->longitude, $apparent->sign()->name(), $mean->longitude, $mean->sign()->name());
}
2011-03-01  apparent 149.99514 Leo    mean 149.98433 Leo
2011-08-01  apparent 149.99005 Leo    mean 149.99016 Leo
2012-01-01  apparent 150.00442 Virgo  mean 149.99598 Leo

The apparent longitude is not monotonic in time, and the reason is a race between two effects running at different speeds: aberration swings up to twenty arcseconds back and forth over a year, and precession only ever advances fifty arcseconds in that same year. Aberration wins locally, so a star's apparent position can run backwards for months at a time and cross the same degree three times within a season, which is exactly what Regulus does between March and August 2011 above. The mean position only carries proper motion and precession, so it only ever moves forward, which is why Crossings and anything that dates an ingress asks for apparent: false.

The eighty-eight constellations, from the sky and not the letter

use Astronomy\Constellations;

echo count(Constellations::all()), " constellations\n";
88 constellations

All eighty-eight of the IAU's list are here, not a subset built around the zodiac and its neighbours: a catalogue that only carries what has been needed so far is a catalogue that fails silently the first time something new arrives, and this one is a published, closed list.

Which constellation a star belongs to is not read off its Bayer letter. It is computed from the star's own position, taken back to the epoch B1875 and tested against the boundaries Eugène Delporte drew in 1930, from the table Nancy Roman published for exactly this purpose (PASP 99, 695, 1987): three hundred and fifty-seven rows of right ascension, declination and constellation, in the very frame that makes the boundaries parallels and meridians instead of the crooked lines they become in any other epoch.

That distinction is not academic. Bayer lettered his stars in 1603 and Flamsteed numbered his in 1712, well over a century before Delporte's boundaries existed, so a handful of stars ended up on the wrong side of a line that was not there yet when they were named:

use Astronomy\Stars;

foreach (['nembus', 'intercrus'] as $key) {
    $star = Stars::catalog()[$key];
    echo $star->designation, ' sits in ', $star->constellation, ", not where its Bayer letter points\n";
}
υ Per sits in Andromeda, not where its Bayer letter points
41 Lyn sits in Ursa Major, not where its Bayer letter points

Measured over the whole catalogue, exactly two stars out of the 1,080 that carry a Bayer or Flamsteed designation disagree with it this way, and the other 1,078 agree. Both exceptions are written down explicitly rather than silently allowed, so that a third disagreement stops the catalogue from being rebuilt: a new one showing up means either a position that moved or a transform that broke, and neither of those announces itself on its own. A star sitting in the wrong constellation reads perfectly well.

Stars::passes() and Stars::occultations()

A star goes through the same horizon machinery as a planet, Limb::Center and nothing else, because a point of light has no disc for a limb to mean anything about:

use Astronomy\Place;
use Astronomy\Stars;

$madrid = new Place('Madrid', null, 'Spain', 'ES', 40.4165, -3.7026, 'Europe/Madrid');
$day = new DateTimeImmutable('2026-09-19', $madrid->timeZone());

$regulus = Stars::find('Regulus');
$passes = Stars::passes($regulus, $madrid, $day);

echo $passes->rise?->date->format('H:i:s T'), "\n";
echo $passes->upperCulmination?->date->format('H:i:s T'), "\n";
echo $passes->set?->date->format('H:i:s T'), "\n";
05:48:02 CEST
12:31:11 CEST
19:14:21 CEST

Stars::occultations() is the Moon passing in front of a star rather than in front of a planet, and it is discarded outright, before any search runs, for a star more than seven degrees from the ecliptic: the Moon never strays past 5.3 degrees of latitude, and parallax and semi-diameter add at most another 1.3, so anything further away could never be occulted and is not worth the search time.

use Astronomy\Stars;
use Astronomy\Time;

$aldebaran = Stars::find('Aldebaran');
$from = Time::julianDay(new DateTimeImmutable('2017-01-01', new DateTimeZone('UTC')));
$to = Time::julianDay(new DateTimeImmutable('2018-01-01', new DateTimeZone('UTC')));

$list = Stars::occultations($aldebaran, $from, $to);
echo count($list), " occultations of Aldebaran in 2017\n";
echo $list[0]->maximum->date->format('Y-m-d H:i'), ' UT  ', $list[0]->type->name(), "\n";
14 occultations of Aldebaran in 2017
2017-01-09 14:25 UT  Total

Aldebaran, close enough to the ecliptic to be occulted often and bright enough to matter, is covered by the Moon fourteen separate times in 2017 alone. Local circumstances from one place work the same way they do for a planet:

use Astronomy\Occultations;

foreach ($list as $o) {
    $local = Occultations::local($o, $madrid);

    if ($local !== null) {
        echo $o->maximum->date->format('Y-m-d'), ' visible from Madrid, disappears ',
            $local->contact2->date->format('H:i:s T'), "\n";
    }
}
2017-02-05 visible from Madrid, disappears 22:57:56 CET
2017-04-28 visible from Madrid, disappears 20:19:45 CEST
2017-06-22 visible from Madrid, disappears 17:29:37 CEST
2017-08-16 visible from Madrid, disappears 08:20:09 CEST

Ten of the fourteen never reach Madrid at all, and Occultations::local() answers null for those instead of a set of times with nothing behind them. Eclipses and occultations of planets are covered in full in Eclipses and occultations.

Precision

Against the catalogue's own published positions, star by star, the median difference is 0.04 arcseconds at the year 2000 and about 0.3 at 1900 and 2100, which is a floor of precession model and not of the stars themselves: the model this engine precesses with agrees with a commercial one's own to a few hundredths of an arcsecond near 2000 and drifts by roughly 0.3 arcseconds per century away from it in either direction (see The sidereal zodiac for where that drift is measured directly, and Precision for how it is measured everywhere else in the engine).

Four things a planet gets that a star deliberately does not:

  • Annual parallax. The nearest star in the catalogue, Toliman (α Centauri), has 0.75 arcseconds of it; Sirius has 0.38, and everything else less than 0.15. Smaller than the engine's own floor against a live check, and only in two stars.
  • Radial velocity. It changes proper motion over centuries because a star is moving closer or further away, and in Sirius, the worst case in the catalogue, that comes to two hundredths of an arcsecond per century. Both parallax and radial velocity are kept on Star in case a use for them ever comes up, and neither is applied yet.
  • The frame correction a planet gets. Ephemeris corrects the eight planets and the Moon towards a modern numerical reference frame, and that correction belongs to the analytic planetary series it is written against; a star from SIMBAD is not in that series' frame to begin with, it is already in ICRS, and the gap between ICRS and the mean equator of J2000 is two hundredths of an arcsecond, left uncorrected either way.
  • Gravitational deflection by the Sun. Thousandths of an arcsecond except within a degree of the Sun, where a star is not visible anyway.