Section 4 - Stars Near Earth

The material in this section and the next on "Star Near Earth" data is optional reading. It requires no fancy math or extra concepts, but is merely the application of C++ and templates to some actual astronomical data. It leads to the production of some cute software that you might add to your portfolio or put on your web site. If you want to do the optional bonus assignment for extra points, you can submit the project as outlined below at any time during the course.

3B.4.1 The RECONS Database

This week we have been working mostly with pencil and paper. The programming was limited to timing algorithms to confirm that if theypic were supposed to be N2 time complexity, they really were N2 time complexity (or logN or N). Aside from examples that you might investigate, there is not much for you to do in your IDE -- but don't worry - I'll find something in your assignment. Meanwhile, let's have some fun and introduce a database and class that we may use later in the quarter for extra credit or research. It consists of the 100 nearest stars to Earth, and it is updated every year to reflect newly discovered stars that might bump some stars off the list (much like a song on the pop charts bumps off other songs as popularity changes, except in this case, the stars positions are not changing -- only our improved measurements of what is there is; over millions of years, the stars will change positions, but that's somebody else's problem).

100 stars is such a miniscule subset of the stars in our galaxy, which picnumber in the hundreds of billions, that you might think that working with this set is really uninteresting. However the distances to even the nearest star is so far that it will be thousands of years, at best, before we can even send probes to these start to study them.

So, how far away are they? That's one of the things we'll find out. Also, we'll build a map on our screens that will give visual information that you cannot get anywhere on the web. We are massaging actual data in new ways.

The data is from this site: http://www.chara.gsu.edu/RECONS/ and a list of the information you will be reading into your programs looks pretty much like this: http://www.chara.gsu.edu/RECONS/TOP100.posted.htm.

If you downloaded the CS_2C_Cient_Support package in a prior week, you also received the StarNearEarth files. If not, you can get the support package here

Unzip the archive and you will see a CS_2C_Client_Support folder, which contains three sub-folders, one of which is a StarNearEarth Folder. Go into that folder and open the Read Me file, which explains how to incorporate the data file into your project. You'll also have a sample main() to get you started.

3B.4.2 The StarNearEarth Class

The data structure we'll be working with is very simple - it contains members for each of the columns in the table you see when you open the above link of TOP100.posted.htm. Here is part of your include file that you can always access from your IDE once it is in your project:

class StarNearEarth
{
public:
   static const unsigned int MIN_STRING = 0;
   static const unsigned int MAX_STRING = 100;
   static const int MIN_DUB = -1000;
   static const int MAX_DUB = 1000;
private:
   string nameCns, spectralType, notes,  nameCommon;
   int rank, nameLhs, numComponents;
   double rAsc, decl,  propMotionMag, propMotionDir, parallaxMean, 
      parallaxVariance, magApparent, magAbsolute, mass;
   bool whiteDwarfFlag;

public:
   StarNearEarth();

   //mutators
   bool setNameCns(string strArg);
   bool setSpectralType(string strArg);
   bool setNotes(string strArg);
   bool setNameCommon(string strArg);

   bool setRank(int intArg);
   bool setNameLhs(int intArg);
   bool setNumComponents(int intArg);

   bool setRAsc(double dblArg);
   bool setDec(double dblArg);
   bool setPropMotionMag(double dblArg);
   bool setPropMotionDir(double dblArg);
   bool setParallaxMean(double dblArg);
   bool setParallaxVariance(double dblArg);
   bool setMagApparent(double dblArg);
   bool setMagAbsolute(double dblArg);
   bool setMass(double dblArg);

   void setWhiteDwarfFlag(bool boolArg);

   // accessors
   string getNameCns() const { return nameCns; }
   string getSpectralType() const { return spectralType; }
   string getNotes() const { return notes; }
   string getNameCommon() const { return nameCommon; }

   int getRank() const { return rank; }
   int getNameLhs() const { return nameLhs; }
   int getNumComponents() const { return numComponents; }

   double getRAsc() const { return rAsc; }
   double getDec() const { return decl; }
   double getPropMotionMag() const { return propMotionMag; }
   double getPropMotionDir() const { return propMotionDir; }
   double getParallaxMean() const { return parallaxMean; }
   double getParallaxVariance() const { return parallaxVariance; }
   double getMagApparent() const { return magApparent; }
   double getMagAbsolute() const { return magAbsolute; }
   double getMass() const { return mass; }
   bool getWhiteDwarfFlag() const { return whiteDwarfFlag; }

   // comparator tools
   // could use static const int, instead:
private:
   static int sortKey;

public:
   enum 
   {
      SORT_BY_NAME_CNS, SORT_BY_SPECTRAL_TYPE, SORT_BY_NAME_COMMON, SORT_BY_RANK,
      SORT_BY_NAME_LHS, SORT_BY_NUM_COMPONENTS, SORT_BY_RA, SORT_BY_DEC, SORT_BY_PROP_MOTION_MAG,
      SORT_BY_PROP_MOTION_DIR, SORT_BY_PARALLAX_MEAN, SORT_BY_PARALLAX_VARIANCE, 
      SORT_BY_MAG_APPARENT, SORT_BY_MAG_ABSOLUTE, SORT_BY_MASS
   };
   static bool setSortType( int whichType );
   bool operator<(const StarNearEarth &other) const;
   bool operator>(const StarNearEarth &other) const;
   bool operator==(const StarNearEarth &other) const;
   bool operator!=(const StarNearEarth &other) const;};
}

It looks big and bad, but mostly it's just a bunch of mutators and accessors driven by the 20 or so private members you see near the top. Members like s_name_common, d_mass, and d_magnitude_apparent are pretty self-explanatory, where s_ means string and d_ means double. Depending on what we are exploring, we will use some subset of these many members, usually only three or four for any given problem. For us, this week, the most import will be:

pic

Because of this, we'll be using the accessors getRAsc(), getDec() and getParallaxMean(). Here is a small program that prints the common name of the stars and the mean parallax in the order given to us in the file.

// Simple Demo for StarNearEarth project.  See Read Me file for details
// CS 2C, Foothill College, Michael Loceff, creator

#include <iostream>
using namespace std;

#include "StarNearEarth.h"

// --------------- main ---------------
int main()
{
   int k, arraySize;
   StarNearEarthReader starInput("nearest_stars.txt");

   if (starInput.readError())
   {
      cout << "couldn't open " << starInput.getFileName() << " for input.\n";
      exit(1);
   }

   cout << starInput.getFileName() << endl;
   cout << starInput.getNumStars() << endl;

   // create an array of objects for our own use:
   arraySize = starInput.getNumStars();
   StarNearEarth *star_array = new StarNearEarth[arraySize];
   for (k = 0; k < arraySize; k++)
      star_array[k] =  starInput[k];

   // display a couple facts about each star
   for (int k = 0; k < arraySize; k++)
      cout << star_array[k].getNameCommon() << " " 
         << star_array[k].getParallaxMean() << endl;

   delete[] star_array;   
   
   return 0;
}

If you run this, you should see the names of some stars along with their parallax values. Because the data happens to be stored from nearest to farthest (distance to our Solar System), it happens that these values will go from largest (around .76 arc seconds) to smallest (around .15 arc seconds). That's because larger parallaxes mean closer stars.

The program uses a data structure to read the files called StarNearEarthReader which is opaque to you (unless you look into the .h and .cpp file). This is merely a convenient interface I wrote to allow you to get the data from the file and have a means to move it, one object-at-a-time into any array, vector or other data structure that is built of StarNearEarth objects. It can be dereferenced with the brackets operator using indices from 0 to getNumStars() - 1 as you see above. This gives you a basic prototype for future projects using this interesting data set.

3B.4.3 The "Trick" Of the Current Project

The trick for us will be to form a model of the galactic neighborhood of our Sun by reading this data and then doing two things that will require fun and work:

  1. Turning the crazy right ascension (RA), declination (DEC) and parallax numbers into Cartesian (x, y, z) coordinates so we can work with the positions of the stars easily.
  2. Projecting the 3-dimensional (x, y, z) onto a selected 2-D x-y (or x-z or y-z) plane and viewing that plane on our console as a rectangular array with the Sun in the center, and the 100 stars distributed around it exactly as they really are in space.

3B.4.4 Deriving a Class From StarNearEarth

We will exercise our class inheritance skills by using the existing StarNearEarth class and noting that we want to add three new private data to it: the x, y and z coordinates of the star (after we compute them, of course). Here is the class prototype for the class, which we call SNE_Analyzer:

class SNE_Analyzer: public StarNearEarth
{
private:
   double x, y, z;

public:
   void calcCartCoords();
   double getX() { return x; }
   double getY() { return y; }
   double getZ() { return z; }
   string coordToString();
   SNE_Analyzer & operator=( const StarNearEarth &sne );
};

Because I don't want to make you work on time-wasting things, I will provide you with the assignment operator:

SNE_Analyzer & SNE_Analyzer::operator=( const StarNearEarth &sne )
{
   
   setRank(sne.getRank()); 
   setNameCns(sne.getNameCns());
   setNumComponents(sne.getNumComponents());
   setNameLhs(sne.getNameLhs());
   setRAsc(sne.getRAsc());
   setDec(sne.getDec());
   setPropMotionMag(sne.getPropMotionMag());
   setPropMotionDir(sne.getPropMotionDir());
   setParallaxMean(sne.getParallaxMean());
   setParallaxVariance(sne.getParallaxVariance());
   setWhiteDwarfFlag(sne.getWhiteDwarfFlag());
   setSpectralType(sne.getSpectralType());
   setMagApparent(sne.getMagApparent());
   setMagAbsolute(sne.getMagAbsolute());
   setMass(sne.getMass());
   setNotes(sne.getNotes());
   setNameCommon(sne.getNameCommon()); 
   calcCartCoords();

   return *this;
}

The reason we need this is because of these lines which will be in our program(s):

   SNE_Analyzer *starArray = new SNE_Analyzer[arraySize];
   for (k = 0; k < arraySize; k++)
      starArray[k] =  starInput[k];

You see we are assigning a StarNearEarth object to an SNE_Analyzer object. This assignment operator puts the base class data into its correct positions in the derived class object, and also sets the x, y and z coordinates with the sought-after Cartesian values using the method calcCartCoords().

I don't expect you all to know the math to convert spherical coordinates (which is what RA, DEC and parallax are) to Cartesian coordinates, which can be easily graphed. We also have to turn parallax to distance in light years (LY), so I will give you those formulae:

equation

This gives us the so-called r of spherical coordinates - the distance of the star to the Earth. We'll use this in a moment. Next, we need to convert from the degrees, which right ascension (RA) and declination (DEC) use, to radians:

equation
equation

Finally, we can combine these to get the (x, y, z) coordinates of each star:

equation

This, then, is the work of the calcCartCoords().

Once you have the class written, you can test it with this main() and see what the x-y-z coordinates of the various stars are:

// --------------- main ---------------
int main()
{
   
   int k, arraySize;
   StarNearEarthReader starInput("nearest_stars.txt");

   if (starInput.readError())
   {
      cout << "couldn't open " << starInput.getFileName() << " for input.\n";
      exit(1);
   }

   cout << starInput.getFileName() << endl;
   cout << starInput.getNumStars() << endl;

   // create an array of objects for our own use:
   arraySize = starInput.getNumStars();
   SNE_Analyzer *starArray = new SNE_Analyzer[arraySize];
   for (k = 0; k < arraySize; k++)
      starArray[k] =  starInput[k];

   // display cartesion coords
   for (int k = 0; k < arraySize; k++)
      cout << starArray[k].getNameCommon() << " " 
         << starArray[k].coordToString() << endl;

   delete[] starArray;   
   
   return 0;
}

I won't print the results, since I don't want to spoil the surprise.

By the way, most of these stars will be strange to you. That's because they are small (way bigger than our Earth or Jupiter, but smaller than the Sun), and so they are not visible with the naked eye, and thus not famous. This gives us some understandable and workable data with which to work.