403 lines
10 KiB
C++
403 lines
10 KiB
C++
// startextdump.cpp
|
|
//
|
|
// Copyright (C) 2004, 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.
|
|
//
|
|
// Dump the contents of a Celestia star database file in a text
|
|
// format that's easy read and edit.
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <celcompat/numbers.h>
|
|
#include <celutil/bytes.h>
|
|
#include <celengine/astro.h>
|
|
#include <celengine/stellarclass.h>
|
|
|
|
using namespace std;
|
|
|
|
|
|
static string inputFilename;
|
|
static string outputFilename;
|
|
static string hdFilename;
|
|
static bool useOldFormat = false;
|
|
static bool useSphericalCoords = false;
|
|
|
|
|
|
void Usage()
|
|
{
|
|
cerr << "Usage: startextdump [options] <star database file> [output file]\n";
|
|
cerr << " Options:\n";
|
|
cerr << " --old (or -o) : input star database is pre-2.0 format\n";
|
|
cerr << " --hd : dump HD catalog cross reference\n";
|
|
cerr << " --spherical (or -s) : output spherical coordinates (RA/dec/distance\n";
|
|
}
|
|
|
|
|
|
static uint32_t readUint(istream& in)
|
|
{
|
|
uint32_t n;
|
|
in.read(reinterpret_cast<char*>(&n), sizeof n);
|
|
LE_TO_CPU_INT32(n, n);
|
|
return n;
|
|
}
|
|
|
|
static float readFloat(istream& in)
|
|
{
|
|
float f;
|
|
in.read(reinterpret_cast<char*>(&f), sizeof f);
|
|
LE_TO_CPU_FLOAT(f, f);
|
|
return f;
|
|
}
|
|
|
|
static int16_t readShort(istream& in)
|
|
{
|
|
int16_t n;
|
|
in.read(reinterpret_cast<char*>(&n), sizeof n);
|
|
LE_TO_CPU_INT16(n, n);
|
|
return n;
|
|
}
|
|
|
|
static uint16_t readUshort(istream& in)
|
|
{
|
|
uint16_t n;
|
|
in.read(reinterpret_cast<char*>(&n), sizeof n);
|
|
LE_TO_CPU_INT16(n, n);
|
|
return n;
|
|
}
|
|
|
|
static uint8_t readUbyte(istream& in)
|
|
{
|
|
uint8_t n;
|
|
in.read((char*) &n, 1);
|
|
return n;
|
|
}
|
|
|
|
|
|
void printStellarClass(uint16_t sc, ostream& out)
|
|
{
|
|
StellarClass::StarType st = (StellarClass::StarType) (sc >> 12);
|
|
|
|
if (st == StellarClass::WhiteDwarf)
|
|
{
|
|
out << "WD";
|
|
}
|
|
else if (st == StellarClass::NeutronStar)
|
|
{
|
|
out << "Q";
|
|
}
|
|
else if (st == StellarClass::BlackHole)
|
|
{
|
|
out << "X";
|
|
}
|
|
else if (st == StellarClass::NormalStar)
|
|
{
|
|
unsigned int spectralClass = (sc >> 8 & 0xf);
|
|
unsigned int spectralSubclass = (sc >> 4 & 0xf);
|
|
unsigned int luminosityClass = (sc & 0xf);
|
|
|
|
if (spectralClass == 12)
|
|
{
|
|
out << '?';
|
|
}
|
|
else
|
|
{
|
|
out << "OBAFGKMRSNWW?LTC"[spectralClass];
|
|
out << "0123456789"[spectralSubclass];
|
|
switch (luminosityClass)
|
|
{
|
|
case StellarClass::Lum_Ia0:
|
|
out << "Ia-0";
|
|
break;
|
|
case StellarClass::Lum_Ia:
|
|
out << "Ia";
|
|
break;
|
|
case StellarClass::Lum_Ib:
|
|
out << "Ib";
|
|
break;
|
|
case StellarClass::Lum_II:
|
|
out << "II";
|
|
break;
|
|
case StellarClass::Lum_III:
|
|
out << "III";
|
|
break;
|
|
case StellarClass::Lum_IV:
|
|
out << "IV";
|
|
break;
|
|
case StellarClass::Lum_V:
|
|
out << "V";
|
|
break;
|
|
case StellarClass::Lum_VI:
|
|
out << "VI";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
out << '?';
|
|
}
|
|
}
|
|
|
|
|
|
bool DumpOldStarDatabase(istream& in, ostream& out, ofstream& hdOut,
|
|
bool spherical)
|
|
{
|
|
uint32_t nStarsInFile = readUint(in);
|
|
if (!in.good())
|
|
{
|
|
cerr << "Error reading count of stars from database.\n";
|
|
return false;
|
|
}
|
|
|
|
out << nStarsInFile << '\n';
|
|
|
|
for (uint32_t i = 0; i < nStarsInFile; i++)
|
|
{
|
|
if (!in.good())
|
|
{
|
|
cerr << "Error reading from star database at record " << i << endl;
|
|
return false;
|
|
}
|
|
|
|
uint32_t catalogNum = readUint(in);
|
|
uint32_t HDCatalogNum = readUint(in);
|
|
float RA = readFloat(in);
|
|
float dec = readFloat(in);
|
|
float parallax = readFloat(in);
|
|
int16_t appMag = readShort(in);
|
|
uint16_t stellarClass = readUshort(in);
|
|
/*uint8_t parallaxError = */readUbyte(in); // not used yet
|
|
|
|
// Compute distance based on parallax
|
|
double distance = LY_PER_PARSEC / (parallax > 0.0 ? parallax / 1000.0 : 1e-6);
|
|
out << catalogNum << ' ';
|
|
out << setprecision(8);
|
|
|
|
if (spherical)
|
|
{
|
|
out << RA * 360.0 / 24.0 << ' ' << dec << ' ' << distance << ' ';
|
|
out << setprecision(6);
|
|
out << (float) appMag / 256.0f << ' ';
|
|
}
|
|
else
|
|
{
|
|
Eigen::Vector3d pos = astro::equatorialToCelestialCart((double) RA, (double) dec, distance);
|
|
float absMag = (float) (appMag / 256.0 + 5 -
|
|
5 * log10(distance / LY_PER_PARSEC));
|
|
out << (float) pos.x() << ' ' <<
|
|
(float) pos.y() << ' ' <<
|
|
(float) pos.z() << ' ';
|
|
out << setprecision(5);
|
|
out << absMag << ' ';
|
|
}
|
|
printStellarClass(stellarClass, out);
|
|
out << '\n';
|
|
|
|
// Dump HD catalog cross reference
|
|
if (hdOut.is_open() && HDCatalogNum != ~0u)
|
|
hdOut << HDCatalogNum << ' ' << catalogNum << '\n';
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool DumpStarDatabase(istream& in, ostream& out, bool spherical)
|
|
{
|
|
char header[8];
|
|
in.read(header, sizeof header);
|
|
if (strncmp(header, "CELSTARS", sizeof header))
|
|
{
|
|
cerr << "Missing header in star database.\n";
|
|
return false;
|
|
}
|
|
|
|
uint16_t version = readUshort(in);
|
|
if (version != 0x0100)
|
|
{
|
|
cerr << "Unsupported file version " << (version >> 8) << '.' <<
|
|
(version & 0xff) << '\n';
|
|
return false;
|
|
}
|
|
|
|
uint32_t nStarsInFile = readUint(in);
|
|
if (!in.good())
|
|
{
|
|
cerr << "Error reading count of stars from database.\n";
|
|
return false;
|
|
}
|
|
|
|
out << nStarsInFile << '\n';
|
|
|
|
for (uint32_t i = 0; i < nStarsInFile; i++)
|
|
{
|
|
if (!in.good())
|
|
{
|
|
cerr << "Error reading from star database at record " << i << endl;
|
|
return false;
|
|
}
|
|
|
|
uint32_t catalogNum = readUint(in);
|
|
float x = readFloat(in);
|
|
float y = readFloat(in);
|
|
float z = readFloat(in);
|
|
int16_t absMag = readShort(in);
|
|
uint16_t stellarClass = readUshort(in);
|
|
|
|
out << catalogNum << ' ';
|
|
out << setprecision(7);
|
|
|
|
if (spherical)
|
|
{
|
|
Eigen::Vector3d eclipticpos = {(double) x, (double) y, (double) z};
|
|
Eigen::Vector3d pos = astro::eclipticToEquatorial(eclipticpos);
|
|
double distance = sqrt(x * x + y * y + z * z);
|
|
// acos outputs angles in interval [0, pi], use negative sign for interval [-pi, 0]
|
|
double phi = -acos(pos.y() / distance) * 180 / celestia::numbers::pi;
|
|
double theta = atan2(pos.z(), -pos.x()) * 180 / celestia::numbers::pi;
|
|
// atan2 outputs angles in interval [-pi, pi], so we add 360 to fix this
|
|
double ra = theta - 180 + 360;
|
|
double dec = phi + 90;
|
|
float appMag = float (absMag / 256.0 - 5 + 5 * log10(distance / LY_PER_PARSEC));
|
|
|
|
out << fixed << setprecision(9) << (float) ra << ' ' << (float) dec << ' ';
|
|
out << setprecision(6) << (float) distance << ' ';
|
|
out << setprecision(2);
|
|
out << appMag << ' ';
|
|
}
|
|
else
|
|
{
|
|
out << x << ' ' << y << ' ' << z << ' ';
|
|
out << setprecision(4);
|
|
out << ((float) absMag / 256.0f) << ' ';
|
|
}
|
|
|
|
printStellarClass(stellarClass, out);
|
|
out << '\n';
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool parseCommandLine(int argc, char* argv[])
|
|
{
|
|
int i = 1;
|
|
int fileCount = 0;
|
|
|
|
while (i < argc)
|
|
{
|
|
if (argv[i][0] == '-')
|
|
{
|
|
if (!strcmp(argv[i], "--hd"))
|
|
{
|
|
if (i == argc - 1)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
hdFilename = string(argv[i]);
|
|
}
|
|
}
|
|
else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--old"))
|
|
{
|
|
useOldFormat = true;
|
|
}
|
|
else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--spherical"))
|
|
{
|
|
useSphericalCoords = true;
|
|
}
|
|
else
|
|
{
|
|
cerr << "Unknown command line switch: " << argv[i] << '\n';
|
|
return false;
|
|
}
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
if (fileCount == 0)
|
|
{
|
|
// input filename first
|
|
inputFilename = string(argv[i]);
|
|
fileCount++;
|
|
}
|
|
else if (fileCount == 1)
|
|
{
|
|
// output filename second
|
|
outputFilename = string(argv[i]);
|
|
fileCount++;
|
|
}
|
|
else
|
|
{
|
|
// more than two filenames on the command line is an error
|
|
return false;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
if (!parseCommandLine(argc, argv) || inputFilename.empty())
|
|
{
|
|
Usage();
|
|
return 1;
|
|
}
|
|
|
|
ifstream stardbFile(inputFilename, ios::in | ios::binary);
|
|
if (!stardbFile.good())
|
|
{
|
|
cerr << "Error opening star database file " << inputFilename << '\n';
|
|
return 1;
|
|
}
|
|
|
|
ofstream hdOut;
|
|
if (useOldFormat && !hdFilename.empty())
|
|
{
|
|
hdOut.open(hdFilename, ios::out);
|
|
if (!hdOut.good())
|
|
{
|
|
cerr << "Error opening HD catalog output file " << hdFilename << '\n';
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
bool success;
|
|
ostream* out = &cout;
|
|
ofstream fout;
|
|
if (!outputFilename.empty())
|
|
{
|
|
fout.open(outputFilename, ios::out);
|
|
if (!fout.good())
|
|
{
|
|
cerr << "Error opening output file " << outputFilename << '\n';
|
|
return 1;
|
|
}
|
|
out = &fout;
|
|
}
|
|
|
|
if (useOldFormat)
|
|
{
|
|
success = DumpOldStarDatabase(stardbFile, *out, hdOut,
|
|
useSphericalCoords);
|
|
}
|
|
else
|
|
{
|
|
success = DumpStarDatabase(stardbFile, *out, useSphericalCoords);
|
|
}
|
|
|
|
return success ? 0 : 1;
|
|
}
|