627 lines
17 KiB
C++
627 lines
17 KiB
C++
// stellarclass.cpp
|
|
//
|
|
// Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
|
|
#include <cstring>
|
|
#include <celutil/debug.h>
|
|
#include <cassert>
|
|
#include <config.h>
|
|
#include "stellarclass.h"
|
|
|
|
using namespace std;
|
|
|
|
|
|
Color StellarClass::getApparentColor() const
|
|
{
|
|
return getApparentColor(getSpectralClass());
|
|
}
|
|
|
|
|
|
Color StellarClass::getApparentColor(StellarClass::SpectralClass sc) const
|
|
{
|
|
switch (sc)
|
|
{
|
|
case Spectral_O:
|
|
return Color(0.7f, 0.8f, 1.0f);
|
|
case Spectral_B:
|
|
return Color(0.8f, 0.9f, 1.0f);
|
|
case Spectral_A:
|
|
return Color(1.0f, 1.0f, 1.0f);
|
|
case Spectral_F:
|
|
return Color(1.0f, 1.0f, 0.88f);
|
|
case Spectral_G:
|
|
return Color(1.0f, 1.0f, 0.75f);
|
|
case StellarClass::Spectral_K:
|
|
return Color(1.0f, 0.9f, 0.7f);
|
|
case StellarClass::Spectral_M:
|
|
return Color(1.0f, 0.7f, 0.7f);
|
|
case StellarClass::Spectral_R:
|
|
case StellarClass::Spectral_S:
|
|
case StellarClass::Spectral_N:
|
|
case StellarClass::Spectral_C:
|
|
return Color(1.0f, 0.4f, 0.4f);
|
|
case StellarClass::Spectral_L:
|
|
case StellarClass::Spectral_T:
|
|
return Color(0.75f, 0.2f, 0.2f);
|
|
case StellarClass::Spectral_Y:
|
|
return Color(0.5f, 0.175f, 0.125f);
|
|
default:
|
|
// TODO: Figure out reasonable colors for Wolf-Rayet stars,
|
|
// white dwarfs, and other oddities
|
|
return Color(1.0f, 1.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
|
|
// The << method of converting the stellar class to a string is
|
|
// preferred, but it's not always practical, especially when you've
|
|
// got a completely broken implementation of stringstreams to
|
|
// deal with (*cough* gcc *cough*).
|
|
string StellarClass::str() const
|
|
{
|
|
char s0, s1;
|
|
const char* s2 = "";
|
|
|
|
switch (getStarType())
|
|
{
|
|
case StellarClass::WhiteDwarf:
|
|
return "WD";
|
|
case StellarClass::NeutronStar:
|
|
return "Q";
|
|
case StellarClass::BlackHole:
|
|
return "X";
|
|
case StellarClass::NormalStar:
|
|
s0 = "OBAFGKMRSNWW?LTYC"[(unsigned int) getSpectralClass()];
|
|
s1 = "0123456789"[getSubclass()];
|
|
switch (getLuminosityClass())
|
|
{
|
|
case StellarClass::Lum_Ia0:
|
|
s2 = " I-a0";
|
|
break;
|
|
case StellarClass::Lum_Ia:
|
|
s2 = " I-a";
|
|
break;
|
|
case StellarClass::Lum_Ib:
|
|
s2 = " I-b";
|
|
break;
|
|
case StellarClass::Lum_II:
|
|
s2 = " II";
|
|
break;
|
|
case StellarClass::Lum_III:
|
|
s2 = " III";
|
|
break;
|
|
case StellarClass::Lum_IV:
|
|
s2 = " IV";
|
|
break;
|
|
case StellarClass::Lum_V:
|
|
s2 = " V";
|
|
break;
|
|
case StellarClass::Lum_VI:
|
|
s2 = " VI";
|
|
break;
|
|
default: break; // Do nothing, but prevent GCC4 warnings (Beware: potentially dangerous)
|
|
}
|
|
return fmt::sprintf("%c%c%s", s0, s1, s2);
|
|
}
|
|
|
|
return "?";
|
|
}
|
|
|
|
|
|
uint16_t
|
|
StellarClass::packV1() const
|
|
{
|
|
// StarDB Ver. 0x0100 doesn't support Spectral_Y.
|
|
// Classes following Spectral_Y are shifted by 1.
|
|
uint16_t sc;
|
|
if (specClass == SpectralClass::Spectral_Y)
|
|
sc = (uint16_t) SpectralClass::Spectral_Unknown;
|
|
else
|
|
sc = (uint16_t) specClass > SpectralClass::Spectral_Y ? specClass - 1 : specClass;
|
|
|
|
return (((uint16_t) starType << 12) |
|
|
(((uint16_t) sc & 0x0f) << 8) |
|
|
((uint16_t) subclass << 4) |
|
|
((uint16_t) lumClass));
|
|
}
|
|
|
|
|
|
uint16_t
|
|
StellarClass::packV2() const
|
|
{
|
|
uint16_t sc = (starType == StellarClass::WhiteDwarf ? specClass - 1 : specClass);
|
|
|
|
return (((uint16_t) starType << 13) |
|
|
(((uint16_t) sc & 0x1f) << 8) |
|
|
(((uint16_t) subclass & 0x0f) << 4) |
|
|
((uint16_t) lumClass & 0x0f));
|
|
}
|
|
|
|
|
|
bool
|
|
StellarClass::unpackV1(uint16_t st)
|
|
{
|
|
starType = static_cast<StellarClass::StarType>(st >> 12);
|
|
|
|
switch (starType)
|
|
{
|
|
case NormalStar:
|
|
specClass = static_cast<SpectralClass>(st >> 8 & 0xf);
|
|
// StarDB Ver. 0x0100 doesn't support Spectral_Y
|
|
// Spectral_Y has the value Spectral_C had earlier.
|
|
if (specClass == SpectralClass::Spectral_Y)
|
|
specClass = SpectralClass::Spectral_C;
|
|
subclass = st >> 4 & 0xf;
|
|
lumClass = static_cast<LuminosityClass>(st & 0xf);
|
|
break;
|
|
case WhiteDwarf:
|
|
if ((st >> 8 & 0xf) >= WDClassCount)
|
|
return false;
|
|
specClass = static_cast<SpectralClass>((st >> 8 & 0xf) + SpectralClass::Spectral_DA);
|
|
subclass = st >> 4 & 0xf;
|
|
lumClass = Lum_Unknown;
|
|
break;
|
|
case NeutronStar:
|
|
case BlackHole:
|
|
specClass = Spectral_Unknown;
|
|
subclass = Subclass_Unknown;
|
|
lumClass = Lum_Unknown;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
StellarClass::unpackV2(uint16_t st)
|
|
{
|
|
starType = static_cast<StellarClass::StarType>(st >> 13);
|
|
|
|
switch (starType)
|
|
{
|
|
case NormalStar:
|
|
specClass = static_cast<SpectralClass>(st >> 8 & 0x1f);
|
|
subclass = st >> 4 & 0xf;
|
|
lumClass = static_cast<LuminosityClass>(st & 0xf);
|
|
break;
|
|
case WhiteDwarf:
|
|
if ((st >> 8 & 0xf) >= WDClassCount)
|
|
return false;
|
|
specClass = static_cast<SpectralClass>((st >> 8 & 0xf) + SpectralClass::Spectral_DA);
|
|
subclass = st >> 4 & 0xf;
|
|
lumClass = Lum_Unknown;
|
|
break;
|
|
case NeutronStar:
|
|
case BlackHole:
|
|
specClass = Spectral_Unknown;
|
|
subclass = Subclass_Unknown;
|
|
lumClass = Lum_Unknown;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
ostream& operator<<(ostream& os, const StellarClass& sc)
|
|
{
|
|
os << sc.str();
|
|
|
|
return os;
|
|
}
|
|
|
|
|
|
bool operator<(const StellarClass& sc0, const StellarClass& sc1)
|
|
{
|
|
return sc0.packV2() < sc1.packV2();
|
|
}
|
|
|
|
|
|
// The following code implements a state machine for parsing spectral
|
|
// types. It is a very forgiving parser, returning unknown for any of the
|
|
// spectral type fields it can't find, and silently ignoring any extra
|
|
// characters in the spectral type. The parser is written this way because
|
|
// the spectral type strings from the Hipparcos catalog are quite irregular.
|
|
enum ParseState
|
|
{
|
|
BeginState,
|
|
EndState,
|
|
NormalStarState,
|
|
WolfRayetTypeState,
|
|
NormalStarClassState,
|
|
NormalStarSubclassState,
|
|
NormalStarSubclassDecimalState,
|
|
NormalStarSubclassFinalState,
|
|
LumClassBeginState,
|
|
LumClassIState,
|
|
LumClassIIState,
|
|
LumClassVState,
|
|
LumClassIdashState,
|
|
LumClassIaState,
|
|
WDTypeState,
|
|
WDExtendedTypeState,
|
|
WDSubclassState,
|
|
SubdwarfPrefixState,
|
|
};
|
|
|
|
|
|
StellarClass
|
|
StellarClass::parse(const string& st)
|
|
{
|
|
uint32_t i = 0;
|
|
ParseState state = BeginState;
|
|
StellarClass::StarType starType = StellarClass::NormalStar;
|
|
StellarClass::SpectralClass specClass = StellarClass::Spectral_Unknown;
|
|
StellarClass::LuminosityClass lumClass = StellarClass::Lum_Unknown;
|
|
unsigned int subclass = StellarClass::Subclass_Unknown;
|
|
|
|
while (state != EndState)
|
|
{
|
|
char c;
|
|
if (i < st.length())
|
|
c = st[i];
|
|
else
|
|
c = '\0';
|
|
|
|
switch (state)
|
|
{
|
|
case BeginState:
|
|
switch (c)
|
|
{
|
|
case 'Q':
|
|
starType = StellarClass::NeutronStar;
|
|
state = EndState;
|
|
break;
|
|
case 'X':
|
|
starType = StellarClass::BlackHole;
|
|
state = EndState;
|
|
break;
|
|
case 'D':
|
|
starType = StellarClass::WhiteDwarf;
|
|
specClass = StellarClass::Spectral_D;
|
|
state = WDTypeState;
|
|
i++;
|
|
break;
|
|
case 's':
|
|
// Hipparcos uses sd prefix for stars with luminosity
|
|
// class VI ('subdwarfs')
|
|
state = SubdwarfPrefixState;
|
|
i++;
|
|
break;
|
|
case '?':
|
|
state = EndState;
|
|
break;
|
|
default:
|
|
state = NormalStarClassState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WolfRayetTypeState:
|
|
switch (c)
|
|
{
|
|
case 'C':
|
|
specClass = StellarClass::Spectral_WC;
|
|
state = NormalStarSubclassState;
|
|
i++;
|
|
break;
|
|
case 'N':
|
|
specClass = StellarClass::Spectral_WN;
|
|
state = NormalStarSubclassState;
|
|
i++;
|
|
break;
|
|
default:
|
|
specClass = StellarClass::Spectral_WC;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SubdwarfPrefixState:
|
|
if (c == 'd')
|
|
{
|
|
lumClass = StellarClass::Lum_VI;
|
|
state = NormalStarClassState;
|
|
i++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
state = EndState;
|
|
}
|
|
break;
|
|
|
|
case NormalStarClassState:
|
|
switch (c)
|
|
{
|
|
case 'W':
|
|
state = WolfRayetTypeState;
|
|
break;
|
|
case 'O':
|
|
specClass = StellarClass::Spectral_O;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'B':
|
|
specClass = StellarClass::Spectral_B;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'A':
|
|
specClass = StellarClass::Spectral_A;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'F':
|
|
specClass = StellarClass::Spectral_F;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'G':
|
|
specClass = StellarClass::Spectral_G;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'K':
|
|
specClass = StellarClass::Spectral_K;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'M':
|
|
specClass = StellarClass::Spectral_M;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'R':
|
|
specClass = StellarClass::Spectral_R;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'S':
|
|
specClass = StellarClass::Spectral_S;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'N':
|
|
specClass = StellarClass::Spectral_N;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'L':
|
|
specClass = StellarClass::Spectral_L;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'T':
|
|
specClass = StellarClass::Spectral_T;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'Y':
|
|
specClass = StellarClass::Spectral_Y;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
case 'C':
|
|
specClass = StellarClass::Spectral_C;
|
|
state = NormalStarSubclassState;
|
|
break;
|
|
default:
|
|
state = EndState;
|
|
break;
|
|
}
|
|
i++;
|
|
break;
|
|
|
|
case NormalStarSubclassState:
|
|
if (isdigit(c))
|
|
{
|
|
subclass = (unsigned int) c - (unsigned int) '0';
|
|
state = NormalStarSubclassDecimalState;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
state = LumClassBeginState;
|
|
}
|
|
break;
|
|
|
|
case NormalStarSubclassDecimalState:
|
|
if (c == '.')
|
|
{
|
|
state = NormalStarSubclassFinalState;
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
state = LumClassBeginState;
|
|
}
|
|
break;
|
|
|
|
case NormalStarSubclassFinalState:
|
|
if (isdigit(c))
|
|
state = LumClassBeginState;
|
|
else
|
|
state = EndState;
|
|
i++;
|
|
break;
|
|
|
|
case LumClassBeginState:
|
|
switch (c)
|
|
{
|
|
case 'I':
|
|
state = LumClassIState;
|
|
break;
|
|
case 'V':
|
|
state = LumClassVState;
|
|
break;
|
|
default:
|
|
state = EndState;
|
|
break;
|
|
}
|
|
i++;
|
|
break;
|
|
|
|
case LumClassIState:
|
|
switch (c)
|
|
{
|
|
case 'I':
|
|
state = LumClassIIState;
|
|
break;
|
|
case 'V':
|
|
lumClass = StellarClass::Lum_IV;
|
|
state = EndState;
|
|
break;
|
|
case 'a':
|
|
state = LumClassIaState;
|
|
break;
|
|
case 'b':
|
|
lumClass = StellarClass::Lum_Ib;
|
|
state = EndState;
|
|
break;
|
|
case '-':
|
|
state = LumClassIdashState;
|
|
break;
|
|
default:
|
|
lumClass = StellarClass::Lum_Ib;
|
|
state = EndState;
|
|
break;
|
|
}
|
|
i++;
|
|
break;
|
|
|
|
case LumClassIIState:
|
|
switch (c)
|
|
{
|
|
case 'I':
|
|
lumClass = StellarClass::Lum_III;
|
|
state = EndState;
|
|
break;
|
|
default:
|
|
lumClass = StellarClass::Lum_II;
|
|
state = EndState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LumClassIdashState:
|
|
switch (c)
|
|
{
|
|
case 'a':
|
|
state = LumClassIaState;
|
|
break;
|
|
case 'b':
|
|
lumClass = StellarClass::Lum_Ib;
|
|
state = EndState;
|
|
break;
|
|
default:
|
|
lumClass = StellarClass::Lum_Ib;
|
|
state = EndState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LumClassIaState:
|
|
switch (c)
|
|
{
|
|
case '0':
|
|
lumClass = StellarClass::Lum_Ia0;
|
|
state = EndState;
|
|
break;
|
|
default:
|
|
lumClass = StellarClass::Lum_Ia;
|
|
state = EndState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case LumClassVState:
|
|
switch (c)
|
|
{
|
|
case 'I':
|
|
lumClass = StellarClass::Lum_VI;
|
|
state = EndState;
|
|
break;
|
|
default:
|
|
lumClass = StellarClass::Lum_V;
|
|
state = EndState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WDTypeState:
|
|
switch (c)
|
|
{
|
|
case 'A':
|
|
specClass = StellarClass::Spectral_DA;
|
|
i++;
|
|
break;
|
|
case 'B':
|
|
specClass = StellarClass::Spectral_DB;
|
|
i++;
|
|
break;
|
|
case 'C':
|
|
specClass = StellarClass::Spectral_DC;
|
|
i++;
|
|
break;
|
|
case 'O':
|
|
specClass = StellarClass::Spectral_DO;
|
|
i++;
|
|
break;
|
|
case 'Q':
|
|
specClass = StellarClass::Spectral_DQ;
|
|
i++;
|
|
break;
|
|
case 'X':
|
|
specClass = StellarClass::Spectral_DX;
|
|
i++;
|
|
break;
|
|
case 'Z':
|
|
specClass = StellarClass::Spectral_DZ;
|
|
i++;
|
|
break;
|
|
default:
|
|
specClass = StellarClass::Spectral_D;
|
|
break;
|
|
}
|
|
state = WDExtendedTypeState;
|
|
break;
|
|
|
|
case WDExtendedTypeState:
|
|
switch (c)
|
|
{
|
|
case 'A':
|
|
case 'B':
|
|
case 'C':
|
|
case 'O':
|
|
case 'Q':
|
|
case 'Z':
|
|
case 'X':
|
|
case 'V': // variable
|
|
case 'P': // magnetic stars with polarized light
|
|
case 'H': // magnetic stars without polarized light
|
|
case 'E': // emission lines
|
|
i++;
|
|
break;
|
|
default:
|
|
state = WDSubclassState;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WDSubclassState:
|
|
if (isdigit(c))
|
|
{
|
|
subclass = (unsigned int) c - (unsigned int) '0';
|
|
i++;
|
|
}
|
|
state = EndState;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
state = EndState;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return {starType, specClass, subclass, lumClass};
|
|
}
|