// SofaJplDemoCpp2_1.cpp // Demonstration program for my SofaJpl 2.1 DLL // Paul S. Hirose 2018 August 26 // DOCUMENTATION // The Visual Studio Object Browser displays the main SofaJpl documentation. After adding a // reference to SofaJpl to your project, open Object Browser and expand the SofaJpl namespace. // revision history #if 0 2018-09-07 Added a compatibility mode to duplicate the equatorial, ecliptical, and az / el of JPL Horizons.The first two agree to 1 mas in my tests.Az / el agrees .0001 deg, which is the Horizons output precision. #endif #include "stdafx.h" using namespace System; // Everything in SofaJpl is in namespace HirosePS.SofaJpl. namespace SofaJpl = HirosePS::SofaJpl; // If this #define is nonzero, convert a JPL ASCII ephemeris to binary. After the binary is created, // change the value to 0. #define MAKE_EPHEMERIS 0 // the available time scales #define UTC 1 #define UT1 2 #define TT 3 // Select the time scale you wish to use. #define TIME_SCALE UTC // If nonzero, use the delta T you provide. Otherwise use the SofaJpl delta T model. // Has no effect if time scale is UTC. #define MANUAL_DELTA_T 0 // If nonzero, use a precession nutation model compatible with JPL Horizons: IAU 1976 / 80 // precession / nutation, no frame bias, apply IERS pole offsets. // NOTE: To obtain coordinates in the horizontal system or the ITRS, compatible with Horizons, it is // also necessary to activate the MANUAL_DELTA_T option and supply the correct delta T of date. // In addition, for compatible coordinates in the horizontal system set the deflection of the // vertical parameters (xi and eta) to zero. #define HORIZONS 0 // The leap second table. One is included in the SofaJpl installation .zip file. #define LEAP_SEC_TABLE "C:\\Users\\Stan\\Documents\\astro\\SofaJpl_2_1_2\\leapSecTable.txt" // JPL ASCII ephemeris and header. Must be downloaded from JPL. Not needed after they are // converted to a binary ephemeris. #define EPHEMERIS_HEADER "C:\\Users\\Stan\\Documents\\astro\\jpl\\header.422" #define ASCII_EPHEMERIS "C:\\Users\\Stan\\Documents\\astro\\jpl\\ascp2000.422" // The binary JPL ephemeris to create and use. #define BINARY_EPHEMERIS "binp2000_2099.422" // mathematical constants const double _degPerHour = 15.0; // an enumeration of the three possible angle output formats enum angleFormat { D, DM, DMS }; // output format desired by the user angleFormat _format; // These control the output resolution of angle and time. The values are computed automatically // from the angle accuracy set by the user. double _angleResolution; // resolution units per degree double _timeResolution; // resolution units per hour // Number of decimal places in the floating point 'f' format to achieve unit vector rectangular // coordinate precision comparable to the angles. int _vectorResolution; // helper functions (source code after main()) void displayPolarMotion(double poleX, double poleY, String ^label); void displayModulus(SofaJpl::Vector ^vec, String ^label); void displayHourAngle(double angle, String ^str); void displaySiderealTime(double gast, String ^label); void displaySemidiameter(double sd, String ^label); void displayPolarMotion(double poleX, double poleY, String ^label); void displayRaDec(SofaJpl::Vector ^vec, String ^label); void displayAzEl(SofaJpl::Vector ^vec, String ^label); void displayEcliptical(SofaJpl::Vector ^vec, String ^label); void displayGeodetic(double lon, double lat, String ^label); void displayXyz(SofaJpl::Vector ^v, String ^xyzLabel, String ^modulusLabel); void displayAzEl(SofaJpl::Spherical ^sph, String ^label); void displayAzZd(SofaJpl::Spherical ^sph, String ^label); int main(array<System::String ^> ^args) { // Set the display format and angle accuracy. _format = DMS; double angleAccuracy = SofaJpl::Angle::DmsToRad(0, 0, 0.1); // Set the epoch. int year = 2018; int month = 6; int day = 6; int hour = 4; int minute = 0; double second = 0.0; bool julian = false; // true if date is in Julian calendar SofaJpl::Duration ^deltaT; #if TIME_SCALE == UTC SofaJpl::Duration ^ut1MinusUtc = SofaJpl::Duration::FromSeconds(0.07522); #else #if MANUAL_DELTA_T deltaT = SofaJpl::Duration::FromSeconds(69.109); #else // Get delta T from the SofaJpl model. deltaT = SofaJpl::DeltaT::GetDeltaT( gcnew SofaJpl::JulianDate(year, month, day, hour, minute, second, julian)); #endif #endif // Observer position. String ^obsName = "Kitt Peak Observatory"; // name of topocenter (optional) double lon = SofaJpl::Angle::DmsToRad(-111, 36, 00.0); // east longitude double lat = SofaJpl::Angle::DmsToRad(31, 57, 48.0); // north latitude float height = 2100.0f; // meters above ellipsoid // This instance of the Topocenter class will supply the observer position and velocity // with respect to the ICRS. SofaJpl::Topocenter ^obs = gcnew SofaJpl::Topocenter(lat, lon, height); // Construct an Atmosphere object to apply refraction. The parameters required by the // constructor are type "float", thus the f suffix on the numbers. float altimiterSetting = 29.90f * SofaJpl::Atmosphere::MillibarsPerInchHg; // millibars float degC = 12.0f; // Celsius float dewPointC = 0.0f; // Celsius. SofaJpl::Atmosphere ^atm = gcnew SofaJpl::Atmosphere(height, degC, altimiterSetting, dewPointC, false); // polar motion (radians) double poleX = SofaJpl::Angle::DmsToRad(0, 0, 0); double poleY = SofaJpl::Angle::DmsToRad(0, 0, 0); // deflection of the vertical at the observer (radians) double xi = SofaJpl::Angle::DmsToRad(0, 0, 0); double eta = SofaJpl::Angle::DmsToRad(0, 0, 0); // Create a binary ephemeris from JPL ASCII files. #if MAKE_EPHEMERIS Console::WriteLine("\nCREATING BINARY JPL EPHEMERIS\n"); SofaJpl::JplEphemeris::AsciiToBinary(EPHEMERIS_HEADER, ASCII_EPHEMERIS, BINARY_EPHEMERIS); #endif // Open the binary JPL ephemeris. This loads the entire ephemeris into RAM. Even if the // target body is a star, a solar system ephemeris is required for parallax, aberration, // and light deflection due to solar gravitation. SofaJpl::JplEphemeris ^eph = gcnew SofaJpl::JplEphemeris(BINARY_EPHEMERIS); // There are several (mutually exclusive) ways to specify the target body. // Use the SofaJpl star catalog, a subset of the Hipparcos catalog complete to mag. 3. #if 0 // Create a HipparcosCatalog object. The star name and star data XML files must be present // in the executable's directory. These files are included with SofaJpl. SofaJpl::HipparcosCatalog ^catalog = gcnew SofaJpl::HipparcosCatalog(); // Look up the star name in the catalog we just constructed, and construct a Star object. // The current SofaJpl implementation requires a full match (not substring) to a designation // in the name dictionary. It may be necessary to examine the dictionary (star_names.vot) // with a text editor to find the correct name of a star. SofaJpl::Body ^body = catalog->GetStar("name Vega", eph); #endif // Star, from data you supply. The first parameter is the epoch of the data, J2000.0 in // this case. #if 0 SofaJpl::Body ^body = gcnew SofaJpl::Star(SofaJpl::JulianDate::J2000Base, 293.0899579 / _degPerHour, +69.6611767, 173.77, 597.482, -1738.313, 26.78, eph, "sig dra"); #endif // Solar system body in the JPL ephemeris. The correction for light time iterates until the // solution converges to the specified angle accuracy. #if 1 SofaJpl::Body ^body = gcnew SofaJpl::JplBody(SofaJpl::JplEphemeris::Body::Venus, eph, angleAccuracy); #endif // Finished setting parameters. Calculate and display results. // Compute the angle, time, and rectangular coordinate resolution consistent with the angle // accuracy (radians) specified by user. These variables control output formatting. _angleResolution = 1.0 / SofaJpl::Angle::RadiansToDegrees(angleAccuracy); _timeResolution = _angleResolution * _degPerHour; _vectorResolution = (int)(-Math::Log10(angleAccuracy) + 1.0); if (_vectorResolution < 1) _vectorResolution = 1; // Always display at least 1 decimal place. // Load the leap second table. SofaJpl::Utc::LoadTableFromFile(LEAP_SEC_TABLE); // Calculate UT1 and TT. UTC is not calculated (yet) unless it's the input time scale. SofaJpl::JulianDate ^ut1, ^tt; // The Utc class converts batween UTC and TT. SofaJpl::Utc ^utc; #if TIME_SCALE == UTC utc = gcnew SofaJpl::Utc(year, month, day, hour, minute, second, julian); tt = utc->TerrestrialTime; ut1 = gcnew SofaJpl::JulianDate(year, month, day, hour, minute, second, julian) + ut1MinusUtc; deltaT = tt - ut1; #endif #if TIME_SCALE == UT1 ut1 = gcnew SofaJpl::JulianDate(year, month, day, hour, minute, second, julian); tt = ut1 + deltaT; #endif #if TIME_SCALE == TT tt = gcnew SofaJpl::JulianDate(year, month, day, hour, minute, second, julian); ut1 = tt - deltaT; #endif // Display date and time in UTC (if applicable). // The TimeFields class breaks down a JulianDate into year, month, etc. SofaJpl::TimeFields ^tf1; #if TIME_SCALE == UTC tf1 = utc->ToTimeFields(_timeResolution, julian); Console::WriteLine("{0} UTC", tf1); #else // The time input from the user was UT1 or TT, so it's not safe to assume conversion to UTC // is possible. It must be compared to the UTC table boundaries, which are in terms of TAI. SofaJpl::JulianDate ^tai = tt - SofaJpl::Duration::TTMinusTai; if (tai >= SofaJpl::Utc::DefaultTable->FirstTai && tai <= SofaJpl::Utc::DefaultTable->LastTai) { utc = gcnew SofaJpl::Utc(tt); tf1 = utc->ToTimeFields(_timeResolution, julian); Console::WriteLine("{0} UTC", tf1); } #endif // Display UT1. tf1 = ut1->ToTimeFields(_timeResolution, julian); Console::WriteLine("{0} UT1", tf1); // Display TT as date and time, and also as 2-part Julian date. tf1 = tt->ToTimeFields(_timeResolution, julian); Console::WriteLine("{0} TT", tf1); Console::WriteLine("Dates are {0} calendar.", julian ? "Julian" : "Gregorian"); Console::WriteLine("JD {0} TT", tt); // Display delta T. SofaJpl::Sexagesimal ^sex1 = gcnew SofaJpl::Sexagesimal(deltaT->ToHours(), _timeResolution); Console::WriteLine("{0:+fhms} delta T", sex1); // Display polar motion. Console::WriteLine("\npolar motion"); displayPolarMotion(poleX, poleY, "x, y"); // Display topocenter geodetic and rectangular coordinates. Console::WriteLine(); Console::WriteLine(obsName); // name of observatory displayGeodetic(lon, lat, "E lon, N lat"); Console::WriteLine("{0:f1} meters above ellipsoid", height); SofaJpl::Vector ^obsVec = obs->ToVector(); displayXyz(obsVec, "ITRS unit vector", "modulus (km)"); // Display deflection of the vertical. The same format as polar motion is appropriate. Console::WriteLine("\ndeflection of the vertical"); displayPolarMotion(xi, eta, "xi, eta"); // Display atmosphere conditions. Console::WriteLine("\n{0:f0} C ({1:f0} F) at observer", atm->StationTemperature, atm->StationTemperature * 1.8 + 32.0); Console::WriteLine("{0,6:f1} mb ({1,5:f2}\" Hg) altimeter setting", atm->AltimeterSetting, atm->AltimeterSetting / SofaJpl::Atmosphere::MillibarsPerInchHg); Console::WriteLine("{0,6:f1} mb ({1,5:f2}\" Hg) station pressure", atm->StationPressure, atm->StationPressure / SofaJpl::Atmosphere::MillibarsPerInchHg); if (atm->HumidityIsRelative) Console::WriteLine("{0:f0}% relative humidity", atm->Humidity); else Console::WriteLine("{0:f0} C ({1:f0} F) dew point", atm->Humidity, atm->Humidity * 1.8 + 32.0); // Create rotation matrices for the coordinate transformations. // ITRS to horizontal (east, north, zenith) system, including deflection of the vertical SofaJpl::RMatrix ^itrstoHor = SofaJpl::RMatrix::ItrsToHor(lat, lon, xi, eta); // polar motion matrix (terrestrial intermediate system to ITRS) SofaJpl::RMatrix ^tirsToItrs = SofaJpl::RMatrix::TirsToItrs(tt, poleX, poleY); // GCRS to true equator & equinox (IAU 2006 precession and 2000B nutation). SofaJpl::RMatrix ^gcrsToMean06 = SofaJpl::RMatrix::Precess06(tt); double eps06 = SofaJpl::RMatrix::MeanObliq06(tt); // mean obliquity double dPsi00, dEps00; // nutation in longitude and obliquity SofaJpl::RMatrix::NutationAngles00b(tt, dPsi00, dEps00); SofaJpl::RMatrix ^gcrsToTrue06 = SofaJpl::RMatrix::Nutate(eps06, dPsi00, dEps00) * gcrsToMean06; #if HORIZONS // Generate coordinates compatible with JPL Horizons (1976/80 precession/nutation). // In SofaJpl, 1976 precession includes frame bias, which must be removed for // Horizons compatibility. SofaJpl::RMatrix ^gcrsToMean = SofaJpl::RMatrix::Precess76(tt) * SofaJpl::RMatrix::IcrsToJ2000->Transpose(); // Calculate the GCRS to ecliptic (mean equinox) rotation matrix. double eps = SofaJpl::RMatrix::MeanObliq80(tt); // mean obl. SofaJpl::RMatrix ^gcrsToEclipticMean = SofaJpl::RMatrix::GcrsToEclip(gcrsToMean, eps); // GCRS vector to the CIP, based on the IAU 2006 precession & 2000 nutation models. SofaJpl::Vector ^cipGcrs06 = gcrsToTrue06->Row(3); // Transform it to spherical coords in the 1976/80 ecliptic and mean equinox system. SofaJpl::Vector ^cipEclip06 = gcrsToEclipticMean * cipGcrs06; SofaJpl::Spherical ^cipSph = gcnew SofaJpl::Spherical(cipEclip06); // Derive and apply the nutation angles to obtain the GCRS to true equator/equinox matrix. double dPsi, dEps; dPsi = SofaJpl::Angle::HalfPi - cipSph->LonEast; dEps = cipSph->NPD - eps; SofaJpl::RMatrix ^gcrsToTrue = SofaJpl::RMatrix::Nutate(eps, dPsi, dEps) * gcrsToMean; // Compute the GCRS to terrestrial intermediate matrix double gast = SofaJpl::Angle::Gast94(ut1); SofaJpl::RMatrix ^gcrsToTirs = SofaJpl::RMatrix::GcrsToTirs(gcrsToTrue, gast); #else // Generate coordinates compatible with IAU 2006/00 precession/nutation. // Mean obliquity and nutation in obliquity have already been computed. double eps = eps06; double dEps = dEps00; // GCRS to true equator/equinox matrix has already been computed. SofaJpl::RMatrix ^gcrsToTrue = gcrsToTrue06; // Get X and Y of the celestial intermediate pole. double cipX, cipY; gcrsToTrue->CipXY(cipX, cipY); // Form the GCRS to celestial intermediate matrix. SofaJpl::RMatrix ^gcrsToCirs = SofaJpl::RMatrix::GcrsToCirs(cipX, cipY, SofaJpl::RMatrix::S06(tt, cipX, cipY)); // Greenwich apparent sidereal time. double gast = SofaJpl::Angle::Gast06b(ut1, tt); // Earth rotation angle double era = SofaJpl::Angle::Era00(ut1); // Compute the GCRS to terrestrial intermediate matrix SofaJpl::RMatrix ^gcrsToTirs = SofaJpl::RMatrix::GcrsToTirs(gcrsToCirs, era); #endif // End code compatible with IAU 2006/00 precession/nutation. The remaining rotation // matrix computations are common to IAU 2006/00 and JPL Horizons modes. // GCRS to ecliptic and true equinox SofaJpl::RMatrix ^gcrsToEcliptic = SofaJpl::RMatrix::GcrsToEclip(gcrsToTrue, eps + dEps); // GCRS to ITRS, including polar motion SofaJpl::RMatrix ^gcrsToItrs = tirsToItrs * gcrsToTirs; // GCRS to horizontal, including deflection of the vertical. SofaJpl::RMatrix ^gcrsToHor = itrstoHor * gcrsToItrs; // Display barycentric coordinates of the body. Console::WriteLine("\n{0} barycentric position & velocity", body->Name); SofaJpl::PVVector ^pv1 = body->Barycentric(tt); displayRaDec(pv1->Position, "RA, dec (ICRS)"); displayXyz(pv1->Position, "unit vector (ICRS)", "distance (km)"); displayXyz(pv1->Velocity, "velocity unit vector", "km/day"); // heliocentric coordinates Console::WriteLine("\n{0} heliocentric position & velocity", body->Name); pv1 = body->Heliocentric(tt); displayRaDec(pv1->Position, "RA, dec (ICRS)"); displayXyz(pv1->Position, "unit vector (ICRS)", "distance (km)"); displayXyz(pv1->Velocity, "velocity unit vector", "km/day"); // geocentric coordinates Console::WriteLine("\n{0} geocentric geometric position & velocity", body->Name); pv1 = body->GeocentricGeometric(tt); displayRaDec(pv1->Position, "RA, dec (ICRS)"); displayXyz(pv1->Position, "unit vector (ICRS)", "geometric distance (km)"); displayXyz(pv1->Velocity, "velocity unit vector", "km/day"); Console::WriteLine("\n{0} geocentric astrometric place", body->Name); SofaJpl::Vector ^vec1 = body->GeocentricAstrometric(tt); displayRaDec(vec1, "RA, dec (ICRS)"); displayModulus(vec1, "astrometric distance (km)"); Console::WriteLine("\n{0} geocentric apparent place", body->Name); vec1 = body->GeocentricApparent(tt); displayRaDec(vec1, "RA, dec (ICRS)"); displayRaDec(gcrsToTrue * vec1, "equinox RA, dec"); #if ! HORIZONS displayRaDec(gcrsToCirs * vec1, "intermediate RA, dec"); #endif displayEcliptical(gcrsToEcliptic * vec1, "ecliptic true lon, lat"); // Geographic position of the body in the ITRS. If the user provided the parameters, this // includes polar motion. SofaJpl::Spherical ^sph1 = gcnew SofaJpl::Spherical(gcrsToItrs * vec1); displayGeodetic(sph1->LonEast, sph1->Lat, "E lon, N lat (ITRS)"); displayGeodetic(sph1->LonWest, sph1->Lat, "W lon, N lat (ITRS)"); displayHourAngle(SofaJpl::Angle::NormPlus(sph1->LonWest + lon), "LHA"); // Semidiameter. All bodies have a property to give radius in km. Stars default to zero. // Solar system bodies are automatically initialized to their adopted IAU radii. This // property can be modified by the user. double sd = SofaJpl::Angle::Semidiameter(vec1->Modulus(), body->Radius); displaySemidiameter(sd, "geocentric semidiameter"); // Greenwich apparent sidereal time (IAU 2006 precession, 2000B nutation) Console::WriteLine(); displaySiderealTime(SofaJpl::Angle::Gast06b(ut1, tt), "Greenwich apparent sidereal time"); // Earth rotation angle. #if ! HORIZONS displayHourAngle(era, "Earth rotation angle"); #endif // topocentric coordinates // Get the GCRS position and velocity of the topocenter in a PVVector (position and velocity // vector). The parameter passed to ToGcrsPV() must be the ITRS to GCRS rotation matrix, but // what we calculated earlier does the opposite transformation. Thus it's transposed to // reverse its sense. SofaJpl::PVVector ^obsPV = obs->ToGcrsPV(gcrsToItrs->Transpose()); Console::WriteLine("\n{0} topocentric geometric place", body->Name); vec1 = body->TopocentricGeometric(tt, obsPV)->Position; displayRaDec(vec1, "RA, dec (ICRS)"); displayModulus(vec1, "km"); Console::WriteLine("\n{0} topocentric astrometric place", body->Name); vec1 = body->TopocentricAstrometric(tt, obsPV->Position); displayRaDec(vec1, "RA, dec (ICRS)"); displayModulus(vec1, "km"); Console::WriteLine("\n{0} topocentric apparent place", body->Name); vec1 = body->TopocentricApparent(tt, obsPV); displayRaDec(vec1, "RA, dec (ICRS)"); // Transform to apparent place from the ICRS to the horizontal system. If the user supplied // the parameters, this includes polar motion and deflection of the vertical. vec1 = gcrsToHor * vec1; // Convert to spherical coordinates. SofaJpl::Spherical ^sphUnref = gcnew SofaJpl::Spherical(vec1); // Use the Atmosphere object created earlier to generate refracted spherical coordinates. // The application of refraction is iterative, so the desired accuracy must be passed // to Refract(). SofaJpl::Spherical ^sphRefr = atm->Refract(sphUnref, angleAccuracy); Console::WriteLine("\n{0} azimuth, zenith distance, elevation", body->Name); displayAzZd(sphUnref, "az, unrefracted ZD"); displayAzEl(sphUnref, "az, unrefracted el"); displayAzEl(sphRefr, "az, refracted el"); // Semidiameter. All bodies have a property to give radius in km. Stars default to zero, but // solar system bodies are automatically initialized to their adopted IAU radii. sd = SofaJpl::Angle::Semidiameter(vec1->Modulus(), body->Radius); displaySemidiameter(sd, "topocentric semidiameter"); // If applicable, display phase angle: the separation angle, at the body, between // vectors directed to the Sun and the observer. These are the negatives of vectors to the // body's heliocentric and topocentric positions in the ICRS. If we omit the negations, // both vectors are off by 180 degrees and thus the angle between them is still correct. if (body->IsSolarSystemBody && body->IsSun == false) { SofaJpl::JplBody ^earth = gcnew SofaJpl::JplBody( SofaJpl::JplEphemeris::Body::Earth, eph, angleAccuracy); SofaJpl::JplBody ^sun = gcnew SofaJpl::JplBody( SofaJpl::JplEphemeris::Body::Sun, eph, angleAccuracy); SofaJpl::Vector ^earthVec = body->TopocentricGeometric(tt, obsPV)->Position; SofaJpl::Vector ^sunVec = body->Heliocentric(tt)->Position; double pa = earthVec->SeparationAngle(sunVec); Console::WriteLine("{0:f0}° phase angle (0 = full, 180 = new)", SofaJpl::Angle::RadiansToDegrees(pa)); } return 0; }// end main() // helper methods to display data with the format and precision selected by user /// <summary> /// Display the modulus of a vector. /// </summary> /// <param name="vec">the vector</param> /// <param name="label">string to display after the modulus</param> void displayModulus(SofaJpl::Vector ^vec, String ^label) { System::Text::StringBuilder ^sb1 = gcnew System::Text::StringBuilder("{0:e", 8); // The _vectorResolution value is appropriate for the 'f' format. But the 'e' format gives // one more significant digit, so subtract 1. sb1->Append(_vectorResolution - 1); sb1->Append("} "); Console::WriteLine(sb1->ToString() + label, vec->Modulus()); } /// <summary> /// Display hour angle as a sexagesimal. /// </summary> /// <param name="angle">hour angle (radians) </param> /// <param name="str">string to display after the angle</param> void displayHourAngle(double angle, String ^str) { String ^formatString; switch (_format) { case D: formatString = "{0:3c°'\"} {1}"; break; case DM: formatString = "{0:3b°'\"} {1}"; break; default: formatString = "{0:3a°'\"} {1}"; break; } SofaJpl::Sexagesimal ^sex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(angle), _angleResolution, 360); Console::WriteLine(formatString, sex, str); } /// <summary> /// Display sidereal time as a sexagesimal. /// </summary> /// <param name="gast">sidereal time (radians)</param> /// <param name="label">string to display after the angle</param> /// <remarks>Unless the format is DMS, display degrees not hours.</remarks> void displaySiderealTime(double gast, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:3c°'\"} {1}"; break; case DM: formatString = "{0:3b°'\"} {1}"; break; default: formatString = "{0:2ahms} {1}"; break; } SofaJpl::Sexagesimal ^sex; if (_format == DMS) sex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToHours(gast), _timeResolution, 24); else sex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(gast), _angleResolution, 360); Console::WriteLine(formatString, sex, label); } /// <summary> /// Display semidiameter as a sexagesimal. /// </summary> /// <param name="sd">semidiameter (radians)</param> /// <param name="label">string to display after the angle</param> void displaySemidiameter(double sd, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:c°'\"} {1}"; break; case DM: formatString = "{0:e°'\"} {1}"; break; default: formatString = "{0:f°'\"} {1}"; break; } sd = SofaJpl::Angle::RadiansToDegrees(sd); SofaJpl::Sexagesimal ^sex = gcnew SofaJpl::Sexagesimal(sd, _angleResolution); Console::WriteLine(formatString, sex, label); } /// <summary> /// Display polar motion angles as sexagesimals. /// </summary> /// <param name="poleX">pole X (radians)</param> /// <param name="poleY">pole Y (radians)</param> /// <param name="label">string to display after the angles</param> void displayPolarMotion(double poleX, double poleY, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:+c°'\"} {1:+c°'\"} {2}"; break; case DM: formatString = "{0:+e°'\"} {1:+e°'\"} {2}"; break; default: formatString = "{0:+f°'\"} {1:+f°'\"} {2}"; break; } double xDeg = SofaJpl::Angle::RadiansToDegrees(poleX); double yDeg = SofaJpl::Angle::RadiansToDegrees(poleY); SofaJpl::Sexagesimal ^sexX = gcnew SofaJpl::Sexagesimal(xDeg, _angleResolution); SofaJpl::Sexagesimal ^sexY = gcnew SofaJpl::Sexagesimal(yDeg, _angleResolution); Console::WriteLine(formatString, sexX, sexY, label); } /// <summary> /// Display a vector as right ascension and declination. /// </summary> /// <param name="vec">vector to the body with respect to the equatorial system</param> /// <param name="label">string to display after the angles</param> /// <remarks>Unless the format is DMS, display RA in degrees not hours.</remarks> void displayRaDec(SofaJpl::Vector ^vec, String ^label) { String ^formatString; switch (_format){ case D: formatString = "{0:3c°'\"} {1:+2c°'\"} {2}"; break; case DM: formatString = "{0:3b°'\"} {1:+2b°'\"} {2}"; break; default: formatString = "{0:2ahms} {1:+2a°'\"} {2}"; break; } SofaJpl::Spherical ^sph = gcnew SofaJpl::Spherical(vec); SofaJpl::Sexagesimal ^raSex; if (_format == DMS) raSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToHours(sph->RA), _timeResolution, 24); else raSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(sph->RA), _angleResolution, 360); SofaJpl::Sexagesimal ^decSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(sph->Dec), _angleResolution); Console::WriteLine(formatString, raSex, decSex, label); } /// <summary> /// Display a vector as azimuth and elevation. /// </summary> /// <param name="vec">vector to the body with respect to the horizontal system</param> /// <param name="label">string to display after the angles</param> void displayAzEl(SofaJpl::Vector ^vec, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:3c°'\"} {1:+2c°'\"} {2}"; break; case DM: formatString = "{0:3b°'\"} {1:+2b°'\"} {2}"; break; default: formatString = "{0:3a°'\"} {1:+2a°'\"} {2}"; break; } SofaJpl::Spherical ^sph = gcnew SofaJpl::Spherical(vec); SofaJpl::Sexagesimal ^azSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(sph->Az), _angleResolution, 360); SofaJpl::Sexagesimal ^elSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(sph->El), _angleResolution); Console::WriteLine(formatString, azSex, elSex, label); } /// <summary> /// Display a vector as ecliptic longitude and latitude. /// </summary> /// <param name="vec">vector to the body with respect to the ecliptic system</param> /// <param name="label">string to display after the angles</param> void displayEcliptical(SofaJpl::Vector ^vec, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:3c°'\"} {1:+2c°'\"} {2}"; break; case DM: formatString = "{0:3b°'\"} {1:+2b°'\"} {2}"; break; default: formatString = "{0:3a°'\"} {1:+2a°'\"} {2}"; break; } SofaJpl::Spherical ^sph = gcnew SofaJpl::Spherical(vec); SofaJpl::Sexagesimal ^lonSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(sph->LonEast), _angleResolution, 360); SofaJpl::Sexagesimal ^latSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(sph->Lat), _angleResolution); Console::WriteLine(formatString, lonSex, latSex, label); } /// <summary> /// Display geodetic longitude and latitude. /// </summary> /// <param name="lon">east longitude (radians)</param> /// <param name="lat">north latitude (radians)</param> /// <param name="label">string to display after the angles</param> void displayGeodetic(double lon, double lat, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:3c°'\"} {1:+2c°'\"} {2}"; break; case DM: formatString = "{0:3b°'\"} {1:+2b°'\"} {2}"; break; default: formatString = "{0:3a°'\"} {1:+2a°'\"} {2}"; break; } SofaJpl::Sexagesimal ^lonSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(lon), _angleResolution, 360); SofaJpl::Sexagesimal ^latSex = gcnew SofaJpl::Sexagesimal( SofaJpl::Angle::RadiansToDegrees(lat), _angleResolution); Console::WriteLine(formatString, lonSex, latSex, label); } /// <summary> /// Display a vector as xyz components of a unit vector and modulus. /// </summary> /// <param name="v">vector</param> /// <param name="xyzLabel">label for the unit vector</param> /// <param name="modulusLabel">label for the modulus</param> void displayXyz(SofaJpl::Vector ^v, String ^xyzLabel, String ^modulusLabel) { System::Text::StringBuilder ^sb1 = gcnew System::Text::StringBuilder("{0:f", 23); sb1->Append(_vectorResolution); sb1->Append("} {1:f"); sb1->Append(_vectorResolution); sb1->Append("} {2:f"); sb1->Append(_vectorResolution); sb1->Append("} "); SofaJpl::Vector ^uv = v->Unit(); Console::WriteLine(sb1->ToString() + xyzLabel, uv->X, uv->Y, uv->Z); displayModulus(v, modulusLabel); } /// <summary> /// Given a Spherical object, display azimuth and elevation as sexagesimals. /// </summary> /// <param name="sph">spherical coordinates in the horizontal system</param> /// <param name="label">string to display after the angles</param> void displayAzEl(SofaJpl::Spherical ^sph, String ^label) { String ^formatString; switch (_format) { case D: formatString = "{0:3c°'\"} {1: 2c°'\"} {2}"; break; case DM: formatString = "{0:3b°'\"} {1: 2b°'\"} {2}"; break; default: formatString = "{0:3a°'\"} {1: 2a°'\"} {2}"; break; } double az = SofaJpl::Angle::RadiansToDegrees(sph->Az); double el = SofaJpl::Angle::RadiansToDegrees(sph->El); SofaJpl::Sexagesimal ^azSex = gcnew SofaJpl::Sexagesimal(az, _angleResolution, 360); SofaJpl::Sexagesimal ^elSex = gcnew SofaJpl::Sexagesimal(el, _angleResolution); Console::WriteLine(formatString, azSex, elSex, label); } /// <summary> /// Given a Spherical object, display azimuth and zenith distance as sexagesimals. /// </summary> /// <param name="sph">spherical coordinates</param> /// <param name="label">string to display after the angles</param> void displayAzZd(SofaJpl::Spherical ^sph, String ^label) { String ^formatString; switch (_format) { case D: // Zenith distance needs a different format than elevation:: There's never a negative // sign, and there can be up to 3 digits before the decimal point:: formatString = "{0:3c°'\"} {1:3c°'\"} {2}"; break; case DM: formatString = "{0:3b°'\"} {1:3b°'\"} {2}"; break; default: formatString = "{0:3a°'\"} {1:3a°'\"} {2}"; break; } double az = SofaJpl::Angle::RadiansToDegrees(sph->Az); double zd = SofaJpl::Angle::RadiansToDegrees(sph->ZenithDistance); SofaJpl::Sexagesimal ^azSex = gcnew SofaJpl::Sexagesimal(az, _angleResolution, 360); SofaJpl::Sexagesimal ^zdSex = gcnew SofaJpl::Sexagesimal(zd, _angleResolution); Console::WriteLine(formatString, azSex, zdSex, label); }
[back]
(last modified 2018-09-09)